diff --git a/HttpClient.cpp b/HttpClient.cpp index 956f99f..5d9b0ad 100644 --- a/HttpClient.cpp +++ b/HttpClient.cpp @@ -4,9 +4,9 @@ #include "HttpClient.h" #include <../b64/b64.h> +#include #include #include -#include "wiring.h" // Initialize constants const char* HttpClient::kUserAgent = "Arduino/2.0"; @@ -16,10 +16,19 @@ const char* HttpClient::kPut = "PUT"; const char* HttpClient::kDelete = "DELETE"; const char* HttpClient::kContentLengthPrefix = "Content-Length: "; -HttpClient::HttpClient(Client& aClient) - : iClient(&aClient) +HttpClient::HttpClient(Client& aClient, const char* aProxy, uint16_t aProxyPort) + : iClient(&aClient), iProxyPort(aProxyPort) { resetState(); + if (aProxy) + { + // Resolve the IP address for the proxy + DNSClient dns; + dns.begin(Ethernet.dnsServerIP()); + // Not ideal that we discard any errors here, but not a lot we can do in the ctor + // and we'll get a connect error later anyway + (void)dns.getHostByName(aProxy, iProxyAddress); + } } void HttpClient::resetState() @@ -44,16 +53,29 @@ int HttpClient::startRequest(const char* aServerName, uint16_t aServerPort, cons return HttpErrAPI; } - if (!iClient->connect(aServerName, aServerPort)) + if (iProxyPort) { + if (!iClient->connect(iProxyAddress, iProxyPort)) + { #ifdef LOGGING - Serial.println("Connection failed"); + Serial.println("Proxy connection failed"); #endif - return HttpErrConnectionFailed; + return HttpErrConnectionFailed; + } + } + else + { + 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); + return sendInitialHeaders(aServerName, IPAddress(0,0,0,0), aServerPort, 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) @@ -63,19 +85,32 @@ int HttpClient::startRequest(const IPAddress& aServerAddress, uint16_t aServerPo return HttpErrAPI; } - if (!iClient->connect(aServerAddress, aServerPort)) + if (iProxyPort) { + if (!iClient->connect(iProxyAddress, iProxyPort)) + { #ifdef LOGGING - Serial.println("Connection failed"); + Serial.println("Proxy connection failed"); #endif - return HttpErrConnectionFailed; + return HttpErrConnectionFailed; + } + } + else + { + 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); + return sendInitialHeaders(aServerName, aServerAddress, aServerPort, aURLPath, aHttpMethod, aUserAgent, aAcceptList); } -int HttpClient::sendInitialHeaders(const char* aServerName, 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, const char* aAcceptList) { #ifdef LOGGING Serial.println("Connected"); @@ -83,13 +118,33 @@ int HttpClient::sendInitialHeaders(const char* aServerName, const char* aURLPath // Send the HTTP command, i.e. "GET /somepath/ HTTP/1.0" print(aHttpMethod); print(" "); + if (iProxyPort) + { + // We're going through a proxy, send a full URL + print("http://"); + if (aServerName) + { + // We've got a server name, so use it + print(aServerName); + } + else + { + // We'll have to use the IP address + print(aServerIP); + } + if (aPort != kHttpPort) + { + print(":"); + print(aPort); + } + } print(aURLPath); println(" HTTP/1.0"); // The host header, if required if (aServerName) { - print("Host: "); - println(aServerName); +// print("Host: "); +// println(aServerName); } // And user-agent string print("User-Agent: "); @@ -339,14 +394,28 @@ bool HttpClient::endOfBodyReached() int HttpClient::read() { - int ret =iClient->read(); + uint8_t b[1]; + int ret = read(b, 1); + if (ret == 1) + { + return b[0]; + } + else + { + return -1; + } +} + +int HttpClient::read(uint8_t *buf, size_t size) +{ + int ret =iClient->read(buf, size); 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++; + iBodyLengthConsumed += ret; } } return ret; diff --git a/HttpClient.h b/HttpClient.h index d297c17..e86f54e 100644 --- a/HttpClient.h +++ b/HttpClient.h @@ -5,10 +5,11 @@ #ifndef HttpClient_h #define HttpClient_h +#include #include #include "Ethernet.h" #include "Client.h" -#include <../b64.h> +#include class HttpClient : public Client { @@ -31,13 +32,14 @@ public: }; static const int kNoContentLengthHeader =-1; + static const int kHttpPort =80; static const char* kUserAgent; static const char* kGet; static const char* kPost; static const char* kPut; static const char* kDelete; - HttpClient(Client& aClient); + HttpClient(Client& aClient, const char* aProxy =NULL, uint16_t aProxyPort =0); /** Connect to the server and start to send a GET request. @param aServerName Name of the server being connected to. If NULL, the @@ -274,15 +276,16 @@ public: 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); }; + virtual size_t write(uint8_t aByte) { return iClient-> write(aByte); }; + virtual size_t write(const char *aStr) { return iClient->write(aStr); }; + virtual size_t write(const uint8_t *aBuffer, size_t aSize) { return 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 read(uint8_t *buf, size_t size); virtual int peek() { return iClient->peek(); }; virtual void flush() { return iClient->flush(); }; @@ -291,6 +294,7 @@ public: virtual int connect(const char *host, uint16_t port) { return iClient->connect(host, port); }; virtual void stop(); virtual uint8_t connected() { iClient->connected(); }; + virtual operator bool() { return bool(iClient); }; protected: /** Reset internal state data back to the "just initialised" state */ @@ -298,6 +302,9 @@ protected: /** 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 aServerIP IP address of the server (only used if we're going through a + proxy and aServerName is NULL + @param aServerPort Port of the server being connected to. @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 @@ -307,6 +314,8 @@ protected: @return 0 if successful, else error */ int sendInitialHeaders(const char* aServerName, + IPAddress aServerIP, + uint16_t aPort, const char* aURLPath, const char* aHttpMethod, const char* aUserAgent, @@ -343,6 +352,9 @@ protected: int iBodyLengthConsumed; // How far through a Content-Length header prefix we are const char* iContentLengthPtr; + // Address of the proxy to use, if we're using one + IPAddress iProxyAddress; + uint16_t iProxyPort; }; #endif diff --git a/examples/SimpleHttpExample/SimpleHttpExample.pde b/examples/SimpleHttpExample/SimpleHttpExample.ino similarity index 94% rename from examples/SimpleHttpExample/SimpleHttpExample.pde rename to examples/SimpleHttpExample/SimpleHttpExample.ino index 12c56a8..801d309 100644 --- a/examples/SimpleHttpExample/SimpleHttpExample.pde +++ b/examples/SimpleHttpExample/SimpleHttpExample.ino @@ -9,7 +9,7 @@ #include #include #include -#include +#include // This example downloads the URL "http://arduino.cc/" @@ -44,7 +44,7 @@ void loop() { int err =0; - Client c; + EthernetClient c; HttpClient http(c); err = http.get(kHostname, 80, kPath); @@ -77,8 +77,8 @@ void loop() 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 )) + while ( (http.connected() || http.available()) && + ((millis() - timeoutStart) < kNetworkTimeout) ) { if (http.available()) {