From 6e632624bf6e48e7a5021ba484d1f28245840c92 Mon Sep 17 00:00:00 2001 From: amcewen Date: Sat, 14 May 2011 14:42:26 +0100 Subject: [PATCH] Initial version on github. Updates old version which lived on googlecode to use new DHCP and DNS API in the Arduino Ethernet library --- HttpClient.cpp | 433 ++++++++++++++++++ HttpClient.h | 345 ++++++++++++++ .../SimpleHttpExample/SimpleHttpExample.pde | 124 +++++ keywords.txt | 33 ++ 4 files changed, 935 insertions(+) create mode 100644 HttpClient.cpp create mode 100644 HttpClient.h create mode 100644 examples/SimpleHttpExample/SimpleHttpExample.pde create mode 100644 keywords.txt diff --git a/HttpClient.cpp b/HttpClient.cpp new file mode 100644 index 0000000..956f99f --- /dev/null +++ b/HttpClient.cpp @@ -0,0 +1,433 @@ +// Class to simplify HTTP fetching on Arduino +// (c) Copyright 2010-2011 MCQN Ltd +// Released under Apache License, version 2.0 + +#include "HttpClient.h" +#include <../b64/b64.h> +#include +#include +#include "wiring.h" + +// Initialize constants +const char* HttpClient::kUserAgent = "Arduino/2.0"; +const char* HttpClient::kGet = "GET"; +const char* HttpClient::kPost = "POST"; +const char* HttpClient::kPut = "PUT"; +const char* HttpClient::kDelete = "DELETE"; +const char* HttpClient::kContentLengthPrefix = "Content-Length: "; + +HttpClient::HttpClient(Client& aClient) + : iClient(&aClient) +{ + resetState(); +} + +void HttpClient::resetState() +{ + iState = eIdle; + iStatusCode = 0; + iContentLength = 0; + iBodyLengthConsumed = 0; + iContentLengthPtr = 0; +} + +void HttpClient::stop() +{ + iClient->stop(); + resetState(); +} + +int HttpClient::startRequest(const char* aServerName, uint16_t aServerPort, const char* aURLPath, const char* aHttpMethod, const char* aUserAgent, const char* aAcceptList) +{ + if (eIdle != iState) + { + return HttpErrAPI; + } + + if (!iClient->connect(aServerName, aServerPort)) + { +#ifdef LOGGING + Serial.println("Connection failed"); +#endif + return HttpErrConnectionFailed; + } + + // Now we're connected, send the first part of the request + return sendInitialHeaders(aServerName, aURLPath, aHttpMethod, aUserAgent, aAcceptList); +} + +int HttpClient::startRequest(const IPAddress& aServerAddress, uint16_t aServerPort, const char* aServerName, const char* aURLPath, const char* aHttpMethod, const char* aUserAgent, const char* aAcceptList) +{ + if (eIdle != iState) + { + return HttpErrAPI; + } + + if (!iClient->connect(aServerAddress, aServerPort)) + { +#ifdef LOGGING + Serial.println("Connection failed"); +#endif + return HttpErrConnectionFailed; + } + + // Now we're connected, send the first part of the request + return sendInitialHeaders(aServerName, aURLPath, aHttpMethod, aUserAgent, aAcceptList); +} + +int HttpClient::sendInitialHeaders(const char* aServerName, const char* aURLPath, const char* aHttpMethod, const char* aUserAgent, const char* aAcceptList) +{ +#ifdef LOGGING + Serial.println("Connected"); +#endif + // Send the HTTP command, i.e. "GET /somepath/ HTTP/1.0" + print(aHttpMethod); + print(" "); + print(aURLPath); + println(" HTTP/1.0"); + // The host header, if required + if (aServerName) + { + print("Host: "); + println(aServerName); + } + // And user-agent string + print("User-Agent: "); + if (aUserAgent) + { + println(aUserAgent); + } + else + { + println(kUserAgent); + } + if (aAcceptList) + { + // We've got an accept list to send + print("Accept: "); + println(aAcceptList); + } + + // Everything has gone well + iState = eRequestStarted; + return HttpSuccess; +} + +void HttpClient::sendHeader(const char* aHeader) +{ + println(aHeader); +} + +void HttpClient::sendHeader(const char* aHeaderName, const char* aHeaderValue) +{ + print(aHeaderName); + print(": "); + println(aHeaderValue); +} + +void HttpClient::sendHeader(const char* aHeaderName, const int aHeaderValue) +{ + print(aHeaderName); + print(": "); + println(aHeaderValue); +} + +void HttpClient::sendBasicAuth(const char* aUser, const char* aPassword) +{ + // Send the initial part of this header line + print("Authorization: Basic "); + // Now Base64 encode "aUser:aPassword" and send that + // This seems trickier than it should be but it's mostly to avoid either + // (a) some arbitrarily sized buffer which hopes to be big enough, or + // (b) allocating and freeing memory + // ...so we'll loop through 3 bytes at a time, outputting the results as we + // go. + // In Base64, each 3 bytes of unencoded data become 4 bytes of encoded data + unsigned char input[3]; + unsigned char output[5]; // Leave space for a '\0' terminator so we can easily print + int userLen = strlen(aUser); + int passwordLen = strlen(aPassword); + int inputOffset = 0; + for (int i = 0; i < (userLen+1+passwordLen); i++) + { + // Copy the relevant input byte into the input + if (i < userLen) + { + input[inputOffset++] = aUser[i]; + } + else if (i == userLen) + { + input[inputOffset++] = ':'; + } + else + { + input[inputOffset++] = aPassword[i-(userLen+1)]; + } + // See if we've got a chunk to encode + if ( (inputOffset == 3) || (i == userLen+passwordLen) ) + { + // We've either got to a 3-byte boundary, or we've reached then end + b64_encode(input, inputOffset, output, 4); + // NUL-terminate the output string + output[4] = '\0'; + // And write it out + print((char*)output); +// FIXME We might want to fill output with '=' characters if b64_encode doesn't +// FIXME do it for us when we're encoding the final chunk + inputOffset = 0; + } + } + // And end the header we've sent + println(); +} + +void HttpClient::finishRequest() +{ + println(); + iState = eRequestSent; +} + +int HttpClient::responseStatusCode() +{ + if (iState < eRequestSent) + { + return HttpErrAPI; + } + // The first line will be of the form Status-Line: + // HTTP-Version SP Status-Code SP Reason-Phrase CRLF + // Where HTTP-Version is of the form: + // HTTP-Version = "HTTP" "/" 1*DIGIT "." 1*DIGIT + + char c = '\0'; + do + { + // Make sure the status code is reset, and likewise the state. This + // lets us easily cope with 1xx informational responses by just + // ignoring them really, and reading the next line for a proper response + iStatusCode = 0; + iState = eRequestSent; + + unsigned long timeoutStart = millis(); + // Psuedo-regexp we're expecting before the status-code + const char* statusPrefix = "HTTP/*.* "; + const char* statusPtr = statusPrefix; + // Whilst we haven't timed out & haven't reached the end of the headers + while ((c != '\n') && + ( (millis() - timeoutStart) < kHttpResponseTimeout )) + { + if (available()) + { + c = read(); + switch(iState) + { + case eRequestSent: + // We haven't reached the status code yet + if ( (*statusPtr == '*') || (*statusPtr == c) ) + { + // This character matches, just move along + statusPtr++; + if (*statusPtr == '\0') + { + // We've reached the end of the prefix + iState = eReadingStatusCode; + } + } + else + { + return HttpErrInvalidResponse; + } + break; + case eReadingStatusCode: + if (isdigit(c)) + { + // This assumes we won't get more than the 3 digits we + // want + iStatusCode = iStatusCode*10 + (c - '0'); + } + else + { + // We've reached the end of the status code + // We could sanity check it here or double-check for ' ' + // rather than anything else, but let's be lenient + iState = eStatusCodeRead; + } + break; + case eStatusCodeRead: + // We're just waiting for the end of the line now + break; + }; + // We read something, reset the timeout counter + timeoutStart = millis(); + } + else + { + // We haven't got any data, so let's pause to allow some to + // arrive + delay(kHttpWaitForDataDelay); + } + } + if ( (c == '\n') && (iStatusCode < 200) ) + { + // We've reached the end of an informational status line + c = '\0'; // Clear c so we'll go back into the data reading loop + } + } + // If we've read a status code successfully but it's informational (1xx) + // loop back to the start + while ( (iState == eStatusCodeRead) && (iStatusCode < 200) ); + + if ( (c == '\n') && (iState == eStatusCodeRead) ) + { + // We've read the status-line successfully + return iStatusCode; + } + else if (c != '\n') + { + // We must've timed out before we reached the end of the line + return HttpErrTimedOut; + } + else + { + // This wasn't a properly formed status line, or at least not one we + // could understand + return HttpErrInvalidResponse; + } +} + +int HttpClient::skipResponseHeaders() +{ + // Just keep reading until we finish reading the headers or time out + unsigned long timeoutStart = millis(); + // Whilst we haven't timed out & haven't reached the end of the headers + while ((!endOfHeadersReached()) && + ( (millis() - timeoutStart) < kHttpResponseTimeout )) + { + if (available()) + { + (void)readHeader(); + // We read something, reset the timeout counter + timeoutStart = millis(); + } + else + { + // We haven't got any data, so let's pause to allow some to + // arrive + delay(kHttpWaitForDataDelay); + } + } + if (endOfHeadersReached()) + { + // Success + return HttpSuccess; + } + else + { + // We must've timed out + return HttpErrTimedOut; + } +} + +bool HttpClient::endOfBodyReached() +{ + if (endOfHeadersReached() && (contentLength() != kNoContentLengthHeader)) + { + // We've got to the body and we know how long it will be + return (iBodyLengthConsumed >= contentLength()); + } + return false; +} + +int HttpClient::read() +{ + int ret =iClient->read(); + if (endOfHeadersReached() && iContentLength > 0) + { + // We're outputting the body now and we've seen a Content-Length header + // So keep track of how many bytes are left + if (ret >= 0) + { + iBodyLengthConsumed++; + } + } + return ret; +} + +int HttpClient::readHeader() +{ + char c = read(); + + if (endOfHeadersReached()) + { + // We've passed the headers, but rather than return an error, we'll just + // act as a slightly less efficient version of read() + return c; + } + + // Whilst reading out the headers to whoever wants them, we'll keep an + // eye out for the "Content-Length" header + switch(iState) + { + case eStatusCodeRead: + // We're at the start of a line, or somewhere in the middle of reading + // the Content-Length prefix + if (*iContentLengthPtr == c) + { + // This character matches, just move along + iContentLengthPtr++; + if (*iContentLengthPtr == '\0') + { + // We've reached the end of the prefix + iState = eReadingContentLength; + // Just in case we get multiple Content-Length headers, this + // will ensure we just get the value of the last one + iContentLength = 0; + } + } + else if ((iContentLengthPtr == kContentLengthPrefix) && (c == '\r')) + { + // We've found a '\r' at the start of a line, so this is probably + // the end of the headers + iState = eLineStartingCRFound; + } + else + { + // This isn't the Content-Length header, skip to the end of the line + iState = eSkipToEndOfHeader; + } + break; + case eReadingContentLength: + if (isdigit(c)) + { + iContentLength = iContentLength*10 + (c - '0'); + } + else + { + // We've reached the end of the content length + // We could sanity check it here or double-check for "\r\n" + // rather than anything else, but let's be lenient + iState = eSkipToEndOfHeader; + } + break; + case eLineStartingCRFound: + if (c == '\n') + { + iState = eReadingBody; + } + break; + default: + // We're just waiting for the end of the line now + break; + }; + + if ( (c == '\n') && !endOfHeadersReached() ) + { + // We've got to the end of this line, start processing again + iState = eStatusCodeRead; + iContentLengthPtr = kContentLengthPrefix; + } + // And return the character read to whoever wants it + return c; +} + + + diff --git a/HttpClient.h b/HttpClient.h new file mode 100644 index 0000000..df5f61f --- /dev/null +++ b/HttpClient.h @@ -0,0 +1,345 @@ +// Class to simplify HTTP fetching on Arduino +// (c) Copyright MCQN Ltd. 2010-2011 +// Released under Apache License, version 2.0 + +#ifndef HttpClient_h +#define HttpClient_h + +#include "Ethernet.h" + +class HttpClient : public Client +{ +public: + enum + { + HttpSuccess = 0, + // The end of the headers has been reached. This consumes the '\n' + // Could not connect to the server + HttpErrConnectionFailed =-1, + // This call was made when the HttpClient class wasn't expecting it + // to be called. Usually indicates your code is using the class + // incorrectly + HttpErrAPI =-2, + // Spent too long waiting for a reply + HttpErrTimedOut =-3, + // The response from the server is invalid, is it definitely an HTTP + // server? + HttpErrInvalidResponse =-4, + }; + + static const int kNoContentLengthHeader =-1; + static const char* kUserAgent; + static const char* kGet; + static const char* kPost; + static const char* kPut; + static const char* kDelete; + + HttpClient(Client& aClient); + + /** Connect to the server and start to send a GET request. + @param aServerName Name of the server being connected to. If NULL, the + "Host" header line won't be sent + @param aServerPort Port to connect to on the server + @param aURLPath Url to request + @param aUserAgent User-Agent string to send. If NULL the default + user-agent kUserAgent will be sent + @param aAcceptList List of MIME types that the client will accept. If + NULL the "Accept" header line won't be sent + @return 0 if successful, else error + */ + int get(const char* aServerName, uint16_t aServerPort, + const char* aURLPath, const char* aUserAgent =NULL, + const char* aAcceptList =NULL) + { return startRequest(aServerName, aServerPort, aURLPath, kGet, aUserAgent, aAcceptList); } + + /** Connect to the server and start to send a GET request. This version connects + doesn't perform a DNS lookup and just connects to the given IP address. + @param aServerAddress IP address of the server to connect to + @param aServerPort Port to connect to on the server + @param aServerName Name of the server being connected to. If NULL, the + "Host" header line won't be sent + @param aURLPath Url to request + @param aUserAgent User-Agent string to send. If NULL the default + user-agent kUserAgent will be sent + @param aAcceptList List of MIME types that the client will accept. If + NULL the "Accept" header line won't be sent + @return 0 if successful, else error + */ + int get(const IPAddress& aServerAddress, + uint16_t aServerPort, + const char* aServerName, + const char* aURLPath, + const char* aUserAgent =NULL, + const char* aAcceptList =NULL) + { return startRequest(aServerAddress, aServerPort, aServerName, aURLPath, kGet, aUserAgent, aAcceptList); } + + /** Connect to the server and start to send a POST request. + @param aServerName Name of the server being connected to. If NULL, the + "Host" header line won't be sent + @param aServerPort Port to connect to on the server + @param aURLPath Url to request + @param aUserAgent User-Agent string to send. If NULL the default + user-agent kUserAgent will be sent + @param aAcceptList List of MIME types that the client will accept. If + NULL the "Accept" header line won't be sent + @return 0 if successful, else error + */ + int post(const char* aServerName, + uint16_t aServerPort, + const char* aURLPath, + const char* aUserAgent =NULL, + const char* aAcceptList =NULL) + { return startRequest(aServerName, aServerPort, aURLPath, kPost, aUserAgent, aAcceptList); } + + /** Connect to the server and start to send a POST request. This version connects + doesn't perform a DNS lookup and just connects to the given IP address. + @param aServerAddress IP address of the server to connect to + @param aServerPort Port to connect to on the server + @param aServerName Name of the server being connected to. If NULL, the + "Host" header line won't be sent + @param aURLPath Url to request + @param aUserAgent User-Agent string to send. If NULL the default + user-agent kUserAgent will be sent + @param aAcceptList List of MIME types that the client will accept. If + NULL the "Accept" header line won't be sent + @return 0 if successful, else error + */ + int post(const IPAddress& aServerAddress, + uint16_t aServerPort, + const char* aServerName, + const char* aURLPath, + const char* aUserAgent =NULL, + const char* aAcceptList =NULL) + { return startRequest(aServerAddress, aServerPort, aServerName, aURLPath, kPost, aUserAgent, aAcceptList); } + + /** Connect to the server and start to send a PUT request. + @param aServerName Name of the server being connected to. If NULL, the + "Host" header line won't be sent + @param aServerPort Port to connect to on the server + @param aURLPath Url to request + @param aUserAgent User-Agent string to send. If NULL the default + user-agent kUserAgent will be sent + @param aAcceptList List of MIME types that the client will accept. If + NULL the "Accept" header line won't be sent + @return 0 if successful, else error + */ + int put(const char* aServerName, + uint16_t aServerPort, + const char* aURLPath, + const char* aUserAgent =NULL, + const char* aAcceptList =NULL) + { return startRequest(aServerName, aServerPort, aURLPath, kPut, aUserAgent, aAcceptList); } + + /** Connect to the server and start to send a PUT request. This version connects + doesn't perform a DNS lookup and just connects to the given IP address. + @param aServerAddress IP address of the server to connect to + @param aServerPort Port to connect to on the server + @param aServerName Name of the server being connected to. If NULL, the + "Host" header line won't be sent + @param aURLPath Url to request + @param aUserAgent User-Agent string to send. If NULL the default + user-agent kUserAgent will be sent + @param aAcceptList List of MIME types that the client will accept. If + NULL the "Accept" header line won't be sent + @return 0 if successful, else error + */ + int put(const IPAddress& aServerAddress, + uint16_t aServerPort, + const char* aServerName, + const char* aURLPath, + const char* aUserAgent =NULL, + const char* aAcceptList =NULL) + { return startRequest(aServerAddress, aServerPort, aServerName, aURLPath, kPut, aUserAgent, aAcceptList); } + + /** Connect to the server and start to send the request. + @param aServerName Name of the server being connected to. + @param aServerPort Port to connect to on the server + @param aURLPath Url to request + @param aHttpMethod Type of HTTP request to make, e.g. "GET", "POST", etc. + @param aUserAgent User-Agent string to send. If NULL the default + user-agent kUserAgent will be sent + @param aAcceptList List of MIME types that the client will accept. If + NULL the "Accept" header line won't be sent + @return 0 if successful, else error + */ + int startRequest(const char* aServerName, + uint16_t aServerPort, + const char* aURLPath, + const char* aHttpMethod, + const char* aUserAgent, + const char* aAcceptList); + + /** Connect to the server and start to send the request. + @param aServerAddress IP address of the server to connect to. + @param aServerPort Port to connect to on the server + @param aServerName Name of the server being connected to. If NULL, the + "Host" header line won't be sent + @param aURLPath Url to request + @param aHttpMethod Type of HTTP request to make, e.g. "GET", "POST", etc. + @param aUserAgent User-Agent string to send. If NULL the default + user-agent kUserAgent will be sent + @param aAcceptList List of MIME types that the client will accept. If + NULL the "Accept" header line won't be sent + @return 0 if successful, else error + */ + int startRequest(const IPAddress& aServerAddress, + uint16_t aServerPort, + const char* aServerName, + const char* aURLPath, + const char* aHttpMethod, + const char* aUserAgent, + const char* aAcceptList); + + /** Send an additional header line. This can only be called in between the + calls to startRequest and finishRequest. + @param aHeader Header line to send, in its entirety (but without the + trailing CRLF. E.g. "Authorization: Basic YQDDCAIGES" + */ + void sendHeader(const char* aHeader); + + /** Send an additional header line. This is an alternate form of + sendHeader() which takes the header name and content as separate strings. + The call will add the ": " to separate the header, so for example, to + send a XXXXXX header call sendHeader("XXXXX", "Something") + @param aHeaderName Type of header being sent + @param aHeaderValue Value for that header + */ + void sendHeader(const char* aHeaderName, const char* aHeaderValue); + + /** Send an additional header line. This is an alternate form of + sendHeader() which takes the header name and content separately but where + the value is provided as an integer. + The call will add the ": " to separate the header, so for example, to + send a XXXXXX header call sendHeader("XXXXX", 123) + @param aHeaderName Type of header being sent + @param aHeaderValue Value for that header + */ + void sendHeader(const char* aHeaderName, const int aHeaderValue); + + /** Send a basic authentication header. This will encode the given username + and password, and send them in suitable header line for doing Basic + Authentication. + @param aUser Username for the authorization + @param aPassword Password for the user aUser + */ + void sendBasicAuth(const char* aUser, const char* aPassword); + + /** Finish sending the HTTP request. This basically just sends the blank + line to signify the end of the request + */ + void finishRequest(); + + /** Get the HTTP status code contained in the response. + For example, 200 for successful request, 404 for file not found, etc. + */ + int responseStatusCode(); + + /** Read the next character of the response headers. + This functions in the same way as read() but to be used when reading + through the headers. Check whether or not the end of the headers has + been reached by calling endOfHeadersReached(), although after that point + this will still return data as read() would, but slightly less efficiently + @return The next character of the response headers + */ + int readHeader(); + + /** Skip any response headers to get to the body. + Use this if you don't want to do any special processing of the headers + returned in the response. You can also use it after you've found all of + the headers you're interested in, and just want to get on with processing + the body. + @return HttpSuccess if successful, else an error code + */ + int skipResponseHeaders(); + + /** Test whether all of the response headers have been consumed. + @return true if we are now processing the response body, else false + */ + bool endOfHeadersReached() { return (iState == eReadingBody); }; + + /** Test whether the end of the body has been reached. + Only works if the Content-Length header was returned by the server + @return true if we are now at the end of the body, else false + */ + bool endOfBodyReached(); + virtual bool eof() { return endOfBodyReached(); }; + + /** Return the length of the body. + @return Length of the body, in bytes, or kNoContentLengthHeader if no + Content-Length header was returned by the server + */ + int contentLength() { return iContentLength; }; + + // Inherited from Print + virtual void write(uint8_t aByte) { iClient-> write(aByte); }; + virtual void write(const char *aStr) { iClient->write(aStr); }; + virtual void write(const uint8_t *aBuffer, size_t aSize) { iClient->write(aBuffer, aSize); }; + // Inherited from Stream + virtual int available() { return iClient->available(); }; + /** Read the next byte from the server. + @return Byte read or -1 if there are no bytes available. + */ + virtual int read(); + virtual int peek() { return iClient->peek(); }; + virtual void flush() { return iClient->flush(); }; + + // Inherited from Client + virtual int connect(IPAddress ip, uint16_t port) { return iClient->connect(ip, port); }; + virtual int connect(const char *host, uint16_t port) { return iClient->connect(host, port); }; + virtual void stop(); + virtual uint8_t connected() { iClient->connected(); }; +protected: + /** Reset internal state data back to the "just initialised" state + */ + void resetState(); + /** Send the first part of the request and the initial headers. + @param aServerName Name of the server being connected to. If NULL, the + "Host" header line won't be sent + @param aURLPath Url to request + @param aHttpMethod Type of HTTP request to make, e.g. "GET", "POST", etc. + @param aUserAgent User-Agent string to send. If NULL the default + user-agent kUserAgent will be sent + @param aAcceptList List of MIME types that the client will accept. If + NULL the "Accept" header line won't be sent + @return 0 if successful, else error + */ + int sendInitialHeaders(const char* aServerName, + const char* aURLPath, + const char* aHttpMethod, + const char* aUserAgent, + const char* aAcceptList); + + // Number of milliseconds that we wait each time there isn't any data + // available to be read (during status code and header processing) + static const int kHttpWaitForDataDelay = 1000; + // Number of milliseconds that we'll wait in total without receiveing any + // data before returning HttpErrTimedOut (during status code and header + // processing) + static const int kHttpResponseTimeout = 30*1000; + static const char* kContentLengthPrefix; + typedef enum { + eIdle, + eRequestStarted, + eRequestSent, + eReadingStatusCode, + eStatusCodeRead, + eReadingContentLength, + eSkipToEndOfHeader, + eLineStartingCRFound, + eReadingBody + } tHttpState; + // Ethernet client we're using + Client* iClient; + // Current state of the finite-state-machine + tHttpState iState; + // Stores the status code for the response, once known + int iStatusCode; + // Stores the value of the Content-Length header, if present + int iContentLength; + // How many bytes of the response body have been read by the user + int iBodyLengthConsumed; + // How far through a Content-Length header prefix we are + const char* iContentLengthPtr; +}; + +#endif diff --git a/examples/SimpleHttpExample/SimpleHttpExample.pde b/examples/SimpleHttpExample/SimpleHttpExample.pde new file mode 100644 index 0000000..12c56a8 --- /dev/null +++ b/examples/SimpleHttpExample/SimpleHttpExample.pde @@ -0,0 +1,124 @@ +// (c) Copyright 2010-2011 MCQN Ltd. +// Released under Apache License, version 2.0 +// +// Simple example to show how to use the HttpClient library +// Get's the web page given at http:// and +// outputs the content to the serial port + +#include +#include +#include +#include +#include + +// This example downloads the URL "http://arduino.cc/" + +// Name of the server we want to connect to +char* kHostname = "arduino.cc"; +// Path to download (this is the bit after the hostname in the URL +// that you want to download +const char* kPath = "/"; + +byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; + +// Number of milliseconds to wait without receiving any data before we give up +const int kNetworkTimeout = 30*1000; +// Number of milliseconds to wait if no data is available before trying again +const int kNetworkDelay = 1000; + +void setup() +{ + // initialize serial communications at 9600 bps: + Serial.begin(9600); + + while (Ethernet.begin(mac) != 1) + { +#ifdef LOGGING + Serial.println("Error getting IP address via DHCP, trying again..."); +#endif + delay(15000); + } +} + +void loop() +{ + int err =0; + + Client c; + HttpClient http(c); + + err = http.get(kHostname, 80, kPath); + if (err == 0) + { + Serial.println("startedRequest ok"); + + http.finishRequest(); + + err = http.responseStatusCode(); + if (err >= 0) + { + Serial.print("Got status code: "); + Serial.println(err); + + // Usually you'd check that the response code is 200 or a + // similar "success" code (200-299) before carrying on, + // but we'll print out whatever response we get + + err = http.skipResponseHeaders(); + if (err >= 0) + { + int bodyLen = http.contentLength(); + Serial.print("Content length is: "); + Serial.println(bodyLen); + Serial.println(); + Serial.println("Body returned follows:"); + + // Now we've got to the body, so we can print it out + unsigned long timeoutStart = millis(); + char c; + // Whilst we haven't timed out & haven't reached the end of the body + while (http.connected() && + ( (millis() - timeoutStart) < kNetworkTimeout )) + { + if (http.available()) + { + c = http.read(); + // Print out this character + Serial.print(c); + + bodyLen--; + // We read something, reset the timeout counter + timeoutStart = millis(); + } + else + { + // We haven't got any data, so let's pause to allow some to + // arrive + delay(kNetworkDelay); + } + } + } + else + { + Serial.print("Failed to skip response headers: "); + Serial.println(err); + } + } + else + { + Serial.print("Getting response failed: "); + Serial.println(err); + } + } + else + { + Serial.print("Connect failed: "); + Serial.println(err); + } + http.stop(); + + // And just stop, now that we've tried a download + while(1); +} + + diff --git a/keywords.txt b/keywords.txt new file mode 100644 index 0000000..780e988 --- /dev/null +++ b/keywords.txt @@ -0,0 +1,33 @@ +####################################### +# Syntax Coloring Map For HttpClient +####################################### + +####################################### +# Datatypes (KEYWORD1) +####################################### + +HttpClient KEYWORD1 + +####################################### +# Methods and Functions (KEYWORD2) +####################################### + +get KEYWORD2 +post KEYWORD2 +put KEYWORD2 +startRequest KEYWORD2 +sendHeader KEYWORD2 +sendBasicAuth KEYWORD2 +finishRequest KEYWORD2 +responseStatusCode KEYWORD2 +readHeader KEYWORD2 +skipResponseHeaders KEYWORD2 +endOfHeadersReached KEYWORD2 +endOfBodyReached KEYWORD2 +eof KEYWORD2 +contentLength KEYWORD2 + +####################################### +# Constants (LITERAL1) +####################################### +