diff --git a/HTTPClient.cpp b/HTTPClient.cpp index d21f4fd..2f5419c 100755 --- a/HTTPClient.cpp +++ b/HTTPClient.cpp @@ -26,7 +26,7 @@ #include #include #include -#include "HTTPClient.h" +#include //a struct to store the uriEncoder & the handle to the http client typedef struct @@ -41,18 +41,58 @@ typedef struct #define URI_ALLOWED(byte) ((byte>='A' && byte<='Z') || (byte>='a' && byte<='z') || (byte>='0' && byte<='9') || byte == '-' || byte == '_' || byte == '.' || byte == '~') #define URI_RESERVED(byte) (byte == '!' || byte == '*' || byte == '\'' || byte == '(' || byte == ')' || byte == ';' || byte == ':' || byte == '&' || byte == '=' || byte == '+' || byte == '$' || byte == ',' || byte == '/' || byte == '?' || byte == '#' || byte == '[' || byte == ']') +HTTPClient::HTTPClient(char* host) : + _theClient(new EthernetClient()), _selfInitializedTheClient(true), hostName(host), debugCommunication(0), _useIp(false), port(80) +{ + //nothing else to do +} + +HTTPClient::HTTPClient(char* host, uint16_t port) : + _theClient(new EthernetClient()), _selfInitializedTheClient(true), hostName(host), debugCommunication(0), _useIp(false), port(port) +{ + //nothing else to do +} + HTTPClient::HTTPClient(char* host, uint8_t* ip) : - EthernetClient(), hostName(host), debugCommunication(0), ip(ip), port(80) + _theClient(new EthernetClient()), _selfInitializedTheClient(true), hostName(host), debugCommunication(0), ip(ip), _useIp(true), port(80) { //nothing else to do } HTTPClient::HTTPClient(char* host, uint8_t* ip, uint16_t port) : - EthernetClient(), hostName(host), debugCommunication(0), ip(ip), port(port) + _theClient(new EthernetClient()), _selfInitializedTheClient(true), hostName(host), debugCommunication(0), ip(ip), _useIp(true), port(port) { //nothing else to do } +HTTPClient::HTTPClient(Client& client, char* host) : + _theClient(&client), hostName(host), debugCommunication(0), _useIp(false), port(80) +{ + //nothing else to do +} + +HTTPClient::HTTPClient(Client& client, char* host, uint16_t port) : + _theClient(&client), hostName(host), debugCommunication(0), _useIp(false), port(port) +{ + //nothing else to do +} + +HTTPClient::HTTPClient(Client& client, char* host, uint8_t* ip) : + _theClient(&client), hostName(host), debugCommunication(0), ip(ip), _useIp(true), port(80) +{ + //nothing else to do +} + +HTTPClient::HTTPClient(Client& client, char* host, uint8_t* ip, uint16_t port) : + _theClient(&client), hostName(host), debugCommunication(0), ip(ip), _useIp(true), port(port) +{ + //nothing else to do +} + +HTTPClient::~HTTPClient() { + if (_selfInitializedTheClient) delete _theClient; +} + FILE* HTTPClient::getURI(char* uri) { @@ -184,11 +224,12 @@ HTTPClient::openClientFile() fdev_set_udata(result,udata); udata->client = this; udata->encode = 0; - if (connected()) - { - stop(); - } - if (connect(ip,port)) + _theClient->stop(); + + int connectRes; + if (_useIp) connectRes=_theClient->connect(ip,port); + else connectRes=_theClient->connect(hostName,port); + if (connectRes) { return result; } @@ -291,14 +332,14 @@ HTTPClient::clientWrite(char byte, FILE* stream) } http_stream_udata* udata = (http_stream_udata*) fdev_get_udata(stream); HTTPClient* client = udata->client; - if (client->connected() == 0) + if (client->_theClient->connected() == 0) { closeStream(stream); return EOF; } if (udata->encode == 0) { - client->write(byte); + client->_theClient->write(byte); if (client->debugCommunication) { Serial.print(byte); @@ -309,7 +350,7 @@ HTTPClient::clientWrite(char byte, FILE* stream) if (URI_ALLOWED(byte) || ((URI_RESERVED(byte) && (udata->encode & URI_ENCODE_RESERVED) == 0))) { - client->write(byte); + client->_theClient->write(byte); if (client->debugCommunication) { Serial.print(byte); @@ -323,7 +364,7 @@ HTTPClient::clientWrite(char byte, FILE* stream) // Write only the first three bytes, not the trailing null for (char i = 0; i < 3; i++) { - client->write(encoded[i]); + client->_theClient->write(encoded[i]); if (client->debugCommunication) { Serial.print(encoded[i]); @@ -343,19 +384,19 @@ HTTPClient::clientRead(FILE* stream) } http_stream_udata* udata = (http_stream_udata*) fdev_get_udata(stream); HTTPClient* client = udata->client; - if (!client->connected()) + if (!client->_theClient->connected()) { return EOF; } //block until we got a byte - while (client->available() == 0) + while (client->_theClient->available() == 0) { - if (client->connected() == 0) + if (client->_theClient->connected() == 0) { return EOF; } }; - int result = client->read(); + int result = client->_theClient->read(); if (result == EOF) { return EOF; @@ -372,9 +413,9 @@ HTTPClient::clientRead(FILE* stream) else { //block until we got the needed bytes - while (client->available() >= 2) + while (client->_theClient->available() >= 2) { - if (client->connected() == 0) + if (client->_theClient->connected() == 0) { return EOF; } @@ -382,7 +423,7 @@ HTTPClient::clientRead(FILE* stream) char return_value = 0; for (char i = 0; i < 2; i++) { - result = client->read(); + result = client->_theClient->read(); if (result == EOF) { return EOF; @@ -417,7 +458,7 @@ HTTPClient::skipHeader(FILE* stream) int delayReadHeaderCount=0; // check if at least the first HTTP-response available, up to 1000ms=1s - while ((client->available()<17) && (delayReadHeaderCount++_theClient->available()<17) && (delayReadHeaderCount++client->stop(); + udata->client->_theClient->stop(); free(udata); fclose(stream); } diff --git a/HTTPClient.h b/HTTPClient.h index 31417b1..a977798 100755 --- a/HTTPClient.h +++ b/HTTPClient.h @@ -26,6 +26,7 @@ #define HTTPCLIENT_H_ #include +#include #include /* With this we try a delay of 20 ms before checking again if content is available @@ -50,18 +51,56 @@ typedef struct * To construct a HTTP client you have to provide the IP AND the name of the server Ð else * the virtual host management of most internet servers will fail. Sorry for the inconvenience. */ -class HTTPClient : private EthernetClient +class HTTPClient { public: /* - * create a HTTP client that connects to the default port 80. + * create a HTTP client that connects to the default port 80 using hostname (and dhcp lookup). + * An internal EthernetClient instance is used for the communication. + */ + HTTPClient(char* host); + /* + * create a HTTP client that connects to another port using hostname (and dhcp lookup). HTTPS is not supported. + * An internal EthernetClient instance is used for the communication. + */ + HTTPClient(char* host, uint16_t port); + /* + * create a HTTP Ethernet client that connects to the default port 80 using provided ip. hostname is used in HTTP message. + * An internal EthernetClient instance is used for the communication. */ HTTPClient(char* host, uint8_t* ip ); /* - * create a HTTP client that connects to another port. HTTPS is not supported. + * create a HTTP Ethernet client that connects to another port using provided ip. hostname is used in HTTP message. HTTPS is not supported. + * An internal EthernetClient instance is used for the communication. */ HTTPClient(char* host, uint8_t* ip, uint16_t port); + /* + * create a HTTP client that connects to the default port 80 using hostname (and dhcp lookup). + * The given instance of a Client (e.g. EthernetClient, WifiClient or such) is used for communication. + */ + HTTPClient(Client& client, char* host); + /* + * create a HTTP client that connects to another port using hostname (and dhcp lookup). HTTPS is not supported. + * The given instance of a Client (e.g. EthernetClient, WifiClient or such) is used for communication. + */ + HTTPClient(Client& client, char* host, uint16_t port); + /* + * create a HTTP client that connects to the default port 80 using provided ip. hostname is used in HTTP message.. + * The given instance of a Client (e.g. EthernetClient, WifiClient or such) is used for communication. + */ + HTTPClient(Client& client, char* host, uint8_t* ip ); + /* + * create a HTTP client that connects to another port using provided ip. hostname is used in HTTP message. HTTPS is not supported. + * The given instance of a Client (e.g. EthernetClient, WifiClient or such) is used for communication. + */ + HTTPClient(Client& client, char* host, uint8_t* ip, uint16_t port); + + /* + * the destructor + */ + ~HTTPClient(); + /* * Post a GET request to the server. * The result is a file handle or null is an error occured. @@ -167,12 +206,19 @@ class HTTPClient : private EthernetClient * And that is the internal stuff of the HTTP client */ private: + //client-instance to use + Client* _theClient; + //flag if Client was self-generated by compatibility constructors and needs to be removed + bool _selfInitializedTheClient=false; + //the name of the host we are talking to char* hostName; //the ip of the host uint8_t* ip; - //the port we are talking to - uint16_t port; + //use ip instead of hostname + bool _useIp; + //the port we are talking to + uint16_t port; //the HTTP return code of the last request int lastReturnCode; //print to serial? diff --git a/README.md b/README.md index 74d12bc..a944bde 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,29 @@ -# HTTPClient for Arduino +# HTTPClient for Arduino (Enhanced) + +## Not about this version: + +This is an enhanced version of the HTTPClient library originally from Interactive-Matter +(https://github.com/interactive-matter/HTTPClient). Additionally changes from Salanki's +fork (https://github.com/salanki/HTTPClient) were incorporated. + +The main differences are: +* The used communication client instance can be specified with new constructors. With + this it can be used with nearly any kind of Client-inherited class (e.g. EthernetClient, + WifiClient, GSMCLient and many more). The old constructor methods without providing a + Client instance are still available and an EthernetClient instance is created then. So + this version should be a drop-in replacement for the original "HTTPClient" +* Additionally some new constructors were introduced that allow the connection to be + done by just providing the hostname and DHCP is used then. To prevent DNS lookup and + such simply use the original constructor versions where IP is provided additionally + to Hostname - then the IP is used for the connection without the need of a lookup. +* My optimizations that are in Pull-request #15 on original library are incorporated as + well: + * make sure connection is correctly closed at end of communication so that reuse of + one Client instance is possible + * check success of parsing for last HTTP returncode and only set the variable on success + +### Open Todos +* add examples for new Client-Feature ... ## Overview