diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..64292ec --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,17 @@ +set(COMPONENT_SRCDIRS + "src" +) + +set(COMPONENT_ADD_INCLUDEDIRS + "src" +) + +set(COMPONENT_REQUIRES + "arduino-esp32" + "AsyncTCP" +) + +register_component() + +target_compile_definitions(${COMPONENT_TARGET} PUBLIC -DESP32) +target_compile_options(${COMPONENT_TARGET} PRIVATE -fno-rtti) diff --git a/README.md b/README.md index 9417b44..b14d36c 100644 --- a/README.md +++ b/README.md @@ -4,34 +4,46 @@ [![Continuous Integration](https://github.com/mathieucarbou/ESPAsyncWebServer/actions/workflows/ci.yml/badge.svg)](https://github.com/mathieucarbou/ESPAsyncWebServer/actions/workflows/ci.yml) [![PlatformIO Registry](https://badges.registry.platformio.org/packages/mathieucarbou/library/ESP%20Async%20WebServer.svg)](https://registry.platformio.org/libraries/mathieucarbou/ESP%20Async%20WebServer) -Asynchronous HTTP and WebSocket Server Library for ESP32. +Asynchronous HTTP and WebSocket Server Library for ESP32, ESP8266 and RP2040 Supports: WebSocket, SSE, Authentication, Arduino Json 7, File Upload, Static File serving, URL Rewrite, URL Redirect, etc. This fork is based on [yubox-node-org/ESPAsyncWebServer](https://github.com/yubox-node-org/ESPAsyncWebServer) and includes all the concurrency fixes. ## Changes in this fork -- Removed SPIFFSEditor -- Deployed in PlatformIO registry and Arduino IDE library manager -- CI -- Some code cleanup -- `SSE_MAX_QUEUED_MESSAGES` to control the maximum number of messages that can be queued for a SSE client -- `write()` function public in `AsyncEventSource.h` -- Arduino Json 7 compatibility and backward compatible with 6 and 6 (changes in `AsyncJson.h`). The API to use Json has not changed. These are only internal changes. -- `WS_MAX_QUEUED_MESSAGES`: control the maximum number of messages that can be queued for a Websocket client -- Resurrected `AsyncWebSocketMessageBuffer` and `makeBuffer()` in order to make the fork API-compatible with the original library from me-no-dev regarding WebSocket. -- [#5](https://github.com/mathieucarbou/ESPAsyncWebServer/pull/5) ([@vortigont](https://github.com/vortigont)): set real "Last-Modified" header based on file's LastWrite time -- [#13](https://github.com/mathieucarbou/ESPAsyncWebServer/pull/13) ([@tueddy](https://github.com/tueddy)): Compile with Arduino 3 (ESP-IDF 5.1) -- [#14](https://github.com/mathieucarbou/ESPAsyncWebServer/pull/14) ([@nilo85](https://github.com/nilo85)): Add support for Auth & GET requests in AsyncCallbackJsonWebHandler -- Added `setAuthentication(const String& username, const String& password)` -- Added `StreamConcat` example to show how to stream multiple files in one response -- Remove filename after inline in Content-Disposition header according to RFC2183 -- Depends on `mathieucarbou/Async TCP @ ^3.1.4` -- Arduino 3 / ESP-IDF 5.1 compatibility -- Added all flavors of `binary()`, `text()`, `binaryAll()` and `textAll()` in `AsyncWebSocket` -- Added `setCloseClientOnQueueFull(bool)` which can be set on a client to either close the connection or discard messages but not close the connection when the queue is full -- [#29](https://github.com/mathieucarbou/ESPAsyncWebServer/pull/29) ([@vortigont](https://github.com/vortigont)): Some websocket code cleanup -- Use `-D DEFAULT_MAX_WS_CLIENTS` to change the number of allows WebSocket clients and use `cleanupClients()` to help cleanup resources about dead clients +- [@ayushsharma82](https://github.com/ayushsharma82) and [@mathieucarbou](https://github.com/mathieucarbou): Add RP2040 support ([#31](https://github.com/mathieucarbou/ESPAsyncWebServer/pull/31)) +- [@mathieucarbou](https://github.com/mathieucarbou): `SSE_MAX_QUEUED_MESSAGES` to control the maximum number of messages that can be queued for a SSE client +- [@mathieucarbou](https://github.com/mathieucarbou): `write()` function public in `AsyncEventSource.h` +- [@mathieucarbou](https://github.com/mathieucarbou): `WS_MAX_QUEUED_MESSAGES`: control the maximum number of messages that can be queued for a Websocket client +- [@mathieucarbou](https://github.com/mathieucarbou): Added `setAuthentication(const String& username, const String& password)` +- [@mathieucarbou](https://github.com/mathieucarbou): Added `setCloseClientOnQueueFull(bool)` which can be set on a client to either close the connection or discard messages but not close the connection when the queue is full +- [@mathieucarbou](https://github.com/mathieucarbou): Added `StreamConcat` example to show how to stream multiple files in one response +- [@mathieucarbou](https://github.com/mathieucarbou): Added all flavors of `binary()`, `text()`, `binaryAll()` and `textAll()` in `AsyncWebSocket` +- [@mathieucarbou](https://github.com/mathieucarbou): Arduino 3 / ESP-IDF 5.1 compatibility +- [@mathieucarbou](https://github.com/mathieucarbou): Arduino Json 7 compatibility and backward compatible with 6 and 6 (changes in `AsyncJson.h`). The API to use Json has not changed. These are only internal changes. +- [@mathieucarbou](https://github.com/mathieucarbou): CI +- [@mathieucarbou](https://github.com/mathieucarbou): Depends on `mathieucarbou/Async TCP @ ^3.1.4` +- [@mathieucarbou](https://github.com/mathieucarbou): Deployed in PlatformIO registry and Arduino IDE library manager +- [@mathieucarbou](https://github.com/mathieucarbou): Firmware size optimization: remove mbedtls dependency (accounts for 33KB in firmware) +- [@mathieucarbou](https://github.com/mathieucarbou): Made DEFAULT_MAX_SSE_CLIENTS customizable +- [@mathieucarbou](https://github.com/mathieucarbou): Made DEFAULT_MAX_WS_CLIENTS customizable +- [@mathieucarbou](https://github.com/mathieucarbou): Remove filename after inline in Content-Disposition header according to RFC2183 +- [@mathieucarbou](https://github.com/mathieucarbou): Removed SPIFFSEditor to reduce library size and maintainance. SPIFF si also deprecated. If you need it, please copy the files from the original repository in your project. This fork focus on maintaining the server part and the SPIFFEditor is an application which has nothing to do inside a server library. +- [@mathieucarbou](https://github.com/mathieucarbou): Resurrected `AsyncWebSocketMessageBuffer` and `makeBuffer()` in order to make the fork API-compatible with the original library from me-no-dev regarding WebSocket. +- [@mathieucarbou](https://github.com/mathieucarbou): Some code cleanup +- [@mathieucarbou](https://github.com/mathieucarbou): Use `-D DEFAULT_MAX_WS_CLIENTS` to change the number of allows WebSocket clients and use `cleanupClients()` to help cleanup resources about dead clients +- [@nilo85](https://github.com/nilo85): Add support for Auth & GET requests in AsyncCallbackJsonWebHandler ([#14](https://github.com/mathieucarbou/ESPAsyncWebServer/pull/14)) +- [@p0p-x](https://github.com/p0p-x): ESP IDF Compatibility (added back CMakeLists.txt) ([#32](https://github.com/mathieucarbou/ESPAsyncWebServer/pull/32)) +- [@tueddy](https://github.com/tueddy): Compile with Arduino 3 (ESP-IDF 5.1) ([#13](https://github.com/mathieucarbou/ESPAsyncWebServer/pull/13)) +- [@vortigont](https://github.com/vortigont): Set real "Last-Modified" header based on file's LastWrite time ([#5](https://github.com/mathieucarbou/ESPAsyncWebServer/pull/5)) +- [@vortigont](https://github.com/vortigont): Some websocket code cleanup ([#29](https://github.com/mathieucarbou/ESPAsyncWebServer/pull/29)) +- [@vortigont](https://github.com/vortigont): Refactor code - replace DYI structs with STL objects ([#39](https://github.com/mathieucarbou/ESPAsyncWebServer/pull/39)) + +## Dependencies: + +- **ESP32**: `mathieucarbou/Async TCP @ ^3.1.4` (Arduino IDE: [https://github.com/mathieucarbou/AsyncTCP#v3.1.4](https://github.com/mathieucarbou/AsyncTCP/releases/tag/v3.1.4)) +- **ESP8266**: `esphome/ESPAsyncTCP-esphome @ 2.0.0` (Arduino IDE: [https://github.com/mathieucarbou/esphome-ESPAsyncTCP#v2.0.0](https://github.com/mathieucarbou/esphome-ESPAsyncTCP/releases/tag/v2.0.0)) +- **RP2040**: `khoih-prog/AsyncTCP_RP2040W @ 1.2.0` (Arduino IDE: [https://github.com/khoih-prog/AsyncTCP_RP2040W#v1.2.0](https://github.com/khoih-prog/AsyncTCP_RP2040W/releases/tag/v1.2.0)) ## Documentation @@ -76,17 +88,30 @@ void send(JsonDocument& doc) { I recommend to use the official API `AsyncWebSocketMessageBuffer` to retain further compatibility. -## Stack size and queues +## Important recommendations -Here are some important flags to tweak depending on your needs: +Most of the crashes are caused by improper configuration of the library for the project. +Here are some recommendations to avoid them. -```cpp - // Async TCP queue size +1. Set the running core to be on the same core of your application (usually core 1) `-D CONFIG_ASYNC_TCP_RUNNING_CORE=1` +2. Set the stack size appropriately with `-D CONFIG_ASYNC_TCP_STACK_SIZE=16384`. + The default value of `16384` might be too much for your project. + You can look at the [MycilaTaskMonitor](https://oss.carbou.me/MycilaTaskMonitor) project to monitor the stack usage. +3. You can change **if you know what you are doing** the task priority with `-D CONFIG_ASYNC_TCP_PRIORITY=10`. + Default is `10`. +4. You can increase the queue size with `-D CONFIG_ASYNC_TCP_QUEUE_SIZE=128`. + Default is `64`. +5. You can decrease the maximum ack time `-D CONFIG_ASYNC_TCP_MAX_ACK_TIME=3000`. + Default is `5000`. + +I personally use the following configuration in my projects because my WS messages can be big (up to 4k). +If you have smaller messages, you can increase `WS_MAX_QUEUED_MESSAGES` to 128. + +```c++ + -D CONFIG_ASYNC_TCP_MAX_ACK_TIME=3000 + -D CONFIG_ASYNC_TCP_PRIORITY=10 -D CONFIG_ASYNC_TCP_QUEUE_SIZE=128 - // Async TCP async task core -D CONFIG_ASYNC_TCP_RUNNING_CORE=1 - // Async TCP async stac ksize - -D CONFIG_ASYNC_TCP_STACK_SIZE=8096 - // WebSocket queue size + -D CONFIG_ASYNC_TCP_STACK_SIZE=4096 -D WS_MAX_QUEUED_MESSAGES=64 ``` diff --git a/docs/_config.yml b/docs/_config.yml index 532070b..e6d54d2 100644 --- a/docs/_config.yml +++ b/docs/_config.yml @@ -1,7 +1,7 @@ # bundle exec jekyll serve --host=0.0.0.0 title: ESP Async WebServer -description: "Asynchronous HTTP and WebSocket Server Library for ESP32" +description: "Asynchronous HTTP and WebSocket Server Library for ESP32, ESP8266 and RP2040" remote_theme: pages-themes/cayman@v0.2.0 plugins: - jekyll-remote-theme diff --git a/docs/index.md b/docs/index.md index 9417b44..b14d36c 100644 --- a/docs/index.md +++ b/docs/index.md @@ -4,34 +4,46 @@ [![Continuous Integration](https://github.com/mathieucarbou/ESPAsyncWebServer/actions/workflows/ci.yml/badge.svg)](https://github.com/mathieucarbou/ESPAsyncWebServer/actions/workflows/ci.yml) [![PlatformIO Registry](https://badges.registry.platformio.org/packages/mathieucarbou/library/ESP%20Async%20WebServer.svg)](https://registry.platformio.org/libraries/mathieucarbou/ESP%20Async%20WebServer) -Asynchronous HTTP and WebSocket Server Library for ESP32. +Asynchronous HTTP and WebSocket Server Library for ESP32, ESP8266 and RP2040 Supports: WebSocket, SSE, Authentication, Arduino Json 7, File Upload, Static File serving, URL Rewrite, URL Redirect, etc. This fork is based on [yubox-node-org/ESPAsyncWebServer](https://github.com/yubox-node-org/ESPAsyncWebServer) and includes all the concurrency fixes. ## Changes in this fork -- Removed SPIFFSEditor -- Deployed in PlatformIO registry and Arduino IDE library manager -- CI -- Some code cleanup -- `SSE_MAX_QUEUED_MESSAGES` to control the maximum number of messages that can be queued for a SSE client -- `write()` function public in `AsyncEventSource.h` -- Arduino Json 7 compatibility and backward compatible with 6 and 6 (changes in `AsyncJson.h`). The API to use Json has not changed. These are only internal changes. -- `WS_MAX_QUEUED_MESSAGES`: control the maximum number of messages that can be queued for a Websocket client -- Resurrected `AsyncWebSocketMessageBuffer` and `makeBuffer()` in order to make the fork API-compatible with the original library from me-no-dev regarding WebSocket. -- [#5](https://github.com/mathieucarbou/ESPAsyncWebServer/pull/5) ([@vortigont](https://github.com/vortigont)): set real "Last-Modified" header based on file's LastWrite time -- [#13](https://github.com/mathieucarbou/ESPAsyncWebServer/pull/13) ([@tueddy](https://github.com/tueddy)): Compile with Arduino 3 (ESP-IDF 5.1) -- [#14](https://github.com/mathieucarbou/ESPAsyncWebServer/pull/14) ([@nilo85](https://github.com/nilo85)): Add support for Auth & GET requests in AsyncCallbackJsonWebHandler -- Added `setAuthentication(const String& username, const String& password)` -- Added `StreamConcat` example to show how to stream multiple files in one response -- Remove filename after inline in Content-Disposition header according to RFC2183 -- Depends on `mathieucarbou/Async TCP @ ^3.1.4` -- Arduino 3 / ESP-IDF 5.1 compatibility -- Added all flavors of `binary()`, `text()`, `binaryAll()` and `textAll()` in `AsyncWebSocket` -- Added `setCloseClientOnQueueFull(bool)` which can be set on a client to either close the connection or discard messages but not close the connection when the queue is full -- [#29](https://github.com/mathieucarbou/ESPAsyncWebServer/pull/29) ([@vortigont](https://github.com/vortigont)): Some websocket code cleanup -- Use `-D DEFAULT_MAX_WS_CLIENTS` to change the number of allows WebSocket clients and use `cleanupClients()` to help cleanup resources about dead clients +- [@ayushsharma82](https://github.com/ayushsharma82) and [@mathieucarbou](https://github.com/mathieucarbou): Add RP2040 support ([#31](https://github.com/mathieucarbou/ESPAsyncWebServer/pull/31)) +- [@mathieucarbou](https://github.com/mathieucarbou): `SSE_MAX_QUEUED_MESSAGES` to control the maximum number of messages that can be queued for a SSE client +- [@mathieucarbou](https://github.com/mathieucarbou): `write()` function public in `AsyncEventSource.h` +- [@mathieucarbou](https://github.com/mathieucarbou): `WS_MAX_QUEUED_MESSAGES`: control the maximum number of messages that can be queued for a Websocket client +- [@mathieucarbou](https://github.com/mathieucarbou): Added `setAuthentication(const String& username, const String& password)` +- [@mathieucarbou](https://github.com/mathieucarbou): Added `setCloseClientOnQueueFull(bool)` which can be set on a client to either close the connection or discard messages but not close the connection when the queue is full +- [@mathieucarbou](https://github.com/mathieucarbou): Added `StreamConcat` example to show how to stream multiple files in one response +- [@mathieucarbou](https://github.com/mathieucarbou): Added all flavors of `binary()`, `text()`, `binaryAll()` and `textAll()` in `AsyncWebSocket` +- [@mathieucarbou](https://github.com/mathieucarbou): Arduino 3 / ESP-IDF 5.1 compatibility +- [@mathieucarbou](https://github.com/mathieucarbou): Arduino Json 7 compatibility and backward compatible with 6 and 6 (changes in `AsyncJson.h`). The API to use Json has not changed. These are only internal changes. +- [@mathieucarbou](https://github.com/mathieucarbou): CI +- [@mathieucarbou](https://github.com/mathieucarbou): Depends on `mathieucarbou/Async TCP @ ^3.1.4` +- [@mathieucarbou](https://github.com/mathieucarbou): Deployed in PlatformIO registry and Arduino IDE library manager +- [@mathieucarbou](https://github.com/mathieucarbou): Firmware size optimization: remove mbedtls dependency (accounts for 33KB in firmware) +- [@mathieucarbou](https://github.com/mathieucarbou): Made DEFAULT_MAX_SSE_CLIENTS customizable +- [@mathieucarbou](https://github.com/mathieucarbou): Made DEFAULT_MAX_WS_CLIENTS customizable +- [@mathieucarbou](https://github.com/mathieucarbou): Remove filename after inline in Content-Disposition header according to RFC2183 +- [@mathieucarbou](https://github.com/mathieucarbou): Removed SPIFFSEditor to reduce library size and maintainance. SPIFF si also deprecated. If you need it, please copy the files from the original repository in your project. This fork focus on maintaining the server part and the SPIFFEditor is an application which has nothing to do inside a server library. +- [@mathieucarbou](https://github.com/mathieucarbou): Resurrected `AsyncWebSocketMessageBuffer` and `makeBuffer()` in order to make the fork API-compatible with the original library from me-no-dev regarding WebSocket. +- [@mathieucarbou](https://github.com/mathieucarbou): Some code cleanup +- [@mathieucarbou](https://github.com/mathieucarbou): Use `-D DEFAULT_MAX_WS_CLIENTS` to change the number of allows WebSocket clients and use `cleanupClients()` to help cleanup resources about dead clients +- [@nilo85](https://github.com/nilo85): Add support for Auth & GET requests in AsyncCallbackJsonWebHandler ([#14](https://github.com/mathieucarbou/ESPAsyncWebServer/pull/14)) +- [@p0p-x](https://github.com/p0p-x): ESP IDF Compatibility (added back CMakeLists.txt) ([#32](https://github.com/mathieucarbou/ESPAsyncWebServer/pull/32)) +- [@tueddy](https://github.com/tueddy): Compile with Arduino 3 (ESP-IDF 5.1) ([#13](https://github.com/mathieucarbou/ESPAsyncWebServer/pull/13)) +- [@vortigont](https://github.com/vortigont): Set real "Last-Modified" header based on file's LastWrite time ([#5](https://github.com/mathieucarbou/ESPAsyncWebServer/pull/5)) +- [@vortigont](https://github.com/vortigont): Some websocket code cleanup ([#29](https://github.com/mathieucarbou/ESPAsyncWebServer/pull/29)) +- [@vortigont](https://github.com/vortigont): Refactor code - replace DYI structs with STL objects ([#39](https://github.com/mathieucarbou/ESPAsyncWebServer/pull/39)) + +## Dependencies: + +- **ESP32**: `mathieucarbou/Async TCP @ ^3.1.4` (Arduino IDE: [https://github.com/mathieucarbou/AsyncTCP#v3.1.4](https://github.com/mathieucarbou/AsyncTCP/releases/tag/v3.1.4)) +- **ESP8266**: `esphome/ESPAsyncTCP-esphome @ 2.0.0` (Arduino IDE: [https://github.com/mathieucarbou/esphome-ESPAsyncTCP#v2.0.0](https://github.com/mathieucarbou/esphome-ESPAsyncTCP/releases/tag/v2.0.0)) +- **RP2040**: `khoih-prog/AsyncTCP_RP2040W @ 1.2.0` (Arduino IDE: [https://github.com/khoih-prog/AsyncTCP_RP2040W#v1.2.0](https://github.com/khoih-prog/AsyncTCP_RP2040W/releases/tag/v1.2.0)) ## Documentation @@ -76,17 +88,30 @@ void send(JsonDocument& doc) { I recommend to use the official API `AsyncWebSocketMessageBuffer` to retain further compatibility. -## Stack size and queues +## Important recommendations -Here are some important flags to tweak depending on your needs: +Most of the crashes are caused by improper configuration of the library for the project. +Here are some recommendations to avoid them. -```cpp - // Async TCP queue size +1. Set the running core to be on the same core of your application (usually core 1) `-D CONFIG_ASYNC_TCP_RUNNING_CORE=1` +2. Set the stack size appropriately with `-D CONFIG_ASYNC_TCP_STACK_SIZE=16384`. + The default value of `16384` might be too much for your project. + You can look at the [MycilaTaskMonitor](https://oss.carbou.me/MycilaTaskMonitor) project to monitor the stack usage. +3. You can change **if you know what you are doing** the task priority with `-D CONFIG_ASYNC_TCP_PRIORITY=10`. + Default is `10`. +4. You can increase the queue size with `-D CONFIG_ASYNC_TCP_QUEUE_SIZE=128`. + Default is `64`. +5. You can decrease the maximum ack time `-D CONFIG_ASYNC_TCP_MAX_ACK_TIME=3000`. + Default is `5000`. + +I personally use the following configuration in my projects because my WS messages can be big (up to 4k). +If you have smaller messages, you can increase `WS_MAX_QUEUED_MESSAGES` to 128. + +```c++ + -D CONFIG_ASYNC_TCP_MAX_ACK_TIME=3000 + -D CONFIG_ASYNC_TCP_PRIORITY=10 -D CONFIG_ASYNC_TCP_QUEUE_SIZE=128 - // Async TCP async task core -D CONFIG_ASYNC_TCP_RUNNING_CORE=1 - // Async TCP async stac ksize - -D CONFIG_ASYNC_TCP_STACK_SIZE=8096 - // WebSocket queue size + -D CONFIG_ASYNC_TCP_STACK_SIZE=4096 -D WS_MAX_QUEUED_MESSAGES=64 ``` diff --git a/examples/CaptivePortal/CaptivePortal.ino b/examples/CaptivePortal/CaptivePortal.ino index c06dccd..2d0de89 100644 --- a/examples/CaptivePortal/CaptivePortal.ino +++ b/examples/CaptivePortal/CaptivePortal.ino @@ -1,10 +1,13 @@ #include #ifdef ESP32 -#include -#include + #include + #include #elif defined(ESP8266) -#include -#include + #include + #include +#elif defined(TARGET_RP2040) + #include + #include #endif #include "ESPAsyncWebServer.h" @@ -12,43 +15,43 @@ DNSServer dnsServer; AsyncWebServer server(80); class CaptiveRequestHandler : public AsyncWebHandler { -public: - CaptiveRequestHandler() {} - virtual ~CaptiveRequestHandler() {} + public: + CaptiveRequestHandler() {} + virtual ~CaptiveRequestHandler() {} - bool canHandle(__unused AsyncWebServerRequest *request){ - //request->addInterestingHeader("ANY"); - return true; - } + bool canHandle(__unused AsyncWebServerRequest* request) { + // request->addInterestingHeader("ANY"); + return true; + } - void handleRequest(AsyncWebServerRequest *request) { - AsyncResponseStream *response = request->beginResponseStream("text/html"); - response->print("Captive Portal"); - response->print("

This is out captive portal front page.

"); - response->printf("

You were trying to reach: http://%s%s

", request->host().c_str(), request->url().c_str()); - response->printf("

Try opening this link instead

", WiFi.softAPIP().toString().c_str()); - response->print(""); - request->send(response); - } + void handleRequest(AsyncWebServerRequest* request) { + AsyncResponseStream* response = request->beginResponseStream("text/html"); + response->print("Captive Portal"); + response->print("

This is out captive portal front page.

"); + response->printf("

You were trying to reach: http://%s%s

", request->host().c_str(), request->url().c_str()); + response->printf("

Try opening this link instead

", WiFi.softAPIP().toString().c_str()); + response->print(""); + request->send(response); + } }; - -void setup(){ +void setup() { Serial.begin(115200); Serial.println(); Serial.println("Configuring access point..."); if (!WiFi.softAP("esp-captive")) { Serial.println("Soft AP creation failed."); - while (1); + while (1) + ; } dnsServer.start(53, "*", WiFi.softAPIP()); - server.addHandler(new CaptiveRequestHandler()).setFilter(ON_AP_FILTER);//only when requested from AP - //more handlers... + server.addHandler(new CaptiveRequestHandler()).setFilter(ON_AP_FILTER); // only when requested from AP + // more handlers... server.begin(); } -void loop(){ +void loop() { dnsServer.processNextRequest(); } diff --git a/examples/Draft/Draft.ino b/examples/Draft/Draft.ino index 0a1cb1f..f10a9e7 100644 --- a/examples/Draft/Draft.ino +++ b/examples/Draft/Draft.ino @@ -1,6 +1,6 @@ +#include "mbedtls/md5.h" #include #include -#include "mbedtls/md5.h" void setup() { Serial.begin(115200); diff --git a/examples/Filters/Filters.ino b/examples/Filters/Filters.ino index 90b6f19..f031a1f 100644 --- a/examples/Filters/Filters.ino +++ b/examples/Filters/Filters.ino @@ -2,11 +2,14 @@ #include #ifdef ESP32 -#include -#include + #include + #include #elif defined(ESP8266) -#include -#include + #include + #include +#elif defined(TARGET_RP2040) + #include + #include #endif #include "ESPAsyncWebServer.h" diff --git a/examples/SimpleServer/SimpleServer.ino b/examples/SimpleServer/SimpleServer.ino index bdbcf60..2472674 100644 --- a/examples/SimpleServer/SimpleServer.ino +++ b/examples/SimpleServer/SimpleServer.ino @@ -7,11 +7,14 @@ #include #ifdef ESP32 -#include -#include + #include + #include #elif defined(ESP8266) -#include -#include + #include + #include +#elif defined(TARGET_RP2040) + #include + #include #endif #include @@ -22,52 +25,52 @@ const char* password = "YOUR_PASSWORD"; const char* PARAM_MESSAGE = "message"; -void notFound(AsyncWebServerRequest *request) { - request->send(404, "text/plain", "Not found"); +void notFound(AsyncWebServerRequest* request) { + request->send(404, "text/plain", "Not found"); } void setup() { - Serial.begin(115200); - WiFi.mode(WIFI_STA); - WiFi.begin(ssid, password); - if (WiFi.waitForConnectResult() != WL_CONNECTED) { - Serial.printf("WiFi Failed!\n"); - return; + Serial.begin(115200); + WiFi.mode(WIFI_STA); + WiFi.begin(ssid, password); + if (WiFi.waitForConnectResult() != WL_CONNECTED) { + Serial.printf("WiFi Failed!\n"); + return; + } + + Serial.print("IP Address: "); + Serial.println(WiFi.localIP()); + + server.on("/", HTTP_GET, [](AsyncWebServerRequest* request) { + request->send(200, "text/plain", "Hello, world"); + }); + + // Send a GET request to /get?message= + server.on("/get", HTTP_GET, [](AsyncWebServerRequest* request) { + String message; + if (request->hasParam(PARAM_MESSAGE)) { + message = request->getParam(PARAM_MESSAGE)->value(); + } else { + message = "No message sent"; } + request->send(200, "text/plain", "Hello, GET: " + message); + }); - Serial.print("IP Address: "); - Serial.println(WiFi.localIP()); + // Send a POST request to /post with a form field message set to + server.on("/post", HTTP_POST, [](AsyncWebServerRequest* request) { + String message; + if (request->hasParam(PARAM_MESSAGE, true)) { + message = request->getParam(PARAM_MESSAGE, true)->value(); + } else { + message = "No message sent"; + } + request->send(200, "text/plain", "Hello, POST: " + message); + }); - server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){ - request->send(200, "text/plain", "Hello, world"); - }); + server.onNotFound(notFound); - // Send a GET request to /get?message= - server.on("/get", HTTP_GET, [] (AsyncWebServerRequest *request) { - String message; - if (request->hasParam(PARAM_MESSAGE)) { - message = request->getParam(PARAM_MESSAGE)->value(); - } else { - message = "No message sent"; - } - request->send(200, "text/plain", "Hello, GET: " + message); - }); - - // Send a POST request to /post with a form field message set to - server.on("/post", HTTP_POST, [](AsyncWebServerRequest *request){ - String message; - if (request->hasParam(PARAM_MESSAGE, true)) { - message = request->getParam(PARAM_MESSAGE, true)->value(); - } else { - message = "No message sent"; - } - request->send(200, "text/plain", "Hello, POST: " + message); - }); - - server.onNotFound(notFound); - - server.begin(); + server.begin(); } void loop() { diff --git a/examples/StreamFiles/StreamConcat.h b/examples/StreamFiles/StreamConcat.h index 1a53f2c..c1e1927 100644 --- a/examples/StreamFiles/StreamConcat.h +++ b/examples/StreamFiles/StreamConcat.h @@ -17,7 +17,11 @@ class StreamConcat : public Stream { return c != -1 ? c : _s2->read(); } +#if defined(TARGET_RP2040) + size_t readBytes(char* buffer, size_t length) { +#else size_t readBytes(char* buffer, size_t length) override { +#endif size_t count = _s1->readBytes(buffer, length); return count > 0 ? count : _s2->readBytes(buffer, length); } diff --git a/examples/StreamFiles/StreamFiles.ino b/examples/StreamFiles/StreamFiles.ino index d5621f5..2a2c1b6 100644 --- a/examples/StreamFiles/StreamFiles.ino +++ b/examples/StreamFiles/StreamFiles.ino @@ -1,11 +1,14 @@ #include #include #ifdef ESP32 -#include -#include + #include + #include #elif defined(ESP8266) -#include -#include + #include + #include +#elif defined(TARGET_RP2040) + #include + #include #endif #include "StreamConcat.h" #include "StreamString.h" @@ -42,7 +45,11 @@ void setup() { StreamConcat stream1(&header, &body); StreamString content; +#if defined(TARGET_RP2040) + content.printf("FreeHeap: %d", rp2040.getFreeHeap()); +#else content.printf("FreeHeap: %" PRIu32, ESP.getFreeHeap()); +#endif StreamConcat stream2 = StreamConcat(&stream1, &content); File footer = LittleFS.open("/footer.html", "r"); @@ -67,7 +74,11 @@ void loop() { // dnsServer.processNextRequest(); if (millis() - last > 2000) { +#if defined(TARGET_RP2040) + Serial.printf("FreeHeap: %d", rp2040.getFreeHeap()); +#else Serial.printf("FreeHeap: %" PRIu32, ESP.getFreeHeap()); +#endif last = millis(); } } \ No newline at end of file diff --git a/examples/StreamFiles/StreamString.h b/examples/StreamFiles/StreamString.h index f392b45..a6e0655 100644 --- a/examples/StreamFiles/StreamString.h +++ b/examples/StreamFiles/StreamString.h @@ -18,7 +18,11 @@ class StreamString : public Stream { return c; } +#if defined(TARGET_RP2040) + size_t readBytes(char* buffer, size_t length) { +#else size_t readBytes(char* buffer, size_t length) override { +#endif if (length > _buffer.length()) length = _buffer.length(); // Don't use _str.ToCharArray() because it inserts a terminator diff --git a/library.json_ b/library.json_ index 37a8190..4e089ba 100644 --- a/library.json_ +++ b/library.json_ @@ -1,7 +1,7 @@ { "name": "ESP Async WebServer", - "version": "2.10.8", - "description": "Asynchronous HTTP and WebSocket Server Library for ESP32. Supports: WebSocket, SSE, Authentication, Arduino Json 7, File Upload, Static File serving, URL Rewrite, URL Redirect, etc.", + "version": "3.0.6", + "description": "Asynchronous HTTP and WebSocket Server Library for ESP32, ESP8266 and RP2040. Supports: WebSocket, SSE, Authentication, Arduino Json 7, File Upload, Static File serving, URL Rewrite, URL Redirect, etc.", "keywords": "http,async,websocket,webserver", "homepage": "https://github.com/mathieucarbou/ESPAsyncWebServer", "repository": { @@ -21,7 +21,8 @@ "frameworks": "arduino", "platforms": [ "espressif32", - "espressif8266" + "espressif8266", + "raspberrypi" ], "dependencies": [ { @@ -39,6 +40,12 @@ { "name": "Hash", "platforms": "espressif8266" + }, + { + "owner": "khoih-prog", + "name": "AsyncTCP_RP2040W", + "version": "^1.2.0", + "platforms": "raspberrypi" } ], "export": { @@ -50,5 +57,8 @@ "LICENSE", "README.md" ] + }, + "build": { + "libCompatMode": "strict" } } \ No newline at end of file diff --git a/library.properties b/library.properties index be71733..e0265c3 100644 --- a/library.properties +++ b/library.properties @@ -1,10 +1,10 @@ name=ESP Async WebServer -version=2.10.8 +version=3.0.6 author=Me-No-Dev maintainer=Mathieu Carbou -sentence=Asynchronous HTTP and WebSocket Server Library for ESP32 +sentence=Asynchronous HTTP and WebSocket Server Library for ESP32, ESP8266 and RP2040 paragraph=Supports: WebSocket, SSE, Authentication, Arduino Json 7, File Upload, Static File serving, URL Rewrite, URL Redirect, etc category=Other url=https://github.com/mathieucarbou/ESPAsyncWebServer -architectures=esp8266,esp32 +architectures=* license=LGPL-3.0 diff --git a/platformio.ini b/platformio.ini index 1f19720..0c85cfe 100644 --- a/platformio.ini +++ b/platformio.ini @@ -4,42 +4,57 @@ build_flags = -Wall -Wextra -D CONFIG_ARDUHAL_LOG_COLORS -D CORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_VERBOSE -lib_deps = - bblanchon/ArduinoJson @ 7.0.4 - mathieucarbou/Async TCP @ ^3.1.4 - ; https://github.com/mathieucarbou/AsyncTCP - ; https://github.com/me-no-dev/AsyncTCP - esphome/ESPAsyncTCP-esphome @ 2.0.0 upload_protocol = esptool monitor_speed = 115200 monitor_filters = esp32_exception_decoder, log2file [platformio] lib_dir = . -src_dir = examples/CaptivePortal +; src_dir = examples/CaptivePortal ; src_dir = examples/SimpleServer -; src_dir = examples/StreamFiles +src_dir = examples/StreamFiles ; src_dir = examples/Filters ; src_dir = examples/Draft [env:arduino] platform = espressif32 board = esp32dev +lib_deps = + bblanchon/ArduinoJson @ 7.1.0 + mathieucarbou/Async TCP @ ^3.1.4 [env:arduino-2] platform = espressif32@6.7.0 board = esp32dev +lib_deps = + bblanchon/ArduinoJson @ 7.1.0 + mathieucarbou/Async TCP @ ^3.1.4 [env:arduino-3] platform = espressif32 platform_packages= - platformio/framework-arduinoespressif32 @ https://github.com/espressif/arduino-esp32.git#3.0.1 - platformio/framework-arduinoespressif32-libs @ https://github.com/espressif/arduino-esp32/releases/download/3.0.1/esp32-arduino-libs-3.0.1.zip + platformio/framework-arduinoespressif32 @ https://github.com/espressif/arduino-esp32.git#3.0.2 + platformio/framework-arduinoespressif32-libs @ https://github.com/espressif/arduino-esp32/releases/download/3.0.2/esp32-arduino-libs-3.0.2.zip board = esp32dev +lib_deps = + bblanchon/ArduinoJson @ 7.1.0 + mathieucarbou/Async TCP @ ^3.1.4 [env:esp8266] platform = espressif8266 board = huzzah lib_deps = - bblanchon/ArduinoJson @ 7.0.4 + bblanchon/ArduinoJson @ 7.1.0 esphome/ESPAsyncTCP-esphome @ 2.0.0 + +; PlatformIO support for Raspberry Pi Pico is not official +; https://github.com/platformio/platform-raspberrypi/pull/36 +; https://github.com/earlephilhower/arduino-pico/blob/master/docs/platformio.rst +; board settings: https://github.com/earlephilhower/arduino-pico/blob/master/tools/json/rpipico.json +[env:rpipicow] +upload_protocol = picotool +platform = https://github.com/maxgerhardt/platform-raspberrypi.git +board = rpipicow +lib_deps = + bblanchon/ArduinoJson @ 7.1.0 + khoih-prog/AsyncTCP_RP2040W @ 1.2.0 diff --git a/src/AsyncEventSource.cpp b/src/AsyncEventSource.cpp index 98f18de..4cafdae 100644 --- a/src/AsyncEventSource.cpp +++ b/src/AsyncEventSource.cpp @@ -18,43 +18,43 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "Arduino.h" -#include "AsyncEventSource.h" -#ifndef ESP8266 +#if defined(ESP32) #include #endif +#include "AsyncEventSource.h" -static String generateEventMessage(const char *message, const char *event, uint32_t id, uint32_t reconnect){ +static String generateEventMessage(const char* message, const char* event, uint32_t id, uint32_t reconnect) { String ev; - if(reconnect){ + if (reconnect) { ev += F("retry: "); ev += reconnect; ev += F("\r\n"); } - if(id){ + if (id) { ev += F("id: "); ev += String(id); ev += F("\r\n"); } - if(event != NULL){ + if (event != NULL) { ev += F("event: "); ev += String(event); ev += F("\r\n"); } - if(message != NULL){ + if (message != NULL) { size_t messageLen = strlen(message); - char * lineStart = (char *)message; - char * lineEnd; + char* lineStart = (char*)message; + char* lineEnd; do { - char * nextN = strchr(lineStart, '\n'); - char * nextR = strchr(lineStart, '\r'); - if(nextN == NULL && nextR == NULL){ - size_t llen = ((char *)message + messageLen) - lineStart; - char * ldata = (char *)malloc(llen+1); - if(ldata != NULL){ + char* nextN = strchr(lineStart, '\n'); + char* nextR = strchr(lineStart, '\r'); + if (nextN == NULL && nextR == NULL) { + size_t llen = ((char*)message + messageLen) - lineStart; + char* ldata = (char*)malloc(llen + 1); + if (ldata != NULL) { memcpy(ldata, lineStart, llen); ldata[llen] = 0; ev += F("data: "); @@ -62,24 +62,24 @@ static String generateEventMessage(const char *message, const char *event, uint3 ev += F("\r\n\r\n"); free(ldata); } - lineStart = (char *)message + messageLen; + lineStart = (char*)message + messageLen; } else { - char * nextLine = NULL; - if(nextN != NULL && nextR != NULL){ - if(nextR < nextN){ + char* nextLine = NULL; + if (nextN != NULL && nextR != NULL) { + if (nextR < nextN) { lineEnd = nextR; - if(nextN == (nextR + 1)) + if (nextN == (nextR + 1)) nextLine = nextN + 1; else nextLine = nextR + 1; } else { lineEnd = nextN; - if(nextR == (nextN + 1)) + if (nextR == (nextN + 1)) nextLine = nextR + 1; else nextLine = nextN + 1; } - } else if(nextN != NULL){ + } else if (nextN != NULL) { lineEnd = nextN; nextLine = nextN + 1; } else { @@ -88,8 +88,8 @@ static String generateEventMessage(const char *message, const char *event, uint3 } size_t llen = lineEnd - lineStart; - char * ldata = (char *)malloc(llen+1); - if(ldata != NULL){ + char* ldata = (char*)malloc(llen + 1); + if (ldata != NULL) { memcpy(ldata, lineStart, llen); ldata[llen] = 0; ev += F("data: "); @@ -98,10 +98,10 @@ static String generateEventMessage(const char *message, const char *event, uint3 free(ldata); } lineStart = nextLine; - if(lineStart == ((char *)message + messageLen)) + if (lineStart == ((char*)message + messageLen)) ev += F("\r\n"); } - } while(lineStart < ((char *)message + messageLen)); + } while (lineStart < ((char*)message + messageLen)); } return ev; @@ -109,11 +109,10 @@ static String generateEventMessage(const char *message, const char *event, uint3 // Message -AsyncEventSourceMessage::AsyncEventSourceMessage(const char * data, size_t len) -: _data(nullptr), _len(len), _sent(0), _acked(0) -{ - _data = (uint8_t*)malloc(_len+1); - if(_data == nullptr){ +AsyncEventSourceMessage::AsyncEventSourceMessage(const char* data, size_t len) + : _data(nullptr), _len(len), _sent(0), _acked(0) { + _data = (uint8_t*)malloc(_len + 1); + if (_data == nullptr) { _len = 0; } else { memcpy(_data, data, len); @@ -122,18 +121,18 @@ AsyncEventSourceMessage::AsyncEventSourceMessage(const char * data, size_t len) } AsyncEventSourceMessage::~AsyncEventSourceMessage() { - if(_data != NULL) - free(_data); + if (_data != NULL) + free(_data); } size_t AsyncEventSourceMessage::ack(size_t len, uint32_t time) { (void)time; // If the whole message is now acked... - if(_acked + len > _len){ - // Return the number of extra bytes acked (they will be carried on to the next message) - const size_t extra = _acked + len - _len; - _acked = _len; - return extra; + if (_acked + len > _len) { + // Return the number of extra bytes acked (they will be carried on to the next message) + const size_t extra = _acked + len - _len; + _acked = _len; + return extra; } // Return that no extra bytes left. _acked += len; @@ -142,188 +141,173 @@ size_t AsyncEventSourceMessage::ack(size_t len, uint32_t time) { // This could also return void as the return value is not used. // Leaving as-is for compatibility... -size_t AsyncEventSourceMessage::send(AsyncClient *client) { - if (_sent >= _len) { - return 0; - } - const size_t len_to_send = _len - _sent; - auto position = reinterpret_cast(_data + _sent); - const size_t sent_now = client->write(position, len_to_send); - _sent += sent_now; - return sent_now; +size_t AsyncEventSourceMessage::send(AsyncClient* client) { + if (_sent >= _len) { + return 0; + } + const size_t len_to_send = _len - _sent; + auto position = reinterpret_cast(_data + _sent); + const size_t sent_now = client->write(position, len_to_send); + _sent += sent_now; + return sent_now; } // Client -AsyncEventSourceClient::AsyncEventSourceClient(AsyncWebServerRequest *request, AsyncEventSource *server) -: _messageQueue(LinkedList([](AsyncEventSourceMessage *m){ delete m; })) -{ +AsyncEventSourceClient::AsyncEventSourceClient(AsyncWebServerRequest* request, AsyncEventSource* server) { _client = request->client(); _server = server; _lastId = 0; - if(request->hasHeader(F("Last-Event-ID"))) + if (request->hasHeader(F("Last-Event-ID"))) _lastId = atoi(request->getHeader(F("Last-Event-ID"))->value().c_str()); _client->setRxTimeout(0); _client->onError(NULL, NULL); - _client->onAck([](void *r, AsyncClient* c, size_t len, uint32_t time){ (void)c; ((AsyncEventSourceClient*)(r))->_onAck(len, time); }, this); - _client->onPoll([](void *r, AsyncClient* c){ (void)c; ((AsyncEventSourceClient*)(r))->_onPoll(); }, this); + _client->onAck([](void* r, AsyncClient* c, size_t len, uint32_t time) { (void)c; ((AsyncEventSourceClient*)(r))->_onAck(len, time); }, this); + _client->onPoll([](void* r, AsyncClient* c) { (void)c; ((AsyncEventSourceClient*)(r))->_onPoll(); }, this); _client->onData(NULL, NULL); - _client->onTimeout([this](void *r, AsyncClient* c __attribute__((unused)), uint32_t time){ ((AsyncEventSourceClient*)(r))->_onTimeout(time); }, this); - _client->onDisconnect([this](void *r, AsyncClient* c){ ((AsyncEventSourceClient*)(r))->_onDisconnect(); delete c; }, this); + _client->onTimeout([this](void* r, AsyncClient* c __attribute__((unused)), uint32_t time) { ((AsyncEventSourceClient*)(r))->_onTimeout(time); }, this); + _client->onDisconnect([this](void* r, AsyncClient* c) { ((AsyncEventSourceClient*)(r))->_onDisconnect(); delete c; }, this); _server->_addClient(this); delete request; } -AsyncEventSourceClient::~AsyncEventSourceClient(){ - _lockmq.lock(); - _messageQueue.free(); - _lockmq.unlock(); +AsyncEventSourceClient::~AsyncEventSourceClient() { +#ifdef ESP32 + std::lock_guard lock(_lockmq); +#endif + _messageQueue.clear(); close(); } -void AsyncEventSourceClient::_queueMessage(AsyncEventSourceMessage *dataMessage){ - if(dataMessage == NULL) - return; - if(!connected()){ - delete dataMessage; - return; - } - //length() is not thread-safe, thus acquiring the lock before this call.. - _lockmq.lock(); - if(_messageQueue.length() >= SSE_MAX_QUEUED_MESSAGES){ +void AsyncEventSourceClient::_queueMessage(const char* message, size_t len) { +#ifdef ESP32 + // length() is not thread-safe, thus acquiring the lock before this call.. + std::lock_guard lock(_lockmq); +#endif + + if (_messageQueue.size() >= SSE_MAX_QUEUED_MESSAGES) { #ifdef ESP8266 ets_printf(String(F("ERROR: Too many messages queued\n")).c_str()); -#else +#elif defined(ESP32) log_e("Too many messages queued: deleting message"); #endif - delete dataMessage; - } else { - _messageQueue.add(dataMessage); - // runqueue trigger when new messages added - if(_client->canSend()) { - _runQueue(); - } + return; } - _lockmq.unlock(); -} -void AsyncEventSourceClient::_onAck(size_t len, uint32_t time){ - // Same here, acquiring the lock early - _lockmq.lock(); - while(len && !_messageQueue.isEmpty()){ - len = _messageQueue.front()->ack(len, time); - if(_messageQueue.front()->finished()) - _messageQueue.remove(_messageQueue.front()); - } - _runQueue(); - _lockmq.unlock(); -} - -void AsyncEventSourceClient::_onPoll(){ - _lockmq.lock(); - if(!_messageQueue.isEmpty()){ + _messageQueue.emplace_back(message, len); + // runqueue trigger when new messages added + if (_client->canSend()) { _runQueue(); } - _lockmq.unlock(); } -void AsyncEventSourceClient::_onTimeout(uint32_t time __attribute__((unused))){ +void AsyncEventSourceClient::_onAck(size_t len, uint32_t time) { +#ifdef ESP32 + // Same here, acquiring the lock early + std::lock_guard lock(_lockmq); +#endif + while (len && _messageQueue.size()) { + len = _messageQueue.front().ack(len, time); + if (_messageQueue.front().finished()) + _messageQueue.pop_front(); + } + _runQueue(); +} + +void AsyncEventSourceClient::_onPoll() { +#ifdef ESP32 + // Same here, acquiring the lock early + std::lock_guard lock(_lockmq); +#endif + if (_messageQueue.size()) { + _runQueue(); + } +} + +void AsyncEventSourceClient::_onTimeout(uint32_t time __attribute__((unused))) { _client->close(true); } -void AsyncEventSourceClient::_onDisconnect(){ +void AsyncEventSourceClient::_onDisconnect() { _client = NULL; _server->_handleDisconnect(this); } -void AsyncEventSourceClient::close(){ - if(_client != NULL) +void AsyncEventSourceClient::close() { + if (_client != NULL) _client->close(); } -void AsyncEventSourceClient::write(const char * message, size_t len){ - _queueMessage(new AsyncEventSourceMessage(message, len)); +void AsyncEventSourceClient::write(const char* message, size_t len) { + if (!connected()) + return; + _queueMessage(message, len); } -void AsyncEventSourceClient::send(const char *message, const char *event, uint32_t id, uint32_t reconnect){ +void AsyncEventSourceClient::send(const char* message, const char* event, uint32_t id, uint32_t reconnect) { + if (!connected()) + return; String ev = generateEventMessage(message, event, id, reconnect); - _queueMessage(new AsyncEventSourceMessage(ev.c_str(), ev.length())); + _queueMessage(ev.c_str(), ev.length()); } size_t AsyncEventSourceClient::packetsWaiting() const { - size_t len; - _lockmq.lock(); - len = _messageQueue.length(); - _lockmq.unlock(); - return len; +#ifdef ESP32 + std::lock_guard lock(_lockmq); +#endif + return _messageQueue.size(); } void AsyncEventSourceClient::_runQueue() { // Calls to this private method now already protected by _lockmq acquisition // 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); - } + for (auto& i : _messageQueue) { + if (!i.sent()) + i.send(_client); } } - // Handler - -AsyncEventSource::AsyncEventSource(const String& url) - : _url(url) - , _clients(LinkedList([](AsyncEventSourceClient *c){ delete c; })) - , _connectcb(NULL) -{} - -AsyncEventSource::~AsyncEventSource(){ - close(); -} - -void AsyncEventSource::onConnect(ArEventHandlerFunction cb){ +void AsyncEventSource::onConnect(ArEventHandlerFunction cb) { _connectcb = cb; } -void AsyncEventSource::authorizeConnect(ArAuthorizeConnectHandler cb){ +void AsyncEventSource::authorizeConnect(ArAuthorizeConnectHandler cb) { _authorizeConnectHandler = cb; } -void AsyncEventSource::_addClient(AsyncEventSourceClient * client){ - /*char * temp = (char *)malloc(2054); - if(temp != NULL){ - memset(temp+1,' ',2048); - temp[0] = ':'; - temp[2049] = '\r'; - temp[2050] = '\n'; - temp[2051] = '\r'; - temp[2052] = '\n'; - temp[2053] = 0; - client->write((const char *)temp, 2053); - free(temp); - }*/ - AsyncWebLockGuard l(_client_queue_lock); - _clients.add(client); - if(_connectcb) +void AsyncEventSource::_addClient(AsyncEventSourceClient* client) { + if (!client) + return; +#ifdef ESP32 + std::lock_guard lock(_client_queue_lock); +#endif + _clients.emplace_back(client); + if (_connectcb) _connectcb(client); } -void AsyncEventSource::_handleDisconnect(AsyncEventSourceClient * client){ - AsyncWebLockGuard l(_client_queue_lock); - _clients.remove(client); +void AsyncEventSource::_handleDisconnect(AsyncEventSourceClient* client) { +#ifdef ESP32 + std::lock_guard lock(_client_queue_lock); +#endif + for (auto i = _clients.begin(); i != _clients.end(); ++i) { + if (i->get() == client) + _clients.erase(i); + } } -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){ - if(c->connected()) +#ifdef ESP32 + std::lock_guard lock(_client_queue_lock); +#endif + for (const auto& c : _clients) { + if (c->connected()) c->close(); } } @@ -332,41 +316,48 @@ void AsyncEventSource::close(){ size_t AsyncEventSource::avgPacketsWaiting() const { size_t aql = 0; uint32_t nConnectedClients = 0; - AsyncWebLockGuard l(_client_queue_lock); - if (_clients.isEmpty()) { +#ifdef ESP32 + std::lock_guard lock(_client_queue_lock); +#endif + if (!_clients.size()) return 0; - } - for(const auto &c: _clients){ - if(c->connected()) { + + for (const auto& c : _clients) { + if (c->connected()) { aql += c->packetsWaiting(); ++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){ + const char* message, const char* event, uint32_t id, uint32_t reconnect) { String ev = generateEventMessage(message, event, id, reconnect); - AsyncWebLockGuard l(_client_queue_lock); - for(const auto &c: _clients){ - if(c->connected()) { +#ifdef ESP32 + std::lock_guard lock(_client_queue_lock); +#endif + for (const auto& c : _clients) { + if (c->connected()) { c->write(ev.c_str(), ev.length()); } } } size_t AsyncEventSource::count() const { - size_t n_clients; - AsyncWebLockGuard l(_client_queue_lock); - n_clients = _clients.count_if([](AsyncEventSourceClient *c){ - return c->connected(); - }); +#ifdef ESP32 + std::lock_guard lock(_client_queue_lock); +#endif + size_t n_clients{0}; + for (const auto& i : _clients) + if (i->connected()) + ++n_clients; + return n_clients; } -bool AsyncEventSource::canHandle(AsyncWebServerRequest *request){ - if(request->method() != HTTP_GET || !request->url().equals(_url)) { +bool AsyncEventSource::canHandle(AsyncWebServerRequest* request) { + if (request->method() != HTTP_GET || !request->url().equals(_url)) { return false; } request->addInterestingHeader(F("Last-Event-ID")); @@ -374,12 +365,12 @@ bool AsyncEventSource::canHandle(AsyncWebServerRequest *request){ return true; } -void AsyncEventSource::handleRequest(AsyncWebServerRequest *request){ - if((_username.length() && _password.length()) && !request->authenticate(_username.c_str(), _password.c_str())) { +void AsyncEventSource::handleRequest(AsyncWebServerRequest* request) { + if ((_username.length() && _password.length()) && !request->authenticate(_username.c_str(), _password.c_str())) { return request->requestAuthentication(); } - if(_authorizeConnectHandler != NULL){ - if(!_authorizeConnectHandler(request)){ + if (_authorizeConnectHandler != NULL) { + if (!_authorizeConnectHandler(request)) { return request->send(401); } } @@ -388,7 +379,7 @@ void AsyncEventSource::handleRequest(AsyncWebServerRequest *request){ // Response -AsyncEventSourceResponse::AsyncEventSourceResponse(AsyncEventSource *server){ +AsyncEventSourceResponse::AsyncEventSourceResponse(AsyncEventSource* server) { _server = server; _code = 200; _contentType = F("text/event-stream"); @@ -397,16 +388,15 @@ AsyncEventSourceResponse::AsyncEventSourceResponse(AsyncEventSource *server){ addHeader(F("Connection"), F("keep-alive")); } -void AsyncEventSourceResponse::_respond(AsyncWebServerRequest *request){ +void AsyncEventSourceResponse::_respond(AsyncWebServerRequest* request) { String out = _assembleHead(request->version()); request->client()->write(out.c_str(), _headLength); _state = RESPONSE_WAIT_ACK; } -size_t AsyncEventSourceResponse::_ack(AsyncWebServerRequest *request, size_t len, uint32_t time __attribute__((unused))){ - if(len){ +size_t AsyncEventSourceResponse::_ack(AsyncWebServerRequest* request, size_t len, uint32_t time __attribute__((unused))) { + if (len) { new AsyncEventSourceClient(request, _server); } return 0; } - diff --git a/src/AsyncEventSource.h b/src/AsyncEventSource.h index 2efb0dd..0289ebf 100644 --- a/src/AsyncEventSource.h +++ b/src/AsyncEventSource.h @@ -21,129 +21,138 @@ #define ASYNCEVENTSOURCE_H_ #include +#include #ifdef ESP32 -#include -#ifndef SSE_MAX_QUEUED_MESSAGES -#define SSE_MAX_QUEUED_MESSAGES 32 -#endif -#else -#include -#ifndef SSE_MAX_QUEUED_MESSAGES -#define SSE_MAX_QUEUED_MESSAGES 8 -#endif + #include + #include + #ifndef SSE_MAX_QUEUED_MESSAGES + #define SSE_MAX_QUEUED_MESSAGES 32 + #endif +#elif defined(ESP8266) + #include + #ifndef SSE_MAX_QUEUED_MESSAGES + #define SSE_MAX_QUEUED_MESSAGES 8 + #endif +#elif defined(TARGET_RP2040) + #include + #ifndef SSE_MAX_QUEUED_MESSAGES + #define SSE_MAX_QUEUED_MESSAGES 32 + #endif #endif #include -#include "AsyncWebSynchronization.h" - #ifdef ESP8266 -#include -#ifdef CRYPTO_HASH_h // include Hash.h from espressif framework if the first include was from the crypto library -#include <../src/Hash.h> -#endif + #include + #ifdef CRYPTO_HASH_h // include Hash.h from espressif framework if the first include was from the crypto library + #include <../src/Hash.h> + #endif #endif #ifndef DEFAULT_MAX_SSE_CLIENTS -#ifdef ESP32 -#define DEFAULT_MAX_SSE_CLIENTS 8 -#else -#define DEFAULT_MAX_SSE_CLIENTS 4 -#endif + #ifdef ESP32 + #define DEFAULT_MAX_SSE_CLIENTS 8 + #else + #define DEFAULT_MAX_SSE_CLIENTS 4 + #endif #endif class AsyncEventSource; class AsyncEventSourceResponse; class AsyncEventSourceClient; -typedef std::function ArEventHandlerFunction; -typedef std::function ArAuthorizeConnectHandler; +using ArEventHandlerFunction = std::function; +using ArAuthorizeConnectHandler = std::function; class AsyncEventSourceMessage { private: - uint8_t * _data; + uint8_t* _data; size_t _len; size_t _sent; - //size_t _ack; + // size_t _ack; size_t _acked; + public: - AsyncEventSourceMessage(const char * data, size_t len); + AsyncEventSourceMessage(const char* data, size_t len); ~AsyncEventSourceMessage(); size_t ack(size_t len, uint32_t time __attribute__((unused))); - size_t send(AsyncClient *client); - bool finished(){ return _acked == _len; } + size_t send(AsyncClient* client); + bool finished() { return _acked == _len; } bool sent() { return _sent == _len; } }; class AsyncEventSourceClient { private: - AsyncClient *_client; - AsyncEventSource *_server; + AsyncClient* _client; + AsyncEventSource* _server; uint32_t _lastId; - LinkedList _messageQueue; - // ArFi 2020-08-27 for protecting/serializing _messageQueue - AsyncPlainLock _lockmq; - void _queueMessage(AsyncEventSourceMessage *dataMessage); + std::list _messageQueue; +#ifdef ESP32 + mutable std::mutex _lockmq; +#endif + void _queueMessage(const char* message, size_t len); void _runQueue(); public: - - AsyncEventSourceClient(AsyncWebServerRequest *request, AsyncEventSource *server); + AsyncEventSourceClient(AsyncWebServerRequest* request, AsyncEventSource* server); ~AsyncEventSourceClient(); - AsyncClient* client(){ return _client; } + AsyncClient* client() { return _client; } 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 write(const char* message, size_t len); + 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(); } uint32_t lastId() const { return _lastId; } - size_t packetsWaiting() const; + size_t packetsWaiting() const; - //system callbacks (do not call) + // system callbacks (do not call) void _onAck(size_t len, uint32_t time); void _onPoll(); void _onTimeout(uint32_t time); void _onDisconnect(); }; -class AsyncEventSource: public AsyncWebHandler { +class AsyncEventSource : public AsyncWebHandler { private: String _url; - LinkedList _clients; + std::list> _clients; +#ifdef ESP32 // Same as for individual messages, protect mutations of _clients list // since simultaneous access from different tasks is possible - AsyncWebLock _client_queue_lock; - ArEventHandlerFunction _connectcb; - ArAuthorizeConnectHandler _authorizeConnectHandler; - public: - AsyncEventSource(const String& url); - ~AsyncEventSource(); + mutable std::mutex _client_queue_lock; +#endif + ArEventHandlerFunction _connectcb{nullptr}; + ArAuthorizeConnectHandler _authorizeConnectHandler; - const char * url() const { return _url.c_str(); } + public: + AsyncEventSource(const String& url) : _url(url){}; + ~AsyncEventSource() { close(); }; + + const char* url() const { return _url.c_str(); } void close(); void onConnect(ArEventHandlerFunction 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); // number of clients connected size_t count() const; - size_t avgPacketsWaiting() const; + size_t avgPacketsWaiting() const; - //system callbacks (do not call) - void _addClient(AsyncEventSourceClient * client); - void _handleDisconnect(AsyncEventSourceClient * client); - virtual bool canHandle(AsyncWebServerRequest *request) override final; - virtual void handleRequest(AsyncWebServerRequest *request) override final; + // system callbacks (do not call) + void _addClient(AsyncEventSourceClient* client); + void _handleDisconnect(AsyncEventSourceClient* client); + virtual bool canHandle(AsyncWebServerRequest* request) override final; + virtual void handleRequest(AsyncWebServerRequest* request) override final; }; -class AsyncEventSourceResponse: public AsyncWebServerResponse { +class AsyncEventSourceResponse : public AsyncWebServerResponse { private: String _content; - AsyncEventSource *_server; + AsyncEventSource* _server; + public: - AsyncEventSourceResponse(AsyncEventSource *server); - void _respond(AsyncWebServerRequest *request); - size_t _ack(AsyncWebServerRequest *request, size_t len, uint32_t time); + AsyncEventSourceResponse(AsyncEventSource* server); + void _respond(AsyncWebServerRequest* request); + size_t _ack(AsyncWebServerRequest* request, size_t len, uint32_t time); bool _sourceValid() const { return true; } }; - #endif /* ASYNCEVENTSOURCE_H_ */ diff --git a/src/AsyncJson.h b/src/AsyncJson.h index 6774454..b85a938 100644 --- a/src/AsyncJson.h +++ b/src/AsyncJson.h @@ -39,9 +39,9 @@ #include #if ARDUINOJSON_VERSION_MAJOR == 6 -#ifndef DYNAMIC_JSON_DOCUMENT_SIZE -#define DYNAMIC_JSON_DOCUMENT_SIZE 1024 -#endif + #ifndef DYNAMIC_JSON_DOCUMENT_SIZE + #define DYNAMIC_JSON_DOCUMENT_SIZE 1024 + #endif #endif constexpr const char* JSON_MIMETYPE = "application/json"; @@ -120,7 +120,6 @@ class AsyncJsonResponse : public AsyncAbstractResponse { } #endif - ~AsyncJsonResponse() {} JsonVariant& getRoot() { return _root; } bool _sourceValid() const { return _isValid; } size_t setLength() { @@ -230,7 +229,7 @@ class AsyncCallbackJsonWebHandler : public AsyncWebHandler { } virtual void handleRequest(AsyncWebServerRequest* request) override final { - if((_username != "" && _password != "") && !request->authenticate(_username.c_str(), _password.c_str())) + if ((_username != "" && _password != "") && !request->authenticate(_username.c_str(), _password.c_str())) return request->requestAuthentication(); if (_onRequest) { if (request->method() == HTTP_GET) { diff --git a/src/AsyncWebSocket.cpp b/src/AsyncWebSocket.cpp index df6821d..e978afc 100644 --- a/src/AsyncWebSocket.cpp +++ b/src/AsyncWebSocket.cpp @@ -18,144 +18,140 @@ License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include "Arduino.h" #include "AsyncWebSocket.h" +#include "Arduino.h" #include #include -#ifndef ESP8266 -#if ESP_IDF_VERSION_MAJOR < 5 -#include "./port/SHA1Builder.h" -#else -#include -#endif -#include -#else -#include +#if defined(ESP32) + #if ESP_IDF_VERSION_MAJOR < 5 + #include "./port/SHA1Builder.h" + #else + #include + #endif + #include +#elif defined(TARGET_RP2040) || defined(ESP8266) + #include #endif #define MAX_PRINTF_LEN 64 -size_t webSocketSendFrameWindow(AsyncClient *client){ - if(!client->canSend()) +size_t webSocketSendFrameWindow(AsyncClient* client) { + if (!client->canSend()) return 0; size_t space = client->space(); - if(space < 9) + if (space < 9) return 0; return space - 8; } -size_t webSocketSendFrame(AsyncClient *client, bool final, uint8_t opcode, bool mask, uint8_t *data, size_t len){ - if(!client->canSend()) { +size_t webSocketSendFrame(AsyncClient* client, bool final, uint8_t opcode, bool mask, uint8_t* data, size_t len) { + if (!client->canSend()) { // Serial.println("SF 1"); return 0; } size_t space = client->space(); - if(space < 2) { + if (space < 2) { // Serial.println("SF 2"); return 0; } - uint8_t mbuf[4] = {0,0,0,0}; + uint8_t mbuf[4] = {0, 0, 0, 0}; uint8_t headLen = 2; - if(len && mask){ + if (len && mask) { headLen += 4; mbuf[0] = rand() % 0xFF; mbuf[1] = rand() % 0xFF; mbuf[2] = rand() % 0xFF; mbuf[3] = rand() % 0xFF; } - if(len > 125) + if (len > 125) headLen += 2; - if(space < headLen) { + if (space < headLen) { // Serial.println("SF 2"); return 0; } space -= headLen; - if(len > space) len = space; + if (len > space) + len = space; - uint8_t *buf = (uint8_t*)malloc(headLen); - if(buf == NULL){ - //os_printf("could not malloc %u bytes for frame header\n", headLen); - // Serial.println("SF 3"); + uint8_t* buf = (uint8_t*)malloc(headLen); + if (buf == NULL) { + // os_printf("could not malloc %u bytes for frame header\n", headLen); + // Serial.println("SF 3"); return 0; } buf[0] = opcode & 0x0F; - if(final) + if (final) buf[0] |= 0x80; - if(len < 126) + if (len < 126) buf[1] = len & 0x7F; else { buf[1] = 126; buf[2] = (uint8_t)((len >> 8) & 0xFF); buf[3] = (uint8_t)(len & 0xFF); } - if(len && mask){ + if (len && mask) { buf[1] |= 0x80; memcpy(buf + (headLen - 4), mbuf, 4); } - if(client->add((const char *)buf, headLen) != headLen){ - //os_printf("error adding %lu header bytes\n", headLen); + if (client->add((const char*)buf, headLen) != headLen) { + // os_printf("error adding %lu header bytes\n", headLen); free(buf); // Serial.println("SF 4"); return 0; } free(buf); - if(len){ - if(len && mask){ + if (len) { + if (len && mask) { size_t i; - for(i=0;iadd((const char *)data, len) != len){ - //os_printf("error adding %lu data bytes\n", len); - // Serial.println("SF 5"); + if (client->add((const char*)data, len) != len) { + // os_printf("error adding %lu data bytes\n", len); + // Serial.println("SF 5"); return 0; } } - if(!client->send()){ - //os_printf("error sending frame: %lu\n", headLen+len); - // Serial.println("SF 6"); + if (!client->send()) { + // os_printf("error sending frame: %lu\n", headLen+len); + // Serial.println("SF 6"); return 0; } // Serial.println("SF"); return len; } - - /* * AsyncWebSocketMessageBuffer */ AsyncWebSocketMessageBuffer::AsyncWebSocketMessageBuffer(const uint8_t* data, size_t size) - : _buffer(std::make_shared>(size)) -{ - if (_buffer->capacity() < size) { - _buffer->reserve(size); - } else { - std::memcpy(_buffer->data(), data, size); - } + : _buffer(std::make_shared>(size)) { + if (_buffer->capacity() < size) { + _buffer->reserve(size); + } else { + std::memcpy(_buffer->data(), data, size); + } } AsyncWebSocketMessageBuffer::AsyncWebSocketMessageBuffer(size_t size) - : _buffer(std::make_shared>(size)) -{ - if (_buffer->capacity() < size) { - _buffer->reserve(size); - } + : _buffer(std::make_shared>(size)) { + if (_buffer->capacity() < size) { + _buffer->reserve(size); + } } -bool AsyncWebSocketMessageBuffer::reserve(size_t size) -{ - if (_buffer->capacity() >= size) - return true; - _buffer->reserve(size); - return _buffer->capacity() >= size; +bool AsyncWebSocketMessageBuffer::reserve(size_t size) { + if (_buffer->capacity() >= size) + return true; + _buffer->reserve(size); + return _buffer->capacity() >= size; } /* @@ -163,386 +159,340 @@ bool AsyncWebSocketMessageBuffer::reserve(size_t size) */ class AsyncWebSocketControl { -private: + private: uint8_t _opcode; - uint8_t *_data; + uint8_t* _data; size_t _len; bool _mask; bool _finished; -public: - AsyncWebSocketControl(uint8_t opcode, const uint8_t *data=NULL, size_t len=0, bool mask=false) - :_opcode(opcode) - ,_len(len) - ,_mask(len && mask) - ,_finished(false) - { - if (data == NULL) - _len = 0; - if (_len) - { - if (_len > 125) - _len = 125; + public: + AsyncWebSocketControl(uint8_t opcode, const uint8_t* data = NULL, size_t len = 0, bool mask = false) + : _opcode(opcode), _len(len), _mask(len && mask), _finished(false) { + if (data == NULL) + _len = 0; + if (_len) { + if (_len > 125) + _len = 125; - _data = (uint8_t*)malloc(_len); + _data = (uint8_t*)malloc(_len); - if(_data == NULL) - _len = 0; - else - memcpy(_data, data, len); - } + if (_data == NULL) + _len = 0; else - _data = NULL; + memcpy(_data, data, len); + } else + _data = NULL; } - virtual ~AsyncWebSocketControl() - { - if (_data != NULL) - free(_data); + virtual ~AsyncWebSocketControl() { + if (_data != NULL) + free(_data); } virtual bool finished() const { return _finished; } - uint8_t opcode(){ return _opcode; } - uint8_t len(){ return _len + 2; } - size_t send(AsyncClient *client){ - _finished = true; - return webSocketSendFrame(client, true, _opcode & 0x0F, _mask, _data, _len); + uint8_t opcode() { return _opcode; } + uint8_t len() { return _len + 2; } + size_t send(AsyncClient* client) { + _finished = true; + return webSocketSendFrame(client, true, _opcode & 0x0F, _mask, _data, _len); } }; - /* * AsyncWebSocketMessage Message */ - -AsyncWebSocketMessage::AsyncWebSocketMessage(AsyncWebSocketSharedBuffer buffer, uint8_t opcode, bool mask) : - _WSbuffer{buffer}, - _opcode(opcode & 0x07), - _mask{mask}, - _status{_WSbuffer?WS_MSG_SENDING:WS_MSG_ERROR} -{ -} - -void AsyncWebSocketMessage::ack(size_t len, uint32_t time) -{ - (void)time; - _acked += len; - if (_sent >= _WSbuffer->size() && _acked >= _ack) - { - _status = WS_MSG_SENT; - } - //ets_printf("A: %u\n", len); +AsyncWebSocketMessage::AsyncWebSocketMessage(AsyncWebSocketSharedBuffer buffer, uint8_t opcode, bool mask) : _WSbuffer{buffer}, + _opcode(opcode & 0x07), + _mask{mask}, + _status{_WSbuffer ? WS_MSG_SENDING : WS_MSG_ERROR} { } -size_t AsyncWebSocketMessage::send(AsyncClient *client) -{ - if (_status != WS_MSG_SENDING) - return 0; - if (_acked < _ack){ - return 0; - } - if (_sent == _WSbuffer->size()) - { - if(_acked == _ack) - _status = WS_MSG_SENT; - return 0; - } - if (_sent > _WSbuffer->size()) - { - _status = WS_MSG_ERROR; - //ets_printf("E: %u > %u\n", _sent, _WSbuffer->length()); - return 0; - } - - size_t toSend = _WSbuffer->size() - _sent; - size_t window = webSocketSendFrameWindow(client); - - if (window < toSend) { - toSend = window; - } - - _sent += toSend; - _ack += toSend + ((toSend < 126)?2:4) + (_mask * 4); - - //ets_printf("W: %u %u\n", _sent - toSend, toSend); - - bool final = (_sent == _WSbuffer->size()); - uint8_t* dPtr = (uint8_t*)(_WSbuffer->data() + (_sent - toSend)); - uint8_t opCode = (toSend && _sent == toSend)?_opcode:(uint8_t)WS_CONTINUATION; - - size_t sent = webSocketSendFrame(client, final, opCode, _mask, dPtr, toSend); - _status = WS_MSG_SENDING; - if (toSend && sent != toSend){ - //ets_printf("E: %u != %u\n", toSend, sent); - _sent -= (toSend - sent); - _ack -= (toSend - sent); - } - //ets_printf("S: %u %u\n", _sent, sent); - return sent; +void AsyncWebSocketMessage::ack(size_t len, uint32_t time) { + (void)time; + _acked += len; + if (_sent >= _WSbuffer->size() && _acked >= _ack) { + _status = WS_MSG_SENT; + } + // ets_printf("A: %u\n", len); } +size_t AsyncWebSocketMessage::send(AsyncClient* client) { + if (_status != WS_MSG_SENDING) + return 0; + if (_acked < _ack) { + return 0; + } + if (_sent == _WSbuffer->size()) { + if (_acked == _ack) + _status = WS_MSG_SENT; + return 0; + } + if (_sent > _WSbuffer->size()) { + _status = WS_MSG_ERROR; + // ets_printf("E: %u > %u\n", _sent, _WSbuffer->length()); + return 0; + } + + size_t toSend = _WSbuffer->size() - _sent; + size_t window = webSocketSendFrameWindow(client); + + if (window < toSend) { + toSend = window; + } + + _sent += toSend; + _ack += toSend + ((toSend < 126) ? 2 : 4) + (_mask * 4); + + // ets_printf("W: %u %u\n", _sent - toSend, toSend); + + bool final = (_sent == _WSbuffer->size()); + uint8_t* dPtr = (uint8_t*)(_WSbuffer->data() + (_sent - toSend)); + uint8_t opCode = (toSend && _sent == toSend) ? _opcode : (uint8_t)WS_CONTINUATION; + + size_t sent = webSocketSendFrame(client, final, opCode, _mask, dPtr, toSend); + _status = WS_MSG_SENDING; + if (toSend && sent != toSend) { + // ets_printf("E: %u != %u\n", toSend, sent); + _sent -= (toSend - sent); + _ack -= (toSend - sent); + } + // ets_printf("S: %u %u\n", _sent, sent); + return sent; +} /* * Async WebSocket Client */ - const char * AWSC_PING_PAYLOAD = "ESPAsyncWebServer-PING"; - const size_t AWSC_PING_PAYLOAD_LEN = 22; +const char* AWSC_PING_PAYLOAD = "ESPAsyncWebServer-PING"; +const size_t AWSC_PING_PAYLOAD_LEN = 22; -AsyncWebSocketClient::AsyncWebSocketClient(AsyncWebServerRequest *request, AsyncWebSocket *server) - : _tempObject(NULL) -{ - _client = request->client(); - _server = server; - _clientId = _server->_getNextId(); - _status = WS_CONNECTED; - _pstate = 0; - _lastMessageTime = millis(); - _keepAlivePeriod = 0; - _client->setRxTimeout(0); - _client->onError([](void *r, AsyncClient* c, int8_t error){ (void)c; ((AsyncWebSocketClient*)(r))->_onError(error); }, this); - _client->onAck([](void *r, AsyncClient* c, size_t len, uint32_t time){ (void)c; ((AsyncWebSocketClient*)(r))->_onAck(len, time); }, this); - _client->onDisconnect([](void *r, AsyncClient* c){ ((AsyncWebSocketClient*)(r))->_onDisconnect(); delete c; }, this); - _client->onTimeout([](void *r, AsyncClient* c, uint32_t time){ (void)c; ((AsyncWebSocketClient*)(r))->_onTimeout(time); }, this); - _client->onData([](void *r, AsyncClient* c, void *buf, size_t len){ (void)c; ((AsyncWebSocketClient*)(r))->_onData(buf, len); }, this); - _client->onPoll([](void *r, AsyncClient* c){ (void)c; ((AsyncWebSocketClient*)(r))->_onPoll(); }, this); - _server->_handleEvent(this, WS_EVT_CONNECT, request, NULL, 0); - delete request; - memset(&_pinfo,0,sizeof(_pinfo)); +AsyncWebSocketClient::AsyncWebSocketClient(AsyncWebServerRequest* request, AsyncWebSocket* server) + : _tempObject(NULL) { + _client = request->client(); + _server = server; + _clientId = _server->_getNextId(); + _status = WS_CONNECTED; + _pstate = 0; + _lastMessageTime = millis(); + _keepAlivePeriod = 0; + _client->setRxTimeout(0); + _client->onError([](void* r, AsyncClient* c, int8_t error) { (void)c; ((AsyncWebSocketClient*)(r))->_onError(error); }, this); + _client->onAck([](void* r, AsyncClient* c, size_t len, uint32_t time) { (void)c; ((AsyncWebSocketClient*)(r))->_onAck(len, time); }, this); + _client->onDisconnect([](void* r, AsyncClient* c) { ((AsyncWebSocketClient*)(r))->_onDisconnect(); delete c; }, this); + _client->onTimeout([](void* r, AsyncClient* c, uint32_t time) { (void)c; ((AsyncWebSocketClient*)(r))->_onTimeout(time); }, this); + _client->onData([](void* r, AsyncClient* c, void* buf, size_t len) { (void)c; ((AsyncWebSocketClient*)(r))->_onData(buf, len); }, this); + _client->onPoll([](void* r, AsyncClient* c) { (void)c; ((AsyncWebSocketClient*)(r))->_onPoll(); }, this); + _server->_handleEvent(this, WS_EVT_CONNECT, request, NULL, 0); + delete request; + memset(&_pinfo, 0, sizeof(_pinfo)); } -AsyncWebSocketClient::~AsyncWebSocketClient() -{ - { - AsyncWebLockGuard l(_lock); - - _messageQueue.clear(); - _controlQueue.clear(); - } - _server->_handleEvent(this, WS_EVT_DISCONNECT, NULL, NULL, 0); +AsyncWebSocketClient::~AsyncWebSocketClient() { + { +#ifdef ESP32 + std::lock_guard lock(_lock); +#endif + _messageQueue.clear(); + _controlQueue.clear(); + } + _server->_handleEvent(this, WS_EVT_DISCONNECT, NULL, NULL, 0); } -void AsyncWebSocketClient::_clearQueue() -{ - while (!_messageQueue.empty() && _messageQueue.front().finished()) - _messageQueue.pop_front(); +void AsyncWebSocketClient::_clearQueue() { + while (!_messageQueue.empty() && _messageQueue.front().finished()) + _messageQueue.pop_front(); } -void AsyncWebSocketClient::_onAck(size_t len, uint32_t time){ - _lastMessageTime = millis(); +void AsyncWebSocketClient::_onAck(size_t len, uint32_t time) { + _lastMessageTime = millis(); - AsyncWebLockGuard l(_lock); +#ifdef ESP32 + std::lock_guard lock(_lock); +#endif - if (!_controlQueue.empty()) { - auto &head = _controlQueue.front(); - if (head.finished()){ - len -= head.len(); - if (_status == WS_DISCONNECTING && head.opcode() == WS_DISCONNECT){ - _controlQueue.pop_front(); - _status = WS_DISCONNECTED; - l.unlock(); - if (_client) _client->close(true); - return; - } - _controlQueue.pop_front(); - } + if (!_controlQueue.empty()) { + auto& head = _controlQueue.front(); + if (head.finished()) { + len -= head.len(); + if (_status == WS_DISCONNECTING && head.opcode() == WS_DISCONNECT) { + _controlQueue.pop_front(); + _status = WS_DISCONNECTED; + if (_client) + _client->close(true); + return; + } + _controlQueue.pop_front(); } + } - if(len && !_messageQueue.empty()){ - _messageQueue.front().ack(len, time); - } + if (len && !_messageQueue.empty()) { + _messageQueue.front().ack(len, time); + } - _clearQueue(); + _clearQueue(); + _runQueue(); +} + +void AsyncWebSocketClient::_onPoll() { + if (!_client) + return; + +#ifdef ESP32 + std::unique_lock lock(_lock); +#endif + if (_client->canSend() && (!_controlQueue.empty() || !_messageQueue.empty())) { + _runQueue(); + } else if (_keepAlivePeriod > 0 && (millis() - _lastMessageTime) >= _keepAlivePeriod && (_controlQueue.empty() && _messageQueue.empty())) { +#ifdef ESP32 + lock.unlock(); +#endif + ping((uint8_t*)AWSC_PING_PAYLOAD, AWSC_PING_PAYLOAD_LEN); + } +} + +void AsyncWebSocketClient::_runQueue() { + // all calls to this method MUST be protected by a mutex lock! + if (!_client) + return; + + _clearQueue(); + + if (!_controlQueue.empty() && (_messageQueue.empty() || _messageQueue.front().betweenFrames()) && webSocketSendFrameWindow(_client) > (size_t)(_controlQueue.front().len() - 1)) { + _controlQueue.front().send(_client); + } else if (!_messageQueue.empty() && _messageQueue.front().betweenFrames() && webSocketSendFrameWindow(_client)) { + _messageQueue.front().send(_client); + } +} + +bool AsyncWebSocketClient::queueIsFull() const { +#ifdef ESP32 + std::lock_guard lock(_lock); +#endif + size_t size = _messageQueue.size(); + ; + return (size >= WS_MAX_QUEUED_MESSAGES) || (_status != WS_CONNECTED); +} + +size_t AsyncWebSocketClient::queueLen() const { +#ifdef ESP32 + std::lock_guard lock(_lock); +#endif + + return _messageQueue.size() + _controlQueue.size(); +} + +bool AsyncWebSocketClient::canSend() const { +#ifdef ESP32 + std::lock_guard lock(_lock); +#endif + return _messageQueue.size() < WS_MAX_QUEUED_MESSAGES; +} + +void AsyncWebSocketClient::_queueControl(uint8_t opcode, const uint8_t* data, size_t len, bool mask) { + if (!_client) + return; + + { +#ifdef ESP32 + std::lock_guard lock(_lock); +#endif + _controlQueue.emplace_back(opcode, data, len, mask); + } + + if (_client && _client->canSend()) _runQueue(); } -void AsyncWebSocketClient::_onPoll() -{ - if (!_client) - return; +void AsyncWebSocketClient::_queueMessage(AsyncWebSocketSharedBuffer buffer, uint8_t opcode, bool mask) { + if (!_client || buffer->size() == 0 || _status != WS_CONNECTED) + return; - AsyncWebLockGuard l(_lock); - if (_client->canSend() && (!_controlQueue.empty() || !_messageQueue.empty())) - { - l.unlock(); - _runQueue(); - } - else if (_keepAlivePeriod > 0 && (millis() - _lastMessageTime) >= _keepAlivePeriod && (_controlQueue.empty() && _messageQueue.empty())) - { - l.unlock(); - ping((uint8_t *)AWSC_PING_PAYLOAD, AWSC_PING_PAYLOAD_LEN); - } -} - -void AsyncWebSocketClient::_runQueue() -{ - if (!_client) - return; - - AsyncWebLockGuard l(_lock); - - _clearQueue(); - - if (!_controlQueue.empty() && (_messageQueue.empty() || _messageQueue.front().betweenFrames()) && webSocketSendFrameWindow(_client) > (size_t)(_controlQueue.front().len() - 1)) - { - //l.unlock(); - _controlQueue.front().send(_client); - } - else if (!_messageQueue.empty() && _messageQueue.front().betweenFrames() && webSocketSendFrameWindow(_client)) - { - //l.unlock(); - _messageQueue.front().send(_client); - } -} - -bool AsyncWebSocketClient::queueIsFull() const -{ - size_t size; - { - AsyncWebLockGuard l(_lock); - size = _messageQueue.size(); - } - return (size >= WS_MAX_QUEUED_MESSAGES) || (_status != WS_CONNECTED); -} - -size_t AsyncWebSocketClient::queueLen() const -{ - AsyncWebLockGuard l(_lock); - - return _messageQueue.size() + _controlQueue.size(); -} - -bool AsyncWebSocketClient::canSend() const -{ - size_t size; - { - AsyncWebLockGuard l(_lock); - size = _messageQueue.size(); - } - return size < WS_MAX_QUEUED_MESSAGES; -} - -void AsyncWebSocketClient::_queueControl(uint8_t opcode, const uint8_t *data, size_t len, bool mask) -{ - if (!_client) - return; - - { - AsyncWebLockGuard l(_lock); - _controlQueue.emplace_back(opcode, data, len, mask); - } - - if (_client && _client->canSend()) - _runQueue(); -} - -void AsyncWebSocketClient::_queueMessage(AsyncWebSocketSharedBuffer buffer, uint8_t opcode, bool mask) -{ - if(_status != WS_CONNECTED) - return; - - if (!_client) - return; - - if (buffer->size() == 0) - return; - - { - AsyncWebLockGuard l(_lock); - if (_messageQueue.size() >= WS_MAX_QUEUED_MESSAGES) - { - l.unlock(); - if(closeWhenFull) - { -#ifdef ESP8266 - ets_printf("AsyncWebSocketClient::_queueMessage: Too many messages queued: closing connection\n"); -#else - log_e("Too many messages queued: closing connection"); +#ifdef ESP32 + std::lock_guard lock(_lock); #endif - _status = WS_DISCONNECTED; - if (_client) _client->close(true); - } else { + if (_messageQueue.size() >= WS_MAX_QUEUED_MESSAGES) { + if (closeWhenFull) { #ifdef ESP8266 - ets_printf("AsyncWebSocketClient::_queueMessage: Too many messages queued: discarding new message\n"); -#else - log_e("Too many messages queued: discarding new message"); + ets_printf("AsyncWebSocketClient::_queueMessage: Too many messages queued: closing connection\n"); +#elif defined(ESP32) + log_e("Too many messages queued: closing connection"); +#endif + _status = WS_DISCONNECTED; + if (_client) + _client->close(true); + } else { +#ifdef ESP8266 + ets_printf("AsyncWebSocketClient::_queueMessage: Too many messages queued: discarding new message\n"); +#elif defined(ESP32) + log_e("Too many messages queued: discarding new message"); #endif - } - return; - } - else - { - _messageQueue.emplace_back(buffer, opcode, mask); - } } + return; + } else { + _messageQueue.emplace_back(buffer, opcode, mask); + } - if (_client && _client->canSend()) - _runQueue(); + if (_client && _client->canSend()) + _runQueue(); } -void AsyncWebSocketClient::close(uint16_t code, const char * message) -{ - if(_status != WS_CONNECTED) - return; +void AsyncWebSocketClient::close(uint16_t code, const char* message) { + if (_status != WS_CONNECTED) + return; - if(code) - { - uint8_t packetLen = 2; - if (message != NULL) - { - size_t mlen = strlen(message); - if(mlen > 123) mlen = 123; - packetLen += mlen; - } - char * buf = (char*)malloc(packetLen); - if (buf != NULL) - { - buf[0] = (uint8_t)(code >> 8); - buf[1] = (uint8_t)(code & 0xFF); - if(message != NULL){ - memcpy(buf+2, message, packetLen -2); - } - _queueControl(WS_DISCONNECT, (uint8_t*)buf, packetLen); - free(buf); - return; - } + if (code) { + uint8_t packetLen = 2; + if (message != NULL) { + size_t mlen = strlen(message); + if (mlen > 123) + mlen = 123; + packetLen += mlen; } - _queueControl(WS_DISCONNECT); + char* buf = (char*)malloc(packetLen); + if (buf != NULL) { + buf[0] = (uint8_t)(code >> 8); + buf[1] = (uint8_t)(code & 0xFF); + if (message != NULL) { + memcpy(buf + 2, message, packetLen - 2); + } + _queueControl(WS_DISCONNECT, (uint8_t*)buf, packetLen); + free(buf); + return; + } + } + _queueControl(WS_DISCONNECT); } -void AsyncWebSocketClient::ping(const uint8_t *data, size_t len) -{ - if (_status == WS_CONNECTED) - _queueControl(WS_PING, data, len); +void AsyncWebSocketClient::ping(const uint8_t* data, size_t len) { + if (_status == WS_CONNECTED) + _queueControl(WS_PING, data, len); } -void AsyncWebSocketClient::_onError(int8_t) -{ - //Serial.println("onErr"); +void AsyncWebSocketClient::_onError(int8_t) { + // Serial.println("onErr"); } -void AsyncWebSocketClient::_onTimeout(uint32_t time) -{ - // Serial.println("onTime"); - (void)time; - _client->close(true); +void AsyncWebSocketClient::_onTimeout(uint32_t time) { + // Serial.println("onTime"); + (void)time; + _client->close(true); } -void AsyncWebSocketClient::_onDisconnect() -{ - // Serial.println("onDis"); - _client = NULL; +void AsyncWebSocketClient::_onDisconnect() { + // Serial.println("onDis"); + _client = NULL; } -void AsyncWebSocketClient::_onData(void *pbuf, size_t plen) -{ +void AsyncWebSocketClient::_onData(void* pbuf, size_t plen) { // Serial.println("onData"); _lastMessageTime = millis(); - uint8_t *data = (uint8_t*)pbuf; - while(plen > 0){ - if(!_pstate){ - const uint8_t *fdata = data; + uint8_t* data = (uint8_t*)pbuf; + while (plen > 0) { + if (!_pstate) { + const uint8_t* fdata = data; _pinfo.index = 0; _pinfo.final = (fdata[0] & 0x80) != 0; _pinfo.opcode = fdata[0] & 0x0F; @@ -550,17 +500,17 @@ void AsyncWebSocketClient::_onData(void *pbuf, size_t plen) _pinfo.len = fdata[1] & 0x7F; data += 2; plen -= 2; - if(_pinfo.len == 126){ + if (_pinfo.len == 126) { _pinfo.len = fdata[3] | (uint16_t)(fdata[2]) << 8; data += 2; plen -= 2; - } else if(_pinfo.len == 127){ + } else if (_pinfo.len == 127) { _pinfo.len = fdata[9] | (uint16_t)(fdata[8]) << 8 | (uint32_t)(fdata[7]) << 16 | (uint32_t)(fdata[6]) << 24 | (uint64_t)(fdata[5]) << 32 | (uint64_t)(fdata[4]) << 40 | (uint64_t)(fdata[3]) << 48 | (uint64_t)(fdata[2]) << 56; data += 8; plen -= 8; } - if(_pinfo.masked){ + if (_pinfo.masked) { memcpy(_pinfo.mask, data, 4); data += 4; plen -= 4; @@ -570,34 +520,35 @@ void AsyncWebSocketClient::_onData(void *pbuf, size_t plen) const size_t datalen = std::min((size_t)(_pinfo.len - _pinfo.index), plen); const auto datalast = data[datalen]; - if(_pinfo.masked){ - for(size_t i=0;i 0) _server->_handleEvent(this, WS_EVT_DATA, (void *)&_pinfo, (uint8_t*)data, datalen); + if (datalen > 0) + _server->_handleEvent(this, WS_EVT_DATA, (void*)&_pinfo, (uint8_t*)data, datalen); _pinfo.index += datalen; - } else if((datalen + _pinfo.index) == _pinfo.len){ + } else if ((datalen + _pinfo.index) == _pinfo.len) { _pstate = 0; - if(_pinfo.opcode == WS_DISCONNECT){ - if(datalen){ + if (_pinfo.opcode == WS_DISCONNECT) { + if (datalen) { uint16_t reasonCode = (uint16_t)(data[0] << 8) + data[1]; - char * reasonString = (char*)(data+2); - if(reasonCode > 1001){ - _server->_handleEvent(this, WS_EVT_ERROR, (void *)&reasonCode, (uint8_t*)reasonString, strlen(reasonString)); + char* reasonString = (char*)(data + 2); + if (reasonCode > 1001) { + _server->_handleEvent(this, WS_EVT_ERROR, (void*)&reasonCode, (uint8_t*)reasonString, strlen(reasonString)); } } - if(_status == WS_DISCONNECTING){ + if (_status == WS_DISCONNECTING) { _status = WS_DISCONNECTED; _client->close(true); } else { @@ -605,19 +556,21 @@ void AsyncWebSocketClient::_onData(void *pbuf, size_t plen) _client->ackLater(); _queueControl(WS_DISCONNECT, data, datalen); } - } else if(_pinfo.opcode == WS_PING){ + } else if (_pinfo.opcode == WS_PING) { _queueControl(WS_PONG, data, datalen); - } else if(_pinfo.opcode == WS_PONG){ - if(datalen != AWSC_PING_PAYLOAD_LEN || memcmp(AWSC_PING_PAYLOAD, data, AWSC_PING_PAYLOAD_LEN) != 0) + } else if (_pinfo.opcode == WS_PONG) { + if (datalen != AWSC_PING_PAYLOAD_LEN || memcmp(AWSC_PING_PAYLOAD, data, AWSC_PING_PAYLOAD_LEN) != 0) _server->_handleEvent(this, WS_EVT_PONG, NULL, data, datalen); - } else if(_pinfo.opcode < 8){//continuation or text/binary frame - _server->_handleEvent(this, WS_EVT_DATA, (void *)&_pinfo, data, datalen); - if (_pinfo.final) _pinfo.num = 0; - else _pinfo.num += 1; + } else if (_pinfo.opcode < 8) { // continuation or text/binary frame + _server->_handleEvent(this, WS_EVT_DATA, (void*)&_pinfo, data, datalen); + if (_pinfo.final) + _pinfo.num = 0; + else + _pinfo.num += 1; } } else { - //os_printf("frame error: len: %u, index: %llu, total: %llu\n", datalen, _pinfo.index, _pinfo.len); - //what should we do? + // os_printf("frame error: len: %u, index: %llu, total: %llu\n", datalen, _pinfo.index, _pinfo.len); + // what should we do? break; } @@ -630,12 +583,11 @@ void AsyncWebSocketClient::_onData(void *pbuf, size_t plen) } } -size_t AsyncWebSocketClient::printf(const char *format, ...) -{ +size_t AsyncWebSocketClient::printf(const char* format, ...) { va_list arg; va_start(arg, format); char* temp = new char[MAX_PRINTF_LEN]; - if(!temp){ + if (!temp) { va_end(arg); return 0; } @@ -646,7 +598,7 @@ size_t AsyncWebSocketClient::printf(const char *format, ...) if (len > (MAX_PRINTF_LEN - 1)) { buffer = new char[len + 1]; if (!buffer) { - delete[] temp; + delete[] temp; return 0; } va_start(arg, format); @@ -662,12 +614,11 @@ size_t AsyncWebSocketClient::printf(const char *format, ...) } #ifndef ESP32 -size_t AsyncWebSocketClient::printf_P(PGM_P formatP, ...) -{ +size_t AsyncWebSocketClient::printf_P(PGM_P formatP, ...) { va_list arg; va_start(arg, formatP); char* temp = new char[MAX_PRINTF_LEN]; - if(!temp){ + if (!temp) { va_end(arg); return 0; } @@ -678,7 +629,7 @@ size_t AsyncWebSocketClient::printf_P(PGM_P formatP, ...) if (len > (MAX_PRINTF_LEN - 1)) { buffer = new char[len + 1]; if (!buffer) { - delete[] temp; + delete[] temp; return 0; } va_start(arg, formatP); @@ -695,453 +646,381 @@ size_t AsyncWebSocketClient::printf_P(PGM_P formatP, ...) #endif namespace { -AsyncWebSocketSharedBuffer makeSharedBuffer(const uint8_t *message, size_t len) -{ + AsyncWebSocketSharedBuffer makeSharedBuffer(const uint8_t* message, size_t len) { auto buffer = std::make_shared>(len); std::memcpy(buffer->data(), message, len); return buffer; -} + } } -void AsyncWebSocketClient::text(AsyncWebSocketMessageBuffer * buffer) -{ - if (buffer) { - text(std::move(buffer->_buffer)); - delete buffer; - } +void AsyncWebSocketClient::text(AsyncWebSocketMessageBuffer* buffer) { + if (buffer) { + text(std::move(buffer->_buffer)); + delete buffer; + } } -void AsyncWebSocketClient::text(AsyncWebSocketSharedBuffer buffer) -{ - _queueMessage(buffer); +void AsyncWebSocketClient::text(AsyncWebSocketSharedBuffer buffer) { + _queueMessage(buffer); } -void AsyncWebSocketClient::text(const uint8_t *message, size_t len) -{ - text(makeSharedBuffer(message, len)); +void AsyncWebSocketClient::text(const uint8_t* message, size_t len) { + text(makeSharedBuffer(message, len)); } -void AsyncWebSocketClient::text(const char *message, size_t len) -{ - text((const uint8_t *)message, len); +void AsyncWebSocketClient::text(const char* message, size_t len) { + text((const uint8_t*)message, len); } -void AsyncWebSocketClient::text(const char *message) -{ - text(message, strlen(message)); +void AsyncWebSocketClient::text(const char* message) { + text(message, strlen(message)); } -void AsyncWebSocketClient::text(const String &message) -{ - text(message.c_str(), message.length()); +void AsyncWebSocketClient::text(const String& message) { + text(message.c_str(), message.length()); } #ifndef ESP32 -void AsyncWebSocketClient::text(const __FlashStringHelper *data) -{ - PGM_P p = reinterpret_cast(data); +void AsyncWebSocketClient::text(const __FlashStringHelper* data) { + PGM_P p = reinterpret_cast(data); - size_t n = 0; - while (1) - { - if (pgm_read_byte(p+n) == 0) break; - n += 1; - } + size_t n = 0; + while (1) { + if (pgm_read_byte(p + n) == 0) + break; + n += 1; + } - char * message = (char*) malloc(n+1); - if(message) - { - memcpy_P(message, p, n); - message[n] = 0; - text(message, n); - free(message); - } + char* message = (char*)malloc(n + 1); + if (message) { + memcpy_P(message, p, n); + message[n] = 0; + text(message, n); + free(message); + } } -#endif // ESP32 +#endif // ESP32 -void AsyncWebSocketClient::binary(AsyncWebSocketMessageBuffer * buffer) -{ - if (buffer) { - binary(std::move(buffer->_buffer)); - delete buffer; - } +void AsyncWebSocketClient::binary(AsyncWebSocketMessageBuffer* buffer) { + if (buffer) { + binary(std::move(buffer->_buffer)); + delete buffer; + } } -void AsyncWebSocketClient::binary(AsyncWebSocketSharedBuffer buffer) -{ - _queueMessage(buffer, WS_BINARY); +void AsyncWebSocketClient::binary(AsyncWebSocketSharedBuffer buffer) { + _queueMessage(buffer, WS_BINARY); } -void AsyncWebSocketClient::binary(const uint8_t *message, size_t len) -{ - binary(makeSharedBuffer(message, len)); +void AsyncWebSocketClient::binary(const uint8_t* message, size_t len) { + binary(makeSharedBuffer(message, len)); } -void AsyncWebSocketClient::binary(const char *message, size_t len) -{ - binary((const uint8_t *)message, len); +void AsyncWebSocketClient::binary(const char* message, size_t len) { + binary((const uint8_t*)message, len); } -void AsyncWebSocketClient::binary(const char *message) -{ - binary(message, strlen(message)); +void AsyncWebSocketClient::binary(const char* message) { + binary(message, strlen(message)); } -void AsyncWebSocketClient::binary(const String &message) -{ - binary(message.c_str(), message.length()); +void AsyncWebSocketClient::binary(const String& message) { + binary(message.c_str(), message.length()); } #ifndef ESP32 -void AsyncWebSocketClient::binary(const __FlashStringHelper *data, size_t len) -{ - PGM_P p = reinterpret_cast(data); - char *message = (char*) malloc(len); - if (message) { - memcpy_P(message, p, len); - binary(message, len); - free(message); - } +void AsyncWebSocketClient::binary(const __FlashStringHelper* data, size_t len) { + PGM_P p = reinterpret_cast(data); + char* message = (char*)malloc(len); + if (message) { + memcpy_P(message, p, len); + binary(message, len); + free(message); + } } #endif -IPAddress AsyncWebSocketClient::remoteIP() const -{ - if (!_client) - return IPAddress((uint32_t)0U); +IPAddress AsyncWebSocketClient::remoteIP() const { + if (!_client) + return IPAddress((uint32_t)0U); - return _client->remoteIP(); + return _client->remoteIP(); } -uint16_t AsyncWebSocketClient::remotePort() const -{ - if(!_client) - return 0; +uint16_t AsyncWebSocketClient::remotePort() const { + if (!_client) + return 0; - return _client->remotePort(); + return _client->remotePort(); } - - /* * Async Web Socket - Each separate socket location */ -void AsyncWebSocket::_handleEvent(AsyncWebSocketClient * client, AwsEventType type, void * arg, uint8_t *data, size_t len){ - if(_eventHandler != NULL){ +void AsyncWebSocket::_handleEvent(AsyncWebSocketClient* client, AwsEventType type, void* arg, uint8_t* data, size_t len) { + if (_eventHandler != NULL) { _eventHandler(this, client, type, arg, data, len); } } -AsyncWebSocketClient *AsyncWebSocket::_newClient(AsyncWebServerRequest *request) -{ - _clients.emplace_back(request, this); - return &_clients.back(); +AsyncWebSocketClient* AsyncWebSocket::_newClient(AsyncWebServerRequest* request) { + _clients.emplace_back(request, this); + return &_clients.back(); } -bool AsyncWebSocket::availableForWriteAll() -{ - return std::none_of(std::begin(_clients), std::end(_clients), - [](const AsyncWebSocketClient &c){ return c.queueIsFull(); }); +bool AsyncWebSocket::availableForWriteAll() { + return std::none_of(std::begin(_clients), std::end(_clients), [](const AsyncWebSocketClient& c) { return c.queueIsFull(); }); } -bool AsyncWebSocket::availableForWrite(uint32_t id) -{ - const auto iter = std::find_if(std::begin(_clients), std::end(_clients), - [id](const AsyncWebSocketClient &c){ return c.id() == id; }); - if (iter == std::end(_clients)) - return true; - return !iter->queueIsFull(); +bool AsyncWebSocket::availableForWrite(uint32_t id) { + const auto iter = std::find_if(std::begin(_clients), std::end(_clients), [id](const AsyncWebSocketClient& c) { return c.id() == id; }); + if (iter == std::end(_clients)) + return true; + return !iter->queueIsFull(); } -size_t AsyncWebSocket::count() const -{ - return std::count_if(std::begin(_clients), std::end(_clients), - [](const AsyncWebSocketClient &c){ return c.status() == WS_CONNECTED; }); +size_t AsyncWebSocket::count() const { + return std::count_if(std::begin(_clients), std::end(_clients), [](const AsyncWebSocketClient& c) { return c.status() == WS_CONNECTED; }); } -AsyncWebSocketClient * AsyncWebSocket::client(uint32_t id) -{ - const auto iter = std::find_if(_clients.begin(), _clients.end(), - [id](const AsyncWebSocketClient &c){ return c.id() == id && c.status() == WS_CONNECTED; }); - if (iter == std::end(_clients)) - return nullptr; +AsyncWebSocketClient* AsyncWebSocket::client(uint32_t id) { + const auto iter = std::find_if(_clients.begin(), _clients.end(), [id](const AsyncWebSocketClient& c) { return c.id() == id && c.status() == WS_CONNECTED; }); + if (iter == std::end(_clients)) + return nullptr; - return &(*iter); + return &(*iter); } - -void AsyncWebSocket::close(uint32_t id, uint16_t code, const char * message) -{ - if (AsyncWebSocketClient *c = client(id)) - c->close(code, message); +void AsyncWebSocket::close(uint32_t id, uint16_t code, const char* message) { + if (AsyncWebSocketClient* c = client(id)) + c->close(code, message); } -void AsyncWebSocket::closeAll(uint16_t code, const char * message) -{ - for (auto &c : _clients) - if (c.status() == WS_CONNECTED) - c.close(code, message); +void AsyncWebSocket::closeAll(uint16_t code, const char* message) { + for (auto& c : _clients) + if (c.status() == WS_CONNECTED) + c.close(code, message); } -void AsyncWebSocket::cleanupClients(uint16_t maxClients) -{ - if (count() > maxClients) - _clients.front().close(); +void AsyncWebSocket::cleanupClients(uint16_t maxClients) { + if (count() > maxClients) + _clients.front().close(); - for (auto iter = std::begin(_clients); iter != std::end(_clients);) - { - if (iter->shouldBeDeleted()) - iter = _clients.erase(iter); - else - iter++; - } + for (auto iter = std::begin(_clients); iter != std::end(_clients);) { + if (iter->shouldBeDeleted()) + iter = _clients.erase(iter); + else + iter++; + } } -void AsyncWebSocket::ping(uint32_t id, const uint8_t *data, size_t len) -{ - if (AsyncWebSocketClient * c = client(id)) - c->ping(data, len); +void AsyncWebSocket::ping(uint32_t id, const uint8_t* data, size_t len) { + if (AsyncWebSocketClient* c = client(id)) + c->ping(data, len); } -void AsyncWebSocket::pingAll(const uint8_t *data, size_t len) -{ - for (auto &c : _clients) - if (c.status() == WS_CONNECTED) - c.ping(data, len); +void AsyncWebSocket::pingAll(const uint8_t* data, size_t len) { + for (auto& c : _clients) + if (c.status() == WS_CONNECTED) + c.ping(data, len); } -void AsyncWebSocket::text(uint32_t id, const uint8_t *message, size_t len) -{ - if (AsyncWebSocketClient * c = client(id)) - c->text(makeSharedBuffer(message, len)); +void AsyncWebSocket::text(uint32_t id, const uint8_t* message, size_t len) { + if (AsyncWebSocketClient* c = client(id)) + c->text(makeSharedBuffer(message, len)); } -void AsyncWebSocket::text(uint32_t id, const char *message, size_t len) -{ - text(id, (const uint8_t *)message, len); +void AsyncWebSocket::text(uint32_t id, const char* message, size_t len) { + text(id, (const uint8_t*)message, len); } -void AsyncWebSocket::text(uint32_t id, const char * message) -{ - text(id, message, strlen(message)); +void AsyncWebSocket::text(uint32_t id, const char* message) { + text(id, message, strlen(message)); } -void AsyncWebSocket::text(uint32_t id, const String &message) -{ - text(id, message.c_str(), message.length()); +void AsyncWebSocket::text(uint32_t id, const String& message) { + text(id, message.c_str(), message.length()); } #ifndef ESP32 -void AsyncWebSocket::text(uint32_t id, const __FlashStringHelper *data) -{ - PGM_P p = reinterpret_cast(data); +void AsyncWebSocket::text(uint32_t id, const __FlashStringHelper* data) { + PGM_P p = reinterpret_cast(data); - size_t n = 0; - while (true) - { - if (pgm_read_byte(p+n) == 0) - break; - n += 1; - } + size_t n = 0; + while (true) { + if (pgm_read_byte(p + n) == 0) + break; + n += 1; + } - char * message = (char*) malloc(n+1); - if (message) - { - memcpy_P(message, p, n); - message[n] = 0; - text(id, message, n); - free(message); - } + char* message = (char*)malloc(n + 1); + if (message) { + memcpy_P(message, p, n); + message[n] = 0; + text(id, message, n); + free(message); + } } -#endif // ESP32 -void AsyncWebSocket::text(uint32_t id, AsyncWebSocketMessageBuffer *buffer) -{ - if (buffer) { - text(id, std::move(buffer->_buffer)); - delete buffer; - } +#endif // ESP32 +void AsyncWebSocket::text(uint32_t id, AsyncWebSocketMessageBuffer* buffer) { + if (buffer) { + text(id, std::move(buffer->_buffer)); + delete buffer; + } } -void AsyncWebSocket::text(uint32_t id, AsyncWebSocketSharedBuffer buffer) -{ - if (AsyncWebSocketClient *c = client(id)) - c->text(buffer); +void AsyncWebSocket::text(uint32_t id, AsyncWebSocketSharedBuffer buffer) { + if (AsyncWebSocketClient* c = client(id)) + c->text(buffer); } -void AsyncWebSocket::textAll(const uint8_t *message, size_t len) -{ - textAll(makeSharedBuffer(message, len)); +void AsyncWebSocket::textAll(const uint8_t* message, size_t len) { + textAll(makeSharedBuffer(message, len)); } -void AsyncWebSocket::textAll(const char * message, size_t len) -{ - textAll((const uint8_t *)message, len); +void AsyncWebSocket::textAll(const char* message, size_t len) { + textAll((const uint8_t*)message, len); } -void AsyncWebSocket::textAll(const char *message) -{ - textAll(message, strlen(message)); +void AsyncWebSocket::textAll(const char* message) { + textAll(message, strlen(message)); } -void AsyncWebSocket::textAll(const String &message) -{ - textAll(message.c_str(), message.length()); +void AsyncWebSocket::textAll(const String& message) { + textAll(message.c_str(), message.length()); } #ifndef ESP32 -void AsyncWebSocket::textAll(const __FlashStringHelper *data) -{ - PGM_P p = reinterpret_cast(data); +void AsyncWebSocket::textAll(const __FlashStringHelper* data) { + PGM_P p = reinterpret_cast(data); - size_t n = 0; - while (1) - { - if (pgm_read_byte(p+n) == 0) break; - n += 1; - } + size_t n = 0; + while (1) { + if (pgm_read_byte(p + n) == 0) + break; + n += 1; + } - char *message = (char*)malloc(n+1); - if(message) - { - memcpy_P(message, p, n); - message[n] = 0; - textAll(message, n); - free(message); - } + char* message = (char*)malloc(n + 1); + if (message) { + memcpy_P(message, p, n); + message[n] = 0; + textAll(message, n); + free(message); + } } -#endif // ESP32 -void AsyncWebSocket::textAll(AsyncWebSocketMessageBuffer * buffer) -{ - if (buffer) { - textAll(std::move(buffer->_buffer)); - delete buffer; - } +#endif // ESP32 +void AsyncWebSocket::textAll(AsyncWebSocketMessageBuffer* buffer) { + if (buffer) { + textAll(std::move(buffer->_buffer)); + delete buffer; + } } -void AsyncWebSocket::textAll(AsyncWebSocketSharedBuffer buffer) -{ - for (auto &c : _clients) - if (c.status() == WS_CONNECTED) - c.text(buffer); +void AsyncWebSocket::textAll(AsyncWebSocketSharedBuffer buffer) { + for (auto& c : _clients) + if (c.status() == WS_CONNECTED) + c.text(buffer); } -void AsyncWebSocket::binary(uint32_t id, const uint8_t *message, size_t len) -{ - if (AsyncWebSocketClient *c = client(id)) - c->binary(makeSharedBuffer(message, len)); +void AsyncWebSocket::binary(uint32_t id, const uint8_t* message, size_t len) { + if (AsyncWebSocketClient* c = client(id)) + c->binary(makeSharedBuffer(message, len)); } -void AsyncWebSocket::binary(uint32_t id, const char * message, size_t len) -{ - binary(id, (const uint8_t *)message, len); +void AsyncWebSocket::binary(uint32_t id, const char* message, size_t len) { + binary(id, (const uint8_t*)message, len); } -void AsyncWebSocket::binary(uint32_t id, const char * message) -{ - binary(id, message, strlen(message)); +void AsyncWebSocket::binary(uint32_t id, const char* message) { + binary(id, message, strlen(message)); } -void AsyncWebSocket::binary(uint32_t id, const String &message) -{ - binary(id, message.c_str(), message.length()); +void AsyncWebSocket::binary(uint32_t id, const String& message) { + binary(id, message.c_str(), message.length()); } #ifndef ESP32 -void AsyncWebSocket::binary(uint32_t id, const __FlashStringHelper *data, size_t len) -{ - PGM_P p = reinterpret_cast(data); - char *message = (char*) malloc(len); - if (message) - { - memcpy_P(message, p, len); - binary(id, message, len); - free(message); - } +void AsyncWebSocket::binary(uint32_t id, const __FlashStringHelper* data, size_t len) { + PGM_P p = reinterpret_cast(data); + char* message = (char*)malloc(len); + if (message) { + memcpy_P(message, p, len); + binary(id, message, len); + free(message); + } } -#endif // ESP32 -void AsyncWebSocket::binary(uint32_t id, AsyncWebSocketMessageBuffer *buffer) -{ - if (buffer) { - binary(id, std::move(buffer->_buffer)); - delete buffer; - } +#endif // ESP32 +void AsyncWebSocket::binary(uint32_t id, AsyncWebSocketMessageBuffer* buffer) { + if (buffer) { + binary(id, std::move(buffer->_buffer)); + delete buffer; + } } -void AsyncWebSocket::binary(uint32_t id, AsyncWebSocketSharedBuffer buffer) -{ - if (AsyncWebSocketClient *c = client(id)) - c->binary(buffer); +void AsyncWebSocket::binary(uint32_t id, AsyncWebSocketSharedBuffer buffer) { + if (AsyncWebSocketClient* c = client(id)) + c->binary(buffer); } - -void AsyncWebSocket::binaryAll(const uint8_t *message, size_t len) -{ - binaryAll(makeSharedBuffer(message, len)); +void AsyncWebSocket::binaryAll(const uint8_t* message, size_t len) { + binaryAll(makeSharedBuffer(message, len)); } -void AsyncWebSocket::binaryAll(const char *message, size_t len) -{ - binaryAll((const uint8_t *)message, len); +void AsyncWebSocket::binaryAll(const char* message, size_t len) { + binaryAll((const uint8_t*)message, len); } -void AsyncWebSocket::binaryAll(const char *message) -{ - binaryAll(message, strlen(message)); +void AsyncWebSocket::binaryAll(const char* message) { + binaryAll(message, strlen(message)); } -void AsyncWebSocket::binaryAll(const String &message) -{ - binaryAll(message.c_str(), message.length()); +void AsyncWebSocket::binaryAll(const String& message) { + binaryAll(message.c_str(), message.length()); } #ifndef ESP32 -void AsyncWebSocket::binaryAll(const __FlashStringHelper *data, size_t len) -{ - PGM_P p = reinterpret_cast(data); - char * message = (char*) malloc(len); - if(message) - { - memcpy_P(message, p, len); - binaryAll(message, len); - free(message); - } +void AsyncWebSocket::binaryAll(const __FlashStringHelper* data, size_t len) { + PGM_P p = reinterpret_cast(data); + char* message = (char*)malloc(len); + if (message) { + memcpy_P(message, p, len); + binaryAll(message, len); + free(message); + } } -#endif // ESP32 -void AsyncWebSocket::binaryAll(AsyncWebSocketMessageBuffer * buffer) -{ - if (buffer) { - binaryAll(std::move(buffer->_buffer)); - delete buffer; - } +#endif // ESP32 +void AsyncWebSocket::binaryAll(AsyncWebSocketMessageBuffer* buffer) { + if (buffer) { + binaryAll(std::move(buffer->_buffer)); + delete buffer; + } } -void AsyncWebSocket::binaryAll(AsyncWebSocketSharedBuffer buffer) -{ - for (auto &c : _clients) - if (c.status() == WS_CONNECTED) - c.binary(buffer); +void AsyncWebSocket::binaryAll(AsyncWebSocketSharedBuffer buffer) { + for (auto& c : _clients) + if (c.status() == WS_CONNECTED) + c.binary(buffer); } -size_t AsyncWebSocket::printf(uint32_t id, const char *format, ...){ - AsyncWebSocketClient * c = client(id); - if (c) - { - va_list arg; - va_start(arg, format); - size_t len = c->printf(format, arg); - va_end(arg); - return len; - } - return 0; -} - -size_t AsyncWebSocket::printfAll(const char *format, ...) -{ +size_t AsyncWebSocket::printf(uint32_t id, const char* format, ...) { + AsyncWebSocketClient* c = client(id); + if (c) { va_list arg; - char *temp = new char[MAX_PRINTF_LEN]; - if (!temp) - return 0; - va_start(arg, format); - size_t len = vsnprintf(temp, MAX_PRINTF_LEN, format, arg); + size_t len = c->printf(format, arg); va_end(arg); - delete[] temp; - - AsyncWebSocketSharedBuffer buffer = std::make_shared>(len); - - va_start(arg, format); - vsnprintf( (char *)buffer->data(), len + 1, format, arg); - va_end(arg); - - textAll(buffer); return len; + } + return 0; +} + +size_t AsyncWebSocket::printfAll(const char* format, ...) { + va_list arg; + char* temp = new char[MAX_PRINTF_LEN]; + if (!temp) + return 0; + + va_start(arg, format); + size_t len = vsnprintf(temp, MAX_PRINTF_LEN, format, arg); + va_end(arg); + delete[] temp; + + AsyncWebSocketSharedBuffer buffer = std::make_shared>(len); + + va_start(arg, format); + vsnprintf((char*)buffer->data(), len + 1, format, arg); + va_end(arg); + + textAll(buffer); + return len; } #ifndef ESP32 -size_t AsyncWebSocket::printf_P(uint32_t id, PGM_P formatP, ...){ - AsyncWebSocketClient * c = client(id); - if(c != NULL){ +size_t AsyncWebSocket::printf_P(uint32_t id, PGM_P formatP, ...) { + AsyncWebSocketClient* c = client(id); + if (c != NULL) { va_list arg; va_start(arg, formatP); size_t len = c->printf_P(formatP, arg); @@ -1150,127 +1029,117 @@ size_t AsyncWebSocket::printf_P(uint32_t id, PGM_P formatP, ...){ } return 0; } + +size_t AsyncWebSocket::printfAll_P(PGM_P formatP, ...) { + va_list arg; + char* temp = new char[MAX_PRINTF_LEN]; + if (!temp) + return 0; + + va_start(arg, formatP); + size_t len = vsnprintf_P(temp, MAX_PRINTF_LEN, formatP, arg); + va_end(arg); + delete[] temp; + + AsyncWebSocketSharedBuffer buffer = std::make_shared>(len + 1); + + va_start(arg, formatP); + vsnprintf_P((char*)buffer->data(), len + 1, formatP, arg); + va_end(arg); + + textAll(buffer); + return len; +} #endif -size_t AsyncWebSocket::printfAll_P(PGM_P formatP, ...) -{ - va_list arg; - char *temp = new char[MAX_PRINTF_LEN]; - if (!temp) - return 0; - - va_start(arg, formatP); - size_t len = vsnprintf_P(temp, MAX_PRINTF_LEN, formatP, arg); - va_end(arg); - delete[] temp; - - AsyncWebSocketSharedBuffer buffer = std::make_shared>(len + 1); - - va_start(arg, formatP); - vsnprintf_P((char *)buffer->data(), len + 1, formatP, arg); - va_end(arg); - - textAll(buffer); - return len; -} - -const char __WS_STR_CONNECTION[] PROGMEM = { "Connection" }; -const char __WS_STR_UPGRADE[] PROGMEM = { "Upgrade" }; -const char __WS_STR_ORIGIN[] PROGMEM = { "Origin" }; -const char __WS_STR_COOKIE[] PROGMEM = { "Cookie" }; -const char __WS_STR_VERSION[] PROGMEM = { "Sec-WebSocket-Version" }; -const char __WS_STR_KEY[] PROGMEM = { "Sec-WebSocket-Key" }; -const char __WS_STR_PROTOCOL[] PROGMEM = { "Sec-WebSocket-Protocol" }; -const char __WS_STR_ACCEPT[] PROGMEM = { "Sec-WebSocket-Accept" }; -const char __WS_STR_UUID[] PROGMEM = { "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" }; +const char __WS_STR_CONNECTION[] PROGMEM = {"Connection"}; +const char __WS_STR_UPGRADE[] PROGMEM = {"Upgrade"}; +const char __WS_STR_ORIGIN[] PROGMEM = {"Origin"}; +const char __WS_STR_COOKIE[] PROGMEM = {"Cookie"}; +const char __WS_STR_VERSION[] PROGMEM = {"Sec-WebSocket-Version"}; +const char __WS_STR_KEY[] PROGMEM = {"Sec-WebSocket-Key"}; +const char __WS_STR_PROTOCOL[] PROGMEM = {"Sec-WebSocket-Protocol"}; +const char __WS_STR_ACCEPT[] PROGMEM = {"Sec-WebSocket-Accept"}; +const char __WS_STR_UUID[] PROGMEM = {"258EAFA5-E914-47DA-95CA-C5AB0DC85B11"}; #define WS_STR_UUID_LEN 36 #define WS_STR_CONNECTION FPSTR(__WS_STR_CONNECTION) -#define WS_STR_UPGRADE FPSTR(__WS_STR_UPGRADE) -#define WS_STR_ORIGIN FPSTR(__WS_STR_ORIGIN) -#define WS_STR_COOKIE FPSTR(__WS_STR_COOKIE) -#define WS_STR_VERSION FPSTR(__WS_STR_VERSION) -#define WS_STR_KEY FPSTR(__WS_STR_KEY) -#define WS_STR_PROTOCOL FPSTR(__WS_STR_PROTOCOL) -#define WS_STR_ACCEPT FPSTR(__WS_STR_ACCEPT) -#define WS_STR_UUID FPSTR(__WS_STR_UUID) +#define WS_STR_UPGRADE FPSTR(__WS_STR_UPGRADE) +#define WS_STR_ORIGIN FPSTR(__WS_STR_ORIGIN) +#define WS_STR_COOKIE FPSTR(__WS_STR_COOKIE) +#define WS_STR_VERSION FPSTR(__WS_STR_VERSION) +#define WS_STR_KEY FPSTR(__WS_STR_KEY) +#define WS_STR_PROTOCOL FPSTR(__WS_STR_PROTOCOL) +#define WS_STR_ACCEPT FPSTR(__WS_STR_ACCEPT) +#define WS_STR_UUID FPSTR(__WS_STR_UUID) -bool AsyncWebSocket::canHandle(AsyncWebServerRequest *request){ - if(!_enabled) - return false; +bool AsyncWebSocket::canHandle(AsyncWebServerRequest* request) { + if (!_enabled) + return false; - if(request->method() != HTTP_GET || !request->url().equals(_url) || !request->isExpectedRequestedConnType(RCT_WS)) - return false; + if (request->method() != HTTP_GET || !request->url().equals(_url) || !request->isExpectedRequestedConnType(RCT_WS)) + return false; - request->addInterestingHeader(WS_STR_CONNECTION); - request->addInterestingHeader(WS_STR_UPGRADE); - request->addInterestingHeader(WS_STR_ORIGIN); - request->addInterestingHeader(WS_STR_COOKIE); - request->addInterestingHeader(WS_STR_VERSION); - request->addInterestingHeader(WS_STR_KEY); - request->addInterestingHeader(WS_STR_PROTOCOL); - return true; + request->addInterestingHeader(WS_STR_CONNECTION); + request->addInterestingHeader(WS_STR_UPGRADE); + request->addInterestingHeader(WS_STR_ORIGIN); + request->addInterestingHeader(WS_STR_COOKIE); + request->addInterestingHeader(WS_STR_VERSION); + request->addInterestingHeader(WS_STR_KEY); + request->addInterestingHeader(WS_STR_PROTOCOL); + return true; } -void AsyncWebSocket::handleRequest(AsyncWebServerRequest *request) -{ - if (!request->hasHeader(WS_STR_VERSION) || !request->hasHeader(WS_STR_KEY)) - { - request->send(400); - return; - } - if ((_username.length() && _password.length()) && !request->authenticate(_username.c_str(), _password.c_str())) - { - return request->requestAuthentication(); - } - if (_handshakeHandler != nullptr){ - if(!_handshakeHandler(request)){ - request->send(401); - return; - } - } - AsyncWebHeader* version = request->getHeader(WS_STR_VERSION); - if (version->value().toInt() != 13) - { - AsyncWebServerResponse *response = request->beginResponse(400); - response->addHeader(WS_STR_VERSION, F("13")); - request->send(response); - return; - } - AsyncWebHeader* key = request->getHeader(WS_STR_KEY); - AsyncWebServerResponse *response = new AsyncWebSocketResponse(key->value(), this); - if (request->hasHeader(WS_STR_PROTOCOL)) - { - AsyncWebHeader* protocol = request->getHeader(WS_STR_PROTOCOL); - //ToDo: check protocol - response->addHeader(WS_STR_PROTOCOL, protocol->value()); +void AsyncWebSocket::handleRequest(AsyncWebServerRequest* request) { + if (!request->hasHeader(WS_STR_VERSION) || !request->hasHeader(WS_STR_KEY)) { + request->send(400); + return; + } + if ((_username.length() && _password.length()) && !request->authenticate(_username.c_str(), _password.c_str())) { + return request->requestAuthentication(); + } + if (_handshakeHandler != nullptr) { + if (!_handshakeHandler(request)) { + request->send(401); + return; } + } + const AsyncWebHeader* version = request->getHeader(WS_STR_VERSION); + if (version->value().toInt() != 13) { + AsyncWebServerResponse* response = request->beginResponse(400); + response->addHeader(WS_STR_VERSION, F("13")); request->send(response); + return; + } + const AsyncWebHeader* key = request->getHeader(WS_STR_KEY); + AsyncWebServerResponse* response = new AsyncWebSocketResponse(key->value(), this); + if (request->hasHeader(WS_STR_PROTOCOL)) { + const AsyncWebHeader* protocol = request->getHeader(WS_STR_PROTOCOL); + // ToDo: check protocol + response->addHeader(WS_STR_PROTOCOL, protocol->value()); + } + request->send(response); } -AsyncWebSocketMessageBuffer * AsyncWebSocket::makeBuffer(size_t size) -{ - AsyncWebSocketMessageBuffer * buffer = new AsyncWebSocketMessageBuffer(size); - if (buffer->length() != size) - { - delete buffer; - return nullptr; - } else { - return buffer; - } +AsyncWebSocketMessageBuffer* AsyncWebSocket::makeBuffer(size_t size) { + AsyncWebSocketMessageBuffer* buffer = new AsyncWebSocketMessageBuffer(size); + if (buffer->length() != size) { + delete buffer; + return nullptr; + } else { + return buffer; + } } -AsyncWebSocketMessageBuffer * AsyncWebSocket::makeBuffer(const uint8_t * data, size_t size) -{ - AsyncWebSocketMessageBuffer * buffer = new AsyncWebSocketMessageBuffer(data, size); - if (buffer->length() != size) - { - delete buffer; - return nullptr; - } else { - return buffer; - } +AsyncWebSocketMessageBuffer* AsyncWebSocket::makeBuffer(const uint8_t* data, size_t size) { + AsyncWebSocketMessageBuffer* buffer = new AsyncWebSocketMessageBuffer(data, size); + if (buffer->length() != size) { + delete buffer; + return nullptr; + } else { + return buffer; + } } /* @@ -1278,55 +1147,51 @@ AsyncWebSocketMessageBuffer * AsyncWebSocket::makeBuffer(const uint8_t * data, s * Authentication code from https://github.com/Links2004/arduinoWebSockets/blob/master/src/WebSockets.cpp#L480 */ -AsyncWebSocketResponse::AsyncWebSocketResponse(const String& key, AsyncWebSocket *server) -{ - _server = server; - _code = 101; - _sendContentLength = false; +AsyncWebSocketResponse::AsyncWebSocketResponse(const String& key, AsyncWebSocket* server) { + _server = server; + _code = 101; + _sendContentLength = false; - uint8_t hash[20]; - char buffer[33]; + uint8_t hash[20]; + char buffer[33]; -#ifdef ESP8266 - sha1(key + WS_STR_UUID, hash); +#if defined(ESP8266) || defined(TARGET_RP2040) + sha1(key + WS_STR_UUID, hash); #else - String k; - k.reserve(key.length() + WS_STR_UUID_LEN); - k.concat(key); - k.concat(WS_STR_UUID); - SHA1Builder sha1; - sha1.begin(); - sha1.add((const uint8_t*)k.c_str(), k.length()); - sha1.calculate(); - sha1.getBytes(hash); + String k; + k.reserve(key.length() + WS_STR_UUID_LEN); + k.concat(key); + k.concat(WS_STR_UUID); + SHA1Builder sha1; + sha1.begin(); + sha1.add((const uint8_t*)k.c_str(), k.length()); + sha1.calculate(); + sha1.getBytes(hash); #endif - base64_encodestate _state; - base64_init_encodestate(&_state); - int len = base64_encode_block((const char *) hash, 20, buffer, &_state); - len = base64_encode_blockend((buffer + len), &_state); - addHeader(WS_STR_CONNECTION, WS_STR_UPGRADE); - addHeader(WS_STR_UPGRADE, F("websocket")); - addHeader(WS_STR_ACCEPT,buffer); + base64_encodestate _state; + base64_init_encodestate(&_state); + int len = base64_encode_block((const char*)hash, 20, buffer, &_state); + len = base64_encode_blockend((buffer + len), &_state); + addHeader(WS_STR_CONNECTION, WS_STR_UPGRADE); + addHeader(WS_STR_UPGRADE, F("websocket")); + addHeader(WS_STR_ACCEPT, buffer); } -void AsyncWebSocketResponse::_respond(AsyncWebServerRequest *request) -{ - if(_state == RESPONSE_FAILED) - { - request->client()->close(true); - return; - } - String out(_assembleHead(request->version())); - request->client()->write(out.c_str(), _headLength); - _state = RESPONSE_WAIT_ACK; +void AsyncWebSocketResponse::_respond(AsyncWebServerRequest* request) { + if (_state == RESPONSE_FAILED) { + request->client()->close(true); + return; + } + String out(_assembleHead(request->version())); + request->client()->write(out.c_str(), _headLength); + _state = RESPONSE_WAIT_ACK; } -size_t AsyncWebSocketResponse::_ack(AsyncWebServerRequest *request, size_t len, uint32_t time) -{ - (void)time; +size_t AsyncWebSocketResponse::_ack(AsyncWebServerRequest* request, size_t len, uint32_t time) { + (void)time; - if(len) - _server->_newClient(request); + if (len) + _server->_newClient(request); - return 0; + return 0; } diff --git a/src/AsyncWebSocket.h b/src/AsyncWebSocket.h index cde0b43..77bf7ee 100644 --- a/src/AsyncWebSocket.h +++ b/src/AsyncWebSocket.h @@ -23,37 +23,42 @@ #include #ifdef ESP32 -#include -#ifndef WS_MAX_QUEUED_MESSAGES -#define WS_MAX_QUEUED_MESSAGES 32 -#endif -#else -#include -#ifndef WS_MAX_QUEUED_MESSAGES -#define WS_MAX_QUEUED_MESSAGES 8 -#endif + #include + #include + #ifndef WS_MAX_QUEUED_MESSAGES + #define WS_MAX_QUEUED_MESSAGES 32 + #endif +#elif defined(ESP8266) + #include + #ifndef WS_MAX_QUEUED_MESSAGES + #define WS_MAX_QUEUED_MESSAGES 8 + #endif +#elif defined(TARGET_RP2040) + #include + #ifndef WS_MAX_QUEUED_MESSAGES + #define WS_MAX_QUEUED_MESSAGES 32 + #endif #endif + #include -#include "AsyncWebSynchronization.h" - -#include #include +#include #include #ifdef ESP8266 -#include -#ifdef CRYPTO_HASH_h // include Hash.h from espressif framework if the first include was from the crypto library -#include <../src/Hash.h> -#endif + #include + #ifdef CRYPTO_HASH_h // include Hash.h from espressif framework if the first include was from the crypto library + #include <../src/Hash.h> + #endif #endif #ifndef DEFAULT_MAX_WS_CLIENTS -#ifdef ESP32 -#define DEFAULT_MAX_WS_CLIENTS 8 -#else -#define DEFAULT_MAX_WS_CLIENTS 4 -#endif + #ifdef ESP32 + #define DEFAULT_MAX_WS_CLIENTS 8 + #else + #define DEFAULT_MAX_WS_CLIENTS 4 + #endif #endif using AsyncWebSocketSharedBuffer = std::shared_ptr>; @@ -87,20 +92,33 @@ typedef struct { uint64_t index; } AwsFrameInfo; -typedef enum { WS_DISCONNECTED, WS_CONNECTED, WS_DISCONNECTING } AwsClientStatus; -typedef enum { WS_CONTINUATION, WS_TEXT, WS_BINARY, WS_DISCONNECT = 0x08, WS_PING, WS_PONG } AwsFrameType; -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_DISCONNECTED, + WS_CONNECTED, + WS_DISCONNECTING } AwsClientStatus; +typedef enum { WS_CONTINUATION, + WS_TEXT, + WS_BINARY, + WS_DISCONNECT = 0x08, + WS_PING, + WS_PONG } AwsFrameType; +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; class AsyncWebSocketMessageBuffer { - friend AsyncWebSocket; - friend AsyncWebSocketClient; + friend AsyncWebSocket; + friend AsyncWebSocketClient; private: AsyncWebSocketSharedBuffer _buffer; public: - AsyncWebSocketMessageBuffer(){} + AsyncWebSocketMessageBuffer() {} explicit AsyncWebSocketMessageBuffer(size_t size); AsyncWebSocketMessageBuffer(const uint8_t* data, size_t size); //~AsyncWebSocketMessageBuffer(); @@ -109,9 +127,8 @@ class AsyncWebSocketMessageBuffer { size_t length() const { return _buffer->size(); } }; -class AsyncWebSocketMessage -{ -private: +class AsyncWebSocketMessage { + private: AsyncWebSocketSharedBuffer _WSbuffer; uint8_t _opcode{WS_TEXT}; bool _mask{false}; @@ -120,25 +137,25 @@ private: size_t _ack{}; size_t _acked{}; -public: - AsyncWebSocketMessage(AsyncWebSocketSharedBuffer buffer, uint8_t opcode=WS_TEXT, bool mask=false); + public: + AsyncWebSocketMessage(AsyncWebSocketSharedBuffer buffer, uint8_t opcode = WS_TEXT, bool mask = false); bool finished() const { return _status != WS_MSG_SENDING; } bool betweenFrames() const { return _acked == _ack; } void ack(size_t len, uint32_t time); - size_t send(AsyncClient *client); + size_t send(AsyncClient* client); }; class AsyncWebSocketClient { private: - AsyncClient *_client; - AsyncWebSocket *_server; + AsyncClient* _client; + AsyncWebSocket* _server; uint32_t _clientId; AwsClientStatus _status; - - AsyncWebLock _lock; - +#ifdef ESP32 + mutable std::mutex _lock; +#endif std::deque _controlQueue; std::deque _messageQueue; bool closeWhenFull = true; @@ -149,223 +166,225 @@ class AsyncWebSocketClient { uint32_t _lastMessageTime; uint32_t _keepAlivePeriod; - void _queueControl(uint8_t opcode, const uint8_t *data=NULL, size_t len=0, bool mask=false); - void _queueMessage(AsyncWebSocketSharedBuffer buffer, uint8_t opcode=WS_TEXT, bool mask=false); + void _queueControl(uint8_t opcode, const uint8_t* data = NULL, size_t len = 0, bool mask = false); + void _queueMessage(AsyncWebSocketSharedBuffer buffer, uint8_t opcode = WS_TEXT, bool mask = false); void _runQueue(); void _clearQueue(); public: - void *_tempObject; + void* _tempObject; - AsyncWebSocketClient(AsyncWebServerRequest *request, AsyncWebSocket *server); + AsyncWebSocketClient(AsyncWebServerRequest* request, AsyncWebSocket* server); ~AsyncWebSocketClient(); - //client id increments for the given server + // client id increments for the given server uint32_t id() const { return _clientId; } AwsClientStatus status() const { return _status; } AsyncClient* client() { return _client; } const AsyncClient* client() const { return _client; } - AsyncWebSocket *server(){ return _server; } - const AsyncWebSocket *server() const { return _server; } - AwsFrameInfo const &pinfo() const { return _pinfo; } + AsyncWebSocket* server() { return _server; } + const AsyncWebSocket* server() const { return _server; } + AwsFrameInfo const& pinfo() const { return _pinfo; } // - If "true" (default), the connection will be closed if the message queue is full. // This is the default behavior in yubox-node-org, which is not silently discarding messages but instead closes the connection. - // The big issue with this behavior is that is can cause the UI to automatically re-create a new WS connection, which can be filled again, + // The big issue with this behavior is that is can cause the UI to automatically re-create a new WS connection, which can be filled again, // and so on, causing a resource exhaustion. - // + // // - If "false", the incoming message will be discarded if the queue is full. // This is the default behavior in the original ESPAsyncWebServer library from me-no-dev. // This behavior allows the best performance at the expense of unreliable message delivery in case the queue is full (some messages may be lost). - // + // // - In any case, when the queue is full, a message is logged. // - IT is recommended to use the methods queueIsFull(), availableForWriteAll(), availableForWrite(clientId) to check if the queue is full before sending a message. - // + // // Usage: // - can be set in the onEvent listener when connecting (event type is: WS_EVT_CONNECT) - // - // Use cases:, + // + // Use cases:, // - if using websocket to send logging messages, maybe some loss is acceptable. // - But if using websocket to send UI update messages, maybe the connection should be closed and the UI redrawn. void setCloseClientOnQueueFull(bool close) { closeWhenFull = close; } bool willCloseClientOnQueueFull() const { return closeWhenFull; } IPAddress remoteIP() const; - uint16_t remotePort() const; + uint16_t remotePort() const; bool shouldBeDeleted() const { return !_client; } - //control frames - void close(uint16_t code=0, const char * message=NULL); - void ping(const uint8_t *data=NULL, size_t len=0); + // control frames + void close(uint16_t code = 0, const char* message = NULL); + void ping(const uint8_t* data = NULL, size_t len = 0); - //set auto-ping period in seconds. disabled if zero (default) - void keepAlivePeriod(uint16_t seconds){ + // set auto-ping period in seconds. disabled if zero (default) + void keepAlivePeriod(uint16_t seconds) { _keepAlivePeriod = seconds * 1000; } - uint16_t keepAlivePeriod(){ + uint16_t keepAlivePeriod() { return (uint16_t)(_keepAlivePeriod / 1000); } - //data packets - void message(AsyncWebSocketSharedBuffer buffer, uint8_t opcode=WS_TEXT, bool mask=false) { _queueMessage(buffer, opcode, mask); } + // data packets + void message(AsyncWebSocketSharedBuffer 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 - 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 void text(AsyncWebSocketSharedBuffer buffer); - void text(const uint8_t *message, size_t len); - void text(const char *message, size_t len); - void text(const char *message); - void text(const String &message); + void text(const uint8_t* message, size_t len); + void text(const char* message, size_t len); + void text(const char* message); + void text(const String& message); #ifndef ESP32 - void text(const __FlashStringHelper *message); -#endif // ESP32 - void text(AsyncWebSocketMessageBuffer *buffer); + void text(const __FlashStringHelper* message); +#endif // ESP32 + void text(AsyncWebSocketMessageBuffer* buffer); void binary(AsyncWebSocketSharedBuffer buffer); - void binary(const uint8_t *message, size_t len); - void binary(const char * message, size_t len); - void binary(const char * message); - void binary(const String &message); + void binary(const uint8_t* message, size_t len); + void binary(const char* message, size_t len); + void binary(const char* message); + void binary(const String& message); #ifndef ESP32 - void binary(const __FlashStringHelper *message, size_t len); -#endif // ESP32 - void binary(AsyncWebSocketMessageBuffer *buffer); + void binary(const __FlashStringHelper* message, size_t len); +#endif // ESP32 + void binary(AsyncWebSocketMessageBuffer* buffer); bool canSend() const; - //system callbacks (do not call) + // system callbacks (do not call) void _onAck(size_t len, uint32_t time); void _onError(int8_t); void _onPoll(); void _onTimeout(uint32_t time); void _onDisconnect(); - void _onData(void *pbuf, size_t plen); + void _onData(void* pbuf, size_t plen); }; -using AwsHandshakeHandler = std::function; -using AwsEventHandler = std::function; +using AwsHandshakeHandler = std::function; +using AwsEventHandler = std::function; -//WebServer Handler implementation that plays the role of a socket server -class AsyncWebSocket: public AsyncWebHandler { +// WebServer Handler implementation that plays the role of a socket server +class AsyncWebSocket : public AsyncWebHandler { private: String _url; std::list _clients; uint32_t _cNextId; AwsEventHandler _eventHandler{nullptr}; - AwsHandshakeHandler _handshakeHandler; + AwsHandshakeHandler _handshakeHandler; bool _enabled; - AsyncWebLock _lock; +#ifdef ESP32 + mutable std::mutex _lock; +#endif public: - explicit AsyncWebSocket(const char* url) : _url(url) ,_cNextId(1), _enabled(true) {} - AsyncWebSocket(const String& url) :_url(url) ,_cNextId(1),_enabled(true) {} + explicit AsyncWebSocket(const char* url) : _url(url), _cNextId(1), _enabled(true) {} + AsyncWebSocket(const String& url) : _url(url), _cNextId(1), _enabled(true) {} ~AsyncWebSocket(){}; - const char * url() const { return _url.c_str(); } - void enable(bool e){ _enabled = e; } + const char* url() const { return _url.c_str(); } + void enable(bool e) { _enabled = e; } bool enabled() const { return _enabled; } bool availableForWriteAll(); bool availableForWrite(uint32_t id); size_t count() const; - AsyncWebSocketClient * client(uint32_t id); - bool hasClient(uint32_t id){ return client(id) != nullptr; } + AsyncWebSocketClient* client(uint32_t id); + bool hasClient(uint32_t id) { return client(id) != nullptr; } - void close(uint32_t id, uint16_t code=0, const char * message=NULL); - void closeAll(uint16_t code=0, const char * message=NULL); + void close(uint32_t id, 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 ping(uint32_t id, const uint8_t *data=NULL, size_t len=0); - void pingAll(const uint8_t *data=NULL, size_t len=0); // done + void ping(uint32_t id, const uint8_t* data = NULL, size_t len = 0); + void pingAll(const uint8_t* data = NULL, size_t len = 0); // done - void text(uint32_t id, const uint8_t * message, size_t len); - void text(uint32_t id, const char *message, size_t len); - void text(uint32_t id, const char *message); - void text(uint32_t id, const String &message); + void text(uint32_t id, const uint8_t* message, size_t len); + void text(uint32_t id, const char* message, size_t len); + void text(uint32_t id, const char* message); + void text(uint32_t id, const String& message); #ifndef ESP32 - void text(uint32_t id, const __FlashStringHelper *message); -#endif // ESP32 - void text(uint32_t id, AsyncWebSocketMessageBuffer *buffer); + void text(uint32_t id, const __FlashStringHelper* message); +#endif // ESP32 + void text(uint32_t id, AsyncWebSocketMessageBuffer* buffer); void text(uint32_t id, AsyncWebSocketSharedBuffer buffer); - void textAll(const uint8_t *message, size_t len); - void textAll(const char * message, size_t len); - void textAll(const char * message); - void textAll(const String &message); + void textAll(const uint8_t* message, size_t len); + void textAll(const char* message, size_t len); + void textAll(const char* message); + void textAll(const String& message); #ifndef ESP32 - void textAll(const __FlashStringHelper *message); -#endif // ESP32 - void textAll(AsyncWebSocketMessageBuffer *buffer); + void textAll(const __FlashStringHelper* message); +#endif // ESP32 + void textAll(AsyncWebSocketMessageBuffer* buffer); void textAll(AsyncWebSocketSharedBuffer buffer); - void binary(uint32_t id, const uint8_t *message, size_t len); - void binary(uint32_t id, const char *message, size_t len); - void binary(uint32_t id, const char *message); - void binary(uint32_t id, const String &message); + void binary(uint32_t id, const uint8_t* message, size_t len); + void binary(uint32_t id, const char* message, size_t len); + void binary(uint32_t id, const char* message); + void binary(uint32_t id, const String& message); #ifndef ESP32 - void binary(uint32_t id, const __FlashStringHelper *message, size_t len); -#endif // ESP32 - void binary(uint32_t id, AsyncWebSocketMessageBuffer *buffer); + void binary(uint32_t id, const __FlashStringHelper* message, size_t len); +#endif // ESP32 + void binary(uint32_t id, AsyncWebSocketMessageBuffer* buffer); void binary(uint32_t id, AsyncWebSocketSharedBuffer buffer); - void binaryAll(const uint8_t *message, size_t len); - void binaryAll(const char *message, size_t len); - void binaryAll(const char *message); - void binaryAll(const String &message); + void binaryAll(const uint8_t* message, size_t len); + void binaryAll(const char* message, size_t len); + void binaryAll(const char* message); + void binaryAll(const String& message); #ifndef ESP32 - void binaryAll(const __FlashStringHelper *message, size_t len); -#endif // ESP32 - void binaryAll(AsyncWebSocketMessageBuffer *buffer); + void binaryAll(const __FlashStringHelper* message, size_t len); +#endif // ESP32 + void binaryAll(AsyncWebSocketMessageBuffer* buffer); void binaryAll(AsyncWebSocketSharedBuffer buffer); - 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))); -#ifndef ESP32 - size_t printf_P(uint32_t id, PGM_P formatP, ...) __attribute__ ((format (printf, 3, 4))); -#endif - size_t printfAll_P(PGM_P formatP, ...) __attribute__ ((format (printf, 2, 3))); + 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))); - //event listener - void onEvent(AwsEventHandler handler){ +#ifndef ESP32 + size_t printf_P(uint32_t id, PGM_P formatP, ...) __attribute__((format(printf, 3, 4))); + size_t printfAll_P(PGM_P formatP, ...) __attribute__((format(printf, 2, 3))); +#endif + + // event listener + void onEvent(AwsEventHandler handler) { _eventHandler = handler; } // Handshake Handler - void handleHandshake(AwsHandshakeHandler handler){ - _handshakeHandler = handler; + void handleHandshake(AwsHandshakeHandler handler) { + _handshakeHandler = handler; } - - //system callbacks (do not call) - uint32_t _getNextId(){ return _cNextId++; } - AsyncWebSocketClient *_newClient(AsyncWebServerRequest *request); - void _handleEvent(AsyncWebSocketClient * client, AwsEventType type, void * arg, uint8_t *data, size_t len); - virtual bool canHandle(AsyncWebServerRequest *request) override final; - virtual void handleRequest(AsyncWebServerRequest *request) override final; + // system callbacks (do not call) + uint32_t _getNextId() { return _cNextId++; } + AsyncWebSocketClient* _newClient(AsyncWebServerRequest* request); + void _handleEvent(AsyncWebSocketClient* client, AwsEventType type, void* arg, uint8_t* data, size_t len); + virtual bool canHandle(AsyncWebServerRequest* request) override final; + virtual void handleRequest(AsyncWebServerRequest* request) override final; - // messagebuffer functions/objects. - AsyncWebSocketMessageBuffer * makeBuffer(size_t size = 0); - AsyncWebSocketMessageBuffer * makeBuffer(const uint8_t * data, size_t size); + // messagebuffer functions/objects. + AsyncWebSocketMessageBuffer* makeBuffer(size_t size = 0); + AsyncWebSocketMessageBuffer* makeBuffer(const uint8_t* data, size_t size); - const std::list &getClients() const { return _clients; } + const std::list& getClients() const { return _clients; } }; -//WebServer response to authenticate the socket and detach the tcp client from the web server request -class AsyncWebSocketResponse: public AsyncWebServerResponse { +// WebServer response to authenticate the socket and detach the tcp client from the web server request +class AsyncWebSocketResponse : public AsyncWebServerResponse { private: String _content; - AsyncWebSocket *_server; + AsyncWebSocket* _server; + public: - AsyncWebSocketResponse(const String& key, AsyncWebSocket *server); - void _respond(AsyncWebServerRequest *request); - size_t _ack(AsyncWebServerRequest *request, size_t len, uint32_t time); + AsyncWebSocketResponse(const String& key, AsyncWebSocket* server); + void _respond(AsyncWebServerRequest* request); + size_t _ack(AsyncWebServerRequest* request, size_t len, uint32_t time); bool _sourceValid() const { return true; } }; - #endif /* ASYNCWEBSOCKET_H_ */ diff --git a/src/AsyncWebSynchronization.h b/src/AsyncWebSynchronization.h deleted file mode 100644 index 0ff8ab6..0000000 --- a/src/AsyncWebSynchronization.h +++ /dev/null @@ -1,134 +0,0 @@ -#ifndef ASYNCWEBSYNCHRONIZATION_H_ -#define ASYNCWEBSYNCHRONIZATION_H_ - -// Synchronisation is only available on ESP32, as the ESP8266 isn't using FreeRTOS by default - -#include - -#ifdef ESP32 - -// This is the ESP32 version of the Sync Lock, using the FreeRTOS Semaphore -// 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: - SemaphoreHandle_t _lock; - -public: - AsyncPlainLock() { - _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); - xSemaphoreGive(_lock); - } - - ~AsyncPlainLock() { - vSemaphoreDelete(_lock); - } - - bool lock() const { - xSemaphoreTake(_lock, portMAX_DELAY); - return true; - } - - void unlock() const { - 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 - -// This is the 8266 version of the Sync Lock which is currently unimplemented -class AsyncWebLock -{ - -public: - AsyncWebLock() { - } - - ~AsyncWebLock() { - } - - bool lock() const { - return false; - } - - void unlock() const { - } -}; - -// Same for AsyncPlainLock, for ESP8266 this is just the unimplemented version above. -using AsyncPlainLock = AsyncWebLock; - -#endif - -class AsyncWebLockGuard -{ -private: - const AsyncWebLock *_lock; - -public: - AsyncWebLockGuard(const AsyncWebLock &l) { - if (l.lock()) { - _lock = &l; - } else { - _lock = NULL; - } - } - - ~AsyncWebLockGuard() { - if (_lock) { - _lock->unlock(); - } - } - - void unlock() { - if (_lock) { - _lock->unlock(); - _lock = NULL; - } - } -}; - -#endif // ASYNCWEBSYNCHRONIZATION_H_ diff --git a/src/ESPAsyncWebServer.h b/src/ESPAsyncWebServer.h index 1bec173..77c5de7 100644 --- a/src/ESPAsyncWebServer.h +++ b/src/ESPAsyncWebServer.h @@ -23,33 +23,36 @@ #include "Arduino.h" +#include "FS.h" #include #include #include -#include "FS.h" - -#include "StringArray.h" #ifdef ESP32 -#include -#include + #include + #include #elif defined(ESP8266) -#include -#include + #include + #include +#elif defined(TARGET_RP2040) + #include + #include + #include + #include #else -#error Platform not supported + #error Platform not supported #endif -#define ASYNCWEBSERVER_VERSION "2.10.8" -#define ASYNCWEBSERVER_VERSION_MAJOR 2 -#define ASYNCWEBSERVER_VERSION_MINOR 10 -#define ASYNCWEBSERVER_VERSION_REVISION 8 +#define ASYNCWEBSERVER_VERSION "3.0.6" +#define ASYNCWEBSERVER_VERSION_MAJOR 3 +#define ASYNCWEBSERVER_VERSION_MINOR 0 +#define ASYNCWEBSERVER_VERSION_REVISION 6 #define ASYNCWEBSERVER_FORK_mathieucarbou #ifdef ASYNCWEBSERVER_REGEX -#define ASYNCWEBSERVER_REGEX_ATTRIBUTE + #define ASYNCWEBSERVER_REGEX_ATTRIBUTE #else -#define ASYNCWEBSERVER_REGEX_ATTRIBUTE __attribute__((warning("ASYNCWEBSERVER_REGEX not defined"))) + #define ASYNCWEBSERVER_REGEX_ATTRIBUTE __attribute__((warning("ASYNCWEBSERVER_REGEX not defined"))) #endif class AsyncWebServer; @@ -63,33 +66,37 @@ class AsyncStaticWebHandler; class AsyncCallbackWebHandler; class AsyncResponseStream; -#ifndef WEBSERVER_H -typedef enum { - HTTP_GET = 0b00000001, - HTTP_POST = 0b00000010, - HTTP_DELETE = 0b00000100, - HTTP_PUT = 0b00001000, - HTTP_PATCH = 0b00010000, - HTTP_HEAD = 0b00100000, - HTTP_OPTIONS = 0b01000000, - HTTP_ANY = 0b01111111, -} WebRequestMethod; +#if defined (TARGET_RP2040) + typedef enum http_method WebRequestMethod; +#else + #ifndef WEBSERVER_H + typedef enum { + HTTP_GET = 0b00000001, + HTTP_POST = 0b00000010, + HTTP_DELETE = 0b00000100, + HTTP_PUT = 0b00001000, + HTTP_PATCH = 0b00010000, + HTTP_HEAD = 0b00100000, + HTTP_OPTIONS = 0b01000000, + HTTP_ANY = 0b01111111, + } WebRequestMethod; + #endif #endif #ifndef HAVE_FS_FILE_OPEN_MODE namespace fs { - class FileOpenMode { + class FileOpenMode { public: - static const char *read; - static const char *write; - static const char *append; - }; + static const char* read; + static const char* write; + static const char* append; + }; }; #else -#include "FileOpenMode.h" + #include "FileOpenMode.h" #endif -//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 typedef uint8_t WebRequestMethodComposite; @@ -108,8 +115,7 @@ class AsyncWebParameter { bool _isFile; public: - - AsyncWebParameter(const String& name, const String& value, bool form=false, bool file=false, size_t size=0): _name(name), _value(value), _size(size), _isForm(form), _isFile(file){} + AsyncWebParameter(const String& name, const String& value, bool form = false, bool file = false, size_t size = 0) : _name(name), _value(value), _size(size), _isForm(form), _isFile(file) {} const String& name() const { return _name; } const String& value() const { return _value; } size_t size() const { return _size; } @@ -128,18 +134,20 @@ class AsyncWebHeader { public: AsyncWebHeader() = default; - AsyncWebHeader(const AsyncWebHeader &) = default; + AsyncWebHeader(const AsyncWebHeader&) = default; - AsyncWebHeader(const String& name, const String& value): _name(name), _value(value){} - AsyncWebHeader(const String& data): _name(), _value(){ - if(!data) return; + AsyncWebHeader(const String& name, const String& value) : _name(name), _value(value) {} + AsyncWebHeader(const String& data) { + if (!data) + return; int index = data.indexOf(':'); - if (index < 0) return; + if (index < 0) + return; _name = data.substring(0, index); _value = data.substring(index + 2); } - AsyncWebHeader &operator=(const AsyncWebHeader &) = default; + AsyncWebHeader& operator=(const AsyncWebHeader&) = default; const String& name() const { return _name; } const String& value() const { return _value; } @@ -150,16 +158,22 @@ class AsyncWebHeader { * REQUEST :: Each incoming Client is wrapped inside a Request and both live together until disconnect * */ -typedef enum { RCT_NOT_USED = -1, RCT_DEFAULT = 0, RCT_HTTP, RCT_WS, RCT_EVENT, RCT_MAX } RequestedConnectionType; +typedef enum { RCT_NOT_USED = -1, + RCT_DEFAULT = 0, + RCT_HTTP, + RCT_WS, + RCT_EVENT, + RCT_MAX } RequestedConnectionType; typedef std::function AwsResponseFiller; typedef std::function AwsTemplateProcessor; class AsyncWebServerRequest { - using File = fs::File; - using FS = fs::FS; - friend class AsyncWebServer; - friend class AsyncCallbackWebHandler; + using File = fs::File; + using FS = fs::FS; + friend class AsyncWebServer; + friend class AsyncCallbackWebHandler; + private: AsyncClient* _client; AsyncWebServer* _server; @@ -188,7 +202,7 @@ class AsyncWebServerRequest { size_t _parsedLength; std::list _headers; - LinkedList _params; + std::list _params; std::vector _pathParams; uint8_t _multiParseState; @@ -199,7 +213,7 @@ class AsyncWebServerRequest { String _itemFilename; String _itemType; String _itemValue; - uint8_t *_itemBuffer; + uint8_t* _itemBuffer; size_t _itemBufferIndex; bool _itemIsFile; @@ -208,10 +222,9 @@ class AsyncWebServerRequest { void _onError(int8_t error); void _onTimeout(uint32_t time); void _onDisconnect(); - void _onData(void *buf, size_t len); + void _onData(void* buf, size_t len); - void _addParam(AsyncWebParameter*); - void _addPathParam(const char *param); + void _addPathParam(const char* param); bool _parseReqHead(); bool _parseReqHeader(); @@ -226,12 +239,12 @@ class AsyncWebServerRequest { public: File _tempFile; - void *_tempObject; + void* _tempObject; AsyncWebServerRequest(AsyncWebServer*, AsyncClient*); ~AsyncWebServerRequest(); - AsyncClient* client(){ return _client; } + AsyncClient* client() { return _client; } uint8_t version() const { return _version; } WebRequestMethodComposite method() const { return _method; } const String& url() const { return _url; } @@ -239,77 +252,156 @@ class AsyncWebServerRequest { const String& contentType() const { return _contentType; } size_t contentLength() const { return _contentLength; } bool multipart() const { return _isMultipart; } - const __FlashStringHelper *methodToString() const; - const __FlashStringHelper *requestedConnTypeToString() const; + +#ifndef ESP8266 + const char* methodToString() const; + const char* requestedConnTypeToString() const; +#else + const __FlashStringHelper* methodToString() const; + const __FlashStringHelper* requestedConnTypeToString() const; +#endif + RequestedConnectionType requestedConnType() const { return _reqconntype; } bool isExpectedRequestedConnType(RequestedConnectionType erct1, RequestedConnectionType erct2 = RCT_NOT_USED, RequestedConnectionType erct3 = RCT_NOT_USED); - void onDisconnect (ArDisconnectHandler fn); + void onDisconnect(ArDisconnectHandler fn); - //hash is the string representation of: - // base64(user:pass) for basic or - // user:realm:md5(user:realm:pass) for digest - bool authenticate(const char * hash); - bool authenticate(const char * username, const char * password, const char * realm = NULL, bool passwordIsHash = false); - void requestAuthentication(const char * realm = NULL, bool isDigest = true); + // hash is the string representation of: + // base64(user:pass) for basic or + // user:realm:md5(user:realm:pass) for digest + bool authenticate(const char* hash); + bool authenticate(const char* username, const char* password, const char* realm = NULL, bool passwordIsHash = false); + void requestAuthentication(const char* realm = NULL, bool isDigest = true); - void setHandler(AsyncWebHandler *handler){ _handler = handler; } - void addInterestingHeader(const String& name); + void setHandler(AsyncWebHandler* handler) { _handler = handler; } - void redirect(const String& url); + /** + * @brief add header to collect from a response + * + * @param name + */ + void addInterestingHeader(const char* name); + void addInterestingHeader(const String& name) { return addInterestingHeader(name.c_str()); }; - void send(AsyncWebServerResponse *response); - void send(int code, const String& contentType=String(), const String& content=String()); - void send(FS &fs, const String& path, const String& contentType=String(), bool download=false, AwsTemplateProcessor callback=nullptr); - void send(File content, const String& path, const String& contentType=String(), bool download=false, AwsTemplateProcessor callback=nullptr); - void send(Stream &stream, const String& contentType, size_t len, AwsTemplateProcessor callback=nullptr); - void send(const String& contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback=nullptr); - void sendChunked(const String& contentType, AwsResponseFiller callback, AwsTemplateProcessor templateCallback=nullptr); - void send_P(int code, const String& contentType, const uint8_t * content, size_t len, AwsTemplateProcessor callback=nullptr); - void send_P(int code, const String& contentType, PGM_P content, AwsTemplateProcessor callback=nullptr); + /** + * @brief issue 302 redirect response + * + * @param url + */ + void redirect(const char* url); + void redirect(const String& url) { return redirect(url.c_str()); }; - AsyncWebServerResponse *beginResponse(int code, const String& contentType=String(), const String& content=String()); - AsyncWebServerResponse *beginResponse(FS &fs, const String& path, const String& contentType=String(), bool download=false, AwsTemplateProcessor callback=nullptr); - AsyncWebServerResponse *beginResponse(File content, const String& path, const String& contentType=String(), bool download=false, AwsTemplateProcessor callback=nullptr); - AsyncWebServerResponse *beginResponse(Stream &stream, const String& contentType, size_t len, AwsTemplateProcessor callback=nullptr); - AsyncWebServerResponse *beginResponse(const String& contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback=nullptr); - AsyncWebServerResponse *beginChunkedResponse(const String& contentType, AwsResponseFiller callback, AwsTemplateProcessor templateCallback=nullptr); - AsyncResponseStream *beginResponseStream(const String& contentType, size_t bufferSize=1460); - AsyncWebServerResponse *beginResponse_P(int code, const String& contentType, const uint8_t * content, size_t len, AwsTemplateProcessor callback=nullptr); - AsyncWebServerResponse *beginResponse_P(int code, const String& contentType, PGM_P content, AwsTemplateProcessor callback=nullptr); + void send(AsyncWebServerResponse* response); + void send(int code, const String& contentType = String(), const String& content = String()); + void send(int code, const String& contentType, const uint8_t* content, size_t len, AwsTemplateProcessor callback = nullptr); + void send(int code, const String& contentType, PGM_P content, AwsTemplateProcessor callback = nullptr); + void send(FS& fs, const String& path, const String& contentType = String(), bool download = false, AwsTemplateProcessor callback = nullptr); + void send(File content, const String& path, const String& contentType = String(), bool download = false, AwsTemplateProcessor callback = nullptr); + void send(Stream& stream, const String& contentType, size_t len, AwsTemplateProcessor callback = nullptr); + void send(const String& contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback = nullptr); + void sendChunked(const String& contentType, AwsResponseFiller callback, AwsTemplateProcessor templateCallback = nullptr); - size_t headers() const; // get header count - bool hasHeader(const String& name) const; // check if header exists - bool hasHeader(const __FlashStringHelper * data) const; // check if header exists + [[deprecated("Replaced by send(...)")]] + void send_P(int code, const String& contentType, const uint8_t* content, size_t len, AwsTemplateProcessor callback = nullptr) { + send(code, contentType, content, len, callback); + } + [[deprecated("Replaced by send(...)")]] + void send_P(int code, const String& contentType, PGM_P content, AwsTemplateProcessor callback = nullptr) { + send(code, contentType, content, callback); + } - AsyncWebHeader* getHeader(const String& name); - const AsyncWebHeader* getHeader(const String& name) const; - AsyncWebHeader* getHeader(const __FlashStringHelper * data); - const AsyncWebHeader* getHeader(const __FlashStringHelper * data) const; - AsyncWebHeader* getHeader(size_t num); + AsyncWebServerResponse* beginResponse(int code, const String& contentType = String(), const String& content = String()); + AsyncWebServerResponse* beginResponse(int code, const String& contentType, const uint8_t* content, size_t len, AwsTemplateProcessor callback = nullptr); + AsyncWebServerResponse* beginResponse(int code, const String& contentType, PGM_P content, AwsTemplateProcessor callback = nullptr); + AsyncWebServerResponse* beginResponse(FS& fs, const String& path, const String& contentType = String(), bool download = false, AwsTemplateProcessor callback = nullptr); + AsyncWebServerResponse* beginResponse(File content, const String& path, const String& contentType = String(), bool download = false, AwsTemplateProcessor callback = nullptr); + AsyncWebServerResponse* beginResponse(Stream& stream, const String& contentType, size_t len, AwsTemplateProcessor callback = nullptr); + AsyncWebServerResponse* beginResponse(const String& contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback = nullptr); + AsyncWebServerResponse* beginChunkedResponse(const String& contentType, AwsResponseFiller callback, AwsTemplateProcessor templateCallback = nullptr); + AsyncResponseStream* beginResponseStream(const String& contentType, size_t bufferSize = 1460); + + + [[deprecated("Replaced by beginResponse(...)")]] + AsyncWebServerResponse* beginResponse_P(int code, const String& contentType, const uint8_t* content, size_t len, AwsTemplateProcessor callback = nullptr) { + return beginResponse(code, contentType, content, len, callback); + } + [[deprecated("Replaced by beginResponse(...)")]] + AsyncWebServerResponse* beginResponse_P(int code, const String& contentType, PGM_P content, AwsTemplateProcessor callback = nullptr) { + return beginResponse(code, contentType, content, callback); + } + + size_t headers() const; // get header count + + // check if header exists + bool hasHeader(const char* name) const; + bool hasHeader(const String& name) const { return hasHeader(name.c_str()); }; +#ifdef ESP8266 + bool hasHeader(const __FlashStringHelper* data) const; // check if header exists +#endif + + const AsyncWebHeader* getHeader(const char* name) const; + const AsyncWebHeader* getHeader(const String& name) const { return getHeader(name.c_str()); }; +#ifdef ESP8266 + const AsyncWebHeader* getHeader(const __FlashStringHelper* data) const; +#endif const AsyncWebHeader* getHeader(size_t num) const; - size_t params() const; // get arguments count - bool hasParam(const String& name, bool post=false, bool file=false) const; - bool hasParam(const __FlashStringHelper * data, bool post=false, bool file=false) const; + size_t params() const; // get arguments count + bool hasParam(const String& name, bool post = false, bool file = false) const; + bool hasParam(const __FlashStringHelper* data, bool post = false, bool file = false) const; - AsyncWebParameter* getParam(const String& name, bool post=false, bool file=false) const; - AsyncWebParameter* getParam(const __FlashStringHelper * data, bool post, bool file) const; - AsyncWebParameter* getParam(size_t num) const; + /** + * @brief Get the Request parameter by name + * + * @param name + * @param post + * @param file + * @return const AsyncWebParameter* + */ + const AsyncWebParameter* getParam(const char* name, bool post = false, bool file = false) const; - size_t args() const { return params(); } // get arguments count - const String& arg(const String& name) const; // get request argument value by name - const String& arg(const __FlashStringHelper * data) const; // get request argument value by F(name) - const String& arg(size_t i) const; // get request argument value by number - const String& argName(size_t i) const; // get request argument name by number - bool hasArg(const char* name) const; // check if argument exists - bool hasArg(const __FlashStringHelper * data) const; // check if F(argument) exists + const AsyncWebParameter* getParam(const String& name, bool post = false, bool file = false) const { return getParam(name.c_str(), post, file); }; +#ifdef ESP8266 + const AsyncWebParameter* getParam(const __FlashStringHelper* data, bool post, bool file) const; +#endif + + /** + * @brief Get request parameter by number + * i.e., n-th parameter + * @param num + * @return const AsyncWebParameter* + */ + const AsyncWebParameter* getParam(size_t num) const; + + size_t args() const { return params(); } // get arguments count + + // get request argument value by name + const String& arg(const char* name) const; + // get request argument value by name + const String& arg(const String& name) const { return arg(name.c_str()); }; +#ifdef ESP8266 + const String& arg(const __FlashStringHelper* data) const; // get request argument value by F(name) +#endif + const String& arg(size_t i) const; // get request argument value by number + const String& argName(size_t i) const; // get request argument name by number + bool hasArg(const char* name) const; // check if argument exists + bool hasArg(const String& name) const { return hasArg(name.c_str()); }; +#ifdef ESP8266 + bool hasArg(const __FlashStringHelper* data) const; // check if F(argument) exists +#endif const String& ASYNCWEBSERVER_REGEX_ATTRIBUTE pathArg(size_t i) const; - const String& header(const char* name) const;// get request header value by name - const String& header(const __FlashStringHelper * data) const;// get request header value by F(name) - const String& header(size_t i) const; // get request header value by number - const String& headerName(size_t i) const; // get request header name by number + // get request header value by name + const String& header(const char* name) const; + const String& header(const String& name) const { return header(name.c_str()); }; + +#ifdef ESP8266 + const String& header(const __FlashStringHelper* data) const; // get request header value by F(name) +#endif + + const String& header(size_t i) const; // get request header value by number + const String& headerName(size_t i) const; // get request header name by number + String urlDecode(const String& text) const; }; @@ -317,11 +409,11 @@ class AsyncWebServerRequest { * FILTER :: Callback to filter AsyncWebRewrite and AsyncWebHandler (done by the Server) * */ -typedef std::function ArRequestFilterFunction; +using ArRequestFilterFunction = std::function; -bool ON_STA_FILTER(AsyncWebServerRequest *request); +bool ON_STA_FILTER(AsyncWebServerRequest* request); -bool ON_AP_FILTER(AsyncWebServerRequest *request); +bool ON_AP_FILTER(AsyncWebServerRequest* request); /* * REWRITE :: One instance can be handle any Request (done by the Server) @@ -332,22 +424,26 @@ class AsyncWebRewrite { String _from; String _toUrl; String _params; - ArRequestFilterFunction _filter; + ArRequestFilterFunction _filter{nullptr}; + public: - AsyncWebRewrite(const char* from, const char* to): _from(from), _toUrl(to), _params(String()), _filter(NULL){ + AsyncWebRewrite(const char* from, const char* to) : _from(from), _toUrl(to) { int index = _toUrl.indexOf('?'); if (index > 0) { - _params = _toUrl.substring(index +1); + _params = _toUrl.substring(index + 1); _toUrl = _toUrl.substring(0, index); } } - virtual ~AsyncWebRewrite(){} - AsyncWebRewrite& setFilter(ArRequestFilterFunction fn) { _filter = fn; return *this; } - bool filter(AsyncWebServerRequest *request) const { return _filter == NULL || _filter(request); } + virtual ~AsyncWebRewrite() {} + AsyncWebRewrite& setFilter(ArRequestFilterFunction fn) { + _filter = fn; + return *this; + } + bool filter(AsyncWebServerRequest* request) const { return _filter == NULL || _filter(request); } const String& from(void) const { return _from; } const String& toUrl(void) const { return _toUrl; } const String& params(void) const { return _params; } - virtual bool match(AsyncWebServerRequest *request) { return from() == request->url() && filter(request); } + virtual bool match(AsyncWebServerRequest* request) { return from() == request->url() && filter(request); } }; /* @@ -356,23 +452,35 @@ class AsyncWebRewrite { class AsyncWebHandler { protected: - ArRequestFilterFunction _filter; + ArRequestFilterFunction _filter{nullptr}; String _username; String _password; + public: - AsyncWebHandler():_username(""), _password(""){} - AsyncWebHandler& setFilter(ArRequestFilterFunction fn) { _filter = fn; return *this; } - AsyncWebHandler& setAuthentication(const char *username, const char *password){ _username = String(username);_password = String(password); return *this; }; - AsyncWebHandler& setAuthentication(const String& username, const String& password){ _username = username;_password = password; return *this; }; - bool filter(AsyncWebServerRequest *request){ return _filter == NULL || _filter(request); } - virtual ~AsyncWebHandler(){} - virtual bool canHandle(AsyncWebServerRequest *request __attribute__((unused))){ + AsyncWebHandler() {} + AsyncWebHandler& setFilter(ArRequestFilterFunction fn) { + _filter = fn; + return *this; + } + AsyncWebHandler& setAuthentication(const char* username, const char* password) { + _username = username; + _password = password; + return *this; + }; + AsyncWebHandler& setAuthentication(const String& username, const String& password) { + _username = username; + _password = password; + return *this; + }; + bool filter(AsyncWebServerRequest* request) { return _filter == NULL || _filter(request); } + virtual ~AsyncWebHandler() {} + virtual bool canHandle(AsyncWebServerRequest* request __attribute__((unused))) { return false; } - virtual void handleRequest(AsyncWebServerRequest *request __attribute__((unused))){} - virtual void handleUpload(AsyncWebServerRequest *request __attribute__((unused)), const String& filename __attribute__((unused)), size_t index __attribute__((unused)), uint8_t *data __attribute__((unused)), size_t len __attribute__((unused)), bool final __attribute__((unused))){} - virtual void handleBody(AsyncWebServerRequest *request __attribute__((unused)), uint8_t *data __attribute__((unused)), size_t len __attribute__((unused)), size_t index __attribute__((unused)), size_t total __attribute__((unused))){} - virtual bool isRequestHandlerTrivial(){return true;} + virtual void handleRequest(AsyncWebServerRequest* request __attribute__((unused))) {} + virtual void handleUpload(AsyncWebServerRequest* request __attribute__((unused)), const String& filename __attribute__((unused)), size_t index __attribute__((unused)), uint8_t* data __attribute__((unused)), size_t len __attribute__((unused)), bool final __attribute__((unused))) {} + virtual void handleBody(AsyncWebServerRequest* request __attribute__((unused)), uint8_t* data __attribute__((unused)), size_t len __attribute__((unused)), size_t index __attribute__((unused)), size_t total __attribute__((unused))) {} + virtual bool isRequestHandlerTrivial() { return true; } }; /* @@ -380,7 +488,12 @@ class AsyncWebHandler { * */ typedef enum { - RESPONSE_SETUP, RESPONSE_HEADERS, RESPONSE_CONTENT, RESPONSE_WAIT_ACK, RESPONSE_END, RESPONSE_FAILED + RESPONSE_SETUP, + RESPONSE_HEADERS, + RESPONSE_CONTENT, + RESPONSE_WAIT_ACK, + RESPONSE_END, + RESPONSE_FAILED } WebResponseState; class AsyncWebServerResponse { @@ -397,8 +510,9 @@ class AsyncWebServerResponse { size_t _writtenLength; WebResponseState _state; const char* _responseCodeToString(int code); -public: - static const __FlashStringHelper *responseCodeToString(int code); + + public: + static const __FlashStringHelper* responseCodeToString(int code); public: AsyncWebServerResponse(); @@ -412,23 +526,23 @@ public: virtual bool _finished() const; virtual bool _failed() const; virtual bool _sourceValid() const; - virtual void _respond(AsyncWebServerRequest *request); - virtual size_t _ack(AsyncWebServerRequest *request, size_t len, uint32_t time); + virtual void _respond(AsyncWebServerRequest* request); + virtual size_t _ack(AsyncWebServerRequest* request, size_t len, uint32_t time); }; /* * SERVER :: One instance * */ -typedef std::function ArRequestHandlerFunction; -typedef std::function ArUploadHandlerFunction; -typedef std::function ArBodyHandlerFunction; +typedef std::function ArRequestHandlerFunction; +typedef std::function ArUploadHandlerFunction; +typedef std::function ArBodyHandlerFunction; class AsyncWebServer { protected: AsyncServer _server; - LinkedList _rewrites; - LinkedList _handlers; + std::list> _rewrites; + std::list> _handlers; AsyncCallbackWebHandler* _catchAllHandler; public: @@ -440,13 +554,50 @@ class AsyncWebServer { #if ASYNC_TCP_SSL_ENABLED void onSslFileRequest(AcSSlFileHandler cb, void* arg); - void beginSecure(const char *cert, const char *private_key_file, const char *password); + void beginSecure(const char* cert, const char* private_key_file, const char* password); #endif AsyncWebRewrite& addRewrite(AsyncWebRewrite* rewrite); - bool removeRewrite(AsyncWebRewrite* rewrite); + + /** + * @brief (compat) Add url rewrite rule by pointer + * a deep copy of the pounter object will be created, + * it is up to user to manage further lifetime of the object in argument + * + * @param rewrite pointer to rewrite object to copy setting from + * @return AsyncWebRewrite& reference to a newly created rewrite rule + */ + AsyncWebRewrite& addRewrite(std::shared_ptr rewrite); + + /** + * @brief add url rewrite rule + * + * @param from + * @param to + * @return AsyncWebRewrite& + */ AsyncWebRewrite& rewrite(const char* from, const char* to); + /** + * @brief (compat) remove rewrite rule via referenced object + * this will NOT deallocate pointed object itself, internal rule with same from/to urls will be removed if any + * it's a compat method, better use `removeRewrite(const char* from, const char* to)` + * @param rewrite + * @return true + * @return false + */ + bool removeRewrite(AsyncWebRewrite* rewrite); + + /** + * @brief remove rewrite rule + * + * @param from + * @param to + * @return true + * @return false + */ + bool removeRewrite(const char* from, const char* to); + AsyncWebHandler& addHandler(AsyncWebHandler* handler); bool removeHandler(AsyncWebHandler* handler); @@ -457,45 +608,45 @@ class AsyncWebServer { AsyncStaticWebHandler& serveStatic(const char* uri, fs::FS& fs, const char* path, const char* cache_control = NULL); - void onNotFound(ArRequestHandlerFunction fn); //called when handler is not assigned - void onFileUpload(ArUploadHandlerFunction fn); //handle file uploads - void onRequestBody(ArBodyHandlerFunction fn); //handle posts with plain body content (JSON often transmitted this way as a request) + void onNotFound(ArRequestHandlerFunction fn); // called when handler is not assigned + void onFileUpload(ArUploadHandlerFunction fn); // handle file uploads + void onRequestBody(ArBodyHandlerFunction fn); // handle posts with plain body content (JSON often transmitted this way as a request) - void reset(); //remove all writers and handlers, with onNotFound/onFileUpload/onRequestBody + void reset(); // remove all writers and handlers, with onNotFound/onFileUpload/onRequestBody - void _handleDisconnect(AsyncWebServerRequest *request); - void _attachHandler(AsyncWebServerRequest *request); - void _rewriteRequest(AsyncWebServerRequest *request); + void _handleDisconnect(AsyncWebServerRequest* request); + void _attachHandler(AsyncWebServerRequest* request); + void _rewriteRequest(AsyncWebServerRequest* request); }; class DefaultHeaders { - using headers_t = std::list; - headers_t _headers; + using headers_t = std::list; + headers_t _headers; -public: - DefaultHeaders() = default; + public: + DefaultHeaders() = default; - using ConstIterator = headers_t::const_iterator; + using ConstIterator = headers_t::const_iterator; - void addHeader(const String& name, const String& value){ - _headers.emplace_back(name, value); - } + void addHeader(const String& name, const String& value) { + _headers.emplace_back(name, value); + } - ConstIterator begin() const { return _headers.begin(); } - ConstIterator end() const { return _headers.end(); } + ConstIterator begin() const { return _headers.begin(); } + ConstIterator end() const { return _headers.end(); } - DefaultHeaders(DefaultHeaders const &) = delete; - DefaultHeaders &operator=(DefaultHeaders const &) = delete; + DefaultHeaders(DefaultHeaders const&) = delete; + DefaultHeaders& operator=(DefaultHeaders const&) = delete; - static DefaultHeaders &Instance() { - static DefaultHeaders instance; - return instance; - } + static DefaultHeaders& Instance() { + static DefaultHeaders instance; + return instance; + } }; -#include "WebResponseImpl.h" -#include "WebHandlerImpl.h" -#include "AsyncWebSocket.h" #include "AsyncEventSource.h" +#include "AsyncWebSocket.h" +#include "WebHandlerImpl.h" +#include "WebResponseImpl.h" #endif /* _AsyncWebServer_H_ */ diff --git a/src/StringArray.h b/src/StringArray.h deleted file mode 100644 index d5096e6..0000000 --- a/src/StringArray.h +++ /dev/null @@ -1,174 +0,0 @@ -/* - Asynchronous WebServer library for Espressif MCUs - - Copyright (c) 2016 Hristo Gochkov. All rights reserved. - This file is part of the esp8266 core for Arduino environment. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -*/ -#ifndef STRINGARRAY_H_ -#define STRINGARRAY_H_ - -#include "stddef.h" -#include "WString.h" - -template -class LinkedListNode { - T _value; - public: - LinkedListNode* next; - LinkedListNode(const T val): _value(val), next(nullptr) {} - ~LinkedListNode(){} - const T& value() const { return _value; }; - T& value(){ return _value; } -}; - -template class Item = LinkedListNode> -class LinkedList { - public: - typedef Item ItemType; - typedef std::function OnRemove; - typedef std::function Predicate; - private: - ItemType* _root; - OnRemove _onRemove; - - class Iterator { - ItemType* _node; - public: - Iterator(ItemType* current = nullptr) : _node(current) {} - Iterator(const Iterator& i) : _node(i._node) {} - Iterator& operator ++() { _node = _node->next; return *this; } - bool operator != (const Iterator& i) const { return _node != i._node; } - const T& operator * () const { return _node->value(); } - const T* operator -> () const { return &_node->value(); } - }; - - public: - typedef const Iterator ConstIterator; - ConstIterator begin() const { return ConstIterator(_root); } - ConstIterator end() const { return ConstIterator(nullptr); } - - LinkedList(OnRemove onRemove) : _root(nullptr), _onRemove(onRemove) {} - ~LinkedList(){} - void add(const T& t){ - auto it = new ItemType(t); - if(!_root){ - _root = it; - } else { - auto i = _root; - while(i->next) i = i->next; - i->next = it; - } - } - T& front() const { - return _root->value(); - } - - bool isEmpty() const { - return _root == nullptr; - } - size_t length() const { - size_t i = 0; - auto it = _root; - while(it){ - i++; - it = it->next; - } - return i; - } - size_t count_if(Predicate predicate) const { - size_t i = 0; - auto it = _root; - while(it){ - if (!predicate){ - i++; - } - else if (predicate(it->value())) { - i++; - } - it = it->next; - } - return i; - } - const T* nth(size_t N) const { - size_t i = 0; - auto it = _root; - while(it){ - if(i++ == N) - return &(it->value()); - it = it->next; - } - return nullptr; - } - bool remove(const T& t){ - auto it = _root; - auto pit = _root; - while(it){ - if(it->value() == t){ - if(it == _root){ - _root = _root->next; - } else { - pit->next = it->next; - } - - if (_onRemove) { - _onRemove(it->value()); - } - - delete it; - return true; - } - pit = it; - it = it->next; - } - return false; - } - bool remove_first(Predicate predicate){ - auto it = _root; - auto pit = _root; - while(it){ - if(predicate(it->value())){ - if(it == _root){ - _root = _root->next; - } else { - pit->next = it->next; - } - if (_onRemove) { - _onRemove(it->value()); - } - delete it; - return true; - } - pit = it; - it = it->next; - } - return false; - } - - void free(){ - while(_root != nullptr){ - auto it = _root; - _root = _root->next; - if (_onRemove) { - _onRemove(it->value()); - } - delete it; - } - _root = nullptr; - } -}; - -#endif /* STRINGARRAY_H_ */ diff --git a/src/WebAuthentication.cpp b/src/WebAuthentication.cpp index 8396795..abd74b8 100644 --- a/src/WebAuthentication.cpp +++ b/src/WebAuthentication.cpp @@ -20,41 +20,40 @@ */ #include "WebAuthentication.h" #include -#ifdef ESP32 -#include +#if defined(ESP32) || defined(TARGET_RP2040) + #include #else -#include "md5.h" + #include "md5.h" #endif - // Basic Auth hash = base64("username:password") -bool checkBasicAuthentication(const char * hash, const char * username, const char * password){ - if(username == NULL || password == NULL || hash == NULL) +bool checkBasicAuthentication(const char* hash, const char* username, const char* password) { + if (username == NULL || password == NULL || hash == NULL) return false; - size_t toencodeLen = strlen(username)+strlen(password)+1; + size_t toencodeLen = strlen(username) + strlen(password) + 1; size_t encodedLen = base64_encode_expected_len(toencodeLen); - if(strlen(hash) != encodedLen) + if (strlen(hash) != encodedLen) // Fix from https://github.com/me-no-dev/ESPAsyncWebServer/issues/667 #ifdef ARDUINO_ARCH_ESP32 - if(strlen(hash) != encodedLen) + if (strlen(hash) != encodedLen) #else if (strlen(hash) != encodedLen - 1) #endif - return false; + return false; - char *toencode = new char[toencodeLen+1]; - if(toencode == NULL){ + char* toencode = new char[toencodeLen + 1]; + if (toencode == NULL) { return false; } - char *encoded = new char[base64_encode_expected_len(toencodeLen)+1]; - if(encoded == NULL){ + char* encoded = new char[base64_encode_expected_len(toencodeLen) + 1]; + if (encoded == NULL) { delete[] toencode; return false; } sprintf_P(toencode, PSTR("%s:%s"), username, password); - if(base64_encode_chars(toencode, toencodeLen, encoded) > 0 && memcmp(hash, encoded, encodedLen) == 0){ + if (base64_encode_chars(toencode, toencodeLen, encoded) > 0 && memcmp(hash, encoded, encodedLen) == 0) { delete[] toencode; delete[] encoded; return true; @@ -64,8 +63,8 @@ bool checkBasicAuthentication(const char * hash, const char * username, const ch return false; } -static bool getMD5(uint8_t * data, uint16_t len, char * output){//33 bytes or more -#ifdef ESP32 +static bool getMD5(uint8_t* data, uint16_t len, char* output) { // 33 bytes or more +#if defined(ESP32) || defined(TARGET_RP2040) MD5Builder md5; md5.begin(); md5.add(data, len); @@ -73,9 +72,9 @@ static bool getMD5(uint8_t * data, uint16_t len, char * output){//33 bytes or mo md5.getChars(output); #else md5_context_t _ctx; - - uint8_t * _buf = (uint8_t*)malloc(16); - if(_buf == NULL) + + uint8_t* _buf = (uint8_t*)malloc(16); + if (_buf == NULL) return false; memset(_buf, 0x00, 16); @@ -83,7 +82,7 @@ static bool getMD5(uint8_t * data, uint16_t len, char * output){//33 bytes or mo MD5Update(&_ctx, data, len); MD5Final(_buf, &_ctx); - for(uint8_t i = 0; i < 16; i++) { + for (uint8_t i = 0; i < 16; i++) { sprintf_P(output + (i * 2), PSTR("%02x"), _buf[i]); } @@ -92,50 +91,50 @@ static bool getMD5(uint8_t * data, uint16_t len, char * output){//33 bytes or mo return true; } -static String genRandomMD5(){ +static String genRandomMD5() { #ifdef ESP8266 uint32_t r = RANDOM_REG32; #else uint32_t r = rand(); #endif - char * out = (char*)malloc(33); - if(out == NULL || !getMD5((uint8_t*)(&r), 4, out)) + char* out = (char*)malloc(33); + if (out == NULL || !getMD5((uint8_t*)(&r), 4, out)) return emptyString; String res = String(out); free(out); return res; } -static String stringMD5(const String& in){ - char * out = (char*)malloc(33); - if(out == NULL || !getMD5((uint8_t*)(in.c_str()), in.length(), out)) +static String stringMD5(const String& in) { + char* out = (char*)malloc(33); + if (out == NULL || !getMD5((uint8_t*)(in.c_str()), in.length(), out)) return emptyString; String res = String(out); free(out); return res; } -String generateDigestHash(const char * username, const char * password, const char * realm){ - if(username == NULL || password == NULL || realm == NULL){ +String generateDigestHash(const char* username, const char* password, const char* realm) { + if (username == NULL || password == NULL || realm == NULL) { return emptyString; } - char * out = (char*)malloc(33); + char* out = (char*)malloc(33); String res = String(username); res += ':'; res.concat(realm); res += ':'; String in = res; in.concat(password); - if(out == NULL || !getMD5((uint8_t*)(in.c_str()), in.length(), out)) + if (out == NULL || !getMD5((uint8_t*)(in.c_str()), in.length(), out)) return emptyString; res.concat(out); free(out); return res; } -String requestDigestAuthentication(const char * realm){ +String requestDigestAuthentication(const char* realm) { String header = F("realm=\""); - if(realm == NULL) + if (realm == NULL) header.concat(F("asyncesp")); else header.concat(realm); @@ -147,95 +146,100 @@ String requestDigestAuthentication(const char * realm){ return header; } -bool checkDigestAuthentication(const char * header, const __FlashStringHelper *method, const char * username, const char * password, const char * realm, bool passwordIsHash, const char * nonce, const char * opaque, const char * uri){ - if(username == NULL || password == NULL || header == NULL || method == NULL){ - //os_printf("AUTH FAIL: missing requred fields\n"); +#ifndef ESP8266 +bool checkDigestAuthentication(const char* header, const char* method, const char* username, const char* password, const char* realm, bool passwordIsHash, const char* nonce, const char* opaque, const char* uri) +#else +bool checkDigestAuthentication(const char* header, const __FlashStringHelper* method, const char* username, const char* password, const char* realm, bool passwordIsHash, const char* nonce, const char* opaque, const char* uri) +#endif +{ + if (username == NULL || password == NULL || header == NULL || method == NULL) { + // os_printf("AUTH FAIL: missing requred fields\n"); return false; } - String myHeader = String(header); + String myHeader(header); int nextBreak = myHeader.indexOf(','); - if(nextBreak < 0){ - //os_printf("AUTH FAIL: no variables\n"); + if (nextBreak < 0) { + // os_printf("AUTH FAIL: no variables\n"); return false; } - String myUsername = String(); - String myRealm = String(); - String myNonce = String(); - String myUri = String(); - String myResponse = String(); - String myQop = String(); - String myNc = String(); - String myCnonce = String(); + String myUsername; + String myRealm; + String myNonce; + String myUri; + String myResponse; + String myQop; + String myNc; + String myCnonce; myHeader += F(", "); do { - String avLine = myHeader.substring(0, nextBreak); + String avLine(myHeader.substring(0, nextBreak)); avLine.trim(); - myHeader = myHeader.substring(nextBreak+1); + myHeader = myHeader.substring(nextBreak + 1); nextBreak = myHeader.indexOf(','); int eqSign = avLine.indexOf('='); - if(eqSign < 0){ - //os_printf("AUTH FAIL: no = sign\n"); + if (eqSign < 0) { + // os_printf("AUTH FAIL: no = sign\n"); return false; } - String varName = avLine.substring(0, eqSign); + String varName(avLine.substring(0, eqSign)); avLine = avLine.substring(eqSign + 1); - if(avLine.startsWith(String('"'))){ + if (avLine.startsWith(String('"'))) { avLine = avLine.substring(1, avLine.length() - 1); } - if(varName.equals(F("username"))){ - if(!avLine.equals(username)){ - //os_printf("AUTH FAIL: username\n"); + if (varName.equals(F("username"))) { + if (!avLine.equals(username)) { + // os_printf("AUTH FAIL: username\n"); return false; } myUsername = avLine; - } else if(varName.equals(F("realm"))){ - if(realm != NULL && !avLine.equals(realm)){ - //os_printf("AUTH FAIL: realm\n"); + } else if (varName.equals(F("realm"))) { + if (realm != NULL && !avLine.equals(realm)) { + // os_printf("AUTH FAIL: realm\n"); return false; } myRealm = avLine; - } else if(varName.equals(F("nonce"))){ - if(nonce != NULL && !avLine.equals(nonce)){ - //os_printf("AUTH FAIL: nonce\n"); + } else if (varName.equals(F("nonce"))) { + if (nonce != NULL && !avLine.equals(nonce)) { + // os_printf("AUTH FAIL: nonce\n"); return false; } myNonce = avLine; - } else if(varName.equals(F("opaque"))){ - if(opaque != NULL && !avLine.equals(opaque)){ - //os_printf("AUTH FAIL: opaque\n"); + } else if (varName.equals(F("opaque"))) { + if (opaque != NULL && !avLine.equals(opaque)) { + // os_printf("AUTH FAIL: opaque\n"); return false; } - } else if(varName.equals(F("uri"))){ - if(uri != NULL && !avLine.equals(uri)){ - //os_printf("AUTH FAIL: uri\n"); + } else if (varName.equals(F("uri"))) { + if (uri != NULL && !avLine.equals(uri)) { + // os_printf("AUTH FAIL: uri\n"); return false; } myUri = avLine; - } else if(varName.equals(F("response"))){ + } else if (varName.equals(F("response"))) { myResponse = avLine; - } else if(varName.equals(F("qop"))){ + } else if (varName.equals(F("qop"))) { myQop = avLine; - } else if(varName.equals(F("nc"))){ + } else if (varName.equals(F("nc"))) { myNc = avLine; - } else if(varName.equals(F("cnonce"))){ + } else if (varName.equals(F("cnonce"))) { myCnonce = avLine; } - } while(nextBreak > 0); + } while (nextBreak > 0); - String ha1 = (passwordIsHash) ? String(password) : stringMD5(myUsername + ':' + myRealm + ':' + String(password)); + String ha1 = (passwordIsHash) ? String(password) : stringMD5(myUsername + ':' + myRealm + ':' + password); String ha2 = String(method) + ':' + myUri; String response = ha1 + ':' + myNonce + ':' + myNc + ':' + myCnonce + ':' + myQop + ':' + stringMD5(ha2); - if(myResponse.equals(stringMD5(response))){ - //os_printf("AUTH SUCCESS\n"); + if (myResponse.equals(stringMD5(response))) { + // os_printf("AUTH SUCCESS\n"); return true; } - //os_printf("AUTH FAIL: password\n"); + // os_printf("AUTH FAIL: password\n"); return false; } diff --git a/src/WebAuthentication.h b/src/WebAuthentication.h index a6f1966..d519777 100644 --- a/src/WebAuthentication.h +++ b/src/WebAuthentication.h @@ -24,11 +24,16 @@ #include "Arduino.h" -bool checkBasicAuthentication(const char * header, const char * username, const char * password); -String requestDigestAuthentication(const char * realm); -bool checkDigestAuthentication(const char * header, const __FlashStringHelper *method, const char * username, const char * password, const char * realm, bool passwordIsHash, const char * nonce, const char * opaque, const char * uri); +bool checkBasicAuthentication(const char* header, const char* username, const char* password); +String requestDigestAuthentication(const char* realm); -//for storing hashed versions on the device that can be authenticated against -String generateDigestHash(const char * username, const char * password, const char * realm); +bool checkDigestAuthentication(const char* header, const char* method, const char* username, const char* password, const char* realm, bool passwordIsHash, const char* nonce, const char* opaque, const char* uri); + +#ifdef ESP8266 +bool checkDigestAuthentication(const char* header, const __FlashStringHelper* method, const char* username, const char* password, const char* realm, bool passwordIsHash, const char* nonce, const char* opaque, const char* uri); +#endif + +// for storing hashed versions on the device that can be authenticated against +String generateDigestHash(const char* username, const char* password, const char* realm); #endif diff --git a/src/WebHandlerImpl.h b/src/WebHandlerImpl.h index 9b7ba1b..22757d7 100644 --- a/src/WebHandlerImpl.h +++ b/src/WebHandlerImpl.h @@ -23,19 +23,21 @@ #include #ifdef ASYNCWEBSERVER_REGEX -#include + #include #endif #include "stddef.h" #include -class AsyncStaticWebHandler: public AsyncWebHandler { - using File = fs::File; - using FS = fs::FS; +class AsyncStaticWebHandler : public AsyncWebHandler { + using File = fs::File; + using FS = fs::FS; + private: - bool _getFile(AsyncWebServerRequest *request); - bool _fileExists(AsyncWebServerRequest *request, const String& path); + bool _getFile(AsyncWebServerRequest* request); + bool _fileExists(AsyncWebServerRequest* request, const String& path); uint8_t _countBits(const uint8_t value) const; + protected: FS _fs; String _uri; @@ -47,23 +49,27 @@ class AsyncStaticWebHandler: public AsyncWebHandler { bool _isDir; bool _gzipFirst; uint8_t _gzipStats; + public: AsyncStaticWebHandler(const char* uri, FS& fs, const char* path, const char* cache_control); - virtual bool canHandle(AsyncWebServerRequest *request) override final; - virtual void handleRequest(AsyncWebServerRequest *request) override final; + virtual bool canHandle(AsyncWebServerRequest* request) override final; + virtual void handleRequest(AsyncWebServerRequest* request) override final; AsyncStaticWebHandler& setIsDir(bool isDir); AsyncStaticWebHandler& setDefaultFile(const char* filename); AsyncStaticWebHandler& setCacheControl(const char* cache_control); AsyncStaticWebHandler& setLastModified(const char* last_modified); AsyncStaticWebHandler& setLastModified(struct tm* last_modified); - #ifdef ESP8266 +#ifdef ESP8266 AsyncStaticWebHandler& setLastModified(time_t last_modified); - AsyncStaticWebHandler& setLastModified(); //sets to current time. Make sure sntp is runing and time is updated - #endif - AsyncStaticWebHandler& setTemplateProcessor(AwsTemplateProcessor newCallback) {_callback = newCallback; return *this;} + AsyncStaticWebHandler& setLastModified(); // sets to current time. Make sure sntp is runing and time is updated +#endif + AsyncStaticWebHandler& setTemplateProcessor(AwsTemplateProcessor newCallback) { + _callback = newCallback; + return *this; + } }; -class AsyncCallbackWebHandler: public AsyncWebHandler { +class AsyncCallbackWebHandler : public AsyncWebHandler { private: protected: String _uri; @@ -72,23 +78,24 @@ class AsyncCallbackWebHandler: public AsyncWebHandler { ArUploadHandlerFunction _onUpload; ArBodyHandlerFunction _onBody; bool _isRegex; + public: AsyncCallbackWebHandler() : _uri(), _method(HTTP_ANY), _onRequest(NULL), _onUpload(NULL), _onBody(NULL), _isRegex(false) {} - void setUri(const String& uri){ - _uri = uri; + void setUri(const String& uri) { + _uri = uri; _isRegex = uri.startsWith("^") && uri.endsWith("$"); } - void setMethod(WebRequestMethodComposite method){ _method = method; } - void onRequest(ArRequestHandlerFunction fn){ _onRequest = fn; } - void onUpload(ArUploadHandlerFunction fn){ _onUpload = fn; } - void onBody(ArBodyHandlerFunction fn){ _onBody = fn; } + void setMethod(WebRequestMethodComposite method) { _method = method; } + void onRequest(ArRequestHandlerFunction fn) { _onRequest = fn; } + void onUpload(ArUploadHandlerFunction fn) { _onUpload = fn; } + void onBody(ArBodyHandlerFunction fn) { _onBody = fn; } - virtual bool canHandle(AsyncWebServerRequest *request) override final{ + virtual bool canHandle(AsyncWebServerRequest* request) override final { - if(!_onRequest) + if (!_onRequest) return false; - if(!(_method & request->method())) + if (!(_method & request->method())) return false; #ifdef ASYNCWEBSERVER_REGEX @@ -96,56 +103,53 @@ class AsyncCallbackWebHandler: public AsyncWebHandler { std::regex pattern(_uri.c_str()); std::smatch matches; std::string s(request->url().c_str()); - if(std::regex_search(s, matches, pattern)) { + if (std::regex_search(s, matches, pattern)) { for (size_t i = 1; i < matches.size(); ++i) { // start from 1 request->_addPathParam(matches[i].str().c_str()); } } else { return false; } - } else + } else #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.startsWith("/*.")) { String uriTemplate = String(_uri); - uriTemplate = uriTemplate.substring(0, uriTemplate.length() - 1); + uriTemplate = uriTemplate.substring(uriTemplate.lastIndexOf(".")); + if (!request->url().endsWith(uriTemplate)) + return false; + } else if (_uri.length() && _uri.endsWith("*")) { + String uriTemplate = String(_uri); + uriTemplate = uriTemplate.substring(0, uriTemplate.length() - 1); if (!request->url().startsWith(uriTemplate)) return false; - } - else if(_uri.length() && (_uri != request->url() && !request->url().startsWith(_uri+"/"))) + } else if (_uri.length() && (_uri != request->url() && !request->url().startsWith(_uri + "/"))) return false; request->addInterestingHeader("ANY"); return true; } - - virtual void handleRequest(AsyncWebServerRequest *request) override final { - if((_username != "" && _password != "") && !request->authenticate(_username.c_str(), _password.c_str())) + + 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); else request->send(500); } - 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())) + 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); } - 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())) + 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); } - virtual bool isRequestHandlerTrivial() override final {return _onRequest ? false : true;} + virtual bool isRequestHandlerTrivial() override final { return _onRequest ? false : true; } }; #endif /* ASYNCWEBSERVERHANDLERIMPL_H_ */ diff --git a/src/WebHandlers.cpp b/src/WebHandlers.cpp index 0b55b0d..d740d86 100644 --- a/src/WebHandlers.cpp +++ b/src/WebHandlers.cpp @@ -22,73 +22,73 @@ #include "WebHandlerImpl.h" AsyncStaticWebHandler::AsyncStaticWebHandler(const char* uri, FS& fs, const char* path, const char* cache_control) - : _fs(fs), _uri(uri), _path(path), _default_file(F("index.htm")), _cache_control(cache_control), _last_modified(), _callback(nullptr) -{ + : _fs(fs), _uri(uri), _path(path), _default_file(F("index.htm")), _cache_control(cache_control), _last_modified(), _callback(nullptr) { // Ensure leading '/' - if (_uri.length() == 0 || _uri[0] != '/') _uri = String('/') + _uri; - if (_path.length() == 0 || _path[0] != '/') _path = String('/') + _path; + if (_uri.length() == 0 || _uri[0] != '/') + _uri = String('/') + _uri; + if (_path.length() == 0 || _path[0] != '/') + _path = String('/') + _path; // If path ends with '/' we assume a hint that this is a directory to improve performance. // However - if it does not end with '/' we, can't assume a file, path can still be a directory. - _isDir = _path[_path.length()-1] == '/'; + _isDir = _path[_path.length() - 1] == '/'; // Remove the trailing '/' so we can handle default file // Notice that root will be "" not "/" - if (_uri[_uri.length()-1] == '/') _uri = _uri.substring(0, _uri.length()-1); - if (_path[_path.length()-1] == '/') _path = _path.substring(0, _path.length()-1); + if (_uri[_uri.length() - 1] == '/') + _uri = _uri.substring(0, _uri.length() - 1); + if (_path[_path.length() - 1] == '/') + _path = _path.substring(0, _path.length() - 1); // Reset stats _gzipFirst = false; _gzipStats = 0xF8; } -AsyncStaticWebHandler& AsyncStaticWebHandler::setIsDir(bool isDir){ +AsyncStaticWebHandler& AsyncStaticWebHandler::setIsDir(bool isDir) { _isDir = isDir; return *this; } -AsyncStaticWebHandler& AsyncStaticWebHandler::setDefaultFile(const char* filename){ +AsyncStaticWebHandler& AsyncStaticWebHandler::setDefaultFile(const char* filename) { _default_file = String(filename); return *this; } -AsyncStaticWebHandler& AsyncStaticWebHandler::setCacheControl(const char* cache_control){ +AsyncStaticWebHandler& AsyncStaticWebHandler::setCacheControl(const char* cache_control) { _cache_control = String(cache_control); return *this; } -AsyncStaticWebHandler& AsyncStaticWebHandler::setLastModified(const char* last_modified){ +AsyncStaticWebHandler& AsyncStaticWebHandler::setLastModified(const char* last_modified) { _last_modified = last_modified; return *this; } -AsyncStaticWebHandler& AsyncStaticWebHandler::setLastModified(struct tm* last_modified){ +AsyncStaticWebHandler& AsyncStaticWebHandler::setLastModified(struct tm* last_modified) { auto formatP = PSTR("%a, %d %b %Y %H:%M:%S %Z"); char format[strlen_P(formatP) + 1]; strcpy_P(format, formatP); char result[30]; strftime(result, sizeof(result), format, last_modified); - return setLastModified((const char *)result); + return setLastModified((const char*)result); } #ifdef ESP8266 -AsyncStaticWebHandler& AsyncStaticWebHandler::setLastModified(time_t last_modified){ - return setLastModified((struct tm *)gmtime(&last_modified)); +AsyncStaticWebHandler& AsyncStaticWebHandler::setLastModified(time_t last_modified) { + return setLastModified((struct tm*)gmtime(&last_modified)); } -AsyncStaticWebHandler& AsyncStaticWebHandler::setLastModified(){ +AsyncStaticWebHandler& AsyncStaticWebHandler::setLastModified() { time_t last_modified; - if(time(&last_modified) == 0) //time is not yet set + if (time(&last_modified) == 0) // time is not yet set return *this; return setLastModified(last_modified); } #endif -bool AsyncStaticWebHandler::canHandle(AsyncWebServerRequest *request){ - if(request->method() != HTTP_GET - || !request->url().startsWith(_uri) - || !request->isExpectedRequestedConnType(RCT_DEFAULT, RCT_HTTP) - ){ +bool AsyncStaticWebHandler::canHandle(AsyncWebServerRequest* request) { + if (request->method() != HTTP_GET || !request->url().startsWith(_uri) || !request->isExpectedRequestedConnType(RCT_DEFAULT, RCT_HTTP)) { return false; } if (_getFile(request)) { @@ -96,7 +96,7 @@ bool AsyncStaticWebHandler::canHandle(AsyncWebServerRequest *request){ if (_last_modified.length()) request->addInterestingHeader(F("If-Modified-Since")); - if(_cache_control.length()) + if (_cache_control.length()) request->addInterestingHeader(F("If-None-Match")); return true; @@ -105,13 +105,12 @@ bool AsyncStaticWebHandler::canHandle(AsyncWebServerRequest *request){ return false; } -bool AsyncStaticWebHandler::_getFile(AsyncWebServerRequest *request) -{ +bool AsyncStaticWebHandler::_getFile(AsyncWebServerRequest* request) { // Remove the found uri String path = request->url().substring(_uri.length()); // We can skip the file check and look for default if request is to the root of a directory or that request path ends with '/' - bool canSkipFileCheck = (_isDir && path.length() == 0) || (path.length() && path[path.length()-1] == '/'); + bool canSkipFileCheck = (_isDir && path.length() == 0) || (path.length() && path[path.length() - 1] == '/'); path = _path + path; @@ -124,7 +123,7 @@ bool AsyncStaticWebHandler::_getFile(AsyncWebServerRequest *request) return false; // Try to add default file, ensure there is a trailing '/' ot the path. - if (path.length() == 0 || path[path.length()-1] != '/') + if (path.length() == 0 || path[path.length() - 1] != '/') path += String('/'); path += _default_file; @@ -132,39 +131,38 @@ bool AsyncStaticWebHandler::_getFile(AsyncWebServerRequest *request) } #ifdef ESP32 -#define FILE_IS_REAL(f) (f == true && !f.isDirectory()) + #define FILE_IS_REAL(f) (f == true && !f.isDirectory()) #else -#define FILE_IS_REAL(f) (f == true) + #define FILE_IS_REAL(f) (f == true) #endif -bool AsyncStaticWebHandler::_fileExists(AsyncWebServerRequest *request, const String& path) -{ +bool AsyncStaticWebHandler::_fileExists(AsyncWebServerRequest* request, const String& path) { bool fileFound = false; bool gzipFound = false; String gzip = path + F(".gz"); if (_gzipFirst) { - if (_fs.exists(gzip)) { - request->_tempFile = _fs.open(gzip, fs::FileOpenMode::read); - gzipFound = FILE_IS_REAL(request->_tempFile); - } - if (!gzipFound){ - if (_fs.exists(path)) { - request->_tempFile = _fs.open(path, fs::FileOpenMode::read); - fileFound = FILE_IS_REAL(request->_tempFile); - } - } - } else { - if (_fs.exists(path)) { - request->_tempFile = _fs.open(path, fs::FileOpenMode::read); - fileFound = FILE_IS_REAL(request->_tempFile); - } - if (!fileFound){ - if (_fs.exists(gzip)) { + if (_fs.exists(gzip)) { request->_tempFile = _fs.open(gzip, fs::FileOpenMode::read); gzipFound = FILE_IS_REAL(request->_tempFile); - } + } + if (!gzipFound) { + if (_fs.exists(path)) { + request->_tempFile = _fs.open(path, fs::FileOpenMode::read); + fileFound = FILE_IS_REAL(request->_tempFile); + } + } + } else { + if (_fs.exists(path)) { + request->_tempFile = _fs.open(path, fs::FileOpenMode::read); + fileFound = FILE_IS_REAL(request->_tempFile); + } + if (!fileFound) { + if (_fs.exists(gzip)) { + request->_tempFile = _fs.open(gzip, fs::FileOpenMode::read); + gzipFound = FILE_IS_REAL(request->_tempFile); + } } } @@ -173,55 +171,71 @@ bool AsyncStaticWebHandler::_fileExists(AsyncWebServerRequest *request, const St if (found) { // Extract the file name from the path and keep it in _tempObject size_t pathLen = path.length(); - char * _tempPath = (char*)malloc(pathLen+1); - snprintf_P(_tempPath, pathLen+1, PSTR("%s"), path.c_str()); + char* _tempPath = (char*)malloc(pathLen + 1); + snprintf_P(_tempPath, pathLen + 1, PSTR("%s"), path.c_str()); request->_tempObject = (void*)_tempPath; // Calculate gzip statistic _gzipStats = (_gzipStats << 1) + (gzipFound ? 1 : 0); - if (_gzipStats == 0x00) _gzipFirst = false; // All files are not gzip - else if (_gzipStats == 0xFF) _gzipFirst = true; // All files are gzip - else _gzipFirst = _countBits(_gzipStats) > 4; // IF we have more gzip files - try gzip first + if (_gzipStats == 0x00) + _gzipFirst = false; // All files are not gzip + else if (_gzipStats == 0xFF) + _gzipFirst = true; // All files are gzip + else + _gzipFirst = _countBits(_gzipStats) > 4; // IF we have more gzip files - try gzip first } return found; } -uint8_t AsyncStaticWebHandler::_countBits(const uint8_t value) const -{ +uint8_t AsyncStaticWebHandler::_countBits(const uint8_t value) const { uint8_t w = value; uint8_t n; - for (n=0; w!=0; n++) w&=w-1; + for (n = 0; w != 0; n++) + w &= w - 1; return n; } -void AsyncStaticWebHandler::handleRequest(AsyncWebServerRequest *request) -{ +void AsyncStaticWebHandler::handleRequest(AsyncWebServerRequest* request) { // Get the filename from request->_tempObject and free it String filename = String((char*)request->_tempObject); free(request->_tempObject); request->_tempObject = NULL; - if((_username.length() && _password.length()) && !request->authenticate(_username.c_str(), _password.c_str())) - return request->requestAuthentication(); + if ((_username.length() && _password.length()) && !request->authenticate(_username.c_str(), _password.c_str())) + return request->requestAuthentication(); if (request->_tempFile == true) { - time_t lw = request->_tempFile.getLastWrite(); // get last file mod time (if supported by FS) - if (lw) setLastModified(gmtime(&lw)); - String etag(lw ? lw : request->_tempFile.size()); // set etag to lastmod timestamp if available, otherwise to size + time_t lw = request->_tempFile.getLastWrite(); // get last file mod time (if supported by FS) + // set etag to lastmod timestamp if available, otherwise to size + String etag; + if (lw) { + setLastModified(gmtime(&lw)); +#if defined(TARGET_RP2040) + // time_t == long long int + const size_t len = 1 + 8 * sizeof(time_t); + char buf[len]; + char* ret = lltoa(lw, buf, len, 10); + etag = ret ? String(ret) : String(request->_tempFile.size()); +#else + etag = String(lw); +#endif + } else { + etag = String(request->_tempFile.size()); + } if (_last_modified.length() && _last_modified == request->header(F("If-Modified-Since"))) { request->_tempFile.close(); request->send(304); // Not modified } else if (_cache_control.length() && request->hasHeader(F("If-None-Match")) && request->header(F("If-None-Match")).equals(etag)) { request->_tempFile.close(); - AsyncWebServerResponse * response = new AsyncBasicResponse(304); // Not modified + AsyncWebServerResponse* response = new AsyncBasicResponse(304); // Not modified response->addHeader(F("Cache-Control"), _cache_control); response->addHeader(F("ETag"), etag); request->send(response); } else { - AsyncWebServerResponse * response = new AsyncFileResponse(request->_tempFile, filename, String(), false, _callback); + AsyncWebServerResponse* response = new AsyncFileResponse(request->_tempFile, filename, String(), false, _callback); if (_last_modified.length()) response->addHeader(F("Last-Modified"), _last_modified); - if (_cache_control.length()){ + if (_cache_control.length()) { response->addHeader(F("Cache-Control"), _cache_control); response->addHeader(F("ETag"), etag); } diff --git a/src/WebRequest.cpp b/src/WebRequest.cpp index 31613df..2331b13 100644 --- a/src/WebRequest.cpp +++ b/src/WebRequest.cpp @@ -19,187 +19,157 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "ESPAsyncWebServer.h" -#include "WebResponseImpl.h" #include "WebAuthentication.h" +#include "WebResponseImpl.h" #ifndef ESP8266 -#define os_strlen strlen + #define os_strlen strlen #endif #define __is_param_char(c) ((c) && ((c) != '{') && ((c) != '[') && ((c) != '&') && ((c) != '=')) -enum { PARSE_REQ_START, PARSE_REQ_HEADERS, PARSE_REQ_BODY, PARSE_REQ_END, PARSE_REQ_FAIL }; +enum { PARSE_REQ_START, + PARSE_REQ_HEADERS, + PARSE_REQ_BODY, + PARSE_REQ_END, + PARSE_REQ_FAIL }; AsyncWebServerRequest::AsyncWebServerRequest(AsyncWebServer* s, AsyncClient* c) - : _client(c) - , _server(s) - , _handler(NULL) - , _response(NULL) - , _temp() - , _parseState(0) - , _version(0) - , _method(HTTP_ANY) - , _url() - , _host() - , _contentType() - , _boundary() - , _authorization() - , _reqconntype(RCT_HTTP) - , _isDigest(false) - , _isMultipart(false) - , _isPlainPost(false) - , _expectingContinue(false) - , _contentLength(0) - , _parsedLength(0) - , _params(LinkedList([](AsyncWebParameter *p){ delete p; })) - , _multiParseState(0) - , _boundaryPosition(0) - , _itemStartIndex(0) - , _itemSize(0) - , _itemName() - , _itemFilename() - , _itemType() - , _itemValue() - , _itemBuffer(0) - , _itemBufferIndex(0) - , _itemIsFile(false) - , _tempObject(NULL) -{ - c->onError([](void *r, AsyncClient* c, int8_t error){ (void)c; AsyncWebServerRequest *req = (AsyncWebServerRequest*)r; req->_onError(error); }, this); - c->onAck([](void *r, AsyncClient* c, size_t len, uint32_t time){ (void)c; AsyncWebServerRequest *req = (AsyncWebServerRequest*)r; req->_onAck(len, time); }, this); - c->onDisconnect([](void *r, AsyncClient* c){ AsyncWebServerRequest *req = (AsyncWebServerRequest*)r; req->_onDisconnect(); delete c; }, this); - c->onTimeout([](void *r, AsyncClient* c, uint32_t time){ (void)c; AsyncWebServerRequest *req = (AsyncWebServerRequest*)r; req->_onTimeout(time); }, this); - c->onData([](void *r, AsyncClient* c, void *buf, size_t len){ (void)c; AsyncWebServerRequest *req = (AsyncWebServerRequest*)r; req->_onData(buf, len); }, this); - c->onPoll([](void *r, AsyncClient* c){ (void)c; AsyncWebServerRequest *req = ( AsyncWebServerRequest*)r; req->_onPoll(); }, this); + : _client(c), _server(s), _handler(NULL), _response(NULL), _temp(), _parseState(0), _version(0), _method(HTTP_ANY), _url(), _host(), _contentType(), _boundary(), _authorization(), _reqconntype(RCT_HTTP), _isDigest(false), _isMultipart(false), _isPlainPost(false), _expectingContinue(false), _contentLength(0), _parsedLength(0), _multiParseState(0), _boundaryPosition(0), _itemStartIndex(0), _itemSize(0), _itemName(), _itemFilename(), _itemType(), _itemValue(), _itemBuffer(0), _itemBufferIndex(0), _itemIsFile(false), _tempObject(NULL) { + c->onError([](void* r, AsyncClient* c, int8_t error) { (void)c; AsyncWebServerRequest *req = (AsyncWebServerRequest*)r; req->_onError(error); }, this); + c->onAck([](void* r, AsyncClient* c, size_t len, uint32_t time) { (void)c; AsyncWebServerRequest *req = (AsyncWebServerRequest*)r; req->_onAck(len, time); }, this); + c->onDisconnect([](void* r, AsyncClient* c) { AsyncWebServerRequest *req = (AsyncWebServerRequest*)r; req->_onDisconnect(); delete c; }, this); + c->onTimeout([](void* r, AsyncClient* c, uint32_t time) { (void)c; AsyncWebServerRequest *req = (AsyncWebServerRequest*)r; req->_onTimeout(time); }, this); + c->onData([](void* r, AsyncClient* c, void* buf, size_t len) { (void)c; AsyncWebServerRequest *req = (AsyncWebServerRequest*)r; req->_onData(buf, len); }, this); + c->onPoll([](void* r, AsyncClient* c) { (void)c; AsyncWebServerRequest *req = ( AsyncWebServerRequest*)r; req->_onPoll(); }, this); } -AsyncWebServerRequest::~AsyncWebServerRequest(){ +AsyncWebServerRequest::~AsyncWebServerRequest() { _headers.clear(); - _params.free(); _pathParams.clear(); _interestingHeaders.clear(); - if(_response != NULL){ + if (_response != NULL) { delete _response; } - if(_tempObject != NULL){ + if (_tempObject != NULL) { free(_tempObject); } - if(_tempFile){ + if (_tempFile) { _tempFile.close(); } - - if(_itemBuffer){ + + if (_itemBuffer) { free(_itemBuffer); } - } -void AsyncWebServerRequest::_onData(void *buf, size_t len){ +void AsyncWebServerRequest::_onData(void* buf, size_t len) { size_t i = 0; while (true) { - if(_parseState < PARSE_REQ_BODY){ - // Find new line in buf - char *str = (char*)buf; - for (i = 0; i < len; i++) { - if (str[i] == '\n') { - break; - } - } - if (i == len) { // No new line, just add the buffer in _temp - char ch = str[len-1]; - str[len-1] = 0; - _temp.reserve(_temp.length()+len); - _temp.concat(str); - _temp.concat(ch); - } else { // Found new line - extract it and parse - str[i] = 0; // Terminate the string at the end of the line. - _temp.concat(str); - _temp.trim(); - _parseLine(); - if (++i < len) { - // Still have more buffer to process - buf = str+i; - len-= i; - continue; - } - } - } else if(_parseState == PARSE_REQ_BODY){ - // A handler should be already attached at this point in _parseLine function. - // If handler does nothing (_onRequest is NULL), we don't need to really parse the body. - const bool needParse = _handler && !_handler->isRequestHandlerTrivial(); - if(_isMultipart){ - if(needParse){ - size_t i; - for(i=0; iisRequestHandlerTrivial(); + if (_isMultipart) { + if (needParse) { + size_t i; + for (i = 0; i < len; i++) { + _parseMultipartPostByte(((uint8_t*)buf)[i], i == len - 1); + _parsedLength++; + } + } else _parsedLength += len; - } else { - if(_parsedLength == 0){ - if(_contentType.startsWith(F("application/x-www-form-urlencoded"))){ - _isPlainPost = true; - } else if(_contentType == F("text/plain") && __is_param_char(((char*)buf)[0])){ - size_t i = 0; - while (ihandleBody(this, (uint8_t*)buf, len, _parsedLength, _contentLength); - _parsedLength += len; - } else if(needParse) { - size_t i; - for(i=0; ihandleBody(this, (uint8_t*)buf, len, _parsedLength, _contentLength); + _parsedLength += len; + } else if (needParse) { + size_t i; + for (i = 0; i < len; i++) { + _parsedLength++; + _parsePlainPostChar(((uint8_t*)buf)[i]); + } + } else { + _parsedLength += len; } - } else { - _parsedLength += len; + } + if (_parsedLength == _contentLength) { + _parseState = PARSE_REQ_END; + // check if authenticated before calling handleRequest and request auth instead + if (_handler) + _handler->handleRequest(this); + else + send(501); } } - if(_parsedLength == _contentLength){ - _parseState = PARSE_REQ_END; - //check if authenticated before calling handleRequest and request auth instead - if(_handler) _handler->handleRequest(this); - else send(501); - } - } - break; + break; } } -void AsyncWebServerRequest::_removeNotInterestingHeaders(){ - if (std::any_of(std::begin(_interestingHeaders), std::end(_interestingHeaders), - [](const String &str){ return str.equalsIgnoreCase(F("ANY")); })) - return; // nothing to do +void AsyncWebServerRequest::_removeNotInterestingHeaders() { + if (std::any_of(std::begin(_interestingHeaders), std::end(_interestingHeaders), [](const String& str) { return str.equalsIgnoreCase(F("ANY")); })) + return; // nothing to do - for(auto iter = std::begin(_headers); iter != std::end(_headers); ) - { - const auto name = iter->name(); + 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++; + 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(){ - //os_printf("p\n"); - if(_response != NULL && _client != NULL && _client->canSend()){ - if(!_response->_finished()){ +void AsyncWebServerRequest::_onPoll() { + // os_printf("p\n"); + if (_response != NULL && _client != NULL && _client->canSend()) { + if (!_response->_finished()) { _response->_ack(this, 0, 0); } else { AsyncWebServerResponse* r = _response; @@ -211,12 +181,12 @@ void AsyncWebServerRequest::_onPoll(){ } } -void AsyncWebServerRequest::_onAck(size_t len, uint32_t time){ - //os_printf("a:%u:%u\n", len, time); - if(_response != NULL){ - if(!_response->_finished()){ +void AsyncWebServerRequest::_onAck(size_t len, uint32_t time) { + // os_printf("a:%u:%u\n", len, time); + if (_response != NULL) { + if (!_response->_finished()) { _response->_ack(this, len, time); - } else if(_response->_finished()){ + } else if (_response->_finished()) { AsyncWebServerResponse* r = _response; _response = NULL; delete r; @@ -226,142 +196,142 @@ void AsyncWebServerRequest::_onAck(size_t len, uint32_t time){ } } -void AsyncWebServerRequest::_onError(int8_t error){ +void AsyncWebServerRequest::_onError(int8_t error) { (void)error; } -void AsyncWebServerRequest::_onTimeout(uint32_t time){ +void AsyncWebServerRequest::_onTimeout(uint32_t time) { (void)time; - //os_printf("TIMEOUT: %u, state: %s\n", time, _client->stateToString()); + // os_printf("TIMEOUT: %u, state: %s\n", time, _client->stateToString()); _client->close(); } -void AsyncWebServerRequest::onDisconnect (ArDisconnectHandler fn){ - _onDisconnectfn=fn; +void AsyncWebServerRequest::onDisconnect(ArDisconnectHandler fn) { + _onDisconnectfn = fn; } -void AsyncWebServerRequest::_onDisconnect(){ - //os_printf("d\n"); - if(_onDisconnectfn) { - _onDisconnectfn(); - } +void AsyncWebServerRequest::_onDisconnect() { + // os_printf("d\n"); + if (_onDisconnectfn) { + _onDisconnectfn(); + } _server->_handleDisconnect(this); } -void AsyncWebServerRequest::_addParam(AsyncWebParameter *p){ - _params.add(p); -} - -void AsyncWebServerRequest::_addPathParam(const char *p){ +void AsyncWebServerRequest::_addPathParam(const char* p) { _pathParams.emplace_back(p); } -void AsyncWebServerRequest::_addGetParams(const String& params){ +void AsyncWebServerRequest::_addGetParams(const String& params) { size_t start = 0; - while (start < params.length()){ + while (start < params.length()) { int end = params.indexOf('&', start); - if (end < 0) end = params.length(); + if (end < 0) + end = params.length(); int equal = params.indexOf('=', start); - if (equal < 0 || equal > end) equal = end; - String name = params.substring(start, equal); - String value = equal + 1 < end ? params.substring(equal + 1, end) : String(); - _addParam(new AsyncWebParameter(urlDecode(name), urlDecode(value))); + if (equal < 0 || equal > end) + equal = end; + String name(params.substring(start, equal)); + String value(equal + 1 < end ? params.substring(equal + 1, end) : String()); + _params.emplace_back(urlDecode(name), urlDecode(value)); start = end + 1; } } -bool AsyncWebServerRequest::_parseReqHead(){ +bool AsyncWebServerRequest::_parseReqHead() { // Split the head into method, url and version int index = _temp.indexOf(' '); String m = _temp.substring(0, index); - index = _temp.indexOf(' ', index+1); - String u = _temp.substring(m.length()+1, index); - _temp = _temp.substring(index+1); + index = _temp.indexOf(' ', index + 1); + String u = _temp.substring(m.length() + 1, index); + _temp = _temp.substring(index + 1); - if(m == F("GET")){ + if (m == F("GET")) { _method = HTTP_GET; - } else if(m == F("POST")){ + } else if (m == F("POST")) { _method = HTTP_POST; - } else if(m == F("DELETE")){ + } else if (m == F("DELETE")) { _method = HTTP_DELETE; - } else if(m == F("PUT")){ + } else if (m == F("PUT")) { _method = HTTP_PUT; - } else if(m == F("PATCH")){ + } else if (m == F("PATCH")) { _method = HTTP_PATCH; - } else if(m == F("HEAD")){ + } else if (m == F("HEAD")) { _method = HTTP_HEAD; - } else if(m == F("OPTIONS")){ + } else if (m == F("OPTIONS")) { _method = HTTP_OPTIONS; } String g; index = u.indexOf('?'); - if(index > 0){ - g = u.substring(index +1); + if (index > 0) { + g = u.substring(index + 1); u = u.substring(0, index); } _url = urlDecode(u); _addGetParams(g); - if(!_temp.startsWith(F("HTTP/1.0"))) + if (!_temp.startsWith(F("HTTP/1.0"))) _version = 1; _temp = String(); return true; } -bool strContains(const String &src, const String &find, bool mindcase = true) { - int pos=0, i=0; +bool strContains(const String& src, const String& find, bool mindcase = true) { + int pos = 0, i = 0; const int slen = src.length(); const int flen = find.length(); - if (slen < flen) return false; + if (slen < flen) + return false; while (pos <= (slen - flen)) { - for (i=0; i < flen; i++) { + for (i = 0; i < flen; i++) { if (mindcase) { - if (src[pos+i] != find[i]) i = flen + 1; // no match - } - else if (tolower(src[pos+i]) != tolower(find[i])) { + if (src[pos + i] != find[i]) + i = flen + 1; // no match + } else if (tolower(src[pos + i]) != tolower(find[i])) { i = flen + 1; // no match } } - if (i == flen) return true; + if (i == flen) + return true; pos++; } return false; } -bool AsyncWebServerRequest::_parseReqHeader(){ +bool AsyncWebServerRequest::_parseReqHeader() { int index = _temp.indexOf(':'); - if(index){ + if (index) { String name = _temp.substring(0, index); String value = _temp.substring(index + 2); - if(name.equalsIgnoreCase("Host")){ + if (name.equalsIgnoreCase("Host")) { _host = value; - } else if(name.equalsIgnoreCase(F("Content-Type"))){ - _contentType = value.substring(0, value.indexOf(';')); - if (value.startsWith(F("multipart/"))){ - _boundary = value.substring(value.indexOf('=')+1); + } else if (name.equalsIgnoreCase(F("Content-Type"))) { + _contentType = value.substring(0, value.indexOf(';')); + if (value.startsWith(F("multipart/"))) { + _boundary = value.substring(value.indexOf('=') + 1); _boundary.replace(String('"'), String()); _isMultipart = true; } - } else if(name.equalsIgnoreCase(F("Content-Length"))){ + } else if (name.equalsIgnoreCase(F("Content-Length"))) { _contentLength = atoi(value.c_str()); - } else if(name.equalsIgnoreCase(F("Expect")) && value == F("100-continue")){ + } else if (name.equalsIgnoreCase(F("Expect")) && value == F("100-continue")) { _expectingContinue = true; - } else if(name.equalsIgnoreCase(F("Authorization"))){ - if(value.length() > 5 && value.substring(0,5).equalsIgnoreCase(F("Basic"))){ + } else if (name.equalsIgnoreCase(F("Authorization"))) { + if (value.length() > 5 && value.substring(0, 5).equalsIgnoreCase(F("Basic"))) { _authorization = value.substring(6); - } else if(value.length() > 6 && value.substring(0,6).equalsIgnoreCase(F("Digest"))){ + } else if (value.length() > 6 && value.substring(0, 6).equalsIgnoreCase(F("Digest"))) { _isDigest = true; _authorization = value.substring(7); } } else { - if(name.equalsIgnoreCase(F("Upgrade")) && value.equalsIgnoreCase(F("websocket"))){ + if (name.equalsIgnoreCase(F("Upgrade")) && value.equalsIgnoreCase(F("websocket"))) { // WebSocket request can be uniquely identified by header: [Upgrade: websocket] _reqconntype = RCT_WS; } else { - if(name.equalsIgnoreCase(F("Accept")) && strContains(value, F("text/event-stream"), false)){ + if (name.equalsIgnoreCase(F("Accept")) && strContains(value, F("text/event-stream"), false)) { // WebEvent request can be uniquely identified by header: [Accept: text/event-stream] _reqconntype = RCT_EVENT; } @@ -373,27 +343,27 @@ bool AsyncWebServerRequest::_parseReqHeader(){ return true; } -void AsyncWebServerRequest::_parsePlainPostChar(uint8_t data){ - if(data && (char)data != '&') +void AsyncWebServerRequest::_parsePlainPostChar(uint8_t data) { + if (data && (char)data != '&') _temp += (char)data; - if(!data || (char)data == '&' || _parsedLength == _contentLength){ + if (!data || (char)data == '&' || _parsedLength == _contentLength) { String name = F("body"); String value = _temp; - if(!_temp.startsWith(String('{')) && !_temp.startsWith(String('[')) && _temp.indexOf('=') > 0){ + if (!_temp.startsWith(String('{')) && !_temp.startsWith(String('[')) && _temp.indexOf('=') > 0) { name = _temp.substring(0, _temp.indexOf('=')); value = _temp.substring(_temp.indexOf('=') + 1); } - _addParam(new AsyncWebParameter(urlDecode(name), urlDecode(value), true)); + _params.emplace_back(urlDecode(name), urlDecode(value), true); _temp = String(); } } -void AsyncWebServerRequest::_handleUploadByte(uint8_t data, bool last){ +void AsyncWebServerRequest::_handleUploadByte(uint8_t data, bool last) { _itemBuffer[_itemBufferIndex++] = data; - if(last || _itemBufferIndex == 1460){ - //check if authenticated before calling the upload - if(_handler) + if (last || _itemBufferIndex == 1460) { + // check if authenticated before calling the upload + if (_handler) _handler->handleUpload(this, _itemFilename, _itemSize - _itemBufferIndex, _itemBuffer, _itemBufferIndex, false); _itemBufferIndex = 0; } @@ -413,10 +383,17 @@ enum { PARSE_ERROR }; -void AsyncWebServerRequest::_parseMultipartPostByte(uint8_t data, bool last){ -#define itemWriteByte(b) do { _itemSize++; if(_itemIsFile) _handleUploadByte(b, last); else _itemValue+=(char)(b); } while(0) +void AsyncWebServerRequest::_parseMultipartPostByte(uint8_t data, bool last) { +#define itemWriteByte(b) \ + do { \ + _itemSize++; \ + if (_itemIsFile) \ + _handleUploadByte(b, last); \ + else \ + _itemValue += (char)(b); \ + } while (0) - if(!_parsedLength){ + if (!_parsedLength) { _multiParseState = EXPECT_BOUNDARY; _temp = String(); _itemName = String(); @@ -424,46 +401,46 @@ void AsyncWebServerRequest::_parseMultipartPostByte(uint8_t data, bool last){ _itemType = String(); } - if(_multiParseState == WAIT_FOR_RETURN1){ - if(data != '\r'){ + if (_multiParseState == WAIT_FOR_RETURN1) { + if (data != '\r') { itemWriteByte(data); } else { _multiParseState = EXPECT_FEED1; } - } else if(_multiParseState == EXPECT_BOUNDARY){ - if(_parsedLength < 2 && data != '-'){ + } else if (_multiParseState == EXPECT_BOUNDARY) { + if (_parsedLength < 2 && data != '-') { _multiParseState = PARSE_ERROR; return; - } else if(_parsedLength - 2 < _boundary.length() && _boundary.c_str()[_parsedLength - 2] != data){ + } else if (_parsedLength - 2 < _boundary.length() && _boundary.c_str()[_parsedLength - 2] != data) { _multiParseState = PARSE_ERROR; return; - } else if(_parsedLength - 2 == _boundary.length() && data != '\r'){ + } else if (_parsedLength - 2 == _boundary.length() && data != '\r') { _multiParseState = PARSE_ERROR; return; - } else if(_parsedLength - 3 == _boundary.length()){ - if(data != '\n'){ + } else if (_parsedLength - 3 == _boundary.length()) { + if (data != '\n') { _multiParseState = PARSE_ERROR; return; } _multiParseState = PARSE_HEADERS; _itemIsFile = false; } - } else if(_multiParseState == PARSE_HEADERS){ - if((char)data != '\r' && (char)data != '\n') - _temp += (char)data; - if((char)data == '\n'){ - if(_temp.length()){ - if(_temp.length() > 12 && _temp.substring(0, 12).equalsIgnoreCase(F("Content-Type"))){ + } else if (_multiParseState == PARSE_HEADERS) { + if ((char)data != '\r' && (char)data != '\n') + _temp += (char)data; + if ((char)data == '\n') { + if (_temp.length()) { + if (_temp.length() > 12 && _temp.substring(0, 12).equalsIgnoreCase(F("Content-Type"))) { _itemType = _temp.substring(14); _itemIsFile = true; - } else if(_temp.length() > 19 && _temp.substring(0, 19).equalsIgnoreCase(F("Content-Disposition"))){ + } else if (_temp.length() > 19 && _temp.substring(0, 19).equalsIgnoreCase(F("Content-Disposition"))) { _temp = _temp.substring(_temp.indexOf(';') + 2); - while(_temp.indexOf(';') > 0){ + while (_temp.indexOf(';') > 0) { String name = _temp.substring(0, _temp.indexOf('=')); String nameVal = _temp.substring(_temp.indexOf('=') + 2, _temp.indexOf(';') - 1); - if(name == F("name")){ + if (name == F("name")) { _itemName = nameVal; - } else if(name == F("filename")){ + } else if (name == F("filename")) { _itemFilename = nameVal; _itemIsFile = true; } @@ -471,9 +448,9 @@ void AsyncWebServerRequest::_parseMultipartPostByte(uint8_t data, bool last){ } String name = _temp.substring(0, _temp.indexOf('=')); String nameVal = _temp.substring(_temp.indexOf('=') + 2, _temp.length() - 1); - if(name == F("name")){ + if (name == F("name")) { _itemName = nameVal; - } else if(name == F("filename")){ + } else if (name == F("filename")) { _itemFilename = nameVal; _itemIsFile = true; } @@ -481,15 +458,15 @@ void AsyncWebServerRequest::_parseMultipartPostByte(uint8_t data, bool last){ _temp = String(); } else { _multiParseState = WAIT_FOR_RETURN1; - //value starts from here + // value starts from here _itemSize = 0; _itemStartIndex = _parsedLength; _itemValue = String(); - if(_itemIsFile){ - if(_itemBuffer) + if (_itemIsFile) { + if (_itemBuffer) free(_itemBuffer); _itemBuffer = (uint8_t*)malloc(1460); - if(_itemBuffer == NULL){ + if (_itemBuffer == NULL) { _multiParseState = PARSE_ERROR; return; } @@ -497,46 +474,56 @@ void AsyncWebServerRequest::_parseMultipartPostByte(uint8_t data, bool last){ } } } - } else if(_multiParseState == EXPECT_FEED1){ - if(data != '\n'){ + } else if (_multiParseState == EXPECT_FEED1) { + if (data != '\n') { _multiParseState = WAIT_FOR_RETURN1; - itemWriteByte('\r'); _parseMultipartPostByte(data, last); + itemWriteByte('\r'); + _parseMultipartPostByte(data, last); } else { _multiParseState = EXPECT_DASH1; } - } else if(_multiParseState == EXPECT_DASH1){ - if(data != '-'){ + } else if (_multiParseState == EXPECT_DASH1) { + if (data != '-') { _multiParseState = WAIT_FOR_RETURN1; - itemWriteByte('\r'); itemWriteByte('\n'); _parseMultipartPostByte(data, last); + itemWriteByte('\r'); + itemWriteByte('\n'); + _parseMultipartPostByte(data, last); } else { _multiParseState = EXPECT_DASH2; } - } else if(_multiParseState == EXPECT_DASH2){ - if(data != '-'){ + } else if (_multiParseState == EXPECT_DASH2) { + if (data != '-') { _multiParseState = WAIT_FOR_RETURN1; - itemWriteByte('\r'); itemWriteByte('\n'); itemWriteByte('-'); _parseMultipartPostByte(data, last); + itemWriteByte('\r'); + itemWriteByte('\n'); + itemWriteByte('-'); + _parseMultipartPostByte(data, last); } else { _multiParseState = BOUNDARY_OR_DATA; _boundaryPosition = 0; } - } else if(_multiParseState == BOUNDARY_OR_DATA){ - if(_boundaryPosition < _boundary.length() && _boundary.c_str()[_boundaryPosition] != data){ + } else if (_multiParseState == BOUNDARY_OR_DATA) { + if (_boundaryPosition < _boundary.length() && _boundary.c_str()[_boundaryPosition] != data) { _multiParseState = WAIT_FOR_RETURN1; - itemWriteByte('\r'); itemWriteByte('\n'); itemWriteByte('-'); itemWriteByte('-'); + itemWriteByte('\r'); + itemWriteByte('\n'); + itemWriteByte('-'); + itemWriteByte('-'); uint8_t i; - for(i=0; i<_boundaryPosition; i++) + for (i = 0; i < _boundaryPosition; i++) itemWriteByte(_boundary.c_str()[i]); _parseMultipartPostByte(data, last); - } else if(_boundaryPosition == _boundary.length() - 1){ + } else if (_boundaryPosition == _boundary.length() - 1) { _multiParseState = DASH3_OR_RETURN2; - if(!_itemIsFile){ - _addParam(new AsyncWebParameter(_itemName, _itemValue, true)); + if (!_itemIsFile) { + _params.emplace_back(_itemName, _itemValue, true); } else { - if(_itemSize){ - //check if authenticated before calling the upload - if(_handler) _handler->handleUpload(this, _itemFilename, _itemSize - _itemBufferIndex, _itemBuffer, _itemBufferIndex, true); + if (_itemSize) { + // check if authenticated before calling the upload + if (_handler) + _handler->handleUpload(this, _itemFilename, _itemSize - _itemBufferIndex, _itemBuffer, _itemBufferIndex, true); _itemBufferIndex = 0; - _addParam(new AsyncWebParameter(_itemName, _itemFilename, true, true, _itemSize)); + _params.emplace_back(_itemName, _itemFilename, true, true, _itemSize); } free(_itemBuffer); _itemBuffer = NULL; @@ -545,37 +532,48 @@ void AsyncWebServerRequest::_parseMultipartPostByte(uint8_t data, bool last){ } else { _boundaryPosition++; } - } else if(_multiParseState == DASH3_OR_RETURN2){ - if(data == '-' && (_contentLength - _parsedLength - 4) != 0){ - //os_printf("ERROR: The parser got to the end of the POST but is expecting %u bytes more!\nDrop an issue so we can have more info on the matter!\n", _contentLength - _parsedLength - 4); - _contentLength = _parsedLength + 4;//lets close the request gracefully + } else if (_multiParseState == DASH3_OR_RETURN2) { + if (data == '-' && (_contentLength - _parsedLength - 4) != 0) { + // os_printf("ERROR: The parser got to the end of the POST but is expecting %u bytes more!\nDrop an issue so we can have more info on the matter!\n", _contentLength - _parsedLength - 4); + _contentLength = _parsedLength + 4; // lets close the request gracefully } - if(data == '\r'){ + if (data == '\r') { _multiParseState = EXPECT_FEED2; - } else if(data == '-' && _contentLength == (_parsedLength + 4)){ + } else if (data == '-' && _contentLength == (_parsedLength + 4)) { _multiParseState = PARSING_FINISHED; } else { _multiParseState = WAIT_FOR_RETURN1; - itemWriteByte('\r'); itemWriteByte('\n'); itemWriteByte('-'); itemWriteByte('-'); - uint8_t i; for(i=0; i<_boundary.length(); i++) itemWriteByte(_boundary.c_str()[i]); + itemWriteByte('\r'); + itemWriteByte('\n'); + itemWriteByte('-'); + itemWriteByte('-'); + uint8_t i; + for (i = 0; i < _boundary.length(); i++) + itemWriteByte(_boundary.c_str()[i]); _parseMultipartPostByte(data, last); } - } else if(_multiParseState == EXPECT_FEED2){ - if(data == '\n'){ + } else if (_multiParseState == EXPECT_FEED2) { + if (data == '\n') { _multiParseState = PARSE_HEADERS; _itemIsFile = false; } else { _multiParseState = WAIT_FOR_RETURN1; - itemWriteByte('\r'); itemWriteByte('\n'); itemWriteByte('-'); itemWriteByte('-'); - uint8_t i; for(i=0; i<_boundary.length(); i++) itemWriteByte(_boundary.c_str()[i]); - itemWriteByte('\r'); _parseMultipartPostByte(data, last); + itemWriteByte('\r'); + itemWriteByte('\n'); + itemWriteByte('-'); + itemWriteByte('-'); + uint8_t i; + for (i = 0; i < _boundary.length(); i++) + itemWriteByte(_boundary.c_str()[i]); + itemWriteByte('\r'); + _parseMultipartPostByte(data, last); } } } -void AsyncWebServerRequest::_parseLine(){ - if(_parseState == PARSE_REQ_START){ - if(!_temp.length()){ +void AsyncWebServerRequest::_parseLine() { + if (_parseState == PARSE_REQ_START) { + if (!_temp.length()) { _parseState = PARSE_REQ_FAIL; _client->close(); } else { @@ -585,253 +583,229 @@ void AsyncWebServerRequest::_parseLine(){ return; } - if(_parseState == PARSE_REQ_HEADERS){ - if(!_temp.length()){ - //end of headers + if (_parseState == PARSE_REQ_HEADERS) { + if (!_temp.length()) { + // end of headers _server->_rewriteRequest(this); _server->_attachHandler(this); _removeNotInterestingHeaders(); - if(_expectingContinue){ + if (_expectingContinue) { String response = F("HTTP/1.1 100 Continue\r\n\r\n"); _client->write(response.c_str(), response.length()); } - //check handler for authentication - if(_contentLength){ + // check handler for authentication + if (_contentLength) { _parseState = PARSE_REQ_BODY; } else { _parseState = PARSE_REQ_END; - if(_handler) _handler->handleRequest(this); - else send(501); + if (_handler) + _handler->handleRequest(this); + else + send(501); } - } else _parseReqHeader(); + } else + _parseReqHeader(); } } -size_t AsyncWebServerRequest::headers() const{ +size_t AsyncWebServerRequest::headers() const { return _headers.size(); } -bool AsyncWebServerRequest::hasHeader(const String& name) const { - for(const auto& h: _headers){ - if(h.name().equalsIgnoreCase(name)){ +bool AsyncWebServerRequest::hasHeader(const char* name) const { + for (const auto& h : _headers) { + if (h.name().equalsIgnoreCase(name)) { return true; } } return false; } -bool AsyncWebServerRequest::hasHeader(const __FlashStringHelper * data) const { +#ifdef ESP8266 +bool AsyncWebServerRequest::hasHeader(const __FlashStringHelper* data) const { return hasHeader(String(data)); } +#endif -AsyncWebHeader* AsyncWebServerRequest::getHeader(const String& name) { - auto iter = std::find_if(std::begin(_headers), std::end(_headers), - [&name](const AsyncWebHeader &header){ return header.name().equalsIgnoreCase(name); }); +const AsyncWebHeader* AsyncWebServerRequest::getHeader(const char* 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); + return (iter == std::end(_headers)) ? nullptr : &(*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) { +#ifdef ESP8266 +const AsyncWebHeader* AsyncWebServerRequest::getHeader(const __FlashStringHelper* data) const { PGM_P p = reinterpret_cast(data); size_t n = strlen_P(p); - char * name = (char*) malloc(n+1); + char* name = (char*)malloc(n + 1); if (name) { strcpy_P(name, p); - AsyncWebHeader* result = getHeader( String(name)); + const AsyncWebHeader* result = getHeader(String(name)); free(name); return result; } else { return nullptr; } } - -const AsyncWebHeader* AsyncWebServerRequest::getHeader(const __FlashStringHelper * data) const { - PGM_P p = reinterpret_cast(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) { - if (num >= _headers.size()) - return nullptr; - return &(*std::next(std::begin(_headers), num)); -} +#endif const AsyncWebHeader* AsyncWebServerRequest::getHeader(size_t num) const { if (num >= _headers.size()) - return nullptr; - return &(*std::next(std::begin(_headers), num)); + return nullptr; + return &(*std::next(_headers.cbegin(), num)); } size_t AsyncWebServerRequest::params() const { - return _params.length(); + return _params.size(); } bool AsyncWebServerRequest::hasParam(const String& name, bool post, bool file) const { - for(const auto& p: _params){ - if(p->name() == name && p->isPost() == post && p->isFile() == file){ + for (const auto& p : _params) { + if (p.name() == name && p.isPost() == post && p.isFile() == file) { return true; } } return false; } -bool AsyncWebServerRequest::hasParam(const __FlashStringHelper * data, bool post, bool file) const { +bool AsyncWebServerRequest::hasParam(const __FlashStringHelper* data, bool post, bool file) const { return hasParam(String(data).c_str(), post, file); } -AsyncWebParameter* AsyncWebServerRequest::getParam(const String& name, bool post, bool file) const { - for(const auto& p: _params){ - if(p->name() == name && p->isPost() == post && p->isFile() == file){ - return p; +const AsyncWebParameter* AsyncWebServerRequest::getParam(const char* name, bool post, bool file) const { + for (const auto& p : _params) { + if (p.name() == name && p.isPost() == post && p.isFile() == file) { + return &p; } } return nullptr; } -AsyncWebParameter* AsyncWebServerRequest::getParam(const __FlashStringHelper * data, bool post, bool file) const { - return getParam(String(data).c_str(), post, file); +#ifdef ESP8266 +const AsyncWebParameter* AsyncWebServerRequest::getParam(const __FlashStringHelper* data, bool post, bool file) const { + return getParam(String(data), post, file); +} +#endif + +const AsyncWebParameter* AsyncWebServerRequest::getParam(size_t num) const { + if (num >= _params.size()) + return nullptr; + return &(*std::next(_params.cbegin(), num)); } -AsyncWebParameter* AsyncWebServerRequest::getParam(size_t num) const { - auto param = _params.nth(num); - return param ? *param : nullptr; +void AsyncWebServerRequest::addInterestingHeader(const char* name) { + if (std::none_of(std::begin(_interestingHeaders), std::end(_interestingHeaders), [&name](const String& str) { return str.equalsIgnoreCase(name); })) + _interestingHeaders.emplace_back(name); } -void AsyncWebServerRequest::addInterestingHeader(const String& name){ - if(std::none_of(std::begin(_interestingHeaders), std::end(_interestingHeaders), - [&name](const String &str){ return str.equalsIgnoreCase(name); })) - _interestingHeaders.push_back(name); +AsyncWebServerResponse* AsyncWebServerRequest::beginResponse(int code, const String& contentType, const String& content) { + return new AsyncBasicResponse(code, contentType, content); } -void AsyncWebServerRequest::send(AsyncWebServerResponse *response){ +AsyncWebServerResponse* AsyncWebServerRequest::beginResponse(int code, const String& contentType, const uint8_t* content, size_t len, AwsTemplateProcessor callback) { + return new AsyncProgmemResponse(code, contentType, content, len, callback); +} + +AsyncWebServerResponse* AsyncWebServerRequest::beginResponse(int code, const String& contentType, PGM_P content, AwsTemplateProcessor callback) { + return new AsyncProgmemResponse(code, contentType, (const uint8_t*)content, strlen_P(content), callback); +} + +AsyncWebServerResponse* AsyncWebServerRequest::beginResponse(FS& fs, const String& path, const String& contentType, bool download, AwsTemplateProcessor callback) { + if (fs.exists(path) || (!download && fs.exists(path + F(".gz")))) + return new AsyncFileResponse(fs, path, contentType, download, callback); + return NULL; +} + +AsyncWebServerResponse* AsyncWebServerRequest::beginResponse(File content, const String& path, const String& contentType, bool download, AwsTemplateProcessor callback) { + if (content == true) + return new AsyncFileResponse(content, path, contentType, download, callback); + return NULL; +} + +AsyncWebServerResponse* AsyncWebServerRequest::beginResponse(Stream& stream, const String& contentType, size_t len, AwsTemplateProcessor callback) { + return new AsyncStreamResponse(stream, contentType, len, callback); +} + +AsyncWebServerResponse* AsyncWebServerRequest::beginResponse(const String& contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback) { + return new AsyncCallbackResponse(contentType, len, callback, templateCallback); +} + +AsyncWebServerResponse* AsyncWebServerRequest::beginChunkedResponse(const String& contentType, AwsResponseFiller callback, AwsTemplateProcessor templateCallback) { + if (_version) + return new AsyncChunkedResponse(contentType, callback, templateCallback); + return new AsyncCallbackResponse(contentType, 0, callback, templateCallback); +} + +AsyncResponseStream* AsyncWebServerRequest::beginResponseStream(const String& contentType, size_t bufferSize) { + return new AsyncResponseStream(contentType, bufferSize); +} + +void AsyncWebServerRequest::send(AsyncWebServerResponse* response) { _response = response; - if(_response == NULL){ + if (_response == NULL) { _client->close(true); _onDisconnect(); return; } - if(!_response->_sourceValid()){ + if (!_response->_sourceValid()) { delete response; _response = NULL; send(500); - } - else { + } else { _client->setRxTimeout(0); _response->_respond(this); } } -AsyncWebServerResponse * AsyncWebServerRequest::beginResponse(int code, const String& contentType, const String& content){ - return new AsyncBasicResponse(code, contentType, content); -} - -AsyncWebServerResponse * AsyncWebServerRequest::beginResponse(FS &fs, const String& path, const String& contentType, bool download, AwsTemplateProcessor callback){ - if(fs.exists(path) || (!download && fs.exists(path+F(".gz")))) - return new AsyncFileResponse(fs, path, contentType, download, callback); - return NULL; -} - -AsyncWebServerResponse * AsyncWebServerRequest::beginResponse(File content, const String& path, const String& contentType, bool download, AwsTemplateProcessor callback){ - if(content == true) - return new AsyncFileResponse(content, path, contentType, download, callback); - return NULL; -} - -AsyncWebServerResponse * AsyncWebServerRequest::beginResponse(Stream &stream, const String& contentType, size_t len, AwsTemplateProcessor callback){ - return new AsyncStreamResponse(stream, contentType, len, callback); -} - -AsyncWebServerResponse * AsyncWebServerRequest::beginResponse(const String& contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback){ - return new AsyncCallbackResponse(contentType, len, callback, templateCallback); -} - -AsyncWebServerResponse * AsyncWebServerRequest::beginChunkedResponse(const String& contentType, AwsResponseFiller callback, AwsTemplateProcessor templateCallback){ - if(_version) - return new AsyncChunkedResponse(contentType, callback, templateCallback); - return new AsyncCallbackResponse(contentType, 0, callback, templateCallback); -} - -AsyncResponseStream * AsyncWebServerRequest::beginResponseStream(const String& contentType, size_t bufferSize){ - return new AsyncResponseStream(contentType, bufferSize); -} - -AsyncWebServerResponse * AsyncWebServerRequest::beginResponse_P(int code, const String& contentType, const uint8_t * content, size_t len, AwsTemplateProcessor callback){ - return new AsyncProgmemResponse(code, contentType, content, len, callback); -} - -AsyncWebServerResponse * AsyncWebServerRequest::beginResponse_P(int code, const String& contentType, PGM_P content, AwsTemplateProcessor callback){ - return beginResponse_P(code, contentType, (const uint8_t *)content, strlen_P(content), callback); -} - -void AsyncWebServerRequest::send(int code, const String& contentType, const String& content){ +void AsyncWebServerRequest::send(int code, const String& contentType, const String& content) { send(beginResponse(code, contentType, content)); } -void AsyncWebServerRequest::send(FS &fs, const String& path, const String& contentType, bool download, AwsTemplateProcessor callback){ - if(fs.exists(path) || (!download && fs.exists(path+F(".gz")))){ +void AsyncWebServerRequest::send(int code, const String& contentType, const uint8_t* content, size_t len, AwsTemplateProcessor callback) { + send(beginResponse(code, contentType, content, len, callback)); +} + +void AsyncWebServerRequest::send(int code, const String& contentType, PGM_P content, AwsTemplateProcessor callback) { + send(beginResponse(code, contentType, content, callback)); +} + +void AsyncWebServerRequest::send(FS& fs, const String& path, const String& contentType, bool download, AwsTemplateProcessor callback) { + if (fs.exists(path) || (!download && fs.exists(path + F(".gz")))) { send(beginResponse(fs, path, contentType, download, callback)); - } else send(404); + } else + send(404); } -void AsyncWebServerRequest::send(File content, const String& path, const String& contentType, bool download, AwsTemplateProcessor callback){ - if(content == true){ +void AsyncWebServerRequest::send(File content, const String& path, const String& contentType, bool download, AwsTemplateProcessor callback) { + if (content == true) { send(beginResponse(content, path, contentType, download, callback)); - } else send(404); + } else + send(404); } -void AsyncWebServerRequest::send(Stream &stream, const String& contentType, size_t len, AwsTemplateProcessor callback){ +void AsyncWebServerRequest::send(Stream& stream, const String& contentType, size_t len, AwsTemplateProcessor callback) { send(beginResponse(stream, contentType, len, callback)); } -void AsyncWebServerRequest::send(const String& contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback){ +void AsyncWebServerRequest::send(const String& contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback) { send(beginResponse(contentType, len, callback, templateCallback)); } -void AsyncWebServerRequest::sendChunked(const String& contentType, AwsResponseFiller callback, AwsTemplateProcessor templateCallback){ +void AsyncWebServerRequest::sendChunked(const String& contentType, AwsResponseFiller callback, AwsTemplateProcessor templateCallback) { send(beginChunkedResponse(contentType, callback, templateCallback)); } -void AsyncWebServerRequest::send_P(int code, const String& contentType, const uint8_t * content, size_t len, AwsTemplateProcessor callback){ - send(beginResponse_P(code, contentType, content, len, callback)); -} - -void AsyncWebServerRequest::send_P(int code, const String& contentType, PGM_P content, AwsTemplateProcessor callback){ - send(beginResponse_P(code, contentType, content, callback)); -} - -void AsyncWebServerRequest::redirect(const String& url){ - AsyncWebServerResponse * response = beginResponse(302); +void AsyncWebServerRequest::redirect(const char* url) { + AsyncWebServerResponse* response = beginResponse(302); response->addHeader(F("Location"), url); send(response); } -bool AsyncWebServerRequest::authenticate(const char * username, const char * password, const char * realm, bool passwordIsHash){ - if(_authorization.length()){ - if(_isDigest) +bool AsyncWebServerRequest::authenticate(const char* username, const char* password, const char* realm, bool passwordIsHash) { + if (_authorization.length()) { + if (_isDigest) return checkDigestAuthentication(_authorization.c_str(), methodToString(), username, password, realm, passwordIsHash, NULL, NULL, NULL); - else if(!passwordIsHash) + else if (!passwordIsHash) return checkBasicAuthentication(_authorization.c_str(), username, password); else return _authorization.equals(password); @@ -839,19 +813,19 @@ bool AsyncWebServerRequest::authenticate(const char * username, const char * pas return false; } -bool AsyncWebServerRequest::authenticate(const char * hash){ - if(!_authorization.length() || hash == NULL) +bool AsyncWebServerRequest::authenticate(const char* hash) { + if (!_authorization.length() || hash == NULL) return false; - if(_isDigest){ + if (_isDigest) { String hStr = String(hash); int separator = hStr.indexOf(':'); - if(separator <= 0) + if (separator <= 0) return false; String username = hStr.substring(0, separator); hStr = hStr.substring(separator + 1); separator = hStr.indexOf(':'); - if(separator <= 0) + if (separator <= 0) return false; String realm = hStr.substring(0, separator); hStr = hStr.substring(separator + 1); @@ -861,11 +835,11 @@ bool AsyncWebServerRequest::authenticate(const char * hash){ return (_authorization.equals(hash)); } -void AsyncWebServerRequest::requestAuthentication(const char * realm, bool isDigest){ - AsyncWebServerResponse * r = beginResponse(401); - if(!isDigest && realm == NULL){ +void AsyncWebServerRequest::requestAuthentication(const char* realm, bool isDigest) { + AsyncWebServerResponse* r = beginResponse(401); + if (!isDigest && realm == NULL) { r->addHeader(F("WWW-Authenticate"), F("Basic realm=\"Login Required\"")); - } else if(!isDigest){ + } else if (!isDigest) { String header = F("Basic realm=\""); header.concat(realm); header += '"'; @@ -879,31 +853,34 @@ void AsyncWebServerRequest::requestAuthentication(const char * realm, bool isDig } bool AsyncWebServerRequest::hasArg(const char* name) const { - for(const auto& arg: _params){ - if(arg->name() == name){ + for (const auto& arg : _params) { + if (arg.name() == name) { return true; } } return false; } -bool AsyncWebServerRequest::hasArg(const __FlashStringHelper * data) const { +#ifdef ESP8266 +bool AsyncWebServerRequest::hasArg(const __FlashStringHelper* data) const { return hasArg(String(data).c_str()); } +#endif - -const String& AsyncWebServerRequest::arg(const String& name) const { - for(const auto& arg: _params){ - if(arg->name() == name){ - return arg->value(); +const String& AsyncWebServerRequest::arg(const char* name) const { + for (const auto& arg : _params) { + if (arg.name() == name) { + return arg.value(); } } return emptyString; } -const String& AsyncWebServerRequest::arg(const __FlashStringHelper * data) const { +#ifdef ESP8266 +const String& AsyncWebServerRequest::arg(const __FlashStringHelper* data) const { return arg(String(data).c_str()); } +#endif const String& AsyncWebServerRequest::arg(size_t i) const { return getParam(i)->value(); @@ -918,18 +895,19 @@ const String& AsyncWebServerRequest::pathArg(size_t i) const { } const String& AsyncWebServerRequest::header(const char* name) const { - const AsyncWebHeader* h = getHeader(String(name)); + const AsyncWebHeader* h = getHeader(name); return h ? h->value() : emptyString; } -const String& AsyncWebServerRequest::header(const __FlashStringHelper * data) const { +#ifdef ESP8266 +const String& AsyncWebServerRequest::header(const __FlashStringHelper* data) const { return header(String(data).c_str()); }; - +#endif const String& AsyncWebServerRequest::header(size_t i) const { const AsyncWebHeader* h = getHeader(i); - return h ? h->value() : emptyString; + return h ? h->value() : emptyString; } const String& AsyncWebServerRequest::headerName(size_t i) const { @@ -941,53 +919,110 @@ String AsyncWebServerRequest::urlDecode(const String& text) const { char temp[] = "0x00"; unsigned int len = text.length(); unsigned int i = 0; - String decoded = String(); + String decoded; decoded.reserve(len); // Allocate the string internal buffer - never longer from source text - while (i < len){ + while (i < len) { char decodedChar; char encodedChar = text.charAt(i++); - if ((encodedChar == '%') && (i + 1 < len)){ + if ((encodedChar == '%') && (i + 1 < len)) { temp[2] = text.charAt(i++); temp[3] = text.charAt(i++); decodedChar = strtol(temp, NULL, 16); } else if (encodedChar == '+') { decodedChar = ' '; } else { - decodedChar = encodedChar; // normal ascii char + decodedChar = encodedChar; // normal ascii char } decoded.concat(decodedChar); } return decoded; } +#ifndef ESP8266 +const char* AsyncWebServerRequest::methodToString() const { + if (_method == HTTP_ANY) + return "ANY"; + if (_method & HTTP_GET) + return "GET"; + if (_method & HTTP_POST) + return "POST"; + if (_method & HTTP_DELETE) + return "DELETE"; + if (_method & HTTP_PUT) + return "PUT"; + if (_method & HTTP_PATCH) + return "PATCH"; + if (_method & HTTP_HEAD) + return "HEAD"; + if (_method & HTTP_OPTIONS) + return "OPTIONS"; + return "UNKNOWN"; +} -const __FlashStringHelper *AsyncWebServerRequest::methodToString() const { - if(_method == HTTP_ANY) return F("ANY"); - else if(_method & HTTP_GET) return F("GET"); - else if(_method & HTTP_POST) return F("POST"); - else if(_method & HTTP_DELETE) return F("DELETE"); - else if(_method & HTTP_PUT) return F("PUT"); - else if(_method & HTTP_PATCH) return F("PATCH"); - else if(_method & HTTP_HEAD) return F("HEAD"); - else if(_method & HTTP_OPTIONS) return F("OPTIONS"); +const char* AsyncWebServerRequest::requestedConnTypeToString() const { + switch (_reqconntype) { + case RCT_NOT_USED: + return "RCT_NOT_USED"; + case RCT_DEFAULT: + return "RCT_DEFAULT"; + case RCT_HTTP: + return "RCT_HTTP"; + case RCT_WS: + return "RCT_WS"; + case RCT_EVENT: + return "RCT_EVENT"; + default: + return "ERROR"; + } +} +#endif + +#ifdef ESP8266 +const __FlashStringHelper* AsyncWebServerRequest::methodToString() const { + if (_method == HTTP_ANY) + return F("ANY"); + else if (_method & HTTP_GET) + return F("GET"); + else if (_method & HTTP_POST) + return F("POST"); + else if (_method & HTTP_DELETE) + return F("DELETE"); + else if (_method & HTTP_PUT) + return F("PUT"); + else if (_method & HTTP_PATCH) + return F("PATCH"); + else if (_method & HTTP_HEAD) + return F("HEAD"); + else if (_method & HTTP_OPTIONS) + return F("OPTIONS"); return F("UNKNOWN"); } -const __FlashStringHelper *AsyncWebServerRequest::requestedConnTypeToString() const { +const __FlashStringHelper* AsyncWebServerRequest::requestedConnTypeToString() const { switch (_reqconntype) { - case RCT_NOT_USED: return F("RCT_NOT_USED"); - case RCT_DEFAULT: return F("RCT_DEFAULT"); - case RCT_HTTP: return F("RCT_HTTP"); - case RCT_WS: return F("RCT_WS"); - case RCT_EVENT: return F("RCT_EVENT"); - default: return F("ERROR"); + case RCT_NOT_USED: + return F("RCT_NOT_USED"); + case RCT_DEFAULT: + return F("RCT_DEFAULT"); + case RCT_HTTP: + return F("RCT_HTTP"); + case RCT_WS: + return F("RCT_WS"); + case RCT_EVENT: + return F("RCT_EVENT"); + default: + return F("ERROR"); } } +#endif bool AsyncWebServerRequest::isExpectedRequestedConnType(RequestedConnectionType erct1, RequestedConnectionType erct2, RequestedConnectionType erct3) { - bool res = false; - if ((erct1 != RCT_NOT_USED) && (erct1 == _reqconntype)) res = true; - if ((erct2 != RCT_NOT_USED) && (erct2 == _reqconntype)) res = true; - if ((erct3 != RCT_NOT_USED) && (erct3 == _reqconntype)) res = true; - return res; + bool res = false; + if ((erct1 != RCT_NOT_USED) && (erct1 == _reqconntype)) + res = true; + if ((erct2 != RCT_NOT_USED) && (erct2 == _reqconntype)) + res = true; + if ((erct3 != RCT_NOT_USED) && (erct3 == _reqconntype)) + res = true; + return res; } diff --git a/src/WebResponseImpl.h b/src/WebResponseImpl.h index 4a47225..26ec223 100644 --- a/src/WebResponseImpl.h +++ b/src/WebResponseImpl.h @@ -22,115 +22,125 @@ #define ASYNCWEBSERVERRESPONSEIMPL_H_ #ifdef Arduino_h -// arduino is not compatible with std::vector -#undef min -#undef max + // arduino is not compatible with std::vector + #undef min + #undef max #endif -#include #include +#include // 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 { private: String _content; + public: - AsyncBasicResponse(int code, const String& contentType=String(), const String& content=String()); - void _respond(AsyncWebServerRequest *request); - size_t _ack(AsyncWebServerRequest *request, size_t len, uint32_t time); + AsyncBasicResponse(int code, const String& contentType = String(), const String& content = String()); + void _respond(AsyncWebServerRequest* request); + size_t _ack(AsyncWebServerRequest* request, size_t len, uint32_t time); bool _sourceValid() const { return true; } }; -class AsyncAbstractResponse: public AsyncWebServerResponse { +class AsyncAbstractResponse : public AsyncWebServerResponse { private: String _head; - // Data is inserted into cache at begin(). - // This is inefficient with vector, but if we use some other container, + // Data is inserted into cache at begin(). + // This is inefficient with vector, but if we use some other container, // we won't be able to access it as contiguous array of bytes when reading from it, // so by gaining performance in one place, we'll lose it in another. std::vector _cache; size_t _readDataFromCacheOrContent(uint8_t* data, const size_t len); size_t _fillBufferAndProcessTemplates(uint8_t* buf, size_t maxLen); + protected: AwsTemplateProcessor _callback; + public: - AsyncAbstractResponse(AwsTemplateProcessor callback=nullptr); - void _respond(AsyncWebServerRequest *request); - size_t _ack(AsyncWebServerRequest *request, size_t len, uint32_t time); + AsyncAbstractResponse(AwsTemplateProcessor callback = nullptr); + void _respond(AsyncWebServerRequest* request); + size_t _ack(AsyncWebServerRequest* request, size_t len, uint32_t time); bool _sourceValid() const { return false; } - virtual size_t _fillBuffer(uint8_t *buf __attribute__((unused)), size_t maxLen __attribute__((unused))) { return 0; } + virtual size_t _fillBuffer(uint8_t* buf __attribute__((unused)), size_t maxLen __attribute__((unused))) { return 0; } }; #ifndef TEMPLATE_PLACEHOLDER -#define TEMPLATE_PLACEHOLDER '%' + #define TEMPLATE_PLACEHOLDER '%' #endif #define TEMPLATE_PARAM_NAME_LENGTH 32 -class AsyncFileResponse: public AsyncAbstractResponse { - using File = fs::File; - using FS = fs::FS; +class AsyncFileResponse : public AsyncAbstractResponse { + using File = fs::File; + using FS = fs::FS; + private: File _content; String _path; void _setContentType(const String& path); + public: - AsyncFileResponse(FS &fs, const String& path, const String& contentType=String(), bool download=false, AwsTemplateProcessor callback=nullptr); - AsyncFileResponse(File content, const String& path, const String& contentType=String(), bool download=false, AwsTemplateProcessor callback=nullptr); + AsyncFileResponse(FS& fs, const String& path, const String& contentType = String(), bool download = false, AwsTemplateProcessor callback = nullptr); + AsyncFileResponse(File content, const String& path, const String& contentType = String(), bool download = false, AwsTemplateProcessor callback = nullptr); ~AsyncFileResponse(); bool _sourceValid() const { return !!(_content); } - virtual size_t _fillBuffer(uint8_t *buf, size_t maxLen) override; + virtual size_t _fillBuffer(uint8_t* buf, size_t maxLen) override; }; -class AsyncStreamResponse: public AsyncAbstractResponse { +class AsyncStreamResponse : public AsyncAbstractResponse { private: - Stream *_content; + Stream* _content; + public: - AsyncStreamResponse(Stream &stream, const String& contentType, size_t len, AwsTemplateProcessor callback=nullptr); + AsyncStreamResponse(Stream& stream, const String& contentType, size_t len, AwsTemplateProcessor callback = nullptr); bool _sourceValid() const { return !!(_content); } - virtual size_t _fillBuffer(uint8_t *buf, size_t maxLen) override; + virtual size_t _fillBuffer(uint8_t* buf, size_t maxLen) override; }; -class AsyncCallbackResponse: public AsyncAbstractResponse { +class AsyncCallbackResponse : public AsyncAbstractResponse { private: AwsResponseFiller _content; size_t _filledLength; + public: - AsyncCallbackResponse(const String& contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback=nullptr); + AsyncCallbackResponse(const String& contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback = nullptr); bool _sourceValid() const { return !!(_content); } - virtual size_t _fillBuffer(uint8_t *buf, size_t maxLen) override; + virtual size_t _fillBuffer(uint8_t* buf, size_t maxLen) override; }; -class AsyncChunkedResponse: public AsyncAbstractResponse { +class AsyncChunkedResponse : public AsyncAbstractResponse { private: AwsResponseFiller _content; size_t _filledLength; + public: - AsyncChunkedResponse(const String& contentType, AwsResponseFiller callback, AwsTemplateProcessor templateCallback=nullptr); + AsyncChunkedResponse(const String& contentType, AwsResponseFiller callback, AwsTemplateProcessor templateCallback = nullptr); bool _sourceValid() const { return !!(_content); } - virtual size_t _fillBuffer(uint8_t *buf, size_t maxLen) override; + virtual size_t _fillBuffer(uint8_t* buf, size_t maxLen) override; }; -class AsyncProgmemResponse: public AsyncAbstractResponse { +class AsyncProgmemResponse : public AsyncAbstractResponse { private: - const uint8_t * _content; + const uint8_t* _content; size_t _readLength; + public: - AsyncProgmemResponse(int code, const String& contentType, const uint8_t * content, size_t len, AwsTemplateProcessor callback=nullptr); + AsyncProgmemResponse(int code, const String& contentType, const uint8_t* content, size_t len, AwsTemplateProcessor callback = nullptr); bool _sourceValid() const { return true; } - virtual size_t _fillBuffer(uint8_t *buf, size_t maxLen) override; + virtual size_t _fillBuffer(uint8_t* buf, size_t maxLen) override; }; class cbuf; -class AsyncResponseStream: public AsyncAbstractResponse, public Print { +class AsyncResponseStream : public AsyncAbstractResponse, public Print { private: std::unique_ptr _content; + public: AsyncResponseStream(const String& contentType, size_t bufferSize); ~AsyncResponseStream(); bool _sourceValid() const { return (_state < RESPONSE_END); } - virtual size_t _fillBuffer(uint8_t *buf, size_t maxLen) override; - size_t write(const uint8_t *data, size_t len); + virtual size_t _fillBuffer(uint8_t* buf, size_t maxLen) override; + size_t write(const uint8_t* data, size_t len); size_t write(uint8_t data); using Print::write; }; diff --git a/src/WebResponses.cpp b/src/WebResponses.cpp index a783de0..1fd41fa 100644 --- a/src/WebResponses.cpp +++ b/src/WebResponses.cpp @@ -23,111 +23,140 @@ #include "cbuf.h" // Since ESP8266 does not link memchr by default, here's its implementation. -void* memchr(void* ptr, int ch, size_t count) -{ +void* memchr(void* ptr, int ch, size_t count) { unsigned char* p = static_cast(ptr); - while(count--) - if(*p++ == static_cast(ch)) + while (count--) + if (*p++ == static_cast(ch)) return --p; return nullptr; } - /* * Abstract Response * */ const char* AsyncWebServerResponse::_responseCodeToString(int code) { - return reinterpret_cast(responseCodeToString(code)); + return reinterpret_cast(responseCodeToString(code)); } -const __FlashStringHelper *AsyncWebServerResponse::responseCodeToString(int code) { +const __FlashStringHelper* AsyncWebServerResponse::responseCodeToString(int code) { switch (code) { - case 100: return F("Continue"); - case 101: return F("Switching Protocols"); - case 200: return F("OK"); - case 201: return F("Created"); - case 202: return F("Accepted"); - case 203: return F("Non-Authoritative Information"); - case 204: return F("No Content"); - case 205: return F("Reset Content"); - case 206: return F("Partial Content"); - case 300: return F("Multiple Choices"); - case 301: return F("Moved Permanently"); - case 302: return F("Found"); - case 303: return F("See Other"); - case 304: return F("Not Modified"); - case 305: return F("Use Proxy"); - case 307: return F("Temporary Redirect"); - case 400: return F("Bad Request"); - case 401: return F("Unauthorized"); - case 402: return F("Payment Required"); - case 403: return F("Forbidden"); - case 404: return F("Not Found"); - case 405: return F("Method Not Allowed"); - case 406: return F("Not Acceptable"); - case 407: return F("Proxy Authentication Required"); - case 408: return F("Request Time-out"); - case 409: return F("Conflict"); - case 410: return F("Gone"); - case 411: return F("Length Required"); - case 412: return F("Precondition Failed"); - case 413: return F("Request Entity Too Large"); - case 414: return F("Request-URI Too Large"); - case 415: return F("Unsupported Media Type"); - case 416: return F("Requested range not satisfiable"); - case 417: return F("Expectation Failed"); - case 500: return F("Internal Server Error"); - case 501: return F("Not Implemented"); - case 502: return F("Bad Gateway"); - case 503: return F("Service Unavailable"); - case 504: return F("Gateway Time-out"); - case 505: return F("HTTP Version not supported"); - default: return F(""); + case 100: + return F("Continue"); + case 101: + return F("Switching Protocols"); + case 200: + return F("OK"); + case 201: + return F("Created"); + case 202: + return F("Accepted"); + case 203: + return F("Non-Authoritative Information"); + case 204: + return F("No Content"); + case 205: + return F("Reset Content"); + case 206: + return F("Partial Content"); + case 300: + return F("Multiple Choices"); + case 301: + return F("Moved Permanently"); + case 302: + return F("Found"); + case 303: + return F("See Other"); + case 304: + return F("Not Modified"); + case 305: + return F("Use Proxy"); + case 307: + return F("Temporary Redirect"); + case 400: + return F("Bad Request"); + case 401: + return F("Unauthorized"); + case 402: + return F("Payment Required"); + case 403: + return F("Forbidden"); + case 404: + return F("Not Found"); + case 405: + return F("Method Not Allowed"); + case 406: + return F("Not Acceptable"); + case 407: + return F("Proxy Authentication Required"); + case 408: + return F("Request Time-out"); + case 409: + return F("Conflict"); + case 410: + return F("Gone"); + case 411: + return F("Length Required"); + case 412: + return F("Precondition Failed"); + case 413: + return F("Request Entity Too Large"); + case 414: + return F("Request-URI Too Large"); + case 415: + return F("Unsupported Media Type"); + case 416: + return F("Requested range not satisfiable"); + case 417: + return F("Expectation Failed"); + case 500: + return F("Internal Server Error"); + case 501: + return F("Not Implemented"); + case 502: + return F("Bad Gateway"); + case 503: + return F("Service Unavailable"); + case 504: + return F("Gateway Time-out"); + case 505: + return F("HTTP Version not supported"); + default: + return F(""); } } AsyncWebServerResponse::AsyncWebServerResponse() - : _code(0) - , _contentType() - , _contentLength(0) - , _sendContentLength(true) - , _chunked(false) - , _headLength(0) - , _sentLength(0) - , _ackedLength(0) - , _writtenLength(0) - , _state(RESPONSE_SETUP) -{ - for(const auto &header: DefaultHeaders::Instance()) { + : _code(0), _contentType(), _contentLength(0), _sendContentLength(true), _chunked(false), _headLength(0), _sentLength(0), _ackedLength(0), _writtenLength(0), _state(RESPONSE_SETUP) { + for (const auto& header : DefaultHeaders::Instance()) { _headers.emplace_back(header); } } AsyncWebServerResponse::~AsyncWebServerResponse() = default; -void AsyncWebServerResponse::setCode(int code){ - if(_state == RESPONSE_SETUP) +void AsyncWebServerResponse::setCode(int code) { + if (_state == RESPONSE_SETUP) _code = code; } -void AsyncWebServerResponse::setContentLength(size_t len){ - if(_state == RESPONSE_SETUP) +void AsyncWebServerResponse::setContentLength(size_t len) { + if (_state == RESPONSE_SETUP) _contentLength = len; } -void AsyncWebServerResponse::setContentType(const String& type){ - if(_state == RESPONSE_SETUP) +void AsyncWebServerResponse::setContentType(const String& type) { + if (_state == RESPONSE_SETUP) _contentType = type; } -void AsyncWebServerResponse::addHeader(const String& name, const String& value){ +void AsyncWebServerResponse::addHeader(const String& name, const String& value) { _headers.emplace_back(name, value); } -String AsyncWebServerResponse::_assembleHead(uint8_t version){ - if(version){ +String AsyncWebServerResponse::_assembleHead(uint8_t version) { + if (version) { addHeader(F("Accept-Ranges"), F("none")); - if(_chunked) + if (_chunked) addHeader(F("Transfer-Encoding"), F("chunked")); } String out = String(); @@ -137,16 +166,16 @@ String AsyncWebServerResponse::_assembleHead(uint8_t version){ snprintf_P(buf, bufSize, PSTR("HTTP/1.%d %d %s\r\n"), version, _code, _responseCodeToString(_code)); out.concat(buf); - if(_sendContentLength) { + if (_sendContentLength) { snprintf_P(buf, bufSize, PSTR("Content-Length: %d\r\n"), _contentLength); out.concat(buf); } - if(_contentType.length()) { + if (_contentType.length()) { snprintf_P(buf, bufSize, PSTR("Content-Type: %s\r\n"), _contentType.c_str()); out.concat(buf); } - 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()); out.concat(buf); } @@ -161,44 +190,52 @@ bool AsyncWebServerResponse::_started() const { return _state > RESPONSE_SETUP; bool AsyncWebServerResponse::_finished() const { return _state > RESPONSE_WAIT_ACK; } bool AsyncWebServerResponse::_failed() const { return _state == RESPONSE_FAILED; } bool AsyncWebServerResponse::_sourceValid() const { return false; } -void AsyncWebServerResponse::_respond(AsyncWebServerRequest *request){ _state = RESPONSE_END; request->client()->close(); } -size_t AsyncWebServerResponse::_ack(AsyncWebServerRequest *request, size_t len, uint32_t time){ (void)request; (void)len; (void)time; return 0; } +void AsyncWebServerResponse::_respond(AsyncWebServerRequest* request) { + _state = RESPONSE_END; + request->client()->close(); +} +size_t AsyncWebServerResponse::_ack(AsyncWebServerRequest* request, size_t len, uint32_t time) { + (void)request; + (void)len; + (void)time; + return 0; +} /* * String/Code Response * */ -AsyncBasicResponse::AsyncBasicResponse(int code, const String& contentType, const String& content){ +AsyncBasicResponse::AsyncBasicResponse(int code, const String& contentType, const String& content) { _code = code; _content = content; _contentType = contentType; - if(_content.length()){ + if (_content.length()) { _contentLength = _content.length(); - if(!_contentType.length()) + if (!_contentType.length()) _contentType = F("text/plain"); } addHeader(F("Connection"), F("close")); } -void AsyncBasicResponse::_respond(AsyncWebServerRequest *request){ +void AsyncBasicResponse::_respond(AsyncWebServerRequest* request) { _state = RESPONSE_HEADERS; String out = _assembleHead(request->version()); size_t outLen = out.length(); size_t space = request->client()->space(); - if(!_contentLength && space >= outLen){ + if (!_contentLength && space >= outLen) { _writtenLength += request->client()->write(out.c_str(), outLen); _state = RESPONSE_WAIT_ACK; - } else if(_contentLength && space >= outLen + _contentLength){ + } else if (_contentLength && space >= outLen + _contentLength) { out += _content; outLen += _contentLength; _writtenLength += request->client()->write(out.c_str(), outLen); _state = RESPONSE_WAIT_ACK; - } else if(space && space < outLen){ + } else if (space && space < outLen) { String partial = out.substring(0, space); _content = out.substring(space) + _content; _contentLength += outLen - space; _writtenLength += request->client()->write(partial.c_str(), partial.length()); _state = RESPONSE_CONTENT; - } else if(space > outLen && space < (outLen + _contentLength)){ + } else if (space > outLen && space < (outLen + _contentLength)) { size_t shift = space - outLen; outLen += shift; _sentLength += shift; @@ -213,58 +250,56 @@ void AsyncBasicResponse::_respond(AsyncWebServerRequest *request){ } } -size_t AsyncBasicResponse::_ack(AsyncWebServerRequest *request, size_t len, uint32_t time){ +size_t AsyncBasicResponse::_ack(AsyncWebServerRequest* request, size_t len, uint32_t time) { (void)time; _ackedLength += len; - if(_state == RESPONSE_CONTENT){ + if (_state == RESPONSE_CONTENT) { size_t available = _contentLength - _sentLength; size_t space = request->client()->space(); - //we can fit in this packet - if(space > available){ + // we can fit in this packet + if (space > available) { _writtenLength += request->client()->write(_content.c_str(), available); _content = String(); _state = RESPONSE_WAIT_ACK; return available; } - //send some data, the rest on ack + // send some data, the rest on ack String out = _content.substring(0, space); _content = _content.substring(space); _sentLength += space; _writtenLength += request->client()->write(out.c_str(), space); return space; - } else if(_state == RESPONSE_WAIT_ACK){ - if(_ackedLength >= _writtenLength){ + } else if (_state == RESPONSE_WAIT_ACK) { + if (_ackedLength >= _writtenLength) { _state = RESPONSE_END; } } return 0; } - /* * Abstract Response * */ -AsyncAbstractResponse::AsyncAbstractResponse(AwsTemplateProcessor callback): _callback(callback) -{ +AsyncAbstractResponse::AsyncAbstractResponse(AwsTemplateProcessor callback) : _callback(callback) { // In case of template processing, we're unable to determine real response size - if(callback) { + if (callback) { _contentLength = 0; _sendContentLength = false; _chunked = true; } } -void AsyncAbstractResponse::_respond(AsyncWebServerRequest *request){ +void AsyncAbstractResponse::_respond(AsyncWebServerRequest* request) { addHeader(F("Connection"), F("close")); _head = _assembleHead(request->version()); _state = RESPONSE_HEADERS; _ack(request, 0, 0); } -size_t AsyncAbstractResponse::_ack(AsyncWebServerRequest *request, size_t len, uint32_t time){ +size_t AsyncAbstractResponse::_ack(AsyncWebServerRequest* request, size_t len, uint32_t time) { (void)time; - if(!_sourceValid()){ + if (!_sourceValid()) { _state = RESPONSE_FAILED; request->client()->close(); return 0; @@ -273,8 +308,8 @@ size_t AsyncAbstractResponse::_ack(AsyncWebServerRequest *request, size_t len, u size_t space = request->client()->space(); size_t headLen = _head.length(); - if(_state == RESPONSE_HEADERS){ - if(space >= headLen){ + if (_state == RESPONSE_HEADERS) { + if (space >= headLen) { _state = RESPONSE_CONTENT; space -= headLen; } else { @@ -285,103 +320,102 @@ size_t AsyncAbstractResponse::_ack(AsyncWebServerRequest *request, size_t len, u } } - if(_state == RESPONSE_CONTENT){ + if (_state == RESPONSE_CONTENT) { size_t outLen; - if(_chunked){ - if(space <= 8){ + if (_chunked) { + if (space <= 8) { return 0; } outLen = space; - } else if(!_sendContentLength){ + } else if (!_sendContentLength) { outLen = space; } else { - outLen = ((_contentLength - _sentLength) > space)?space:(_contentLength - _sentLength); + outLen = ((_contentLength - _sentLength) > space) ? space : (_contentLength - _sentLength); } - uint8_t *buf = (uint8_t *)malloc(outLen+headLen); + uint8_t* buf = (uint8_t*)malloc(outLen + headLen); if (!buf) { // os_printf("_ack malloc %d failed\n", outLen+headLen); return 0; } - if(headLen){ + if (headLen) { memcpy(buf, _head.c_str(), _head.length()); } size_t readLen = 0; - if(_chunked){ + if (_chunked) { // HTTP 1.1 allows leading zeros in chunk length. Or spaces may be added. // See RFC2616 sections 2, 3.6.1. - readLen = _fillBufferAndProcessTemplates(buf+headLen+6, outLen - 8); - if(readLen == RESPONSE_TRY_AGAIN){ - free(buf); - return 0; + readLen = _fillBufferAndProcessTemplates(buf + headLen + 6, outLen - 8); + if (readLen == RESPONSE_TRY_AGAIN) { + free(buf); + return 0; } - outLen = sprintf_P((char*)buf+headLen, PSTR("%x"), readLen) + headLen; - while(outLen < headLen + 4) buf[outLen++] = ' '; + outLen = sprintf_P((char*)buf + headLen, PSTR("%x"), readLen) + headLen; + while (outLen < headLen + 4) + buf[outLen++] = ' '; buf[outLen++] = '\r'; buf[outLen++] = '\n'; outLen += readLen; buf[outLen++] = '\r'; buf[outLen++] = '\n'; } else { - readLen = _fillBufferAndProcessTemplates(buf+headLen, outLen); - if(readLen == RESPONSE_TRY_AGAIN){ - free(buf); - return 0; + readLen = _fillBufferAndProcessTemplates(buf + headLen, outLen); + if (readLen == RESPONSE_TRY_AGAIN) { + free(buf); + return 0; } outLen = readLen + headLen; } - if(headLen){ - _head = String(); + if (headLen) { + _head = String(); } - if(outLen){ - _writtenLength += request->client()->write((const char*)buf, outLen); + if (outLen) { + _writtenLength += request->client()->write((const char*)buf, outLen); } - if(_chunked){ - _sentLength += readLen; + if (_chunked) { + _sentLength += readLen; } else { - _sentLength += outLen - headLen; + _sentLength += outLen - headLen; } free(buf); - if((_chunked && readLen == 0) || (!_sendContentLength && outLen == 0) || (!_chunked && _sentLength == _contentLength)){ + if ((_chunked && readLen == 0) || (!_sendContentLength && outLen == 0) || (!_chunked && _sentLength == _contentLength)) { _state = RESPONSE_WAIT_ACK; } return outLen; - } else if(_state == RESPONSE_WAIT_ACK){ - if(!_sendContentLength || _ackedLength >= _writtenLength){ + } else if (_state == RESPONSE_WAIT_ACK) { + if (!_sendContentLength || _ackedLength >= _writtenLength) { _state = RESPONSE_END; - if(!_chunked && !_sendContentLength) + if (!_chunked && !_sendContentLength) request->client()->close(true); } } return 0; } -size_t AsyncAbstractResponse::_readDataFromCacheOrContent(uint8_t* data, const size_t len) -{ - // If we have something in cache, copy it to buffer - const size_t readFromCache = std::min(len, _cache.size()); - if(readFromCache) { - memcpy(data, _cache.data(), readFromCache); - _cache.erase(_cache.begin(), _cache.begin() + readFromCache); - } - // If we need to read more... - const size_t needFromFile = len - readFromCache; - const size_t readFromContent = _fillBuffer(data + readFromCache, needFromFile); - return readFromCache + readFromContent; +size_t AsyncAbstractResponse::_readDataFromCacheOrContent(uint8_t* data, const size_t len) { + // If we have something in cache, copy it to buffer + const size_t readFromCache = std::min(len, _cache.size()); + if (readFromCache) { + memcpy(data, _cache.data(), readFromCache); + _cache.erase(_cache.begin(), _cache.begin() + readFromCache); + } + // If we need to read more... + const size_t needFromFile = len - readFromCache; + const size_t readFromContent = _fillBuffer(data + readFromCache, needFromFile); + return readFromCache + readFromContent; } -size_t AsyncAbstractResponse::_fillBufferAndProcessTemplates(uint8_t* data, size_t len) -{ - if(!_callback) +size_t AsyncAbstractResponse::_fillBufferAndProcessTemplates(uint8_t* data, size_t len) { + if (!_callback) return _fillBuffer(data, len); const size_t originalLen = len; @@ -389,16 +423,16 @@ size_t AsyncAbstractResponse::_fillBufferAndProcessTemplates(uint8_t* data, size // Now we've read 'len' bytes, either from cache or from file // Search for template placeholders uint8_t* pTemplateStart = data; - while((pTemplateStart < &data[len]) && (pTemplateStart = (uint8_t*)memchr(pTemplateStart, TEMPLATE_PLACEHOLDER, &data[len - 1] - pTemplateStart + 1))) { // data[0] ... data[len - 1] + while ((pTemplateStart < &data[len]) && (pTemplateStart = (uint8_t*)memchr(pTemplateStart, TEMPLATE_PLACEHOLDER, &data[len - 1] - pTemplateStart + 1))) { // data[0] ... data[len - 1] uint8_t* pTemplateEnd = (pTemplateStart < &data[len - 1]) ? (uint8_t*)memchr(pTemplateStart + 1, TEMPLATE_PLACEHOLDER, &data[len - 1] - pTemplateStart) : nullptr; // temporary buffer to hold parameter name uint8_t buf[TEMPLATE_PARAM_NAME_LENGTH + 1]; String paramName; // If closing placeholder is found: - if(pTemplateEnd) { + if (pTemplateEnd) { // prepare argument to callback const size_t paramNameLength = std::min((size_t)sizeof(buf) - 1, (size_t)(pTemplateEnd - pTemplateStart - 1)); - if(paramNameLength) { + if (paramNameLength) { memcpy(buf, pTemplateStart + 1, paramNameLength); buf[paramNameLength] = 0; paramName = String(reinterpret_cast(buf)); @@ -408,32 +442,29 @@ size_t AsyncAbstractResponse::_fillBufferAndProcessTemplates(uint8_t* data, size len += _readDataFromCacheOrContent(&data[len - 1], 1) - 1; ++pTemplateStart; } - } else if(&data[len - 1] - pTemplateStart + 1 < TEMPLATE_PARAM_NAME_LENGTH + 2) { // closing placeholder not found, check if it's in the remaining file data + } else if (&data[len - 1] - pTemplateStart + 1 < TEMPLATE_PARAM_NAME_LENGTH + 2) { // closing placeholder not found, check if it's in the remaining file data memcpy(buf, pTemplateStart + 1, &data[len - 1] - pTemplateStart); const size_t readFromCacheOrContent = _readDataFromCacheOrContent(buf + (&data[len - 1] - pTemplateStart), TEMPLATE_PARAM_NAME_LENGTH + 2 - (&data[len - 1] - pTemplateStart + 1)); - if(readFromCacheOrContent) { + if (readFromCacheOrContent) { pTemplateEnd = (uint8_t*)memchr(buf + (&data[len - 1] - pTemplateStart), TEMPLATE_PLACEHOLDER, readFromCacheOrContent); - if(pTemplateEnd) { + if (pTemplateEnd) { // prepare argument to callback *pTemplateEnd = 0; paramName = String(reinterpret_cast(buf)); // Copy remaining read-ahead data into cache _cache.insert(_cache.begin(), pTemplateEnd + 1, buf + (&data[len - 1] - pTemplateStart) + readFromCacheOrContent); pTemplateEnd = &data[len - 1]; - } - else // closing placeholder not found in file data, store found percent symbol as is and advance to the next position + } else // closing placeholder not found in file data, store found percent symbol as is and advance to the next position { // but first, store read file data in cache _cache.insert(_cache.begin(), buf + (&data[len - 1] - pTemplateStart), buf + (&data[len - 1] - pTemplateStart) + readFromCacheOrContent); ++pTemplateStart; } - } - else // closing placeholder not found in content data, store found percent symbol as is and advance to the next position + } else // closing placeholder not found in content data, store found percent symbol as is and advance to the next position ++pTemplateStart; - } - else // closing placeholder not found in content data, store found percent symbol as is and advance to the next position + } else // closing placeholder not found in content data, store found percent symbol as is and advance to the next position ++pTemplateStart; - if(paramName.length()) { + if (paramName.length()) { // call callback and replace with result. // Everything in range [pTemplateStart, pTemplateEnd] can be safely replaced with parameter value. // Data after pTemplateEnd may need to be moved. @@ -445,21 +476,21 @@ size_t AsyncAbstractResponse::_fillBufferAndProcessTemplates(uint8_t* data, size const size_t numBytesCopied = std::min(pvlen, static_cast(&data[originalLen - 1] - pTemplateStart + 1)); // make room for param value // 1. move extra data to cache if parameter value is longer than placeholder AND if there is no room to store - if((pTemplateEnd + 1 < pTemplateStart + numBytesCopied) && (originalLen - (pTemplateStart + numBytesCopied - pTemplateEnd - 1) < len)) { + if ((pTemplateEnd + 1 < pTemplateStart + numBytesCopied) && (originalLen - (pTemplateStart + numBytesCopied - pTemplateEnd - 1) < len)) { _cache.insert(_cache.begin(), &data[originalLen - (pTemplateStart + numBytesCopied - pTemplateEnd - 1)], &data[len]); - //2. parameter value is longer than placeholder text, push the data after placeholder which not saved into cache further to the end + // 2. parameter value is longer than placeholder text, push the data after placeholder which not saved into cache further to the end memmove(pTemplateStart + numBytesCopied, pTemplateEnd + 1, &data[originalLen] - pTemplateStart - numBytesCopied); len = originalLen; // fix issue with truncated data, not sure if it has any side effects - } else if(pTemplateEnd + 1 != pTemplateStart + numBytesCopied) - //2. Either parameter value is shorter than placeholder text OR there is enough free space in buffer to fit. - // Move the entire data after the placeholder + } else if (pTemplateEnd + 1 != pTemplateStart + numBytesCopied) + // 2. Either parameter value is shorter than placeholder text OR there is enough free space in buffer to fit. + // Move the entire data after the placeholder memmove(pTemplateStart + numBytesCopied, pTemplateEnd + 1, &data[len] - pTemplateEnd - 1); // 3. replace placeholder with actual value memcpy(pTemplateStart, pvstr, numBytesCopied); // If result is longer than buffer, copy the remainder into cache (this could happen only if placeholder text itself did not fit entirely in buffer) - if(numBytesCopied < pvlen) { + if (numBytesCopied < pvlen) { _cache.insert(_cache.begin(), pvstr + numBytesCopied, pvstr + pvlen); - } else if(pTemplateStart + numBytesCopied < pTemplateEnd + 1) { // result is copied fully; if result is shorter than placeholder text... + } else if (pTemplateStart + numBytesCopied < pTemplateEnd + 1) { // result is copied fully; if result is shorter than placeholder text... // there is some free room, fill it from cache const size_t roomFreed = pTemplateEnd + 1 - pTemplateStart - numBytesCopied; const size_t totalFreeRoom = originalLen - len + roomFreed; @@ -473,48 +504,66 @@ size_t AsyncAbstractResponse::_fillBufferAndProcessTemplates(uint8_t* data, size return len; } - /* * File Response * */ -AsyncFileResponse::~AsyncFileResponse(){ - if(_content) +AsyncFileResponse::~AsyncFileResponse() { + if (_content) _content.close(); } -void AsyncFileResponse::_setContentType(const String& path){ +void AsyncFileResponse::_setContentType(const String& path) { #if HAVE_EXTERN_GET_CONTENT_TYPE_FUNCTION - extern const __FlashStringHelper *getContentType(const String &path); + extern const __FlashStringHelper* getContentType(const String& path); _contentType = getContentType(path); #else - if (path.endsWith(F(".html"))) _contentType = F("text/html"); - else if (path.endsWith(F(".htm"))) _contentType = F("text/html"); - else if (path.endsWith(F(".css"))) _contentType = F("text/css"); - else if (path.endsWith(F(".json"))) _contentType = F("application/json"); - else if (path.endsWith(F(".js"))) _contentType = F("application/javascript"); - else if (path.endsWith(F(".png"))) _contentType = F("image/png"); - else if (path.endsWith(F(".gif"))) _contentType = F("image/gif"); - else if (path.endsWith(F(".jpg"))) _contentType = F("image/jpeg"); - else if (path.endsWith(F(".ico"))) _contentType = F("image/x-icon"); - else if (path.endsWith(F(".svg"))) _contentType = F("image/svg+xml"); - else if (path.endsWith(F(".eot"))) _contentType = F("font/eot"); - else if (path.endsWith(F(".woff"))) _contentType = F("font/woff"); - else if (path.endsWith(F(".woff2"))) _contentType = F("font/woff2"); - else if (path.endsWith(F(".ttf"))) _contentType = F("font/ttf"); - else if (path.endsWith(F(".xml"))) _contentType = F("text/xml"); - else if (path.endsWith(F(".pdf"))) _contentType = F("application/pdf"); - else if (path.endsWith(F(".zip"))) _contentType = F("application/zip"); - else if(path.endsWith(F(".gz"))) _contentType = F("application/x-gzip"); - else _contentType = F("text/plain"); + if (path.endsWith(F(".html"))) + _contentType = F("text/html"); + else if (path.endsWith(F(".htm"))) + _contentType = F("text/html"); + else if (path.endsWith(F(".css"))) + _contentType = F("text/css"); + else if (path.endsWith(F(".json"))) + _contentType = F("application/json"); + else if (path.endsWith(F(".js"))) + _contentType = F("application/javascript"); + else if (path.endsWith(F(".png"))) + _contentType = F("image/png"); + else if (path.endsWith(F(".gif"))) + _contentType = F("image/gif"); + else if (path.endsWith(F(".jpg"))) + _contentType = F("image/jpeg"); + else if (path.endsWith(F(".ico"))) + _contentType = F("image/x-icon"); + else if (path.endsWith(F(".svg"))) + _contentType = F("image/svg+xml"); + else if (path.endsWith(F(".eot"))) + _contentType = F("font/eot"); + else if (path.endsWith(F(".woff"))) + _contentType = F("font/woff"); + else if (path.endsWith(F(".woff2"))) + _contentType = F("font/woff2"); + else if (path.endsWith(F(".ttf"))) + _contentType = F("font/ttf"); + else if (path.endsWith(F(".xml"))) + _contentType = F("text/xml"); + else if (path.endsWith(F(".pdf"))) + _contentType = F("application/pdf"); + else if (path.endsWith(F(".zip"))) + _contentType = F("application/zip"); + else if (path.endsWith(F(".gz"))) + _contentType = F("application/x-gzip"); + else + _contentType = F("text/plain"); #endif } -AsyncFileResponse::AsyncFileResponse(FS &fs, const String& path, const String& contentType, bool download, AwsTemplateProcessor callback): AsyncAbstractResponse(callback){ +AsyncFileResponse::AsyncFileResponse(FS& fs, const String& path, const String& contentType, bool download, AwsTemplateProcessor callback) : AsyncAbstractResponse(callback) { _code = 200; _path = path; - if(!download && !fs.exists(_path) && fs.exists(_path + F(".gz"))){ + if (!download && !fs.exists(_path) && fs.exists(_path + F(".gz"))) { _path = _path + F(".gz"); addHeader(F("Content-Encoding"), F("gzip")); _callback = nullptr; // Unable to process zipped templates @@ -525,30 +574,30 @@ AsyncFileResponse::AsyncFileResponse(FS &fs, const String& path, const String& c _content = fs.open(_path, fs::FileOpenMode::read); _contentLength = _content.size(); - if(contentType.length() == 0) + if (contentType.length() == 0) _setContentType(path); else _contentType = contentType; int filenameStart = path.lastIndexOf('/') + 1; - char buf[26+path.length()-filenameStart]; + char buf[26 + path.length() - filenameStart]; char* filename = (char*)path.c_str() + filenameStart; - if(download) { + if (download) { // set filename and force download - snprintf_P(buf, sizeof (buf), PSTR("attachment; filename=\"%s\""), filename); + snprintf_P(buf, sizeof(buf), PSTR("attachment; filename=\"%s\""), filename); } else { // set filename and force rendering - snprintf_P(buf, sizeof (buf), PSTR("inline")); + snprintf_P(buf, sizeof(buf), PSTR("inline")); } addHeader(F("Content-Disposition"), buf); } -AsyncFileResponse::AsyncFileResponse(File content, const String& path, const String& contentType, bool download, AwsTemplateProcessor callback): AsyncAbstractResponse(callback){ +AsyncFileResponse::AsyncFileResponse(File content, const String& path, const String& contentType, bool download, AwsTemplateProcessor callback) : AsyncAbstractResponse(callback) { _code = 200; _path = path; - if(!download && String(content.name()).endsWith(F(".gz")) && !path.endsWith(F(".gz"))){ + if (!download && String(content.name()).endsWith(F(".gz")) && !path.endsWith(F(".gz"))) { addHeader(F("Content-Encoding"), F("gzip")); _callback = nullptr; // Unable to process gzipped templates _sendContentLength = true; @@ -558,24 +607,24 @@ AsyncFileResponse::AsyncFileResponse(File content, const String& path, const Str _content = content; _contentLength = _content.size(); - if(contentType.length() == 0) + if (contentType.length() == 0) _setContentType(path); else _contentType = contentType; int filenameStart = path.lastIndexOf('/') + 1; - char buf[26+path.length()-filenameStart]; + char buf[26 + path.length() - filenameStart]; char* filename = (char*)path.c_str() + filenameStart; - if(download) { - snprintf_P(buf, sizeof (buf), PSTR("attachment; filename=\"%s\""), filename); + if (download) { + snprintf_P(buf, sizeof(buf), PSTR("attachment; filename=\"%s\""), filename); } else { - snprintf_P(buf, sizeof (buf), PSTR("inline")); + snprintf_P(buf, sizeof(buf), PSTR("inline")); } addHeader(F("Content-Disposition"), buf); } -size_t AsyncFileResponse::_fillBuffer(uint8_t *data, size_t len){ +size_t AsyncFileResponse::_fillBuffer(uint8_t* data, size_t len) { return _content.read(data, len); } @@ -583,18 +632,18 @@ size_t AsyncFileResponse::_fillBuffer(uint8_t *data, size_t len){ * Stream Response * */ -AsyncStreamResponse::AsyncStreamResponse(Stream &stream, const String& contentType, size_t len, AwsTemplateProcessor callback): AsyncAbstractResponse(callback) { +AsyncStreamResponse::AsyncStreamResponse(Stream& stream, const String& contentType, size_t len, AwsTemplateProcessor callback) : AsyncAbstractResponse(callback) { _code = 200; _content = &stream; _contentLength = len; _contentType = contentType; } -size_t AsyncStreamResponse::_fillBuffer(uint8_t *data, size_t len){ +size_t AsyncStreamResponse::_fillBuffer(uint8_t* data, size_t len) { size_t available = _content->available(); - size_t outLen = (available > len)?len:available; + size_t outLen = (available > len) ? len : available; size_t i; - for(i=0;iread(); return outLen; } @@ -603,20 +652,20 @@ size_t AsyncStreamResponse::_fillBuffer(uint8_t *data, size_t len){ * Callback Response * */ -AsyncCallbackResponse::AsyncCallbackResponse(const String& contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback): AsyncAbstractResponse(templateCallback) { +AsyncCallbackResponse::AsyncCallbackResponse(const String& contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback) : AsyncAbstractResponse(templateCallback) { _code = 200; _content = callback; _contentLength = len; - if(!len) + if (!len) _sendContentLength = false; _contentType = contentType; _filledLength = 0; } -size_t AsyncCallbackResponse::_fillBuffer(uint8_t *data, size_t len){ +size_t AsyncCallbackResponse::_fillBuffer(uint8_t* data, size_t len) { size_t ret = _content(data, len, _filledLength); - if(ret != RESPONSE_TRY_AGAIN){ - _filledLength += ret; + if (ret != RESPONSE_TRY_AGAIN) { + _filledLength += ret; } return ret; } @@ -625,7 +674,7 @@ size_t AsyncCallbackResponse::_fillBuffer(uint8_t *data, size_t len){ * Chunked Response * */ -AsyncChunkedResponse::AsyncChunkedResponse(const String& contentType, AwsResponseFiller callback, AwsTemplateProcessor processorCallback): AsyncAbstractResponse(processorCallback) { +AsyncChunkedResponse::AsyncChunkedResponse(const String& contentType, AwsResponseFiller callback, AwsTemplateProcessor processorCallback) : AsyncAbstractResponse(processorCallback) { _code = 200; _content = callback; _contentLength = 0; @@ -635,10 +684,10 @@ AsyncChunkedResponse::AsyncChunkedResponse(const String& contentType, AwsRespons _filledLength = 0; } -size_t AsyncChunkedResponse::_fillBuffer(uint8_t *data, size_t len){ +size_t AsyncChunkedResponse::_fillBuffer(uint8_t* data, size_t len) { size_t ret = _content(data, len, _filledLength); - if(ret != RESPONSE_TRY_AGAIN){ - _filledLength += ret; + if (ret != RESPONSE_TRY_AGAIN) { + _filledLength += ret; } return ret; } @@ -647,7 +696,7 @@ size_t AsyncChunkedResponse::_fillBuffer(uint8_t *data, size_t len){ * Progmem Response * */ -AsyncProgmemResponse::AsyncProgmemResponse(int code, const String& contentType, const uint8_t * content, size_t len, AwsTemplateProcessor callback): AsyncAbstractResponse(callback) { +AsyncProgmemResponse::AsyncProgmemResponse(int code, const String& contentType, const uint8_t* content, size_t len, AwsTemplateProcessor callback) : AsyncAbstractResponse(callback) { _code = code; _content = content; _contentType = contentType; @@ -655,7 +704,7 @@ AsyncProgmemResponse::AsyncProgmemResponse(int code, const String& contentType, _readLength = 0; } -size_t AsyncProgmemResponse::_fillBuffer(uint8_t *data, size_t len){ +size_t AsyncProgmemResponse::_fillBuffer(uint8_t* data, size_t len) { size_t left = _contentLength - _readLength; if (left > len) { memcpy_P(data, _content + _readLength, len); @@ -667,30 +716,28 @@ size_t AsyncProgmemResponse::_fillBuffer(uint8_t *data, size_t len){ return left; } - /* * 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; _contentLength = 0; _contentType = contentType; - _content = std::unique_ptr(new cbuf(bufferSize)); //std::make_unique(bufferSize); + _content = std::unique_ptr(new cbuf(bufferSize)); // std::make_unique(bufferSize); } AsyncResponseStream::~AsyncResponseStream() = default; -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); } -size_t AsyncResponseStream::write(const uint8_t *data, size_t len){ - if(_started()) +size_t AsyncResponseStream::write(const uint8_t* data, size_t len) { + if (_started()) return 0; - if(len > _content->room()){ + if (len > _content->room()) { size_t needed = len - _content->room(); _content->resizeAdd(needed); } @@ -699,6 +746,6 @@ size_t AsyncResponseStream::write(const uint8_t *data, size_t len){ return written; } -size_t AsyncResponseStream::write(uint8_t data){ +size_t AsyncResponseStream::write(uint8_t data) { return write(&data, 1); } diff --git a/src/WebServer.cpp b/src/WebServer.cpp index 62e85b2..9d78e02 100644 --- a/src/WebServer.cpp +++ b/src/WebServer.cpp @@ -21,105 +21,126 @@ #include "ESPAsyncWebServer.h" #include "WebHandlerImpl.h" -bool ON_STA_FILTER(AsyncWebServerRequest *request) { +bool ON_STA_FILTER(AsyncWebServerRequest* request) { return WiFi.localIP() == request->client()->localIP(); } -bool ON_AP_FILTER(AsyncWebServerRequest *request) { +bool ON_AP_FILTER(AsyncWebServerRequest* request) { return WiFi.localIP() != request->client()->localIP(); } #ifndef HAVE_FS_FILE_OPEN_MODE -const char *fs::FileOpenMode::read = "r"; -const char *fs::FileOpenMode::write = "w"; -const char *fs::FileOpenMode::append = "a"; +const char* fs::FileOpenMode::read = "r"; +const char* fs::FileOpenMode::write = "w"; +const char* fs::FileOpenMode::append = "a"; #endif AsyncWebServer::AsyncWebServer(uint16_t port) - : _server(port) - , _rewrites(LinkedList([](AsyncWebRewrite* r){ delete r; })) - , _handlers(LinkedList([](AsyncWebHandler* h){ delete h; })) -{ + : _server(port) { _catchAllHandler = new AsyncCallbackWebHandler(); - if(_catchAllHandler == NULL) + if (_catchAllHandler == NULL) return; - _server.onClient([](void *s, AsyncClient* c){ - if(c == NULL) + _server.onClient([](void* s, AsyncClient* c) { + if (c == NULL) return; c->setRxTimeout(3); - AsyncWebServerRequest *r = new AsyncWebServerRequest((AsyncWebServer*)s, c); - if(r == NULL){ + AsyncWebServerRequest* r = new AsyncWebServerRequest((AsyncWebServer*)s, c); + if (r == NULL) { c->close(true); c->free(); delete c; } - }, this); + }, + this); } -AsyncWebServer::~AsyncWebServer(){ +AsyncWebServer::~AsyncWebServer() { reset(); end(); - if(_catchAllHandler) delete _catchAllHandler; + if (_catchAllHandler) + delete _catchAllHandler; } -AsyncWebRewrite& AsyncWebServer::addRewrite(AsyncWebRewrite* rewrite){ - _rewrites.add(rewrite); - return *rewrite; +AsyncWebRewrite& AsyncWebServer::addRewrite(std::shared_ptr rewrite) { + _rewrites.emplace_back(rewrite); + return *_rewrites.back().get(); } -bool AsyncWebServer::removeRewrite(AsyncWebRewrite *rewrite){ - return _rewrites.remove(rewrite); +AsyncWebRewrite& AsyncWebServer::addRewrite(AsyncWebRewrite* rewrite) { + _rewrites.emplace_back(rewrite); + return *_rewrites.back().get(); } -AsyncWebRewrite& AsyncWebServer::rewrite(const char* from, const char* to){ - return addRewrite(new AsyncWebRewrite(from, to)); +bool AsyncWebServer::removeRewrite(AsyncWebRewrite* rewrite) { + return removeRewrite(rewrite->from().c_str(), rewrite->toUrl().c_str()); } -AsyncWebHandler& AsyncWebServer::addHandler(AsyncWebHandler* handler){ - _handlers.add(handler); - return *handler; +bool AsyncWebServer::removeRewrite(const char* from, const char* to) { + for (auto r = _rewrites.begin(); r != _rewrites.end(); ++r) { + if (r->get()->from() == from && r->get()->toUrl() == to) { + _rewrites.erase(r); + return true; + } + } + return false; } -bool AsyncWebServer::removeHandler(AsyncWebHandler *handler){ - return _handlers.remove(handler); +AsyncWebRewrite& AsyncWebServer::rewrite(const char* from, const char* to) { + _rewrites.emplace_back(std::make_shared(from, to)); + return *_rewrites.back().get(); } -void AsyncWebServer::begin(){ +AsyncWebHandler& AsyncWebServer::addHandler(AsyncWebHandler* handler) { + _handlers.emplace_back(handler); + return *(_handlers.back().get()); +} + +bool AsyncWebServer::removeHandler(AsyncWebHandler* handler) { + for (auto i = _handlers.begin(); i != _handlers.end(); ++i) { + if (i->get() == handler) { + _handlers.erase(i); + return true; + } + } + return false; +} + +void AsyncWebServer::begin() { _server.setNoDelay(true); _server.begin(); } -void AsyncWebServer::end(){ +void AsyncWebServer::end() { _server.end(); } #if ASYNC_TCP_SSL_ENABLED -void AsyncWebServer::onSslFileRequest(AcSSlFileHandler cb, void* arg){ +void AsyncWebServer::onSslFileRequest(AcSSlFileHandler cb, void* arg) { _server.onSslFileRequest(cb, arg); } -void AsyncWebServer::beginSecure(const char *cert, const char *key, const char *password){ +void AsyncWebServer::beginSecure(const char* cert, const char* key, const char* password) { _server.beginSecure(cert, key, password); } #endif -void AsyncWebServer::_handleDisconnect(AsyncWebServerRequest *request){ +void AsyncWebServer::_handleDisconnect(AsyncWebServerRequest* request) { delete request; } -void AsyncWebServer::_rewriteRequest(AsyncWebServerRequest *request){ - for(const auto& r: _rewrites){ - if (r->match(request)){ +void AsyncWebServer::_rewriteRequest(AsyncWebServerRequest* request) { + for (const auto& r : _rewrites) { + if (r->match(request)) { request->_url = r->toUrl(); request->_addGetParams(r->params()); } } } -void AsyncWebServer::_attachHandler(AsyncWebServerRequest *request){ - for(const auto& h: _handlers){ - if (h->filter(request) && h->canHandle(request)){ - request->setHandler(h); +void AsyncWebServer::_attachHandler(AsyncWebServerRequest* request) { + for (auto& h : _handlers) { + if (h->filter(request) && h->canHandle(request)) { + request->setHandler(h.get()); return; } } @@ -128,8 +149,7 @@ void AsyncWebServer::_attachHandler(AsyncWebServerRequest *request){ request->setHandler(_catchAllHandler); } - -AsyncCallbackWebHandler& AsyncWebServer::on(const char* uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest, ArUploadHandlerFunction onUpload, ArBodyHandlerFunction onBody){ +AsyncCallbackWebHandler& AsyncWebServer::on(const char* uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest, ArUploadHandlerFunction onUpload, ArBodyHandlerFunction onBody) { AsyncCallbackWebHandler* handler = new AsyncCallbackWebHandler(); handler->setUri(uri); handler->setMethod(method); @@ -140,7 +160,7 @@ AsyncCallbackWebHandler& AsyncWebServer::on(const char* uri, WebRequestMethodCom return *handler; } -AsyncCallbackWebHandler& AsyncWebServer::on(const char* uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest, ArUploadHandlerFunction onUpload){ +AsyncCallbackWebHandler& AsyncWebServer::on(const char* uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest, ArUploadHandlerFunction onUpload) { AsyncCallbackWebHandler* handler = new AsyncCallbackWebHandler(); handler->setUri(uri); handler->setMethod(method); @@ -150,7 +170,7 @@ AsyncCallbackWebHandler& AsyncWebServer::on(const char* uri, WebRequestMethodCom return *handler; } -AsyncCallbackWebHandler& AsyncWebServer::on(const char* uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest){ +AsyncCallbackWebHandler& AsyncWebServer::on(const char* uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest) { AsyncCallbackWebHandler* handler = new AsyncCallbackWebHandler(); handler->setUri(uri); handler->setMethod(method); @@ -159,7 +179,7 @@ AsyncCallbackWebHandler& AsyncWebServer::on(const char* uri, WebRequestMethodCom return *handler; } -AsyncCallbackWebHandler& AsyncWebServer::on(const char* uri, ArRequestHandlerFunction onRequest){ +AsyncCallbackWebHandler& AsyncWebServer::on(const char* uri, ArRequestHandlerFunction onRequest) { AsyncCallbackWebHandler* handler = new AsyncCallbackWebHandler(); handler->setUri(uri); handler->onRequest(onRequest); @@ -167,32 +187,31 @@ AsyncCallbackWebHandler& AsyncWebServer::on(const char* uri, ArRequestHandlerFun return *handler; } -AsyncStaticWebHandler& AsyncWebServer::serveStatic(const char* uri, fs::FS& fs, const char* path, const char* cache_control){ +AsyncStaticWebHandler& AsyncWebServer::serveStatic(const char* uri, fs::FS& fs, const char* path, const char* cache_control) { AsyncStaticWebHandler* handler = new AsyncStaticWebHandler(uri, fs, path, cache_control); addHandler(handler); return *handler; } -void AsyncWebServer::onNotFound(ArRequestHandlerFunction fn){ +void AsyncWebServer::onNotFound(ArRequestHandlerFunction fn) { _catchAllHandler->onRequest(fn); } -void AsyncWebServer::onFileUpload(ArUploadHandlerFunction fn){ +void AsyncWebServer::onFileUpload(ArUploadHandlerFunction fn) { _catchAllHandler->onUpload(fn); } -void AsyncWebServer::onRequestBody(ArBodyHandlerFunction fn){ +void AsyncWebServer::onRequestBody(ArBodyHandlerFunction fn) { _catchAllHandler->onBody(fn); } -void AsyncWebServer::reset(){ - _rewrites.free(); - _handlers.free(); +void AsyncWebServer::reset() { + _rewrites.clear(); + _handlers.clear(); - if (_catchAllHandler != NULL){ + if (_catchAllHandler != NULL) { _catchAllHandler->onRequest(NULL); _catchAllHandler->onUpload(NULL); _catchAllHandler->onBody(NULL); } } -