diff --git a/src/app/TCPServer.h b/src/app/TCPServer.h index 0317b71..fcc2ba3 100644 --- a/src/app/TCPServer.h +++ b/src/app/TCPServer.h @@ -130,21 +130,9 @@ class TCPServer return; } - uint32_t bytesAvailable((_currentClient->_client).available()); - - if(bytesAvailable) + if((_currentClient->_client).available()) { - uint16_t freeSpace = (_currentClient->_dataBufferSize-1/*for \0*/ - _currentClient->_dataSize); - - 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; - } + fillDataBuffer(_currentClient); } else if(!(_currentClient->_client).connected()) { @@ -185,6 +173,22 @@ class TCPServer } 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() { diff --git a/src/app/WEBClient.cpp b/src/app/WEBClient.cpp index e88c5cb..e0d104f 100644 --- a/src/app/WEBClient.cpp +++ b/src/app/WEBClient.cpp @@ -11,6 +11,7 @@ WEBClient::WEBClient(WiFiClient client, uint8_t id, uint16_t maxResourceBuffer, _httpRequestData.HRM = WEBServer::HttpRequestMethod::UNDEFINED; _httpRequestData.HV = WEBServer::HttpVersion::UNKNOWN; _httpRequestData.HMT = WEBServer::HttpMIMEType::UNKNOWN_MIME; + _httpRequestData.contentLength = 0; _httpRequestData.postParamsDataPointer = NULL; diff --git a/src/app/WEBServer.h b/src/app/WEBServer.h index 544da1f..2c340f2 100644 --- a/src/app/WEBServer.h +++ b/src/app/WEBServer.h @@ -29,6 +29,7 @@ class WEBServer : public TCPServer, public HttpConstants HttpRequestMethod HRM; HttpVersion HV; HttpMIMEType HMT; + size_t contentLength; Dictionary getParams; Dictionary postParams; @@ -90,6 +91,35 @@ class WEBServer : public TCPServer, public HttpConstants { return new T(wc, TCPServer::freeClientId(), TCPServer::_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) { @@ -308,7 +338,11 @@ class WEBServer : public TCPServer, public HttpConstants if(*(pEndLine+2) == '\r') //We got \r\n\r\n -> so we go to the post data section { - client->_httpParserState = HttpParserStatus::PARSE_HTTP_POST_DATA; + if(client->_httpRequestData.contentLength) //If a body is expected + 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... break; } @@ -328,16 +362,15 @@ class WEBServer : public TCPServer, public HttpConstants switch(client->_httpRequestData.HMT) { case APPLICATION_X_WWW_FORM_URLENCODED: - #ifdef DEBUG_WEBS - Serial.printf("Post data : APPLICATION_X_WWW_FORM_URLENCODED\n"); - Serial.printf("Post data : %s\n", client->_data); + #if 1//def DEBUG_WEBS + Serial.printf("Post data : APPLICATION_X_WWW_FORM_URLENCODED\nPost data : #%s#\n", client->_data); #endif //we parse it ! if(!httpPostParamParser(client)) { //Parsing done! - #ifdef DEBUG_WEBS + #if 1//def DEBUG_WEBS Serial.println("Post params :"); for(int i = 0; i < client->_httpRequestData.postParams.count(); i++) { @@ -372,27 +405,29 @@ class WEBServer : public TCPServer, public HttpConstants //Here we check if we have interesting params char *search = strstr((char *)client->_data, "t-Type: application/x-www-form-urlen"); - if(search != NULL) + if(search != nullptr) { #ifdef DEBUG_WEBS Serial.printf("Content-Type : APPLICATION_X_WWW_FORM_URLENCODED\n"); #endif client->_httpRequestData.HMT = APPLICATION_X_WWW_FORM_URLENCODED; + return; //No need to look further } search = strstr((char *)client->_data, "ion: keep-al"); - if(search != NULL) + if(search != nullptr) { #ifdef DEBUG_WEBS Serial.printf("Connection : keep-alive\n"); #endif client->_keepAlive = true; + return; //No need to look further } //Range part for file downloads and media playback search = strstr((char *)client->_data, "nge: bytes="); - if(search != NULL) + if(search != nullptr) { //We parse the range byte data if(fillRangeByteStruct(client)) @@ -408,6 +443,26 @@ class WEBServer : public TCPServer, public HttpConstants Serial.printf("Range (bytes) data parse error : start -> %u ; end -> %u\n", client->_rangeData._rangeStart, client->_rangeData._rangeEnd); #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, public HttpConstants 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 */ @@ -505,6 +573,8 @@ class WEBServer : public TCPServer, public HttpConstants { 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 } @@ -556,6 +626,10 @@ class WEBServer : public TCPServer, public HttpConstants if(ref == NULL) return false; + + // We consume the body's \r\n\r\n + uint8_t discard[4]; + client->_client.read(discard, 4); if(ref->HRM == UNDEFINED) { @@ -620,7 +694,7 @@ class WEBServer : public TCPServer, public HttpConstants Serial.println(pageToSend.name()); #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(client->_httpRequestData.httpResource[strlen(client->_httpRequestData.httpResource)-1] != '/') @@ -784,16 +858,16 @@ class WEBServer : public TCPServer, public HttpConstants { if(!(nextFile = pageToSend.openNextFile()))break; - char zero_prepended[4][3] = {"","","",""}; + char zero_prepended[8][3] = {"","","","","","","",""}; time_t rawCreationTime(nextFile.getCreationTime()), rawLastModifiedTime(nextFile.getLastWrite()); tm creationTime(*localtime(&rawCreationTime)), lastModifiedTime(*localtime(&rawLastModifiedTime)); - client->_client.printf_P(PSTR("%s%s%d-%d-%d %s:%s%d-%d-%d %s:%s%.1fK\r\n"), + client->_client.printf_P(PSTR("%s%s%d-%s-%s %s:%s%d-%s-%s %s:%s%.1fK\r\n"), nextFile.isDirectory() ? "[DIR]":"[FILE]", 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'), - 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'), + 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, 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 ); @@ -801,6 +875,7 @@ class WEBServer : public TCPServer, public HttpConstants Serial.printf("File name : %s\nFile size : %u\nFree stack : %u\n", nextFile.name(), nextFile.size(), ESP.getFreeContStack()); #endif nextFile.close(); + delay(5); } client->_client.printf_P(PSTR( "
\r\n\ diff --git a/src/app/webApi.cpp b/src/app/webApi.cpp index c2f5bb6..67a9493 100644 --- a/src/app/webApi.cpp +++ b/src/app/webApi.cpp @@ -108,14 +108,16 @@ boolean rtcSetTimeApi(WEBServer::HttpRequestData &HRD, WiFiClient *wc } else { - p->getRtcManager().setDateTime(DateTime( + DateTime dt( atoi(dictio->getAt(2)->getString()), atoi(dictio->getAt(1)->getString()), atoi(dictio->getAt((unsigned int)0)->getString()), atoi(dictio->getAt(3)->getString()), atoi(dictio->getAt(4)->getString()), atoi(dictio->getAt(5)->getString()) - )); + ); + p->getRTC_DS3231().adjust(dt); + p->getRtcManager().setDateTime(dt); 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()); } @@ -274,7 +276,7 @@ boolean systemInfoApi(WEBServer::HttpRequestData &HRD, WiFiClient *wc ESP.getHeapStats(&freeMem, &biggestContigMemBlock, &frag); 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::sendHTTPHeader(wc, HttpConstants::httpMIMETypeToString(HttpConstants::APPLICATION_JSON), strlen(buffer)); wc->print(buffer); @@ -562,23 +564,27 @@ boolean otaUpdateApi(WEBServer::HttpRequestData &HRD, WiFiClient *wc, { (void)HRD; (void)pData; - Serial.printf("OTA Update resquest\n"); + Serial.printf("OTA Update resquest\n#"); - char buffer[500]; - size_t read(0); + char buffer[30] = ""; + 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++) - Serial.printf("%02X %s",buffer[i], i % 30 == 0 ? "\n" : ""); - + Serial.printf("%02X",buffer[i]); + Serial.println(); + //Serial.printf("%s",buffer); yield(); } + Serial.println("#"); WEBServer::sendHTTPHeader(wc, HttpConstants::httpMIMETypeToString(HttpConstants::TEXT_PLAIN), 2); wc->print("OK"); - //wc->peekBuffer return true; }