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