7 Commits

36 changed files with 1836 additions and 1845 deletions

View File

@ -1,2 +1,3 @@
/*.js.gz /*.gz
/edit_gz
/.exclude.files /.exclude.files

Binary file not shown.

Binary file not shown.

View File

@ -166,7 +166,8 @@ float t = 0;
float h = 0; float h = 0;
bool udht = false; bool udht = false;
bool heat_enabled_prev = false; bool heat_enabled_prev = false;
int ledState = LED_OFF; bool ledState = LED_OFF;
bool ledOut = LED_OFF;
struct EE_bl { struct EE_bl {
byte memid; //here goes the EEMARK stamp byte memid; //here goes the EEMARK stamp
@ -180,7 +181,7 @@ struct EE_bl {
EE_bl ee = {0,0,0,0,0,0.1}; //populate as initial EE_bl ee = {0,0,0,0,0,0.1}; //populate as initial
// SUBS // SUBS
void writeEE() { void writeEE(){
ee.memid = EEMARK; ee.memid = EEMARK;
//EEPROM.put(EESC, sched); // only separately when needed with commit() //EEPROM.put(EESC, sched); // only separately when needed with commit()
//EEPROM.put(EECH, memch); // not need to store and retrieve memch //EEPROM.put(EECH, memch); // not need to store and retrieve memch
@ -188,7 +189,7 @@ void writeEE() {
EEPROM.commit(); //needed for ESP8266? EEPROM.commit(); //needed for ESP8266?
} }
void readEE() { void readEE(){
byte ChkEE; byte ChkEE;
if (memch > MEMMAX) memch = 0; if (memch > MEMMAX) memch = 0;
EEPROM.get(EEBEGIN + memch*sizeof(ee), ChkEE); EEPROM.get(EEBEGIN + memch*sizeof(ee), ChkEE);
@ -199,6 +200,20 @@ void readEE() {
} }
} }
void doOut(){
if (ledOut != ledState){ // only if changed
digitalWrite(ledPin, ledState); //consolidated here
ledOut = ledState; //update
if (ledState == LED_OFF){
ws.textAll("led,ledoff");
Serial.println(F("LED-OFF"));
} else {
ws.textAll("led,ledon");
Serial.println(F("LED-ON"));
}
}
}
void showTime() void showTime()
{ {
byte tmpch = 0; byte tmpch = 0;
@ -208,13 +223,13 @@ void showTime()
now = time(nullptr); now = time(nullptr);
const tm* tm = localtime(&now); const tm* tm = localtime(&now);
ws.printfAll("Now,Clock,%02d:%02d,%d", tm->tm_hour, tm->tm_min, tm->tm_wday); ws.printfAll("Now,Clock,%02d:%02d,%d", tm->tm_hour, tm->tm_min, tm->tm_wday);
if ((2==tm->tm_hour )&&(2==tm->tm_min)) { if ((2==tm->tm_hour )&&(2==tm->tm_min)){
configTzTime(MYTZ, "pool.ntp.org"); configTzTime(MYTZ, "pool.ntp.org");
Serial.print(F("Sync Clock at 02:02\n")); Serial.print(F("Sync Clock at 02:02\n"));
} }
Serial.printf("RTC: %02d:%02d\n", tm->tm_hour, tm->tm_min); Serial.printf("RTC: %02d:%02d\n", tm->tm_hour, tm->tm_min);
if (sched == 0) { // automatic if (sched == 0){ // automatic
if ((tm->tm_wday > 0)&&(tm->tm_wday < 6)) tmpch = 0; //Mon - Fri if ((tm->tm_wday > 0)&&(tm->tm_wday < 6)) tmpch = 0; //Mon - Fri
else if (tm->tm_wday == 6) tmpch = 1; //Sat else if (tm->tm_wday == 6) tmpch = 1; //Sat
else if (tm->tm_wday == 0) tmpch = 2; //Sun else if (tm->tm_wday == 0) tmpch = 2; //Sun
@ -222,7 +237,7 @@ void showTime()
tmpch = sched - 1; //and stays tmpch = sched - 1; //and stays
} }
if (tmpch != memch) { // update if different if (tmpch != memch){ // update if different
memch = tmpch; memch = tmpch;
readEE(); readEE();
ws.printfAll("Now,Setting,%02d:%02d,%02d:%02d,%+2.1f", ee.hstart, ee.mstart, ee.hstop, ee.mstop, ee.tempe); ws.printfAll("Now,Setting,%02d:%02d,%02d:%02d,%+2.1f", ee.hstart, ee.mstart, ee.hstop, ee.mstop, ee.tempe);
@ -237,20 +252,18 @@ void showTime()
else { //enable smart if different else { //enable smart if different
if (((bmi < emi)&&(bmi <= xmi)&&(xmi < emi))|| if (((bmi < emi)&&(bmi <= xmi)&&(xmi < emi))||
((emi < bmi)&&((bmi <= xmi)||(xmi < emi)))) { ((emi < bmi)&&((bmi <= xmi)||(xmi < emi)))){
heat_enabled = true; heat_enabled = true;
} else heat_enabled = false; } else heat_enabled = false;
} }
if (heat_enabled_prev) { // smart control (delayed one cycle) if (heat_enabled_prev){ // smart control (delayed one cycle)
if (((t - HYST) < ee.tempe)&&(ledState == LED_OFF)) { // OFF->ON once if (((t + HYST) < ee.tempe)&&(ledState == LED_OFF)){ // OFF->ON once
ledState = LED_ON; ledState = LED_ON;
digitalWrite(ledPin, ledState); // apply change
ws.textAll("led,ledon"); ws.textAll("led,ledon");
} }
if ((((t + HYST) > ee.tempe)&&(ledState == LED_ON))||(!heat_enabled)) { // ON->OFF once, also turn off at end of period. if ((((t - HYST) > ee.tempe)&&(ledState == LED_ON))||(!heat_enabled)){ // ->OFF
ledState = LED_OFF; ledState = LED_OFF;
digitalWrite(ledPin, ledState); // apply change
ws.textAll("led,ledoff"); ws.textAll("led,ledoff");
} }
@ -263,7 +276,7 @@ void showTime()
void updateDHT(){ void updateDHT(){
float h1 = dht.readHumidity(); float h1 = dht.readHumidity();
float t1 = dht.readTemperature(); //Celsius or dht.readTemperature(true) for Fahrenheit float t1 = dht.readTemperature(); //Celsius or dht.readTemperature(true) for Fahrenheit
if (isnan(h1) || isnan(t1)) { if (isnan(h1) || isnan(t1)){
Serial.println(F("Failed to read from DHT sensor!")); Serial.println(F("Failed to read from DHT sensor!"));
} else { } else {
h = h1 + DHT_H_CORR; h = h1 + DHT_H_CORR;
@ -279,17 +292,9 @@ void analogSample()
void checkPhysicalButton() void checkPhysicalButton()
{ {
if (digitalRead(btnPin) == LOW) { if (digitalRead(btnPin) == LOW){
if (btnState != LOW) { // btnState is used to avoid sequential toggles if (btnState != LOW){ // btnState is used to avoid sequential toggles
ledState = !ledState; ledState = !ledState;
digitalWrite(ledPin, ledState);
if (ledState == LED_OFF) {
ws.textAll("led,ledoff");
Serial.println(F("LED-OFF"));
} else {
ws.textAll("led,ledon");
Serial.println(F("LED-ON"));
}
} }
btnState = LOW; btnState = LOW;
} else { } else {
@ -297,17 +302,17 @@ void checkPhysicalButton()
} }
} }
void mytimer() { void mytimer(){
++count; //200ms increments ++count; //200ms increments
checkPhysicalButton(); checkPhysicalButton();
if ((count % 25) == 1) { // update temp every 5 seconds if ((count % 25) == 1){ // update temp every 5 seconds
analogSample(); analogSample();
udht = true; udht = true;
} }
if ((count % 50) == 0) { // update temp every 10 seconds if ((count % 50) == 0){ // update temp every 10 seconds
ws.cleanupClients(); ws.cleanupClients();
} }
if (count >= 150) { // cycle every 30 sec if (count >= 150){ // cycle every 30 sec
showTime(); showTime();
count = 0; count = 0;
} }
@ -385,22 +390,21 @@ void onWsEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventT
Serial.printf("ws[%s][%u] %s-message[%llu]: ", server->url(), client->id(), (info->opcode == WS_TEXT)?"text":"binary", info->len); Serial.printf("ws[%s][%u] %s-message[%llu]: ", server->url(), client->id(), (info->opcode == WS_TEXT)?"text":"binary", info->len);
if(info->opcode == WS_TEXT){ if(info->opcode == WS_TEXT){
for(size_t i=0; i < info->len; i++) { //debug for(size_t i=0; i < info->len; i++){ //debug
msg += (char) data[i]; msg += (char) data[i];
} }
if(data[0] == 'L') { // LED if(data[0] == 'L'){ // LED
if(data[1] == '1') { if(data[1] == '1'){
ledState = LED_ON; ledState = LED_ON;
ws.textAll("led,ledon"); // for others ws.textAll("led,ledon"); // for others
} }
else if(data[1] == '0') { else if(data[1] == '0'){
ledState = LED_OFF; ledState = LED_OFF;
ws.textAll("led,ledoff"); ws.textAll("led,ledoff");
} }
digitalWrite(ledPin, ledState); // apply change
} else if(data[0] == 'T') { // timeset } else if(data[0] == 'T'){ // timeset
if (len > 11) { if (len > 11){
data[3] = data[6] = data[9] = data[12] = 0; // cut strings data[3] = data[6] = data[9] = data[12] = 0; // cut strings
ee.hstart = (uint8_t) atoi((const char *) &data[1]); ee.hstart = (uint8_t) atoi((const char *) &data[1]);
ee.mstart = (uint8_t) atoi((const char *) &data[4]); ee.mstart = (uint8_t) atoi((const char *) &data[4]);
@ -411,8 +415,8 @@ void onWsEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventT
memch = 255; // to force showTime()to send Setting memch = 255; // to force showTime()to send Setting
showTime(); showTime();
} }
} else if(data[0] == 'W') { // temperatureset } else if(data[0] == 'W'){ // temperatureset
if (len > 3) { if (len > 3){
if (ee.tempe != (float) atof((const char *) &data[1])){ if (ee.tempe != (float) atof((const char *) &data[1])){
ee.tempe = (float) atof((const char *) &data[1]); ee.tempe = (float) atof((const char *) &data[1]);
Serial.printf("[%u] Temp set %+2.1f\n", client->id(), ee.tempe); Serial.printf("[%u] Temp set %+2.1f\n", client->id(), ee.tempe);
@ -421,7 +425,7 @@ void onWsEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventT
showTime(); showTime();
} }
} }
} else if ((data[0] == 'Z')&&(len > 2)) { // sched } else if ((data[0] == 'Z')&&(len > 2)){ // sched
data[2] = 0; data[2] = 0;
if (sched != (uint8_t) atoi((const char *) &data[1])){ if (sched != (uint8_t) atoi((const char *) &data[1])){
sched = (uint8_t) atoi((const char *) &data[1]); sched = (uint8_t) atoi((const char *) &data[1]);
@ -434,7 +438,7 @@ void onWsEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventT
} else { } else {
char buff[3]; char buff[3];
for(size_t i=0; i < info->len; i++) { for(size_t i=0; i < info->len; i++){
sprintf(buff, "%02x ", (uint8_t) data[i]); sprintf(buff, "%02x ", (uint8_t) data[i]);
msg += buff ; msg += buff ;
} }
@ -457,12 +461,12 @@ void onWsEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventT
Serial.printf("ws[%s][%u] frame[%u] %s[%llu - %llu]: ", server->url(), client->id(), info->num, (info->message_opcode == WS_TEXT)?"text":"binary", info->index, info->index + len); Serial.printf("ws[%s][%u] frame[%u] %s[%llu - %llu]: ", server->url(), client->id(), info->num, (info->message_opcode == WS_TEXT)?"text":"binary", info->index, info->index + len);
if(info->opcode == WS_TEXT){ if(info->opcode == WS_TEXT){
for(size_t i=0; i < len; i++) { for(size_t i=0; i < len; i++){
msg += (char) data[i]; msg += (char) data[i];
} }
} else { } else {
char buff[3]; char buff[3];
for(size_t i=0; i < len; i++) { for(size_t i=0; i < len; i++){
sprintf(buff, "%02x ", (uint8_t) data[i]); sprintf(buff, "%02x ", (uint8_t) data[i]);
msg += buff ; msg += buff ;
} }
@ -488,7 +492,7 @@ void onWsEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventT
void setup(){ void setup(){
Serial.begin(115200); Serial.begin(115200);
Serial.setDebugOutput(true); //Serial.setDebugOutput(true);
//Wifi //Wifi
#ifdef USE_WFM #ifdef USE_WFM
@ -511,7 +515,7 @@ void setup(){
//WiFi.softAP(hostName); // Core SVN 5179 use STA as default interface in mDNS (#7042) //WiFi.softAP(hostName); // Core SVN 5179 use STA as default interface in mDNS (#7042)
WiFi.mode(WIFI_STA); // Core SVN 5179 use STA as default interface in mDNS (#7042) WiFi.mode(WIFI_STA); // Core SVN 5179 use STA as default interface in mDNS (#7042)
WiFi.begin(ssid, password); WiFi.begin(ssid, password);
if (WiFi.waitForConnectResult() != WL_CONNECTED) { if (WiFi.waitForConnectResult() != WL_CONNECTED){
Serial.print(F("STA: Failed!\n")); Serial.print(F("STA: Failed!\n"));
WiFi.disconnect(false); WiFi.disconnect(false);
delay(1000); delay(1000);
@ -545,15 +549,14 @@ void setup(){
EEPROM.begin(EEALL); EEPROM.begin(EEALL);
//EEPROM.get(EECH, memch); //current channel, no need //EEPROM.get(EECH, memch); //current channel, no need
readEE(); // populate structure if healthy readEE(); // populate structure if healthy
digitalWrite(ledPin, ledState);
Serial.printf("Timer set %02d:%02d - %02d:%02d\n", ee.hstart, ee.mstart, ee.hstop, ee.mstop); Serial.printf("Timer set %02d:%02d - %02d:%02d\n", ee.hstart, ee.mstart, ee.hstop, ee.mstop);
Serial.printf("Temp set %+2.1f\n", ee.tempe); Serial.printf("Temp set %+2.1f\n", ee.tempe);
//FS //FS
#ifdef USE_FatFS #ifdef USE_FatFS
if (MYFS.begin(false,"/ffat",3)) { //limit the RAM usage, bottom line 8kb + 4kb takes per each file, default is 10 if (MYFS.begin(false,"/ffat",3)){ //limit the RAM usage, bottom line 8kb + 4kb takes per each file, default is 10
#else #else
if (MYFS.begin()) { if (MYFS.begin()){
#endif #endif
Serial.print(F("FS mounted\n")); Serial.print(F("FS mounted\n"));
} else { } else {
@ -654,7 +657,7 @@ void setup(){
#ifdef USE_AUTH_STAT #ifdef USE_AUTH_STAT
if(!request->authenticate(http_username, http_password)) return request->requestAuthentication(); if(!request->authenticate(http_username, http_password)) return request->requestAuthentication();
#endif #endif
request->onDisconnect([]() { request->onDisconnect([](){
#ifdef ESP32 #ifdef ESP32
ESP.restart(); ESP.restart();
#elif defined(ESP8266) #elif defined(ESP8266)
@ -672,11 +675,23 @@ void setup(){
#ifdef USE_AUTH_STAT #ifdef USE_AUTH_STAT
if(!request->authenticate(http_username, http_password)) return request->requestAuthentication(); if(!request->authenticate(http_username, http_password)) return request->requestAuthentication();
#endif #endif
request->onDisconnect([]() { request->onDisconnect([](){
WiFi.disconnect(true);
#ifdef ESP32 #ifdef ESP32
/*
//https://github.com/espressif/arduino-esp32/issues/400#issuecomment-499631249
//WiFi.disconnect(true); // doesn't work on esp32, below needs #include "esp_wifi.h"
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); //load the flash-saved configs
esp_wifi_init(&cfg); //initiate and allocate wifi resources (does not matter if connection fails)
if(esp_wifi_restore()!=ESP_OK){
Serial.print(F("WiFi is not initialized by esp_wifi_init "));
} else {
Serial.print(F("WiFi Configurations Cleared!"));
}
*/
WiFi.disconnect(true, true); // Works on esp32
ESP.restart(); ESP.restart();
#elif defined(ESP8266) #elif defined(ESP8266)
WiFi.disconnect(true);
ESP.reset(); ESP.reset();
#endif #endif
}); });
@ -714,7 +729,7 @@ void setup(){
//OTA //OTA
ArduinoOTA.setHostname(hostName); ArduinoOTA.setHostname(hostName);
ArduinoOTA.onStart([]() { ArduinoOTA.onStart([](){
Serial.print(F("OTA Started ...\n")); Serial.print(F("OTA Started ...\n"));
MYFS.end(); // Clean FS MYFS.end(); // Clean FS
ws.textAll("Now,OTA"); // for all clients ws.textAll("Now,OTA"); // for all clients
@ -730,5 +745,6 @@ void loop(){
updateDHT(); updateDHT();
udht = false; udht = false;
} }
doOut();
ArduinoOTA.handle(); ArduinoOTA.handle();
} }

View File

@ -1,2 +1,3 @@
/*.gz /*.gz
/edit_gz
/.exclude.files /.exclude.files

Binary file not shown.

Binary file not shown.

View File

@ -17,6 +17,9 @@ until a } is found on separate new line.
``` npm install html-minifier-terser -g, npm install -g github-files-fetcher ``` ``` npm install html-minifier-terser -g, npm install -g github-files-fetcher ```
### Batch files provided ### Batch files provided
- **do.bat:** Generates **edit.htm.gz.h** file - **do_emb.bat:** Generates **edit.htm.gz.h** file for embedding to **SPIFFSEditor.cpp** as binary C array -
You need to comment **#define EDFS** at **SPIFFSEditor.cpp** for this choice
- **do_ed_fs.bat:** Alternatively, makes a gzip-ed **edit.htm** file for fs -
uncomment **#define EDFS** for this choice. Saves about 4k of program flash storage.
- **undo.bat:** Reverts **edit.htm** from C array header to file (still minified!) - **undo.bat:** Reverts **edit.htm** from C array header to file (still minified!)
- **update_ace.bat:** Updates **acefull.js.gz** file from latest GitHub Ace sources - **update_ace.bat:** Updates **acefull.js.gz** file from latest GitHub Ace sources

9
extras/do_ed_fs.bat Normal file
View File

@ -0,0 +1,9 @@
copy ..\src\edit.htm edit_src.htm
call html-minifier-terser --collapse-whitespace --remove-comments --remove-optional-tags --remove-redundant-attributes --remove-script-type-attributes --minify-css true --minify-js true -o edit.htm edit_src.htm
"C:\Program Files\7-Zip\7z.exe" a -tgzip -mx9 edit.htm.gz edit.htm
copy edit.htm.gz ..\examples\SmartSwitch\data\edit_gz
copy edit.htm.gz ..\examples\ESP_AsyncFSBrowser\data\edit_gz
ehg edit.htm.gz PROGMEM
copy edit.htm.gz.h ..\src\edit.htm.gz.h
pause
del edit.htm edit.htm.gz edit.htm.gz.h edit_src.htm

View File

@ -1,7 +1,9 @@
copy ..\src\edit.htm edit_src.htm copy ..\src\edit.htm edit_src.htm
call html-minifier-terser --collapse-whitespace --remove-comments --remove-optional-tags --remove-redundant-attributes --remove-script-type-attributes --minify-css true --minify-js true -o edit.htm edit_src.htm call html-minifier-terser --collapse-whitespace --remove-comments --remove-optional-tags --remove-redundant-attributes --remove-script-type-attributes --minify-css true --minify-js true -o edit.htm edit_src.htm
"C:\Program Files\7-Zip\7z.exe" a -tgzip -mx9 edit.htm.gz edit.htm "C:\Program Files\7-Zip\7z.exe" a -tgzip -mx9 edit.htm.gz edit.htm
ehg edit.htm.gz PROGMEM del ..\examples\SmartSwitch\data\edit.htm
copy edit.htm.gz.h ..\src\edit.htm.gz.h del ..\examples\ESP_AsyncFSBrowser\data\edit.htm
pause ehg edit.htm.gz PROGMEM
copy edit.htm.gz.h ..\src\edit.htm.gz.h
pause
del edit.htm edit.htm.gz edit.htm.gz.h edit_src.htm del edit.htm edit.htm.gz edit.htm.gz.h edit_src.htm

View File

@ -4,6 +4,7 @@ REM fetcher --url=resource_url --out=output_directory
call fetcher --url="https://github.com/ajaxorg/ace-builds/blob/master/src-min-noconflict/ace.js" --out=tmp1 call fetcher --url="https://github.com/ajaxorg/ace-builds/blob/master/src-min-noconflict/ace.js" --out=tmp1
call fetcher --url="https://github.com/ajaxorg/ace-builds/blob/master/src-min-noconflict/mode-html.js" --out=tmp1 call fetcher --url="https://github.com/ajaxorg/ace-builds/blob/master/src-min-noconflict/mode-html.js" --out=tmp1
call fetcher --url="https://github.com/ajaxorg/ace-builds/blob/master/src-min-noconflict/mode-json.js" --out=tmp1
call fetcher --url="https://github.com/ajaxorg/ace-builds/blob/master/src-min-noconflict/theme-monokai.js" --out=tmp1 call fetcher --url="https://github.com/ajaxorg/ace-builds/blob/master/src-min-noconflict/theme-monokai.js" --out=tmp1
call fetcher --url="https://github.com/ajaxorg/ace-builds/blob/master/src-min-noconflict/ext-searchbox.js" --out=tmp1 call fetcher --url="https://github.com/ajaxorg/ace-builds/blob/master/src-min-noconflict/ext-searchbox.js" --out=tmp1
@ -12,12 +13,14 @@ REM and do not take and include them below
call fetcher --url="https://github.com/ajaxorg/ace-builds/blob/master/src-min-noconflict/worker-html.js" --out=tmp1 call fetcher --url="https://github.com/ajaxorg/ace-builds/blob/master/src-min-noconflict/worker-html.js" --out=tmp1
call fetcher --url="https://github.com/ajaxorg/ace-builds/blob/master/src-min-noconflict/worker-css.js" --out=tmp1 call fetcher --url="https://github.com/ajaxorg/ace-builds/blob/master/src-min-noconflict/worker-css.js" --out=tmp1
call fetcher --url="https://github.com/ajaxorg/ace-builds/blob/master/src-min-noconflict/worker-javascript.js" --out=tmp1 call fetcher --url="https://github.com/ajaxorg/ace-builds/blob/master/src-min-noconflict/worker-javascript.js" --out=tmp1
call fetcher --url="https://github.com/ajaxorg/ace-builds/blob/master/src-min-noconflict/worker-json.js" --out=tmp1
cd tmp1 cd tmp1
type ace.js mode-html.js theme-monokai.js ext-searchbox.js > acefull.js type ace.js mode-html.js mode-json.js theme-monokai.js ext-searchbox.js > acefull.js
"C:\Program Files\7-Zip\7z.exe" a -tgzip -mx9 acefull.js.gz acefull.js "C:\Program Files\7-Zip\7z.exe" a -tgzip -mx9 acefull.js.gz acefull.js
"C:\Program Files\7-Zip\7z.exe" a -tgzip -mx9 worker-html.js.gz worker-html.js "C:\Program Files\7-Zip\7z.exe" a -tgzip -mx9 worker-html.js.gz worker-html.js
"C:\Program Files\7-Zip\7z.exe" a -tgzip -mx9 worker-javascript.js.gz worker-javascript.js "C:\Program Files\7-Zip\7z.exe" a -tgzip -mx9 worker-javascript.js.gz worker-javascript.js
"C:\Program Files\7-Zip\7z.exe" a -tgzip -mx9 worker-json.js.gz worker-json.js
"C:\Program Files\7-Zip\7z.exe" a -tgzip -mx9 worker-css.js.gz worker-css.js "C:\Program Files\7-Zip\7z.exe" a -tgzip -mx9 worker-css.js.gz worker-css.js
REM update SmartSwitch /data: REM update SmartSwitch /data:
@ -25,6 +28,7 @@ pause
copy acefull.js.gz ..\..\examples\SmartSwitch\data\acefull.js.gz copy acefull.js.gz ..\..\examples\SmartSwitch\data\acefull.js.gz
copy worker-html.js.gz ..\..\examples\SmartSwitch\data\worker-html.js.gz copy worker-html.js.gz ..\..\examples\SmartSwitch\data\worker-html.js.gz
copy worker-javascript.js.gz ..\..\examples\SmartSwitch\data\worker-javascript.js.gz copy worker-javascript.js.gz ..\..\examples\SmartSwitch\data\worker-javascript.js.gz
copy worker-json.js.gz ..\..\examples\SmartSwitch\data\worker-json.js.gz
copy worker-css.js.gz ..\..\examples\SmartSwitch\data\worker-css.js.gz copy worker-css.js.gz ..\..\examples\SmartSwitch\data\worker-css.js.gz
REM update ESP_AsyncFSBrowser /data: REM update ESP_AsyncFSBrowser /data:
@ -32,6 +36,7 @@ pause
copy acefull.js.gz ..\..\examples\ESP_AsyncFSBrowser\data\acefull.js.gz copy acefull.js.gz ..\..\examples\ESP_AsyncFSBrowser\data\acefull.js.gz
copy worker-html.js.gz ..\..\examples\ESP_AsyncFSBrowser\data\worker-html.js.gz copy worker-html.js.gz ..\..\examples\ESP_AsyncFSBrowser\data\worker-html.js.gz
copy worker-javascript.js.gz ..\..\examples\ESP_AsyncFSBrowser\data\worker-javascript.js.gz copy worker-javascript.js.gz ..\..\examples\ESP_AsyncFSBrowser\data\worker-javascript.js.gz
copy worker-json.js.gz ..\..\examples\ESP_AsyncFSBrowser\data\worker-json.js.gz
copy worker-css.js.gz ..\..\examples\ESP_AsyncFSBrowser\data\worker-css.js.gz copy worker-css.js.gz ..\..\examples\ESP_AsyncFSBrowser\data\worker-css.js.gz
REM delete temporary stuff REM delete temporary stuff

View File

@ -18,11 +18,15 @@
"platforms": ["espressif8266", "espressif32"], "platforms": ["espressif8266", "espressif32"],
"dependencies": [ "dependencies": [
{ {
"owner": "me-no-dev",
"name": "ESPAsyncTCP", "name": "ESPAsyncTCP",
"version": "^1.2.2",
"platforms": "espressif8266" "platforms": "espressif8266"
}, },
{ {
"owner": "me-no-dev",
"name": "AsyncTCP", "name": "AsyncTCP",
"version": "^1.1.1",
"platforms": "espressif32" "platforms": "espressif32"
}, },
{ {

View File

@ -137,16 +137,17 @@ size_t AsyncEventSourceMessage::ack(size_t len, uint32_t time) {
return 0; return 0;
} }
// This could also return void as the return value is not used.
// Leaving as-is for compatibility...
size_t AsyncEventSourceMessage::send(AsyncClient *client) { size_t AsyncEventSourceMessage::send(AsyncClient *client) {
const size_t len = _len - _sent; if (_sent >= _len) {
if(client->space() < len){ return 0;
return 0; }
} const size_t len_to_send = _len - _sent;
size_t sent = client->add((const char *)_data, len); auto position = reinterpret_cast<const char*>(_data + _sent);
if(client->canSend()) const size_t sent_now = client->write(position, len_to_send);
client->send(); _sent += sent_now;
_sent += sent; return sent_now;
return sent;
} }
// Client // Client
@ -173,7 +174,9 @@ AsyncEventSourceClient::AsyncEventSourceClient(AsyncWebServerRequest *request, A
} }
AsyncEventSourceClient::~AsyncEventSourceClient(){ AsyncEventSourceClient::~AsyncEventSourceClient(){
_messageQueue.free(); _lockmq.lock();
_messageQueue.free();
_lockmq.unlock();
close(); close();
} }
@ -184,33 +187,41 @@ void AsyncEventSourceClient::_queueMessage(AsyncEventSourceMessage *dataMessage)
delete dataMessage; delete dataMessage;
return; return;
} }
//length() is not thread-safe, thus acquiring the lock before this call..
_lockmq.lock();
if(_messageQueue.length() >= SSE_MAX_QUEUED_MESSAGES){ if(_messageQueue.length() >= SSE_MAX_QUEUED_MESSAGES){
ets_printf(String(F("ERROR: Too many messages queued\n")).c_str()); ets_printf(String(F("ERROR: Too many messages queued\n")).c_str());
delete dataMessage; delete dataMessage;
} else { } else {
_messageQueue.add(dataMessage); _messageQueue.add(dataMessage);
// runqueue trigger when new messages added
if(_client->canSend()) {
_runQueue();
}
} }
if(_client->canSend()) _lockmq.unlock();
_runQueue();
} }
void AsyncEventSourceClient::_onAck(size_t len, uint32_t time){ void AsyncEventSourceClient::_onAck(size_t len, uint32_t time){
// Same here, acquiring the lock early
_lockmq.lock();
while(len && !_messageQueue.isEmpty()){ while(len && !_messageQueue.isEmpty()){
len = _messageQueue.front()->ack(len, time); len = _messageQueue.front()->ack(len, time);
if(_messageQueue.front()->finished()) if(_messageQueue.front()->finished())
_messageQueue.remove(_messageQueue.front()); _messageQueue.remove(_messageQueue.front());
} }
_runQueue(); _runQueue();
_lockmq.unlock();
} }
void AsyncEventSourceClient::_onPoll(){ void AsyncEventSourceClient::_onPoll(){
_lockmq.lock();
if(!_messageQueue.isEmpty()){ if(!_messageQueue.isEmpty()){
_runQueue(); _runQueue();
} }
_lockmq.unlock();
} }
void AsyncEventSourceClient::_onTimeout(uint32_t time __attribute__((unused))){ void AsyncEventSourceClient::_onTimeout(uint32_t time __attribute__((unused))){
_client->close(true); _client->close(true);
} }
@ -225,7 +236,7 @@ void AsyncEventSourceClient::close(){
_client->close(); _client->close();
} }
void AsyncEventSourceClient::write(const char * message, size_t len){ void AsyncEventSourceClient::_write(const char * message, size_t len){
_queueMessage(new AsyncEventSourceMessage(message, len)); _queueMessage(new AsyncEventSourceMessage(message, len));
} }
@ -234,15 +245,23 @@ void AsyncEventSourceClient::send(const char *message, const char *event, uint32
_queueMessage(new AsyncEventSourceMessage(ev.c_str(), ev.length())); _queueMessage(new AsyncEventSourceMessage(ev.c_str(), ev.length()));
} }
void AsyncEventSourceClient::_runQueue(){ size_t AsyncEventSourceClient::packetsWaiting() const {
while(!_messageQueue.isEmpty() && _messageQueue.front()->finished()){ size_t len;
_messageQueue.remove(_messageQueue.front()); _lockmq.lock();
} len = _messageQueue.length();
_lockmq.unlock();
return len;
}
for(auto i = _messageQueue.begin(); i != _messageQueue.end(); ++i) void AsyncEventSourceClient::_runQueue() {
{ // Calls to this private method now already protected by _lockmq acquisition
if(!(*i)->sent()) // so no extra call of _lockmq.lock() here..
for (auto i = _messageQueue.begin(); i != _messageQueue.end(); ++i) {
// If it crashes here, iterator (i) has been invalidated as _messageQueue
// has been changed... (UL 2020-11-15: Not supposed to happen any more ;-) )
if (!(*i)->sent()) {
(*i)->send(_client); (*i)->send(_client);
}
} }
} }
@ -280,17 +299,22 @@ void AsyncEventSource::_addClient(AsyncEventSourceClient * client){
client->write((const char *)temp, 2053); client->write((const char *)temp, 2053);
free(temp); free(temp);
}*/ }*/
AsyncWebLockGuard l(_client_queue_lock);
_clients.add(client); _clients.add(client);
if(_connectcb) if(_connectcb)
_connectcb(client); _connectcb(client);
} }
void AsyncEventSource::_handleDisconnect(AsyncEventSourceClient * client){ void AsyncEventSource::_handleDisconnect(AsyncEventSourceClient * client){
AsyncWebLockGuard l(_client_queue_lock);
_clients.remove(client); _clients.remove(client);
} }
void AsyncEventSource::close(){ void AsyncEventSource::close(){
// While the whole loop is not done, the linked list is locked and so the
// iterator should remain valid even when AsyncEventSource::_handleDisconnect()
// is called very early
AsyncWebLockGuard l(_client_queue_lock);
for(const auto &c: _clients){ for(const auto &c: _clients){
if(c->connected()) if(c->connected())
c->close(); c->close();
@ -299,37 +323,39 @@ void AsyncEventSource::close(){
// pmb fix // pmb fix
size_t AsyncEventSource::avgPacketsWaiting() const { size_t AsyncEventSource::avgPacketsWaiting() const {
if(_clients.isEmpty()) size_t aql = 0;
uint32_t nConnectedClients = 0;
AsyncWebLockGuard l(_client_queue_lock);
if (_clients.isEmpty()) {
return 0; return 0;
}
size_t aql=0;
uint32_t nConnectedClients=0;
for(const auto &c: _clients){ for(const auto &c: _clients){
if(c->connected()) { if(c->connected()) {
aql+=c->packetsWaiting(); aql += c->packetsWaiting();
++nConnectedClients; ++nConnectedClients;
} }
} }
// return aql / nConnectedClients; return ((aql) + (nConnectedClients/2)) / (nConnectedClients); // round up
return ((aql) + (nConnectedClients/2))/(nConnectedClients); // round up
} }
void AsyncEventSource::send(const char *message, const char *event, uint32_t id, uint32_t reconnect){ void AsyncEventSource::send(
const char *message, const char *event, uint32_t id, uint32_t reconnect){
String ev = generateEventMessage(message, event, id, reconnect); String ev = generateEventMessage(message, event, id, reconnect);
AsyncWebLockGuard l(_client_queue_lock);
for(const auto &c: _clients){ for(const auto &c: _clients){
if(c->connected()) { if(c->connected()) {
c->write(ev.c_str(), ev.length()); c->_write(ev.c_str(), ev.length());
} }
} }
} }
size_t AsyncEventSource::count() const { size_t AsyncEventSource::count() const {
return _clients.count_if([](AsyncEventSourceClient *c){ size_t n_clients;
return c->connected(); AsyncWebLockGuard l(_client_queue_lock);
}); n_clients = _clients.count_if([](AsyncEventSourceClient *c){
return c->connected();
});
return n_clients;
} }
bool AsyncEventSource::canHandle(AsyncWebServerRequest *request){ bool AsyncEventSource::canHandle(AsyncWebServerRequest *request){

View File

@ -73,6 +73,8 @@ class AsyncEventSourceClient {
AsyncEventSource *_server; AsyncEventSource *_server;
uint32_t _lastId; uint32_t _lastId;
LinkedList<AsyncEventSourceMessage *> _messageQueue; LinkedList<AsyncEventSourceMessage *> _messageQueue;
// ArFi 2020-08-27 for protecting/serializing _messageQueue
AsyncPlainLock _lockmq;
void _queueMessage(AsyncEventSourceMessage *dataMessage); void _queueMessage(AsyncEventSourceMessage *dataMessage);
void _runQueue(); void _runQueue();
@ -83,12 +85,12 @@ class AsyncEventSourceClient {
AsyncClient* client(){ return _client; } AsyncClient* client(){ return _client; }
void close(); void close();
void write(const char * message, size_t len);
void send(const char *message, const char *event=NULL, uint32_t id=0, uint32_t reconnect=0); void send(const char *message, const char *event=NULL, uint32_t id=0, uint32_t reconnect=0);
bool connected() const { return (_client != NULL) && _client->connected(); } bool connected() const { return (_client != NULL) && _client->connected(); }
uint32_t lastId() const { return _lastId; } uint32_t lastId() const { return _lastId; }
size_t packetsWaiting() const { return _messageQueue.length(); } size_t packetsWaiting() const;
void _write(const char * message, size_t len);
//system callbacks (do not call) //system callbacks (do not call)
void _onAck(size_t len, uint32_t time); void _onAck(size_t len, uint32_t time);
void _onPoll(); void _onPoll();
@ -100,6 +102,9 @@ class AsyncEventSource: public AsyncWebHandler {
private: private:
String _url; String _url;
LinkedList<AsyncEventSourceClient *> _clients; LinkedList<AsyncEventSourceClient *> _clients;
// Same as for individual messages, protect mutations of _clients list
// since simultaneous access from different tasks is possible
AsyncWebLock _client_queue_lock;
ArEventHandlerFunction _connectcb; ArEventHandlerFunction _connectcb;
ArAuthorizeConnectHandler _authorizeConnectHandler; ArAuthorizeConnectHandler _authorizeConnectHandler;
public: public:
@ -111,7 +116,7 @@ class AsyncEventSource: public AsyncWebHandler {
void onConnect(ArEventHandlerFunction cb); void onConnect(ArEventHandlerFunction cb);
void authorizeConnect(ArAuthorizeConnectHandler cb); void authorizeConnect(ArAuthorizeConnectHandler cb);
void send(const char *message, const char *event=NULL, uint32_t id=0, uint32_t reconnect=0); void send(const char *message, const char *event=NULL, uint32_t id=0, uint32_t reconnect=0);
size_t count() const; //number clinets connected size_t count() const; //number clients connected
size_t avgPacketsWaiting() const; size_t avgPacketsWaiting() const;
//system callbacks (do not call) //system callbacks (do not call)

View File

@ -41,7 +41,9 @@
#if ARDUINOJSON_VERSION_MAJOR == 5 #if ARDUINOJSON_VERSION_MAJOR == 5
#define ARDUINOJSON_5_COMPATIBILITY #define ARDUINOJSON_5_COMPATIBILITY
#else #else
#define DYNAMIC_JSON_DOCUMENT_SIZE 1024 #ifndef DYNAMIC_JSON_DOCUMENT_SIZE
#define DYNAMIC_JSON_DOCUMENT_SIZE 1024
#endif
#endif #endif
constexpr const char* JSON_MIMETYPE = "application/json"; constexpr const char* JSON_MIMETYPE = "application/json";

File diff suppressed because it is too large Load Diff

View File

@ -24,7 +24,7 @@
#include <Arduino.h> #include <Arduino.h>
#ifdef ESP32 #ifdef ESP32
#include <AsyncTCP.h> #include <AsyncTCP.h>
#define WS_MAX_QUEUED_MESSAGES 32 #define WS_MAX_QUEUED_MESSAGES 16
#else #else
#include <ESPAsyncTCP.h> #include <ESPAsyncTCP.h>
#define WS_MAX_QUEUED_MESSAGES 8 #define WS_MAX_QUEUED_MESSAGES 8
@ -33,6 +33,10 @@
#include "AsyncWebSynchronization.h" #include "AsyncWebSynchronization.h"
#include <list>
#include <deque>
#include <memory>
#ifdef ESP8266 #ifdef ESP8266
#include <Hash.h> #include <Hash.h>
#ifdef CRYPTO_HASH_h // include Hash.h from espressif framework if the first include was from the crypto library #ifdef CRYPTO_HASH_h // include Hash.h from espressif framework if the first include was from the crypto library
@ -46,6 +50,8 @@
#define DEFAULT_MAX_WS_CLIENTS 4 #define DEFAULT_MAX_WS_CLIENTS 4
#endif #endif
using AsyncWebSocketMessageBuffer = std::shared_ptr<std::vector<uint8_t>>;
class AsyncWebSocket; class AsyncWebSocket;
class AsyncWebSocketResponse; class AsyncWebSocketResponse;
class AsyncWebSocketClient; class AsyncWebSocketClient;
@ -80,78 +86,26 @@ typedef enum { WS_CONTINUATION, WS_TEXT, WS_BINARY, WS_DISCONNECT = 0x08, WS_PIN
typedef enum { WS_MSG_SENDING, WS_MSG_SENT, WS_MSG_ERROR } AwsMessageStatus; typedef enum { WS_MSG_SENDING, WS_MSG_SENT, WS_MSG_ERROR } AwsMessageStatus;
typedef enum { WS_EVT_CONNECT, WS_EVT_DISCONNECT, WS_EVT_PONG, WS_EVT_ERROR, WS_EVT_DATA } AwsEventType; typedef enum { WS_EVT_CONNECT, WS_EVT_DISCONNECT, WS_EVT_PONG, WS_EVT_ERROR, WS_EVT_DATA } AwsEventType;
class AsyncWebSocketMessageBuffer {
private:
uint8_t * _data;
size_t _len;
bool _lock;
uint32_t _count;
public: class AsyncWebSocketMessage
AsyncWebSocketMessageBuffer(); {
AsyncWebSocketMessageBuffer(size_t size); private:
AsyncWebSocketMessageBuffer(uint8_t * data, size_t size); std::shared_ptr<std::vector<uint8_t>> _WSbuffer;
AsyncWebSocketMessageBuffer(const AsyncWebSocketMessageBuffer &); uint8_t _opcode{WS_TEXT};
AsyncWebSocketMessageBuffer(AsyncWebSocketMessageBuffer &&); bool _mask{false};
~AsyncWebSocketMessageBuffer(); AwsMessageStatus _status{WS_MSG_ERROR};
void operator ++(int i) { (void)i; _count++; } size_t _sent{};
void operator --(int i) { (void)i; if (_count > 0) { _count--; } ; } size_t _ack{};
bool reserve(size_t size); size_t _acked{};
void lock() { _lock = true; }
void unlock() { _lock = false; }
uint8_t * get() { return _data; }
size_t length() { return _len; }
uint32_t count() { return _count; }
bool canDelete() { return (!_count && !_lock); }
friend AsyncWebSocket;
};
class AsyncWebSocketMessage {
protected:
uint8_t _opcode;
bool _mask;
AwsMessageStatus _status;
public:
AsyncWebSocketMessage():_opcode(WS_TEXT),_mask(false),_status(WS_MSG_ERROR){}
virtual ~AsyncWebSocketMessage(){}
virtual void ack(size_t len __attribute__((unused)), uint32_t time __attribute__((unused))){}
virtual size_t send(AsyncClient *client __attribute__((unused))){ return 0; }
virtual bool finished(){ return _status != WS_MSG_SENDING; }
virtual bool betweenFrames() const { return false; }
};
class AsyncWebSocketBasicMessage: public AsyncWebSocketMessage {
private:
size_t _len;
size_t _sent;
size_t _ack;
size_t _acked;
uint8_t * _data;
public: public:
AsyncWebSocketBasicMessage(const char * data, size_t len, uint8_t opcode=WS_TEXT, bool mask=false); AsyncWebSocketMessage(std::shared_ptr<std::vector<uint8_t>> buffer, uint8_t opcode=WS_TEXT, bool mask=false);
AsyncWebSocketBasicMessage(uint8_t opcode=WS_TEXT, bool mask=false);
virtual ~AsyncWebSocketBasicMessage() override;
virtual bool betweenFrames() const override { return _acked == _ack; }
virtual void ack(size_t len, uint32_t time) override ;
virtual size_t send(AsyncClient *client) override ;
};
class AsyncWebSocketMultiMessage: public AsyncWebSocketMessage { bool finished() const { return _status != WS_MSG_SENDING; }
private: bool betweenFrames() const { return _acked == _ack; }
uint8_t * _data;
size_t _len; void ack(size_t len, uint32_t time);
size_t _sent; size_t send(AsyncClient *client);
size_t _ack;
size_t _acked;
AsyncWebSocketMessageBuffer * _WSbuffer;
public:
AsyncWebSocketMultiMessage(AsyncWebSocketMessageBuffer * buffer, uint8_t opcode=WS_TEXT, bool mask=false);
virtual ~AsyncWebSocketMultiMessage() override;
virtual bool betweenFrames() const override { return _acked == _ack; }
virtual void ack(size_t len, uint32_t time) override ;
virtual size_t send(AsyncClient *client) override ;
}; };
class AsyncWebSocketClient { class AsyncWebSocketClient {
@ -161,8 +115,10 @@ class AsyncWebSocketClient {
uint32_t _clientId; uint32_t _clientId;
AwsClientStatus _status; AwsClientStatus _status;
LinkedList<AsyncWebSocketControl *> _controlQueue; AsyncWebLock _lock;
LinkedList<AsyncWebSocketMessage *> _messageQueue;
std::deque<AsyncWebSocketControl> _controlQueue;
std::deque<AsyncWebSocketMessage> _messageQueue;
uint8_t _pstate; uint8_t _pstate;
AwsFrameInfo _pinfo; AwsFrameInfo _pinfo;
@ -170,9 +126,10 @@ class AsyncWebSocketClient {
uint32_t _lastMessageTime; uint32_t _lastMessageTime;
uint32_t _keepAlivePeriod; uint32_t _keepAlivePeriod;
void _queueMessage(AsyncWebSocketMessage *dataMessage); void _queueControl(uint8_t opcode, const uint8_t *data=NULL, size_t len=0, bool mask=false);
void _queueControl(AsyncWebSocketControl *controlMessage); void _queueMessage(std::shared_ptr<std::vector<uint8_t>> buffer, uint8_t opcode=WS_TEXT, bool mask=false);
void _runQueue(); void _runQueue();
void _clearQueue();
public: public:
void *_tempObject; void *_tempObject;
@ -181,18 +138,22 @@ class AsyncWebSocketClient {
~AsyncWebSocketClient(); ~AsyncWebSocketClient();
//client id increments for the given server //client id increments for the given server
uint32_t id(){ return _clientId; } uint32_t id() const { return _clientId; }
AwsClientStatus status(){ return _status; } AwsClientStatus status() const { return _status; }
AsyncClient* client(){ return _client; } AsyncClient* client() { return _client; }
const AsyncClient* client() const { return _client; }
AsyncWebSocket *server(){ return _server; } AsyncWebSocket *server(){ return _server; }
const AsyncWebSocket *server() const { return _server; }
AwsFrameInfo const &pinfo() const { return _pinfo; } AwsFrameInfo const &pinfo() const { return _pinfo; }
IPAddress remoteIP(); IPAddress remoteIP() const;
uint16_t remotePort(); uint16_t remotePort() const;
bool shouldBeDeleted() const { return !_client; }
//control frames //control frames
void close(uint16_t code=0, const char * message=NULL); void close(uint16_t code=0, const char * message=NULL);
void ping(uint8_t *data=NULL, size_t len=0); void ping(const uint8_t *data=NULL, size_t len=0);
//set auto-ping period in seconds. disabled if zero (default) //set auto-ping period in seconds. disabled if zero (default)
void keepAlivePeriod(uint16_t seconds){ void keepAlivePeriod(uint16_t seconds){
@ -203,30 +164,49 @@ class AsyncWebSocketClient {
} }
//data packets //data packets
void message(AsyncWebSocketMessage *message){ _queueMessage(message); } /**
bool queueIsFull(); * @brief allocate memory buffer owned by shared-pointer and copy provided data
* used to keep the data untill websocket send is complete for single/multiple clients
*
* @param message
* @param len
* @return AsyncWebSocketMessageBuffer
*/
AsyncWebSocketMessageBuffer makeBuffer(const uint8_t *message, size_t len);
/**
* @brief allocate empty memory buffer owned by shared-pointer
* used to keep the data untill websocket send is complete for single/multiple clients
*
* @param len
* @return AsyncWebSocketMessageBuffer
*/
inline AsyncWebSocketMessageBuffer makeBuffer(size_t len){ return std::make_shared<std::vector<uint8_t>>(len); };
void message(AsyncWebSocketMessageBuffer buffer, uint8_t opcode=WS_TEXT, bool mask=false) { _queueMessage(buffer, opcode, mask); }
bool queueIsFull() const;
size_t queueLen() const;
size_t printf(const char *format, ...) __attribute__ ((format (printf, 2, 3))); size_t printf(const char *format, ...) __attribute__ ((format (printf, 2, 3)));
#ifndef ESP32 #ifndef ESP32
size_t printf_P(PGM_P formatP, ...) __attribute__ ((format (printf, 2, 3))); size_t printf_P(PGM_P formatP, ...) __attribute__ ((format (printf, 2, 3)));
#endif #endif
void text(const char * message, size_t len);
void text(const char * message);
void text(uint8_t * message, size_t len);
void text(char * message);
void text(const String &message);
void text(const __FlashStringHelper *data);
void text(AsyncWebSocketMessageBuffer *buffer);
void binary(const char * message, size_t len); inline void text(AsyncWebSocketMessageBuffer buffer){ _queueMessage(buffer); };
void binary(const char * message); inline void text(const uint8_t *message, size_t len){ text(makeBuffer(message, len)); };
void binary(uint8_t * message, size_t len); inline void text(const char *message, size_t len){ text((const uint8_t *)message, len); };
void binary(char * message); inline void text(const char *message){ text(message, strlen(message)); };
void binary(const String &message); inline void text(const String &message){ text(message.c_str(), message.length()); };
void binary(const __FlashStringHelper *data, size_t len); void text(const __FlashStringHelper *message);
void binary(AsyncWebSocketMessageBuffer *buffer);
bool canSend() { return _messageQueue.length() < WS_MAX_QUEUED_MESSAGES; } inline void binary(AsyncWebSocketMessageBuffer buffer){ _queueMessage(buffer, WS_BINARY); };
inline void binary(const uint8_t *message, size_t len){ binary(makeBuffer(message, len)); };
inline void binary(const char * message, size_t len){ binary((const uint8_t *)message, len); };
inline void binary(const char * message){ binary(message, strlen(message)); };
inline void binary(const String &message){ binary(message.c_str(), message.length()); };
void binary(const __FlashStringHelper *message, size_t len);
bool canSend() const;
//system callbacks (do not call) //system callbacks (do not call)
void _onAck(size_t len, uint32_t time); void _onAck(size_t len, uint32_t time);
@ -242,14 +222,12 @@ typedef std::function<void(AsyncWebSocket * server, AsyncWebSocketClient * clien
//WebServer Handler implementation that plays the role of a socket server //WebServer Handler implementation that plays the role of a socket server
class AsyncWebSocket: public AsyncWebHandler { class AsyncWebSocket: public AsyncWebHandler {
public:
typedef LinkedList<AsyncWebSocketClient *> AsyncWebSocketClientLinkedList;
private: private:
String _url; String _url;
AsyncWebSocketClientLinkedList _clients; std::list<AsyncWebSocketClient> _clients;
uint32_t _cNextId; uint32_t _cNextId;
AwsEventHandler _eventHandler; AwsEventHandler _eventHandler;
AwsHandshakeHandler _handshakeHandler; AwsHandshakeHandler _handshakeHandler;
bool _enabled; bool _enabled;
AsyncWebLock _lock; AsyncWebLock _lock;
@ -270,41 +248,56 @@ class AsyncWebSocket: public AsyncWebHandler {
void closeAll(uint16_t code=0, const char * message=NULL); void closeAll(uint16_t code=0, const char * message=NULL);
void cleanupClients(uint16_t maxClients = DEFAULT_MAX_WS_CLIENTS); void cleanupClients(uint16_t maxClients = DEFAULT_MAX_WS_CLIENTS);
void ping(uint32_t id, uint8_t *data=NULL, size_t len=0); void ping(uint32_t id, const uint8_t *data=NULL, size_t len=0);
void pingAll(uint8_t *data=NULL, size_t len=0); // done void pingAll(const uint8_t *data=NULL, size_t len=0); // done
void text(uint32_t id, const char * message, size_t len); //data packets
void text(uint32_t id, const char * message); /**
void text(uint32_t id, uint8_t * message, size_t len); * @brief allocate memory buffer owned by shared-pointer and copy provided data
void text(uint32_t id, char * message); * used to keep the data untill websocket send is complete for single/multiple clients
void text(uint32_t id, const String &message); *
* @param message
* @param len
* @return AsyncWebSocketMessageBuffer
*/
AsyncWebSocketMessageBuffer makeBuffer(const uint8_t *message, size_t len);
/**
* @brief allocate empty memory buffer owned by shared-pointer
* used to keep the data untill websocket send is complete for single/multiple clients
*
* @param len
* @return AsyncWebSocketMessageBuffer
*/
inline AsyncWebSocketMessageBuffer makeBuffer(size_t len){ return std::make_shared<std::vector<uint8_t>>(len); };
void text(uint32_t id, AsyncWebSocketMessageBuffer message);
void text(uint32_t id, const uint8_t * message, size_t len);
inline void text(uint32_t id, const char *message, size_t len){ text(id, (const uint8_t *)message, len); };
inline void text(uint32_t id, const char *message){ text(id, message, strlen(message)); };
inline void text(uint32_t id, const String &message){ text(id, message.c_str(), message.length()); };
void text(uint32_t id, const __FlashStringHelper *message); void text(uint32_t id, const __FlashStringHelper *message);
void textAll(AsyncWebSocketMessageBuffer buffer);
void textAll(const uint8_t *message, size_t len);
void textAll(const char * message, size_t len); void textAll(const char * message, size_t len);
void textAll(const char * message); void textAll(const char * message);
void textAll(uint8_t * message, size_t len);
void textAll(char * message);
void textAll(const String &message); void textAll(const String &message);
void textAll(const __FlashStringHelper *message); // need to convert void textAll(const __FlashStringHelper *message); // need to convert
void textAll(AsyncWebSocketMessageBuffer * buffer);
void binary(uint32_t id, const char * message, size_t len); void binary(uint32_t id, AsyncWebSocketMessageBuffer message);
void binary(uint32_t id, const char * message); void binary(uint32_t id, const uint8_t *message, size_t len);
void binary(uint32_t id, uint8_t * message, size_t len); void binary(uint32_t id, const char *message, size_t len);
void binary(uint32_t id, char * message); void binary(uint32_t id, const char *message);
void binary(uint32_t id, const String &message); void binary(uint32_t id, const String &message);
void binary(uint32_t id, const __FlashStringHelper *message, size_t len); void binary(uint32_t id, const __FlashStringHelper *message, size_t len);
void binaryAll(const char * message, size_t len); void binaryAll(AsyncWebSocketMessageBuffer buffer);
void binaryAll(const char * message); void binaryAll(const uint8_t *message, size_t len);
void binaryAll(uint8_t * message, size_t len); void binaryAll(const char *message, size_t len);
void binaryAll(char * message); void binaryAll(const char *message);
void binaryAll(const String &message); void binaryAll(const String &message);
void binaryAll(const __FlashStringHelper *message, size_t len); void binaryAll(const __FlashStringHelper *message, size_t len);
void binaryAll(AsyncWebSocketMessageBuffer * buffer);
void message(uint32_t id, AsyncWebSocketMessage *message);
void messageAll(AsyncWebSocketMultiMessage *message);
size_t printf(uint32_t id, const char *format, ...) __attribute__ ((format (printf, 3, 4))); size_t printf(uint32_t id, const char *format, ...) __attribute__ ((format (printf, 3, 4)));
size_t printfAll(const char *format, ...) __attribute__ ((format (printf, 2, 3))); size_t printfAll(const char *format, ...) __attribute__ ((format (printf, 2, 3)));
@ -325,20 +318,12 @@ class AsyncWebSocket: public AsyncWebHandler {
//system callbacks (do not call) //system callbacks (do not call)
uint32_t _getNextId(){ return _cNextId++; } uint32_t _getNextId(){ return _cNextId++; }
void _addClient(AsyncWebSocketClient * client); AsyncWebSocketClient *_newClient(AsyncWebServerRequest *request);
void _handleDisconnect(AsyncWebSocketClient * client);
void _handleEvent(AsyncWebSocketClient * client, AwsEventType type, void * arg, uint8_t *data, size_t len); void _handleEvent(AsyncWebSocketClient * client, AwsEventType type, void * arg, uint8_t *data, size_t len);
virtual bool canHandle(AsyncWebServerRequest *request) override final; virtual bool canHandle(AsyncWebServerRequest *request) override final;
virtual void handleRequest(AsyncWebServerRequest *request) override final; virtual void handleRequest(AsyncWebServerRequest *request) override final;
const std::list<AsyncWebSocketClient> &getClients() const { return _clients; }
// messagebuffer functions/objects.
AsyncWebSocketMessageBuffer * makeBuffer(size_t size = 0);
AsyncWebSocketMessageBuffer * makeBuffer(uint8_t * data, size_t size);
LinkedList<AsyncWebSocketMessageBuffer *> _buffers;
void _cleanBuffers();
AsyncWebSocketClientLinkedList getClients() const;
}; };
//WebServer response to authenticate the socket and detach the tcp client from the web server request //WebServer response to authenticate the socket and detach the tcp client from the web server request

View File

@ -8,39 +8,75 @@
#ifdef ESP32 #ifdef ESP32
// This is the ESP32 version of the Sync Lock, using the FreeRTOS Semaphore // This is the ESP32 version of the Sync Lock, using the FreeRTOS Semaphore
class AsyncWebLock // Modified 'AsyncWebLock' to just only use mutex since pxCurrentTCB is not
// always available. According to example by Arjan Filius, changed name,
// added unimplemented version for ESP8266
class AsyncPlainLock
{ {
private: private:
SemaphoreHandle_t _lock; SemaphoreHandle_t _lock;
mutable void *_lockedBy;
public: public:
AsyncWebLock() { AsyncPlainLock() {
_lock = xSemaphoreCreateBinary(); _lock = xSemaphoreCreateBinary();
_lockedBy = NULL; // In this fails, the system is likely that much out of memory that
// we should abort anyways. If assertions are disabled, nothing is lost..
assert(_lock);
xSemaphoreGive(_lock); xSemaphoreGive(_lock);
} }
~AsyncWebLock() { ~AsyncPlainLock() {
vSemaphoreDelete(_lock); vSemaphoreDelete(_lock);
} }
bool lock() const { bool lock() const {
extern void *pxCurrentTCB;
if (_lockedBy != pxCurrentTCB) {
xSemaphoreTake(_lock, portMAX_DELAY); xSemaphoreTake(_lock, portMAX_DELAY);
_lockedBy = pxCurrentTCB;
return true; return true;
}
return false;
} }
void unlock() const { void unlock() const {
_lockedBy = NULL;
xSemaphoreGive(_lock); xSemaphoreGive(_lock);
} }
}; };
// This is the ESP32 version of the Sync Lock, using the FreeRTOS Semaphore
class AsyncWebLock
{
private:
SemaphoreHandle_t _lock;
mutable TaskHandle_t _lockedBy{};
public:
AsyncWebLock()
{
_lock = xSemaphoreCreateBinary();
// In this fails, the system is likely that much out of memory that
// we should abort anyways. If assertions are disabled, nothing is lost..
assert(_lock);
_lockedBy = NULL;
xSemaphoreGive(_lock);
}
~AsyncWebLock() {
vSemaphoreDelete(_lock);
}
bool lock() const {
const auto currentTask = xTaskGetCurrentTaskHandle();
if (_lockedBy != currentTask) {
xSemaphoreTake(_lock, portMAX_DELAY);
_lockedBy = currentTask;
return true;
}
return false;
}
void unlock() const {
_lockedBy = NULL;
xSemaphoreGive(_lock);
}
};
#else #else
// This is the 8266 version of the Sync Lock which is currently unimplemented // This is the 8266 version of the Sync Lock which is currently unimplemented
@ -61,6 +97,10 @@ public:
void unlock() const { void unlock() const {
} }
}; };
// Same for AsyncPlainLock, for ESP8266 this is just the unimplemented version above.
using AsyncPlainLock = AsyncWebLock;
#endif #endif
class AsyncWebLockGuard class AsyncWebLockGuard
@ -82,6 +122,13 @@ public:
_lock->unlock(); _lock->unlock();
} }
} }
void unlock() {
if (_lock) {
_lock->unlock();
_lock = NULL;
}
}
}; };
#endif // ASYNCWEBSYNCHRONIZATION_H_ #endif // ASYNCWEBSYNCHRONIZATION_H_

View File

@ -24,6 +24,8 @@
#include "Arduino.h" #include "Arduino.h"
#include <functional> #include <functional>
#include <list>
#include <vector>
#include "FS.h" #include "FS.h"
#include "StringArray.h" #include "StringArray.h"
@ -59,22 +61,14 @@ class AsyncResponseStream;
#ifndef WEBSERVER_H #ifndef WEBSERVER_H
typedef enum { typedef enum {
HTTP_GET = 0b0000000000000001, HTTP_GET = 0b00000001,
HTTP_POST = 0b0000000000000010, HTTP_POST = 0b00000010,
HTTP_DELETE = 0b0000000000000100, HTTP_DELETE = 0b00000100,
HTTP_PUT = 0b0000000000001000, HTTP_PUT = 0b00001000,
HTTP_PATCH = 0b0000000000010000, HTTP_PATCH = 0b00010000,
HTTP_HEAD = 0b0000000000100000, HTTP_HEAD = 0b00100000,
HTTP_OPTIONS = 0b0000000001000000, HTTP_OPTIONS = 0b01000000,
HTTP_PROPFIND = 0b0000000010000000, HTTP_ANY = 0b01111111,
HTTP_LOCK = 0b0000000100000000,
HTTP_UNLOCK = 0b0000001000000000,
HTTP_PROPPATCH = 0b0000010000000000,
HTTP_MKCOL = 0b0000100000000000,
HTTP_MOVE = 0b0001000000000000,
HTTP_COPY = 0b0010000000000000,
HTTP_RESERVED = 0b0100000000000000,
HTTP_ANY = 0b0111111111111111,
} WebRequestMethod; } WebRequestMethod;
#endif #endif
@ -94,7 +88,7 @@ namespace fs {
//if this value is returned when asked for data, packet will not be sent and you will be asked for data again //if this value is returned when asked for data, packet will not be sent and you will be asked for data again
#define RESPONSE_TRY_AGAIN 0xFFFFFFFF #define RESPONSE_TRY_AGAIN 0xFFFFFFFF
typedef uint16_t WebRequestMethodComposite; typedef uint8_t WebRequestMethodComposite;
typedef std::function<void(void)> ArDisconnectHandler; typedef std::function<void(void)> ArDisconnectHandler;
/* /*
@ -129,6 +123,9 @@ class AsyncWebHeader {
String _value; String _value;
public: public:
AsyncWebHeader() = default;
AsyncWebHeader(const AsyncWebHeader &) = default;
AsyncWebHeader(const String& name, const String& value): _name(name), _value(value){} AsyncWebHeader(const String& name, const String& value): _name(name), _value(value){}
AsyncWebHeader(const String& data): _name(), _value(){ AsyncWebHeader(const String& data): _name(), _value(){
if(!data) return; if(!data) return;
@ -137,10 +134,12 @@ class AsyncWebHeader {
_name = data.substring(0, index); _name = data.substring(0, index);
_value = data.substring(index + 2); _value = data.substring(index + 2);
} }
~AsyncWebHeader(){}
AsyncWebHeader &operator=(const AsyncWebHeader &) = default;
const String& name() const { return _name; } const String& name() const { return _name; }
const String& value() const { return _value; } const String& value() const { return _value; }
String toString() const { return String(_name + F(": ") + _value + F("\r\n")); } String toString() const { return _name + F(": ") + _value + F("\r\n"); }
}; };
/* /*
@ -162,7 +161,7 @@ class AsyncWebServerRequest {
AsyncWebServer* _server; AsyncWebServer* _server;
AsyncWebHandler* _handler; AsyncWebHandler* _handler;
AsyncWebServerResponse* _response; AsyncWebServerResponse* _response;
StringArray _interestingHeaders; std::vector<String> _interestingHeaders;
ArDisconnectHandler _onDisconnectfn; ArDisconnectHandler _onDisconnectfn;
String _temp; String _temp;
@ -184,9 +183,9 @@ class AsyncWebServerRequest {
size_t _contentLength; size_t _contentLength;
size_t _parsedLength; size_t _parsedLength;
LinkedList<AsyncWebHeader *> _headers; std::list<AsyncWebHeader> _headers;
LinkedList<AsyncWebParameter *> _params; LinkedList<AsyncWebParameter *> _params;
LinkedList<String *> _pathParams; std::vector<String> _pathParams;
uint8_t _multiParseState; uint8_t _multiParseState;
uint8_t _boundaryPosition; uint8_t _boundaryPosition;
@ -278,9 +277,12 @@ class AsyncWebServerRequest {
bool hasHeader(const String& name) const; // check if header exists bool hasHeader(const String& name) const; // check if header exists
bool hasHeader(const __FlashStringHelper * data) const; // check if header exists bool hasHeader(const __FlashStringHelper * data) const; // check if header exists
AsyncWebHeader* getHeader(const String& name) const; AsyncWebHeader* getHeader(const String& name);
AsyncWebHeader* getHeader(const __FlashStringHelper * data) const; const AsyncWebHeader* getHeader(const String& name) const;
AsyncWebHeader* getHeader(size_t num) const; AsyncWebHeader* getHeader(const __FlashStringHelper * data);
const AsyncWebHeader* getHeader(const __FlashStringHelper * data) const;
AsyncWebHeader* getHeader(size_t num);
const AsyncWebHeader* getHeader(size_t num) const;
size_t params() const; // get arguments count size_t params() const; // get arguments count
bool hasParam(const String& name, bool post=false, bool file=false) const; bool hasParam(const String& name, bool post=false, bool file=false) const;
@ -379,7 +381,7 @@ typedef enum {
class AsyncWebServerResponse { class AsyncWebServerResponse {
protected: protected:
int _code; int _code;
LinkedList<AsyncWebHeader *> _headers; std::list<AsyncWebHeader> _headers;
String _contentType; String _contentType;
size_t _contentLength; size_t _contentLength;
bool _sendContentLength; bool _sendContentLength;
@ -462,17 +464,16 @@ class AsyncWebServer {
}; };
class DefaultHeaders { class DefaultHeaders {
using headers_t = LinkedList<AsyncWebHeader *>; using headers_t = std::list<AsyncWebHeader>;
headers_t _headers; headers_t _headers;
DefaultHeaders()
:_headers(headers_t([](AsyncWebHeader *h){ delete h; }))
{}
public: public:
using ConstIterator = headers_t::ConstIterator; DefaultHeaders() = default;
using ConstIterator = headers_t::const_iterator;
void addHeader(const String& name, const String& value){ void addHeader(const String& name, const String& value){
_headers.add(new AsyncWebHeader(name, value)); _headers.emplace_back(name, value);
} }
ConstIterator begin() const { return _headers.begin(); } ConstIterator begin() const { return _headers.begin(); }
@ -480,6 +481,7 @@ public:
DefaultHeaders(DefaultHeaders const &) = delete; DefaultHeaders(DefaultHeaders const &) = delete;
DefaultHeaders &operator=(DefaultHeaders const &) = delete; DefaultHeaders &operator=(DefaultHeaders const &) = delete;
static DefaultHeaders &Instance() { static DefaultHeaders &Instance() {
static DefaultHeaders instance; static DefaultHeaders instance;
return instance; return instance;

View File

@ -1,14 +1,18 @@
#include "SPIFFSEditor.h" #include "SPIFFSEditor.h"
#include <FS.h> #include <FS.h>
#include "edit.htm.gz.h" #define EDFS
#ifndef EDFS
#include "edit.htm.gz.h"
#endif
#ifdef ESP32 #ifdef ESP32
#define fullName(x) name(x) #define fullName(x) name(x)
#endif #endif
#define SPIFFS_MAXLENGTH_FILEPATH 32 #define SPIFFS_MAXLENGTH_FILEPATH 32
const char *excludeListFile = "/.exclude.files"; static const char excludeListFile[] PROGMEM = "/.exclude.files";
typedef struct ExcludeListS { typedef struct ExcludeListS {
char *item; char *item;
@ -103,7 +107,7 @@ static void loadExcludeList(fs::FS &_fs, const char *filename){
static bool isExcluded(fs::FS &_fs, const char *filename) { static bool isExcluded(fs::FS &_fs, const char *filename) {
if(excludes == NULL){ if(excludes == NULL){
loadExcludeList(_fs, excludeListFile); loadExcludeList(_fs, String(FPSTR(excludeListFile)).c_str());
} }
ExcludeList *e = excludes; ExcludeList *e = excludes;
while(e){ while(e){
@ -130,12 +134,12 @@ SPIFFSEditor::SPIFFSEditor(const String& username, const String& password, const
{} {}
bool SPIFFSEditor::canHandle(AsyncWebServerRequest *request){ bool SPIFFSEditor::canHandle(AsyncWebServerRequest *request){
if(request->url().equalsIgnoreCase("/edit")){ if(request->url().equalsIgnoreCase(F("/edit"))){
if(request->method() == HTTP_GET){ if(request->method() == HTTP_GET){
if(request->hasParam("list")) if(request->hasParam(F("list")))
return true; return true;
if(request->hasParam("edit")){ if(request->hasParam(F("edit"))){
request->_tempFile = _fs.open(request->arg("edit"), "r"); request->_tempFile = _fs.open(request->arg(F("edit")), "r");
if(!request->_tempFile){ if(!request->_tempFile){
return false; return false;
} }
@ -147,7 +151,7 @@ bool SPIFFSEditor::canHandle(AsyncWebServerRequest *request){
#endif #endif
} }
if(request->hasParam("download")){ if(request->hasParam("download")){
request->_tempFile = _fs.open(request->arg("download"), "r"); request->_tempFile = _fs.open(request->arg(F("download")), "r");
if(!request->_tempFile){ if(!request->_tempFile){
return false; return false;
} }
@ -158,7 +162,7 @@ bool SPIFFSEditor::canHandle(AsyncWebServerRequest *request){
} }
#endif #endif
} }
request->addInterestingHeader("If-Modified-Since"); request->addInterestingHeader(F("If-Modified-Since"));
return true; return true;
} }
else if(request->method() == HTTP_POST) else if(request->method() == HTTP_POST)
@ -178,8 +182,8 @@ void SPIFFSEditor::handleRequest(AsyncWebServerRequest *request){
return request->requestAuthentication(); return request->requestAuthentication();
if(request->method() == HTTP_GET){ if(request->method() == HTTP_GET){
if(request->hasParam("list")){ if(request->hasParam(F("list"))){
String path = request->getParam("list")->value(); String path = request->getParam(F("list"))->value();
#ifdef ESP32 #ifdef ESP32
File dir = _fs.open(path); File dir = _fs.open(path);
#else #else
@ -204,11 +208,11 @@ void SPIFFSEditor::handleRequest(AsyncWebServerRequest *request){
continue; continue;
} }
if (output != "[") output += ','; if (output != "[") output += ',';
output += "{\"type\":\""; output += F("{\"type\":\"");
output += "file"; output += F("file");
output += "\",\"name\":\""; output += F("\",\"name\":\"");
output += String(fname); output += String(fname);
output += "\",\"size\":"; output += F("\",\"size\":");
output += String(entry.size()); output += String(entry.size());
output += "}"; output += "}";
#ifdef ESP32 #ifdef ESP32
@ -221,41 +225,44 @@ void SPIFFSEditor::handleRequest(AsyncWebServerRequest *request){
dir.close(); dir.close();
#endif #endif
output += "]"; output += "]";
request->send(200, "application/json", output); request->send(200, F("application/json"), output);
output = String(); output = String();
} }
else if(request->hasParam("edit") || request->hasParam("download")){ else if(request->hasParam(F("edit")) || request->hasParam(F("download"))){
request->send(request->_tempFile, request->_tempFile.fullName(), String(), request->hasParam("download")); request->send(request->_tempFile, request->_tempFile.fullName(), String(), request->hasParam(F("download")));
} }
else { else {
const char * buildTime = __DATE__ " " __TIME__ " GMT"; const char * buildTime = __DATE__ " " __TIME__ " GMT";
if (request->header("If-Modified-Since").equals(buildTime)) { if (request->header(F("If-Modified-Since")).equals(buildTime)) {
request->send(304); request->send(304);
} else { } else {
AsyncWebServerResponse *response = request->beginResponse_P(200, "text/html", edit_htm_gz, edit_htm_gz_len); #ifdef EDFS
response->addHeader("Content-Encoding", "gzip"); AsyncWebServerResponse *response = request->beginResponse(_fs, F("/edit_gz"), F("text/html"), false);
response->addHeader("Last-Modified", buildTime); #else
request->send(response); AsyncWebServerResponse *response = request->beginResponse_P(200, F("text/html"), edit_htm_gz, edit_htm_gz_len);
#endif
response->addHeader(F("Content-Encoding"), F("gzip"));
response->addHeader(F("Last-Modified"), buildTime);
request->send(response);
} }
} }
} else if(request->method() == HTTP_DELETE){ } else if(request->method() == HTTP_DELETE){
if(request->hasParam("path", true)){ if(request->hasParam(F("path"), true)){
if(!(_fs.remove(request->getParam(F("path"), true)->value()))){
if(!(_fs.remove(request->getParam("path", true)->value()))){
#ifdef ESP32 #ifdef ESP32
_fs.rmdir(request->getParam("path", true)->value()); // try rmdir for littlefs _fs.rmdir(request->getParam(F("path"), true)->value()); // try rmdir for littlefs
#endif #endif
} }
request->send(200, "", "DELETE: "+request->getParam("path", true)->value()); request->send(200, "", String(F("DELETE: "))+request->getParam(F("path"), true)->value());
} else } else
request->send(404); request->send(404);
} else if(request->method() == HTTP_POST){ } else if(request->method() == HTTP_POST){
if(request->hasParam("data", true, true) && _fs.exists(request->getParam("data", true, true)->value())) if(request->hasParam(F("data"), true, true) && _fs.exists(request->getParam(F("data"), true, true)->value()))
request->send(200, "", "UPLOADED: "+request->getParam("data", true, true)->value()); request->send(200, "", String(F("UPLOADED: "))+request->getParam(F("data"), true, true)->value());
else if(request->hasParam("rawname", true) && request->hasParam("raw0", true)){ else if(request->hasParam(F("rawname"), true) && request->hasParam(F("raw0"), true)){
String rawnam = request->getParam("rawname", true)->value(); String rawnam = request->getParam(F("rawname"), true)->value();
if (_fs.exists(rawnam)) _fs.remove(rawnam); // delete it to allow a mode if (_fs.exists(rawnam)) _fs.remove(rawnam); // delete it to allow a mode
@ -263,22 +270,22 @@ void SPIFFSEditor::handleRequest(AsyncWebServerRequest *request){
uint16_t i = 0; uint16_t i = 0;
fs::File f = _fs.open(rawnam, "a"); fs::File f = _fs.open(rawnam, "a");
while (request->hasParam("raw" + String(k), true)) { //raw0 .. raw1 while (request->hasParam(String(F("raw")) + String(k), true)) { //raw0 .. raw1
if(f){ if(f){
i += f.print(request->getParam("raw" + String(k), true)->value()); i += f.print(request->getParam(String(F("raw")) + String(k), true)->value());
} }
k++; k++;
} }
f.close(); f.close();
request->send(200, "", "IPADWRITE: " + rawnam + ":" + String(i)); request->send(200, "", String(F("IPADWRITE: ")) + rawnam + ":" + String(i));
} else { } else {
request->send(500); request->send(500);
} }
} else if(request->method() == HTTP_PUT){ } else if(request->method() == HTTP_PUT){
if(request->hasParam("path", true)){ if(request->hasParam(F("path"), true)){
String filename = request->getParam("path", true)->value(); String filename = request->getParam(F("path"), true)->value();
if(_fs.exists(filename)){ if(_fs.exists(filename)){
request->send(200); request->send(200);
} else { } else {
@ -306,7 +313,7 @@ void SPIFFSEditor::handleRequest(AsyncWebServerRequest *request){
if(f){ if(f){
f.write((uint8_t)0x00); f.write((uint8_t)0x00);
f.close(); f.close();
request->send(200, "", "CREATE: "+filename); request->send(200, "", String(F("CREATE: "))+filename);
} else { } else {
request->send(500); request->send(500);
} }

View File

@ -171,23 +171,4 @@ class LinkedList {
} }
}; };
class StringArray : public LinkedList<String> {
public:
StringArray() : LinkedList(nullptr) {}
bool containsIgnoreCase(const String& str){
for (const auto& s : *this) {
if (str.equalsIgnoreCase(s)) {
return true;
}
}
return false;
}
};
#endif /* STRINGARRAY_H_ */ #endif /* STRINGARRAY_H_ */

View File

@ -77,9 +77,9 @@ static bool getMD5(uint8_t * data, uint16_t len, char * output){//33 bytes or mo
memset(_buf, 0x00, 16); memset(_buf, 0x00, 16);
#ifdef ESP32 #ifdef ESP32
mbedtls_md5_init(&_ctx); mbedtls_md5_init(&_ctx);
mbedtls_md5_starts(&_ctx); mbedtls_md5_starts_ret(&_ctx);
mbedtls_md5_update(&_ctx, data, len); mbedtls_md5_update_ret(&_ctx, data, len);
mbedtls_md5_finish(&_ctx, _buf); mbedtls_md5_finish_ret(&_ctx, _buf);
#else #else
MD5Init(&_ctx); MD5Init(&_ctx);
MD5Update(&_ctx, data, len); MD5Update(&_ctx, data, len);

View File

@ -105,6 +105,13 @@ class AsyncCallbackWebHandler: public AsyncWebHandler {
} }
} else } else
#endif #endif
if (_uri.length() && _uri.startsWith("/*.")) {
String uriTemplate = String (_uri);
uriTemplate = uriTemplate.substring(uriTemplate.lastIndexOf("."));
if (!request->url().endsWith(uriTemplate))
return false;
}
else
if (_uri.length() && _uri.endsWith("*")) { if (_uri.length() && _uri.endsWith("*")) {
String uriTemplate = String(_uri); String uriTemplate = String(_uri);
uriTemplate = uriTemplate.substring(0, uriTemplate.length() - 1); uriTemplate = uriTemplate.substring(0, uriTemplate.length() - 1);
@ -119,16 +126,22 @@ class AsyncCallbackWebHandler: public AsyncWebHandler {
} }
virtual void handleRequest(AsyncWebServerRequest *request) override final { virtual void handleRequest(AsyncWebServerRequest *request) override final {
if((_username != "" && _password != "") && !request->authenticate(_username.c_str(), _password.c_str()))
return request->requestAuthentication();
if(_onRequest) if(_onRequest)
_onRequest(request); _onRequest(request);
else else
request->send(500); request->send(500);
} }
virtual void handleUpload(AsyncWebServerRequest *request, const String& filename, size_t index, uint8_t *data, size_t len, bool final) override final { virtual void handleUpload(AsyncWebServerRequest *request, const String& filename, size_t index, uint8_t *data, size_t len, bool final) override final {
if((_username != "" && _password != "") && !request->authenticate(_username.c_str(), _password.c_str()))
return request->requestAuthentication();
if(_onUpload) if(_onUpload)
_onUpload(request, filename, index, data, len, final); _onUpload(request, filename, index, data, len, final);
} }
virtual void handleBody(AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total) override final { virtual void handleBody(AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total) override final {
if((_username != "" && _password != "") && !request->authenticate(_username.c_str(), _password.c_str()))
return request->requestAuthentication();
if(_onBody) if(_onBody)
_onBody(request, data, len, index, total); _onBody(request, data, len, index, total);
} }

View File

@ -51,9 +51,7 @@ AsyncWebServerRequest::AsyncWebServerRequest(AsyncWebServer* s, AsyncClient* c)
, _expectingContinue(false) , _expectingContinue(false)
, _contentLength(0) , _contentLength(0)
, _parsedLength(0) , _parsedLength(0)
, _headers(LinkedList<AsyncWebHeader *>([](AsyncWebHeader *h){ delete h; }))
, _params(LinkedList<AsyncWebParameter *>([](AsyncWebParameter *p){ delete p; })) , _params(LinkedList<AsyncWebParameter *>([](AsyncWebParameter *p){ delete p; }))
, _pathParams(LinkedList<String *>([](String *p){ delete p; }))
, _multiParseState(0) , _multiParseState(0)
, _boundaryPosition(0) , _boundaryPosition(0)
, _itemStartIndex(0) , _itemStartIndex(0)
@ -76,12 +74,12 @@ AsyncWebServerRequest::AsyncWebServerRequest(AsyncWebServer* s, AsyncClient* c)
} }
AsyncWebServerRequest::~AsyncWebServerRequest(){ AsyncWebServerRequest::~AsyncWebServerRequest(){
_headers.free(); _headers.clear();
_params.free(); _params.free();
_pathParams.free(); _pathParams.clear();
_interestingHeaders.free(); _interestingHeaders.clear();
if(_response != NULL){ if(_response != NULL){
delete _response; delete _response;
@ -94,11 +92,11 @@ AsyncWebServerRequest::~AsyncWebServerRequest(){
if(_tempFile){ if(_tempFile){
_tempFile.close(); _tempFile.close();
} }
if(_itemBuffer){ if(_itemBuffer){
free(_itemBuffer); free(_itemBuffer);
} }
} }
void AsyncWebServerRequest::_onData(void *buf, size_t len){ void AsyncWebServerRequest::_onData(void *buf, size_t len){
@ -182,18 +180,34 @@ void AsyncWebServerRequest::_onData(void *buf, size_t len){
} }
void AsyncWebServerRequest::_removeNotInterestingHeaders(){ void AsyncWebServerRequest::_removeNotInterestingHeaders(){
if (_interestingHeaders.containsIgnoreCase(F("ANY"))) return; // nothing to do if (std::any_of(std::begin(_interestingHeaders), std::end(_interestingHeaders),
for(const auto& header: _headers){ [](const String &str){ return str.equalsIgnoreCase(F("ANY")); }))
if(!_interestingHeaders.containsIgnoreCase(header->name().c_str())){ return; // nothing to do
_headers.remove(header);
} for(auto iter = std::begin(_headers); iter != std::end(_headers); )
{
const auto name = iter->name();
if (std::none_of(std::begin(_interestingHeaders), std::end(_interestingHeaders),
[&name](const String &str){ return str.equalsIgnoreCase(name); }))
iter = _headers.erase(iter);
else
iter++;
} }
} }
void AsyncWebServerRequest::_onPoll(){ void AsyncWebServerRequest::_onPoll(){
//os_printf("p\n"); //os_printf("p\n");
if(_response != NULL && _client != NULL && _client->canSend() && !_response->_finished()){ if(_response != NULL && _client != NULL && _client->canSend()){
_response->_ack(this, 0, 0); if(!_response->_finished()){
_response->_ack(this, 0, 0);
} else {
AsyncWebServerResponse* r = _response;
_response = NULL;
delete r;
_client->close();
}
} }
} }
@ -202,10 +216,12 @@ void AsyncWebServerRequest::_onAck(size_t len, uint32_t time){
if(_response != NULL){ if(_response != NULL){
if(!_response->_finished()){ if(!_response->_finished()){
_response->_ack(this, len, time); _response->_ack(this, len, time);
} else { } else if(_response->_finished()){
AsyncWebServerResponse* r = _response; AsyncWebServerResponse* r = _response;
_response = NULL; _response = NULL;
delete r; delete r;
_client->close();
} }
} }
} }
@ -237,7 +253,7 @@ void AsyncWebServerRequest::_addParam(AsyncWebParameter *p){
} }
void AsyncWebServerRequest::_addPathParam(const char *p){ void AsyncWebServerRequest::_addPathParam(const char *p){
_pathParams.add(new String(p)); _pathParams.emplace_back(p);
} }
void AsyncWebServerRequest::_addGetParams(const String& params){ void AsyncWebServerRequest::_addGetParams(const String& params){
@ -276,24 +292,6 @@ bool AsyncWebServerRequest::_parseReqHead(){
_method = HTTP_HEAD; _method = HTTP_HEAD;
} else if(m == F("OPTIONS")){ } else if(m == F("OPTIONS")){
_method = HTTP_OPTIONS; _method = HTTP_OPTIONS;
} else if(m == F("PROPFIND")){
_method = HTTP_PROPFIND;
} else if(m == F("LOCK")){
_method = HTTP_LOCK;
} else if(m == F("UNLOCK")){
_method = HTTP_UNLOCK;
} else if(m == F("PROPPATCH")){
_method = HTTP_PROPPATCH;
} else if(m == F("MKCOL")){
_method = HTTP_MKCOL;
} else if(m == F("MOVE")){
_method = HTTP_MOVE;
} else if(m == F("COPY")){
_method = HTTP_COPY;
} else if(m == F("RESERVED")){
_method = HTTP_RESERVED;
} else if(m == F("ANY")){
_method = HTTP_ANY;
} }
String g; String g;
@ -369,7 +367,7 @@ bool AsyncWebServerRequest::_parseReqHeader(){
} }
} }
} }
_headers.add(new AsyncWebHeader(name, value)); _headers.emplace_back(name, value);
} }
_temp = String(); _temp = String();
return true; return true;
@ -610,12 +608,12 @@ void AsyncWebServerRequest::_parseLine(){
} }
size_t AsyncWebServerRequest::headers() const{ size_t AsyncWebServerRequest::headers() const{
return _headers.length(); return _headers.size();
} }
bool AsyncWebServerRequest::hasHeader(const String& name) const { bool AsyncWebServerRequest::hasHeader(const String& name) const {
for(const auto& h: _headers){ for(const auto& h: _headers){
if(h->name().equalsIgnoreCase(name)){ if(h.name().equalsIgnoreCase(name)){
return true; return true;
} }
} }
@ -626,22 +624,64 @@ bool AsyncWebServerRequest::hasHeader(const __FlashStringHelper * data) const {
return hasHeader(String(data)); return hasHeader(String(data));
} }
AsyncWebHeader* AsyncWebServerRequest::getHeader(const String& name) const { AsyncWebHeader* AsyncWebServerRequest::getHeader(const String& name) {
for(const auto& h: _headers){ auto iter = std::find_if(std::begin(_headers), std::end(_headers),
if(h->name().equalsIgnoreCase(name)){ [&name](const AsyncWebHeader &header){ return header.name().equalsIgnoreCase(name); });
return h;
} if (iter == std::end(_headers))
return nullptr;
return &(*iter);
}
const AsyncWebHeader* AsyncWebServerRequest::getHeader(const String& name) const {
auto iter = std::find_if(std::begin(_headers), std::end(_headers),
[&name](const AsyncWebHeader &header){ return header.name().equalsIgnoreCase(name); });
if (iter == std::end(_headers))
return nullptr;
return &(*iter);
}
AsyncWebHeader* AsyncWebServerRequest::getHeader(const __FlashStringHelper * data) {
PGM_P p = reinterpret_cast<PGM_P>(data);
size_t n = strlen_P(p);
char * name = (char*) malloc(n+1);
if (name) {
strcpy_P(name, p);
AsyncWebHeader* result = getHeader( String(name));
free(name);
return result;
} else {
return nullptr;
} }
return nullptr;
} }
AsyncWebHeader* AsyncWebServerRequest::getHeader(const __FlashStringHelper * data) const { const AsyncWebHeader* AsyncWebServerRequest::getHeader(const __FlashStringHelper * data) const {
return getHeader(String(data)); PGM_P p = reinterpret_cast<PGM_P>(data);
size_t n = strlen_P(p);
char * name = (char*) malloc(n+1);
if (name) {
strcpy_P(name, p);
const AsyncWebHeader* result = getHeader( String(name));
free(name);
return result;
} else {
return nullptr;
}
} }
AsyncWebHeader* AsyncWebServerRequest::getHeader(size_t num) const { AsyncWebHeader* AsyncWebServerRequest::getHeader(size_t num) {
auto header = _headers.nth(num); if (num >= _headers.size())
return header ? *header : nullptr; return nullptr;
return &(*std::next(std::begin(_headers), num));
}
const AsyncWebHeader* AsyncWebServerRequest::getHeader(size_t num) const {
if (num >= _headers.size())
return nullptr;
return &(*std::next(std::begin(_headers), num));
} }
size_t AsyncWebServerRequest::params() const { size_t AsyncWebServerRequest::params() const {
@ -680,8 +720,9 @@ AsyncWebParameter* AsyncWebServerRequest::getParam(size_t num) const {
} }
void AsyncWebServerRequest::addInterestingHeader(const String& name){ void AsyncWebServerRequest::addInterestingHeader(const String& name){
if(!_interestingHeaders.containsIgnoreCase(name)) if(std::none_of(std::begin(_interestingHeaders), std::end(_interestingHeaders),
_interestingHeaders.add(name); [&name](const String &str){ return str.equalsIgnoreCase(name); }))
_interestingHeaders.push_back(name);
} }
void AsyncWebServerRequest::send(AsyncWebServerResponse *response){ void AsyncWebServerRequest::send(AsyncWebServerResponse *response){
@ -873,12 +914,11 @@ const String& AsyncWebServerRequest::argName(size_t i) const {
} }
const String& AsyncWebServerRequest::pathArg(size_t i) const { const String& AsyncWebServerRequest::pathArg(size_t i) const {
auto param = _pathParams.nth(i); return i < _pathParams.size() ? _pathParams[i] : emptyString;
return param ? **param : emptyString;
} }
const String& AsyncWebServerRequest::header(const char* name) const { const String& AsyncWebServerRequest::header(const char* name) const {
AsyncWebHeader* h = getHeader(String(name)); const AsyncWebHeader* h = getHeader(String(name));
return h ? h->value() : emptyString; return h ? h->value() : emptyString;
} }
@ -888,12 +928,12 @@ const String& AsyncWebServerRequest::header(const __FlashStringHelper * data) co
const String& AsyncWebServerRequest::header(size_t i) const { const String& AsyncWebServerRequest::header(size_t i) const {
AsyncWebHeader* h = getHeader(i); const AsyncWebHeader* h = getHeader(i);
return h ? h->value() : emptyString; return h ? h->value() : emptyString;
} }
const String& AsyncWebServerRequest::headerName(size_t i) const { const String& AsyncWebServerRequest::headerName(size_t i) const {
AsyncWebHeader* h = getHeader(i); const AsyncWebHeader* h = getHeader(i);
return h ? h->name() : emptyString; return h ? h->name() : emptyString;
} }
@ -930,14 +970,6 @@ const __FlashStringHelper *AsyncWebServerRequest::methodToString() const {
else if(_method & HTTP_PATCH) return F("PATCH"); else if(_method & HTTP_PATCH) return F("PATCH");
else if(_method & HTTP_HEAD) return F("HEAD"); else if(_method & HTTP_HEAD) return F("HEAD");
else if(_method & HTTP_OPTIONS) return F("OPTIONS"); else if(_method & HTTP_OPTIONS) return F("OPTIONS");
else if(_method & HTTP_PROPFIND) return F("PROPFIND");
else if(_method & HTTP_LOCK) return F("LOCK");
else if(_method & HTTP_UNLOCK) return F("UNLOCK");
else if(_method & HTTP_PROPPATCH) return F("PROPPATCH");
else if(_method & HTTP_MKCOL) return F("MKCOL");
else if(_method & HTTP_MOVE) return F("MOVE");
else if(_method & HTTP_COPY) return F("COPY");
else if(_method & HTTP_RESERVED) return F("RESERVED");
return F("UNKNOWN"); return F("UNKNOWN");
} }

View File

@ -27,6 +27,8 @@
#undef max #undef max
#endif #endif
#include <vector> #include <vector>
#include <memory>
// It is possible to restore these defines, but one can use _min and _max instead. Or std::min, std::max. // It is possible to restore these defines, but one can use _min and _max instead. Or std::min, std::max.
class AsyncBasicResponse: public AsyncWebServerResponse { class AsyncBasicResponse: public AsyncWebServerResponse {
@ -122,7 +124,7 @@ class cbuf;
class AsyncResponseStream: public AsyncAbstractResponse, public Print { class AsyncResponseStream: public AsyncAbstractResponse, public Print {
private: private:
cbuf *_content; std::unique_ptr<cbuf> _content;
public: public:
AsyncResponseStream(const String& contentType, size_t bufferSize); AsyncResponseStream(const String& contentType, size_t bufferSize);
~AsyncResponseStream(); ~AsyncResponseStream();

View File

@ -88,7 +88,6 @@ const __FlashStringHelper *AsyncWebServerResponse::responseCodeToString(int code
AsyncWebServerResponse::AsyncWebServerResponse() AsyncWebServerResponse::AsyncWebServerResponse()
: _code(0) : _code(0)
, _headers(LinkedList<AsyncWebHeader *>([](AsyncWebHeader *h){ delete h; }))
, _contentType() , _contentType()
, _contentLength(0) , _contentLength(0)
, _sendContentLength(true) , _sendContentLength(true)
@ -99,14 +98,12 @@ AsyncWebServerResponse::AsyncWebServerResponse()
, _writtenLength(0) , _writtenLength(0)
, _state(RESPONSE_SETUP) , _state(RESPONSE_SETUP)
{ {
for(auto header: DefaultHeaders::Instance()) { for(const auto &header: DefaultHeaders::Instance()) {
_headers.add(new AsyncWebHeader(header->name(), header->value())); _headers.emplace_back(header);
} }
} }
AsyncWebServerResponse::~AsyncWebServerResponse(){ AsyncWebServerResponse::~AsyncWebServerResponse() = default;
_headers.free();
}
void AsyncWebServerResponse::setCode(int code){ void AsyncWebServerResponse::setCode(int code){
if(_state == RESPONSE_SETUP) if(_state == RESPONSE_SETUP)
@ -124,7 +121,7 @@ void AsyncWebServerResponse::setContentType(const String& type){
} }
void AsyncWebServerResponse::addHeader(const String& name, const String& value){ void AsyncWebServerResponse::addHeader(const String& name, const String& value){
_headers.add(new AsyncWebHeader(name, value)); _headers.emplace_back(name, value);
} }
String AsyncWebServerResponse::_assembleHead(uint8_t version){ String AsyncWebServerResponse::_assembleHead(uint8_t version){
@ -150,10 +147,10 @@ String AsyncWebServerResponse::_assembleHead(uint8_t version){
} }
for(const auto& header: _headers){ for(const auto& header: _headers){
snprintf_P(buf, bufSize, PSTR("%s: %s\r\n"), header->name().c_str(), header->value().c_str()); snprintf_P(buf, bufSize, PSTR("%s: %s\r\n"), header.name().c_str(), header.value().c_str());
out.concat(buf); out.concat(buf);
} }
_headers.free(); _headers.clear();
out.concat(F("\r\n")); out.concat(F("\r\n"));
_headLength = out.length(); _headLength = out.length();
@ -675,16 +672,15 @@ size_t AsyncProgmemResponse::_fillBuffer(uint8_t *data, size_t len){
* Response Stream (You can print/write/printf to it, up to the contentLen bytes) * Response Stream (You can print/write/printf to it, up to the contentLen bytes)
* */ * */
AsyncResponseStream::AsyncResponseStream(const String& contentType, size_t bufferSize){ AsyncResponseStream::AsyncResponseStream(const String& contentType, size_t bufferSize)
{
_code = 200; _code = 200;
_contentLength = 0; _contentLength = 0;
_contentType = contentType; _contentType = contentType;
_content = new cbuf(bufferSize); _content = std::unique_ptr<cbuf>(new cbuf(bufferSize)); //std::make_unique<cbuf>(bufferSize);
} }
AsyncResponseStream::~AsyncResponseStream(){ AsyncResponseStream::~AsyncResponseStream() = default;
delete _content;
}
size_t AsyncResponseStream::_fillBuffer(uint8_t *buf, size_t maxLen){ size_t AsyncResponseStream::_fillBuffer(uint8_t *buf, size_t maxLen){
return _content->read((char*)buf, maxLen); return _content->read((char*)buf, maxLen);

File diff suppressed because it is too large Load Diff

View File

@ -2,7 +2,7 @@
//File: edit.htm.gz, Size: 4503 //File: edit.htm.gz, Size: 4503
#define edit_htm_gz_len 4503 #define edit_htm_gz_len 4503
const uint8_t edit_htm_gz[] PROGMEM = { const uint8_t edit_htm_gz[] PROGMEM = {
0x1F,0x8B,0x08,0x08,0x9B,0xC8,0x22,0x5F,0x02,0x00,0x65,0x64,0x69,0x74,0x2E,0x68,0x74,0x6D,0x00,0xB5, 0x1F,0x8B,0x08,0x08,0x5A,0xE6,0xAE,0x5F,0x02,0x00,0x65,0x64,0x69,0x74,0x2E,0x68,0x74,0x6D,0x00,0xB5,
0x1A,0x0B,0x5B,0xDB,0x36,0xF0,0xAF,0x18,0x6F,0x63,0xF6,0xE2,0x38,0x0E,0x50,0xD6,0x3A,0x18,0x16,0x1E, 0x1A,0x0B,0x5B,0xDB,0x36,0xF0,0xAF,0x18,0x6F,0x63,0xF6,0xE2,0x38,0x0E,0x50,0xD6,0x3A,0x18,0x16,0x1E,
0xEB,0xBB,0x50,0x12,0xDA,0xD1,0x8E,0xED,0x53,0x6C,0x25,0x56,0xB1,0x25,0xCF,0x96,0x09,0x34,0xCD,0x7F, 0xEB,0xBB,0x50,0x12,0xDA,0xD1,0x8E,0xED,0x53,0x6C,0x25,0x56,0xB1,0x25,0xCF,0x96,0x09,0x34,0xCD,0x7F,
0xDF,0x49,0xF2,0x93,0x84,0xEE,0xF1,0x6D,0xA5,0x60,0x49,0xA7,0x3B,0xDD,0x9D,0xEE,0x25,0xD9,0x7B,0x1B, 0xDF,0x49,0xF2,0x93,0x84,0xEE,0xF1,0x6D,0xA5,0x60,0x49,0xA7,0x3B,0xDD,0x9D,0xEE,0x25,0xD9,0x7B,0x1B,