From 32e32e46d240b9303e624242d172c871dfefe4d8 Mon Sep 17 00:00:00 2001 From: anschrammh Date: Thu, 7 Apr 2022 01:20:13 +0200 Subject: [PATCH] Now handling range bytes request :) --- src/app/WEBServer.h | 140 ++++++++++++++++++++++++++++++++------------ 1 file changed, 103 insertions(+), 37 deletions(-) diff --git a/src/app/WEBServer.h b/src/app/WEBServer.h index d40f9dd..6369a94 100644 --- a/src/app/WEBServer.h +++ b/src/app/WEBServer.h @@ -259,7 +259,7 @@ class WEBServer : public TCPServer, public HttpConstants break; } - //Before in the buffer : key1: value1\0\nkey2: value2 + //Before in the buffer : key1: value1\r\nkey2: value2 //After in the buffer : key2: value2\r\n client->freeDataBuffer((pEndLine - (char *)client->_data) + 2); } @@ -317,27 +317,77 @@ class WEBServer : public TCPServer, public HttpConstants #endif //Here we check if we have interesting params - char *contentTypeP = strstr((char *)client->_data, "t-Type: application/x-www-form-urlen"); - - if(contentTypeP != NULL) + char *search = strstr((char *)client->_data, "t-Type: application/x-www-form-urlen"); + if(search != NULL) { #ifdef DEBUG_WEBS - Serial.printf("Data is of type : APPLICATION_X_WWW_FORM_URLENCODED\n"); + Serial.printf("Content-Type : APPLICATION_X_WWW_FORM_URLENCODED\n"); #endif client->_httpRequestData.HMT = APPLICATION_X_WWW_FORM_URLENCODED; } - - //Range part for file downloads - /*char *startingP = strstr((char *)client->_data, "nge: bytes="), *endP = strchr((char *)client->_data, '-'); - if(startingP != NULL && endP != NULL) + search = strstr((char *)client->_data, "ion: keep-al"); + if(search != NULL) { - endP = '\0'; - client->_range = strtoul(startingP + 11,NULL, 10); #ifdef DEBUG_WEBS - Serial.printf("Range : %d\n", client->_range); + Serial.printf("Connection : keep-alive\n"); #endif - }*/ + client->_keepAlive = true; + } + + //Range part for file downloads and media playback + search = strstr((char *)client->_data, "nge: bytes="); + + if(search != NULL) + { + //We parse the range byte data + if(fillRangeByteStruct(client)) + { + #ifdef DEBUG_WEBS + Serial.printf("Range (bytes) data : start -> %u ; end -> %u\n", client->_rangeData._rangeStart, client->_rangeData._rangeEnd); + #endif + client->_rangeData._rangeRequest = true; + } + else + { + #ifdef DEBUG_WEBS + Serial.printf("Range (bytes) data parse error : start -> %u ; end -> %u\n", client->_rangeData._rangeStart, client->_rangeData._rangeEnd); + #endif + } + } + } + + /* + * This function fills the client's _rangeData struct with proper values + */ + bool fillRangeByteStruct(T *client) + { + char *rangeStart = strchr((char *)client->_data, '='), *delimiter = strchr((char *)client->_data, '-'), *check(nullptr); + + if(!rangeStart)return false; + + rangeStart++; //We move one char forward + + //We parse the 1st part of the range byte + if(!delimiter) // If only one part (ill-formed) + { + client->_rangeData._rangeStart = strtoull(rangeStart, &check, 10); + if(*check != '\0')return false; + return true; + } + else + { + *delimiter = '\0'; + client->_rangeData._rangeStart = strtoull(rangeStart, &check, 10); + if(*check != '\0')return false; + } + + rangeStart = delimiter+1; + + //We parse the 2nd part of the range byte + client->_rangeData._rangeEnd = strtoull(rangeStart, &check, 10); + if(*check != '\0')return false; + return true; } /* @@ -531,12 +581,29 @@ class WEBServer : public TCPServer, public HttpConstants return false; } - if(client->_fileSentBytes == 0 /*&& client->_range == 0*/) + if(client->_fileSentBytes == 0) { + size_t pageToSendSize(pageToSend.size()); char *fileName = (char *) malloc(sizeof(char) * strlen(pageToSend.name()) + 1); - if(fileName != NULL)strcpy(fileName, pageToSend.name()); - header = getHTTPHeader(getMIMETypeByExtension(strlwr(getFileExtension(fileName))), pageToSend.size()); + if(fileName == NULL) + { + sendInfoResponse(HTTP_CODE::HTTP_CODE_INTERNAL_SERVER_ERROR, client, "Failed to allocate memory for the response"); + pageToSend.close(); + return false; + } + strcpy(fileName, pageToSend.name()); + + if(client->_rangeData._rangeRequest) + { + client->_fileSentBytes = client->_rangeData._rangeStart; + client->_rangeData._rangeEnd = !client->_rangeData._rangeEnd ? pageToSendSize - 1 : client->_rangeData._rangeEnd; + pageToSend.seek(client->_fileSentBytes); + } + else + client->_rangeData._rangeEnd = pageToSendSize - 1; //Needed to carry on sending when not a partial file + + header = getHTTPHeader(getMIMETypeByExtension(strlwr(getFileExtension(fileName))), pageToSendSize, client->_rangeData._rangeRequest, client->_rangeData._rangeStart, client->_rangeData._rangeEnd); #ifdef DEBUG_WEBS Serial.print("FILE EXTENSION : "); @@ -553,19 +620,15 @@ class WEBServer : public TCPServer, public HttpConstants } client->_client.print(header); + free(header);header = NULL; } - /*else if(client->_fileSentBytes == 0 && (!client->_range == 0)) - { - client->_fileSentBytes = client->_range-500; - Serial.println("RANGE SET"); - }*/ else { pageToSend.seek(client->_fileSentBytes); } - if(pageToSend.available()) + if(pageToSend.available() && client->_fileSentBytes < client->_rangeData._rangeEnd + 1) // File is done sending once the whole file was sent or the partial part is done sending { readBytes = pageToSend.read(sendBuffer,READ_WRITE_BUFFER_SIZE); client->_client.write(sendBuffer, readBytes); @@ -652,29 +715,32 @@ class WEBServer : public TCPServer, public HttpConstants if(extension == NULL)return UNKNOWN_MIME; //TEXT_PLAIN, TEXT_CSS, TEXT_HTML, TEXT_JAVASCRIPT - if(strcmp(extension,"web") == 0) return TEXT_HTML; - else if(strcmp(extension,"htm") == 0) return TEXT_HTML; - else if(strcmp(extension,"css") == 0) return TEXT_CSS; - else if(strcmp(extension,"js") == 0) return TEXT_JAVASCRIPT; - else if(strcmp(extension,"png") == 0) return IMAGE_PNG; - else if(strcmp(extension,"jpg") == 0) return IMAGE_JPEG; + if(strcmp(extension, "web") == 0) return TEXT_HTML; + else if(strcmp(extension, "htm") == 0) return TEXT_HTML; + else if(strcmp(extension, "css") == 0) return TEXT_CSS; + else if(strcmp(extension, "js") == 0) return TEXT_JAVASCRIPT; + else if(strcmp(extension, "png") == 0) return IMAGE_PNG; + else if(strcmp(extension, "jpg") == 0) return IMAGE_JPEG; else if(strcmp(extension, "mp3") == 0) return AUDIO_MPEG; else return UNKNOWN_MIME; } - static char *getHTTPHeader(HttpMIMEType httpMIMEType, const size_t size) + static char *getHTTPHeader(HttpMIMEType httpMIMEType, const size_t contentLength, bool acceptRanges = false, size_t rangeStart = 0, size_t rangeEnd = 0) { - char *header = (char *) malloc(sizeof(char) /*strlen("HTTP/1.1 200 OK\r\nContent-Type: \r\nContent-Length: \r\nCache-Control: max-age=31536000\r\n\r\n")*/* (86 + 255/*Longest MIME-TYPE that RFC allows*/ + 10 /*Max unsigned long footprint*/ + 1)); + size_t headerToAllocSize(/*strlen("HTTP/1.1 200 OK\r\nContent-Type: \r\nContent-Length: \r\nCache-Control: max-age=31536000\r\n\r\n")*/86 + 255/*Longest MIME-TYPE that RFC allows*/ + 10 /*Max unsigned long footprint*/ + 1 /*\0 character*/); + if(acceptRanges)headerToAllocSize += (22 + 25 + 10*3 + 13); //"Accept-Ranges: bytes\r\n" is 22 characters + "Content-Range: bytes %u-%u/%u\r\n" + 3*Max unsigned long footprint + space for 206 Partial Content + + char *header = (char *) malloc(sizeof(char) * headerToAllocSize); + + if(!header)return NULL; + + if(!acceptRanges) + sprintf(header,"HTTP/1.1 200 OK\r\nContent-Type: %s\r\nContent-Length: %u\r\nCache-Control: max-age=31536000\r\n\r\n", httpMIMETypeToString(httpMIMEType), contentLength); + else + sprintf(header,"HTTP/1.1 206 Partial Content\r\nContent-Type: %s\r\nAccept-Ranges: bytes\r\nContent-Length: %u\r\nContent-Range: bytes %u-%u/%u\r\nCache-Control: max-age=31536000\r\n\r\n", httpMIMETypeToString(httpMIMEType), rangeEnd-rangeStart+1,rangeStart ,rangeEnd, contentLength); - injectHeaderLayout(header, httpMIMETypeToString(httpMIMEType), size); - return header; } - - static void injectHeaderLayout(char *header, const char *contentType, const size_t size) - { - sprintf(header,"HTTP/1.1 200 OK\r\nContent-Type: %s\r\nContent-Length: %u\r\nCache-Control: max-age=31536000\r\n\r\n", contentType, size); - } static char *getFileExtension(char *name) {