Fix POST form parser edge cases (#2182)
From https://github.com/espressif/arduino-esp32/pull/9167
This commit is contained in:
parent
a3dba8be5d
commit
2043623ce6
1 changed files with 60 additions and 125 deletions
|
|
@ -358,42 +358,13 @@ void HTTPServer::_uploadWriteByte(uint8_t b) {
|
|||
|
||||
int HTTPServer::_uploadReadByte(WiFiClient * client) {
|
||||
int res = client->read();
|
||||
|
||||
if (res < 0) {
|
||||
// keep trying until you either read a valid byte or timeout
|
||||
unsigned long startMillis = millis();
|
||||
unsigned long timeoutIntervalMillis = client->getTimeout();
|
||||
bool timedOut = false;
|
||||
for (;;) {
|
||||
if (!client->connected()) {
|
||||
return -1;
|
||||
}
|
||||
// loosely modeled after blinkWithoutDelay pattern
|
||||
while (!timedOut && !client->available() && client->connected()) {
|
||||
while (!client->available() && client->connected()) {
|
||||
delay(2);
|
||||
timedOut = millis() - startMillis >= timeoutIntervalMillis;
|
||||
}
|
||||
|
||||
res = client->read();
|
||||
if (res >= 0) {
|
||||
return res; // exit on a valid read
|
||||
}
|
||||
// NOTE: it is possible to get here and have all of the following
|
||||
// assertions hold true
|
||||
//
|
||||
// -- client->available() > 0
|
||||
// -- client->connected == true
|
||||
// -- res == -1
|
||||
//
|
||||
// a simple retry strategy overcomes this which is to say the
|
||||
// assertion is not permanent, but the reason that this works
|
||||
// is elusive, and possibly indicative of a more subtle underlying
|
||||
// issue
|
||||
|
||||
timedOut = millis() - startMillis >= timeoutIntervalMillis;
|
||||
if (timedOut) {
|
||||
return res; // exit on a timeout
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
|
|
@ -448,10 +419,12 @@ bool HTTPServer::_parseForm(WiFiClient * client, String boundary, uint32_t len)
|
|||
argType = FPSTR(mimeTable[txt].mimeType);
|
||||
line = client->readStringUntil('\r');
|
||||
client->readStringUntil('\n');
|
||||
while (line.length() > 0) {
|
||||
if (line.length() > 12 && line.substring(0, 12).equalsIgnoreCase(FPSTR(Content_Type))) {
|
||||
argType = line.substring(line.indexOf(':') + 2);
|
||||
//skip next line
|
||||
client->readStringUntil('\r');
|
||||
}
|
||||
//skip over any other headers
|
||||
line = client->readStringUntil('\r');
|
||||
client->readStringUntil('\n');
|
||||
}
|
||||
log_v("PostArg Type: %s", argType.c_str());
|
||||
|
|
@ -493,66 +466,41 @@ bool HTTPServer::_parseForm(WiFiClient * client, String boundary, uint32_t len)
|
|||
_currentHandler->upload(*this, _currentUri, *_currentUpload);
|
||||
}
|
||||
_currentUpload->status = UPLOAD_FILE_WRITE;
|
||||
int argByte = _uploadReadByte(client);
|
||||
readfile:
|
||||
|
||||
while (argByte != 0x0D) {
|
||||
if (argByte < 0) {
|
||||
int fastBoundaryLen = 4 /* \r\n-- */ + boundary.length() + 1 /* \0 */;
|
||||
char fastBoundary[fastBoundaryLen];
|
||||
snprintf(fastBoundary, fastBoundaryLen, "\r\n--%s", boundary.c_str());
|
||||
int boundaryPtr = 0;
|
||||
while (true) {
|
||||
int ret = _uploadReadByte(client);
|
||||
if (ret < 0) {
|
||||
// Unexpected, we should have had data available per above
|
||||
return _parseFormUploadAborted();
|
||||
}
|
||||
_uploadWriteByte(argByte);
|
||||
argByte = _uploadReadByte(client);
|
||||
char in = (char)ret;
|
||||
if (in == fastBoundary[boundaryPtr]) {
|
||||
// The input matched the current expected character, advance and possibly exit this file
|
||||
boundaryPtr++;
|
||||
if (boundaryPtr == fastBoundaryLen - 1) {
|
||||
// We read the whole boundary line, we're done here!
|
||||
break;
|
||||
}
|
||||
|
||||
argByte = _uploadReadByte(client);
|
||||
if (argByte < 0) {
|
||||
return _parseFormUploadAborted();
|
||||
}
|
||||
if (argByte == 0x0A) {
|
||||
argByte = _uploadReadByte(client);
|
||||
if (argByte < 0) {
|
||||
return _parseFormUploadAborted();
|
||||
}
|
||||
if ((char)argByte != '-') {
|
||||
//continue reading the file
|
||||
_uploadWriteByte(0x0D);
|
||||
_uploadWriteByte(0x0A);
|
||||
goto readfile;
|
||||
} else {
|
||||
argByte = _uploadReadByte(client);
|
||||
if (argByte < 0) {
|
||||
return _parseFormUploadAborted();
|
||||
// The char doesn't match what we want, so dump whatever matches we had, the read in char, and reset ptr to start
|
||||
for (int i = 0; i < boundaryPtr; i++) {
|
||||
_uploadWriteByte(fastBoundary[i]);
|
||||
}
|
||||
if ((char)argByte != '-') {
|
||||
//continue reading the file
|
||||
_uploadWriteByte(0x0D);
|
||||
_uploadWriteByte(0x0A);
|
||||
_uploadWriteByte((uint8_t)('-'));
|
||||
goto readfile;
|
||||
if (in == fastBoundary[0]) {
|
||||
// This could be the start of the real end, mark it so and don't emit/skip it
|
||||
boundaryPtr = 1;
|
||||
} else {
|
||||
// Not the 1st char of our pattern, so emit and ignore
|
||||
_uploadWriteByte(in);
|
||||
boundaryPtr = 0;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t endBuf[boundary.length()];
|
||||
uint32_t i = 0;
|
||||
while (i < boundary.length()) {
|
||||
argByte = _uploadReadByte(client);
|
||||
if (argByte < 0) {
|
||||
return _parseFormUploadAborted();
|
||||
}
|
||||
if ((char)argByte == 0x0D) {
|
||||
_uploadWriteByte(0x0D);
|
||||
_uploadWriteByte(0x0A);
|
||||
_uploadWriteByte((uint8_t)('-'));
|
||||
_uploadWriteByte((uint8_t)('-'));
|
||||
for (uint32_t j = 0; j < i; j++) {
|
||||
_uploadWriteByte(endBuf[j]);
|
||||
}
|
||||
goto readfile;
|
||||
}
|
||||
endBuf[i++] = (uint8_t)argByte;
|
||||
}
|
||||
|
||||
if (strstr((const char*)endBuf, boundary.c_str()) != nullptr) {
|
||||
// Found the boundary string, finish processing this file upload
|
||||
if (_currentHandler && _currentHandler->canUpload(_currentUri)) {
|
||||
_currentHandler->upload(*this, _currentUri, *_currentUpload);
|
||||
}
|
||||
|
|
@ -561,30 +509,17 @@ readfile:
|
|||
if (_currentHandler && _currentHandler->canUpload(_currentUri)) {
|
||||
_currentHandler->upload(*this, _currentUri, *_currentUpload);
|
||||
}
|
||||
log_v("End File: %s Type: %s Size: %d", _currentUpload->filename.c_str(), _currentUpload->type.c_str(), _currentUpload->totalSize);
|
||||
line = client->readStringUntil(0x0D);
|
||||
client->readStringUntil(0x0A);
|
||||
if (line == "--") {
|
||||
log_v("End File: %s Type: %s Size: %d", _currentUpload->filename.c_str(), _currentUpload->type.c_str(), (int)_currentUpload->totalSize);
|
||||
if (!client->connected()) {
|
||||
return _parseFormUploadAborted();
|
||||
}
|
||||
line = client->readStringUntil('\r');
|
||||
client->readStringUntil('\n');
|
||||
if (line == "--") { // extra two dashes mean we reached the end of all form fields
|
||||
log_v("Done Parsing POST");
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
} else {
|
||||
_uploadWriteByte(0x0D);
|
||||
_uploadWriteByte(0x0A);
|
||||
_uploadWriteByte((uint8_t)('-'));
|
||||
_uploadWriteByte((uint8_t)('-'));
|
||||
for (uint32_t j = 0; j < boundary.length(); j++) {
|
||||
_uploadWriteByte(endBuf[j]);
|
||||
}
|
||||
argByte = _uploadReadByte(client);
|
||||
goto readfile;
|
||||
}
|
||||
} else {
|
||||
_uploadWriteByte(0x0D);
|
||||
goto readfile;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue