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

This commit is contained in:
anschrammh 2022-10-05 08:20:15 +02:00
parent a23fb4cba9
commit a951cdd6fc
14 changed files with 932 additions and 752 deletions

View File

@ -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

View File

@ -15,6 +15,10 @@
/*flex: 1 0 auto;*/
}
td,tr
{
}
.switch label input[type=checkbox]:checked+.lever
{
background-color: #fa3 !important;
@ -116,18 +120,28 @@
</div>
<!-- End of modal window definition -->
<section id="mainpage_id">
<div class="row">
<div class="row col m6">
<table>
<tr>
<td colspan="2" style="width: 50%;">
<div style="margin:10px;padding:5px 0 10px 0;" class="grey lighten-2 z-depth-3">
<h5 class="center-align">Connectivity</h5>
</div>
<div class="col m6">
<div class="card teal darken-1">
<div class="card-content white-text">
<span class="card-title center-align" style="font-weight:bold;">Access Point Mode</span>
</td>
<td colspan="2" style="width: 50%;">
<div style="margin:10px;padding:5px 0 10px 0;" class="grey lighten-2 z-depth-3">
<h5 class="center-align">System Info</h5>
</div>
<div class="card-action center-align">
<div class="switch">
</td>
</tr>
<tr>
<td colspan="4">
<div class="row" style="display:flex;">
<div class="teal darken-1 col m3" style="margin: 7px 22px 7px 22px;border-radius: 5px;padding-bottom: 15px;">
<div class="white-text">
<h5 style="font-weight:bold;" class="center-align">Access Point Mode</h5>
</div>
<hr>
<div class="switch center-align">
<label class="white-text">
Disabled
<input type="checkbox">
@ -145,24 +159,20 @@
</div>
<input placeholder="SSID" id="ssid" type="text" class="validate white-text">
<input onmouseleave="showHidePassword('pwd','hide')" onmouseover="showHidePassword('pwd','show');" value="somePassword" placeholder="Password" id="pwd" type="password" class="validate white-text">
</div>
<div class="card-action">
<hr>
<div class="center-align">
<button class="btn waves-effect waves-light green darken-3" type="submit" name="apply">
Apply &#10004;
</button>
<button class="btn waves-effect waves-light red darken-3" style="" type="submit" name="cancel">
Cancel &#10008;
</button>
</div>
</div>
<div class="teal darken-1 col m3" style="margin: 7px 22px 7px 22px;border-radius: 5px;padding-bottom: 15px;">
<div class="white-text">
<h5 style="font-weight:bold;" class="center-align">Station Mode</h5>
</div>
<div class="col m6">
<div class="card teal darken-1">
<div class="card-content white-text">
<span class="card-title center-align" style="font-weight:bold;">Station Mode</span>
</div>
<div class="card-action center-align">
<div class="switch">
<hr>
<div class="switch center-align">
<label class="white-text">
Disabled
<input type="checkbox">
@ -182,28 +192,27 @@
<option value="" disabled selected>Click on scan</option>
</select>
<input placeholder="Password" id="pwd" type="password" class="validate white-text">
</div>
<div class="card-action">
<hr>
<div class="row">
<div class="col m6">
<button class="btn waves-effect waves-light green darken-3" type="submit" name="apply">
Connect
</button>
</div>
<div class="col m6 right-align">
<button class="btn waves-effect waves-light lime darken-3" style="" onclick="scanAvailableNetworks();" type="submit" name="Scan">
Scan
</button>
</div>
</div>
</div>
<div class="teal darken-1 col m6" style="margin: 7px 22px 7px 22px;border-radius: 5px;padding-bottom: 15px;">
<div class="white-text">
<h5 style="font-weight:bold;" class="center-align">System State</h5>
</div>
<div class="row col m6">
<div style="margin:10px;padding:5px 0 10px 0;" class="grey lighten-2 z-depth-3">
<h5 class="center-align">System Info</h5>
</div>
<div class="col m12">
<div class="card teal darken-1">
<div class="card-content white-text">
<span class="card-title center-align" style="font-weight:bold;">System State</span>
</div>
<div class="card-action white-text" style="font-size:1.2em;">
<hr>
<div class="white-text" style="font-size:1.2em;">
<div class="row">
<div class="col m6">
Cpu frequency :
@ -246,45 +255,57 @@
</div>
</div>
</div>
</div>
</div>
<div class="row" style="margin-top:-20px;">
<div class="row col m3">
</td>
</tr>
<tr>
<td>
<div style="margin:10px;padding:5px 0 10px 0;" class="grey lighten-2 z-depth-3">
<h5 class="center-align">Clock</h5>
</div>
<div class="col m12">
<div class="card teal darken-1">
<div class="card-content white-text">
<span class="card-title center-align" style="font-weight:bold;">DS3231 RTC</span>
</td>
<td>
<div style="margin:10px;padding:5px 0 10px 0;" class="grey lighten-2 z-depth-3">
<h5 class="center-align">Storage</h5>
</div>
<div class="card-action" style="font-size:1.2em;">
</td>
<td colspan="2">
<div style="margin:10px;padding:5px 0 10px 0;" class="grey lighten-2 z-depth-3">
<h5 class="center-align">Services</h5>
</div>
</td>
</tr>
<tr>
<td colspan="4">
<div class="row" style="display:flex;">
<div class="teal darken-1 col m3" style="margin: 7px 22px 7px 22px;border-radius: 5px;padding-bottom: 15px;">
<div class="white-text">
<h5 style="font-weight:bold;" class="center-align">DS3231 RTC</h5>
</div>
<hr>
<div style="font-size:1.2em;">
<input type="text" class="datepicker" id="datepicker_id">
<input type="text" class="timepicker" id="timepicker_id">
</div>
<div class="card-action">
<hr>
<div class="row">
<div class="col m6">
<button class="btn waves-effect waves-light green darken-3" onclick="setRtcTime();" name="apply">
Apply &#10004;
</button>
</div>
<div class="col m6 right-align">
<button class="btn waves-effect waves-light green darken-3 tooltipped" onclick="setRtc2BrowserTime();" data-position="bottom" data-tooltip="Use the browser time to set the RTC time" name="browserTime">
Set browser time &#10004;
</button>
</div>
</div>
</div>
<div class="teal darken-1 col m3" style="margin: 7px 22px 7px 22px;border-radius: 5px;padding-bottom: 15px;">
<div class="white-text">
<h5 style="font-weight:bold;" class="center-align">SD Card</h5>
</div>
<div class="row col m3">
<div style="margin:10px;padding:5px 0 10px 0;" class="grey lighten-2 z-depth-3">
<h5 class="center-align">Storage</h5>
</div>
<div class="col m12">
<div class="card teal darken-1">
<div class="card-content white-text">
<span class="card-title center-align" style="font-weight:bold;">SD Card</span>
</div>
<div class="card-action white-text center-align" style="font-size:1.2em;">
<hr>
<div class="white-text center-align" style="font-size:1.2em;">
<div class="switch">
<label class="white-text">
Unmounted
@ -310,11 +331,51 @@
</div>
</div>
</div>
</td>
</tr>
<tr>
<td>
</td>
<td>
</td>
<td>
</td>
<td>
</td>
</tr>
<tr>
<td>
</td>
<td>
</td>
<td>
</td>
<td>
</td>
</tr>
</table>
<div class="row" style="margin-top:-20px;">
<div class="row col m3">
<div class="col m12">
</div>
</div>
<div class="row col m3">
<div class="col m12">
</div>
</div>
<div class="col m6">
<div style="margin:10px;padding:5px 0 10px 0;" class="grey lighten-2 z-depth-3">
<h5 class="center-align">Services</h5>
</div>
<div class="col m6">
<div class="card teal darken-1">
<div class="card-content white-text">

View File

@ -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)
@ -13,17 +12,10 @@ 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<CFGParameterValue> *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;
}

View File

@ -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

View File

@ -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<WEBClient>& SAB::getWebServer()
WEBServerManager& SAB::getWebServerManager()
{
return _webServer;
return _webServerManager;
}
FTPServer<FTPClient>& 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();

View File

@ -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<WEBClient>& getWebServer();
WEBServerManager& getWebServerManager();
FTPServer<FTPClient>& getFtpServer();
OTAManager& getOTAManager();
IOManager& getIOManager();
@ -57,7 +57,7 @@ class SAB
RTC_DS3231 _rtc;
RtcManager _rtcManager;
ConnectivityManager _connectivityManager;
WEBServer<WEBClient> _webServer;
WEBServerManager _webServerManager;
FTPServer<FTPClient> _ftpServer;
OTAManager _otaManager;
DashboardWSServer _dbWSServer;

View File

@ -93,6 +93,11 @@ class WEBServer : public TCPServer<T>, public HttpConstants
}
}
void setSDClass(SDClass *sdClass)
{
_sdClass = sdClass;
}
const char * getWWWDir(void) const
{
return _WWWDir;

View File

@ -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<DictionaryHelper::StringEntity>(), Dictionary<DictionaryHelper::StringEntity>(), 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();
CFGDictionary<CFGParameterValue> *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)
{
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;
}
boolean WEBServerManager::removeApiRoutine(const char *uri)
//We managed to read the config file, now we can apply it !
if((*serverCfg)("WEB_ENABLED"))
{
return _apiDictionary.remove(uri);
if((*serverCfg)("WEB_ENABLED")->booleanValue())
{
_isServiceEnabled = true;
if((*serverCfg)("WEB_PORT"))
{
DEBUG_WEB_SERVER_MANAGER("WEB_PORT is %u", (*serverCfg)("WEB_PORT")->uintValue());
_webServer.setPort((*serverCfg)("WEB_PORT")->uintValue());
}
boolean WEBServerManager::runServer()
if((*serverCfg)("WEB_MAX_CLIENT"))
{
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;
DEBUG_WEB_SERVER_MANAGER("WEB_MAX_CLIENT is %u", (*serverCfg)("WEB_MAX_CLIENT")->uintValue());
_webServer.setMaxClient((*serverCfg)("WEB_MAX_CLIENT")->uintValue());
}
boolean WEBServerManager::parseQuery(WiFiClient *wifiClient)
if((*serverCfg)("WEB_WWW_DIR"))
{
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
DEBUG_WEB_SERVER_MANAGER("WEB_WWW_DIR is %s", (*serverCfg)("WEB_WWW_DIR")->stringValue());
_webServer.setWWWDir((*serverCfg)("WEB_WWW_DIR")->stringValue());
}
#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;
_webServer.start();
}
else
{
if(readChar != '/' && readChar != '\\') slashesOrAntiSlashesOnly = false;
parseBuffer = addChar(parseBuffer, readChar);
_httpParserState = HTTP_RESOURCE_SECTION;
_webServer.stop();
_isServiceEnabled = false;
}
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);
DEBUG_WEB_SERVER_MANAGER("WEB_ENABLED is NULL !");
toReturn = false;
}
break;
case HTTP_VER_SECTION:
if((readChar >= 48 && readChar <= 57) || readChar == '.')
return toReturn;
}
boolean WEBServerManager::isEnabled(void) const
{
parseBuffer = addChar(parseBuffer, readChar);
_httpParserState = HTTP_VER_SECTION;
return _isServiceEnabled;
}
else if(readChar == '\n')
WEBServer<WEBClient> &WEBServerManager::getWEBServer(void)
{
_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::getPort() const
{
return _port;
}
boolean WEBServerManager::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<!DOCTYPE HTML>\r\n<html>\r\n<p>Failed to malloc filePath</p>\r\n</html>"));
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<!DOCTYPE HTML>\r\n<html>\r\n<h1>Page not found for : </h1>\r\n<h4></h4>\r\n</html>") + 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<!DOCTYPE HTML>\r\n<html>\r\n<h1>Failed to malloc response</h1>\r\n</html>"));
return false;
}
sprintf(response, "HTTP/1.1 404 Not Found\r\nContent-Type: text/html\r\n\r\n<!DOCTYPE HTML>\r\n<html>\r\n<h1>Page not found for : </h1>\r\n<h4>%s</h4>\r\n</html>", _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<!DOCTYPE HTML>\r\n<html>\r\n<h1>Failed to malloc header</h1>\r\n</html>"));
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<!DOCTYPE HTML>\r\n<html>\r\n<h1>Method Not Allowed</h1>\r\n</html>"));
break;
}
}else
{
wifiClient->print(F("HTTP/1.1 500 Internal Server Error\r\nContent-Type: text/html\r\n\r\n<!DOCTYPE HTML>\r\n<html>\r\n<h1>SDCardManager is NULL<br \\>Check code</h1>\r\n</html>"));
}
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;
}

View File

@ -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 <WiFiServer.h>
#include <WiFiClient.h>
#include "SDCardManager.h"
#include "definition.h"
#include "Dictionary.h"
struct HttpRequestData;
#include "WEBClient.h" //includes WEBServer internally
class WEBServerManager
{
friend class SAB;
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<DictionaryHelper::StringEntity> getParams;
Dictionary<DictionaryHelper::StringEntity> postParams;
char *httpResource;
char *httpBody;
};
~WEBServerManager();
boolean init(void);
boolean isEnabled(void) const;
WEBServer<WEBClient> &getWEBServer(void);
WEBServerManager(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:
WEBServerManager(SDCardManager &sdCardManager);
private:
struct ApiRoutine
{
boolean (*apiRoutine)(HttpRequestData&, WiFiClient*, void*);
void *pData;
HttpRequestMethod HRM;
};
SDCardManager *_sdCardManager = nullptr;
boolean _isServiceEnabled = false;
WEBServer<WEBClient> _webServer;
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<ApiRoutine> _apiDictionary;
ClientStatus _clientState;
HttpParserStatus _httpParserState;
unsigned long _clientTimeout;
unsigned int _port;
};
#endif //WEBSERVERMANAGER_H

View File

@ -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<DictionaryHelper::StringEntity>(), Dictionary<DictionaryHelper::StringEntity>(), 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<!DOCTYPE HTML>\r\n<html>\r\n<p>Failed to malloc filePath</p>\r\n</html>"));
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<!DOCTYPE HTML>\r\n<html>\r\n<h1>Page not found for : </h1>\r\n<h4></h4>\r\n</html>") + 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<!DOCTYPE HTML>\r\n<html>\r\n<h1>Failed to malloc response</h1>\r\n</html>"));
return false;
}
sprintf(response, "HTTP/1.1 404 Not Found\r\nContent-Type: text/html\r\n\r\n<!DOCTYPE HTML>\r\n<html>\r\n<h1>Page not found for : </h1>\r\n<h4>%s</h4>\r\n</html>", _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<!DOCTYPE HTML>\r\n<html>\r\n<h1>Failed to malloc header</h1>\r\n</html>"));
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<!DOCTYPE HTML>\r\n<html>\r\n<h1>Method Not Allowed</h1>\r\n</html>"));
break;
}
}else
{
wifiClient->print(F("HTTP/1.1 500 Internal Server Error\r\nContent-Type: text/html\r\n\r\n<!DOCTYPE HTML>\r\n<html>\r\n<h1>SDCardManager is NULL<br \\>Check code</h1>\r\n</html>"));
}
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();
}

View File

@ -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 <WiFiServer.h>
#include <WiFiClient.h>
#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<DictionaryHelper::StringEntity> getParams;
Dictionary<DictionaryHelper::StringEntity> 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<ApiRoutine> _apiDictionary;
ClientStatus _clientState;
HttpParserStatus _httpParserState;
unsigned long _clientTimeout;
unsigned int _port;
};
#endif //WEBSERVERMANAGERDEPRECATED_H

View File

@ -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<WEBClient>::GET);
sab.getWebServer().addApiRoutine("/sab/view/reloadcfg", &(reloadViewApi), &sab, WEBServer<WEBClient>::GET);
sab.getWebServer().addApiRoutine("/sab/view", &(viewByUIDApi), &sab, WEBServer<WEBClient>::GET);
sab.getWebServer().addApiRoutine("/sab/rtc/get/datetime", &(rtcGetTimeApi), &sab, WEBServer<WEBClient>::GET);
sab.getWebServer().addApiRoutine("/sab/rtc/set/datetime", &(rtcSetTimeApi), &sab, WEBServer<WEBClient>::GET);
sab.getWebServer().addApiRoutine("/sab/sdcard/size", &(sdCardSizeApi), &sab, WEBServer<WEBClient>::GET);
sab.getWebServerManager().getWEBServer().addApiRoutine("/sab/web/apitester", &(apiTesterApi), NULL);
sab.getWebServerManager().getWEBServer().addApiRoutine("/sab/view/next", &(nextViewApi), &sab, WEBServer<WEBClient>::GET);
sab.getWebServerManager().getWEBServer().addApiRoutine("/sab/view/reloadcfg", &(reloadViewApi), &sab, WEBServer<WEBClient>::GET);
sab.getWebServerManager().getWEBServer().addApiRoutine("/sab/view", &(viewByUIDApi), &sab, WEBServer<WEBClient>::GET);
sab.getWebServerManager().getWEBServer().addApiRoutine("/sab/rtc/get/datetime", &(rtcGetTimeApi), &sab, WEBServer<WEBClient>::GET);
sab.getWebServerManager().getWEBServer().addApiRoutine("/sab/rtc/set/datetime", &(rtcSetTimeApi), &sab, WEBServer<WEBClient>::GET);
sab.getWebServerManager().getWEBServer().addApiRoutine("/sab/sdcard/size", &(sdCardSizeApi), &sab, WEBServer<WEBClient>::GET);
sdCardApiPacket.pSab = &sab;sdCardApiPacket.pView = &v1p;
sab.getWebServer().addApiRoutine("/sab/sdcard/action", &(sdCardActionApi), &sdCardApiPacket, WEBServer<WEBClient>::GET);
sab.getWebServerManager().getWEBServer().addApiRoutine("/sab/sdcard/action", &(sdCardActionApi), &sdCardApiPacket, WEBServer<WEBClient>::GET);
sab.getWebServer().addApiRoutine("/esp/restart", &(espRestartApi), &sab, WEBServer<WEBClient>::GET);
sab.getWebServer().addApiRoutine("/esp/reset", &(espResetApi), &sab, WEBServer<WEBClient>::GET);
sab.getWebServer().addApiRoutine("/sab/wifi/stainfo", &(staWifiInfoApi), &sab, WEBServer<WEBClient>::GET);
sab.getWebServer().addApiRoutine("/sab/wifi/scanner", &(apScannerApi), NULL, WEBServer<WEBClient>::GET);
sab.getWebServer().addApiRoutine("/sab/systeminfo", &(systemInfoApi), &sab, WEBServer<WEBClient>::GET);
sab.getWebServer().addApiRoutine("/sab/power/info", &(powerInfoApi), &sab, WEBServer<WEBClient>::GET);
sab.getWebServer().addApiRoutine("/sab/io/get/level", &(ioGetLevelApi), vio.ioState, WEBServer<WEBClient>::GET);
sab.getWebServer().addApiRoutine("/sab/io/set/level", &(ioSetLevelApi), &sab, WEBServer<WEBClient>::GET);
sab.getWebServer().addApiRoutine("/sab/io/get/mode", &(ioGetModeApi), &sab, WEBServer<WEBClient>::GET);
sab.getWebServer().addApiRoutine("/sab/io/set/mode", &(ioSetModeApi), &sab, WEBServer<WEBClient>::GET);
sab.getWebServer().addApiRoutine("/sab/sw/version", &(swVersionApi), &sab, WEBServer<WEBClient>::GET);
sab.getWebServer().addApiRoutine("/sab/ota/update/upload", &(otaUpdateUploadApi), NULL, WEBServer<WEBClient>::POST);
sab.getWebServer().addApiRoutine("/sab/ota/update/device", &(otaUpdateRemoteApi), &sab, WEBServer<WEBClient>::GET);
sab.getWebServerManager().getWEBServer().addApiRoutine("/esp/restart", &(espRestartApi), &sab, WEBServer<WEBClient>::GET);
sab.getWebServerManager().getWEBServer().addApiRoutine("/esp/reset", &(espResetApi), &sab, WEBServer<WEBClient>::GET);
sab.getWebServerManager().getWEBServer().addApiRoutine("/sab/wifi/stainfo", &(staWifiInfoApi), &sab, WEBServer<WEBClient>::GET);
sab.getWebServerManager().getWEBServer().addApiRoutine("/sab/wifi/scanner", &(apScannerApi), NULL, WEBServer<WEBClient>::GET);
sab.getWebServerManager().getWEBServer().addApiRoutine("/sab/systeminfo", &(systemInfoApi), &sab, WEBServer<WEBClient>::GET);
sab.getWebServerManager().getWEBServer().addApiRoutine("/sab/power/info", &(powerInfoApi), &sab, WEBServer<WEBClient>::GET);
sab.getWebServerManager().getWEBServer().addApiRoutine("/sab/io/get/level", &(ioGetLevelApi), vio.ioState, WEBServer<WEBClient>::GET);
sab.getWebServerManager().getWEBServer().addApiRoutine("/sab/io/set/level", &(ioSetLevelApi), &sab, WEBServer<WEBClient>::GET);
sab.getWebServerManager().getWEBServer().addApiRoutine("/sab/io/get/mode", &(ioGetModeApi), &sab, WEBServer<WEBClient>::GET);
sab.getWebServerManager().getWEBServer().addApiRoutine("/sab/io/set/mode", &(ioSetModeApi), &sab, WEBServer<WEBClient>::GET);
sab.getWebServerManager().getWEBServer().addApiRoutine("/sab/sw/version", &(swVersionApi), &sab, WEBServer<WEBClient>::GET);
sab.getWebServerManager().getWEBServer().addApiRoutine("/sab/ota/update/upload", &(otaUpdateUploadApi), NULL, WEBServer<WEBClient>::POST);
sab.getWebServerManager().getWEBServer().addApiRoutine("/sab/ota/update/device", &(otaUpdateRemoteApi), &sab, WEBServer<WEBClient>::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);

View File

@ -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;
}

View File

@ -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
{