1
0
mirror of https://github.com/esp8266/Arduino.git synced 2025-06-06 05:21:22 +03:00

fix http parsing (#5262)

* follows #5252
* use const refs where relevant (aka stop being nasty with ram and cpu)
This commit is contained in:
david gauchard 2018-10-23 22:17:54 +02:00 committed by GitHub
parent e954022b94
commit a063c2b36f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 93 additions and 91 deletions

View File

@ -796,3 +796,7 @@ float String::toFloat(void) const {
return atof(buffer); return atof(buffer);
return 0; return 0;
} }
// global empty string to allow returning const String& with nothing
const String emptyString;

View File

@ -294,5 +294,7 @@ class StringSumHelper: public String {
} }
}; };
extern const String emptyString;
#endif // __cplusplus #endif // __cplusplus
#endif // String_class_h #endif // String_class_h

View File

@ -100,10 +100,10 @@ void ESP8266WebServer::begin(uint16_t port) {
_server.begin(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); int _begin = authReq.indexOf(param);
if (_begin == -1) if (_begin == -1)
return ""; return emptyString;
return authReq.substring(_begin+param.length(),authReq.indexOf(delimit,_begin+param.length())); 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))) { contentType != String(FPSTR(mimeTable[none].mimeType))) {
sendHeader(F("Content-Encoding"), F("gzip")); 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) { for (int i = 0; i < _currentArgCount; ++i) {
if ( _currentArgs[i].key == name ) if ( _currentArgs[i].key == name )
return _currentArgs[i].value; return _currentArgs[i].value;
} }
return ""; return emptyString;
} }
String ESP8266WebServer::arg(int i) { const String& ESP8266WebServer::arg(int i) const {
if (i >= 0 && i < _currentArgCount) if (i >= 0 && i < _currentArgCount)
return _currentArgs[i].value; return _currentArgs[i].value;
return ""; return emptyString;
} }
String ESP8266WebServer::argName(int i) { const String& ESP8266WebServer::argName(int i) const {
if (i >= 0 && i < _currentArgCount) if (i >= 0 && i < _currentArgCount)
return _currentArgs[i].key; return _currentArgs[i].key;
return ""; return emptyString;
} }
int ESP8266WebServer::args() { int ESP8266WebServer::args() const {
return _currentArgCount; return _currentArgCount;
} }
bool ESP8266WebServer::hasArg(String name) { bool ESP8266WebServer::hasArg(const String& name) const {
for (int i = 0; i < _currentArgCount; ++i) { for (int i = 0; i < _currentArgCount; ++i) {
if (_currentArgs[i].key == name) if (_currentArgs[i].key == name)
return true; 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) { for (int i = 0; i < _headerKeysCount; ++i) {
if (_currentHeaders[i].key.equalsIgnoreCase(name)) if (_currentHeaders[i].key.equalsIgnoreCase(name))
return _currentHeaders[i].value; return _currentHeaders[i].value;
} }
return ""; return emptyString;
} }
void ESP8266WebServer::collectHeaders(const char* headerKeys[], const size_t headerKeysCount) { 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) if (i < _headerKeysCount)
return _currentHeaders[i].value; return _currentHeaders[i].value;
return ""; return emptyString;
} }
String ESP8266WebServer::headerName(int i) { const String& ESP8266WebServer::headerName(int i) const {
if (i < _headerKeysCount) if (i < _headerKeysCount)
return _currentHeaders[i].key; return _currentHeaders[i].key;
return ""; return emptyString;
} }
int ESP8266WebServer::headers() { int ESP8266WebServer::headers() const {
return _headerKeysCount; return _headerKeysCount;
} }
bool ESP8266WebServer::hasHeader(String name) { bool ESP8266WebServer::hasHeader(String name) const {
for (int i = 0; i < _headerKeysCount; ++i) { for (int i = 0; i < _headerKeysCount; ++i) {
if ((_currentHeaders[i].key.equalsIgnoreCase(name)) && (_currentHeaders[i].value.length() > 0)) if ((_currentHeaders[i].key.equalsIgnoreCase(name)) && (_currentHeaders[i].value.length() > 0))
return true; return true;
@ -567,7 +567,7 @@ bool ESP8266WebServer::hasHeader(String name) {
return false; return false;
} }
String ESP8266WebServer::hostHeader() { const String& ESP8266WebServer::hostHeader() const {
return _hostHeader; return _hostHeader;
} }
@ -612,11 +612,11 @@ void ESP8266WebServer::_handleRequest() {
void ESP8266WebServer::_finalizeResponse() { void ESP8266WebServer::_finalizeResponse() {
if (_chunked) { if (_chunked) {
sendContent(""); sendContent(emptyString);
} }
} }
String ESP8266WebServer::_responseCodeToString(int code) { const String ESP8266WebServer::_responseCodeToString(int code) {
switch (code) { switch (code) {
case 100: return F("Continue"); case 100: return F("Continue");
case 101: return F("Switching Protocols"); case 101: return F("Switching Protocols");

View File

@ -92,24 +92,23 @@ public:
void onNotFound(THandlerFunction fn); //called when handler is not assigned void onNotFound(THandlerFunction fn); //called when handler is not assigned
void onFileUpload(THandlerFunction fn); //handle file uploads void onFileUpload(THandlerFunction fn); //handle file uploads
String uri() { return _currentUri; } const String& uri() const { return _currentUri; }
HTTPMethod method() { return _currentMethod; } HTTPMethod method() const { return _currentMethod; }
virtual WiFiClient client() { return _currentClient; } virtual WiFiClient client() { return _currentClient; }
HTTPUpload& upload() { return *_currentUpload; } HTTPUpload& upload() { return *_currentUpload; }
String arg(String name); // get request argument value by name const String& arg(String name) const; // get request argument value by name
String arg(int i); // get request argument value by number const String& arg(int i) const; // get request argument value by number
String argName(int i); // get request argument name by number const String& argName(int i) const; // get request argument name by number
int args(); // get arguments count int args() const; // get arguments count
bool hasArg(String name); // check if argument exists 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 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 const String& header(String name) const; // get request header value by name
String header(int i); // get request header value by number const String& header(int i) const; // get request header value by number
String headerName(int i); // get request header name by number const String& headerName(int i) const; // get request header name by number
int headers(); // get header count int headers() const; // get header count
bool hasHeader(String name); // check if header exists bool hasHeader(String name) const; // check if header exists
const String& hostHeader() const; // get request host header if available or empty String if not
String hostHeader(); // get request host header if available or empty String if not
// send response to the client // send response to the client
// code - HTTP response code, can be 200 or 404 // code - HTTP response code, can be 200 or 404
@ -129,12 +128,12 @@ public:
static String urlDecode(const String& text); static String urlDecode(const String& text);
template<typename T> template<typename T>
size_t streamFile(T &file, const String& contentType) { size_t streamFile(T &file, const String& contentType) {
_streamFileCore(file.size(), file.name(), contentType); _streamFileCore(file.size(), file.name(), contentType);
return _currentClient.write(file); return _currentClient.write(file);
} }
protected: protected:
virtual size_t _currentClientWrite(const char* b, size_t l) { return _currentClient.write( b, l ); } 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 ); } virtual size_t _currentClientWrite_P(PGM_P b, size_t l) { return _currentClient.write_P( b, l ); }
@ -144,19 +143,19 @@ protected:
bool _parseRequest(WiFiClient& client); bool _parseRequest(WiFiClient& client);
void _parseArguments(const String& data); void _parseArguments(const String& data);
int _parseArgumentsPrivate(const String& data, std::function<void(String&,String&,const String&,int,int,int,int)> handler); int _parseArgumentsPrivate(const String& data, std::function<void(String&,String&,const String&,int,int,int,int)> handler);
static String _responseCodeToString(int code); static const String _responseCodeToString(int code);
bool _parseForm(WiFiClient& client, String boundary, uint32_t len); bool _parseForm(WiFiClient& client, const String& boundary, uint32_t len);
bool _parseFormUploadAborted(); bool _parseFormUploadAborted();
void _uploadWriteByte(uint8_t b); void _uploadWriteByte(uint8_t b);
uint8_t _uploadReadByte(WiFiClient& client); uint8_t _uploadReadByte(WiFiClient& client);
void _prepareHeader(String& response, int code, const char* content_type, size_t contentLength); void _prepareHeader(String& response, int code, const char* content_type, size_t contentLength);
bool _collectHeader(const char* headerName, const char* headerValue); bool _collectHeader(const char* headerName, const char* headerValue);
void _streamFileCore(const size_t fileSize, const String & fileName, const String & contentType); void _streamFileCore(const size_t fileSize, const String & fileName, const String & contentType);
String _getRandomHexString(); static String _getRandomHexString();
// for extracting Auth parameters // 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 { struct RequestArgument {
String key; String key;

View File

@ -35,41 +35,33 @@
static const char Content_Type[] PROGMEM = "Content-Type"; static const char Content_Type[] PROGMEM = "Content-Type";
static const char filename[] PROGMEM = "filename"; 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; if (!data.reserve(maxLength + 1))
dataLength = 0; return false;
while (dataLength < maxLength) { data[0] = 0; // data.clear()??
while (data.length() < maxLength) {
int tries = timeout_ms; int tries = timeout_ms;
size_t newLength; size_t avail;
while (!(newLength = client.available()) && tries--) delay(1); while (!(avail = client.available()) && tries--)
if (!newLength) { delay(1);
if (!avail)
break; break;
} if (data.length() + avail > maxLength)
if (!buf) { avail = maxLength - data.length();
buf = (char *) malloc(newLength + 1); while (avail--)
if (!buf) { data += (char)client.read();
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';
} }
return buf; return data.length() == maxLength;
} }
bool ESP8266WebServer::_parseRequest(WiFiClient& client) { bool ESP8266WebServer::_parseRequest(WiFiClient& client) {
// Read the first line of HTTP request // Read the first line of HTTP request
String req = client.readStringUntil('\r'); String req = client.readStringUntil('\r');
#ifdef DEBUG_ESP_HTTP_SERVER
DEBUG_OUTPUT.print("request: ");
DEBUG_OUTPUT.println(req);
#endif
client.readStringUntil('\n'); client.readStringUntil('\n');
//reset header value //reset header value
for (int i = 0; i < _headerKeysCount; ++i) { for (int i = 0; i < _headerKeysCount; ++i) {
@ -82,8 +74,7 @@ bool ESP8266WebServer::_parseRequest(WiFiClient& client) {
int addr_end = req.indexOf(' ', addr_start + 1); int addr_end = req.indexOf(' ', addr_start + 1);
if (addr_start == -1 || addr_end == -1) { if (addr_start == -1 || addr_end == -1) {
#ifdef DEBUG_ESP_HTTP_SERVER #ifdef DEBUG_ESP_HTTP_SERVER
DEBUG_OUTPUT.print("Invalid request: "); DEBUG_OUTPUT.println("Invalid request");
DEBUG_OUTPUT.println(req);
#endif #endif
return false; return false;
} }
@ -139,7 +130,7 @@ bool ESP8266WebServer::_parseRequest(WiFiClient& client) {
String headerName; String headerName;
String headerValue; String headerValue;
bool isForm = false; bool isForm = false;
//bool isEncoded = false; bool isEncoded = false;
uint32_t contentLength = 0; uint32_t contentLength = 0;
//parse headers //parse headers
while(1){ while(1){
@ -168,7 +159,7 @@ bool ESP8266WebServer::_parseRequest(WiFiClient& client) {
isForm = false; isForm = false;
} else if (headerValue.startsWith(F("application/x-www-form-urlencoded"))){ } else if (headerValue.startsWith(F("application/x-www-form-urlencoded"))){
isForm = false; isForm = false;
//isEncoded = true; isEncoded = true;
} else if (headerValue.startsWith(F("multipart/"))){ } else if (headerValue.startsWith(F("multipart/"))){
boundaryStr = headerValue.substring(headerValue.indexOf('=') + 1); boundaryStr = headerValue.substring(headerValue.indexOf('=') + 1);
boundaryStr.replace("\"",""); 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); _parseArguments(searchStr);
if (!isForm) { if (!isForm) {
if (contentLength) { if (contentLength) {
// add key=value: plain={body} (post json or other data) // 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++]; RequestArgument& arg = _currentArgs[_currentArgCount++];
arg.key = F("plain"); arg.key = F("plain");
arg.value = String(plainBuf); arg.value = plainBuf;
free(plainBuf);
} }
} else { // isForm is true } else { // isForm is true
// here: content is not yet read (plainBuf is still empty)
if (!_parseForm(client, boundaryStr, contentLength)) { if (!_parseForm(client, boundaryStr, contentLength)) {
return false; return false;
} }
} }
} else { } else {
String headerName; String headerName;
@ -368,7 +365,7 @@ uint8_t ESP8266WebServer::_uploadReadByte(WiFiClient& client){
return (uint8_t)res; 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; (void) len;
#ifdef DEBUG_ESP_HTTP_SERVER #ifdef DEBUG_ESP_HTTP_SERVER
DEBUG_OUTPUT.print("Parse Form: Boundary: "); DEBUG_OUTPUT.print("Parse Form: Boundary: ");