diff --git a/HttpClient.cpp b/HttpClient.cpp index 9a42612..6bf8121 100644 --- a/HttpClient.cpp +++ b/HttpClient.cpp @@ -3,7 +3,7 @@ // Released under Apache License, version 2.0 #include "HttpClient.h" -#include <../b64/b64.h> +#include "b64.h" #include #include #include @@ -46,11 +46,17 @@ void HttpClient::stop() resetState(); } -int HttpClient::startRequest(const char* aServerName, uint16_t aServerPort, const char* aURLPath, const char* aHttpMethod, const char* aUserAgent, const char* aAcceptList) +void HttpClient::beginRequest() { - if (eIdle != iState) + iState = eRequestStarted; +} + +int HttpClient::startRequest(const char* aServerName, uint16_t aServerPort, const char* aURLPath, const char* aHttpMethod, const char* aUserAgent) +{ + tHttpState initialState = iState; + if ((eIdle != iState) && (eRequestStarted != iState)) { - return HttpErrAPI; + return HTTP_ERROR_API; } if (iProxyPort) @@ -60,7 +66,7 @@ int HttpClient::startRequest(const char* aServerName, uint16_t aServerPort, cons #ifdef LOGGING Serial.println("Proxy connection failed"); #endif - return HttpErrConnectionFailed; + return HTTP_ERROR_CONNECTION_FAILED; } } else @@ -70,19 +76,28 @@ int HttpClient::startRequest(const char* aServerName, uint16_t aServerPort, cons #ifdef LOGGING Serial.println("Connection failed"); #endif - return HttpErrConnectionFailed; + return HTTP_ERROR_CONNECTION_FAILED; } } // Now we're connected, send the first part of the request - return sendInitialHeaders(aServerName, IPAddress(0,0,0,0), aServerPort, aURLPath, aHttpMethod, aUserAgent, aAcceptList); + int ret = sendInitialHeaders(aServerName, IPAddress(0,0,0,0), aServerPort, aURLPath, aHttpMethod, aUserAgent); + if ((initialState == eIdle) && (HTTP_SUCCESS == ret)) + { + // This was a simple version of the API, so terminate the headers now + finishHeaders(); + } + // else we'll call it in endRequest or in the first call to print, etc. + + return ret; } -int HttpClient::startRequest(const IPAddress& aServerAddress, uint16_t aServerPort, const char* aServerName, const char* aURLPath, const char* aHttpMethod, const char* aUserAgent, const char* aAcceptList) +int HttpClient::startRequest(const IPAddress& aServerAddress, const char* aServerName, uint16_t aServerPort, const char* aURLPath, const char* aHttpMethod, const char* aUserAgent) { - if (eIdle != iState) + tHttpState initialState = iState; + if ((eIdle != iState) && (eRequestStarted != iState)) { - return HttpErrAPI; + return HTTP_ERROR_API; } if (iProxyPort) @@ -92,7 +107,7 @@ int HttpClient::startRequest(const IPAddress& aServerAddress, uint16_t aServerPo #ifdef LOGGING Serial.println("Proxy connection failed"); #endif - return HttpErrConnectionFailed; + return HTTP_ERROR_CONNECTION_FAILED; } } else @@ -102,95 +117,96 @@ int HttpClient::startRequest(const IPAddress& aServerAddress, uint16_t aServerPo #ifdef LOGGING Serial.println("Connection failed"); #endif - return HttpErrConnectionFailed; + return HTTP_ERROR_CONNECTION_FAILED; } } // Now we're connected, send the first part of the request - return sendInitialHeaders(aServerName, aServerAddress, aServerPort, aURLPath, aHttpMethod, aUserAgent, aAcceptList); + int ret = sendInitialHeaders(aServerName, aServerAddress, aServerPort, aURLPath, aHttpMethod, aUserAgent); + if ((initialState == eIdle) && (HTTP_SUCCESS == ret)) + { + // This was a simple version of the API, so terminate the headers now + finishHeaders(); + } + // else we'll call it in endRequest or in the first call to print, etc. + + return ret; } -int HttpClient::sendInitialHeaders(const char* aServerName, IPAddress aServerIP, uint16_t aPort, const char* aURLPath, const char* aHttpMethod, const char* aUserAgent, const char* aAcceptList) +int HttpClient::sendInitialHeaders(const char* aServerName, IPAddress aServerIP, uint16_t aPort, const char* aURLPath, const char* aHttpMethod, const char* aUserAgent) { #ifdef LOGGING Serial.println("Connected"); #endif // Send the HTTP command, i.e. "GET /somepath/ HTTP/1.0" - print(aHttpMethod); - print(" "); + iClient->print(aHttpMethod); + iClient->print(" "); if (iProxyPort) { // We're going through a proxy, send a full URL - print("http://"); + iClient->print("http://"); if (aServerName) { // We've got a server name, so use it - print(aServerName); + iClient->print(aServerName); } else { // We'll have to use the IP address - print(aServerIP); + iClient->print(aServerIP); } if (aPort != kHttpPort) { - print(":"); - print(aPort); + iClient->print(":"); + iClient->print(aPort); } } - print(aURLPath); - println(" HTTP/1.0"); + iClient->print(aURLPath); + iClient->println(" HTTP/1.0"); // The host header, if required if (aServerName) { -// print("Host: "); -// println(aServerName); + sendHeader("Host", aServerName); } // And user-agent string - print("User-Agent: "); + iClient->print("User-Agent: "); if (aUserAgent) { - println(aUserAgent); + iClient->println(aUserAgent); } else { - println(kUserAgent); - } - if (aAcceptList) - { - // We've got an accept list to send - print("Accept: "); - println(aAcceptList); + iClient->println(kUserAgent); } // Everything has gone well iState = eRequestStarted; - return HttpSuccess; + return HTTP_SUCCESS; } void HttpClient::sendHeader(const char* aHeader) { - println(aHeader); + iClient->println(aHeader); } void HttpClient::sendHeader(const char* aHeaderName, const char* aHeaderValue) { - print(aHeaderName); - print(": "); - println(aHeaderValue); + iClient->print(aHeaderName); + iClient->print(": "); + iClient->println(aHeaderValue); } void HttpClient::sendHeader(const char* aHeaderName, const int aHeaderValue) { - print(aHeaderName); - print(": "); - println(aHeaderValue); + iClient->print(aHeaderName); + iClient->print(": "); + iClient->println(aHeaderValue); } void HttpClient::sendBasicAuth(const char* aUser, const char* aPassword) { // Send the initial part of this header line - print("Authorization: Basic "); + iClient->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 @@ -226,27 +242,37 @@ void HttpClient::sendBasicAuth(const char* aUser, const char* aPassword) // NUL-terminate the output string output[4] = '\0'; // And write it out - print((char*)output); + iClient->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(); + iClient->println(); } -void HttpClient::finishRequest() +void HttpClient::finishHeaders() { - println(); + iClient->println(); iState = eRequestSent; } +void HttpClient::endRequest() +{ + if (iState < eRequestSent) + { + // We still need to finish off the headers + finishHeaders(); + } + // else the end of headers has already been sent, so nothing to do here +} + int HttpClient::responseStatusCode() { if (iState < eRequestSent) { - return HttpErrAPI; + return HTTP_ERROR_API; } // The first line will be of the form Status-Line: // HTTP-Version SP Status-Code SP Reason-Phrase CRLF @@ -289,7 +315,7 @@ int HttpClient::responseStatusCode() } else { - return HttpErrInvalidResponse; + return HTTP_ERROR_INVALID_RESPONSE; } break; case eReadingStatusCode: @@ -339,13 +365,13 @@ int HttpClient::responseStatusCode() else if (c != '\n') { // We must've timed out before we reached the end of the line - return HttpErrTimedOut; + return HTTP_ERROR_TIMED_OUT; } else { // This wasn't a properly formed status line, or at least not one we // could understand - return HttpErrInvalidResponse; + return HTTP_ERROR_INVALID_RESPONSE; } } @@ -373,12 +399,12 @@ int HttpClient::skipResponseHeaders() if (endOfHeadersReached()) { // Success - return HttpSuccess; + return HTTP_SUCCESS; } else { // We must've timed out - return HttpErrTimedOut; + return HTTP_ERROR_TIMED_OUT; } } diff --git a/HttpClient.h b/HttpClient.h index e703e63..78cbefe 100644 --- a/HttpClient.h +++ b/HttpClient.h @@ -1,5 +1,5 @@ // Class to simplify HTTP fetching on Arduino -// (c) Copyright MCQN Ltd. 2010-2011 +// (c) Copyright MCQN Ltd. 2010-2012 // Released under Apache License, version 2.0 #ifndef HttpClient_h @@ -9,28 +9,24 @@ #include #include "Ethernet.h" #include "Client.h" -#include + +static const int HTTP_SUCCESS =0; +// The end of the headers has been reached. This consumes the '\n' +// Could not connect to the server +static const int HTTP_ERROR_CONNECTION_FAILED =-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 +static const int HTTP_ERROR_API =-2; +// Spent too long waiting for a reply +static const int HTTP_ERROR_TIMED_OUT =-3; +// The response from the server is invalid, is it definitely an HTTP +// server? +static const int HTTP_ERROR_INVALID_RESPONSE =-4; 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 int kHttpPort =80; static const char* kUserAgent; @@ -39,8 +35,23 @@ public: static const char* kPut; static const char* kDelete; +// FIXME Write longer API request, using port and user-agent, example +// FIXME Update tempToPachube example to calculate Content-Length correctly + HttpClient(Client& aClient, const char* aProxy =NULL, uint16_t aProxyPort =0); + /** Start a more complex request. + Use this when you need to send additional headers in the request, + but you will also need to call endRequest() when you are finished. + */ + void beginRequest(); + + /** End a more complex request. + Use this when you need to have sent additional headers in the request, + but you will also need to call beginRequest() at the start. + */ + void endRequest(); + /** 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 @@ -48,35 +59,56 @@ public: @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); } + int get(const char* aServerName, uint16_t aServerPort, const char* aURLPath, + const char* aUserAgent =NULL) + { return startRequest(aServerName, aServerPort, aURLPath, kGet, aUserAgent); } + + /** 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 aURLPath Url to request + @param aUserAgent User-Agent string to send. If NULL the default + user-agent kUserAgent will be sent + @return 0 if successful, else error + */ + int get(const char* aServerName, const char* aURLPath, const char* aUserAgent =NULL) + { return startRequest(aServerName, kHttpPort, aURLPath, kGet, aUserAgent); } + + /** 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 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 + @return 0 if successful, else error + */ + int get(const IPAddress& aServerAddress, + const char* aServerName, + uint16_t aServerPort, + const char* aURLPath, + const char* aUserAgent =NULL) + { return startRequest(aServerAddress, aServerName, aServerPort, aURLPath, kGet, aUserAgent); } /** 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); } + const char* aUserAgent =NULL) + { return startRequest(aServerAddress, aServerName, kHttpPort, aURLPath, kGet, aUserAgent); } /** Connect to the server and start to send a POST request. @param aServerName Name of the server being connected to. If NULL, the @@ -85,37 +117,60 @@ public: @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); } + const char* aUserAgent =NULL) + { return startRequest(aServerName, aServerPort, aURLPath, kPost, aUserAgent); } - /** 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 + /** 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 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, + const char* aURLPath, + const char* aUserAgent =NULL) + { return startRequest(aServerName, kHttpPort, aURLPath, kPost, aUserAgent); } + + /** 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 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 @return 0 if successful, else error */ int post(const IPAddress& aServerAddress, + const char* aServerName, uint16_t aServerPort, + const char* aURLPath, + const char* aUserAgent =NULL) + { return startRequest(aServerAddress, aServerName, aServerPort, aURLPath, kPost, aUserAgent); } + + /** 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 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 + @return 0 if successful, else error + */ + int post(const IPAddress& aServerAddress, const char* aServerName, const char* aURLPath, - const char* aUserAgent =NULL, - const char* aAcceptList =NULL) - { return startRequest(aServerAddress, aServerPort, aServerName, aURLPath, kPost, aUserAgent, aAcceptList); } + const char* aUserAgent =NULL) + { return startRequest(aServerAddress, aServerName, kHttpPort, aURLPath, kPost, aUserAgent); } /** Connect to the server and start to send a PUT request. @param aServerName Name of the server being connected to. If NULL, the @@ -124,37 +179,60 @@ public: @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); } + const char* aUserAgent =NULL) + { return startRequest(aServerName, aServerPort, aURLPath, kPut, aUserAgent); } - /** 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 + /** 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 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, + const char* aURLPath, + const char* aUserAgent =NULL) + { return startRequest(aServerName, kHttpPort, aURLPath, kPut, aUserAgent); } + + /** 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 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 @return 0 if successful, else error */ int put(const IPAddress& aServerAddress, + const char* aServerName, uint16_t aServerPort, + const char* aURLPath, + const char* aUserAgent =NULL) + { return startRequest(aServerAddress, aServerName, aServerPort, aURLPath, kPut, aUserAgent); } + + /** 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 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 + @return 0 if successful, else error + */ + int put(const IPAddress& aServerAddress, const char* aServerName, const char* aURLPath, - const char* aUserAgent =NULL, - const char* aAcceptList =NULL) - { return startRequest(aServerAddress, aServerPort, aServerName, aURLPath, kPut, aUserAgent, aAcceptList); } + const char* aUserAgent =NULL) + { return startRequest(aServerAddress, aServerName, kHttpPort, aURLPath, kPut, aUserAgent); } /** Connect to the server and start to send the request. @param aServerName Name of the server being connected to. @@ -163,37 +241,31 @@ public: @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, + uint16_t aServerPort, const char* aURLPath, const char* aHttpMethod, - const char* aUserAgent, - const char* aAcceptList); + const char* aUserAgent); /** 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 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 IPAddress& aServerAddress, - uint16_t aServerPort, const char* aServerName, + uint16_t aServerPort, const char* aURLPath, const char* aHttpMethod, - const char* aUserAgent, - const char* aAcceptList); + const char* aUserAgent); /** Send an additional header line. This can only be called in between the calls to startRequest and finishRequest. @@ -253,7 +325,7 @@ public: 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 + @return HTTP_SUCCESS if successful, else an error code */ int skipResponseHeaders(); @@ -267,7 +339,8 @@ public: @return true if we are now at the end of the body, else false */ bool endOfBodyReached(); - virtual bool eof() { return endOfBodyReached(); }; + virtual bool endOfStream() { return endOfBodyReached(); }; + virtual bool completed() { return endOfBodyReached(); }; /** Return the length of the body. @return Length of the body, in bytes, or kNoContentLengthHeader if no @@ -276,8 +349,10 @@ public: int contentLength() { return iContentLength; }; // Inherited from Print - virtual size_t write(uint8_t aByte) { return iClient-> write(aByte); }; - virtual size_t write(const uint8_t *aBuffer, size_t aSize) { return iClient->write(aBuffer, aSize); }; + // Note: 1st call to these indicates the user is sending the body, so if need + // Note: be we should finish the header first + virtual size_t write(uint8_t aByte) { if (iState < eRequestSent) { finishHeaders(); }; return iClient-> write(aByte); }; + virtual size_t write(const uint8_t *aBuffer, size_t aSize) { if (iState < eRequestSent) { finishHeaders(); }; return iClient->write(aBuffer, aSize); }; // Inherited from Stream virtual int available() { return iClient->available(); }; /** Read the next byte from the server. @@ -298,6 +373,7 @@ 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 @@ -308,8 +384,6 @@ protected: @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, @@ -317,14 +391,17 @@ protected: uint16_t aPort, const char* aURLPath, const char* aHttpMethod, - const char* aUserAgent, - const char* aAcceptList); + const char* aUserAgent); + + /* Let the server know that we've reached the end of the headers + */ + void finishHeaders(); // 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 + // data before returning HTTP_ERROR_TIMED_OUT (during status code and header // processing) static const int kHttpResponseTimeout = 30*1000; static const char* kContentLengthPrefix; diff --git a/README b/README index c7124dc..d223d80 100644 --- a/README +++ b/README @@ -2,7 +2,6 @@ HttpClient is a library to make it easier to interact with web servers from Ardu Dependencies: - Requires the new Ethernet library API (with DHCP and DNS) which is in Arduino 1.0. -- Requires the b64 base64 encoding library, available from https://github.com/amcewen/b64 In normal usage, handles the outgoing request and Host header. The returned status code is parsed for you, as is the Content-Length header (if present). diff --git a/b64.cpp b/b64.cpp new file mode 100644 index 0000000..b926cad --- /dev/null +++ b/b64.cpp @@ -0,0 +1,70 @@ +// Simple Base64 code +// (c) Copyright 2010 MCQN Ltd. +// Released under Apache License, version 2.0 + +#include "b64.h" + +/* Simple test program +#include +void main() +{ + char* in = "amcewen"; + char out[22]; + + b64_encode(in, 15, out, 22); + out[21] = '\0'; + + printf(out); +} +*/ + +int b64_encode(const unsigned char* aInput, int aInputLen, unsigned char* aOutput, int aOutputLen) +{ + // Work out if we've got enough space to encode the input + // Every 6 bits of input becomes a byte of output + if (aOutputLen < (aInputLen*8)/6) + { + // FIXME Should we return an error here, or just the length + return (aInputLen*8)/6; + } + + // If we get here we've got enough space to do the encoding + + const char* b64_dictionary = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + if (aInputLen == 3) + { + aOutput[0] = b64_dictionary[aInput[0] >> 2]; + aOutput[1] = b64_dictionary[(aInput[0] & 0x3)<<4|(aInput[1]>>4)]; + aOutput[2] = b64_dictionary[(aInput[1]&0x0F)<<2|(aInput[2]>>6)]; + aOutput[3] = b64_dictionary[aInput[2]&0x3F]; + } + else if (aInputLen == 2) + { + aOutput[0] = b64_dictionary[aInput[0] >> 2]; + aOutput[1] = b64_dictionary[(aInput[0] & 0x3)<<4|(aInput[1]>>4)]; + aOutput[2] = b64_dictionary[(aInput[1]&0x0F)<<2]; + aOutput[3] = '='; + } + else if (aInputLen == 1) + { + aOutput[0] = b64_dictionary[aInput[0] >> 2]; + aOutput[1] = b64_dictionary[(aInput[0] & 0x3)<<4]; + aOutput[2] = '='; + aOutput[3] = '='; + } + else + { + // Break the input into 3-byte chunks and process each of them + int i; + for (i = 0; i < aInputLen/3; i++) + { + b64_encode(&aInput[i*3], 3, &aOutput[i*4], 4); + } + if (aInputLen % 3 > 0) + { + // It doesn't fit neatly into a 3-byte chunk, so process what's left + b64_encode(&aInput[i*3], aInputLen % 3, &aOutput[i*4], aOutputLen - (i*4)); + } + } +} + diff --git a/b64.h b/b64.h new file mode 100644 index 0000000..cdb1226 --- /dev/null +++ b/b64.h @@ -0,0 +1,6 @@ +#ifndef b64_h +#define b64_h + +int b64_encode(const unsigned char* aInput, int aInputLen, unsigned char* aOutput, int aOutputLen); + +#endif diff --git a/examples/SimpleHttpExample/SimpleHttpExample.ino b/examples/SimpleHttpExample/SimpleHttpExample.ino index 801d309..f12f987 100644 --- a/examples/SimpleHttpExample/SimpleHttpExample.ino +++ b/examples/SimpleHttpExample/SimpleHttpExample.ino @@ -1,4 +1,4 @@ -// (c) Copyright 2010-2011 MCQN Ltd. +// (c) Copyright 2010-2012 MCQN Ltd. // Released under Apache License, version 2.0 // // Simple example to show how to use the HttpClient library @@ -7,17 +7,16 @@ #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"; +const 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 = "/"; +const char kPath[] = "/"; byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; @@ -33,9 +32,7 @@ void setup() while (Ethernet.begin(mac) != 1) { -#ifdef LOGGING Serial.println("Error getting IP address via DHCP, trying again..."); -#endif delay(15000); } } @@ -47,13 +44,11 @@ void loop() EthernetClient c; HttpClient http(c); - err = http.get(kHostname, 80, kPath); + err = http.get(kHostname, kPath); if (err == 0) { Serial.println("startedRequest ok"); - http.finishRequest(); - err = http.responseStatusCode(); if (err >= 0) { diff --git a/keywords.txt b/keywords.txt index 780e988..9f1ae84 100644 --- a/keywords.txt +++ b/keywords.txt @@ -16,9 +16,10 @@ get KEYWORD2 post KEYWORD2 put KEYWORD2 startRequest KEYWORD2 +beginRequest KEYWORD2 sendHeader KEYWORD2 sendBasicAuth KEYWORD2 -finishRequest KEYWORD2 +endRequest KEYWORD2 responseStatusCode KEYWORD2 readHeader KEYWORD2 skipResponseHeaders KEYWORD2 @@ -30,4 +31,9 @@ contentLength KEYWORD2 ####################################### # Constants (LITERAL1) ####################################### +HTTP_SUCCESS LITERAL1 +HTTP_ERROR_CONNECTION_FAILED LITERAL1 +HTTP_ERROR_API LITERAL1 +HTTP_ERROR_TIMED_OUT LITERAL1 +HTTP_ERROR_INVALID_RESPONSE LITERAL1