Reworking the way the post body content is handled, not done yet

This commit is contained in:
Th3maz1ng 2022-05-01 16:27:36 +02:00
parent eb0a370cda
commit 380539e57e
4 changed files with 124 additions and 38 deletions

View File

@ -130,21 +130,9 @@ class TCPServer
return; return;
} }
uint32_t bytesAvailable((_currentClient->_client).available()); if((_currentClient->_client).available())
if(bytesAvailable)
{ {
uint16_t freeSpace = (_currentClient->_dataBufferSize-1/*for \0*/ - _currentClient->_dataSize); fillDataBuffer(_currentClient);
if(freeSpace != 0)
{
int amountToBeRead = bytesAvailable < freeSpace ? bytesAvailable : freeSpace;
(_currentClient->_client).read(_currentClient->_data + _currentClient->_dataSize, amountToBeRead);
_currentClient->_dataSize += amountToBeRead;
_currentClient->_data[_currentClient->_dataSize] = '\0';
_currentClient->_newDataAvailable = true;
}
} }
else if(!(_currentClient->_client).connected()) else if(!(_currentClient->_client).connected())
{ {
@ -186,6 +174,22 @@ class TCPServer
client->freeDataBuffer(client->_dataSize); client->freeDataBuffer(client->_dataSize);
} }
virtual void fillDataBuffer(T *client)
{
uint16_t freeSpace = (client->_dataBufferSize-1/*for \0*/ - client->_dataSize);
if(freeSpace > 0)
{
uint32_t bytesAvailable(client->_client.available());
int amountToBeRead = bytesAvailable < freeSpace ? bytesAvailable : freeSpace;
(client->_client).read(client->_data + client->_dataSize, amountToBeRead);
client->_dataSize += amountToBeRead;
client->_data[_currentClient->_dataSize] = '\0';
client->_newDataAvailable = true;
}
}
uint8_t freeClientId() uint8_t freeClientId()
{ {
int listCounter(_clientList.count()); int listCounter(_clientList.count());

View File

@ -11,6 +11,7 @@ WEBClient::WEBClient(WiFiClient client, uint8_t id, uint16_t maxResourceBuffer,
_httpRequestData.HRM = WEBServer<WEBClient>::HttpRequestMethod::UNDEFINED; _httpRequestData.HRM = WEBServer<WEBClient>::HttpRequestMethod::UNDEFINED;
_httpRequestData.HV = WEBServer<WEBClient>::HttpVersion::UNKNOWN; _httpRequestData.HV = WEBServer<WEBClient>::HttpVersion::UNKNOWN;
_httpRequestData.HMT = WEBServer<WEBClient>::HttpMIMEType::UNKNOWN_MIME; _httpRequestData.HMT = WEBServer<WEBClient>::HttpMIMEType::UNKNOWN_MIME;
_httpRequestData.contentLength = 0;
_httpRequestData.postParamsDataPointer = NULL; _httpRequestData.postParamsDataPointer = NULL;

View File

@ -29,6 +29,7 @@ class WEBServer : public TCPServer<T>, public HttpConstants
HttpRequestMethod HRM; HttpRequestMethod HRM;
HttpVersion HV; HttpVersion HV;
HttpMIMEType HMT; HttpMIMEType HMT;
size_t contentLength;
Dictionary<DictionaryHelper::StringEntity> getParams; Dictionary<DictionaryHelper::StringEntity> getParams;
Dictionary<DictionaryHelper::StringEntity> postParams; Dictionary<DictionaryHelper::StringEntity> postParams;
@ -91,6 +92,35 @@ class WEBServer : public TCPServer<T>, public HttpConstants
return new T(wc, TCPServer<T>::freeClientId(), TCPServer<T>::_clientDataBufferSize); return new T(wc, TCPServer<T>::freeClientId(), TCPServer<T>::_clientDataBufferSize);
} }
/**
* The fillDataBuffer method from the TCPServer base class was overloaded in order to read from socket
* until the \r\n\r\n body delimiter.
* The remaining data should NOT BE READ if the MIME TYPE is not APPLICATION_X_WWW_FORM_URLENCODED
* so that we can consume the full request body later.
*/
virtual void fillDataBuffer(T *client)
{
uint16_t freeSpace = (client->_dataBufferSize-1/*for \0*/ - client->_dataSize);
uint32_t bytesAvailable(client->_client.available());
void *bodyDelimiter(memmem((client->_client).peekBuffer(), (client->_client).peekAvailable(), "\r\n\r\n", 4));
if(bodyDelimiter && client->_httpRequestData.HMT != HttpMIMEType::APPLICATION_X_WWW_FORM_URLENCODED)
{
bytesAvailable = (const char *)bodyDelimiter - (client->_client).peekBuffer();
}
if(freeSpace > 0)
{
int amountToBeRead = bytesAvailable < freeSpace ? bytesAvailable : freeSpace, read(0);
read = (client->_client).read(client->_data + client->_dataSize, amountToBeRead);
client->_dataSize += read;
client->_data[client->_dataSize] = '\0';
client->_newDataAvailable = true;
}
}
virtual void greetClient(T *client) virtual void greetClient(T *client)
{ {
(void)client; (void)client;
@ -308,7 +338,11 @@ class WEBServer : public TCPServer<T>, public HttpConstants
if(*(pEndLine+2) == '\r') //We got \r\n\r\n -> so we go to the post data section if(*(pEndLine+2) == '\r') //We got \r\n\r\n -> so we go to the post data section
{ {
if(client->_httpRequestData.contentLength) //If a body is expected
client->_httpParserState = HttpParserStatus::PARSE_HTTP_POST_DATA; client->_httpParserState = HttpParserStatus::PARSE_HTTP_POST_DATA;
else //Else we are done
client->_WEBClientState = WEBClientState::QUERY_PARSED;
client->freeDataBuffer((pEndLine - (char *)client->_data) +3); //client->_data must not be empty... client->freeDataBuffer((pEndLine - (char *)client->_data) +3); //client->_data must not be empty...
break; break;
} }
@ -328,16 +362,15 @@ class WEBServer : public TCPServer<T>, public HttpConstants
switch(client->_httpRequestData.HMT) switch(client->_httpRequestData.HMT)
{ {
case APPLICATION_X_WWW_FORM_URLENCODED: case APPLICATION_X_WWW_FORM_URLENCODED:
#ifdef DEBUG_WEBS #if 1//def DEBUG_WEBS
Serial.printf("Post data : APPLICATION_X_WWW_FORM_URLENCODED\n"); Serial.printf("Post data : APPLICATION_X_WWW_FORM_URLENCODED\nPost data : #%s#\n", client->_data);
Serial.printf("Post data : %s\n", client->_data);
#endif #endif
//we parse it ! //we parse it !
if(!httpPostParamParser(client)) if(!httpPostParamParser(client))
{ {
//Parsing done! //Parsing done!
#ifdef DEBUG_WEBS #if 1//def DEBUG_WEBS
Serial.println("Post params :"); Serial.println("Post params :");
for(int i = 0; i < client->_httpRequestData.postParams.count(); i++) for(int i = 0; i < client->_httpRequestData.postParams.count(); i++)
{ {
@ -372,27 +405,29 @@ class WEBServer : public TCPServer<T>, public HttpConstants
//Here we check if we have interesting params //Here we check if we have interesting params
char *search = strstr((char *)client->_data, "t-Type: application/x-www-form-urlen"); char *search = strstr((char *)client->_data, "t-Type: application/x-www-form-urlen");
if(search != NULL) if(search != nullptr)
{ {
#ifdef DEBUG_WEBS #ifdef DEBUG_WEBS
Serial.printf("Content-Type : APPLICATION_X_WWW_FORM_URLENCODED\n"); Serial.printf("Content-Type : APPLICATION_X_WWW_FORM_URLENCODED\n");
#endif #endif
client->_httpRequestData.HMT = APPLICATION_X_WWW_FORM_URLENCODED; client->_httpRequestData.HMT = APPLICATION_X_WWW_FORM_URLENCODED;
return; //No need to look further
} }
search = strstr((char *)client->_data, "ion: keep-al"); search = strstr((char *)client->_data, "ion: keep-al");
if(search != NULL) if(search != nullptr)
{ {
#ifdef DEBUG_WEBS #ifdef DEBUG_WEBS
Serial.printf("Connection : keep-alive\n"); Serial.printf("Connection : keep-alive\n");
#endif #endif
client->_keepAlive = true; client->_keepAlive = true;
return; //No need to look further
} }
//Range part for file downloads and media playback //Range part for file downloads and media playback
search = strstr((char *)client->_data, "nge: bytes="); search = strstr((char *)client->_data, "nge: bytes=");
if(search != NULL) if(search != nullptr)
{ {
//We parse the range byte data //We parse the range byte data
if(fillRangeByteStruct(client)) if(fillRangeByteStruct(client))
@ -408,6 +443,26 @@ class WEBServer : public TCPServer<T>, public HttpConstants
Serial.printf("Range (bytes) data parse error : start -> %u ; end -> %u\n", client->_rangeData._rangeStart, client->_rangeData._rangeEnd); Serial.printf("Range (bytes) data parse error : start -> %u ; end -> %u\n", client->_rangeData._rangeStart, client->_rangeData._rangeEnd);
#endif #endif
} }
return; //No need to look further
}
//Content-length header
search = strstr((char *)client->_data, "ent-Length: ");
if(search != nullptr)
{
if(!fillContentLength(client))
{
#if 1//def DEBUG_WEBS
Serial.printf("Failed to parse content length\n");
#endif
}
else
{
#if 1//def DEBUG_WEBS
Serial.printf("Parsed content length is :%u\n", client->_httpRequestData.contentLength);
#endif
}
return; //No need to look further
} }
} }
@ -444,6 +499,19 @@ class WEBServer : public TCPServer<T>, public HttpConstants
return true; return true;
} }
/**
* This function fills the client's _httpRequestData.contentLength attribut
*/
bool fillContentLength(T *client)
{
char *start(strchr((char *)client->_data, ':')), *check(nullptr);
if(!start)return false;
start++;
client->_httpRequestData.contentLength = strtoul(start, &check, 10);
if(*check != '\0')return false;
return true;
}
/* /*
* This function is here to parse resources query parameters * This function is here to parse resources query parameters
*/ */
@ -505,6 +573,8 @@ class WEBServer : public TCPServer<T>, public HttpConstants
{ {
if(client->_httpRequestData.postParamsDataPointer == NULL) if(client->_httpRequestData.postParamsDataPointer == NULL)
{ {
if(strlen((char *)client->_data + 1) != client->_httpRequestData.contentLength)
return true;
client->_httpRequestData.postParamsDataPointer = (char *)client->_data + 1;//We save the starting position of the string to parse and we ignore the \n client->_httpRequestData.postParamsDataPointer = (char *)client->_data + 1;//We save the starting position of the string to parse and we ignore the \n
} }
@ -557,6 +627,10 @@ class WEBServer : public TCPServer<T>, public HttpConstants
if(ref == NULL) if(ref == NULL)
return false; return false;
// We consume the body's \r\n\r\n
uint8_t discard[4];
client->_client.read(discard, 4);
if(ref->HRM == UNDEFINED) if(ref->HRM == UNDEFINED)
{ {
return (*(ref->apiRoutine))(client->_httpRequestData, &(client->_client), ref->pData); return (*(ref->apiRoutine))(client->_httpRequestData, &(client->_client), ref->pData);
@ -620,7 +694,7 @@ class WEBServer : public TCPServer<T>, public HttpConstants
Serial.println(pageToSend.name()); Serial.println(pageToSend.name());
#endif #endif
if(pageToSend.isDirectory()) //TODO : List the files present in the directory if(pageToSend.isDirectory())
{ {
//If the ressource wasn't terminated by a '/' then we issue a 301 Moved Permanently, else all's good //If the ressource wasn't terminated by a '/' then we issue a 301 Moved Permanently, else all's good
if(client->_httpRequestData.httpResource[strlen(client->_httpRequestData.httpResource)-1] != '/') if(client->_httpRequestData.httpResource[strlen(client->_httpRequestData.httpResource)-1] != '/')
@ -784,16 +858,16 @@ class WEBServer : public TCPServer<T>, public HttpConstants
{ {
if(!(nextFile = pageToSend.openNextFile()))break; if(!(nextFile = pageToSend.openNextFile()))break;
char zero_prepended[4][3] = {"","","",""}; char zero_prepended[8][3] = {"","","","","","","",""};
time_t rawCreationTime(nextFile.getCreationTime()), rawLastModifiedTime(nextFile.getLastWrite()); time_t rawCreationTime(nextFile.getCreationTime()), rawLastModifiedTime(nextFile.getLastWrite());
tm creationTime(*localtime(&rawCreationTime)), lastModifiedTime(*localtime(&rawLastModifiedTime)); tm creationTime(*localtime(&rawCreationTime)), lastModifiedTime(*localtime(&rawLastModifiedTime));
client->_client.printf_P(PSTR("<tr><td>%s</td><td><a href=\"%s\">%s</a></td><td align=\"right\">%d-%d-%d %s:%s</td><td align=\"right\">%d-%d-%d %s:%s</td><td align=\"right\">%.1fK</td></tr>\r\n"), client->_client.printf_P(PSTR("<tr><td>%s</td><td><a href=\"%s\">%s</a></td><td align=\"right\">%d-%s-%s %s:%s</td><td align=\"right\">%d-%s-%s %s:%s</td><td align=\"right\">%.1fK</td></tr>\r\n"),
nextFile.isDirectory() ? "[DIR]":"[FILE]", nextFile.isDirectory() ? "[DIR]":"[FILE]",
nextFile.name(), nextFile.name(),
nextFile.name(), nextFile.name(),
creationTime.tm_year + 1900, creationTime.tm_mon + 1, creationTime.tm_mday, dateTimeFormater(zero_prepended[0], creationTime.tm_hour, '0'), dateTimeFormater(zero_prepended[1], creationTime.tm_min, '0'), creationTime.tm_year + 1900, dateTimeFormater(zero_prepended[0], creationTime.tm_mon + 1, '0'), dateTimeFormater(zero_prepended[1], creationTime.tm_mday, '0'), dateTimeFormater(zero_prepended[2], creationTime.tm_hour, '0'), dateTimeFormater(zero_prepended[3], creationTime.tm_min, '0'),
lastModifiedTime.tm_year + 1900, lastModifiedTime.tm_mon + 1, lastModifiedTime.tm_mday, dateTimeFormater(zero_prepended[2], lastModifiedTime.tm_hour, '0'), dateTimeFormater(zero_prepended[3], lastModifiedTime.tm_min, '0'), lastModifiedTime.tm_year + 1900, dateTimeFormater(zero_prepended[4], lastModifiedTime.tm_mon + 1, '0'), dateTimeFormater(zero_prepended[5], lastModifiedTime.tm_mday, '0'), dateTimeFormater(zero_prepended[6], lastModifiedTime.tm_hour, '0'), dateTimeFormater(zero_prepended[7], lastModifiedTime.tm_min, '0'),
nextFile.size() / 1024.0 nextFile.size() / 1024.0
); );
@ -801,6 +875,7 @@ class WEBServer : public TCPServer<T>, public HttpConstants
Serial.printf("File name : %s\nFile size : %u\nFree stack : %u\n", nextFile.name(), nextFile.size(), ESP.getFreeContStack()); Serial.printf("File name : %s\nFile size : %u\nFree stack : %u\n", nextFile.name(), nextFile.size(), ESP.getFreeContStack());
#endif #endif
nextFile.close(); nextFile.close();
delay(5);
} }
client->_client.printf_P(PSTR( "<tr><th colspan=\"5\"><hr></th></tr>\r\n\ client->_client.printf_P(PSTR( "<tr><th colspan=\"5\"><hr></th></tr>\r\n\

View File

@ -108,14 +108,16 @@ boolean rtcSetTimeApi(WEBServer<WEBClient>::HttpRequestData &HRD, WiFiClient *wc
} }
else else
{ {
p->getRtcManager().setDateTime(DateTime( DateTime dt(
atoi(dictio->getAt(2)->getString()), atoi(dictio->getAt(2)->getString()),
atoi(dictio->getAt(1)->getString()), atoi(dictio->getAt(1)->getString()),
atoi(dictio->getAt((unsigned int)0)->getString()), atoi(dictio->getAt((unsigned int)0)->getString()),
atoi(dictio->getAt(3)->getString()), atoi(dictio->getAt(3)->getString()),
atoi(dictio->getAt(4)->getString()), atoi(dictio->getAt(4)->getString()),
atoi(dictio->getAt(5)->getString()) atoi(dictio->getAt(5)->getString())
)); );
p->getRTC_DS3231().adjust(dt);
p->getRtcManager().setDateTime(dt);
DateTime d = p->getRtcManager().getDateTime(); DateTime d = p->getRtcManager().getDateTime();
sprintf(buffer,"{\"status\":\"ok\",\"date\":\"%d/%d/%d\",\"time\":\"%d:%d:%d\"}", d.day(), d.month(), d.year(), d.hour(), d.minute(), d.second()); sprintf(buffer,"{\"status\":\"ok\",\"date\":\"%d/%d/%d\",\"time\":\"%d:%d:%d\"}", d.day(), d.month(), d.year(), d.hour(), d.minute(), d.second());
} }
@ -274,7 +276,7 @@ boolean systemInfoApi(WEBServer<WEBClient>::HttpRequestData &HRD, WiFiClient *wc
ESP.getHeapStats(&freeMem, &biggestContigMemBlock, &frag); ESP.getHeapStats(&freeMem, &biggestContigMemBlock, &frag);
TimeSpan ts(p->getUpTime()); TimeSpan ts(p->getUpTime());
sprintf(buffer, "{\"status\":\"ok\",\"CPU freq\":%u,\"free RAM\":%u,\"heap frag\":%u,\"max block\":%u,\"nb views\":%u,\"up time\":{\"days\":%d,\"hours\":%d,\"minutes\":%d,\"seconds\":%d},\"temperature\":{\"level\":%.2f, \"unit\":\"°C\"}}", ESP.getCpuFreqMHz(), freeMem, frag, biggestContigMemBlock, p->getScreenManager().getViewCount(), ts.days(), ts.hours(), ts.minutes(), ts.seconds(), p->getRtcManager().getTemperature()); sprintf(buffer, "{\"status\":\"ok\",\"CPU freq\":%u,\"free RAM\":%u,\"heap frag\":%u,\"max block\":%u,\"nb views\":%u,\"up time\":{\"days\":%d,\"hours\":%d,\"minutes\":%d,\"seconds\":%d},\"temperature\":{\"level\":%.2f, \"unit\":\"°C\"}}", ESP.getCpuFreqMHz(), freeMem, frag, biggestContigMemBlock, p->getScreenManager().getViewCount(), ts.days(), ts.hours(), ts.minutes(), ts.seconds(), p->getRTC_DS3231().getTemperature());
WEBServer<WEBClient>::sendHTTPHeader(wc, HttpConstants::httpMIMETypeToString(HttpConstants::APPLICATION_JSON), strlen(buffer)); WEBServer<WEBClient>::sendHTTPHeader(wc, HttpConstants::httpMIMETypeToString(HttpConstants::APPLICATION_JSON), strlen(buffer));
wc->print(buffer); wc->print(buffer);
@ -562,23 +564,27 @@ boolean otaUpdateApi(WEBServer<WEBClient>::HttpRequestData &HRD, WiFiClient *wc,
{ {
(void)HRD; (void)HRD;
(void)pData; (void)pData;
Serial.printf("OTA Update resquest\n"); Serial.printf("OTA Update resquest\n#");
char buffer[500]; char buffer[30] = "";
size_t read(0); size_t read(0), cnt(0);
unsigned long ts(millis());
while(wc->available()) while(wc->available() || millis() - ts < 10)
{ {
read = wc->read(buffer, 500); read = wc->read(buffer, 30);
if(read)ts = millis();
//buffer[read] = '\0';
for(uint8_t i(0); i < read; i++) for(uint8_t i(0); i < read; i++)
Serial.printf("%02X %s",buffer[i], i % 30 == 0 ? "\n" : ""); Serial.printf("%02X",buffer[i]);
Serial.println();
//Serial.printf("%s",buffer);
yield(); yield();
} }
Serial.println("#");
WEBServer<WEBClient>::sendHTTPHeader(wc, HttpConstants::httpMIMETypeToString(HttpConstants::TEXT_PLAIN), 2); WEBServer<WEBClient>::sendHTTPHeader(wc, HttpConstants::httpMIMETypeToString(HttpConstants::TEXT_PLAIN), 2);
wc->print("OK"); wc->print("OK");
//wc->peekBuffer
return true; return true;
} }