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 {