From a951cdd6fcc3a8ab7d96704267aee239ad1c1da4 Mon Sep 17 00:00:00 2001 From: anschrammh Date: Wed, 5 Oct 2022 08:20:15 +0200 Subject: [PATCH] Added the WEBServerManager class to enable the initialization of the WEBServer using a config file, I might change the behaviour of this initialization when some parameters are missing, renamed the old WEBServerManager class to WEBServerManager_deprecated, now using the new WEBServerManager in the SAB object instead of the WEBServer directly, minor changes to the OTAManager, migrating the dashboard v2 layout so that every tile has the same size --- sdCard_content/CONFIG/SERVER.CFG | 4 +- sdCard_content/WWW/indexV2.htm | 275 ++++++----- src/app/OTAManager.cpp | 18 +- src/app/OTAManager.h | 4 +- src/app/SAB.cpp | 18 +- src/app/SAB.h | 10 +- src/app/WEBServer.h | 5 + src/app/WEBServerManager.cpp | 577 +++--------------------- src/app/WEBServerManager.h | 87 +--- src/app/WEBServerManager_deprecated.cpp | 552 +++++++++++++++++++++++ src/app/WEBServerManager_deprecated.h | 81 ++++ src/app/app.ino | 43 +- src/app/tasks.cpp | 9 +- src/app/tasks.h | 1 + 14 files changed, 932 insertions(+), 752 deletions(-) create mode 100644 src/app/WEBServerManager_deprecated.cpp create mode 100644 src/app/WEBServerManager_deprecated.h diff --git a/sdCard_content/CONFIG/SERVER.CFG b/sdCard_content/CONFIG/SERVER.CFG index c3978d2..42b0589 100644 --- a/sdCard_content/CONFIG/SERVER.CFG +++ b/sdCard_content/CONFIG/SERVER.CFG @@ -7,7 +7,7 @@ WEB_ENABLED : 'true' WEB_PORT : 80 -WEB_MAX_CLIENT : 0 +WEB_MAX_CLIENT : 10 WEB_WWW_DIR : '/WWW' FTP_ENABLED : 'true' FTP_LOGIN : 'ESP8266' @@ -15,4 +15,4 @@ FTP_PASSWORD : '12345678' FTP_ROOT_DIR : '/FTP' FTP_PORT : 21 FTP_DATA_PORT : 1024 -FTP_MAX_CLIENT : 0 +FTP_MAX_CLIENT : 10 diff --git a/sdCard_content/WWW/indexV2.htm b/sdCard_content/WWW/indexV2.htm index 15a70b2..2530b13 100644 --- a/sdCard_content/WWW/indexV2.htm +++ b/sdCard_content/WWW/indexV2.htm @@ -14,6 +14,10 @@ { /*flex: 1 0 auto;*/ } + + td,tr + { + } .switch label input[type=checkbox]:checked+.lever { @@ -116,18 +120,28 @@
-
-
-
-
Connectivity
-
-
-
-
- Access Point Mode + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
Connectivity
-
-
+
+
+
System Info
+
+
+
+
+
+
Access Point Mode
+
+
+
+
+
+ +
-
- - -
-
- -
-
-
- Station Mode -
-
-
+ +
+
+
Station Mode
+
+
+
-
-
- - -
-
-
-
-
-
-
System Info
-
-
-
-
- System State +
+
+
+ +
+
+ +
-
+
+ +
+
+
System State
+
+
+
Cpu frequency : @@ -242,79 +251,131 @@
-
+
-
-
- -
-
+
Clock
-
-
-
- DS3231 RTC +
+
+
Storage
+
+
+
+
Services
+
+
+
+
+
+
DS3231 RTC
+
+
+
+ + +
+
+
+
+ +
+
+ +
+
- -
- - -
-
- - +
+
+
SD Card
+
+
+
+
+ +
+
+
+ Size : +
+
+ NaN Gbytes +
+
+ Free : +
+
+ XX GBytes +
+
+
+
+ + + + + + + +
+ + + + + + + +
+ +
+
+
+
-
-
Storage
-
+
-
-
- SD Card -
-
-
- -
-
-
- Size : -
-
- NaN Gbytes -
-
- Free : -
-
- XX GBytes -
-
-
-
+
-
-
Services
-
diff --git a/src/app/OTAManager.cpp b/src/app/OTAManager.cpp index 8c96b5c..8cfef66 100644 --- a/src/app/OTAManager.cpp +++ b/src/app/OTAManager.cpp @@ -2,7 +2,6 @@ #include "definition.h" #define DEBUG_OTA_MANAGER(...) do {} while(0) - //#define DEBUG_OTA_MANAGER(...) do { Serial.printf(__VA_ARGS__); Serial.println();} while(0) OTAManager::OTAManager(SDCardManager &sdCardManager, const BoardConfig &boardConfig) : _sdCardManager(&sdCardManager), _boardConfig(&boardConfig) @@ -12,18 +11,11 @@ OTAManager::~OTAManager() { } boolean OTAManager::init(void) -{ - //If the SDCardManager is not available, then we disable the OTA service - if(!_sdCardManager) - { - DEBUG_OTA_MANAGER("SDCardMng is NULL !"); - return true; - } - - //If the SDCardManager is available, then we try to read the config file +{ + //We try to read the config file CFGDictionary *otaCfg = _sdCardManager->getCFGFile(OTA_CFG_FILE); boolean toReturn(true); - //If we did not find the file + //If we did not find the file or we didn't manage to open it ! if(!otaCfg) { DEBUG_OTA_MANAGER("otaCfg is NULL !"); @@ -68,13 +60,17 @@ boolean OTAManager::init(void) else { DEBUG_OTA_MANAGER("OTA_SERVER_ADDRESS is NULL !"); + _isServiceEnabled = false; toReturn = false; } } + else + _isServiceEnabled = false; } else { DEBUG_OTA_MANAGER("ENABLED is NULL !"); + _isServiceEnabled = false; toReturn = false; } diff --git a/src/app/OTAManager.h b/src/app/OTAManager.h index 1c1fd08..8ce6e7a 100644 --- a/src/app/OTAManager.h +++ b/src/app/OTAManager.h @@ -14,9 +14,10 @@ class OTAManager boolean init(void); boolean isEnabled(void) const; OTAUpdater& getOTAUpdater(void); + protected: OTAManager(SDCardManager &sdCardManager, const BoardConfig &boardConfig); - OTAManager(); + private: void updateStartedCb(void); void updateFinishedCb(void); @@ -27,6 +28,7 @@ class OTAManager SDCardManager *_sdCardManager = nullptr; const BoardConfig * _boardConfig = nullptr; boolean _isServiceEnabled = false; + }; #endif //OTAMANAGER_H \ No newline at end of file diff --git a/src/app/SAB.cpp b/src/app/SAB.cpp index eab8021..3379104 100644 --- a/src/app/SAB.cpp +++ b/src/app/SAB.cpp @@ -4,7 +4,7 @@ SAB::SAB() : _sdCardManager(_boardConfig.getSPI_SDCard_cs(), _boardConfig.getSPI _display(_boardConfig.getScreenWidth(),_boardConfig.getScreenHeight(), &Wire), _screenManager(_display, &_sdCardManager), _connectivityManager(_sdCardManager), -_webServer(80, &_sdCardManager, 10), +_webServerManager(_sdCardManager), _ftpServer(&_sdCardManager, 21, "ESP8266", "12345678", 10), _otaManager(_sdCardManager, _boardConfig), _dbWSServer(81), @@ -15,12 +15,12 @@ _taskSchedulerManager(_rtcManager) initCommonConfig(); } -SAB::SAB(const BoardConfig boardConfig, const unsigned int webServerPort, const unsigned int ftpServerPort) : _boardConfig(boardConfig), +SAB::SAB(const BoardConfig boardConfig, const unsigned int ftpServerPort) : _boardConfig(boardConfig), _sdCardManager(_boardConfig.getSPI_SDCard_cs(), _boardConfig.getSPISpeed()), _display(_boardConfig.getScreenWidth(), _boardConfig.getScreenHeight(), &Wire), _screenManager(_display, &_sdCardManager), _connectivityManager(_sdCardManager), -_webServer(webServerPort, &_sdCardManager, 10), +_webServerManager(_sdCardManager), _ftpServer(&_sdCardManager, ftpServerPort, "ESP8266", "12345678", 10), _otaManager(_sdCardManager, _boardConfig), _dbWSServer(81), @@ -64,16 +64,14 @@ void SAB::initCommonConfig() _screenManager.init(); if(!_connectivityManager.connect()){ _error |= CONNECT_ERR;} if(!_pcf.begin()){_error |= IO_INIT_ERR;} - if(!_otaManager.init()){_error |= OTA_INIT_ERR;} + if(!_otaManager.init()){_error |= OTAMAN_INIT_ERR;} + if(!_webServerManager.init()){_error |= WEBSRVMAN_INIT_ERR;} //We set the different servers : - _webServer.setWWWDir(WWW_DIR); _ftpServer.setFTPDir(FTP_DIR); //We start the servers _dbWSServer.begin(); - _webServer.enableTCPKeepAlive(15,5,5); _ftpServer.enableTCPKeepAlive(15,5,5); - _webServer.start(); _ftpServer.start(); } @@ -106,9 +104,9 @@ ConnectivityManager& SAB::getConnectivityManager() return _connectivityManager; } -WEBServer& SAB::getWebServer() +WEBServerManager& SAB::getWebServerManager() { - return _webServer; + return _webServerManager; } FTPServer& SAB::getFtpServer() @@ -158,7 +156,7 @@ uint8_t SAB::getError() const void SAB::run() { - _webServer.run(); + _webServerManager.getWEBServer().run(); _ftpServer.run(); _taskSchedulerManager.run(); _screenManager.run(); diff --git a/src/app/SAB.h b/src/app/SAB.h index 13154a0..3e928b2 100644 --- a/src/app/SAB.h +++ b/src/app/SAB.h @@ -6,7 +6,7 @@ #include "ScreenManager.h" #include "SDCardManager.h" #include "ConnectivityManager.h" -#include "WEBClient.h" //includes WEBServer internally +#include "WEBServerManager.h" #include "FTPClient.h" //includes FTPServer internally #include "DashboardWSServer.h" #include "IOManager.h" @@ -21,10 +21,10 @@ class SAB { public: - enum Error {RTC_BEGIN_ERR = 1, DISP_BEGIN_ERR = 2, SDCARD_INIT_ERR = 4, IO_INIT_ERR = 8, CONNECT_ERR = 16, OTA_INIT_ERR = 32}; + enum Error {RTC_BEGIN_ERR = 1 << 0, DISP_BEGIN_ERR = 1 << 1, SDCARD_INIT_ERR = 1 << 2, IO_INIT_ERR = 1 << 3, CONNECT_ERR = 1 << 4, OTAMAN_INIT_ERR = 1 << 5, WEBSRVMAN_INIT_ERR = 1 << 6}; SAB(); - SAB(const BoardConfig boardConfig, const unsigned int webServerPort = 80, const unsigned int ftpServerPort = 21); + SAB(const BoardConfig boardConfig, const unsigned int ftpServerPort = 21); ~SAB() { } @@ -34,7 +34,7 @@ class SAB RTC_DS3231& getRTC_DS3231(); SDCardManager& getSdCardManager(); ConnectivityManager& getConnectivityManager(); - WEBServer& getWebServer(); + WEBServerManager& getWebServerManager(); FTPServer& getFtpServer(); OTAManager& getOTAManager(); IOManager& getIOManager(); @@ -57,7 +57,7 @@ class SAB RTC_DS3231 _rtc; RtcManager _rtcManager; ConnectivityManager _connectivityManager; - WEBServer _webServer; + WEBServerManager _webServerManager; FTPServer _ftpServer; OTAManager _otaManager; DashboardWSServer _dbWSServer; diff --git a/src/app/WEBServer.h b/src/app/WEBServer.h index 1e0a3a0..2611bed 100644 --- a/src/app/WEBServer.h +++ b/src/app/WEBServer.h @@ -93,6 +93,11 @@ class WEBServer : public TCPServer, public HttpConstants } } + void setSDClass(SDClass *sdClass) + { + _sdClass = sdClass; + } + const char * getWWWDir(void) const { return _WWWDir; diff --git a/src/app/WEBServerManager.cpp b/src/app/WEBServerManager.cpp index aaa625c..1b7faf9 100644 --- a/src/app/WEBServerManager.cpp +++ b/src/app/WEBServerManager.cpp @@ -1,552 +1,83 @@ -/** - * @file WEBServerManager.cpp - * @author Anatole SCHRAMM-HENRY - * @brief Single client WEB Server. - * This class is now retired and replaced by the much better WEBServer class - * which handles multiclients among other things. - * @version 0.1 - * @date 31/03/2019 - * - * @copyright MIT - * - */ #include "WEBServerManager.h" +#include "definition.h" -//#define DEBUG -#define DEBUG_BODY -//#define DEBUG_PARAMETER -//#define DEBUG_CONTENT_LENGTH -//#define DEBUG_RAW -//#define DEBUG_FILEPATH +//#define DEBUG_WEB_SERVER_MANAGER(...) do {} while(0) +#define DEBUG_WEB_SERVER_MANAGER(...) do { Serial.printf(__VA_ARGS__); Serial.println();} while(0) -WEBServerManager::WEBServerManager(unsigned int port, SDCardManager *sdCardManager) : _wifiServer(port), _sdCardManager(sdCardManager), _httpRequestData({UNDEFINED, UNKNOWN, UNKNOWN_MIME, Dictionary(), Dictionary(), NULL,NULL}), _httpParserState(INIT), _clientState(WAITING_FOR_CLIENT), _port(port), _clientTimeout(0) +WEBServerManager::WEBServerManager(SDCardManager &sdCardManager) : _sdCardManager(&sdCardManager), _webServer(80, &sdCardManager, 10) { - _wifiServer.begin(); + } -boolean WEBServerManager::addApiRoutine(const char *uri, boolean (*apiRoutine)(HttpRequestData&, WiFiClient* , void*), void *pData, HttpRequestMethod HRM) +WEBServerManager::~WEBServerManager() { - return _apiDictionary.add(uri,new ApiRoutine({apiRoutine,pData, HRM})); + } -void WEBServerManager::clearApiRoutine() +boolean WEBServerManager::init(void) { - _apiDictionary.clear(); -} - -boolean WEBServerManager::removeApiRoutine(const char *uri) -{ - return _apiDictionary.remove(uri); -} - -boolean WEBServerManager::runServer() -{ - switch(_clientState) - { - case WAITING_FOR_CLIENT: - _wifiClient.stopAll(); - - _wifiClient = _wifiServer.available(); - if(_wifiClient) - { - _clientState = NEW; - #ifdef DEBUG - Serial.println("Client connected !!!"); - #endif - } - break; - case NEW: - _clientState = NOT_HANDLED; - break; - case NOT_HANDLED: - clearHttpRequestData(); - parseQuery(&_wifiClient); - #ifdef DEBUG - Serial.println("Nothing more from client !!!"); - #endif - _clientState = QUERY_PARSED; - break; - case QUERY_PARSED: - #ifdef DEBUG - Serial.println("Sending response !!!"); - #endif - //We first check if it's an api call - if(!sendPageToClientFromApiDictio(&_wifiClient)) - { - sendPageToClientFromSdCard(&_wifiClient); - } - _clientState = RESPONSE_SENT; - break; - case RESPONSE_SENT: - #ifdef DEBUG - Serial.println("Client handled !!!"); - #endif - _clientState = HANDLED; - break; - case HANDLED: - _wifiClient.stopAll(); - _clientState = WAITING_FOR_CLIENT; - #ifdef DEBUG - Serial.println("Client discarded !!!"); - #endif - break; - default: - break; - } - return true; -} - -boolean WEBServerManager::parseQuery(WiFiClient *wifiClient) -{ - char readChar(0), *parseBuffer(NULL), *parseKey(NULL), *parseValue(NULL), *parseParameter(NULL), *contentLength(NULL); - boolean isKey(true), receivingDone(false); - unsigned int activeTimeout = 10000; - unsigned long dataBytesCounter = 0, dataBytes = 0; - - /* Better way to read data - char temp[2048]; - temp[wifiClient->read((uint8_t*)temp,2040)] = '\0'; - Serial.print(temp); - */ - _httpParserState = INIT; - _clientTimeout = millis(); - boolean slashesOrAntiSlashesOnly(true); - while((wifiClient->available() || ( millis() - _clientTimeout < activeTimeout)) && wifiClient->connected()) - { - if(wifiClient->available()) + CFGDictionary *serverCfg = _sdCardManager->getCFGFile(SERVER_CFG_FILE); + boolean toReturn(true); + + //We were not able to read the config file, so we apply default parameters + if(!serverCfg) { - readChar = (char)wifiClient->read(); - - #ifdef DEBUG_RAW - Serial.print(readChar); - #endif - //INIT, LINE_BREAK, HTTP_VERB_SECTION, HTTP_RESOURCE_SECTION, HTTP_VER_SECTION, BODY_SECTION, IGNORED, ERROR - switch(_httpParserState) - { - case INIT: - if(readChar >= 65 && readChar <= 90) - { - parseBuffer = addChar(parseBuffer, readChar); - _httpParserState = HTTP_VERB_SECTION; - } - else - _httpParserState = ERROR; - break; - case LINE_BREAK: - if(readChar == '\n') - { - if(_httpRequestData.HRM == GET) - { - #ifdef DEBUG - Serial.println("GET DONE"); - #endif - receivingDone = true; - } - _httpParserState = BODY_SECTION; - } - else if(readChar != '\r') - { - if(parseParameter != NULL) - { - contentLength = strstr(parseParameter, "ent-Len");//Matches Content-Length short to save some RAM - if(contentLength != NULL) - { - dataBytes = strtol(contentLength+11,NULL,10); - #ifdef DEBUG_CONTENT_LENGTH - Serial.print("Data length : ");Serial.println(dataBytes); - #endif - } + DEBUG_WEB_SERVER_MANAGER("serverCfg is NULL, applying default parameters !"); + _webServer.setWWWDir(WWW_DIR); + _webServer.enableTCPKeepAlive(15,5,5); + _webServer.start(); + _isServiceEnabled = true; + return toReturn; + } - #ifdef DEBUG_PARAMETER - Serial.println(parseParameter); - #endif - free(parseParameter);parseParameter = NULL; - } - parseParameter = addChar(parseParameter, readChar); - _httpParserState = PARAMETER_SECTION; - } - break; - case HTTP_VERB_SECTION: - if(readChar >= 65 && readChar <= 90) - { - parseBuffer = addChar(parseBuffer, readChar); - _httpParserState = HTTP_VERB_SECTION; - } - else if (readChar == ' ') - { - //This is the end of the section - _httpRequestData.HRM = getHttpVerbEnumValue(parseBuffer); - free(parseBuffer);parseBuffer = NULL; - _httpParserState = HTTP_RESOURCE_SECTION; - } - else - _httpParserState = ERROR; - break; - case HTTP_RESOURCE_SECTION: - if(readChar == '?' ) - { - free(_httpRequestData.httpResource);_httpRequestData.httpResource = NULL; - _httpRequestData.httpResource = parseBuffer;parseBuffer = NULL; + //We managed to read the config file, now we can apply it ! + if((*serverCfg)("WEB_ENABLED")) + { + if((*serverCfg)("WEB_ENABLED")->booleanValue()) + { + _isServiceEnabled = true; - _httpParserState = HTTP_RESOURCE_PARAM_SECTION; - } - else if(readChar == ' ') - { - free(_httpRequestData.httpResource);_httpRequestData.httpResource = NULL; - if(slashesOrAntiSlashesOnly) + if((*serverCfg)("WEB_PORT")) { - free(parseBuffer);parseBuffer = NULL; - _httpRequestData.httpResource = (char *) malloc(sizeof(char)*2); - strcpy(_httpRequestData.httpResource,"/"); + DEBUG_WEB_SERVER_MANAGER("WEB_PORT is %u", (*serverCfg)("WEB_PORT")->uintValue()); + _webServer.setPort((*serverCfg)("WEB_PORT")->uintValue()); } - else - _httpRequestData.httpResource = parseBuffer;parseBuffer = NULL; - - _httpParserState = HTTP_VER_SECTION; - } - else - { - if(readChar != '/' && readChar != '\\') slashesOrAntiSlashesOnly = false; - parseBuffer = addChar(parseBuffer, readChar); - _httpParserState = HTTP_RESOURCE_SECTION; - } - break; - case HTTP_RESOURCE_PARAM_SECTION: //index.web?var1=1&var2=2... - if(readChar == ' ') + if((*serverCfg)("WEB_MAX_CLIENT")) { - _httpRequestData.getParams.add(parseKey,new DictionaryHelper::StringEntity(parseValue)); - free(parseKey);free(parseValue); - parseKey = NULL;parseValue = NULL; - _httpParserState = HTTP_VER_SECTION; + DEBUG_WEB_SERVER_MANAGER("WEB_MAX_CLIENT is %u", (*serverCfg)("WEB_MAX_CLIENT")->uintValue()); + _webServer.setMaxClient((*serverCfg)("WEB_MAX_CLIENT")->uintValue()); } - else if( readChar == '=') - isKey = false; - else if(readChar == '&') + + if((*serverCfg)("WEB_WWW_DIR")) { - isKey = true; - _httpRequestData.getParams.add(parseKey, new DictionaryHelper::StringEntity(parseValue)); - free(parseKey);free(parseValue); - parseKey = NULL;parseValue = NULL; + DEBUG_WEB_SERVER_MANAGER("WEB_WWW_DIR is %s", (*serverCfg)("WEB_WWW_DIR")->stringValue()); + _webServer.setWWWDir((*serverCfg)("WEB_WWW_DIR")->stringValue()); } - else - { - if(isKey) - parseKey = addChar(parseKey, readChar); - else - parseValue = addChar(parseValue, readChar); - } - break; - case HTTP_VER_SECTION: - if((readChar >= 48 && readChar <= 57) || readChar == '.') - { - parseBuffer = addChar(parseBuffer, readChar); - _httpParserState = HTTP_VER_SECTION; - } - else if(readChar == '\n') - { - _httpRequestData.HV = getHttpVersionEnumValue(parseBuffer); - free(parseBuffer);parseBuffer = NULL; - _httpParserState = LINE_BREAK; - } - break; - case BODY_SECTION: - //parseBuffer = addChar(parseBuffer, readChar); - if(_httpRequestData.HRM != GET) - { - dataBytesCounter++;//Should be always true - } - #ifdef DEBUG_BODY - Serial.print(readChar); - #endif - break; - case PARAMETER_SECTION: //Here are all the http header params - if(readChar == '\n') - { - _httpParserState = LINE_BREAK; - }else - parseParameter = addChar(parseParameter, readChar); - break; - case IGNORED: - break; - case ERROR: - return false; - break; //Not necessary - default : - break; - } - //Exit condition - if(receivingDone) break; - if(_httpRequestData.HRM == POST && dataBytes != 0 && dataBytes == dataBytesCounter) break; - - _clientTimeout = millis(); - } - //yield(); //Likely causing a crash - ESP.wdtFeed(); - } - if(parseBuffer != NULL) - { - if(strlen(parseBuffer) > 0) + _webServer.start(); + } + else + { + _webServer.stop(); + _isServiceEnabled = false; + } + } + else { - _httpRequestData.httpBody = parseBuffer; - parseBuffer = NULL; + DEBUG_WEB_SERVER_MANAGER("WEB_ENABLED is NULL !"); + toReturn = false; } - } - - free(parseParameter);parseParameter = NULL; - - #ifdef DEBUG - Serial.print("HTTP VERB : "); - Serial.println(_httpRequestData.HRM); - Serial.print("HTTP RESOURCE : "); - Serial.println(_httpRequestData.httpResource); - Serial.print("HTTP VERSION : "); - Serial.println(_httpRequestData.HV); - Serial.print("BODY CONTENT : "); - Serial.println(_httpRequestData.httpBody); - Serial.println("GET PARAMS :"); - for(int i = 0; i < _httpRequestData.getParams.count(); i++) - { - Serial.print(_httpRequestData.getParams.getParameter(i));Serial.print(" : ");Serial.println(_httpRequestData.getParams.getAt(i)->getString()); - } - #endif - - return true; + + return toReturn; } -unsigned int WEBServerManager::getPort() const +boolean WEBServerManager::isEnabled(void) const { - return _port; + return _isServiceEnabled; } -boolean WEBServerManager::sendPageToClientFromSdCard(WiFiClient *wifiClient) +WEBServer &WEBServerManager::getWEBServer(void) { - if(_sdCardManager != NULL) - { - File pageToSend; - char readChar(0), *filePath(NULL), *header(NULL), sendBuffer[2048]; - - //We check what kind of http verb it is - switch(_httpRequestData.HRM) - { - case GET: - filePath = getFilePathByHttpResource(_httpRequestData.httpResource); - if(filePath == NULL) - { - wifiClient->print(F("HTTP/1.1 500 Internal Server Error\r\nContent-Type: text/html\r\n\r\n\r\n\r\n

Failed to malloc filePath

\r\n")); - return false; - } - - #ifdef DEBUG - Serial.print("FILE PATH : "); - Serial.println(filePath); - #endif - - pageToSend = _sdCardManager->open(filePath); - free(filePath);filePath = NULL; - - if(!pageToSend) - { - char *response(NULL); - response = (char *) malloc(sizeof(char) * (strlen("HTTP/1.1 404 Not Found\r\nContent-Type: text/html\r\n\r\n\r\n\r\n

Page not found for :

\r\n

\r\n") + strlen(_httpRequestData.httpResource) + 1)); - if(response == NULL) - { - wifiClient->print(F("HTTP/1.1 500 Internal Server Error\r\nContent-Type: text/html\r\n\r\n\r\n\r\n

Failed to malloc response

\r\n")); - return false; - } - sprintf(response, "HTTP/1.1 404 Not Found\r\nContent-Type: text/html\r\n\r\n\r\n\r\n

Page not found for :

\r\n

%s

\r\n", _httpRequestData.httpResource); - wifiClient->print(response); - free(response);response = NULL; - pageToSend.close(); - return false; - } - - #ifdef DEBUG - Serial.print("FILE SIZE : "); - Serial.println(pageToSend.size()); - Serial.print("FILE NAME : "); - Serial.println(pageToSend.name()); - Serial.print("FILE EXTENSION : "); - //Serial.println(getFileExtension(pageToSend.name())); - #endif - - header = getHTTPHeader(getMIMETypeByExtension(getFileExtension(/*pageToSend.name()*/ "dummy")), pageToSend.size()); - if(header == NULL) - { - wifiClient->print(F("HTTP/1.1 500 Internal Server Error\r\nContent-Type: text/html\r\n\r\n\r\n\r\n

Failed to malloc header

\r\n")); - pageToSend.close(); - return false; - } - - wifiClient->print(header); - free(header);header = NULL; - - while(pageToSend.available()) - { - //if(wifiClient->write(sendBuffer, pageToSend.read(sendBuffer,2048)) == 0) - break; - } - - pageToSend.close(); - break; - default: //If not supported - wifiClient->print(F("HTTP/1.1 405 Method Not Allowed\r\nContent-Type: text/html\r\n\r\n\r\n\r\n

Method Not Allowed

\r\n")); - break; - } - }else - { - wifiClient->print(F("HTTP/1.1 500 Internal Server Error\r\nContent-Type: text/html\r\n\r\n\r\n\r\n

SDCardManager is NULL
Check code

\r\n")); - } - - return true; -} - -boolean WEBServerManager::sendPageToClientFromApiDictio(WiFiClient *wifiClient) -{ - if(_apiDictionary.count() == 0 || _httpRequestData.httpResource == NULL) - return false; - - ApiRoutine *ref = _apiDictionary(_httpRequestData.httpResource); - - if(ref == NULL) - return false; - - if(ref->HRM == UNDEFINED) - { - return (*(ref->apiRoutine))(_httpRequestData, wifiClient, ref->pData); - }else if(ref->HRM == _httpRequestData.HRM) - { - return (*(ref->apiRoutine))(_httpRequestData, wifiClient, ref->pData); - } - else - return false; -} - -WEBServerManager::HttpMIMEType WEBServerManager::getMIMETypeByExtension(const char *extension) -{ - //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; -} - -char *WEBServerManager::getHTTPHeader(HttpMIMEType httpMIMEType, unsigned long size) -{ - char *header = (char *) malloc(sizeof(char) + strlen("HTTP/1.1 200 OK\r\nContent-Type: \r\nContent-Length: \r\n\r\n") + 74/*Longest MIME-TYPE*/ + 10 /*Max unsigned long footprint*/ + 1); - - switch(httpMIMEType) - { - case TEXT_HTML: - sprintf(header,"HTTP/1.1 200 OK\r\nContent-Type: %s\r\nContent-Length: %lu\r\n\r\n","text/html",size); - break; - case TEXT_CSS: - sprintf(header,"HTTP/1.1 200 OK\r\nContent-Type: %s\r\nContent-Length: %lu\r\n\r\n","text/css",size); - break; - case TEXT_JAVASCRIPT: - sprintf(header,"HTTP/1.1 200 OK\r\nContent-Type: %s\r\nContent-Length: %lu\r\n\r\n","text/javascript",size); - break; - case IMAGE_PNG: - sprintf(header,"HTTP/1.1 200 OK\r\nContent-Type: %s\r\nContent-Length: %lu\r\n\r\n","image/png",size); - break; - case IMAGE_JPEG: - sprintf(header,"HTTP/1.1 200 OK\r\nContent-Type: %s\r\nContent-Length: %lu\r\n\r\n","image/jpeg",size); - break; - case TEXT_PLAIN: - sprintf(header,"HTTP/1.1 200 OK\r\nContent-Type: %s\r\nContent-Length: %lu\r\n\r\n","text/plain",size); - break; - case AUDIO_MPEG: - sprintf(header,"HTTP/1.1 200 OK\r\nContent-Type: %s\r\nContent-Length: %lu\r\n\r\n","audio/mpeg",size); - break; - case APPLICATION_OCTET_STREAM: - sprintf(header,"HTTP/1.1 200 OK\r\nContent-Type: %s\r\nContent-Length: %lu\r\n\r\n","application/octet-stream",size); - break; - default: - sprintf(header,"HTTP/1.1 200 OK\r\nContent-Type: %s\r\nContent-Length: %lu\r\n\r\n","application/octet-stream",size); - break; - break; - } - - return header; -} - -char *WEBServerManager::getFileExtension(char *name) -{ - if(name == NULL)return ""; - - char *ptr(name); - int index(0); - while(ptr[index] != '\0') - { - if(ptr[++index] == '.') - { - return (name + index +1); - } - } - return ""; -} - -char *WEBServerManager::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); - //sprintf(filePath,"%s%s",WWW_DIR, 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; -} - -WEBServerManager::HttpRequestMethod WEBServerManager::getHttpVerbEnumValue(const char *parseBuffer) -{ - //UNDEFINED, GET, HEAD, POST, PUT, DELETE, CONNECT, OPTIONS, TRACE, PATCH - if(strcmp(parseBuffer,"GET") == 0){return GET;} - else if(strcmp(parseBuffer,"POST") == 0){return POST;} - else if(strcmp(parseBuffer,"HEAD") == 0){return HEAD;} - else if(strcmp(parseBuffer,"PUT") == 0){return PUT;} - else if(strcmp(parseBuffer,"DELETE") == 0){return DELETE;} - else if(strcmp(parseBuffer,"CONNECT") == 0){return CONNECT;} - else if(strcmp(parseBuffer,"TRACE") == 0){return TRACE;} - else if(strcmp(parseBuffer,"PATCH") == 0){return PATCH;} - else if(strcmp(parseBuffer,"OPTIONS") == 0){return OPTIONS;} - else - return UNDEFINED; -} - -WEBServerManager::HttpVersion WEBServerManager::getHttpVersionEnumValue(const char *parseBuffer) -{ - //HTTP_0_9, HTTP_1_1, HTTP_1_0, HTTP_2_0 - if(strcmp(parseBuffer,"1.1") == 0){return HTTP_1_1;} - else if(strcmp(parseBuffer,"2.0") == 0){return HTTP_2_0;} - else if(strcmp(parseBuffer,"1.0") == 0){return HTTP_1_0;} - else if(strcmp(parseBuffer,"0.9") == 0){return HTTP_0_9;} - else - return UNKNOWN; -} - -void WEBServerManager::clearHttpRequestData() -{ - _httpRequestData.HRM = UNDEFINED; - _httpRequestData.HV = UNKNOWN; - _httpRequestData.HMT = UNKNOWN_MIME; - free(_httpRequestData.httpResource);free(_httpRequestData.httpBody); - _httpRequestData.httpResource = NULL;_httpRequestData.httpBody = NULL; - _httpRequestData.getParams.dispose(); - _httpRequestData.postParams.dispose(); -} + return _webServer; +} \ No newline at end of file diff --git a/src/app/WEBServerManager.h b/src/app/WEBServerManager.h index 20e225f..fa80e79 100644 --- a/src/app/WEBServerManager.h +++ b/src/app/WEBServerManager.h @@ -1,81 +1,26 @@ -/** - * @file WEBServerManager.h - * @author Anatole SCHRAMM-HENRY - * @brief Single client WEB Server. - * This class is now retired and replaced by the much better WEBServer class - * which handles multiclients among other things. - * @version 0.1 - * @date 31/03/2019 - * - * @copyright MIT - * - */ #ifndef WEBSERVERMANAGER_H #define WEBSERVERMANAGER_H -#include -#include #include "SDCardManager.h" -#include "definition.h" -#include "Dictionary.h" - -struct HttpRequestData; +#include "WEBClient.h" //includes WEBServer internally class WEBServerManager { - public: - enum ClientStatus {NOT_HANDLED, HANDLED, NEW, WAITING_FOR_CLIENT, QUERY_PARSED, RESPONSE_SENT}; - 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 HttpParserStatus {INIT, LINE_BREAK, HTTP_VERB_SECTION, HTTP_RESOURCE_SECTION, HTTP_RESOURCE_PARAM_SECTION, HTTP_VER_SECTION, PARAMETER_SECTION, BODY_SECTION, IGNORED, ERROR}; - 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}; - struct HttpRequestData{ - HttpRequestMethod HRM; - HttpVersion HV; - HttpMIMEType HMT; - Dictionary getParams; - Dictionary postParams; - char *httpResource; - char *httpBody; - }; - - WEBServerManager(unsigned int port = 80, SDCardManager *sdCardManager = NULL); + friend class SAB; + public: + ~WEBServerManager(); + boolean init(void); + boolean isEnabled(void) const; + WEBServer &getWEBServer(void); - boolean runServer(); - unsigned int getPort() const; - boolean addApiRoutine(const char *uri, boolean (*apiRoutine)(HttpRequestData&, WiFiClient*, void*), void *pData, HttpRequestMethod HRM = UNDEFINED); - void clearApiRoutine(); - boolean removeApiRoutine(const char *uri); - protected: - private: - struct ApiRoutine - { - boolean (*apiRoutine)(HttpRequestData&, WiFiClient*, void*); - void *pData; - HttpRequestMethod HRM; - }; - - boolean parseQuery(WiFiClient *wifiClient); - boolean sendPageToClientFromSdCard(WiFiClient *wifiClient); - boolean sendPageToClientFromApiDictio(WiFiClient *wifiClient); - HttpRequestMethod getHttpVerbEnumValue(const char *parseBuffer); - HttpVersion getHttpVersionEnumValue(const char *parseBuffer); - char *getFilePathByHttpResource(char *res); - char *getFileExtension(char *name); - HttpMIMEType getMIMETypeByExtension(const char *extension); - char *getHTTPHeader(HttpMIMEType httpMIMEType, unsigned long size); - void clearHttpRequestData(); - - WiFiServer _wifiServer; - WiFiClient _wifiClient;//One client only, may be replaced with a fifo in the future - SDCardManager *_sdCardManager; - HttpRequestData _httpRequestData; - Dictionary _apiDictionary; - - ClientStatus _clientState; - HttpParserStatus _httpParserState; - unsigned long _clientTimeout; - unsigned int _port; + protected: + WEBServerManager(SDCardManager &sdCardManager); + + private: + SDCardManager *_sdCardManager = nullptr; + boolean _isServiceEnabled = false; + WEBServer _webServer; + }; -#endif //WEBSERVERMANAGER_H +#endif //WEBSERVERMANAGER_H \ No newline at end of file diff --git a/src/app/WEBServerManager_deprecated.cpp b/src/app/WEBServerManager_deprecated.cpp new file mode 100644 index 0000000..c81bd31 --- /dev/null +++ b/src/app/WEBServerManager_deprecated.cpp @@ -0,0 +1,552 @@ +/** + * @file WEBServerManager.cpp + * @author Anatole SCHRAMM-HENRY + * @brief Single client WEB Server. + * This class is now retired and replaced by the much better WEBServer class + * which handles multiclients among other things. + * @version 0.1 + * @date 31/03/2019 + * + * @copyright MIT + * + */ +#include "WEBServerManager_deprecated.h" + +//#define DEBUG +#define DEBUG_BODY +//#define DEBUG_PARAMETER +//#define DEBUG_CONTENT_LENGTH +//#define DEBUG_RAW +//#define DEBUG_FILEPATH + +WEBServerManager_deprecated::WEBServerManager_deprecated(unsigned int port, SDCardManager *sdCardManager) : _wifiServer(port), _sdCardManager(sdCardManager), _httpRequestData({UNDEFINED, UNKNOWN, UNKNOWN_MIME, Dictionary(), Dictionary(), NULL,NULL}), _httpParserState(INIT), _clientState(WAITING_FOR_CLIENT), _port(port), _clientTimeout(0) +{ + _wifiServer.begin(); +} + +boolean WEBServerManager_deprecated::addApiRoutine(const char *uri, boolean (*apiRoutine)(HttpRequestData&, WiFiClient* , void*), void *pData, HttpRequestMethod HRM) +{ + return _apiDictionary.add(uri,new ApiRoutine({apiRoutine,pData, HRM})); +} + +void WEBServerManager_deprecated::clearApiRoutine() +{ + _apiDictionary.clear(); +} + +boolean WEBServerManager_deprecated::removeApiRoutine(const char *uri) +{ + return _apiDictionary.remove(uri); +} + +boolean WEBServerManager_deprecated::runServer() +{ + switch(_clientState) + { + case WAITING_FOR_CLIENT: + _wifiClient.stopAll(); + + _wifiClient = _wifiServer.available(); + if(_wifiClient) + { + _clientState = NEW; + #ifdef DEBUG + Serial.println("Client connected !!!"); + #endif + } + break; + case NEW: + _clientState = NOT_HANDLED; + break; + case NOT_HANDLED: + clearHttpRequestData(); + parseQuery(&_wifiClient); + #ifdef DEBUG + Serial.println("Nothing more from client !!!"); + #endif + _clientState = QUERY_PARSED; + break; + case QUERY_PARSED: + #ifdef DEBUG + Serial.println("Sending response !!!"); + #endif + //We first check if it's an api call + if(!sendPageToClientFromApiDictio(&_wifiClient)) + { + sendPageToClientFromSdCard(&_wifiClient); + } + _clientState = RESPONSE_SENT; + break; + case RESPONSE_SENT: + #ifdef DEBUG + Serial.println("Client handled !!!"); + #endif + _clientState = HANDLED; + break; + case HANDLED: + _wifiClient.stopAll(); + _clientState = WAITING_FOR_CLIENT; + #ifdef DEBUG + Serial.println("Client discarded !!!"); + #endif + break; + default: + break; + } + return true; +} + +boolean WEBServerManager_deprecated::parseQuery(WiFiClient *wifiClient) +{ + char readChar(0), *parseBuffer(NULL), *parseKey(NULL), *parseValue(NULL), *parseParameter(NULL), *contentLength(NULL); + boolean isKey(true), receivingDone(false); + unsigned int activeTimeout = 10000; + unsigned long dataBytesCounter = 0, dataBytes = 0; + + /* Better way to read data + char temp[2048]; + temp[wifiClient->read((uint8_t*)temp,2040)] = '\0'; + Serial.print(temp); + */ + _httpParserState = INIT; + _clientTimeout = millis(); + boolean slashesOrAntiSlashesOnly(true); + while((wifiClient->available() || ( millis() - _clientTimeout < activeTimeout)) && wifiClient->connected()) + { + if(wifiClient->available()) + { + readChar = (char)wifiClient->read(); + + #ifdef DEBUG_RAW + Serial.print(readChar); + #endif + //INIT, LINE_BREAK, HTTP_VERB_SECTION, HTTP_RESOURCE_SECTION, HTTP_VER_SECTION, BODY_SECTION, IGNORED, ERROR + switch(_httpParserState) + { + case INIT: + if(readChar >= 65 && readChar <= 90) + { + parseBuffer = addChar(parseBuffer, readChar); + _httpParserState = HTTP_VERB_SECTION; + } + else + _httpParserState = ERROR; + break; + case LINE_BREAK: + if(readChar == '\n') + { + if(_httpRequestData.HRM == GET) + { + #ifdef DEBUG + Serial.println("GET DONE"); + #endif + receivingDone = true; + } + _httpParserState = BODY_SECTION; + } + else if(readChar != '\r') + { + if(parseParameter != NULL) + { + contentLength = strstr(parseParameter, "ent-Len");//Matches Content-Length short to save some RAM + if(contentLength != NULL) + { + dataBytes = strtol(contentLength+11,NULL,10); + #ifdef DEBUG_CONTENT_LENGTH + Serial.print("Data length : ");Serial.println(dataBytes); + #endif + } + + #ifdef DEBUG_PARAMETER + Serial.println(parseParameter); + #endif + free(parseParameter);parseParameter = NULL; + } + parseParameter = addChar(parseParameter, readChar); + _httpParserState = PARAMETER_SECTION; + } + break; + case HTTP_VERB_SECTION: + if(readChar >= 65 && readChar <= 90) + { + parseBuffer = addChar(parseBuffer, readChar); + _httpParserState = HTTP_VERB_SECTION; + } + else if (readChar == ' ') + { + //This is the end of the section + _httpRequestData.HRM = getHttpVerbEnumValue(parseBuffer); + free(parseBuffer);parseBuffer = NULL; + _httpParserState = HTTP_RESOURCE_SECTION; + } + else + _httpParserState = ERROR; + break; + case HTTP_RESOURCE_SECTION: + if(readChar == '?' ) + { + free(_httpRequestData.httpResource);_httpRequestData.httpResource = NULL; + _httpRequestData.httpResource = parseBuffer;parseBuffer = NULL; + + _httpParserState = HTTP_RESOURCE_PARAM_SECTION; + } + else if(readChar == ' ') + { + free(_httpRequestData.httpResource);_httpRequestData.httpResource = NULL; + if(slashesOrAntiSlashesOnly) + { + free(parseBuffer);parseBuffer = NULL; + _httpRequestData.httpResource = (char *) malloc(sizeof(char)*2); + strcpy(_httpRequestData.httpResource,"/"); + } + else + _httpRequestData.httpResource = parseBuffer;parseBuffer = NULL; + + _httpParserState = HTTP_VER_SECTION; + } + else + { + if(readChar != '/' && readChar != '\\') slashesOrAntiSlashesOnly = false; + + parseBuffer = addChar(parseBuffer, readChar); + _httpParserState = HTTP_RESOURCE_SECTION; + } + break; + case HTTP_RESOURCE_PARAM_SECTION: //index.web?var1=1&var2=2... + if(readChar == ' ') + { + _httpRequestData.getParams.add(parseKey,new DictionaryHelper::StringEntity(parseValue)); + free(parseKey);free(parseValue); + parseKey = NULL;parseValue = NULL; + _httpParserState = HTTP_VER_SECTION; + } + else if( readChar == '=') + isKey = false; + else if(readChar == '&') + { + isKey = true; + _httpRequestData.getParams.add(parseKey, new DictionaryHelper::StringEntity(parseValue)); + free(parseKey);free(parseValue); + parseKey = NULL;parseValue = NULL; + } + else + { + if(isKey) + parseKey = addChar(parseKey, readChar); + else + parseValue = addChar(parseValue, readChar); + } + break; + case HTTP_VER_SECTION: + if((readChar >= 48 && readChar <= 57) || readChar == '.') + { + parseBuffer = addChar(parseBuffer, readChar); + _httpParserState = HTTP_VER_SECTION; + } + else if(readChar == '\n') + { + _httpRequestData.HV = getHttpVersionEnumValue(parseBuffer); + free(parseBuffer);parseBuffer = NULL; + _httpParserState = LINE_BREAK; + } + break; + case BODY_SECTION: + //parseBuffer = addChar(parseBuffer, readChar); + if(_httpRequestData.HRM != GET) + { + dataBytesCounter++;//Should be always true + } + #ifdef DEBUG_BODY + Serial.print(readChar); + #endif + break; + case PARAMETER_SECTION: //Here are all the http header params + if(readChar == '\n') + { + _httpParserState = LINE_BREAK; + }else + parseParameter = addChar(parseParameter, readChar); + break; + case IGNORED: + break; + case ERROR: + return false; + break; //Not necessary + default : + break; + } + //Exit condition + if(receivingDone) break; + if(_httpRequestData.HRM == POST && dataBytes != 0 && dataBytes == dataBytesCounter) break; + + _clientTimeout = millis(); + } + //yield(); //Likely causing a crash + ESP.wdtFeed(); + } + + if(parseBuffer != NULL) + { + if(strlen(parseBuffer) > 0) + { + _httpRequestData.httpBody = parseBuffer; + parseBuffer = NULL; + } + } + + free(parseParameter);parseParameter = NULL; + + #ifdef DEBUG + Serial.print("HTTP VERB : "); + Serial.println(_httpRequestData.HRM); + Serial.print("HTTP RESOURCE : "); + Serial.println(_httpRequestData.httpResource); + Serial.print("HTTP VERSION : "); + Serial.println(_httpRequestData.HV); + Serial.print("BODY CONTENT : "); + Serial.println(_httpRequestData.httpBody); + Serial.println("GET PARAMS :"); + for(int i = 0; i < _httpRequestData.getParams.count(); i++) + { + Serial.print(_httpRequestData.getParams.getParameter(i));Serial.print(" : ");Serial.println(_httpRequestData.getParams.getAt(i)->getString()); + } + #endif + + return true; +} + +unsigned int WEBServerManager_deprecated::getPort() const +{ + return _port; +} + +boolean WEBServerManager_deprecated::sendPageToClientFromSdCard(WiFiClient *wifiClient) +{ + if(_sdCardManager != NULL) + { + File pageToSend; + char readChar(0), *filePath(NULL), *header(NULL), sendBuffer[2048]; + + //We check what kind of http verb it is + switch(_httpRequestData.HRM) + { + case GET: + filePath = getFilePathByHttpResource(_httpRequestData.httpResource); + if(filePath == NULL) + { + wifiClient->print(F("HTTP/1.1 500 Internal Server Error\r\nContent-Type: text/html\r\n\r\n\r\n\r\n

Failed to malloc filePath

\r\n")); + return false; + } + + #ifdef DEBUG + Serial.print("FILE PATH : "); + Serial.println(filePath); + #endif + + pageToSend = _sdCardManager->open(filePath); + free(filePath);filePath = NULL; + + if(!pageToSend) + { + char *response(NULL); + response = (char *) malloc(sizeof(char) * (strlen("HTTP/1.1 404 Not Found\r\nContent-Type: text/html\r\n\r\n\r\n\r\n

Page not found for :

\r\n

\r\n") + strlen(_httpRequestData.httpResource) + 1)); + if(response == NULL) + { + wifiClient->print(F("HTTP/1.1 500 Internal Server Error\r\nContent-Type: text/html\r\n\r\n\r\n\r\n

Failed to malloc response

\r\n")); + return false; + } + sprintf(response, "HTTP/1.1 404 Not Found\r\nContent-Type: text/html\r\n\r\n\r\n\r\n

Page not found for :

\r\n

%s

\r\n", _httpRequestData.httpResource); + wifiClient->print(response); + free(response);response = NULL; + pageToSend.close(); + return false; + } + + #ifdef DEBUG + Serial.print("FILE SIZE : "); + Serial.println(pageToSend.size()); + Serial.print("FILE NAME : "); + Serial.println(pageToSend.name()); + Serial.print("FILE EXTENSION : "); + //Serial.println(getFileExtension(pageToSend.name())); + #endif + + header = getHTTPHeader(getMIMETypeByExtension(getFileExtension(/*pageToSend.name()*/ "dummy")), pageToSend.size()); + if(header == NULL) + { + wifiClient->print(F("HTTP/1.1 500 Internal Server Error\r\nContent-Type: text/html\r\n\r\n\r\n\r\n

Failed to malloc header

\r\n")); + pageToSend.close(); + return false; + } + + wifiClient->print(header); + free(header);header = NULL; + + while(pageToSend.available()) + { + //if(wifiClient->write(sendBuffer, pageToSend.read(sendBuffer,2048)) == 0) + break; + } + + pageToSend.close(); + break; + default: //If not supported + wifiClient->print(F("HTTP/1.1 405 Method Not Allowed\r\nContent-Type: text/html\r\n\r\n\r\n\r\n

Method Not Allowed

\r\n")); + break; + } + }else + { + wifiClient->print(F("HTTP/1.1 500 Internal Server Error\r\nContent-Type: text/html\r\n\r\n\r\n\r\n

SDCardManager is NULL
Check code

\r\n")); + } + + return true; +} + +boolean WEBServerManager_deprecated::sendPageToClientFromApiDictio(WiFiClient *wifiClient) +{ + if(_apiDictionary.count() == 0 || _httpRequestData.httpResource == NULL) + return false; + + ApiRoutine *ref = _apiDictionary(_httpRequestData.httpResource); + + if(ref == NULL) + return false; + + if(ref->HRM == UNDEFINED) + { + return (*(ref->apiRoutine))(_httpRequestData, wifiClient, ref->pData); + }else if(ref->HRM == _httpRequestData.HRM) + { + return (*(ref->apiRoutine))(_httpRequestData, wifiClient, ref->pData); + } + else + return false; +} + +WEBServerManager_deprecated::HttpMIMEType WEBServerManager_deprecated::getMIMETypeByExtension(const char *extension) +{ + //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; +} + +char *WEBServerManager_deprecated::getHTTPHeader(HttpMIMEType httpMIMEType, unsigned long size) +{ + char *header = (char *) malloc(sizeof(char) + strlen("HTTP/1.1 200 OK\r\nContent-Type: \r\nContent-Length: \r\n\r\n") + 74/*Longest MIME-TYPE*/ + 10 /*Max unsigned long footprint*/ + 1); + + switch(httpMIMEType) + { + case TEXT_HTML: + sprintf(header,"HTTP/1.1 200 OK\r\nContent-Type: %s\r\nContent-Length: %lu\r\n\r\n","text/html",size); + break; + case TEXT_CSS: + sprintf(header,"HTTP/1.1 200 OK\r\nContent-Type: %s\r\nContent-Length: %lu\r\n\r\n","text/css",size); + break; + case TEXT_JAVASCRIPT: + sprintf(header,"HTTP/1.1 200 OK\r\nContent-Type: %s\r\nContent-Length: %lu\r\n\r\n","text/javascript",size); + break; + case IMAGE_PNG: + sprintf(header,"HTTP/1.1 200 OK\r\nContent-Type: %s\r\nContent-Length: %lu\r\n\r\n","image/png",size); + break; + case IMAGE_JPEG: + sprintf(header,"HTTP/1.1 200 OK\r\nContent-Type: %s\r\nContent-Length: %lu\r\n\r\n","image/jpeg",size); + break; + case TEXT_PLAIN: + sprintf(header,"HTTP/1.1 200 OK\r\nContent-Type: %s\r\nContent-Length: %lu\r\n\r\n","text/plain",size); + break; + case AUDIO_MPEG: + sprintf(header,"HTTP/1.1 200 OK\r\nContent-Type: %s\r\nContent-Length: %lu\r\n\r\n","audio/mpeg",size); + break; + case APPLICATION_OCTET_STREAM: + sprintf(header,"HTTP/1.1 200 OK\r\nContent-Type: %s\r\nContent-Length: %lu\r\n\r\n","application/octet-stream",size); + break; + default: + sprintf(header,"HTTP/1.1 200 OK\r\nContent-Type: %s\r\nContent-Length: %lu\r\n\r\n","application/octet-stream",size); + break; + break; + } + + return header; +} + +char *WEBServerManager_deprecated::getFileExtension(char *name) +{ + if(name == NULL)return ""; + + char *ptr(name); + int index(0); + while(ptr[index] != '\0') + { + if(ptr[++index] == '.') + { + return (name + index +1); + } + } + return ""; +} + +char *WEBServerManager_deprecated::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); + //sprintf(filePath,"%s%s",WWW_DIR, 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; +} + +WEBServerManager_deprecated::HttpRequestMethod WEBServerManager_deprecated::getHttpVerbEnumValue(const char *parseBuffer) +{ + //UNDEFINED, GET, HEAD, POST, PUT, DELETE, CONNECT, OPTIONS, TRACE, PATCH + if(strcmp(parseBuffer,"GET") == 0){return GET;} + else if(strcmp(parseBuffer,"POST") == 0){return POST;} + else if(strcmp(parseBuffer,"HEAD") == 0){return HEAD;} + else if(strcmp(parseBuffer,"PUT") == 0){return PUT;} + else if(strcmp(parseBuffer,"DELETE") == 0){return DELETE;} + else if(strcmp(parseBuffer,"CONNECT") == 0){return CONNECT;} + else if(strcmp(parseBuffer,"TRACE") == 0){return TRACE;} + else if(strcmp(parseBuffer,"PATCH") == 0){return PATCH;} + else if(strcmp(parseBuffer,"OPTIONS") == 0){return OPTIONS;} + else + return UNDEFINED; +} + +WEBServerManager_deprecated::HttpVersion WEBServerManager_deprecated::getHttpVersionEnumValue(const char *parseBuffer) +{ + //HTTP_0_9, HTTP_1_1, HTTP_1_0, HTTP_2_0 + if(strcmp(parseBuffer,"1.1") == 0){return HTTP_1_1;} + else if(strcmp(parseBuffer,"2.0") == 0){return HTTP_2_0;} + else if(strcmp(parseBuffer,"1.0") == 0){return HTTP_1_0;} + else if(strcmp(parseBuffer,"0.9") == 0){return HTTP_0_9;} + else + return UNKNOWN; +} + +void WEBServerManager_deprecated::clearHttpRequestData() +{ + _httpRequestData.HRM = UNDEFINED; + _httpRequestData.HV = UNKNOWN; + _httpRequestData.HMT = UNKNOWN_MIME; + free(_httpRequestData.httpResource);free(_httpRequestData.httpBody); + _httpRequestData.httpResource = NULL;_httpRequestData.httpBody = NULL; + _httpRequestData.getParams.dispose(); + _httpRequestData.postParams.dispose(); +} diff --git a/src/app/WEBServerManager_deprecated.h b/src/app/WEBServerManager_deprecated.h new file mode 100644 index 0000000..b20a1d7 --- /dev/null +++ b/src/app/WEBServerManager_deprecated.h @@ -0,0 +1,81 @@ +/** + * @file WEBServerManager.h + * @author Anatole SCHRAMM-HENRY + * @brief Single client WEB Server. + * This class is now retired and replaced by the much better WEBServer class + * which handles multiclients among other things. + * @version 0.1 + * @date 31/03/2019 + * + * @copyright MIT + * + */ +#ifndef WEBSERVERMANAGERDEPRECATED_H +#define WEBSERVERMANAGERDEPRECATED_H + +#include +#include +#include "SDCardManager.h" +#include "definition.h" +#include "Dictionary.h" + +struct HttpRequestData; + +class WEBServerManager_deprecated +{ + public: + enum ClientStatus {NOT_HANDLED, HANDLED, NEW, WAITING_FOR_CLIENT, QUERY_PARSED, RESPONSE_SENT}; + 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 HttpParserStatus {INIT, LINE_BREAK, HTTP_VERB_SECTION, HTTP_RESOURCE_SECTION, HTTP_RESOURCE_PARAM_SECTION, HTTP_VER_SECTION, PARAMETER_SECTION, BODY_SECTION, IGNORED, ERROR}; + 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}; + struct HttpRequestData{ + HttpRequestMethod HRM; + HttpVersion HV; + HttpMIMEType HMT; + Dictionary getParams; + Dictionary postParams; + char *httpResource; + char *httpBody; + }; + + WEBServerManager_deprecated(unsigned int port = 80, SDCardManager *sdCardManager = NULL); + + boolean runServer(); + unsigned int getPort() const; + boolean addApiRoutine(const char *uri, boolean (*apiRoutine)(HttpRequestData&, WiFiClient*, void*), void *pData, HttpRequestMethod HRM = UNDEFINED); + void clearApiRoutine(); + boolean removeApiRoutine(const char *uri); + protected: + private: + struct ApiRoutine + { + boolean (*apiRoutine)(HttpRequestData&, WiFiClient*, void*); + void *pData; + HttpRequestMethod HRM; + }; + + boolean parseQuery(WiFiClient *wifiClient); + boolean sendPageToClientFromSdCard(WiFiClient *wifiClient); + boolean sendPageToClientFromApiDictio(WiFiClient *wifiClient); + HttpRequestMethod getHttpVerbEnumValue(const char *parseBuffer); + HttpVersion getHttpVersionEnumValue(const char *parseBuffer); + char *getFilePathByHttpResource(char *res); + char *getFileExtension(char *name); + HttpMIMEType getMIMETypeByExtension(const char *extension); + char *getHTTPHeader(HttpMIMEType httpMIMEType, unsigned long size); + void clearHttpRequestData(); + + WiFiServer _wifiServer; + WiFiClient _wifiClient;//One client only, may be replaced with a fifo in the future + SDCardManager *_sdCardManager; + HttpRequestData _httpRequestData; + Dictionary _apiDictionary; + + ClientStatus _clientState; + HttpParserStatus _httpParserState; + unsigned long _clientTimeout; + unsigned int _port; +}; + +#endif //WEBSERVERMANAGERDEPRECATED_H diff --git a/src/app/app.ino b/src/app/app.ino index e6d6669..dfe548d 100644 --- a/src/app/app.ino +++ b/src/app/app.ino @@ -74,34 +74,35 @@ void setup() sab.getRtcManager().setDateTime(sab.getRTC_DS3231().now()); } - sab.getWebServer().addApiRoutine("/sab/web/apitester", &(apiTesterApi), NULL); - sab.getWebServer().addApiRoutine("/sab/view/next", &(nextViewApi), &sab, WEBServer::GET); - sab.getWebServer().addApiRoutine("/sab/view/reloadcfg", &(reloadViewApi), &sab, WEBServer::GET); - sab.getWebServer().addApiRoutine("/sab/view", &(viewByUIDApi), &sab, WEBServer::GET); - sab.getWebServer().addApiRoutine("/sab/rtc/get/datetime", &(rtcGetTimeApi), &sab, WEBServer::GET); - sab.getWebServer().addApiRoutine("/sab/rtc/set/datetime", &(rtcSetTimeApi), &sab, WEBServer::GET); - sab.getWebServer().addApiRoutine("/sab/sdcard/size", &(sdCardSizeApi), &sab, WEBServer::GET); + sab.getWebServerManager().getWEBServer().addApiRoutine("/sab/web/apitester", &(apiTesterApi), NULL); + sab.getWebServerManager().getWEBServer().addApiRoutine("/sab/view/next", &(nextViewApi), &sab, WEBServer::GET); + sab.getWebServerManager().getWEBServer().addApiRoutine("/sab/view/reloadcfg", &(reloadViewApi), &sab, WEBServer::GET); + sab.getWebServerManager().getWEBServer().addApiRoutine("/sab/view", &(viewByUIDApi), &sab, WEBServer::GET); + sab.getWebServerManager().getWEBServer().addApiRoutine("/sab/rtc/get/datetime", &(rtcGetTimeApi), &sab, WEBServer::GET); + sab.getWebServerManager().getWEBServer().addApiRoutine("/sab/rtc/set/datetime", &(rtcSetTimeApi), &sab, WEBServer::GET); + sab.getWebServerManager().getWEBServer().addApiRoutine("/sab/sdcard/size", &(sdCardSizeApi), &sab, WEBServer::GET); sdCardApiPacket.pSab = &sab;sdCardApiPacket.pView = &v1p; - sab.getWebServer().addApiRoutine("/sab/sdcard/action", &(sdCardActionApi), &sdCardApiPacket, WEBServer::GET); + sab.getWebServerManager().getWEBServer().addApiRoutine("/sab/sdcard/action", &(sdCardActionApi), &sdCardApiPacket, WEBServer::GET); - sab.getWebServer().addApiRoutine("/esp/restart", &(espRestartApi), &sab, WEBServer::GET); - sab.getWebServer().addApiRoutine("/esp/reset", &(espResetApi), &sab, WEBServer::GET); - sab.getWebServer().addApiRoutine("/sab/wifi/stainfo", &(staWifiInfoApi), &sab, WEBServer::GET); - sab.getWebServer().addApiRoutine("/sab/wifi/scanner", &(apScannerApi), NULL, WEBServer::GET); - sab.getWebServer().addApiRoutine("/sab/systeminfo", &(systemInfoApi), &sab, WEBServer::GET); - sab.getWebServer().addApiRoutine("/sab/power/info", &(powerInfoApi), &sab, WEBServer::GET); - sab.getWebServer().addApiRoutine("/sab/io/get/level", &(ioGetLevelApi), vio.ioState, WEBServer::GET); - sab.getWebServer().addApiRoutine("/sab/io/set/level", &(ioSetLevelApi), &sab, WEBServer::GET); - sab.getWebServer().addApiRoutine("/sab/io/get/mode", &(ioGetModeApi), &sab, WEBServer::GET); - sab.getWebServer().addApiRoutine("/sab/io/set/mode", &(ioSetModeApi), &sab, WEBServer::GET); - sab.getWebServer().addApiRoutine("/sab/sw/version", &(swVersionApi), &sab, WEBServer::GET); - sab.getWebServer().addApiRoutine("/sab/ota/update/upload", &(otaUpdateUploadApi), NULL, WEBServer::POST); - sab.getWebServer().addApiRoutine("/sab/ota/update/device", &(otaUpdateRemoteApi), &sab, WEBServer::GET); + sab.getWebServerManager().getWEBServer().addApiRoutine("/esp/restart", &(espRestartApi), &sab, WEBServer::GET); + sab.getWebServerManager().getWEBServer().addApiRoutine("/esp/reset", &(espResetApi), &sab, WEBServer::GET); + sab.getWebServerManager().getWEBServer().addApiRoutine("/sab/wifi/stainfo", &(staWifiInfoApi), &sab, WEBServer::GET); + sab.getWebServerManager().getWEBServer().addApiRoutine("/sab/wifi/scanner", &(apScannerApi), NULL, WEBServer::GET); + sab.getWebServerManager().getWEBServer().addApiRoutine("/sab/systeminfo", &(systemInfoApi), &sab, WEBServer::GET); + sab.getWebServerManager().getWEBServer().addApiRoutine("/sab/power/info", &(powerInfoApi), &sab, WEBServer::GET); + sab.getWebServerManager().getWEBServer().addApiRoutine("/sab/io/get/level", &(ioGetLevelApi), vio.ioState, WEBServer::GET); + sab.getWebServerManager().getWEBServer().addApiRoutine("/sab/io/set/level", &(ioSetLevelApi), &sab, WEBServer::GET); + sab.getWebServerManager().getWEBServer().addApiRoutine("/sab/io/get/mode", &(ioGetModeApi), &sab, WEBServer::GET); + sab.getWebServerManager().getWEBServer().addApiRoutine("/sab/io/set/mode", &(ioSetModeApi), &sab, WEBServer::GET); + sab.getWebServerManager().getWEBServer().addApiRoutine("/sab/sw/version", &(swVersionApi), &sab, WEBServer::GET); + sab.getWebServerManager().getWEBServer().addApiRoutine("/sab/ota/update/upload", &(otaUpdateUploadApi), NULL, WEBServer::POST); + sab.getWebServerManager().getWEBServer().addApiRoutine("/sab/ota/update/device", &(otaUpdateRemoteApi), &sab, WEBServer::GET); sab.getIOManager().setISROnIOChange(&(ioISR), GPIO_3_RX); sab.getTaskSchedulerManager().addTask((uint16_t)0, TaskSchedulerManagerHelper::Schedule::scheduleBuilder()->setMillis(5000), &(task_blink), &sab); sab.getTaskSchedulerManager().addTask(1, TaskSchedulerManagerHelper::Schedule::scheduleBuilder()->setSeconds(10), &(task_sys_info), &v1p); + sab.getTaskSchedulerManager().addTask(2, TaskSchedulerManagerHelper::Schedule::scheduleBuilder()->setSeconds(5), &(testTask), &sab); //dataLogger.client.keepAlive(true); //sab.getTaskSchedulerManager().addTask(2, TaskSchedulerManagerHelper::Schedule::scheduleBuilder()->setSeconds(1)->setTriggerRightAway(false), &(task_post_data_logger), &dataLogger); diff --git a/src/app/tasks.cpp b/src/app/tasks.cpp index 999f040..d7d0966 100644 --- a/src/app/tasks.cpp +++ b/src/app/tasks.cpp @@ -27,7 +27,7 @@ boolean task_sys_info(void *pData) p->sab->getConnectivityManager().RSSI(), p->sab->getConnectivityManager().softAPIP().toString().c_str(), p->sab->getConnectivityManager().macAddress().c_str(), - p->sab->getWebServer().getConnectedClientsCount(), + p->sab->getWebServerManager().getWEBServer().getConnectedClientsCount(), freeRAM, HEAPfrag, biggestContigMemBlock); @@ -108,3 +108,10 @@ boolean task_post_data_logger(void * pData) return true; } + +boolean testTask(void *pData) +{ + SAB *pSab = (SAB *)pData; + + return true; +} diff --git a/src/app/tasks.h b/src/app/tasks.h index eae3b7a..ecb0e2f 100644 --- a/src/app/tasks.h +++ b/src/app/tasks.h @@ -8,6 +8,7 @@ boolean task_blink(void *); boolean task_sys_info(void *); boolean task_esp_reset_restart(void *); +boolean testTask(void *); typedef struct dataLogger {