From 4a98c5ebc6bf11a971f3756cb028ea0c96196b37 Mon Sep 17 00:00:00 2001 From: Th3maz1ng Date: Fri, 13 Sep 2019 21:15:53 +0200 Subject: [PATCH] Developped new multi client tcp server as well as web server --- src/software_test/tcpServer_test/List.h | 3 +- .../tcpServer_test/TCPClient.cpp | 82 +++++ src/software_test/tcpServer_test/TCPClient.h | 34 ++ .../tcpServer_test/TCPServer.cpp | 117 ------- src/software_test/tcpServer_test/TCPServer.h | 244 ++++++++++---- .../tcpServer_test/TCPWebServer.cpp | 143 -------- .../tcpServer_test/TCPWebServer.h | 17 - .../tcpServer_test/WEBClient.cpp | 37 +++ src/software_test/tcpServer_test/WEBClient.h | 40 +++ src/software_test/tcpServer_test/WEBServer.h | 305 ++++++++++++++++++ .../tcpServer_test/tcpServer_test.ino | 37 ++- 11 files changed, 709 insertions(+), 350 deletions(-) create mode 100644 src/software_test/tcpServer_test/TCPClient.cpp create mode 100644 src/software_test/tcpServer_test/TCPClient.h delete mode 100644 src/software_test/tcpServer_test/TCPServer.cpp delete mode 100644 src/software_test/tcpServer_test/TCPWebServer.cpp delete mode 100644 src/software_test/tcpServer_test/TCPWebServer.h create mode 100644 src/software_test/tcpServer_test/WEBClient.cpp create mode 100644 src/software_test/tcpServer_test/WEBClient.h create mode 100644 src/software_test/tcpServer_test/WEBServer.h diff --git a/src/software_test/tcpServer_test/List.h b/src/software_test/tcpServer_test/List.h index d856117..18476f2 100644 --- a/src/software_test/tcpServer_test/List.h +++ b/src/software_test/tcpServer_test/List.h @@ -99,6 +99,7 @@ public: return cursor->_value; } + T getLast() { T* p = getLastRef(); @@ -112,7 +113,7 @@ public: return value; } } - + T* removeRef(unsigned int index) { unsigned int position(0); diff --git a/src/software_test/tcpServer_test/TCPClient.cpp b/src/software_test/tcpServer_test/TCPClient.cpp new file mode 100644 index 0000000..6655d34 --- /dev/null +++ b/src/software_test/tcpServer_test/TCPClient.cpp @@ -0,0 +1,82 @@ +#include "TCPClient.h" + +#define DEBUG_CL + +TCPClient::TCPClient(WiFiClient client, uint8_t id, uint16_t dataBufferSize) : _client(client), +_clientState(NEW), +_error(OK), +_data(NULL), +_dataSize(0), +_dataBufferSize(dataBufferSize+1), +_newDataAvailable(false), +_id(id) +{ + #ifdef DEBUG_CL + Serial.println("TCPClient : Standard constructor called"); + #endif + + uint8_t *p = (uint8_t *) malloc(sizeof(uint8_t) * _dataBufferSize); + if(p != NULL) + _data = p; + else + _error = MALLOC_ERR; +} + +TCPClient::TCPClient(const TCPClient &Object) : _client(Object._client), +_clientState(Object._clientState), +_error(Object._error), +_data(NULL), +_dataSize(Object._dataSize), +_dataBufferSize(Object._dataBufferSize), +_newDataAvailable(Object._newDataAvailable), +_id(Object._id) +{ + #ifdef DEBUG_CL + Serial.println("TCPClient : Copy constructor called"); + #endif + + uint8_t *p = (uint8_t *) malloc(sizeof(uint8_t) * _dataBufferSize); + if(p != NULL) + { + _data = p; + memcpy(_data,Object._data,_dataSize); + } + else + _error = MALLOC_ERR; +} + +TCPClient::~TCPClient() +{ + #ifdef DEBUG_CL + Serial.println("TCPClient : Destructor called"); + #endif + + free(_data); +} + +bool TCPClient::operator==(TCPClient& Object) +{ + return this->_client == Object._client; +} + +bool TCPClient::closeConnection() +{ + _client.stop(); +} + +void TCPClient::freeDataBuffer(uint16_t size) +{ + if(size > _dataBufferSize-1) size = _dataBufferSize - 1; + + uint16_t secureSize = size > _dataSize ? _dataSize : size; + + if(*(_data + secureSize) == '\0') + { + #ifdef DEBUG_CL + Serial.println("Its the terminating string"); + #endif + } + + strcpy((char *)_data, (char *)_data + secureSize); + _dataSize -= secureSize; +} diff --git a/src/software_test/tcpServer_test/TCPClient.h b/src/software_test/tcpServer_test/TCPClient.h new file mode 100644 index 0000000..582c6bc --- /dev/null +++ b/src/software_test/tcpServer_test/TCPClient.h @@ -0,0 +1,34 @@ +#ifndef TCPCLIENT_H +#define TCPCLIENT_H + +#include + +//Forward class declaration +template class TCPServer; + +class TCPClient +{ + template + friend class TCPServer; + public: + TCPClient(WiFiClient client, uint8_t id, uint16_t dataBufferSize = 255); + TCPClient(const TCPClient &Object); + virtual ~TCPClient(); + bool operator==(TCPClient& Object); + bool closeConnection(); + protected: + enum ClientState {NEW, HANDLED, DISCARDED} _clientState; + enum Error {OK = 0, MALLOC_ERR = 1} _error; + + WiFiClient _client; + uint8_t *_data; //The actual data + uint16_t _dataSize; //The logic size of the data contained + uint16_t _dataBufferSize; //The physic size of the buffer + boolean _newDataAvailable; + uint8_t _id; + + void freeDataBuffer(uint16_t size); + private: +}; + +#endif //TCPCLIENT_H diff --git a/src/software_test/tcpServer_test/TCPServer.cpp b/src/software_test/tcpServer_test/TCPServer.cpp deleted file mode 100644 index 80b6263..0000000 --- a/src/software_test/tcpServer_test/TCPServer.cpp +++ /dev/null @@ -1,117 +0,0 @@ -#include "TCPServer.h" -#define DEBUG - -TCPServer::TCPServer(unsigned int port, uint8_t maxClient) : _wifiServer(port), _port(port), _serverStarted(true), _maxClient(maxClient), _currentClient(NULL) -{ - _wifiServer.begin(); -} - -TCPServer::~TCPServer() -{ - _clientList.dispose(); -} - -void TCPServer::runServer() -{ - handleNewClients(); - getClientData(); -} - -void TCPServer::handleNewClients() -{ - WiFiClient wc; - - if(_maxClient == -1 || _maxClient > _clientList.count()) - { - wc = _wifiServer.available(); - } - - if(wc && wc.connected()) - { - _clientList.addFirst(new TCPServerHelper::TCPServerClient({wc, TCPServerHelper::TCPServerClient::NEW,{'/0'},0,false,_clientList.count(),{TCPServerHelper::UNDEFINED, TCPServerHelper::UNKNOWN, TCPServerHelper::UNKNOWN_MIME, Dictionary(), Dictionary(), NULL,NULL},TCPServerHelper::HTTP_VERB})); - #ifdef DEBUG - Serial.print("New client accepted : ");Serial.println(_clientList.count()-1); - #endif - } - - _currentClient = _clientList.removeLastRef();//We pick a client in the list to process it's request - -} - -void TCPServer::getClientData() -{ - if(_currentClient == NULL) - { - return; - } - - uint32_t bytesAvailable((_currentClient->client).available()); - - if(bytesAvailable) - { - if(_currentClient->dataSize < 255) - { - int freeSpace = (255-1/*for \0*/ - _currentClient->dataSize); - int amountToBeRead = bytesAvailable < freeSpace ? bytesAvailable : freeSpace;//;bytesAvailable < 254 ? bytesAvailable : 254; - - _currentClient->client.read(_currentClient->data + _currentClient->dataSize, amountToBeRead); - _currentClient->dataSize+=amountToBeRead; - _currentClient->data[_currentClient->dataSize] = '\0'; - _currentClient->newDataAvailable = true; - } - } - else if(!(_currentClient->client).connected()) - { - #ifdef DEBUG - Serial.print("Client can be discarded : ");Serial.println(_currentClient->id); - #endif - _currentClient->clientState = TCPServerHelper::TCPServerClient::DISCARDED; - } - - - if(_currentClient->dataSize > 0) - { - processClientData(_currentClient);//We process the actual data - _currentClient->newDataAvailable = false; - } - - if(_currentClient->clientState == TCPServerHelper::TCPServerClient::DISCARDED) - { - _currentClient->client.stop(); - #ifdef DEBUG - Serial.print("Client was discarded : ");Serial.println(_currentClient->id); - #endif - delete _currentClient; - _currentClient = NULL; - return; - } - - _clientList.addFirst(_currentClient); - -} - -void TCPServer::processClientData(TCPServerHelper::TCPServerClient *client) -{ - Serial.print("Client --> ");Serial.print(client->id);Serial.print(" : ");Serial.println((char *)client->data); -} - -uint8_t TCPServer::getMaxClient() -{ - return _maxClient; -} - -uint8_t TCPServer::getClientsCount() -{ - return _clientList.count(); -} - -void TCPServer::startServer() -{ - _serverStarted = true; -} - -void TCPServer::stopServer() -{ - _serverStarted = false; - _clientList.dispose(); -} diff --git a/src/software_test/tcpServer_test/TCPServer.h b/src/software_test/tcpServer_test/TCPServer.h index d0705ad..afc05f0 100644 --- a/src/software_test/tcpServer_test/TCPServer.h +++ b/src/software_test/tcpServer_test/TCPServer.h @@ -1,86 +1,194 @@ #ifndef TCPSERVER_H #define TCPSERVER_H -#include + #include #include "List.h" -#include "Dictionary.h" +#include "TCPClient.h" #define MAX_CLIENT -1 +#define DEBUG -namespace TCPServerHelper -{ - enum HttpRequestMethod {UNDEFINED, GET, HEAD, POST, PUT, DELETE, CONNECT, OPTIONS, TRACE, PATCH}; - enum HttpVersion {UNKNOWN, HTTP_0_9, HTTP_1_1, HTTP_1_0, HTTP_2_0}; - enum HttpMIMEType{UNKNOWN_MIME, TEXT_PLAIN, TEXT_CSS, TEXT_HTML, TEXT_JAVASCRIPT, APPLICATION_JSON, APPLICATION_X_WWW_FORM_URLENCODED, IMAGE_PNG, IMAGE_JPEG, AUDIO_MPEG, APPLICATION_OCTET_STREAM}; - enum HttpParserStatus {HTTP_VERB, HTTP_RESSOURCE, HTTP_VERSION, HTTP_PARAMS, POST_DATA}; - - struct HttpRequestData - { - HttpRequestMethod HRM; - HttpVersion HV; - HttpMIMEType HMT; - Dictionary getParams; - Dictionary postParams; - char *httpResource; - char *httpBody; - }; - - struct TCPServerClient - { - WiFiClient client; - enum ClientState {NEW, HANDLED, QUERY_PARSED, RESPONSE_SENT, DISCARDED} clientState; - uint8_t data[255]; - uint16_t dataSize; - boolean newDataAvailable; - uint8_t id; - HttpRequestData httpRequestData; - HttpParserStatus httpParserState; - - bool operator==(TCPServerClient& Object){return this->client == Object.client;} - void clearHttpRequestData(TCPServerHelper::HttpRequestData *httpRequestData) - { - free(httpRequestData->httpResource);free(httpRequestData->httpBody); - httpRequestData->getParams.dispose(); - httpRequestData->postParams.dispose(); - } - ~TCPServerClient() - { - client.stop(); - clearHttpRequestData(&httpRequestData); - } - void freeDataBuffer(uint16_t size) - { - if(size > 254) size = 254; - - uint16_t secureSize = size > dataSize ? dataSize : size; - strcpy((char *)data, (char *)data + secureSize); - dataSize -= secureSize; - } - }; -} +template class TCPServer { public: - TCPServer(unsigned int port = 80, uint8_t maxClient = MAX_CLIENT); - ~TCPServer(); - uint8_t getMaxClient(); - uint8_t getClientsCount(); - virtual void runServer(); - virtual void startServer(); - virtual void stopServer(); + TCPServer(unsigned int port = 80, uint8_t maxClient = MAX_CLIENT, uint16_t clientDataBufferSize = 255) : _wifiServer(port), _port(port), _serverStarted(true), _maxClient(maxClient), _clientDataBufferSize(clientDataBufferSize), _currentClient(NULL) + { + _wifiServer.begin(); + } + + virtual ~TCPServer() + { + _clientList.dispose(); + } + + uint8_t getMaxClient() + { + return _maxClient; + } + + uint8_t getConnectedClientsCount() + { + return _clientList.count(); + } + + virtual void runServer() + { + handleNewClients(); + getClientData(); + } + + virtual void startServer() + { + if(!_serverStarted) + { + _wifiServer.begin(); + _serverStarted = true; + } + } + + virtual void stopServer() + { + if(_serverStarted) + { + _wifiServer.stop(); + _clientList.dispose(); + _serverStarted = false; + } + } + protected: - private: - virtual void handleNewClients(); - virtual void getClientData(); - virtual void processClientData(TCPServerHelper::TCPServerClient *client); - - boolean _serverStarted; + virtual T* createNewClient(WiFiClient wc) + { + return new T(wc, freeClientId(), _clientDataBufferSize); + } + + virtual void handleNewClients() + { + WiFiClient wc; + + if(_maxClient == -1 || _maxClient > _clientList.count()) + { + wc = _wifiServer.available(); + } + + if(wc && wc.connected()) + { + T *clientPointer = createNewClient(wc); + _clientList.addFirst(clientPointer); + #ifdef DEBUG + Serial.print("TCPServer : New client accepted : ");Serial.println(clientPointer->_id); + #endif + greetClient(clientPointer); + } + + _currentClient = _clientList.removeLastRef();//We pick a client in the list to process it's request + + if(_currentClient != NULL) + { + if(_currentClient->_error != TCPClient::OK) + { + _currentClient->closeConnection(); + delete _currentClient; _currentClient = NULL; + } + } + } + + virtual void greetClient(T *client) + { + client->_client.println(F("Successfully connected !")); + client->_client.print(F("Your are client with id : ")); + client->_client.println(client->_id); + } + + virtual void getClientData() + { + if(_currentClient == NULL) + { + return; + } + + uint32_t bytesAvailable((_currentClient->_client).available()); + + if(bytesAvailable) + { + 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; + } + } + else if(!(_currentClient->_client).connected()) + { + #ifdef DEBUG + Serial.print("TCPServer : Client disconnected and can be discarded : ");Serial.println(_currentClient->_id); + #endif + _currentClient->_clientState = TCPClient::DISCARDED; + } + + if(_currentClient->_dataSize > 0) + { + processClientData(_currentClient);//We process the actual data + _currentClient->_newDataAvailable = false; + } + + if(_currentClient->_clientState == TCPClient::ClientState::DISCARDED) + { + _currentClient->closeConnection(); + #ifdef DEBUG + Serial.print("TCPServer : Client was discarded : ");Serial.println(_currentClient->_id); + #endif + delete _currentClient; + _currentClient = NULL; + + return; + } + + _clientList.addFirst(_currentClient); + + } + + virtual void processClientData(T *client) + { + if(client->_dataSize > 0 && ((char) client->_data[0] != '\r' && (char) client->_data[0] != '\n')) + { + Serial.print("Client --> ");Serial.print(client->_id);Serial.print(" : ");Serial.print((char *)client->_data);Serial.println("#"); + } + + client->freeDataBuffer(client->_dataSize); + } + + uint8_t freeClientId() + { + int listCounter(_clientList.count()); + int freeId(0); + + for(int i(0); i < listCounter; i++) + { + if(_clientList.getRef(i)->_id == freeId) + { + freeId++; + i = -1; + } + } + return freeId; + } + + boolean _serverStarted; uint8_t _maxClient; unsigned int _port; + uint16_t _clientDataBufferSize; WiFiServer _wifiServer; - TCPServerHelper::TCPServerClient *_currentClient; //current client to be processed - List _clientList; + T *_currentClient; //current client to be processed + List _clientList; + private: }; #endif //TCPSERVER_H diff --git a/src/software_test/tcpServer_test/TCPWebServer.cpp b/src/software_test/tcpServer_test/TCPWebServer.cpp deleted file mode 100644 index 50ce476..0000000 --- a/src/software_test/tcpServer_test/TCPWebServer.cpp +++ /dev/null @@ -1,143 +0,0 @@ -#include "TCPWebServer.h" -#define DEBUG - -TCPWebServer::TCPWebServer(unsigned int port, uint8_t maxClient) : TCPServer(port, maxClient) -{ - -} - -void debugInfo() -{ - uint32_t freeMem; - uint16_t biggestContigMemBlock; - uint8_t frag; - ESP.getHeapStats(&freeMem, &biggestContigMemBlock, &frag); - Serial.print("Free MEM : ");Serial.println(freeMem); - Serial.print("Heap Frag : ");Serial.println(frag); -} - -void TCPWebServer::processClientData(TCPServerHelper::TCPServerClient *client) -{ - if(client->newDataAvailable) - { - switch(client->httpParserState) - { - case TCPServerHelper::HTTP_VERB: - { - 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); - #ifdef DEBUG - Serial.print("Verb : ");Serial.println(client->httpRequestData.HRM); - #endif - client->httpParserState = TCPServerHelper::HttpParserStatus::HTTP_RESSOURCE; - } - } - break; - case TCPServerHelper::HTTP_RESSOURCE: - { - char *pRsrc = strstr((char *)client->data, " "); - - if(pRsrc != NULL) - { - *pRsrc = '\0'; - uint16_t length = pRsrc - (char *)client->data; - client->httpRequestData.httpResource = (char *) malloc(sizeof(char) * length + 1 ); //for \0 - if(client->httpRequestData.httpResource != NULL) - strcpy(client->httpRequestData.httpResource, (char *)client->data); - else - { - client->clientState = TCPServerHelper::TCPServerClient::ClientState::DISCARDED; - break; - } - - #ifdef DEBUG - Serial.print("Resrc : ");Serial.println(client->httpRequestData.httpResource); - #endif - client->freeDataBuffer(length + 1); - - client->httpParserState = TCPServerHelper::HttpParserStatus::HTTP_VERSION; - } - else if(client->dataSize == 254) - { - client->httpRequestData.httpResource = (char *) malloc(sizeof(char) * client->dataSize + 1 ); //for \0 - if(client->httpRequestData.httpResource != NULL) - strcpy(client->httpRequestData.httpResource, (char *)client->data); - else - { - client->clientState = TCPServerHelper::TCPServerClient::ClientState::DISCARDED; - break; - } - - #ifdef DEBUG - Serial.print("Resrc : ");Serial.println(client->httpRequestData.httpResource); - #endif - client->freeDataBuffer(client->dataSize); - client->httpParserState = TCPServerHelper::HttpParserStatus::HTTP_VERSION; - } - } - break; - case TCPServerHelper::HTTP_VERSION: - { - char *pEndline = strstr((char *)client->data, "\r\n"); - char *pVers = strstr((char *)client->data, "HTTP/"); - - if(pEndline != NULL && pVers!= NULL) - { - *pEndline = '\0'; - client->httpRequestData.HV = getHttpVersionEnumValue(pVers+5); - #ifdef DEBUG - Serial.print("Vers : ");Serial.println(pVers+5); - Serial.print("Vers : ");Serial.println(client->httpRequestData.HV); - #endif - client->freeDataBuffer((pEndline - (char *)client->data)+2); - client->clientState = TCPServerHelper::TCPServerClient::ClientState::DISCARDED; - } - else //If we do not find it, it's probably split at the end of the buffer, so we ignore some of the resource left over - { - client->freeDataBuffer(100); - } - } - break; - /*case TCPServerHelper::HTTP_PARAMS: - break; - case TCPServerHelper::POST_DATA: - break; - default :*/ - } - /*Serial.print("WEB Client --> ");Serial.print(client->id);Serial.print(" : ");Serial.println((char *)client->data); - client->freeDataBuffer(client->dataSize);*/ - } - -} - -TCPServerHelper::HttpRequestMethod TCPWebServer::getHttpVerbEnumValue(const char *parseBuffer) -{ - //UNDEFINED, GET, HEAD, POST, PUT, DELETE, CONNECT, OPTIONS, TRACE, PATCH - if(strcmp(parseBuffer,"GET") == 0){return TCPServerHelper::HttpRequestMethod::GET;} - else if(strcmp(parseBuffer,"POST") == 0){return TCPServerHelper::HttpRequestMethod::POST;} - else if(strcmp(parseBuffer,"HEAD") == 0){return TCPServerHelper::HttpRequestMethod::HEAD;} - else if(strcmp(parseBuffer,"PUT") == 0){return TCPServerHelper::HttpRequestMethod::PUT;} - else if(strcmp(parseBuffer,"DELETE") == 0){return TCPServerHelper::HttpRequestMethod::DELETE;} - else if(strcmp(parseBuffer,"CONNECT") == 0){return TCPServerHelper::HttpRequestMethod::CONNECT;} - else if(strcmp(parseBuffer,"TRACE") == 0){return TCPServerHelper::HttpRequestMethod::TRACE;} - else if(strcmp(parseBuffer,"PATCH") == 0){return TCPServerHelper::HttpRequestMethod::PATCH;} - else if(strcmp(parseBuffer,"OPTIONS") == 0){return TCPServerHelper::HttpRequestMethod::OPTIONS;} - else - return TCPServerHelper::HttpRequestMethod::UNDEFINED; -} - -TCPServerHelper::HttpVersion TCPWebServer::getHttpVersionEnumValue(const char *parseBuffer) -{ - //HTTP_0_9, HTTP_1_1, HTTP_1_0, HTTP_2_0 - if(strcmp(parseBuffer,"1.1") == 0){return TCPServerHelper::HttpVersion::HTTP_1_1;} - else if(strcmp(parseBuffer,"2.0") == 0){return TCPServerHelper::HttpVersion::HTTP_2_0;} - else if(strcmp(parseBuffer,"1.0") == 0){return TCPServerHelper::HttpVersion::HTTP_1_0;} - else if(strcmp(parseBuffer,"0.9") == 0){return TCPServerHelper::HttpVersion::HTTP_0_9;} - else - return TCPServerHelper::HttpVersion::UNKNOWN; -} diff --git a/src/software_test/tcpServer_test/TCPWebServer.h b/src/software_test/tcpServer_test/TCPWebServer.h deleted file mode 100644 index 63826c6..0000000 --- a/src/software_test/tcpServer_test/TCPWebServer.h +++ /dev/null @@ -1,17 +0,0 @@ -#ifndef TCPWEBSERVER_H -#define TCPWEBSERVER_H - -#include "TCPServer.h" - -class TCPWebServer : public TCPServer -{ - public: - TCPWebServer(unsigned int port = 80, uint8_t maxClient = MAX_CLIENT); - protected: - private: - virtual void processClientData(TCPServerHelper::TCPServerClient *client); - TCPServerHelper::HttpRequestMethod getHttpVerbEnumValue(const char *parseBuffer); - TCPServerHelper::HttpVersion getHttpVersionEnumValue(const char *parseBuffer); -}; - -#endif //TCPWEBSERVER_H \ No newline at end of file diff --git a/src/software_test/tcpServer_test/WEBClient.cpp b/src/software_test/tcpServer_test/WEBClient.cpp new file mode 100644 index 0000000..bbe920f --- /dev/null +++ b/src/software_test/tcpServer_test/WEBClient.cpp @@ -0,0 +1,37 @@ +#include "WEBClient.h" + +#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::WEBClientState::ACCEPTED), _httpParserState(WEBServer::HttpParserStatus::HTTP_VERB) +{ + #ifdef DEBUG_WEBCL + Serial.println("WEBClient : Standard constructor called"); + #endif + + _httpRequestData.HRM = WEBServer::HttpRequestMethod::UNDEFINED; + _httpRequestData.HV = WEBServer::HttpVersion::UNKNOWN; + _httpRequestData.HMT = WEBServer::HttpMIMEType::UNKNOWN_MIME; + + _httpRequestData.getParamsDataPointer = NULL; + _httpRequestData.postParamsDataPointer = NULL; + + _httpRequestData.httpResource = NULL; + _httpRequestData.maxResourceBuffer = maxResourceBuffer; + _httpRequestData.httpBody = NULL; + _httpRequestData.maxBodyBuffer = maxBodyBuffer; +} + +WEBClient::~WEBClient() +{ + #ifdef DEBUG_WEBCL + Serial.println("WEBClient : Destructor called"); + #endif + clearHttpRequestData(); +} + +void WEBClient::clearHttpRequestData() +{ + free(_httpRequestData.httpResource);free(_httpRequestData.httpBody); + _httpRequestData.getParams.dispose(); + _httpRequestData.postParams.dispose(); +} diff --git a/src/software_test/tcpServer_test/WEBClient.h b/src/software_test/tcpServer_test/WEBClient.h new file mode 100644 index 0000000..f840a8f --- /dev/null +++ b/src/software_test/tcpServer_test/WEBClient.h @@ -0,0 +1,40 @@ +#ifndef WEBCLIENT_H +#define WEBCLIENT_H + +#include "WEBServer.h" +#include "TCPClient.h" +#include "Dictionary.h" + +class WEBClient : public TCPClient +{ + template + friend class WEBServer; + public: + WEBClient(WiFiClient client, uint8_t id, uint16_t maxResourceBuffer = 255, uint16_t maxBodyBuffer = 255, uint16_t dataBufferSize = 511); + virtual ~WEBClient(); + protected: + WEBServer::WEBClientState _WEBClientState; + private: + struct HttpRequestData + { + WEBServer::HttpRequestMethod HRM; + WEBServer::HttpVersion HV; + WEBServer::HttpMIMEType HMT; + + Dictionary getParams; + char *getParamsDataPointer; //Used in the getParams algorithm + Dictionary postParams; + char *postParamsDataPointer; //Used in the postParams algorithm + + char *httpResource; + uint16_t maxResourceBuffer; + char *httpBody; + uint16_t maxBodyBuffer; + } _httpRequestData; + + WEBServer::HttpParserStatus _httpParserState; + + void clearHttpRequestData(); +}; + +#endif //WEBCLIENT_H diff --git a/src/software_test/tcpServer_test/WEBServer.h b/src/software_test/tcpServer_test/WEBServer.h new file mode 100644 index 0000000..21d4dde --- /dev/null +++ b/src/software_test/tcpServer_test/WEBServer.h @@ -0,0 +1,305 @@ +#ifndef WEBSERVER_H +#define WEBSERVER_H + +#include "TCPServer.h" +#include "Dictionary.h" +#define DEBUG_WEBS + +template +class WEBServer : public TCPServer +{ + public: + enum HttpRequestMethod {UNDEFINED, GET, HEAD, POST, PUT, DELETE, CONNECT, OPTIONS, TRACE, PATCH}; + enum HttpVersion {UNKNOWN, HTTP_0_9, HTTP_1_1, HTTP_1_0, HTTP_2_0}; + enum HttpMIMEType{UNKNOWN_MIME, TEXT_PLAIN, TEXT_CSS, TEXT_HTML, TEXT_JAVASCRIPT, APPLICATION_JSON, APPLICATION_X_WWW_FORM_URLENCODED, IMAGE_PNG, IMAGE_JPEG, AUDIO_MPEG, APPLICATION_OCTET_STREAM}; + enum HttpParserStatus {HTTP_VERB, HTTP_RESSOURCE, HTTP_VERSION, HTTP_PARAMS, POST_DATA}; + enum WEBClientState {ACCEPTED, QUERY_PARSED, RESPONSE_SENT, DONE}; + enum HTTP_CODE {_400, _401, _403, _404, _500, _501}; + + WEBServer(unsigned int port = 80, uint8_t maxClient = MAX_CLIENT, uint16_t clientDataBufferSize = 512) : TCPServer(port, maxClient, clientDataBufferSize) {} + protected: + private: + virtual T* createNewClient(WiFiClient wc) + { + return new T(wc, TCPServer::freeClientId()); + } + + virtual void greetClient(T *client) + { + + } + + virtual void processClientData(T *client) + { + switch(client->_WEBClientState) + { + case ACCEPTED: + queryParser(client); + break; + case QUERY_PARSED: + sendDataToClient(client); + break; + case RESPONSE_SENT: + break; + case DONE: + 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::_400, 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::_400, client, "The server could not understand the request due to invalid syntax"); + client->_clientState = TCPClient::ClientState::DISCARDED; + break; + } + } + 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::_500, 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::_500, 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"); + 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::POST_DATA; + } + } + break; + case HttpParserStatus::HTTP_PARAMS: //index.htm?var1=1&var2=2... + if(!httpParamParser(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::POST_DATA: + + client->_WEBClientState = WEBClientState::QUERY_PARSED; + break; + default : + sendInfoResponse(HTTP_CODE::_500, client, "WEB server error"); + client->_clientState = TCPClient::ClientState::DISCARDED; + break; + } + } + + boolean httpParamParser(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)); + strcpy(client->_httpRequestData.getParamsDataPointer,value+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)); + strcpy(client->_httpRequestData.getParamsDataPointer,value+1); + } + } + else //nothing to parse or done + { + return false; + } + } + + void sendDataToClient(T *client) + { + + } + + void sendInfoResponse(HTTP_CODE http_code, T *client, const char *message) + { + switch(http_code) + { + case _500: + client->_client.print(F("HTTP/1.1 500 Internal Server Error\r\nContent-Type: text/html\r\nContent-Length: ")); + client->_client.print(strlen(message) + 59); + client->_client.print(F("\r\n\r\n\r\n\r\n

Error 500

")); + client->_client.print(message); + client->_client.print(F("

\r\n")); + break; + case _400: + client->_client.print(F("HTTP/1.1 400 Bad Request\r\nContent-Type: text/html\r\nContent-Length: ")); + client->_client.print(strlen(message) + 59); + client->_client.print(F("\r\n\r\n\r\n\r\n

Error 400

")); + client->_client.print(message); + client->_client.print(F("

\r\n")); + break; + } + } + + /*Static helper methods*/ + + static HttpRequestMethod getHttpVerbEnumValue(const char *parseBuffer) + { + //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 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; + } +}; + +#endif //WEBSERVER_H diff --git a/src/software_test/tcpServer_test/tcpServer_test.ino b/src/software_test/tcpServer_test/tcpServer_test.ino index 31dda11..a146d64 100644 --- a/src/software_test/tcpServer_test/tcpServer_test.ino +++ b/src/software_test/tcpServer_test/tcpServer_test.ino @@ -1,12 +1,20 @@ /** - * This sketch was written in order to developp and test the multi client tcp server which will be used in my project + * This sketch was written in order to developp and test the multi-client tcp server which will later be used in my project * Anatole SCHRAMM-HENRY 08/05/2019 */ -#include "TCPWebServer.h" +#include "TCPClient.h" +#include "TCPServer.h" +#include "WEBServer.h" +#include "WEBClient.h" + +uint32_t lastFreeMem(0); +uint16_t lastClientCount(0); + +TCPServer server(80, MAX_CLIENT, 5); +WEBServer webServer(8080); WiFiEventHandler gotIpEventHandler, disconnectedEventHandler; -TCPWebServer tws(80,2); void setup() { // put your setup code here, to run once: @@ -19,9 +27,30 @@ void setup() { WiFi.begin("freebox_Henry","eustache1930"); } +void debugInfo() +{ + uint32_t freeMem; + uint16_t biggestContigMemBlock; + uint8_t frag; + ESP.getHeapStats(&freeMem, &biggestContigMemBlock, &frag); + if(lastFreeMem != freeMem) + { + Serial.print("Free MEM : ");Serial.println(freeMem); + Serial.print("Heap Frag : ");Serial.println(frag); + lastFreeMem = freeMem; + } + if(lastClientCount != server.getConnectedClientsCount()) + { + lastClientCount = server.getConnectedClientsCount(); + Serial.print("Connected client(s) : ");Serial.println(lastClientCount); + } +} + void loop() { // put your main code here, to run repeatedly: - tws.runServer(); + server.runServer(); + webServer.runServer(); + debugInfo(); }