Compare commits
	
		
			3 Commits
		
	
	
		
			5fb02304be
			...
			32e32e46d2
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 32e32e46d2 | ||
|  | bf4caea902 | ||
|  | bf05086381 | 
| @ -137,6 +137,17 @@ class HttpConstants | |||||||
|     } |     } | ||||||
| 	} | 	} | ||||||
|   |   | ||||||
|  |   static const char *httpStatusToString(HTTP_CODE c) | ||||||
|  |   { | ||||||
|  |     switch(c) | ||||||
|  |     { | ||||||
|  |       case HTTP_CODE_PARTIAL_CONTENT: | ||||||
|  |         return HTTPCONSTANTS_STRING("206 Partial Content"); | ||||||
|  |         break; | ||||||
|  |       default: | ||||||
|  |         return HTTPCONSTANTS_STRING("200 OK"); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|   protected: |   protected: | ||||||
|   private: |   private: | ||||||
| }; | }; | ||||||
|  | |||||||
| @ -2,7 +2,7 @@ | |||||||
| 
 | 
 | ||||||
| //#define DEBUG_WEBCL
 | //#define DEBUG_WEBCL
 | ||||||
| 
 | 
 | ||||||
| WEBClient::WEBClient(WiFiClient client, uint8_t id, uint16_t maxResourceBuffer, uint16_t maxBodyBuffer, uint16_t dataBufferSize) : TCPClient(client, id, dataBufferSize), _WEBClientState(WEBServer<WEBClient>::WEBClientState::ACCEPTED), _httpParserState(WEBServer<WEBClient>::HttpParserStatus::HTTP_VERB), _fileSentBytes(0), _range(0) | WEBClient::WEBClient(WiFiClient client, uint8_t id, uint16_t maxResourceBuffer, uint16_t maxBodyBuffer, uint16_t dataBufferSize) : TCPClient(client, id, dataBufferSize) | ||||||
| { | { | ||||||
|   #ifdef DEBUG_WEBCL |   #ifdef DEBUG_WEBCL | ||||||
|   Serial.println("WEBClient : Standard constructor called"); |   Serial.println("WEBClient : Standard constructor called"); | ||||||
|  | |||||||
| @ -12,12 +12,18 @@ class WEBClient : public TCPClient | |||||||
| 		WEBClient(WiFiClient client, uint8_t id, uint16_t maxResourceBuffer = 255, uint16_t maxBodyBuffer = 255, uint16_t dataBufferSize = 511); | 		WEBClient(WiFiClient client, uint8_t id, uint16_t maxResourceBuffer = 255, uint16_t maxBodyBuffer = 255, uint16_t dataBufferSize = 511); | ||||||
| 		virtual ~WEBClient(); | 		virtual ~WEBClient(); | ||||||
| 	protected: | 	protected: | ||||||
|     WEBServer<WEBClient>::WEBClientState _WEBClientState; |     WEBServer<WEBClient>::WEBClientState _WEBClientState = WEBServer<WEBClient>::WEBClientState::ACCEPTED; | ||||||
| 	private: | 	private: | ||||||
|     WEBServer<WEBClient>::HttpRequestData _httpRequestData; |     WEBServer<WEBClient>::HttpRequestData _httpRequestData; | ||||||
|     WEBServer<WEBClient>::HttpParserStatus _httpParserState; |     WEBServer<WEBClient>::HttpParserStatus _httpParserState = WEBServer<WEBClient>::HttpParserStatus::HTTP_VERB; | ||||||
|     uint64_t _fileSentBytes; |     uint64_t _fileSentBytes = 0; | ||||||
|     uint64_t _range; //Used to store the value of the range param for file downloading
 |     struct  | ||||||
|  |     { | ||||||
|  |       bool _rangeRequest; | ||||||
|  |       size_t _rangeStart; | ||||||
|  |       size_t _rangeEnd; | ||||||
|  |     } _rangeData = {false, 0, 0}; //Used to store the values of the range param for file downloads and media playback
 | ||||||
|  |     bool _keepAlive = false; | ||||||
|     void clearHttpRequestData(); |     void clearHttpRequestData(); | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -259,7 +259,7 @@ class WEBServer : public TCPServer<T>, public HttpConstants | |||||||
|                   break; |                   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
 |                 //After in the buffer : key2: value2\r\n
 | ||||||
|                 client->freeDataBuffer((pEndLine - (char *)client->_data) + 2); |                 client->freeDataBuffer((pEndLine - (char *)client->_data) + 2); | ||||||
|               } |               } | ||||||
| @ -317,27 +317,77 @@ class WEBServer : public TCPServer<T>, public HttpConstants | |||||||
|       #endif |       #endif | ||||||
| 
 | 
 | ||||||
|       //Here we check if we have interesting params
 |       //Here we check if we have interesting params
 | ||||||
|       char *contentTypeP = 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(contentTypeP != NULL) |  | ||||||
|       { |       { | ||||||
|         #ifdef DEBUG_WEBS |         #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 |         #endif | ||||||
|         client->_httpRequestData.HMT = APPLICATION_X_WWW_FORM_URLENCODED; |         client->_httpRequestData.HMT = APPLICATION_X_WWW_FORM_URLENCODED; | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|       //Range part for file downloads
 |       search = strstr((char *)client->_data, "ion: keep-al"); | ||||||
|       /*char *startingP = strstr((char *)client->_data, "nge: bytes="), *endP = strchr((char *)client->_data, '-');
 |       if(search != NULL) | ||||||
| 
 |  | ||||||
|       if(startingP != NULL && endP != NULL) |  | ||||||
|       { |       { | ||||||
|         endP = '\0'; |  | ||||||
|         client->_range = strtoul(startingP + 11,NULL, 10); |  | ||||||
|         #ifdef DEBUG_WEBS |         #ifdef DEBUG_WEBS | ||||||
|         Serial.printf("Range : %d\n", client->_range); |         Serial.printf("Connection : keep-alive\n"); | ||||||
|         #endif |         #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<T>, public HttpConstants | |||||||
|               return false; |               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);  |               char *fileName = (char *) malloc(sizeof(char) * strlen(pageToSend.name()) + 1);  | ||||||
|                |                | ||||||
|               if(fileName != NULL)strcpy(fileName, pageToSend.name()); |               if(fileName == NULL) | ||||||
|               header = getHTTPHeader(getMIMETypeByExtension(strlwr(getFileExtension(fileName))), pageToSend.size()); |               { | ||||||
|  |                 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 |               #ifdef DEBUG_WEBS | ||||||
|               Serial.print("FILE EXTENSION : "); |               Serial.print("FILE EXTENSION : "); | ||||||
| @ -553,19 +620,15 @@ class WEBServer : public TCPServer<T>, public HttpConstants | |||||||
|               } |               } | ||||||
|                |                | ||||||
|               client->_client.print(header); |               client->_client.print(header); | ||||||
|  | 
 | ||||||
|               free(header);header = NULL; |               free(header);header = NULL; | ||||||
|             } |             } | ||||||
|             /*else if(client->_fileSentBytes == 0 && (!client->_range == 0))
 |  | ||||||
|             { |  | ||||||
|               client->_fileSentBytes = client->_range-500; |  | ||||||
|               Serial.println("RANGE SET"); |  | ||||||
|             }*/ |  | ||||||
|             else |             else | ||||||
|             { |             { | ||||||
|               pageToSend.seek(client->_fileSentBytes); |               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); |               readBytes = pageToSend.read(sendBuffer,READ_WRITE_BUFFER_SIZE); | ||||||
|               client->_client.write(sendBuffer, readBytes); |               client->_client.write(sendBuffer, readBytes); | ||||||
| @ -652,30 +715,33 @@ class WEBServer : public TCPServer<T>, public HttpConstants | |||||||
|       if(extension == NULL)return UNKNOWN_MIME; |       if(extension == NULL)return UNKNOWN_MIME; | ||||||
|        |        | ||||||
|       //TEXT_PLAIN, TEXT_CSS, TEXT_HTML, TEXT_JAVASCRIPT
 |       //TEXT_PLAIN, TEXT_CSS, TEXT_HTML, TEXT_JAVASCRIPT
 | ||||||
|       if(strcmp(extension,"web") == 0) return TEXT_HTML; |       if(strcmp(extension, "web") == 0) return TEXT_HTML; | ||||||
|       else if(strcmp(extension,"htm") == 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, "css") == 0) return TEXT_CSS; | ||||||
|       else if(strcmp(extension,"js") == 0) return TEXT_JAVASCRIPT; |       else if(strcmp(extension, "js") == 0) return TEXT_JAVASCRIPT; | ||||||
|       else if(strcmp(extension,"png") == 0) return IMAGE_PNG; |       else if(strcmp(extension, "png") == 0) return IMAGE_PNG; | ||||||
|       else if(strcmp(extension,"jpg") == 0) return IMAGE_JPEG; |       else if(strcmp(extension, "jpg") == 0) return IMAGE_JPEG; | ||||||
|       else if(strcmp(extension, "mp3") == 0) return AUDIO_MPEG; |       else if(strcmp(extension, "mp3") == 0) return AUDIO_MPEG; | ||||||
|       else return UNKNOWN_MIME; |       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
 | ||||||
| 
 | 
 | ||||||
|       injectHeaderLayout(header, httpMIMETypeToString(httpMIMEType), size); |       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); | ||||||
| 
 | 
 | ||||||
|       return header; |       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) |     static char *getFileExtension(char *name) | ||||||
|     { |     { | ||||||
|       char *p(lastIndexOf(name, '.')); |       char *p(lastIndexOf(name, '.')); | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user