diff --git a/libraries/ESP8266HTTPClient/library.properties b/libraries/ESP8266HTTPClient/library.properties index ab96e9a9e..871617105 100644 --- a/libraries/ESP8266HTTPClient/library.properties +++ b/libraries/ESP8266HTTPClient/library.properties @@ -1,5 +1,5 @@ name=ESP8266HTTPClient -version=1.0 +version=1.1 author=Markus Sattler maintainer=Markus Sattler sentence=http Client for ESP8266 diff --git a/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.cpp b/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.cpp index fd11c0360..383af599a 100644 --- a/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.cpp +++ b/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.cpp @@ -30,168 +30,188 @@ #include "ESP8266HTTPClient.h" +class TransportTraits +{ +public: + virtual std::unique_ptr create() + { + return std::unique_ptr(new WiFiClient()); + } + + virtual bool verify(WiFiClient& client, const char* host) + { + return true; + } +}; + +class TLSTraits : public TransportTraits +{ +public: + TLSTraits(const String& fingerprint) : + _fingerprint(fingerprint) + { + } + + std::unique_ptr create() override + { + return std::unique_ptr(new WiFiClientSecure()); + } + + bool verify(WiFiClient& client, const char* host) override + { + auto wcs = reinterpret_cast(client); + return wcs.verify(_fingerprint.c_str(), host); + } + +protected: + String _fingerprint; +}; + /** - * constractor + * constructor */ -HTTPClient::HTTPClient() { - _tcp = NULL; - _tcps = NULL; - - _port = 0; - - _reuse = false; - _tcpTimeout = HTTPCLIENT_DEFAULT_TCP_TIMEOUT; - _useHTTP10 = false; - - _https = false; - - _userAgent = "ESP8266HTTPClient"; - - _headerKeysCount = 0; - _currentHeaders = NULL; - - _returnCode = 0; - _size = -1; - _canReuse = false; - _transferEncoding = HTTPC_TE_IDENTITY; - +HTTPClient::HTTPClient() +{ } /** - * deconstractor + * destructor */ -HTTPClient::~HTTPClient() { - - if(_tcps) { - _tcps->stop(); - delete _tcps; - _tcps = NULL; - _tcp = NULL; - } else if(_tcp) { +HTTPClient::~HTTPClient() +{ + if(_tcp) { _tcp->stop(); - delete _tcp; - _tcp = NULL; } - if(_currentHeaders) { delete[] _currentHeaders; } } -/** - * phasing the url for all needed informations - * @param url const char * - * @param httpsFingerprint const char * - */ -void HTTPClient::begin(const char *url, const char * httpsFingerprint) { - begin(String(url), String(httpsFingerprint)); + +bool HTTPClient::begin(String url, String httpsFingerprint) +{ + if (httpsFingerprint.length() == 0) { + return false; + } + if (!begin(url)) { + return false; + } + _transportTraits = TransportTraitsPtr(new TLSTraits(httpsFingerprint)); + DEBUG_HTTPCLIENT("[HTTP-Client][begin] httpsFingerprint: %s\n", httpsFingerprint.c_str()); + return true; } -/** - * phasing the url for all needed informations - * @param url String - * @param httpsFingerprint String - */ -void HTTPClient::begin(String url, String httpsFingerprint) { - - DEBUG_HTTPCLIENT("[HTTP-Client][begin] url: %s\n", url.c_str()); - - _httpsFingerprint = httpsFingerprint; +void HTTPClient::clear() +{ _returnCode = 0; _size = -1; + _headers = ""; +} - _Headers = ""; - String protocol; +/** + * parsing the url for all needed parameters + * @param url String + */ +bool HTTPClient::begin(String url) +{ + DEBUG_HTTPCLIENT("[HTTP-Client][begin] url: %s\n", url.c_str()); + bool hasPort = false; + clear(); + // check for : (http: or https: int index = url.indexOf(':'); - //int index2; - bool hasPort = false; - if(index >= 0) { - protocol = url.substring(0, index); - url.remove(0, (index + 3)); // remove http:// or https:// - - index = url.indexOf('/'); - String host = url.substring(0, index); - url.remove(0, index); // remove host part - - // get Authorization - index = host.indexOf('@'); - if(index >= 0) { - // auth info - String auth = host.substring(0, index); - host.remove(0, index + 1); // remove auth part including @ - _base64Authorization = base64::encode(auth); - } - - // get port - index = host.indexOf(':'); - if(index >= 0) { - _host = host.substring(0, index); // hostname - host.remove(0, (index + 1)); // remove hostname + : - _port = host.toInt(); // get port - hasPort = true; - } else { - _host = host; - } - - _url = url; - - if(protocol.equalsIgnoreCase("http")) { - _https = false; - if(!hasPort) { - _port = 80; - } - } else if(protocol.equalsIgnoreCase("https")) { - _https = true; - if(!hasPort) { - _port = 443; - } - } else { - DEBUG_HTTPCLIENT("[HTTP-Client][begin] protocol: %s unknown?!\n", protocol.c_str()); - return; - } - + if(index < 0) { + DEBUG_HTTPCLIENT("[HTTP-Client][begin] failed to parse protocol\n"); + return false; } - DEBUG_HTTPCLIENT("[HTTP-Client][begin] host: %s port: %d url: %s https: %d httpsFingerprint: %s\n", _host.c_str(), _port, _url.c_str(), _https, _httpsFingerprint.c_str()); + _protocol = url.substring(0, index); + url.remove(0, (index + 3)); // remove http:// or https:// + index = url.indexOf('/'); + String host = url.substring(0, index); + url.remove(0, index); // remove host part + + // get Authorization + index = host.indexOf('@'); + if(index >= 0) { + // auth info + String auth = host.substring(0, index); + host.remove(0, index + 1); // remove auth part including @ + _base64Authorization = base64::encode(auth); + } + + // get port + index = host.indexOf(':'); + if(index >= 0) { + _host = host.substring(0, index); // hostname + host.remove(0, (index + 1)); // remove hostname + : + _port = host.toInt(); // get port + hasPort = true; + } else { + _host = host; + } + _uri = url; + + if(_protocol.equalsIgnoreCase("http")) { + if(!hasPort) { + _port = 80; + } + } else if(_protocol.equalsIgnoreCase("https")) { + if(!hasPort) { + _port = 443; + } + } else { + DEBUG_HTTPCLIENT("[HTTP-Client][begin] protocol: %s unknown?!\n", _protocol.c_str()); + return false; + } + _transportTraits = TransportTraitsPtr(new TransportTraits()); + DEBUG_HTTPCLIENT("[HTTP-Client][begin] host: %s port: %d url: %s\n", _host.c_str(), _port, _uri.c_str()); + return true; } -/** - * begin - * @param host const char * - * @param port uint16_t - * @param url const char * - * @param https bool - * @param httpsFingerprint const char * - */ -void HTTPClient::begin(const char *host, uint16_t port, const char * url, bool https, const char * httpsFingerprint) { - - DEBUG_HTTPCLIENT("[HTTP-Client][begin] host: %s port:%d url: %s https: %d httpsFingerprint: %s\n", host, port, url, https, httpsFingerprint); - +bool HTTPClient::begin(String host, uint16_t port, String uri) +{ + clear(); _host = host; _port = port; - _url = url; - _https = https; - _httpsFingerprint = httpsFingerprint; - - _returnCode = 0; - _size = -1; - - _Headers = ""; - + _uri = uri; + _transportTraits = TransportTraitsPtr(new TransportTraits()); + DEBUG_HTTPCLIENT("[HTTP-Client][begin] host: %s port: %d uri: %s\n", host.c_str(), port, uri.c_str()); + return true; } -void HTTPClient::begin(String host, uint16_t port, String url, bool https, String httpsFingerprint) { - begin(host.c_str(), port, url.c_str(), https, httpsFingerprint.c_str()); +bool HTTPClient::begin(String host, uint16_t port, String uri, bool https, String httpsFingerprint) +{ + if (https) { + return begin(host, port, uri, httpsFingerprint); + } else { + return begin(host, port, uri); + } +} + +bool HTTPClient::begin(String host, uint16_t port, String uri, String httpsFingerprint) +{ + clear(); + _host = host; + _port = port; + _uri = uri; + + if (httpsFingerprint.length() == 0) { + return false; + } + _transportTraits = TransportTraitsPtr(new TLSTraits(httpsFingerprint)); + DEBUG_HTTPCLIENT("[HTTP-Client][begin] host: %s port: %d url: %s httpsFingerprint: %s\n", host.c_str(), port, uri.c_str(), httpsFingerprint.c_str()); + return true; } /** * end * called after the payload is handled */ -void HTTPClient::end(void) { +void HTTPClient::end(void) +{ if(connected()) { if(_tcp->available() > 0) { DEBUG_HTTPCLIENT("[HTTP-Client][end] still data in buffer (%d), clean up.\n", _tcp->available()); @@ -214,7 +234,8 @@ void HTTPClient::end(void) { * connected * @return connected status */ -bool HTTPClient::connected() { +bool HTTPClient::connected() +{ if(_tcp) { return (_tcp->connected() || (_tcp->available() > 0)); } @@ -226,7 +247,8 @@ bool HTTPClient::connected() { * keep-alive * @param reuse bool */ -void HTTPClient::setReuse(bool reuse) { +void HTTPClient::setReuse(bool reuse) +{ _reuse = reuse; } @@ -234,7 +256,8 @@ void HTTPClient::setReuse(bool reuse) { * set User Agent * @param userAgent const char * */ -void HTTPClient::setUserAgent(const char * userAgent) { +void HTTPClient::setUserAgent(const String& userAgent) +{ _userAgent = userAgent; } @@ -243,7 +266,8 @@ void HTTPClient::setUserAgent(const char * userAgent) { * @param user const char * * @param password const char * */ -void HTTPClient::setAuthorization(const char * user, const char * password) { +void HTTPClient::setAuthorization(const char * user, const char * password) +{ if(user && password) { String auth = user; auth += ":"; @@ -256,7 +280,8 @@ void HTTPClient::setAuthorization(const char * user, const char * password) { * set the Authorizatio for the http request * @param auth const char * base64 */ -void HTTPClient::setAuthorization(const char * auth) { +void HTTPClient::setAuthorization(const char * auth) +{ if(auth) { _base64Authorization = auth; } @@ -266,7 +291,8 @@ void HTTPClient::setAuthorization(const char * auth) { * set the timeout for the TCP connection * @param timeout unsigned int */ -void HTTPClient::setTimeout(uint16_t timeout) { +void HTTPClient::setTimeout(uint16_t timeout) +{ _tcpTimeout = timeout; if(connected()) { _tcp->setTimeout(timeout); @@ -277,7 +303,8 @@ void HTTPClient::setTimeout(uint16_t timeout) { * use HTTP1.0 * @param timeout */ -void HTTPClient::useHTTP10(bool useHTTP10) { +void HTTPClient::useHTTP10(bool useHTTP10) +{ _useHTTP10 = useHTTP10; } @@ -285,7 +312,8 @@ void HTTPClient::useHTTP10(bool useHTTP10) { * send a GET request * @return http code */ -int HTTPClient::GET() { +int HTTPClient::GET() +{ return sendRequest("GET"); } @@ -295,11 +323,13 @@ int HTTPClient::GET() { * @param size size_t * @return http code */ -int HTTPClient::POST(uint8_t * payload, size_t size) { +int HTTPClient::POST(uint8_t * payload, size_t size) +{ return sendRequest("POST", payload, size); } -int HTTPClient::POST(String payload) { +int HTTPClient::POST(String payload) +{ return POST((uint8_t *) payload.c_str(), payload.length()); } @@ -309,7 +339,8 @@ int HTTPClient::POST(String payload) { * @param payload String data for the message body * @return */ -int HTTPClient::sendRequest(const char * type, String payload) { +int HTTPClient::sendRequest(const char * type, String payload) +{ return sendRequest(type, (uint8_t *) payload.c_str(), payload.length()); } @@ -320,14 +351,15 @@ int HTTPClient::sendRequest(const char * type, String payload) { * @param size size_t size for the message body if 0 not send * @return -1 if no info or > 0 when Content-Length is set by server */ -int HTTPClient::sendRequest(const char * type, uint8_t * payload, size_t size) { +int HTTPClient::sendRequest(const char * type, uint8_t * payload, size_t size) +{ // connect to server if(!connect()) { return returnError(HTTPC_ERROR_CONNECTION_REFUSED); } if(payload && size > 0) { - addHeader("Content-Length", String(size)); + addHeader(F("Content-Length"), String(size)); } // send Header @@ -353,7 +385,8 @@ int HTTPClient::sendRequest(const char * type, uint8_t * payload, size_t size) { * @param size size_t size for the message body if 0 not Content-Length is send * @return -1 if no info or > 0 when Content-Length is set by server */ -int HTTPClient::sendRequest(const char * type, Stream * stream, size_t size) { +int HTTPClient::sendRequest(const char * type, Stream * stream, size_t size) +{ if(!stream) { return returnError(HTTPC_ERROR_NO_STREAM); @@ -468,7 +501,8 @@ int HTTPClient::sendRequest(const char * type, Stream * stream, size_t size) { free(buff); if(size && (int) size != bytesWritten) { - DEBUG_HTTPCLIENT("[HTTP-Client][sendRequest] Stream payload bytesWritten %d and size %d mismatch!.\n", bytesWritten, size); DEBUG_HTTPCLIENT("[HTTP-Client][sendRequest] ERROR SEND PAYLOAD FAILED!"); + DEBUG_HTTPCLIENT("[HTTP-Client][sendRequest] Stream payload bytesWritten %d and size %d mismatch!.\n", bytesWritten, size); + DEBUG_HTTPCLIENT("[HTTP-Client][sendRequest] ERROR SEND PAYLOAD FAILED!"); return returnError(HTTPC_ERROR_SEND_PAYLOAD_FAILED); } else { DEBUG_HTTPCLIENT("[HTTP-Client][sendRequest] Stream payload written: %d\n", bytesWritten); @@ -487,36 +521,38 @@ int HTTPClient::sendRequest(const char * type, Stream * stream, size_t size) { * size of message body / payload * @return -1 if no info or > 0 when Content-Length is set by server */ -int HTTPClient::getSize(void) { +int HTTPClient::getSize(void) +{ return _size; } /** - * deprecated Note: this is not working with https! * returns the stream of the tcp connection * @return WiFiClient */ -WiFiClient & HTTPClient::getStream(void) { +WiFiClient& HTTPClient::getStream(void) +{ if(connected()) { return *_tcp; } - DEBUG_HTTPCLIENT("[HTTP-Client] no stream to return!?\n"); - - // todo return error? + DEBUG_HTTPCLIENT("[HTTP-Client] getStream: not connected\n"); + static WiFiClient empty; + return empty; } /** * returns the stream of the tcp connection * @return WiFiClient * */ -WiFiClient * HTTPClient::getStreamPtr(void) { +WiFiClient* HTTPClient::getStreamPtr(void) +{ if(connected()) { - return _tcp; + return _tcp.get(); } - DEBUG_HTTPCLIENT("[HTTP-Client] no stream to return!?\n"); - return NULL; + DEBUG_HTTPCLIENT("[HTTP-Client] getStreamPtr: not connected\n"); + return nullptr; } /** @@ -524,7 +560,8 @@ WiFiClient * HTTPClient::getStreamPtr(void) { * @param stream Stream * * @return bytes written ( negative values are error codes ) */ -int HTTPClient::writeToStream(Stream * stream) { +int HTTPClient::writeToStream(Stream * stream) +{ if(!stream) { return returnError(HTTPC_ERROR_NO_STREAM); @@ -600,14 +637,15 @@ int HTTPClient::writeToStream(Stream * stream) { * return all payload as String (may need lot of ram or trigger out of memory!) * @return String */ -String HTTPClient::getString(void) { +String HTTPClient::getString(void) +{ StreamString sstring; if(_size) { // try to reserve needed memmory if(!sstring.reserve((_size + 1))) { - DEBUG_HTTPCLIENT("[HTTP-Client][getString] too less memory to reserve as string! need: %d\n", (_size + 1)); - return String("--too less memory--"); + DEBUG_HTTPCLIENT("[HTTP-Client][getString] not enough memory to reserve a string! need: %d\n", (_size + 1)); + return ""; } } @@ -620,32 +658,33 @@ String HTTPClient::getString(void) { * @param error int * @return String */ -String HTTPClient::errorToString(int error) { +String HTTPClient::errorToString(int error) +{ switch(error) { - case HTTPC_ERROR_CONNECTION_REFUSED: - return String("connection refused"); - case HTTPC_ERROR_SEND_HEADER_FAILED: - return String("send header failed"); - case HTTPC_ERROR_SEND_PAYLOAD_FAILED: - return String("send payload failed"); - case HTTPC_ERROR_NOT_CONNECTED: - return String("not connected"); - case HTTPC_ERROR_CONNECTION_LOST: - return String("connection lost"); - case HTTPC_ERROR_NO_STREAM: - return String("no stream"); - case HTTPC_ERROR_NO_HTTP_SERVER: - return String("no HTTP server"); - case HTTPC_ERROR_TOO_LESS_RAM: - return String("too less ram"); - case HTTPC_ERROR_ENCODING: - return String("Transfer-Encoding not supported"); - case HTTPC_ERROR_STREAM_WRITE: - return String("Stream write error"); - case HTTPC_ERROR_READ_TIMEOUT: - return String("read Timeout"); - default: - return String(); + case HTTPC_ERROR_CONNECTION_REFUSED: + return F("connection refused"); + case HTTPC_ERROR_SEND_HEADER_FAILED: + return F("send header failed"); + case HTTPC_ERROR_SEND_PAYLOAD_FAILED: + return F("send payload failed"); + case HTTPC_ERROR_NOT_CONNECTED: + return F("not connected"); + case HTTPC_ERROR_CONNECTION_LOST: + return F("connection lost"); + case HTTPC_ERROR_NO_STREAM: + return F("no stream"); + case HTTPC_ERROR_NO_HTTP_SERVER: + return F("no HTTP server"); + case HTTPC_ERROR_TOO_LESS_RAM: + return F("too less ram"); + case HTTPC_ERROR_ENCODING: + return F("Transfer-Encoding not supported"); + case HTTPC_ERROR_STREAM_WRITE: + return F("Stream write error"); + case HTTPC_ERROR_READ_TIMEOUT: + return F("read Timeout"); + default: + return String(); } } @@ -655,62 +694,77 @@ String HTTPClient::errorToString(int error) { * @param value * @param first */ -void HTTPClient::addHeader(const String& name, const String& value, bool first) { +void HTTPClient::addHeader(const String& name, const String& value, bool first) +{ // not allow set of Header handled by code - if(!name.equalsIgnoreCase("Connection") && !name.equalsIgnoreCase("User-Agent") && !name.equalsIgnoreCase("Host") && !(_base64Authorization.length() && name.equalsIgnoreCase("Authorization"))) { + if(!name.equalsIgnoreCase(F("Connection")) && + !name.equalsIgnoreCase(F("User-Agent")) && + !name.equalsIgnoreCase(F("Host")) && + !(name.equalsIgnoreCase(F("Authorization")) && _base64Authorization.length())){ String headerLine = name; headerLine += ": "; headerLine += value; headerLine += "\r\n"; if(first) { - _Headers = headerLine + _Headers; + _headers = headerLine + _headers; } else { - _Headers += headerLine; + _headers += headerLine; } } } -void HTTPClient::collectHeaders(const char* headerKeys[], const size_t headerKeysCount) { +void HTTPClient::collectHeaders(const char* headerKeys[], const size_t headerKeysCount) +{ _headerKeysCount = headerKeysCount; - if(_currentHeaders) + if(_currentHeaders) { delete[] _currentHeaders; + } _currentHeaders = new RequestArgument[_headerKeysCount]; for(size_t i = 0; i < _headerKeysCount; i++) { _currentHeaders[i].key = headerKeys[i]; } } -String HTTPClient::header(const char* name) { +String HTTPClient::header(const char* name) +{ for(size_t i = 0; i < _headerKeysCount; ++i) { - if(_currentHeaders[i].key == name) + if(_currentHeaders[i].key == name) { return _currentHeaders[i].value; + } } return String(); } -String HTTPClient::header(size_t i) { - if(i < _headerKeysCount) +String HTTPClient::header(size_t i) +{ + if(i < _headerKeysCount) { return _currentHeaders[i].value; + } return String(); } -String HTTPClient::headerName(size_t i) { - if(i < _headerKeysCount) +String HTTPClient::headerName(size_t i) +{ + if(i < _headerKeysCount) { return _currentHeaders[i].key; + } return String(); } -int HTTPClient::headers() { +int HTTPClient::headers() +{ return _headerKeysCount; } -bool HTTPClient::hasHeader(const char* name) { +bool HTTPClient::hasHeader(const char* name) +{ for(size_t i = 0; i < _headerKeysCount; ++i) { - if((_currentHeaders[i].key == name) && (_currentHeaders[i].value.length() > 0)) + if((_currentHeaders[i].key == name) && (_currentHeaders[i].value.length() > 0)) { return true; + } } return false; } @@ -719,7 +773,8 @@ bool HTTPClient::hasHeader(const char* name) { * init TCP connection and handle ssl verify if needed * @return true if connection is ok */ -bool HTTPClient::connect(void) { +bool HTTPClient::connect(void) +{ if(connected()) { DEBUG_HTTPCLIENT("[HTTP-Client] connect. already connected, try reuse!\n"); @@ -729,24 +784,13 @@ bool HTTPClient::connect(void) { return true; } - if(_https) { - DEBUG_HTTPCLIENT("[HTTP-Client] connect https...\n"); - if(_tcps) { - delete _tcps; - _tcps = NULL; - _tcp = NULL; - } - _tcps = new WiFiClientSecure(); - _tcp = _tcps; - } else { - DEBUG_HTTPCLIENT("[HTTP-Client] connect http...\n"); - if(_tcp) { - delete _tcp; - _tcp = NULL; - } - _tcp = new WiFiClient(); + if (!_transportTraits) { + DEBUG_HTTPCLIENT("[HTTP-Client] _transportTraits is null (HTTPClient::begin not called?)\n"); + return false; } + _tcp = _transportTraits->create(); + if(!_tcp->connect(_host.c_str(), _port)) { DEBUG_HTTPCLIENT("[HTTP-Client] failed connect to %s:%u\n", _host.c_str(), _port); return false; @@ -754,14 +798,10 @@ bool HTTPClient::connect(void) { DEBUG_HTTPCLIENT("[HTTP-Client] connected to %s:%u\n", _host.c_str(), _port); - if(_https && _httpsFingerprint.length() > 0) { - if(_tcps->verify(_httpsFingerprint.c_str(), _host.c_str())) { - DEBUG_HTTPCLIENT("[HTTP-Client] https certificate matches\n"); - } else { - DEBUG_HTTPCLIENT("[HTTP-Client] https certificate doesn't match!\n"); - _tcp->stop(); - return false; - } + if (!_transportTraits->verify(*_tcp, _host.c_str())) { + DEBUG_HTTPCLIENT("[HTTP-Client] transport level verify failed\n"); + _tcp->stop(); + return false; } // set Timeout for readBytesUntil and readStringUntil @@ -778,12 +818,13 @@ bool HTTPClient::connect(void) { * @param type (GET, POST, ...) * @return status */ -bool HTTPClient::sendHeader(const char * type) { +bool HTTPClient::sendHeader(const char * type) +{ if(!connected()) { return false; } - String header = String(type) + " " + _url + " HTTP/1."; + String header = String(type) + " " + _uri + F(" HTTP/1."); if(_useHTTP10) { header += "0"; @@ -791,36 +832,38 @@ bool HTTPClient::sendHeader(const char * type) { header += "1"; } - header += "\r\n" - "Host: " + _host + "\r\n" - "User-Agent: " + _userAgent + "\r\n" - "Connection: "; + header += String(F("\r\nHost: ")) + _host + + F("\r\nUser-Agent: ") + _userAgent + + F("\r\nConnection: "); if(_reuse) { - header += "keep-alive"; + header += F("keep-alive"); } else { - header += "close"; + header += F("close"); } header += "\r\n"; if(!_useHTTP10) { - header += "Accept-Encoding: identity;q=1,chunked;q=0.1,*;q=0\r\n"; + header += F("Accept-Encoding: identity;q=1,chunked;q=0.1,*;q=0\r\n"); } if(_base64Authorization.length()) { - header += "Authorization: Basic " + _base64Authorization + "\r\n"; + header += F("Authorization: Basic "); + header += _base64Authorization; + header += "\r\n"; } - header += _Headers + "\r\n"; + header += _headers + "\r\n"; - return (_tcp->write(header.c_str(), header.length()) == header.length()); + return (_tcp->write((const uint8_t *) header.c_str(), header.length()) == header.length()); } /** * reads the response from the server * @return int http code */ -int HTTPClient::handleHeaderResponse() { +int HTTPClient::handleHeaderResponse() +{ if(!connected()) { return HTTPC_ERROR_NOT_CONNECTED; @@ -911,7 +954,8 @@ int HTTPClient::handleHeaderResponse() { * @param size int * @return < 0 = error >= 0 = size written */ -int HTTPClient::writeToStreamDataBlock(Stream * stream, int size) { +int HTTPClient::writeToStreamDataBlock(Stream * stream, int size) +{ int buff_size = HTTP_TCP_BUFFER_SIZE; int len = size; int bytesWritten = 0; @@ -1021,7 +1065,8 @@ int HTTPClient::writeToStreamDataBlock(Stream * stream, int size) { * @param error * @return error */ -int HTTPClient::returnError(int error) { +int HTTPClient::returnError(int error) +{ if(error < 0) { DEBUG_HTTPCLIENT("[HTTP-Client][returnError] error(%d): %s\n", error, errorToString(error).c_str()); if(connected()) { diff --git a/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.h b/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.h index 0b865c2a6..1f5ef7fbc 100644 --- a/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.h +++ b/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.h @@ -25,6 +25,10 @@ #ifndef ESP8266HTTPClient_H_ #define ESP8266HTTPClient_H_ +#include +#include +#include + #ifdef DEBUG_ESP_HTTP_CLIENT #ifdef DEBUG_ESP_PORT #define DEBUG_HTTPCLIENT(...) DEBUG_ESP_PORT.printf( __VA_ARGS__ ) @@ -120,97 +124,100 @@ typedef enum { HTTPC_TE_CHUNKED } transferEncoding_t; -class HTTPClient { - public: - HTTPClient(); - ~HTTPClient(); +class TransportTraits; +typedef std::unique_ptr TransportTraitsPtr; - void begin(const char *url, const char * httpsFingerprint = ""); - void begin(String url, String httpsFingerprint = ""); +class HTTPClient +{ +public: + HTTPClient(); + ~HTTPClient(); - void begin(const char *host, uint16_t port, const char * url = "/", bool https = false, const char * httpsFingerprint = ""); - void begin(String host, uint16_t port, String url = "/", bool https = false, String httpsFingerprint = ""); + bool begin(String url); + bool begin(String url, String httpsFingerprint); + bool begin(String host, uint16_t port, String uri = "/"); + bool begin(String host, uint16_t port, String uri, String httpsFingerprint); + // deprecated, use the overload above instead + bool begin(String host, uint16_t port, String uri, bool https, String httpsFingerprint) __attribute__ ((deprecated)); - void end(void); + void end(void); - bool connected(void); + bool connected(void); - void setReuse(bool reuse); /// keep-alive - void setUserAgent(const char * userAgent); - void setAuthorization(const char * user, const char * password); - void setAuthorization(const char * auth); - void setTimeout(uint16_t timeout); + void setReuse(bool reuse); /// keep-alive + void setUserAgent(const String& userAgent); + void setAuthorization(const char * user, const char * password); + void setAuthorization(const char * auth); + void setTimeout(uint16_t timeout); - void useHTTP10(bool usehttp10 = true); + void useHTTP10(bool usehttp10 = true); - /// request handling - int GET(); - int POST(uint8_t * payload, size_t size); - int POST(String payload); - int sendRequest(const char * type, String payload); - int sendRequest(const char * type, uint8_t * payload = NULL, size_t size = 0); - int sendRequest(const char * type, Stream * stream, size_t size = 0); + /// request handling + int GET(); + int POST(uint8_t * payload, size_t size); + int POST(String payload); + int sendRequest(const char * type, String payload); + int sendRequest(const char * type, uint8_t * payload = NULL, size_t size = 0); + int sendRequest(const char * type, Stream * stream, size_t size = 0); - void addHeader(const String& name, const String& value, bool first = false); + void addHeader(const String& name, const String& value, bool first = false); - /// Response handling - void collectHeaders(const char* headerKeys[], const size_t headerKeysCount); - String header(const char* name); // get request header value by name - String header(size_t i); // get request header value by number - String headerName(size_t i); // get request header name by number - int headers(); // get header count - bool hasHeader(const char* name); // check if header exists + /// Response handling + void collectHeaders(const char* headerKeys[], const size_t headerKeysCount); + String header(const char* name); // get request header value by name + String header(size_t i); // get request header value by number + String headerName(size_t i); // get request header name by number + int headers(); // get header count + bool hasHeader(const char* name); // check if header exists - int getSize(void); + int getSize(void); - WiFiClient & getStream(void) __attribute__ ((deprecated)) ; - WiFiClient * getStreamPtr(void); - int writeToStream(Stream * stream); - String getString(void); + WiFiClient& getStream(void); + WiFiClient* getStreamPtr(void); + int writeToStream(Stream* stream); + String getString(void); - static String errorToString(int error); + static String errorToString(int error); - protected: +protected: + struct RequestArgument { + String key; + String value; + }; - struct RequestArgument { - String key; - String value; - }; + void clear(); + int returnError(int error); + bool connect(void); + bool sendHeader(const char * type); + int handleHeaderResponse(); + int writeToStreamDataBlock(Stream * stream, int len); - WiFiClient * _tcp; - WiFiClientSecure * _tcps; + TransportTraitsPtr _transportTraits; + std::unique_ptr _tcp; - /// request handling - String _host; - uint16_t _port; - bool _reuse; - uint16_t _tcpTimeout; - bool _useHTTP10; + /// request handling + String _host; + uint16_t _port = 0; + bool _reuse = false; + uint16_t _tcpTimeout = HTTPCLIENT_DEFAULT_TCP_TIMEOUT; + bool _useHTTP10 = false; - String _url; - bool _https; - String _httpsFingerprint; + String _uri; + String _protocol; + String _headers; + String _userAgent = "ESP8266HTTPClient"; + String _base64Authorization; - String _Headers; - String _userAgent; - String _base64Authorization; + /// Response handling + RequestArgument* _currentHeaders = nullptr; + size_t _headerKeysCount = 0; - /// Response handling - RequestArgument* _currentHeaders; - size_t _headerKeysCount; - - int _returnCode; - int _size; - bool _canReuse; - transferEncoding_t _transferEncoding; - - int returnError(int error); - bool connect(void); - bool sendHeader(const char * type); - int handleHeaderResponse(); - int writeToStreamDataBlock(Stream * stream, int len); + int _returnCode = 0; + int _size = -1; + bool _canReuse = false; + transferEncoding_t _transferEncoding = HTTPC_TE_IDENTITY; }; diff --git a/libraries/ESP8266httpUpdate/library.properties b/libraries/ESP8266httpUpdate/library.properties index ca5f75f6e..2116c04a0 100644 --- a/libraries/ESP8266httpUpdate/library.properties +++ b/libraries/ESP8266httpUpdate/library.properties @@ -1,5 +1,5 @@ name=ESP8266httpUpdate -version=1.0 +version=1.1 author=Markus Sattler maintainer=Markus Sattler sentence=Http Update for ESP8266 diff --git a/libraries/ESP8266httpUpdate/src/ESP8266httpUpdate.cpp b/libraries/ESP8266httpUpdate/src/ESP8266httpUpdate.cpp index 5d2fb2782..b0020aad7 100644 --- a/libraries/ESP8266httpUpdate/src/ESP8266httpUpdate.cpp +++ b/libraries/ESP8266httpUpdate/src/ESP8266httpUpdate.cpp @@ -1,365 +1,387 @@ -/** - * - * @file ESP8266HTTPUpdate.cpp - * @date 21.06.2015 - * @author Markus Sattler - * - * Copyright (c) 2015 Markus Sattler. All rights reserved. - * This file is part of the ESP8266 Http Updater. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -#include "ESP8266httpUpdate.h" -#include - -extern "C" uint32_t _SPIFFS_start; -extern "C" uint32_t _SPIFFS_end; - -ESP8266HTTPUpdate::ESP8266HTTPUpdate(void) { -} - -ESP8266HTTPUpdate::~ESP8266HTTPUpdate(void) { -} - -/** - * - * @param url const char * - * @param current_version const char * - * @param httpsFingerprint const char * - * @return t_httpUpdate_return - */ -t_httpUpdate_return ESP8266HTTPUpdate::update(const char * url, const char * current_version, const char * httpsFingerprint, bool reboot) { - HTTPClient http; - http.begin(url, httpsFingerprint); - return handleUpdate(&http, current_version, reboot, false); -} - -/** - * - * @param url const char * - * @param current_version const char * - * @param httpsFingerprint const char * - * @return t_httpUpdate_return - */ -t_httpUpdate_return ESP8266HTTPUpdate::updateSpiffs(const char * url, const char * current_version, const char * httpsFingerprint, bool reboot) { - HTTPClient http; - http.begin(url, httpsFingerprint); - return handleUpdate(&http, current_version, reboot, true); -} - -/** - * - * @param host const char * - * @param port uint16_t - * @param url const char * - * @param current_version const char * - * @param httpsFingerprint const char * - * @return - */ -t_httpUpdate_return ESP8266HTTPUpdate::update(const char * host, uint16_t port, const char * url, const char * current_version, bool https, const char * httpsFingerprint, bool reboot) { - HTTPClient http; - http.begin(host, port, url, https, httpsFingerprint); - return handleUpdate(&http, current_version, reboot, false); -} - -t_httpUpdate_return ESP8266HTTPUpdate::update(String host, uint16_t port, String url, String current_version, bool https, String httpsFingerprint, bool reboot) { - HTTPClient http; - http.begin(host, port, url, https, httpsFingerprint); - return handleUpdate(&http, current_version.c_str(), reboot, false); -} - -/** - * return error code as int - * @return int error code - */ -int ESP8266HTTPUpdate::getLastError(void){ - return lastError; -} - -/** - * return error code as String - * @return String error - */ -String ESP8266HTTPUpdate::getLastErrorString(void) { - - if(lastError == 0) { - return String(); // no error - } - - // error from Update class - if(lastError > 0) { - StreamString error; - Update.printError(error); - error.trim(); // remove line ending - return "Update error: " + error; - } - - // error from http client - if(lastError > -100) { - return "HTTP error: " + HTTPClient::errorToString(lastError); - } - - switch(lastError) { - case HTTP_UE_TOO_LESS_SPACE: - return String("To less space"); - case HTTP_UE_SERVER_NOT_REPORT_SIZE: - return String("Server not Report Size"); - case HTTP_UE_SERVER_FILE_NOT_FOUND: - return String("File not Found (404)"); - case HTTP_UE_SERVER_FORBIDDEN: - return String("Forbidden (403)"); - case HTTP_UE_SERVER_WRONG_HTTP_CODE: - return String("Wrong HTTP code"); - case HTTP_UE_SERVER_FAULTY_MD5: - return String("Faulty MD5"); - case HTTP_UE_BIN_VERIFY_HEADER_FAILED: - return String("Verify bin header failed"); - case HTTP_UE_BIN_FOR_WRONG_FLASH: - return String("bin for wrong flash size"); - } - - return String(); -} - - -/** - * - * @param http HTTPClient * - * @param current_version const char * - * @return t_httpUpdate_return - */ -t_httpUpdate_return ESP8266HTTPUpdate::handleUpdate(HTTPClient * http, const char * current_version, bool reboot, bool spiffs) { - - t_httpUpdate_return ret = HTTP_UPDATE_FAILED; - - // use HTTP/1.0 for update since the update handler not support any transfer Encoding - http->useHTTP10(true); - http->setTimeout(8000); - http->setUserAgent("ESP8266-http-Update"); - http->addHeader("x-ESP8266-STA-MAC", WiFi.macAddress()); - http->addHeader("x-ESP8266-AP-MAC", WiFi.softAPmacAddress()); - http->addHeader("x-ESP8266-free-space", String(ESP.getFreeSketchSpace())); - http->addHeader("x-ESP8266-sketch-size", String(ESP.getSketchSize())); - http->addHeader("x-ESP8266-chip-size", String(ESP.getFlashChipRealSize())); - http->addHeader("x-ESP8266-sdk-version", ESP.getSdkVersion()); - - if(spiffs) { - http->addHeader("x-ESP8266-mode", "spiffs"); - } else { - http->addHeader("x-ESP8266-mode", "sketch"); - } - - if(current_version && current_version[0] != 0x00) { - http->addHeader("x-ESP8266-version", current_version); - } - - const char * headerkeys[] = { "x-MD5" }; - size_t headerkeyssize = sizeof(headerkeys) / sizeof(char*); - - // track these headers - http->collectHeaders(headerkeys, headerkeyssize); - - - int code = http->GET(); - int len = http->getSize(); - - if(code <= 0) { - DEBUG_HTTP_UPDATE("[httpUpdate] HTTP error: %s\n", http->errorToString(code).c_str()); - lastError = code; - http->end(); - return HTTP_UPDATE_FAILED; - } - - - DEBUG_HTTP_UPDATE("[httpUpdate] Header read fin.\n"); - DEBUG_HTTP_UPDATE("[httpUpdate] Server header:\n"); - DEBUG_HTTP_UPDATE("[httpUpdate] - code: %d\n", code); - DEBUG_HTTP_UPDATE("[httpUpdate] - len: %d\n", len); - - if(http->hasHeader("x-MD5")) { - DEBUG_HTTP_UPDATE("[httpUpdate] - MD5: %s\n", http->header("x-MD5").c_str()); - } - - DEBUG_HTTP_UPDATE("[httpUpdate] ESP8266 info:\n"); - DEBUG_HTTP_UPDATE("[httpUpdate] - free Space: %d\n", ESP.getFreeSketchSpace()); - DEBUG_HTTP_UPDATE("[httpUpdate] - current Sketch Size: %d\n", ESP.getSketchSize()); - - if(current_version && current_version[0] != 0x00) { - DEBUG_HTTP_UPDATE("[httpUpdate] - current version: %s\n", current_version); - } - - switch(code) { - case HTTP_CODE_OK: ///< OK (Start Update) - if(len > 0) { - bool startUpdate = true; - if(spiffs) { - size_t spiffsSize = ((size_t) &_SPIFFS_end - (size_t) &_SPIFFS_start); - if(len > (int) spiffsSize) { - DEBUG_HTTP_UPDATE("[httpUpdate] spiffsSize to low (%d) needed: %d\n", spiffsSize, len); - startUpdate = false; - } - } else { - if(len > (int) ESP.getFreeSketchSpace()) { - DEBUG_HTTP_UPDATE("[httpUpdate] FreeSketchSpace to low (%d) needed: %d\n", ESP.getFreeSketchSpace(), len); - startUpdate = false; - } - } - - if(!startUpdate) { - lastError = HTTP_UE_TOO_LESS_SPACE; - ret = HTTP_UPDATE_FAILED; - } else { - - WiFiClient * tcp = http->getStreamPtr(); - - WiFiUDP::stopAll(); - WiFiClient::stopAllExcept(tcp); - - delay(100); - - int command; - - if(spiffs) { - command = U_SPIFFS; - DEBUG_HTTP_UPDATE("[httpUpdate] runUpdate spiffs...\n"); - } else { - command = U_FLASH; - DEBUG_HTTP_UPDATE("[httpUpdate] runUpdate flash...\n"); - } - - if(!spiffs) { - uint8_t buf[4]; - if(tcp->peekBytes(&buf[0], 4) != 4) { - DEBUG_HTTP_UPDATE("[httpUpdate] peekBytes magic header failed\n"); - lastError = HTTP_UE_BIN_VERIFY_HEADER_FAILED; - http->end(); - return HTTP_UPDATE_FAILED; - } - - // check for valid first magic byte - if(buf[0] != 0xE9) { - DEBUG_HTTP_UPDATE("[httpUpdate] magic header not starts with 0xE9\n"); - lastError = HTTP_UE_BIN_VERIFY_HEADER_FAILED; - http->end(); - return HTTP_UPDATE_FAILED; - - } - - uint32_t bin_flash_size = ESP.magicFlashChipSize((buf[3] & 0xf0) >> 4); - - // check if new bin fits to SPI flash - if(bin_flash_size > ESP.getFlashChipRealSize()) { - DEBUG_HTTP_UPDATE("[httpUpdate] magic header, new bin not fits SPI Flash\n"); - lastError = HTTP_UE_BIN_FOR_WRONG_FLASH; - http->end(); - return HTTP_UPDATE_FAILED; - } - } - - if(runUpdate(*tcp, len, http->header("x-MD5"), command)) { - ret = HTTP_UPDATE_OK; - DEBUG_HTTP_UPDATE("[httpUpdate] Update ok\n"); - http->end(); - - if(reboot) { - ESP.restart(); - } - - } else { - ret = HTTP_UPDATE_FAILED; - DEBUG_HTTP_UPDATE("[httpUpdate] Update failed\n"); - } - } - } else { - lastError = HTTP_UE_SERVER_NOT_REPORT_SIZE; - ret = HTTP_UPDATE_FAILED; - DEBUG_HTTP_UPDATE("[httpUpdate] Content-Length is 0 or not set by Server?!\n"); - } - break; - case HTTP_CODE_NOT_MODIFIED: - ///< Not Modified (No updates) - ret = HTTP_UPDATE_NO_UPDATES; - break; - case HTTP_CODE_NOT_FOUND: - lastError = HTTP_UE_SERVER_FILE_NOT_FOUND; - ret = HTTP_UPDATE_FAILED; - break; - case HTTP_CODE_FORBIDDEN: - lastError = HTTP_UE_SERVER_FORBIDDEN; - ret = HTTP_UPDATE_FAILED; - break; - default: - lastError = HTTP_UE_SERVER_WRONG_HTTP_CODE; - ret = HTTP_UPDATE_FAILED; - DEBUG_HTTP_UPDATE("[httpUpdate] HTTP Code is (%d)\n", code); - //http->writeToStream(&Serial1); - break; - } - - http->end(); - return ret; -} - -/** - * write Update to flash - * @param in Stream& - * @param size uint32_t - * @param md5 String - * @return true if Update ok - */ -bool ESP8266HTTPUpdate::runUpdate(Stream& in, uint32_t size, String md5, int command) { - - StreamString error; - - if(!Update.begin(size, command)) { - lastError = Update.getError(); - Update.printError(error); - error.trim(); // remove line ending - DEBUG_HTTP_UPDATE("[httpUpdate] Update.begin failed! (%s)\n", error.c_str()); - return false; - } - - if(md5.length()) { - if(!Update.setMD5(md5.c_str())) { - lastError = HTTP_UE_SERVER_FAULTY_MD5; - DEBUG_HTTP_UPDATE("[httpUpdate] Update.setMD5 failed! (%s)\n", md5.c_str()); - return false; - } - } - - if(Update.writeStream(in) != size) { - lastError = Update.getError(); - Update.printError(error); - error.trim(); // remove line ending - DEBUG_HTTP_UPDATE("[httpUpdate] Update.writeStream failed! (%s)\n", error.c_str()); - return false; - } - - if(!Update.end()) { - lastError = Update.getError(); - Update.printError(error); - error.trim(); // remove line ending - DEBUG_HTTP_UPDATE("[httpUpdate] Update.end failed! (%s)\n", error.c_str()); - return false; - } - - return true; -} - - - -ESP8266HTTPUpdate ESPhttpUpdate; +/** + * + * @file ESP8266HTTPUpdate.cpp + * @date 21.06.2015 + * @author Markus Sattler + * + * Copyright (c) 2015 Markus Sattler. All rights reserved. + * This file is part of the ESP8266 Http Updater. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include "ESP8266httpUpdate.h" +#include + +extern "C" uint32_t _SPIFFS_start; +extern "C" uint32_t _SPIFFS_end; + +ESP8266HTTPUpdate::ESP8266HTTPUpdate(void) +{ +} + +ESP8266HTTPUpdate::~ESP8266HTTPUpdate(void) +{ +} + +HTTPUpdateResult ESP8266HTTPUpdate::update(const String& url, const String& currentVersion, + const String& httpsFingerprint, bool reboot) +{ + rebootOnUpdate(reboot); + return update(url, currentVersion, httpsFingerprint); +} + +HTTPUpdateResult ESP8266HTTPUpdate::update(const String& url, const String& currentVersion) +{ + HTTPClient http; + http.begin(url); + return handleUpdate(http, currentVersion, false); +} + +HTTPUpdateResult ESP8266HTTPUpdate::update(const String& url, const String& currentVersion, + const String& httpsFingerprint) +{ + HTTPClient http; + http.begin(url, httpsFingerprint); + return handleUpdate(http, currentVersion, false); +} + +HTTPUpdateResult ESP8266HTTPUpdate::updateSpiffs(const String& url, const String& currentVersion, const String& httpsFingerprint) +{ + HTTPClient http; + http.begin(url, httpsFingerprint); + return handleUpdate(http, currentVersion, true); +} + +HTTPUpdateResult ESP8266HTTPUpdate::updateSpiffs(const String& url, const String& currentVersion) +{ + HTTPClient http; + http.begin(url); + return handleUpdate(http, currentVersion, true); +} + +HTTPUpdateResult ESP8266HTTPUpdate::update(const String& host, uint16_t port, const String& uri, const String& currentVersion, + bool https, const String& httpsFingerprint, bool reboot) +{ + rebootOnUpdate(reboot); + if (httpsFingerprint.length() == 0) { + return update(host, port, uri, currentVersion); + } else { + return update(host, port, uri, currentVersion, httpsFingerprint); + } +} + +HTTPUpdateResult ESP8266HTTPUpdate::update(const String& host, uint16_t port, const String& uri, + const String& currentVersion) +{ + HTTPClient http; + http.begin(host, port, uri); + return handleUpdate(http, currentVersion, false); +} +HTTPUpdateResult ESP8266HTTPUpdate::update(const String& host, uint16_t port, const String& url, + const String& currentVersion, const String& httpsFingerprint) +{ + HTTPClient http; + http.begin(host, port, url, httpsFingerprint); + return handleUpdate(http, currentVersion, false); + +} + +/** + * return error code as int + * @return int error code + */ +int ESP8266HTTPUpdate::getLastError(void) +{ + return _lastError; +} + +/** + * return error code as String + * @return String error + */ +String ESP8266HTTPUpdate::getLastErrorString(void) +{ + + if(_lastError == 0) { + return String(); // no error + } + + // error from Update class + if(_lastError > 0) { + StreamString error; + Update.printError(error); + error.trim(); // remove line ending + return String(F("Update error: ")) + error; + } + + // error from http client + if(_lastError > -100) { + return String(F("HTTP error: ")) + HTTPClient::errorToString(_lastError); + } + + switch(_lastError) { + case HTTP_UE_TOO_LESS_SPACE: + return F("To less space"); + case HTTP_UE_SERVER_NOT_REPORT_SIZE: + return F("Server not Report Size"); + case HTTP_UE_SERVER_FILE_NOT_FOUND: + return F("File not Found (404)"); + case HTTP_UE_SERVER_FORBIDDEN: + return F("Forbidden (403)"); + case HTTP_UE_SERVER_WRONG_HTTP_CODE: + return F("Wrong HTTP code"); + case HTTP_UE_SERVER_FAULTY_MD5: + return F("Faulty MD5"); + case HTTP_UE_BIN_VERIFY_HEADER_FAILED: + return F("Verify bin header failed"); + case HTTP_UE_BIN_FOR_WRONG_FLASH: + return F("bin for wrong flash size"); + } + + return String(); +} + + +/** + * + * @param http HTTPClient * + * @param currentVersion const char * + * @return HTTPUpdateResult + */ +HTTPUpdateResult ESP8266HTTPUpdate::handleUpdate(HTTPClient& http, const String& currentVersion, bool spiffs) +{ + + HTTPUpdateResult ret = HTTP_UPDATE_FAILED; + + // use HTTP/1.0 for update since the update handler not support any transfer Encoding + http.useHTTP10(true); + http.setTimeout(8000); + http.setUserAgent(F("ESP8266-http-Update")); + http.addHeader(F("x-ESP8266-STA-MAC"), WiFi.macAddress()); + http.addHeader(F("x-ESP8266-AP-MAC"), WiFi.softAPmacAddress()); + http.addHeader(F("x-ESP8266-free-space"), String(ESP.getFreeSketchSpace())); + http.addHeader(F("x-ESP8266-sketch-size"), String(ESP.getSketchSize())); + http.addHeader(F("x-ESP8266-chip-size"), String(ESP.getFlashChipRealSize())); + http.addHeader(F("x-ESP8266-sdk-version"), ESP.getSdkVersion()); + + if(spiffs) { + http.addHeader(F("x-ESP8266-mode"), F("spiffs")); + } else { + http.addHeader(F("x-ESP8266-mode"), F("sketch")); + } + + if(currentVersion && currentVersion[0] != 0x00) { + http.addHeader(F("x-ESP8266-version"), currentVersion); + } + + const char * headerkeys[] = { "x-MD5" }; + size_t headerkeyssize = sizeof(headerkeys) / sizeof(char*); + + // track these headers + http.collectHeaders(headerkeys, headerkeyssize); + + + int code = http.GET(); + int len = http.getSize(); + + if(code <= 0) { + DEBUG_HTTP_UPDATE("[httpUpdate] HTTP error: %s\n", http.errorToString(code).c_str()); + _lastError = code; + http.end(); + return HTTP_UPDATE_FAILED; + } + + + DEBUG_HTTP_UPDATE("[httpUpdate] Header read fin.\n"); + DEBUG_HTTP_UPDATE("[httpUpdate] Server header:\n"); + DEBUG_HTTP_UPDATE("[httpUpdate] - code: %d\n", code); + DEBUG_HTTP_UPDATE("[httpUpdate] - len: %d\n", len); + + if(http.hasHeader("x-MD5")) { + DEBUG_HTTP_UPDATE("[httpUpdate] - MD5: %s\n", http.header("x-MD5").c_str()); + } + + DEBUG_HTTP_UPDATE("[httpUpdate] ESP8266 info:\n"); + DEBUG_HTTP_UPDATE("[httpUpdate] - free Space: %d\n", ESP.getFreeSketchSpace()); + DEBUG_HTTP_UPDATE("[httpUpdate] - current Sketch Size: %d\n", ESP.getSketchSize()); + + if(currentVersion && currentVersion[0] != 0x00) { + DEBUG_HTTP_UPDATE("[httpUpdate] - current version: %s\n", currentVersion); + } + + switch(code) { + case HTTP_CODE_OK: ///< OK (Start Update) + if(len > 0) { + bool startUpdate = true; + if(spiffs) { + size_t spiffsSize = ((size_t) &_SPIFFS_end - (size_t) &_SPIFFS_start); + if(len > (int) spiffsSize) { + DEBUG_HTTP_UPDATE("[httpUpdate] spiffsSize to low (%d) needed: %d\n", spiffsSize, len); + startUpdate = false; + } + } else { + if(len > (int) ESP.getFreeSketchSpace()) { + DEBUG_HTTP_UPDATE("[httpUpdate] FreeSketchSpace to low (%d) needed: %d\n", ESP.getFreeSketchSpace(), len); + startUpdate = false; + } + } + + if(!startUpdate) { + _lastError = HTTP_UE_TOO_LESS_SPACE; + ret = HTTP_UPDATE_FAILED; + } else { + + WiFiClient * tcp = http.getStreamPtr(); + + WiFiUDP::stopAll(); + WiFiClient::stopAllExcept(tcp); + + delay(100); + + int command; + + if(spiffs) { + command = U_SPIFFS; + DEBUG_HTTP_UPDATE("[httpUpdate] runUpdate spiffs...\n"); + } else { + command = U_FLASH; + DEBUG_HTTP_UPDATE("[httpUpdate] runUpdate flash...\n"); + } + + if(!spiffs) { + uint8_t buf[4]; + if(tcp->peekBytes(&buf[0], 4) != 4) { + DEBUG_HTTP_UPDATE("[httpUpdate] peekBytes magic header failed\n"); + _lastError = HTTP_UE_BIN_VERIFY_HEADER_FAILED; + http.end(); + return HTTP_UPDATE_FAILED; + } + + // check for valid first magic byte + if(buf[0] != 0xE9) { + DEBUG_HTTP_UPDATE("[httpUpdate] magic header not starts with 0xE9\n"); + _lastError = HTTP_UE_BIN_VERIFY_HEADER_FAILED; + http.end(); + return HTTP_UPDATE_FAILED; + + } + + uint32_t bin_flash_size = ESP.magicFlashChipSize((buf[3] & 0xf0) >> 4); + + // check if new bin fits to SPI flash + if(bin_flash_size > ESP.getFlashChipRealSize()) { + DEBUG_HTTP_UPDATE("[httpUpdate] magic header, new bin not fits SPI Flash\n"); + _lastError = HTTP_UE_BIN_FOR_WRONG_FLASH; + http.end(); + return HTTP_UPDATE_FAILED; + } + } + + if(runUpdate(*tcp, len, http.header("x-MD5"), command)) { + ret = HTTP_UPDATE_OK; + DEBUG_HTTP_UPDATE("[httpUpdate] Update ok\n"); + http.end(); + + if(_rebootOnUpdate) { + ESP.restart(); + } + + } else { + ret = HTTP_UPDATE_FAILED; + DEBUG_HTTP_UPDATE("[httpUpdate] Update failed\n"); + } + } + } else { + _lastError = HTTP_UE_SERVER_NOT_REPORT_SIZE; + ret = HTTP_UPDATE_FAILED; + DEBUG_HTTP_UPDATE("[httpUpdate] Content-Length is 0 or not set by Server?!\n"); + } + break; + case HTTP_CODE_NOT_MODIFIED: + ///< Not Modified (No updates) + ret = HTTP_UPDATE_NO_UPDATES; + break; + case HTTP_CODE_NOT_FOUND: + _lastError = HTTP_UE_SERVER_FILE_NOT_FOUND; + ret = HTTP_UPDATE_FAILED; + break; + case HTTP_CODE_FORBIDDEN: + _lastError = HTTP_UE_SERVER_FORBIDDEN; + ret = HTTP_UPDATE_FAILED; + break; + default: + _lastError = HTTP_UE_SERVER_WRONG_HTTP_CODE; + ret = HTTP_UPDATE_FAILED; + DEBUG_HTTP_UPDATE("[httpUpdate] HTTP Code is (%d)\n", code); + //http.writeToStream(&Serial1); + break; + } + + http.end(); + return ret; +} + +/** + * write Update to flash + * @param in Stream& + * @param size uint32_t + * @param md5 String + * @return true if Update ok + */ +bool ESP8266HTTPUpdate::runUpdate(Stream& in, uint32_t size, String md5, int command) +{ + + StreamString error; + + if(!Update.begin(size, command)) { + _lastError = Update.getError(); + Update.printError(error); + error.trim(); // remove line ending + DEBUG_HTTP_UPDATE("[httpUpdate] Update.begin failed! (%s)\n", error.c_str()); + return false; + } + + if(md5.length()) { + if(!Update.setMD5(md5.c_str())) { + _lastError = HTTP_UE_SERVER_FAULTY_MD5; + DEBUG_HTTP_UPDATE("[httpUpdate] Update.setMD5 failed! (%s)\n", md5.c_str()); + return false; + } + } + + if(Update.writeStream(in) != size) { + _lastError = Update.getError(); + Update.printError(error); + error.trim(); // remove line ending + DEBUG_HTTP_UPDATE("[httpUpdate] Update.writeStream failed! (%s)\n", error.c_str()); + return false; + } + + if(!Update.end()) { + _lastError = Update.getError(); + Update.printError(error); + error.trim(); // remove line ending + DEBUG_HTTP_UPDATE("[httpUpdate] Update.end failed! (%s)\n", error.c_str()); + return false; + } + + return true; +} + + + +ESP8266HTTPUpdate ESPhttpUpdate; diff --git a/libraries/ESP8266httpUpdate/src/ESP8266httpUpdate.h b/libraries/ESP8266httpUpdate/src/ESP8266httpUpdate.h index 81f03385f..af6931b15 100644 --- a/libraries/ESP8266httpUpdate/src/ESP8266httpUpdate.h +++ b/libraries/ESP8266httpUpdate/src/ESP8266httpUpdate.h @@ -1,84 +1,110 @@ -/** - * - * @file ESP8266HTTPUpdate.h - * @date 21.06.2015 - * @author Markus Sattler - * - * Copyright (c) 2015 Markus Sattler. All rights reserved. - * This file is part of the ESP8266 Http Updater. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -#ifndef ESP8266HTTPUPDATE_H_ -#define ESP8266HTTPUPDATE_H_ - -#include -#include -#include -#include -#include - -#ifdef DEBUG_ESP_HTTP_UPDATE -#ifdef DEBUG_ESP_PORT -#define DEBUG_HTTP_UPDATE(...) DEBUG_ESP_PORT.printf( __VA_ARGS__ ) -#endif -#endif - -#ifndef DEBUG_HTTP_UPDATE -#define DEBUG_HTTP_UPDATE(...) -#endif - -/// note we use HTTP client errors too so we start at 100 -#define HTTP_UE_TOO_LESS_SPACE (-100) -#define HTTP_UE_SERVER_NOT_REPORT_SIZE (-101) -#define HTTP_UE_SERVER_FILE_NOT_FOUND (-102) -#define HTTP_UE_SERVER_FORBIDDEN (-103) -#define HTTP_UE_SERVER_WRONG_HTTP_CODE (-104) -#define HTTP_UE_SERVER_FAULTY_MD5 (-105) -#define HTTP_UE_BIN_VERIFY_HEADER_FAILED (-106) -#define HTTP_UE_BIN_FOR_WRONG_FLASH (-107) - -typedef enum { - HTTP_UPDATE_FAILED, - HTTP_UPDATE_NO_UPDATES, - HTTP_UPDATE_OK -} t_httpUpdate_return; - -class ESP8266HTTPUpdate { - public: - ESP8266HTTPUpdate(void); - ~ESP8266HTTPUpdate(void); - - t_httpUpdate_return update(const char * url, const char * current_version = "", const char * httpsFingerprint = "", bool reboot = true); - t_httpUpdate_return update(const char * host, uint16_t port, const char * url = "/", const char * current_version = "", bool https = false, const char * httpsFingerprint = "", bool reboot = true); - t_httpUpdate_return update(String host, uint16_t port, String url = "/", String current_version = "", bool https = false, String httpsFingerprint = "", bool reboot = true); - - t_httpUpdate_return updateSpiffs(const char * url, const char * current_version = "", const char * httpsFingerprint = "", bool reboot = false); - - int getLastError(void); - String getLastErrorString(void); - - protected: - t_httpUpdate_return handleUpdate(HTTPClient * http, const char * current_version, bool reboot = true, bool spiffs = false); - bool runUpdate(Stream& in, uint32_t size, String md5, int command = U_FLASH); - - int lastError; -}; - -extern ESP8266HTTPUpdate ESPhttpUpdate; - -#endif /* ESP8266HTTPUPDATE_H_ */ +/** + * + * @file ESP8266HTTPUpdate.h + * @date 21.06.2015 + * @author Markus Sattler + * + * Copyright (c) 2015 Markus Sattler. All rights reserved. + * This file is part of the ESP8266 Http Updater. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef ESP8266HTTPUPDATE_H_ +#define ESP8266HTTPUPDATE_H_ + +#include +#include +#include +#include +#include + +#ifdef DEBUG_ESP_HTTP_UPDATE +#ifdef DEBUG_ESP_PORT +#define DEBUG_HTTP_UPDATE(...) DEBUG_ESP_PORT.printf( __VA_ARGS__ ) +#endif +#endif + +#ifndef DEBUG_HTTP_UPDATE +#define DEBUG_HTTP_UPDATE(...) +#endif + +/// note we use HTTP client errors too so we start at 100 +#define HTTP_UE_TOO_LESS_SPACE (-100) +#define HTTP_UE_SERVER_NOT_REPORT_SIZE (-101) +#define HTTP_UE_SERVER_FILE_NOT_FOUND (-102) +#define HTTP_UE_SERVER_FORBIDDEN (-103) +#define HTTP_UE_SERVER_WRONG_HTTP_CODE (-104) +#define HTTP_UE_SERVER_FAULTY_MD5 (-105) +#define HTTP_UE_BIN_VERIFY_HEADER_FAILED (-106) +#define HTTP_UE_BIN_FOR_WRONG_FLASH (-107) + +enum HTTPUpdateResult { + HTTP_UPDATE_FAILED, + HTTP_UPDATE_NO_UPDATES, + HTTP_UPDATE_OK +}; + +typedef HTTPUpdateResult t_httpUpdate_return; // backward compatibility + +class ESP8266HTTPUpdate +{ +public: + ESP8266HTTPUpdate(void); + ~ESP8266HTTPUpdate(void); + + void rebootOnUpdate(bool reboot) + { + _rebootOnUpdate = reboot; + } + + // This function is deprecated, use rebootOnUpdate and the next one instead + t_httpUpdate_return update(const String& url, const String& currentVersion, + const String& httpsFingerprint, bool reboot) __attribute__((deprecated)); + t_httpUpdate_return update(const String& url, const String& currentVersion = ""); + t_httpUpdate_return update(const String& url, const String& currentVersion, + const String& httpsFingerprint); + + // This function is deprecated, use one of the overloads below along with rebootOnUpdate + t_httpUpdate_return update(const String& host, uint16_t port, const String& uri, const String& currentVersion, + bool https, const String& httpsFingerprint, bool reboot) __attribute__((deprecated)); + + t_httpUpdate_return update(const String& host, uint16_t port, const String& uri = "/", + const String& currentVersion = ""); + t_httpUpdate_return update(const String& host, uint16_t port, const String& url, + const String& currentVersion, const String& httpsFingerprint); + + // This function is deprecated, use rebootOnUpdate and the next one instead + t_httpUpdate_return updateSpiffs(const String& url, const String& currentVersion, + const String& httpsFingerprint, bool reboot) __attribute__((deprecated)); + t_httpUpdate_return updateSpiffs(const String& url, const String& currentVersion = ""); + t_httpUpdate_return updateSpiffs(const String& url, const String& currentVersion, const String& httpsFingerprint); + + + int getLastError(void); + String getLastErrorString(void); + +protected: + t_httpUpdate_return handleUpdate(HTTPClient& http, const String& currentVersion, bool spiffs = false); + bool runUpdate(Stream& in, uint32_t size, String md5, int command = U_FLASH); + + int _lastError; + bool _rebootOnUpdate = true; +}; + +extern ESP8266HTTPUpdate ESPhttpUpdate; + +#endif /* ESP8266HTTPUPDATE_H_ */