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:
parent
e954022b94
commit
a063c2b36f
@ -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;
|
||||||
|
@ -294,5 +294,7 @@ class StringSumHelper: public String {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
extern const String emptyString;
|
||||||
|
|
||||||
#endif // __cplusplus
|
#endif // __cplusplus
|
||||||
#endif // String_class_h
|
#endif // String_class_h
|
||||||
|
@ -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");
|
||||||
|
@ -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;
|
||||||
|
@ -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: ");
|
||||||
|
Loading…
x
Reference in New Issue
Block a user