diff --git a/cores/esp8266/WString.cpp b/cores/esp8266/WString.cpp index edc8ab8b54..51c91238e2 100644 --- a/cores/esp8266/WString.cpp +++ b/cores/esp8266/WString.cpp @@ -796,3 +796,7 @@ float String::toFloat(void) const { return atof(buffer); return 0; } + +// global empty string to allow returning const String& with nothing + +const String emptyString; diff --git a/cores/esp8266/WString.h b/cores/esp8266/WString.h index fbf3c59b2f..bbf8ee40ad 100644 --- a/cores/esp8266/WString.h +++ b/cores/esp8266/WString.h @@ -294,5 +294,7 @@ class StringSumHelper: public String { } }; +extern const String emptyString; + #endif // __cplusplus #endif // String_class_h diff --git a/libraries/ESP8266WebServer/src/ESP8266WebServer.cpp b/libraries/ESP8266WebServer/src/ESP8266WebServer.cpp index 62de8546be..aec3f732d6 100644 --- a/libraries/ESP8266WebServer/src/ESP8266WebServer.cpp +++ b/libraries/ESP8266WebServer/src/ESP8266WebServer.cpp @@ -100,10 +100,10 @@ void ESP8266WebServer::begin(uint16_t port) { _server.begin(port); } -String ESP8266WebServer::_extractParam(String& authReq,const String& param,const char delimit){ +String ESP8266WebServer::_extractParam(String& authReq,const String& param,const char delimit) const { int _begin = authReq.indexOf(param); - if (_begin == -1) - return ""; + if (_begin == -1) + return emptyString; return authReq.substring(_begin+param.length(),authReq.indexOf(delimit,_begin+param.length())); } @@ -487,35 +487,35 @@ void ESP8266WebServer::_streamFileCore(const size_t fileSize, const String & fil contentType != String(FPSTR(mimeTable[none].mimeType))) { sendHeader(F("Content-Encoding"), F("gzip")); } - send(200, contentType, ""); + send(200, contentType, emptyString); } -String ESP8266WebServer::arg(String name) { +const String& ESP8266WebServer::arg(String name) const { for (int i = 0; i < _currentArgCount; ++i) { if ( _currentArgs[i].key == name ) return _currentArgs[i].value; } - return ""; + return emptyString; } -String ESP8266WebServer::arg(int i) { +const String& ESP8266WebServer::arg(int i) const { if (i >= 0 && i < _currentArgCount) return _currentArgs[i].value; - return ""; + return emptyString; } -String ESP8266WebServer::argName(int i) { +const String& ESP8266WebServer::argName(int i) const { if (i >= 0 && i < _currentArgCount) return _currentArgs[i].key; - return ""; + return emptyString; } -int ESP8266WebServer::args() { +int ESP8266WebServer::args() const { return _currentArgCount; } -bool ESP8266WebServer::hasArg(String name) { +bool ESP8266WebServer::hasArg(const String& name) const { for (int i = 0; i < _currentArgCount; ++i) { if (_currentArgs[i].key == name) return true; @@ -524,12 +524,12 @@ bool ESP8266WebServer::hasArg(String name) { } -String ESP8266WebServer::header(String name) { +const String& ESP8266WebServer::header(String name) const { for (int i = 0; i < _headerKeysCount; ++i) { if (_currentHeaders[i].key.equalsIgnoreCase(name)) return _currentHeaders[i].value; } - return ""; + return emptyString; } void ESP8266WebServer::collectHeaders(const char* headerKeys[], const size_t headerKeysCount) { @@ -543,23 +543,23 @@ void ESP8266WebServer::collectHeaders(const char* headerKeys[], const size_t hea } } -String ESP8266WebServer::header(int i) { +const String& ESP8266WebServer::header(int i) const { if (i < _headerKeysCount) return _currentHeaders[i].value; - return ""; + return emptyString; } -String ESP8266WebServer::headerName(int i) { +const String& ESP8266WebServer::headerName(int i) const { if (i < _headerKeysCount) return _currentHeaders[i].key; - return ""; + return emptyString; } -int ESP8266WebServer::headers() { +int ESP8266WebServer::headers() const { return _headerKeysCount; } -bool ESP8266WebServer::hasHeader(String name) { +bool ESP8266WebServer::hasHeader(String name) const { for (int i = 0; i < _headerKeysCount; ++i) { if ((_currentHeaders[i].key.equalsIgnoreCase(name)) && (_currentHeaders[i].value.length() > 0)) return true; @@ -567,7 +567,7 @@ bool ESP8266WebServer::hasHeader(String name) { return false; } -String ESP8266WebServer::hostHeader() { +const String& ESP8266WebServer::hostHeader() const { return _hostHeader; } @@ -612,11 +612,11 @@ void ESP8266WebServer::_handleRequest() { void ESP8266WebServer::_finalizeResponse() { if (_chunked) { - sendContent(""); + sendContent(emptyString); } } -String ESP8266WebServer::_responseCodeToString(int code) { +const String ESP8266WebServer::_responseCodeToString(int code) { switch (code) { case 100: return F("Continue"); case 101: return F("Switching Protocols"); diff --git a/libraries/ESP8266WebServer/src/ESP8266WebServer.h b/libraries/ESP8266WebServer/src/ESP8266WebServer.h index 9dbabff0c4..6ad50deafa 100644 --- a/libraries/ESP8266WebServer/src/ESP8266WebServer.h +++ b/libraries/ESP8266WebServer/src/ESP8266WebServer.h @@ -92,24 +92,23 @@ class ESP8266WebServer void onNotFound(THandlerFunction fn); //called when handler is not assigned void onFileUpload(THandlerFunction fn); //handle file uploads - String uri() { return _currentUri; } - HTTPMethod method() { return _currentMethod; } + const String& uri() const { return _currentUri; } + HTTPMethod method() const { return _currentMethod; } virtual WiFiClient client() { return _currentClient; } HTTPUpload& upload() { return *_currentUpload; } - String arg(String name); // get request argument value by name - String arg(int i); // get request argument value by number - String argName(int i); // get request argument name by number - int args(); // get arguments count - bool hasArg(String name); // check if argument exists + const String& arg(String name) const; // get request argument value by name + const String& arg(int i) const; // get request argument value by number + const String& argName(int i) const; // get request argument name by number + int args() const; // get arguments count + bool hasArg(const String& name) const; // check if argument exists void collectHeaders(const char* headerKeys[], const size_t headerKeysCount); // set the request headers to collect - String header(String name); // get request header value by name - String header(int i); // get request header value by number - String headerName(int i); // get request header name by number - int headers(); // get header count - bool hasHeader(String name); // check if header exists - - String hostHeader(); // get request host header if available or empty String if not + const String& header(String name) const; // get request header value by name + const String& header(int i) const; // get request header value by number + const String& headerName(int i) const; // get request header name by number + int headers() const; // get header count + bool hasHeader(String name) const; // check if header exists + const String& hostHeader() const; // get request host header if available or empty String if not // send response to the client // code - HTTP response code, can be 200 or 404 @@ -129,12 +128,12 @@ class ESP8266WebServer static String urlDecode(const String& text); - template + template size_t streamFile(T &file, const String& contentType) { _streamFileCore(file.size(), file.name(), contentType); return _currentClient.write(file); } - + protected: virtual size_t _currentClientWrite(const char* b, size_t l) { return _currentClient.write( b, l ); } virtual size_t _currentClientWrite_P(PGM_P b, size_t l) { return _currentClient.write_P( b, l ); } @@ -144,19 +143,19 @@ class ESP8266WebServer bool _parseRequest(WiFiClient& client); void _parseArguments(const String& data); int _parseArgumentsPrivate(const String& data, std::function handler); - static String _responseCodeToString(int code); - bool _parseForm(WiFiClient& client, String boundary, uint32_t len); + static const String _responseCodeToString(int code); + bool _parseForm(WiFiClient& client, const String& boundary, uint32_t len); bool _parseFormUploadAborted(); void _uploadWriteByte(uint8_t b); uint8_t _uploadReadByte(WiFiClient& client); void _prepareHeader(String& response, int code, const char* content_type, size_t contentLength); bool _collectHeader(const char* headerName, const char* headerValue); - + void _streamFileCore(const size_t fileSize, const String & fileName, const String & contentType); - String _getRandomHexString(); + static String _getRandomHexString(); // for extracting Auth parameters - String _extractParam(String& authReq,const String& param,const char delimit = '"'); + String _extractParam(String& authReq,const String& param,const char delimit = '"') const; struct RequestArgument { String key; diff --git a/libraries/ESP8266WebServer/src/Parsing.cpp b/libraries/ESP8266WebServer/src/Parsing.cpp index 3e4c48b9a0..08bb07b4a8 100644 --- a/libraries/ESP8266WebServer/src/Parsing.cpp +++ b/libraries/ESP8266WebServer/src/Parsing.cpp @@ -35,41 +35,33 @@ static const char Content_Type[] PROGMEM = "Content-Type"; static const char filename[] PROGMEM = "filename"; -static char* readBytesWithTimeout(WiFiClient& client, size_t maxLength, size_t& dataLength, int timeout_ms) +static bool readBytesWithTimeout(WiFiClient& client, size_t maxLength, String& data, int timeout_ms) { - char *buf = nullptr; - dataLength = 0; - while (dataLength < maxLength) { + if (!data.reserve(maxLength + 1)) + return false; + data[0] = 0; // data.clear()?? + while (data.length() < maxLength) { int tries = timeout_ms; - size_t newLength; - while (!(newLength = client.available()) && tries--) delay(1); - if (!newLength) { + size_t avail; + while (!(avail = client.available()) && tries--) + delay(1); + if (!avail) break; - } - if (!buf) { - buf = (char *) malloc(newLength + 1); - if (!buf) { - return nullptr; - } - } - else { - char* newBuf = (char *) realloc(buf, dataLength + newLength + 1); - if (!newBuf) { - free(buf); - return nullptr; - } - buf = newBuf; - } - client.readBytes(buf + dataLength, newLength); - dataLength += newLength; - buf[dataLength] = '\0'; + if (data.length() + avail > maxLength) + avail = maxLength - data.length(); + while (avail--) + data += (char)client.read(); } - return buf; + return data.length() == maxLength; } bool ESP8266WebServer::_parseRequest(WiFiClient& client) { // Read the first line of HTTP request String req = client.readStringUntil('\r'); +#ifdef DEBUG_ESP_HTTP_SERVER + DEBUG_OUTPUT.print("request: "); + DEBUG_OUTPUT.println(req); +#endif client.readStringUntil('\n'); //reset header value for (int i = 0; i < _headerKeysCount; ++i) { @@ -82,8 +74,7 @@ bool ESP8266WebServer::_parseRequest(WiFiClient& client) { int addr_end = req.indexOf(' ', addr_start + 1); if (addr_start == -1 || addr_end == -1) { #ifdef DEBUG_ESP_HTTP_SERVER - DEBUG_OUTPUT.print("Invalid request: "); - DEBUG_OUTPUT.println(req); + DEBUG_OUTPUT.println("Invalid request"); #endif return false; } @@ -139,7 +130,7 @@ bool ESP8266WebServer::_parseRequest(WiFiClient& client) { String headerName; String headerValue; bool isForm = false; - //bool isEncoded = false; + bool isEncoded = false; uint32_t contentLength = 0; //parse headers while(1){ @@ -168,7 +159,7 @@ bool ESP8266WebServer::_parseRequest(WiFiClient& client) { isForm = false; } else if (headerValue.startsWith(F("application/x-www-form-urlencoded"))){ isForm = false; - //isEncoded = true; + isEncoded = true; } else if (headerValue.startsWith(F("multipart/"))){ boundaryStr = headerValue.substring(headerValue.indexOf('=') + 1); boundaryStr.replace("\"",""); @@ -181,34 +172,40 @@ bool ESP8266WebServer::_parseRequest(WiFiClient& client) { } } - // always parse url for key/value pairs + String plainBuf; + if ( !isForm + && // read content into plainBuf + ( !readBytesWithTimeout(client, contentLength, plainBuf, HTTP_MAX_POST_WAIT) + || (plainBuf.length() < contentLength) + ) + ) + { + return false; + } + + if (isEncoded) { + // isEncoded => !isForm => plainBuf is not empty + // add plainBuf in search str + if (searchStr.length()) + searchStr += '&'; + searchStr += plainBuf; + } + + // parse searchStr for key/value pairs _parseArguments(searchStr); if (!isForm) { if (contentLength) { - // add key=value: plain={body} (post json or other data) - - size_t plainLength; - char* plainBuf = readBytesWithTimeout(client, contentLength, plainLength, HTTP_MAX_POST_WAIT); - if (plainLength < contentLength) { - free(plainBuf); - return false; - } - RequestArgument& arg = _currentArgs[_currentArgCount++]; arg.key = F("plain"); - arg.value = String(plainBuf); - - free(plainBuf); - + arg.value = plainBuf; } } else { // isForm is true - + // here: content is not yet read (plainBuf is still empty) if (!_parseForm(client, boundaryStr, contentLength)) { return false; } - } } else { String headerName; @@ -368,7 +365,7 @@ uint8_t ESP8266WebServer::_uploadReadByte(WiFiClient& client){ return (uint8_t)res; } -bool ESP8266WebServer::_parseForm(WiFiClient& client, String boundary, uint32_t len){ +bool ESP8266WebServer::_parseForm(WiFiClient& client, const String& boundary, uint32_t len){ (void) len; #ifdef DEBUG_ESP_HTTP_SERVER DEBUG_OUTPUT.print("Parse Form: Boundary: ");