757 lines
28 KiB
C++
757 lines
28 KiB
C++
#ifndef WEBSERVER_H
|
|
#define WEBSERVER_H
|
|
|
|
#include "TCPServer.h"
|
|
#include "Dictionary.h"
|
|
#include "SDCardManager.h"
|
|
#include "HttpConstants.h"
|
|
//#define DEBUG_WEBS
|
|
#define READ_WRITE_BUFFER_SIZE 2000
|
|
|
|
template <typename T>
|
|
class WEBServer : public TCPServer<T>, public HttpConstants
|
|
{
|
|
public:
|
|
enum HttpParserStatus {HTTP_VERB, HTTP_RESSOURCE, HTTP_VERSION, HTTP_PARAMS, POST_DATA, HEADER_PARAMS};
|
|
enum WEBClientState {ACCEPTED, PARSING, QUERY_PARSED, RESPONSE_SENT, DONE};
|
|
|
|
struct HttpRequestData
|
|
{
|
|
HttpRequestMethod HRM;
|
|
HttpVersion HV;
|
|
HttpMIMEType HMT;
|
|
|
|
Dictionary<DictionaryHelper::StringEntity> getParams;
|
|
char *getParamsDataPointer; //Used in the getParams algorithm
|
|
Dictionary<DictionaryHelper::StringEntity> postParams;
|
|
char *postParamsDataPointer; //Used in the postParams algorithm
|
|
|
|
char *httpResource;
|
|
uint16_t maxResourceBuffer;
|
|
char *httpBody;
|
|
uint16_t maxBodyBuffer;
|
|
};
|
|
|
|
WEBServer(uint16_t port = 80, SDCardManager *sdCardManager = NULL, uint8_t maxClient = MAX_CLIENT, uint16_t clientDataBufferSize = 255) : TCPServer<T>(port, maxClient, clientDataBufferSize), _sdCardManager(sdCardManager) {}
|
|
|
|
boolean addApiRoutine(const char *uri, boolean (*apiRoutine)(HttpRequestData&, WiFiClient*, void*), void *pData, HttpRequestMethod HRM = UNDEFINED)
|
|
{
|
|
return _apiDictionary.add(uri, new ApiRoutine({apiRoutine, pData, HRM}));
|
|
}
|
|
|
|
void clearApiRoutine() { _apiDictionary.clear(); };
|
|
|
|
boolean removeApiRoutine(const char* uri)
|
|
{
|
|
return _apiDictionary.remove(uri);
|
|
}
|
|
|
|
//Helper function used for the webApi
|
|
static void injectApiHeader(char *header, const char *contentType, char *content)
|
|
{
|
|
char *buffer = (char *)malloc(sizeof(char) * strlen(content) + 1);
|
|
if(buffer != NULL)
|
|
{
|
|
strcpy(buffer, content);
|
|
sprintf(header,"HTTP/1.1 200 OK\r\nContent-Type: %s\r\nContent-Length: %d\r\n\r\n%s",contentType,strlen(buffer), buffer);
|
|
free(buffer);
|
|
}
|
|
}
|
|
protected:
|
|
private:
|
|
virtual T* createNewClient(WiFiClient wc)
|
|
{
|
|
return new T(wc, TCPServer<T>::freeClientId(), TCPServer<T>::_clientDataBufferSize);
|
|
}
|
|
|
|
virtual void greetClient(T *client)
|
|
{
|
|
|
|
}
|
|
|
|
virtual void processClientData(T *client)
|
|
{
|
|
if(client->_dataSize > 0)
|
|
{
|
|
switch(client->_WEBClientState)
|
|
{
|
|
case ACCEPTED:
|
|
#ifdef DEBUG_WEBS
|
|
Serial.println("WEBServer : ACCEPTED");
|
|
#endif
|
|
client->_WEBClientState = WEBClientState::PARSING;
|
|
break;
|
|
case PARSING:
|
|
queryParser(client);
|
|
break;
|
|
case QUERY_PARSED:
|
|
#ifdef DEBUG_WEBS
|
|
Serial.println("WEBServer : QUERY_PARSED");
|
|
#endif
|
|
sendDataToClient(client);
|
|
break;
|
|
case RESPONSE_SENT:
|
|
#ifdef DEBUG_WEBS
|
|
Serial.println("WEBServer : RESPONSE_SENT");
|
|
#endif
|
|
client->_WEBClientState = WEBClientState::DONE;
|
|
break;
|
|
case DONE:
|
|
#ifdef DEBUG_WEBS
|
|
Serial.println("WEBServer : DONE");
|
|
#endif
|
|
client->_clientState = TCPClient::ClientState::DISCARDED;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void queryParser(T *client)
|
|
{
|
|
switch(client->_httpParserState)
|
|
{
|
|
case HttpParserStatus::HTTP_VERB:
|
|
{
|
|
#ifdef DEBUG_WEBS
|
|
Serial.println((char *)client->_data);
|
|
#endif
|
|
|
|
char *pVerb = strstr((char *)client->_data, " ");
|
|
|
|
if(pVerb != NULL)
|
|
{
|
|
*pVerb = '\0';
|
|
client->_httpRequestData.HRM = getHttpVerbEnumValue((char *)client->_data);
|
|
client->freeDataBuffer((pVerb - (char *)client->_data) +1);
|
|
|
|
if(client->_httpRequestData.HRM == HttpRequestMethod::UNDEFINED) //Error 400
|
|
{
|
|
sendInfoResponse(HTTP_CODE::HTTP_CODE_BAD_REQUEST, client, "The server could not understand the request due to invalid syntax");
|
|
client->_clientState = TCPClient::ClientState::DISCARDED;
|
|
break;
|
|
}
|
|
|
|
#ifdef DEBUG_WEBS
|
|
Serial.print("Verb : ");Serial.println(client->_httpRequestData.HRM);
|
|
Serial.println((char *)client->_data);
|
|
#endif
|
|
|
|
client->_httpParserState = HttpParserStatus::HTTP_RESSOURCE;
|
|
}
|
|
else
|
|
{
|
|
sendInfoResponse(HTTP_CODE::HTTP_CODE_BAD_REQUEST, client, "The server could not understand the request due to invalid syntax");
|
|
client->_clientState = TCPClient::ClientState::DISCARDED;
|
|
}
|
|
}
|
|
break;
|
|
case HttpParserStatus::HTTP_RESSOURCE:
|
|
{
|
|
char *pRsrc = strstr((char *)client->_data, " ");
|
|
|
|
if(pRsrc != NULL)
|
|
{
|
|
*pRsrc = '\0';
|
|
uint16_t safeLength = pRsrc - (char *)client->_data <= client->_httpRequestData.maxResourceBuffer ? pRsrc - (char *)client->_data : client->_httpRequestData.maxResourceBuffer;
|
|
|
|
#ifdef DEBUG_WEBS
|
|
Serial.print("Resrc length : ");Serial.println(safeLength);
|
|
#endif
|
|
|
|
client->_httpRequestData.httpResource = (char *) malloc(sizeof(char) * (safeLength+1) ); //for \0
|
|
if(client->_httpRequestData.httpResource != NULL)
|
|
{
|
|
strncpy(client->_httpRequestData.httpResource, (char *)client->_data, safeLength);
|
|
client->_httpRequestData.httpResource[safeLength] = '\0';
|
|
}
|
|
else //Error 500
|
|
{
|
|
sendInfoResponse(HTTP_CODE::HTTP_CODE_INTERNAL_SERVER_ERROR, client, "Failed to allocate memory for resources");
|
|
client->_clientState = TCPClient::ClientState::DISCARDED;
|
|
break;
|
|
}
|
|
|
|
client->freeDataBuffer(safeLength + 1);
|
|
|
|
#ifdef DEBUG_WEBS
|
|
Serial.print("Resrc : ");Serial.println(client->_httpRequestData.httpResource);
|
|
Serial.println((char *)client->_data);
|
|
#endif
|
|
}
|
|
else //Resource is probably too long, so we truncate it
|
|
{
|
|
client->_httpRequestData.httpResource = (char *) malloc(sizeof(char) * (client->_httpRequestData.maxResourceBuffer+1) ); //for \0
|
|
if(client->_httpRequestData.httpResource != NULL)
|
|
{
|
|
strncpy(client->_httpRequestData.httpResource, (char *)client->_data, client->_httpRequestData.maxResourceBuffer);
|
|
client->_httpRequestData.httpResource[client->_httpRequestData.maxResourceBuffer] = '\0';
|
|
}
|
|
else //Error 500
|
|
{
|
|
sendInfoResponse(HTTP_CODE::HTTP_CODE_INTERNAL_SERVER_ERROR, client, "Failed to allocate memory for resources");
|
|
client->_clientState = TCPClient::ClientState::DISCARDED;
|
|
break;
|
|
}
|
|
|
|
client->freeDataBuffer(client->_httpRequestData.maxResourceBuffer + 1);
|
|
}
|
|
client->_httpParserState = HttpParserStatus::HTTP_PARAMS;
|
|
}
|
|
break;
|
|
case HttpParserStatus::HTTP_VERSION:
|
|
{
|
|
char *pEndline = strstr((char *)client->_data, "\r\n");
|
|
|
|
if(pEndline == NULL) pEndline = strstr((char *)client->_data, "\n");
|
|
|
|
char *pVers = strstr((char *)client->_data, "HTTP/");
|
|
|
|
if(pEndline != NULL && pVers!= NULL)
|
|
{
|
|
*pEndline = '\0';
|
|
client->_httpRequestData.HV = getHttpVersionEnumValue(pVers+5);
|
|
|
|
#ifdef DEBUG_WEBS
|
|
Serial.print("Vers : ");Serial.println(pVers+5);
|
|
Serial.print("Vers : ");Serial.println(client->_httpRequestData.HV);
|
|
#endif
|
|
|
|
client->freeDataBuffer((pEndline - (char *)client->_data)+2);
|
|
|
|
#ifdef DEBUG_WEBS
|
|
Serial.println((char *)client->_data);
|
|
#endif
|
|
|
|
client->_httpParserState = HttpParserStatus::HEADER_PARAMS;
|
|
}
|
|
}
|
|
break;
|
|
case HttpParserStatus::HTTP_PARAMS: //index.htm?var1=1&var2=2...
|
|
if(!httpRsrcParamParser(client))
|
|
{
|
|
#ifdef DEBUG_WEBS
|
|
Serial.print("Resrc : ");Serial.println(client->_httpRequestData.httpResource);
|
|
Serial.println("Get params :");
|
|
for(int i = 0; i < client->_httpRequestData.getParams.count(); i++)
|
|
{
|
|
Serial.print(client->_httpRequestData.getParams.getParameter(i));Serial.print(" : ");Serial.println(client->_httpRequestData.getParams.getAt(i)->getString());
|
|
}
|
|
#endif
|
|
client->_httpParserState = HttpParserStatus::HTTP_VERSION;
|
|
}
|
|
break;
|
|
case HttpParserStatus::HEADER_PARAMS: //Here we parse the different header params until we arrive to \r\n\r\n
|
|
{
|
|
char *pEndLine = strstr((char *)client->_data, "\r\n");
|
|
|
|
if( pEndLine != NULL )
|
|
{
|
|
*pEndLine = '\0';
|
|
|
|
httpHeaderParamParser(client);
|
|
|
|
if(*(pEndLine+2) == '\r') //We got \r\n\r\n -> so we go to the post data section
|
|
{
|
|
client->_httpParserState = HttpParserStatus::POST_DATA;
|
|
client->freeDataBuffer((pEndLine - (char *)client->_data) +3); //client->_data must not be empty...
|
|
break;
|
|
}
|
|
|
|
//Before in the buffer : key1: value1\0\nkey2: value2
|
|
//After in the buffer : key2: value2\r\n
|
|
client->freeDataBuffer((pEndLine - (char *)client->_data) + 2);
|
|
}
|
|
else //Error : indeed, we should at least have : \r\n. We go to the next step anyway
|
|
{
|
|
client->_httpParserState = HttpParserStatus::POST_DATA;
|
|
}
|
|
}
|
|
break;
|
|
case HttpParserStatus::POST_DATA:
|
|
|
|
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);
|
|
#endif
|
|
|
|
//we parse it !
|
|
if(!httpPostParamParser(client))
|
|
{
|
|
//Parsing done!
|
|
#ifdef DEBUG_WEBS
|
|
Serial.println("Post params :");
|
|
for(int i = 0; i < client->_httpRequestData.postParams.count(); i++)
|
|
{
|
|
Serial.print(client->_httpRequestData.postParams.getParameter(i));Serial.print(" : ");Serial.println(client->_httpRequestData.postParams.getAt(i)->getString());
|
|
}
|
|
#endif
|
|
client->_WEBClientState = WEBClientState::QUERY_PARSED;
|
|
}
|
|
|
|
break;
|
|
default :
|
|
client->_WEBClientState = WEBClientState::QUERY_PARSED;
|
|
}
|
|
|
|
break;
|
|
default :
|
|
sendInfoResponse(HTTP_CODE::HTTP_CODE_INTERNAL_SERVER_ERROR, client, "WEB server error");
|
|
client->_clientState = TCPClient::ClientState::DISCARDED;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* This function parses the header parameters in order to find particular parameters.
|
|
* For example we look for the "Content-Type" header or for the "Range: bytes=" header
|
|
*/
|
|
void httpHeaderParamParser(T *client)
|
|
{
|
|
#ifdef DEBUG_WEBS
|
|
Serial.printf("Header param : %s\n",(char *)client->_data);
|
|
#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)
|
|
{
|
|
#ifdef DEBUG_WEBS
|
|
Serial.printf("Data is of 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)
|
|
{
|
|
endP = '\0';
|
|
client->_range = strtoul(startingP + 11,NULL, 10);
|
|
#ifdef DEBUG_WEBS
|
|
Serial.printf("Range : %d\n", client->_range);
|
|
#endif
|
|
}*/
|
|
}
|
|
|
|
/*
|
|
* This function is here to parse resources parameters
|
|
*/
|
|
boolean httpRsrcParamParser(T *client)
|
|
{
|
|
char *pGetParam = strchr((char *)client->_httpRequestData.httpResource, '?');
|
|
|
|
|
|
if(pGetParam != NULL) //There are some params to be parsed
|
|
{
|
|
if(client->_httpRequestData.getParamsDataPointer == NULL)
|
|
{
|
|
client->_httpRequestData.getParamsDataPointer = pGetParam +1;//We save the starting position of the string to parse
|
|
}
|
|
|
|
char *key = strchr(client->_httpRequestData.getParamsDataPointer, '=');
|
|
char *value = strchr(client->_httpRequestData.getParamsDataPointer, '&');
|
|
|
|
if(key == NULL && value == NULL) //Only the key is present
|
|
{
|
|
client->_httpRequestData.getParams.add(client->_httpRequestData.getParamsDataPointer, new DictionaryHelper::StringEntity(NULL));
|
|
*pGetParam = '\0';
|
|
return false;
|
|
}
|
|
else if(key != NULL && value != NULL)
|
|
{
|
|
if(key < value)*key = '\0';
|
|
*value = '\0';
|
|
|
|
client->_httpRequestData.getParams.add(client->_httpRequestData.getParamsDataPointer, new DictionaryHelper::StringEntity(key > value ? NULL : key + 1));
|
|
memmove(client->_httpRequestData.getParamsDataPointer, value+1, strlen(value+1)+1);
|
|
|
|
#ifdef DEBUG_WEBS
|
|
Serial.print("Params pointer : ");Serial.println(client->_httpRequestData.getParamsDataPointer);
|
|
#endif
|
|
}
|
|
else if(key != NULL && value == NULL) //Only one key/value pair present
|
|
{
|
|
*key = '\0';
|
|
|
|
client->_httpRequestData.getParams.add(client->_httpRequestData.getParamsDataPointer, new DictionaryHelper::StringEntity(key+1));
|
|
*pGetParam = '\0';
|
|
return false;
|
|
}
|
|
else if(key == NULL && value != NULL)
|
|
{
|
|
*value = '\0';
|
|
|
|
client->_httpRequestData.getParams.add(client->_httpRequestData.getParamsDataPointer, new DictionaryHelper::StringEntity(NULL));
|
|
memmove(client->_httpRequestData.getParamsDataPointer, value+1, strlen(value+1)+1);
|
|
}
|
|
}
|
|
else //nothing to parse or done
|
|
{
|
|
#ifdef DEBUG_WEBS
|
|
Serial.println("Nothing to parse or done");
|
|
#endif
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
boolean httpPostParamParser(T* client)
|
|
{
|
|
if(client->_httpRequestData.postParamsDataPointer == NULL)
|
|
{
|
|
client->_httpRequestData.postParamsDataPointer = (char *)client->_data + 1;//We save the starting position of the string to parse and we ignore the \n
|
|
}
|
|
|
|
char *key = strchr(client->_httpRequestData.postParamsDataPointer, '=');
|
|
char *value = strchr(client->_httpRequestData.postParamsDataPointer, '&');
|
|
|
|
if(key == NULL && value == NULL) //Nothing to parse or done
|
|
{
|
|
return false;
|
|
}
|
|
else if(key != NULL && value == NULL) //Only one key is present
|
|
{
|
|
*key = '\0';
|
|
client->_httpRequestData.postParams.add(client->_httpRequestData.postParamsDataPointer, new DictionaryHelper::StringEntity(key+1));
|
|
return false;
|
|
}
|
|
else if(key != NULL && value != NULL)
|
|
{
|
|
*key = '\0';
|
|
*value = '\0';
|
|
client->_httpRequestData.postParams.add(client->_httpRequestData.postParamsDataPointer, new DictionaryHelper::StringEntity(key+1));
|
|
memmove(client->_httpRequestData.postParamsDataPointer, value +1, strlen(value+1) + 1);
|
|
}
|
|
else if(key == NULL && value != NULL)//Should never happen
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
void sendDataToClient(T *client)
|
|
{
|
|
if(!sendPageToClientFromApiDictio(client)) //Then we check if it is not a file that is requested
|
|
{
|
|
if(!sendPageToClientFromSdCard(client)) //If this function returns false, we close the connection with the client. An error occured (An error message has already been sent) or the whole file has been sent.
|
|
{
|
|
client->_WEBClientState = WEBClientState::RESPONSE_SENT;
|
|
}
|
|
}
|
|
else //If we found the api endpoint, we can close the connection with the client after the data has been sent.
|
|
client->_WEBClientState = WEBClientState::RESPONSE_SENT;
|
|
}
|
|
|
|
boolean sendPageToClientFromApiDictio(T *client)
|
|
{
|
|
if(_apiDictionary.count() == 0 || client->_httpRequestData.httpResource == NULL)
|
|
return false;
|
|
|
|
ApiRoutine *ref = _apiDictionary(client->_httpRequestData.httpResource);
|
|
|
|
if(ref == NULL)
|
|
return false;
|
|
|
|
if(ref->HRM == UNDEFINED)
|
|
{
|
|
return (*(ref->apiRoutine))(client->_httpRequestData, &(client->_client), ref->pData);
|
|
}else if(ref->HRM == client->_httpRequestData.HRM)
|
|
{
|
|
return (*(ref->apiRoutine))(client->_httpRequestData, &(client->_client), ref->pData);
|
|
}
|
|
else
|
|
return false;
|
|
}
|
|
|
|
boolean sendPageToClientFromSdCard(T *client)
|
|
{
|
|
if(_sdCardManager != NULL)
|
|
{
|
|
File pageToSend;
|
|
char *filePath(NULL), *header(NULL);
|
|
uint8_t sendBuffer[READ_WRITE_BUFFER_SIZE];
|
|
int readBytes(0);
|
|
//We check what kind of http verb it is
|
|
switch(client->_httpRequestData.HRM)
|
|
{
|
|
case GET:
|
|
filePath = getFilePathByHttpResource(client->_httpRequestData.httpResource);
|
|
if(filePath == NULL)
|
|
{
|
|
sendInfoResponse(HTTP_CODE::HTTP_CODE_INTERNAL_SERVER_ERROR, client, "Failed to allocate memory for the filePath");
|
|
return false;
|
|
}
|
|
|
|
#ifdef DEBUG_WEBS
|
|
Serial.print("FILE PATH : ");
|
|
Serial.println(filePath);
|
|
#endif
|
|
|
|
pageToSend = _sdCardManager->open(filePath);
|
|
free(filePath);filePath = NULL;
|
|
|
|
//If we couldn't open the file
|
|
if(!pageToSend)
|
|
{
|
|
char *response(NULL);
|
|
|
|
response = (char *) malloc(sizeof(char) * (strlen_P((PGM_P)F("Resource : not found on this server")) + strlen(client->_httpRequestData.httpResource) + 1));
|
|
if(response == NULL)
|
|
{
|
|
sendInfoResponse(HTTP_CODE::HTTP_CODE_INTERNAL_SERVER_ERROR, client, "Failed to allocate memory for the response");
|
|
return false;
|
|
}
|
|
|
|
sprintf(response, "Resource : %s not found on this server", client->_httpRequestData.httpResource);
|
|
sendInfoResponse(HTTP_CODE::HTTP_CODE_NOT_FOUND, client, response);
|
|
|
|
free(response);response = NULL;
|
|
return false;
|
|
}
|
|
|
|
#ifdef DEBUG_WEBS
|
|
Serial.print("FILE SIZE : ");
|
|
Serial.println(pageToSend.size());
|
|
Serial.print("FILE NAME : ");
|
|
Serial.println(pageToSend.name());
|
|
#endif
|
|
|
|
if(pageToSend.isDirectory()) //To DO : List the files present in the directory
|
|
{
|
|
pageToSend.close();
|
|
|
|
sendInfoResponse(HTTP_CODE::HTTP_CODE_FORBIDDEN, client, "The file you want to access is a folder");
|
|
return false;
|
|
}
|
|
|
|
if(client->_fileSentBytes == 0 /*&& client->_range == 0*/)
|
|
{
|
|
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());
|
|
|
|
#ifdef DEBUG_WEBS
|
|
Serial.print("FILE EXTENSION : ");
|
|
Serial.println(getFileExtension(fileName));
|
|
#endif
|
|
|
|
free(fileName);
|
|
|
|
if(header == NULL)
|
|
{
|
|
sendInfoResponse(HTTP_CODE::HTTP_CODE_INTERNAL_SERVER_ERROR, client, "Failed to allocate memory for the header");
|
|
pageToSend.close();
|
|
return false;
|
|
}
|
|
|
|
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())
|
|
{
|
|
readBytes = pageToSend.read(sendBuffer,READ_WRITE_BUFFER_SIZE);
|
|
client->_client.write(sendBuffer, readBytes);
|
|
|
|
#ifdef DEBUG_WEBS
|
|
Serial.print("BYTES SENT : ");
|
|
Serial.println(readBytes);
|
|
#endif
|
|
|
|
client->_fileSentBytes += readBytes; //We save the number of bytes sent so that we can reopen the file to this position later on.
|
|
}
|
|
else
|
|
{
|
|
pageToSend.close();
|
|
return false;
|
|
}
|
|
|
|
pageToSend.close();
|
|
break;
|
|
default: //If not supported
|
|
sendInfoResponse(HTTP_CODE::HTTP_CODE_METHOD_NOT_ALLOWED, client, "The method used is not allowed");
|
|
return false;
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
sendInfoResponse(HTTP_CODE::HTTP_CODE_INTERNAL_SERVER_ERROR, client, "Api endpoint does not exist");
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/*Static helper methods*/
|
|
|
|
static void sendInfoResponse(HTTP_CODE http_code, T *client, const char *message)
|
|
{
|
|
char codeLiteral[100];
|
|
switch(http_code)
|
|
{
|
|
case HTTP_CODE_BAD_REQUEST:
|
|
strcpy_P(codeLiteral,PSTR("Bad Request"));
|
|
break;
|
|
case HTTP_CODE_NOT_FOUND:
|
|
strcpy_P(codeLiteral,PSTR("Not Found"));
|
|
break;
|
|
case HTTP_CODE_FORBIDDEN:
|
|
strcpy_P(codeLiteral,PSTR("Forbidden"));
|
|
break;
|
|
case HTTP_CODE_METHOD_NOT_ALLOWED:
|
|
strcpy_P(codeLiteral,PSTR("Method Not Allowed"));
|
|
break;
|
|
case HTTP_CODE_INTERNAL_SERVER_ERROR:
|
|
strcpy_P(codeLiteral,PSTR("Internal Server Error"));
|
|
break;
|
|
default:
|
|
strcpy_P(codeLiteral,PSTR("Error Not Defined"));
|
|
break;
|
|
}
|
|
client->_client.printf_P(PSTR("HTTP/1.1 %d %s\r\nContent-Type: text/html\r\nContent-Length: %d\r\n\r\n<!DOCTYPE HTML>\r\n<html>\r\n<h1>Error %d</h1><p>%s</p>\r\n</html>"), http_code, codeLiteral, strlen(message) + 56 + (http_code != 0 ? 3:1), http_code , message);
|
|
}
|
|
|
|
static HttpRequestMethod getHttpVerbEnumValue(const char *parseBuffer)
|
|
{
|
|
if(parseBuffer == NULL)return HttpRequestMethod::UNDEFINED;
|
|
|
|
//UNDEFINED, GET, HEAD, POST, PUT, DELETE, CONNECT, OPTIONS, TRACE, PATCH
|
|
if(strcmp(parseBuffer,"GET") == 0){return HttpRequestMethod::GET;}
|
|
else if(strcmp(parseBuffer,"POST") == 0){return HttpRequestMethod::POST;}
|
|
else if(strcmp(parseBuffer,"HEAD") == 0){return HttpRequestMethod::HEAD;}
|
|
else if(strcmp(parseBuffer,"PUT") == 0){return HttpRequestMethod::PUT;}
|
|
else if(strcmp(parseBuffer,"DELETE") == 0){return HttpRequestMethod::DELETE;}
|
|
else if(strcmp(parseBuffer,"CONNECT") == 0){return HttpRequestMethod::CONNECT;}
|
|
else if(strcmp(parseBuffer,"TRACE") == 0){return HttpRequestMethod::TRACE;}
|
|
else if(strcmp(parseBuffer,"PATCH") == 0){return HttpRequestMethod::PATCH;}
|
|
else if(strcmp(parseBuffer,"OPTIONS") == 0){return HttpRequestMethod::OPTIONS;}
|
|
else
|
|
return HttpRequestMethod::UNDEFINED;
|
|
}
|
|
|
|
static HttpMIMEType getMIMETypeByExtension(const char *extension)
|
|
{
|
|
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;
|
|
else if(strcmp(extension, "mp3") == 0) return AUDIO_MPEG;
|
|
else return UNKNOWN_MIME;
|
|
}
|
|
|
|
static char *getHTTPHeader(HttpMIMEType httpMIMEType, size_t size)
|
|
{
|
|
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));
|
|
|
|
switch(httpMIMEType)
|
|
{
|
|
case TEXT_HTML:
|
|
injectHeaderLayout(header,"text/html", size);
|
|
break;
|
|
case TEXT_CSS:
|
|
injectHeaderLayout(header,"text/css", size);
|
|
break;
|
|
case TEXT_JAVASCRIPT:
|
|
injectHeaderLayout(header,"text/javascript", size);
|
|
break;
|
|
case IMAGE_PNG:
|
|
injectHeaderLayout(header,"image/png", size);
|
|
break;
|
|
case IMAGE_JPEG:
|
|
injectHeaderLayout(header,"image/jpeg", size);
|
|
break;
|
|
case TEXT_PLAIN:
|
|
injectHeaderLayout(header,"text/plain", size);
|
|
break;
|
|
case AUDIO_MPEG:
|
|
injectHeaderLayout(header,"audio/mpeg", size);
|
|
break;
|
|
case APPLICATION_OCTET_STREAM:
|
|
injectHeaderLayout(header,"application/octet-stream", size);
|
|
break;
|
|
default:
|
|
injectHeaderLayout(header,"application/octet-stream", size);
|
|
break;
|
|
}
|
|
|
|
return header;
|
|
}
|
|
|
|
static void injectHeaderLayout(char *header, const char *contentType, size_t size)
|
|
{
|
|
sprintf(header,"HTTP/1.1 200 OK\r\nContent-Type: %s\r\nContent-Length: %lu\r\nCache-Control: max-age=31536000\r\n\r\n",contentType,size);
|
|
}
|
|
|
|
static char *getFileExtension(char *name)
|
|
{
|
|
char *p(lastIndexOf(name, '.'));
|
|
|
|
return p != NULL ? p+1 : NULL;
|
|
}
|
|
|
|
static char *getFilePathByHttpResource(char *res)
|
|
{
|
|
uint16_t buffSize = strlen(WWW_DIR) + (strcmp(res, "/") == 0 ? 10:strlen(res)) + 1;//10 for /index.htm +1 for \0
|
|
char *filePath = (char*) malloc( sizeof(char) * buffSize);
|
|
|
|
if(filePath == NULL)
|
|
return NULL;
|
|
|
|
strcpy(filePath, WWW_DIR);
|
|
strcat(filePath, strcmp(res, "/") == 0 ? "/index.htm":res);
|
|
|
|
#ifdef DEBUG_FILEPATH
|
|
Serial.println(res);
|
|
Serial.print("Reserved space : ");Serial.println(buffSize);
|
|
Serial.print("Actual size : ");Serial.println(strlen(filePath));
|
|
Serial.println(filePath);
|
|
#endif
|
|
|
|
return filePath;
|
|
}
|
|
|
|
static HttpVersion getHttpVersionEnumValue(const char *parseBuffer)
|
|
{
|
|
//HTTP_0_9, HTTP_1_1, HTTP_1_0, HTTP_2_0
|
|
if(strcmp(parseBuffer,"1.1") == 0){return HttpVersion::HTTP_1_1;}
|
|
else if(strcmp(parseBuffer,"2.0") == 0){return HttpVersion::HTTP_2_0;}
|
|
else if(strcmp(parseBuffer,"1.0") == 0){return HttpVersion::HTTP_1_0;}
|
|
else if(strcmp(parseBuffer,"0.9") == 0){return HttpVersion::HTTP_0_9;}
|
|
else
|
|
return HttpVersion::UNKNOWN;
|
|
}
|
|
|
|
struct ApiRoutine
|
|
{
|
|
boolean (*apiRoutine)(HttpRequestData&, WiFiClient*, void*);
|
|
void *pData;
|
|
HttpRequestMethod HRM;
|
|
};
|
|
|
|
Dictionary<ApiRoutine> _apiDictionary;
|
|
SDCardManager *_sdCardManager;
|
|
};
|
|
|
|
#endif //WEBSERVER_H
|