diff --git a/README.md b/README.md index 55efb4b..b4a3442 100644 --- a/README.md +++ b/README.md @@ -15,8 +15,9 @@ Supports: WebSocket, SSE, Authentication, Arduino Json 7, File Upload, Static Fi This fork is based on [yubox-node-org/ESPAsyncWebServer](https://github.com/yubox-node-org/ESPAsyncWebServer) and includes all the concurrency fixes. -- [Coordinate and dependencies](#coordinate-and-dependencies) - [Changes in this fork](#changes-in-this-fork) +- [Dependencies](#dependencies) +- [Performance](#performance) - [Important recommendations](#important-recommendations) - [`AsyncWebSocketMessageBuffer` and `makeBuffer()`](#asyncwebsocketmessagebuffer-and-makebuffer) - [How to replace a response](#how-to-replace-a-response) @@ -25,20 +26,6 @@ This fork is based on [yubox-node-org/ESPAsyncWebServer](https://github.com/yubo - [Migration to Middleware to improve performance and memory usage](#migration-to-middleware-to-improve-performance-and-memory-usage) - [Original Documentation](#original-documentation) -## Coordinate and dependencies - -**WARNING** The library name was changed from `ESP Async WebServer` to `ESPAsyncWebServer` as per the Arduino Lint recommendations. - -``` -mathieucarbou/ESPAsyncWebServer @ 3.3.7 -``` - -Dependency: - -- **ESP32**: `mathieucarbou/AsyncTCP @ 3.2.5` (Arduino IDE: [https://github.com/mathieucarbou/AsyncTCP#v3.2.5](https://github.com/mathieucarbou/AsyncTCP/releases)) -- **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)) - ## Changes in this fork - (bug) A lot of bug fixes @@ -55,6 +42,7 @@ Dependency: - (feat) **Resumable download** support using HEAD and bytes range - (feat) `StreamConcat` example to show how to stream multiple files in one response - (feat) Removed ESPIDF Editor (this is not the role of a web server library to do that - get the source files from the original repos if required) +- (perf) [AsyncTCPSock](https://github.com/mathieucarbou/AsyncTCPSock) support: AsyncTCP can be ignored and AsyncTCPSock used instead - (perf) `char*` overloads to avoid using `String` - (perf) `DEFAULT_MAX_WS_CLIENTS` to change the number of allows WebSocket clients and use `cleanupClients()` to help cleanup resources about dead clients - (perf) `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 @@ -64,6 +52,112 @@ Dependency: - (perf) Lot of code cleanup and optimizations - (perf) Performance improvements in terms of memory, speed and size +--- + +## WARNING: Important notes about future version 4.x + +This ESPAsyncWebServer fork is now at version 3.x. + +Next version 4.x will: + +1. Drop support for ESP8266, which goes EOL in a few years. All ESP8266 boards can be replaced by equivalent ESP32 boards. +2. Drop support for Arduino 2.x and ESP-IDF 4.x. The library will be compatible with Arduino 3.x and ESP-IDF 5.x. +3. Drop support for ArduinoJson 5.x and 6.x. The library will be compatible with ArduinoJson 7.x. + +So if you need one of these feature, you will have to stick with 3.x or another fork. + +## Dependencies + +**WARNING** The library name was changed from `ESP Async WebServer` to `ESPAsyncWebServer` as per the Arduino Lint recommendations, but its name had to stay `ESP Async WebServer` in Arduino Registry. + +**PlatformIO / pioarduino:** + +```ini +lib_compat_mode = strict +lib_ldf_mode = chain +lib_deps = mathieucarbou/ESPAsyncWebServer @ 3.3.23 +``` + +**Dependencies:** + +- **ESP32 with AsyncTCP**: `mathieucarbou/AsyncTCP @ 3.2.14` + Arduino IDE: [https://github.com/mathieucarbou/AsyncTCP#v3.2.14](https://github.com/mathieucarbou/AsyncTCP/releases) + +- **ESP32 with AsyncTCPSock**: `https://github.com/mathieucarbou/AsyncTCPSock/archive/refs/tags/v1.0.3-dev.zip` + +- **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) + +**AsyncTCPSock** + +AsyncTCPSock can be used instead of AsyncTCP by excluding AsyncTCP from the library dependencies and adding AsyncTCPSock instead: + +```ini +lib_compat_mode = strict +lib_ldf_mode = chain +lib_deps = + ; mathieucarbou/AsyncTCP @ 3.2.14 + https://github.com/mathieucarbou/AsyncTCPSock/archive/refs/tags/v1.0.3-dev.zip + mathieucarbou/ESPAsyncWebServer @ 3.3.23 +lib_ignore = + AsyncTCP + mathieucarbou/AsyncTCP +``` + +## Performance + +Performance of `mathieucarbou/ESPAsyncWebServer @ 3.3.23`: + +```bash +> brew install autocannon +> autocannon -c 10 -w 10 -d 20 http://192.168.4.1 +``` + +With `mathieucarbou/AsyncTCP @ 3.2.14` + +[![](https://mathieu.carbou.me/ESPAsyncWebServer/perf-c10.png)](https://mathieu.carbou.me/ESPAsyncWebServer/perf-c10.png) + +With `https://github.com/mathieucarbou/AsyncTCPSock/archive/refs/tags/v1.0.3-dev.zip`: + +[![](https://mathieu.carbou.me/ESPAsyncWebServer/perf-c10-asynctcpsock.png)](https://mathieu.carbou.me/ESPAsyncWebServer/perf-c10-asynctcpsock.png) + +**SSE performance** + +In the example, there is an endpoint `/events` with some comments showing how these metrics are calculated. + +Test is running for 20 seconds with 10 connections. + +``` +// With AsyncTCP, with 10 workers: no message discarded from the queue +// +// Total: 1875 events, 468.75000000000000000000 events / second +// Total: 1870 events, 467.50000000000000000000 events / second +// Total: 1871 events, 467.75000000000000000000 events / second +// Total: 1875 events, 468.75000000000000000000 events / second +// Total: 1871 events, 467.75000000000000000000 events / second +// Total: 1805 events, 451.25000000000000000000 events / second +// Total: 1803 events, 450.75000000000000000000 events / second +// Total: 1873 events, 468.25000000000000000000 events / second +// Total: 1872 events, 468.00000000000000000000 events / second +// Total: 1805 events, 451.25000000000000000000 events / second +// +// With AsyncTCPSock, with 10 workers: no message discarded from the queue +// +// Total: 1242 events, 310.50000000000000000000 events / second +// Total: 1242 events, 310.50000000000000000000 events / second +// Total: 1242 events, 310.50000000000000000000 events / second +// Total: 1242 events, 310.50000000000000000000 events / second +// Total: 1181 events, 295.25000000000000000000 events / second +// Total: 1182 events, 295.50000000000000000000 events / second +// Total: 1240 events, 310.00000000000000000000 events / second +// Total: 1181 events, 295.25000000000000000000 events / second +// Total: 1181 events, 295.25000000000000000000 events / second +// Total: 1183 events, 295.75000000000000000000 events / second +``` + ## Important recommendations Most of the crashes are caused by improper configuration of the library for the project. @@ -72,7 +166,7 @@ Here are some recommendations to avoid them. 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. + You can look at the [MycilaTaskMonitor](https://mathieu.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`. @@ -528,7 +622,7 @@ Endpoints which consume JSON can use a special handler to get ready to use JSON #include "ArduinoJson.h" AsyncCallbackJsonWebHandler* handler = new AsyncCallbackJsonWebHandler("/rest/endpoint", [](AsyncWebServerRequest *request, JsonVariant &json) { - JsonObject& jsonObj = json.as(); + JsonObject jsonObj = json.as(); // ... }); server.addHandler(handler); @@ -1186,7 +1280,7 @@ For actual serving the file. ### Param Rewrite With Matching -It is possible to rewrite the request url with parameter matchg. Here is an example with one parameter: +It is possible to rewrite the request url with parameter match. Here is an example with one parameter: Rewrite for example "/radio/{frequence}" -> "/radio?f={frequence}" ```cpp diff --git a/docs/index.md b/docs/index.md index 55efb4b..d47c640 100644 --- a/docs/index.md +++ b/docs/index.md @@ -15,8 +15,9 @@ Supports: WebSocket, SSE, Authentication, Arduino Json 7, File Upload, Static Fi This fork is based on [yubox-node-org/ESPAsyncWebServer](https://github.com/yubox-node-org/ESPAsyncWebServer) and includes all the concurrency fixes. -- [Coordinate and dependencies](#coordinate-and-dependencies) - [Changes in this fork](#changes-in-this-fork) +- [Dependencies](#dependencies) +- [Performance](#performance) - [Important recommendations](#important-recommendations) - [`AsyncWebSocketMessageBuffer` and `makeBuffer()`](#asyncwebsocketmessagebuffer-and-makebuffer) - [How to replace a response](#how-to-replace-a-response) @@ -25,20 +26,6 @@ This fork is based on [yubox-node-org/ESPAsyncWebServer](https://github.com/yubo - [Migration to Middleware to improve performance and memory usage](#migration-to-middleware-to-improve-performance-and-memory-usage) - [Original Documentation](#original-documentation) -## Coordinate and dependencies - -**WARNING** The library name was changed from `ESP Async WebServer` to `ESPAsyncWebServer` as per the Arduino Lint recommendations. - -``` -mathieucarbou/ESPAsyncWebServer @ 3.3.7 -``` - -Dependency: - -- **ESP32**: `mathieucarbou/AsyncTCP @ 3.2.5` (Arduino IDE: [https://github.com/mathieucarbou/AsyncTCP#v3.2.5](https://github.com/mathieucarbou/AsyncTCP/releases)) -- **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)) - ## Changes in this fork - (bug) A lot of bug fixes @@ -55,6 +42,7 @@ Dependency: - (feat) **Resumable download** support using HEAD and bytes range - (feat) `StreamConcat` example to show how to stream multiple files in one response - (feat) Removed ESPIDF Editor (this is not the role of a web server library to do that - get the source files from the original repos if required) +- (perf) [AsyncTCPSock](https://github.com/mathieucarbou/AsyncTCPSock) support: AsyncTCP can be ignored and AsyncTCPSock used instead - (perf) `char*` overloads to avoid using `String` - (perf) `DEFAULT_MAX_WS_CLIENTS` to change the number of allows WebSocket clients and use `cleanupClients()` to help cleanup resources about dead clients - (perf) `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 @@ -64,6 +52,112 @@ Dependency: - (perf) Lot of code cleanup and optimizations - (perf) Performance improvements in terms of memory, speed and size +--- + +## WARNING: Important notes about future version 4.x + +This ESPAsyncWebServer fork is now at version 3.x. + +Next version 4.x will: + +1. Drop support for ESP8266, which goes EOL in a few years. All ESP8266 boards can be replaced by equivalent ESP32 boards. +2. Drop support for Arduino 2.x and ESP-IDF 4.x. The library will be compatible with Arduino 3.x and ESP-IDF 5.x. +3. Drop support for ArduinoJson 5.x and 6.x. The library will be compatible with ArduinoJson 7.x. + +So if you need one of these feature, you will have to stick with 3.x or another fork. + +## Dependencies + +**WARNING** The library name was changed from `ESP Async WebServer` to `ESPAsyncWebServer` as per the Arduino Lint recommendations, but its name had to stay `ESP Async WebServer` in Arduino Registry. + +**PlatformIO / pioarduino:** + +```ini +lib_compat_mode = strict +lib_ldf_mode = chain +lib_deps = mathieucarbou/ESPAsyncWebServer @ 3.3.23 +``` + +**Dependencies:** + +- **ESP32 with AsyncTCP**: `mathieucarbou/AsyncTCP @ 3.2.14` + Arduino IDE: [https://github.com/mathieucarbou/AsyncTCP#v3.2.14](https://github.com/mathieucarbou/AsyncTCP/releases) + +- **ESP32 with AsyncTCPSock**: `https://github.com/mathieucarbou/AsyncTCPSock/archive/refs/tags/v1.0.3-dev.zip` + +- **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) + +**AsyncTCPSock** + +AsyncTCPSock can be used instead of AsyncTCP by excluding AsyncTCP from the library dependencies and adding AsyncTCPSock instead: + +```ini +lib_compat_mode = strict +lib_ldf_mode = chain +lib_deps = + ; mathieucarbou/AsyncTCP @ 3.2.14 + https://github.com/mathieucarbou/AsyncTCPSock/archive/refs/tags/v1.0.3-dev.zip + mathieucarbou/ESPAsyncWebServer @ 3.3.23 +lib_ignore = + AsyncTCP + mathieucarbou/AsyncTCP +``` + +## Performance + +Performance of `mathieucarbou/ESPAsyncWebServer @ 3.3.23`: + +```bash +> brew install autocannon +> autocannon -c 10 -w 10 -d 20 http://192.168.4.1 +``` + +With `mathieucarbou/AsyncTCP @ 3.2.14` + +[![](https://mathieu.carbou.me/ESPAsyncWebServer/perf-c10.png)](https://mathieu.carbou.me/ESPAsyncWebServer/perf-c10.png) + +With `https://github.com/mathieucarbou/AsyncTCPSock/archive/refs/tags/v1.0.3-dev.zip`: + +[![](https://mathieu.carbou.me/ESPAsyncWebServer/perf-c10-asynctcpsock.png)](https://mathieu.carbou.me/ESPAsyncWebServer/perf-c10-asynctcpsock.png) + +**SSE performance** + +In the example, there is an endpoint `/events` with some comments showing how these metrics are calculated. + +Test is running for 20 seconds with 10 connections. + +``` +// With AsyncTCP, with 10 workers: no message discarded from the queue +// +// Total: 1875 events, 468.75000000000000000000 events / second +// Total: 1870 events, 467.50000000000000000000 events / second +// Total: 1871 events, 467.75000000000000000000 events / second +// Total: 1875 events, 468.75000000000000000000 events / second +// Total: 1871 events, 467.75000000000000000000 events / second +// Total: 1805 events, 451.25000000000000000000 events / second +// Total: 1803 events, 450.75000000000000000000 events / second +// Total: 1873 events, 468.25000000000000000000 events / second +// Total: 1872 events, 468.00000000000000000000 events / second +// Total: 1805 events, 451.25000000000000000000 events / second +// +// With AsyncTCPSock, with 10 workers: no message discarded from the queue +// +// Total: 1242 events, 310.50000000000000000000 events / second +// Total: 1242 events, 310.50000000000000000000 events / second +// Total: 1242 events, 310.50000000000000000000 events / second +// Total: 1242 events, 310.50000000000000000000 events / second +// Total: 1181 events, 295.25000000000000000000 events / second +// Total: 1182 events, 295.50000000000000000000 events / second +// Total: 1240 events, 310.00000000000000000000 events / second +// Total: 1181 events, 295.25000000000000000000 events / second +// Total: 1181 events, 295.25000000000000000000 events / second +// Total: 1183 events, 295.75000000000000000000 events / second +``` + ## Important recommendations Most of the crashes are caused by improper configuration of the library for the project. @@ -72,7 +166,7 @@ Here are some recommendations to avoid them. 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. + You can look at the [MycilaTaskMonitor](https://mathieu.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`. @@ -1186,7 +1280,7 @@ For actual serving the file. ### Param Rewrite With Matching -It is possible to rewrite the request url with parameter matchg. Here is an example with one parameter: +It is possible to rewrite the request url with parameter match. Here is an example with one parameter: Rewrite for example "/radio/{frequence}" -> "/radio?f={frequence}" ```cpp diff --git a/docs/perf-c10-asynctcpsock.png b/docs/perf-c10-asynctcpsock.png new file mode 100644 index 0000000..b1d4d7a Binary files /dev/null and b/docs/perf-c10-asynctcpsock.png differ diff --git a/docs/perf-c10.png b/docs/perf-c10.png new file mode 100644 index 0000000..e63e71a Binary files /dev/null and b/docs/perf-c10.png differ diff --git a/examples/CaptivePortal/CaptivePortal.ino b/examples/CaptivePortal/CaptivePortal.ino index ed9dfff..69232d8 100644 --- a/examples/CaptivePortal/CaptivePortal.ino +++ b/examples/CaptivePortal/CaptivePortal.ino @@ -16,10 +16,7 @@ AsyncWebServer server(80); class CaptiveRequestHandler : public AsyncWebHandler { public: - CaptiveRequestHandler() {} - virtual ~CaptiveRequestHandler() {} - - bool canHandle(__unused AsyncWebServerRequest* request) { + bool canHandle(__unused AsyncWebServerRequest* request) const override { return true; } diff --git a/examples/Draft/Draft.ino b/examples/Draft/Draft.ino deleted file mode 100644 index f10a9e7..0000000 --- a/examples/Draft/Draft.ino +++ /dev/null @@ -1,37 +0,0 @@ -#include "mbedtls/md5.h" -#include -#include - -void setup() { - Serial.begin(115200); - delay(2000); - - const char* data = "Hello World"; - - { - uint8_t md5[16]; - mbedtls_md5_context _ctx; - mbedtls_md5_init(&_ctx); - mbedtls_md5_starts(&_ctx); - mbedtls_md5_update(&_ctx, (const unsigned char*)data, strlen(data)); - mbedtls_md5_finish(&_ctx, md5); - char output[33]; - for (int i = 0; i < 16; i++) { - sprintf_P(output + (i * 2), PSTR("%02x"), md5[i]); - } - Serial.println(String(output)); - } - - { - MD5Builder md5; - md5.begin(); - md5.add(data, strlen(data); - md5.calculate(); - char output[33]; - md5.getChars(output); - Serial.println(String(output)); - } -} - -void loop() { -} diff --git a/examples/Filters/Filters.ino b/examples/Filters/Filters.ino index de5129a..2335364 100644 --- a/examples/Filters/Filters.ino +++ b/examples/Filters/Filters.ino @@ -18,14 +18,11 @@ AsyncWebServer server(80); class CaptiveRequestHandler : public AsyncWebHandler { public: - CaptiveRequestHandler() {} - virtual ~CaptiveRequestHandler() {} - - bool canHandle(__unused AsyncWebServerRequest* request) { + bool canHandle(__unused AsyncWebServerRequest* request) const override { return true; } - void handleRequest(AsyncWebServerRequest* request) { + void handleRequest(AsyncWebServerRequest* request) override { AsyncResponseStream* response = request->beginResponseStream("text/html"); response->print("Captive Portal"); response->print("

This is out captive portal front page.

"); diff --git a/examples/Issue162/Issue162.ino b/examples/Issue162/Issue162.ino new file mode 100644 index 0000000..9f7312f --- /dev/null +++ b/examples/Issue162/Issue162.ino @@ -0,0 +1,84 @@ +/** + * + * Connect to AP and run in bash: + * + * > while true; do echo "PING"; sleep 0.1; done | websocat ws://192.168.4.1/ws + * + */ +#include +#ifdef ESP8266 + #include +#endif +#ifdef ESP32 + #include +#endif +#include + +#include +AsyncWebServer server(80); +AsyncWebSocket ws("/ws"); + +void onWsEvent(AsyncWebSocket* server, AsyncWebSocketClient* client, AwsEventType type, void* arg, uint8_t* data, size_t len) { + if (type == WS_EVT_CONNECT) { + Serial.printf("Client #%" PRIu32 " connected.\n", client->id()); + } else if (type == WS_EVT_DISCONNECT) { + Serial.printf("Client #%" PRIu32 " disconnected.\n", client->id()); + } else if (type == WS_EVT_ERROR) { + Serial.printf("Client #%" PRIu32 " error.\n", client->id()); + } else if (type == WS_EVT_DATA) { + Serial.printf("Client #%" PRIu32 " len: %u\n", client->id(), len); + } else if (type == WS_EVT_PONG) { + Serial.printf("Client #%" PRIu32 " pong.\n", client->id()); + } else if (type == WS_EVT_PING) { + Serial.printf("Client #%" PRIu32 " ping.\n", client->id()); + } +} + +void setup() { + Serial.begin(115200); + + WiFi.mode(WIFI_AP); + WiFi.softAP("esp-captive"); + + ws.onEvent(onWsEvent); + server.addHandler(&ws); + + server.on("/close_all_ws_clients", HTTP_GET | HTTP_POST, [](AsyncWebServerRequest* request) { + Serial.println("Closing all WebSocket clients..."); + ws.closeAll(); + request->send(200, "application/json", "{\"status\":\"all clients closed\"}"); + }); + + server.on("/", HTTP_GET, [](AsyncWebServerRequest* request) { + request->send(200, "text/html", R"rawliteral( + + + + + +

+ + + )rawliteral"); + }); + + server.begin(); +} + +void loop() { + vTaskDelete(NULL); +} diff --git a/examples/Issue85/Issue85.ino b/examples/Issue85/Issue85.ino new file mode 100644 index 0000000..fa2aa55 --- /dev/null +++ b/examples/Issue85/Issue85.ino @@ -0,0 +1,127 @@ +/** + * + * Connect to AP and run in bash: + * + * > while true; do echo "PING"; sleep 0.1; done | websocat ws://192.168.4.1/ws + * + */ +#include +#ifdef ESP8266 + #include +#endif +#ifdef ESP32 + #include +#endif +#include + +#include + +size_t msgCount = 0; +size_t window = 100; +std::list times; + +void connect_wifi() { + WiFi.mode(WIFI_AP); + WiFi.softAP("esp-captive"); + + // Serial.print("Connecting"); + // while (WiFi.status() != WL_CONNECTED) { + // delay(500); + // Serial.print("."); + // } + // Serial.println(); + + // Serial.print("Connected, IP address: "); + // Serial.println(WiFi.localIP()); +} + +void notFound(AsyncWebServerRequest* request) { + request->send(404, "text/plain", "Not found"); +} + +AsyncWebServer server(80); +AsyncWebSocket ws("/ws"); + +// initial stack +char* stack_start; + +void printStackSize() { + char stack; + Serial.print(F("stack size ")); + Serial.print(stack_start - &stack); + Serial.print(F(" | Heap free:")); + Serial.print(ESP.getFreeHeap()); +#ifdef ESP8266 + Serial.print(F(" frag:")); + Serial.print(ESP.getHeapFragmentation()); + Serial.print(F(" maxFreeBlock:")); + Serial.print(ESP.getMaxFreeBlockSize()); +#endif + Serial.println(); +} + +void onWsEventEmpty(AsyncWebSocket* server, AsyncWebSocketClient* client, AwsEventType type, void* arg, uint8_t* data, size_t len) { + msgCount++; + Serial.printf("count: %d\n", msgCount); + + times.push_back(millis()); + while (times.size() > window) + times.pop_front(); + if (times.size() == window) + Serial.printf("%f req/s\n", 1000.0 * window / (times.back() - times.front())); + + printStackSize(); + + client->text("PONG"); +} + +void serve_upload(AsyncWebServerRequest* request, const String& filename, size_t index, uint8_t* data, size_t len, bool final) { + Serial.print("> onUpload "); + Serial.print("index: "); + Serial.print(index); + Serial.print(" len:"); + Serial.print(len); + Serial.print(" final:"); + Serial.print(final); + Serial.println(); +} + +void setup() { + char stack; + stack_start = &stack; + + Serial.begin(115200); + Serial.println("\n\n\nStart"); + Serial.printf("stack_start: %p\n", stack_start); + + connect_wifi(); + + server.onNotFound(notFound); + + ws.onEvent(onWsEventEmpty); + server.addHandler(&ws); + + server.on("/upload", HTTP_POST, [](AsyncWebServerRequest* request) { request->send(200); }, serve_upload); + + server.begin(); + Serial.println("Server started"); +} + +String msg = ""; +uint32_t count = 0; +void loop() { + // ws.cleanupClients(); + // count += 1; + // // Concatenate some string, and clear it after some time + // static unsigned long millis_string = millis(); + // if (millis() - millis_string > 1) { + // millis_string += 100; + // if (count % 100 == 0) { + // // printStackSize(); + // // Serial.print(msg); + // msg = String(); + // } else { + // msg += 'T'; + // } + // } +} \ No newline at end of file diff --git a/examples/SimpleServer/SimpleServer.ino b/examples/SimpleServer/SimpleServer.ino index 899c77d..6d2863f 100644 --- a/examples/SimpleServer/SimpleServer.ino +++ b/examples/SimpleServer/SimpleServer.ino @@ -19,7 +19,7 @@ #include -#if ASYNC_JSON_SUPPORT == 1 +#if __has_include("ArduinoJson.h") #include #include #include @@ -27,6 +27,80 @@ #include +const char* htmlContent PROGMEM = R"( + + + + Sample HTML + + +

Hello, World!

+

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod + rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper + arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit + accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi. + Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo + dapibus elit, id varius sem dui id lacus.

+

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod + rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper + arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit + accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi. + Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo + dapibus elit, id varius sem dui id lacus.

+

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod + rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper + arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit + accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi. + Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo + dapibus elit, id varius sem dui id lacus.

+

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod + rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper + arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit + accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi. + Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo + dapibus elit, id varius sem dui id lacus.

+

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod + rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper + arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit + accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi. + Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo + dapibus elit, id varius sem dui id lacus.

+

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod + rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper + arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit + accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi. + Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo + dapibus elit, id varius sem dui id lacus.

+

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod + rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper + arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit + accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi. + Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo + dapibus elit, id varius sem dui id lacus.

+

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod + rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper + arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit + accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi. + Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo + dapibus elit, id varius sem dui id lacus.

+ + +)"; + +const size_t htmlContentLength = strlen_P(htmlContent); + +const char* staticContent PROGMEM = R"( + + + + Sample HTML + + +

Hello, %IP%

+ + +)"; + AsyncWebServer server(80); AsyncEventSource events("/events"); AsyncWebSocket ws("/ws"); @@ -111,11 +185,14 @@ void notFound(AsyncWebServerRequest* request) { request->send(404, "text/plain", "Not found"); } -#if ASYNC_JSON_SUPPORT == 1 +#if __has_include("ArduinoJson.h") AsyncCallbackJsonWebHandler* jsonHandler = new AsyncCallbackJsonWebHandler("/json2"); AsyncCallbackMessagePackWebHandler* msgPackHandler = new AsyncCallbackMessagePackWebHandler("/msgpack2"); #endif +static const char characters[] = "0123456789ABCDEF"; +static size_t charactersIndex = 0; + void setup() { Serial.begin(115200); @@ -134,6 +211,32 @@ void setup() { WiFi.softAP("esp-captive"); #endif +#ifdef ESP32 + LittleFS.begin(true); +#else + LittleFS.begin(); +#endif + + { + File f = LittleFS.open("/index.txt", "w"); + if (f) { + for (size_t c = 0; c < sizeof(characters); c++) { + for (size_t i = 0; i < 1024; i++) { + f.print(characters[c]); + } + } + f.close(); + } + } + + { + File f = LittleFS.open("/index.html", "w"); + if (f) { + f.print(staticContent); + f.close(); + } + } + // curl -v -X GET http://192.168.4.1/handler-not-sending-response server.on("/handler-not-sending-response", HTTP_GET, [](AsyncWebServerRequest* request) { // handler forgot to send a response to the client => 501 Not Implemented @@ -215,16 +318,18 @@ void setup() { headerFree.keep("X-Keep-Me"); headerFree.keep("host"); - // global middleware - server.addMiddleware(&requestLogger); - server.addMiddlewares({&rateLimit, &cors, &headerFilter}); - cors.setOrigin("http://192.168.4.1"); cors.setMethods("POST, GET, OPTIONS, DELETE"); cors.setHeaders("X-Custom-Header"); cors.setAllowCredentials(false); cors.setMaxAge(600); +#ifndef PERF_TEST + // global middleware + server.addMiddleware(&requestLogger); + server.addMiddlewares({&rateLimit, &cors, &headerFilter}); +#endif + // Test CORS preflight request // curl -v -X OPTIONS -H "origin: http://192.168.4.1" http://192.168.4.1/middleware/cors server.on("/middleware/cors", HTTP_GET, [](AsyncWebServerRequest* request) { @@ -299,12 +404,103 @@ void setup() { request->redirect("/"); }); + // PERF TEST: + // > brew install autocannon + // > autocannon -c 10 -w 10 -d 20 http://192.168.4.1 server.on("/", HTTP_GET, [](AsyncWebServerRequest* request) { - request->send(200, "text/plain", "Hello, world"); + request->send(200, "text/html", htmlContent); }); - server.on("/file", HTTP_GET, [](AsyncWebServerRequest* request) { - request->send(LittleFS, "/index.html"); + // curl -v -X GET http://192.168.4.1/index.txt + server.serveStatic("/index.txt", LittleFS, "/index.txt"); + + // curl -v -X GET http://192.168.4.1/index-private.txt + server.serveStatic("/index-private.txt", LittleFS, "/index.txt").setAuthentication("admin", "admin"); + + // ServeStatic static is used to serve static output which never changes over time. + // This special endpoints automatyically adds caching headers. + // If a template processor is used, it must enure that the outputed content will always be the ame over time and never changes. + // Otherwise, do not use serveStatic. + // Example below: IP never changes. + // curl -v -X GET http://192.168.4.1/index-static.html + server.serveStatic("/index-static.html", LittleFS, "/index.html").setTemplateProcessor([](const String& var) -> String { + if (var == "IP") { + // for CI, commented out since H2 board doesn ot support WiFi + // return WiFi.localIP().toString(); + // return WiFi.softAPIP().toString(); + return "127.0.0..1"; + } + return emptyString; + }); + + // to serve a template with dynamic content (output changes over time), use normal + // Example below: content changes over tinme do not use serveStatic. + // curl -v -X GET http://192.168.4.1/index-dynamic.html + server.on("/index-dynamic.html", HTTP_GET, [](AsyncWebServerRequest* request) { + request->send(LittleFS, "/index.html", "text/html", false, [](const String& var) -> String { + if (var == "IP") + return String(random(0, 1000)); + return emptyString; + }); + }); + + // Issue #14: assert failed: tcp_update_rcv_ann_wnd (needs help to test fix) + // > curl -v http://192.168.4.1/issue-14 + pinMode(4, OUTPUT); + server.on("/issue-14", HTTP_GET, [](AsyncWebServerRequest* request) { + digitalWrite(4, HIGH); + request->send(LittleFS, "/index.txt", "text/pain"); + delay(500); + digitalWrite(4, LOW); + }); + + /* + Chunked encoding test: sends 16k of characters. + ❯ curl -N -v -X GET -H "origin: http://192.168.4.1" http://192.168.4.1/chunk + */ + server.on("/chunk", HTTP_HEAD | HTTP_GET, [](AsyncWebServerRequest* request) { + AsyncWebServerResponse* response = request->beginChunkedResponse("text/html", [](uint8_t* buffer, size_t maxLen, size_t index) -> size_t { + if (index >= 16384) + return 0; + memset(buffer, characters[charactersIndex], maxLen); + charactersIndex = (charactersIndex + 1) % sizeof(characters); + return maxLen; + }); + request->send(response); + }); + + // curl -N -v -X GET http://192.168.4.1/chunked.html --output - + // curl -N -v -X GET -H "if-none-match: 4272" http://192.168.4.1/chunked.html --output - + server.on("/chunked.html", HTTP_GET, [](AsyncWebServerRequest* request) { + String len = String(htmlContentLength); + + if (request->header(asyncsrv::T_INM) == len) { + request->send(304); + return; + } + + AsyncWebServerResponse* response = request->beginChunkedResponse("text/html", [](uint8_t* buffer, size_t maxLen, size_t index) -> size_t { + Serial.printf("%u / %u\n", index, htmlContentLength); + + // finished ? + if (htmlContentLength <= index) { + Serial.println("finished"); + return 0; + } + + // serve a maximum of 1024 or maxLen bytes of the remaining content + const int chunkSize = min((size_t)1024, min(maxLen, htmlContentLength - index)); + Serial.printf("sending: %u\n", chunkSize); + + memcpy(buffer, htmlContent + index, chunkSize); + + return chunkSize; + }); + + response->addHeader(asyncsrv::T_Cache_Control, "public,max-age=60"); + response->addHeader(asyncsrv::T_ETag, len); + + request->send(response); }); /* @@ -353,14 +549,12 @@ void setup() { request->send(200, "text/plain", "Hello, POST: " + message); }); -#if ASYNC_JSON_SUPPORT == 1 +#if __has_include("ArduinoJson.h") // JSON - // receives JSON and sends JSON - jsonHandler->onRequest([](AsyncWebServerRequest* request, JsonVariant& json) { - // JsonObject jsonObj = json.as(); - // ... - + // sends JSON + // curl -v -X GET http://192.168.4.1/json1 + server.on("/json1", HTTP_GET, [](AsyncWebServerRequest* request) { AsyncJsonResponse* response = new AsyncJsonResponse(); JsonObject root = response->getRoot().to(); root["hello"] = "world"; @@ -368,11 +562,24 @@ void setup() { request->send(response); }); - // sends JSON - server.on("/json1", HTTP_GET, [](AsyncWebServerRequest* request) { + // curl -v -X GET http://192.168.4.1/json2 + server.on("/json2", HTTP_GET, [](AsyncWebServerRequest* request) { + AsyncResponseStream* response = request->beginResponseStream("application/json"); + JsonDocument doc; + JsonObject root = doc.to(); + root["foo"] = "bar"; + serializeJson(root, *response); + request->send(response); + }); + + // curl -v -X POST -H 'Content-Type: application/json' -d '{"name":"You"}' http://192.168.4.1/json2 + // curl -v -X PUT -H 'Content-Type: application/json' -d '{"name":"You"}' http://192.168.4.1/json2 + jsonHandler->setMethod(HTTP_POST | HTTP_PUT); + jsonHandler->onRequest([](AsyncWebServerRequest* request, JsonVariant& json) { + serializeJson(json, Serial); AsyncJsonResponse* response = new AsyncJsonResponse(); JsonObject root = response->getRoot().to(); - root["hello"] = "world"; + root["hello"] = json.as()["name"]; response->setLength(); request->send(response); }); @@ -436,10 +643,69 @@ void setup() { } }); + // SSS endpoints + // sends a message every 10 ms + // + // go to http://192.168.4.1/sse + // > curl -v -N -H "Accept: text/event-stream" http://192.168.4.1/events + // + // some perf tests: + // launch 16 concurrent workers for 30 seconds + // > for i in {1..16}; do ( count=$(gtimeout 30 curl -s -N -H "Accept: text/event-stream" http://192.168.4.1/events 2>&1 | grep -c "^data:"); echo "Total: $count events, $(echo "$count / 4" | bc -l) events / second" ) & done; + // + // With AsyncTCP, with 16 workers: a lot of Too many messages queued: deleting message + // + // Total: 119 events, 29.75000000000000000000 events / second + // Total: 727 events, 181.75000000000000000000 events / second + // Total: 1386 events, 346.50000000000000000000 events / second + // Total: 1385 events, 346.25000000000000000000 events / second + // Total: 1276 events, 319.00000000000000000000 events / second + // Total: 1411 events, 352.75000000000000000000 events / second + // Total: 1276 events, 319.00000000000000000000 events / second + // Total: 1333 events, 333.25000000000000000000 events / second + // Total: 1250 events, 312.50000000000000000000 events / second + // Total: 1275 events, 318.75000000000000000000 events / second + // Total: 1271 events, 317.75000000000000000000 events / second + // Total: 1271 events, 317.75000000000000000000 events / second + // Total: 1254 events, 313.50000000000000000000 events / second + // Total: 1251 events, 312.75000000000000000000 events / second + // Total: 1254 events, 313.50000000000000000000 events / second + // Total: 1262 events, 315.50000000000000000000 events / second + // + // With AsyncTCP, with 10 workers: + // + // Total: 1875 events, 468.75000000000000000000 events / second + // Total: 1870 events, 467.50000000000000000000 events / second + // Total: 1871 events, 467.75000000000000000000 events / second + // Total: 1875 events, 468.75000000000000000000 events / second + // Total: 1871 events, 467.75000000000000000000 events / second + // Total: 1805 events, 451.25000000000000000000 events / second + // Total: 1803 events, 450.75000000000000000000 events / second + // Total: 1873 events, 468.25000000000000000000 events / second + // Total: 1872 events, 468.00000000000000000000 events / second + // Total: 1805 events, 451.25000000000000000000 events / second + // + // With AsyncTCPSock, with 16 workers: ESP32 CRASH !!! + // + // With AsyncTCPSock, with 10 workers: + // + // Total: 1242 events, 310.50000000000000000000 events / second + // Total: 1242 events, 310.50000000000000000000 events / second + // Total: 1242 events, 310.50000000000000000000 events / second + // Total: 1242 events, 310.50000000000000000000 events / second + // Total: 1181 events, 295.25000000000000000000 events / second + // Total: 1182 events, 295.50000000000000000000 events / second + // Total: 1240 events, 310.00000000000000000000 events / second + // Total: 1181 events, 295.25000000000000000000 events / second + // Total: 1181 events, 295.25000000000000000000 events / second + // Total: 1183 events, 295.75000000000000000000 events / second + // server.addHandler(&events); + + // Run: websocat ws://192.168.4.1/ws server.addHandler(&ws); -#if ASYNC_JSON_SUPPORT == 1 +#if __has_include("ArduinoJson.h") server.addHandler(jsonHandler); server.addHandler(msgPackHandler); #endif @@ -450,7 +716,7 @@ void setup() { } uint32_t lastSSE = 0; -uint32_t deltaSSE = 5; +uint32_t deltaSSE = 10; uint32_t lastWS = 0; uint32_t deltaWS = 100; @@ -463,9 +729,8 @@ void loop() { } if (now - lastWS >= deltaWS) { ws.printfAll("kp%.4f", (10.0 / 3.0)); - // ws.getClients for (auto& client : ws.getClients()) { - client.text("kp%.4f", (10.0 / 3.0)); + client.printf("kp%.4f", (10.0 / 3.0)); } lastWS = millis(); } diff --git a/examples/StreamFiles/StreamFiles.ino b/examples/StreamFiles/StreamFiles.ino index 508298d..a0d6b9f 100644 --- a/examples/StreamFiles/StreamFiles.ino +++ b/examples/StreamFiles/StreamFiles.ino @@ -10,11 +10,13 @@ #include #include #endif -#include "StreamConcat.h" -#include "StreamString.h" + +#include #include #include +#include "StreamConcat.h" + DNSServer dnsServer; AsyncWebServer server(80); diff --git a/examples/StreamFiles/StreamString.h b/examples/StreamFiles/StreamString.h deleted file mode 100644 index a6e0655..0000000 --- a/examples/StreamFiles/StreamString.h +++ /dev/null @@ -1,40 +0,0 @@ -#pragma once - -#include - -class StreamString : public Stream { - public: - size_t write(const uint8_t* p, size_t n) override { return _buffer.concat(reinterpret_cast(p), n) ? n : 0; } - size_t write(uint8_t c) override { return _buffer.concat(static_cast(c)) ? 1 : 0; } - void flush() override {} - - int available() override { return static_cast(_buffer.length()); } - - int read() override { - if (_buffer.length() == 0) - return -1; - char c = _buffer[0]; - _buffer.remove(0, 1); - 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 - memcpy(buffer, _buffer.c_str(), length); - _buffer.remove(0, static_cast(length)); - return length; - } - - int peek() override { return _buffer.length() > 0 ? _buffer[0] : -1; } - - const String& buffer() const { return _buffer; } - - private: - String _buffer; -}; diff --git a/examples/issues/Issue14/Issue14.ino b/examples/issues/Issue14/Issue14.ino deleted file mode 100644 index f62084a..0000000 --- a/examples/issues/Issue14/Issue14.ino +++ /dev/null @@ -1,107 +0,0 @@ -#include -#ifdef ESP32 - #include - #include -#elif defined(ESP8266) - #include - #include -#elif defined(TARGET_RP2040) - #include - #include -#endif - -#include "ESPAsyncWebServer.h" - -const char appWebPage[] PROGMEM = R"rawliteral( - - - - -)rawliteral"; - -AsyncWebServer server(80); -AsyncEventSource events("/events"); - -const uint32_t interval = 1000; -const int button1Pin = 4; - -uint32_t lastSend = 0; - -void prepareJson(String& buffer) { - buffer.reserve(512); - buffer.concat("{\"button1\":"); - buffer.concat(digitalRead(button1Pin) == LOW); - buffer.concat(",\"1234567890abcdefghij1234567890abcdefghij1234567890abcdefghij1234567890abcdefghij1234567890abcdefghij1234567890abcdefghij\":"); - buffer.concat(random(0, 999999999)); - buffer.concat("}"); -} - -void setup() { - Serial.begin(115200); -#if ARDUINO_USB_CDC_ON_BOOT - Serial.setTxTimeoutMs(0); - delay(100); -#else - while (!Serial) - yield(); -#endif - - randomSeed(micros()); - - pinMode(button1Pin, OUTPUT); - digitalWrite(button1Pin, HIGH); - - WiFi.softAP("esp-captive"); - - server.on("/", HTTP_GET, [](AsyncWebServerRequest* request) { - request->send(200, "text/html", appWebPage); - }); - - server.on("/button1", HTTP_GET, [](AsyncWebServerRequest* request) { - request->send(200, "text/plain", "OK"); - digitalWrite(button1Pin, digitalRead(button1Pin) == LOW ? HIGH : LOW); - - String buffer; - prepareJson(buffer); - ESP_LOGI("async_tcp", "Sending from handler..."); - events.send(buffer.c_str(), "state", millis()); - ESP_LOGI("async_tcp", "Sent from handler!"); - }); - - events.onConnect([](AsyncEventSourceClient* client) { - String buffer; - prepareJson(buffer); - ESP_LOGI("async_tcp", "Sending from onConnect..."); - client->send(buffer.c_str(), "state", millis(), 5000); - ESP_LOGI("async_tcp", "Sent from onConnect!"); - }); - - server.addHandler(&events); - DefaultHeaders::Instance().addHeader("Access-Control-Allow-Origin", "*"); - - server.begin(); -} - -void loop() { - if (millis() - lastSend >= interval) { - String buffer; - prepareJson(buffer); - ESP_LOGI("loop", "Sending..."); - events.send(buffer.c_str(), "state", millis()); - ESP_LOGI("loop", "Sent!"); - lastSend = millis(); - } -} diff --git a/library.json_ b/library.json_ index d228842..f7e219c 100644 --- a/library.json_ +++ b/library.json_ @@ -1,6 +1,6 @@ { "name": "ESPAsyncWebServer", - "version": "3.3.7", + "version": "3.3.23", "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", @@ -28,7 +28,7 @@ { "owner": "mathieucarbou", "name": "AsyncTCP", - "version": "^3.2.5", + "version": "^3.2.14", "platforms": "espressif32" }, { diff --git a/library.properties b/library.properties index bb170b9..865e911 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,6 @@ -name=ESPAsyncWebServer -version=3.3.7 +name=ESP Async WebServer +includes=ESPAsyncWebServer.h +version=3.3.23 author=Me-No-Dev maintainer=Mathieu Carbou sentence=Asynchronous HTTP and WebSocket Server Library for ESP32, ESP8266 and RP2040 @@ -7,4 +8,4 @@ paragraph=Supports: WebSocket, SSE, Authentication, Arduino Json 7, File Upload, category=Other url=https://github.com/mathieucarbou/ESPAsyncWebServer architectures=* -license=LGPL-3.0 +license=LGPL-3.0 \ No newline at end of file diff --git a/partitions-4MB.csv b/partitions-4MB.csv new file mode 100644 index 0000000..75efc35 --- /dev/null +++ b/partitions-4MB.csv @@ -0,0 +1,7 @@ +# Name ,Type ,SubType ,Offset ,Size ,Flags +nvs ,data ,nvs ,36K ,20K , +otadata ,data ,ota ,56K ,8K , +app0 ,app ,ota_0 ,64K ,1856K , +app1 ,app ,ota_1 ,1920K ,1856K , +spiffs ,data ,spiffs ,3776K ,256K , +coredump ,data ,coredump ,4032K ,64K , diff --git a/platformio.ini b/platformio.ini index b0a28f1..328f427 100644 --- a/platformio.ini +++ b/platformio.ini @@ -1,16 +1,17 @@ [platformio] -default_envs = arduino-2, arduino-3, arduino-310rc1, esp8266, raspberrypi +default_envs = arduino-2, arduino-3, arduino-310, esp8266, raspberrypi lib_dir = . ; src_dir = examples/CaptivePortal -src_dir = examples/SimpleServer +; src_dir = examples/SimpleServer ; src_dir = examples/StreamFiles ; src_dir = examples/Filters -; src_dir = examples/Draft -; src_dir = examples/issues/Issue14 +; src_dir = examples/Issue85 +src_dir = examples/Issue162 [env] framework = arduino build_flags = + -Og -Wall -Wextra -Wno-unused-parameter -D CONFIG_ARDUHAL_LOG_COLORS @@ -23,12 +24,17 @@ build_flags = upload_protocol = esptool monitor_speed = 115200 monitor_filters = esp32_exception_decoder, log2file +; monitor_filters = esp8266_exception_decoder, log2file +lib_compat_mode = strict +lib_ldf_mode = chain lib_deps = ; bblanchon/ArduinoJson @ 5.13.4 ; bblanchon/ArduinoJson @ 6.21.5 - bblanchon/ArduinoJson @ 7.2.0 - mathieucarbou/AsyncTCP @ 3.2.5 + bblanchon/ArduinoJson @ 7.2.1 + mathieucarbou/AsyncTCP @ 3.2.14 board = esp32dev +board_build.partitions = partitions-4MB.csv +board_build.filesystem = littlefs [env:arduino-2] platform = espressif32@6.9.0 @@ -43,33 +49,42 @@ platform = https://github.com/pioarduino/platform-espressif32/releases/download/ ; board = esp32-s3-devkitc-1 ; board = esp32-c6-devkitc-1 lib_deps = - mathieucarbou/AsyncTCP @ 3.2.5 + mathieucarbou/AsyncTCP @ 3.2.14 -[env:arduino-310rc1] -platform = https://github.com/pioarduino/platform-espressif32/releases/download/53.03.10-rc1/platform-espressif32.zip +[env:arduino-310] +platform = https://github.com/pioarduino/platform-espressif32/releases/download/53.03.10-rc3/platform-espressif32.zip ; board = esp32-s3-devkitc-1 ; board = esp32-c6-devkitc-1 +; board = esp32-h2-devkitm-1 + +[env:perf-test-AsyncTCP] +platform = https://github.com/pioarduino/platform-espressif32/releases/download/53.03.10-rc3/platform-espressif32.zip +build_flags = ${env.build_flags} + -D PERF_TEST=1 + +[env:perf-test-AsyncTCPSock] +platform = https://github.com/pioarduino/platform-espressif32/releases/download/53.03.10-rc3/platform-espressif32.zip +lib_deps = + https://github.com/mathieucarbou/AsyncTCPSock/archive/refs/tags/v1.0.3-dev.zip +build_flags = ${env.build_flags} + -D PERF_TEST=1 [env:esp8266] platform = espressif8266 -board = huzzah -; board = d1_mini +; board = huzzah +board = d1_mini lib_deps = - bblanchon/ArduinoJson @ 7.2.0 + bblanchon/ArduinoJson @ 7.2.1 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:raspberrypi] -upload_protocol = picotool -; platform = raspberrypi -platform = https://github.com/maxgerhardt/platform-raspberrypi.git#f2687073f73d554c9db41f29b4769fd9703f4e55 +platform = https://github.com/maxgerhardt/platform-raspberrypi.git board = rpipicow lib_deps = - bblanchon/ArduinoJson @ 7.2.0 + bblanchon/ArduinoJson @ 7.2.1 khoih-prog/AsyncTCP_RP2040W @ 1.2.0 +lib_ignore = + lwIP_ESPHost build_flags = ${env.build_flags} -Wno-missing-field-initializers @@ -87,25 +102,26 @@ board = ${sysenv.PIO_BOARD} platform = https://github.com/pioarduino/platform-espressif32/releases/download/51.03.05/platform-espressif32.zip board = ${sysenv.PIO_BOARD} lib_deps = - mathieucarbou/AsyncTCP @ 3.2.5 + mathieucarbou/AsyncTCP @ 3.2.14 -[env:ci-arduino-310rc1] -platform = https://github.com/pioarduino/platform-espressif32/releases/download/53.03.10-rc1/platform-espressif32.zip +[env:ci-arduino-310] +platform = https://github.com/pioarduino/platform-espressif32/releases/download/53.03.10-rc3/platform-espressif32.zip board = ${sysenv.PIO_BOARD} [env:ci-esp8266] platform = espressif8266 board = ${sysenv.PIO_BOARD} lib_deps = - bblanchon/ArduinoJson @ 7.2.0 + bblanchon/ArduinoJson @ 7.2.1 esphome/ESPAsyncTCP-esphome @ 2.0.0 [env:ci-raspberrypi] -; platform = raspberrypi -platform = https://github.com/maxgerhardt/platform-raspberrypi.git#f2687073f73d554c9db41f29b4769fd9703f4e55 +platform = https://github.com/maxgerhardt/platform-raspberrypi.git board = ${sysenv.PIO_BOARD} lib_deps = - bblanchon/ArduinoJson @ 7.2.0 + bblanchon/ArduinoJson @ 7.2.1 khoih-prog/AsyncTCP_RP2040W @ 1.2.0 +lib_ignore = + lwIP_ESPHost build_flags = ${env.build_flags} -Wno-missing-field-initializers diff --git a/src/AsyncEventSource.cpp b/src/AsyncEventSource.cpp index 2d6ab7b..1c0ff2a 100644 --- a/src/AsyncEventSource.cpp +++ b/src/AsyncEventSource.cpp @@ -141,6 +141,9 @@ size_t AsyncEventSourceMessage::ack(size_t len, __attribute__((unused)) uint32_t } size_t AsyncEventSourceMessage::write(AsyncClient* client) { + if (!client) + return 0; + if (_sent >= _len || !client->canSend()) { return 0; } @@ -186,7 +189,10 @@ AsyncEventSourceClient::~AsyncEventSourceClient() { close(); } -void AsyncEventSourceClient::_queueMessage(const char* message, size_t len) { +bool AsyncEventSourceClient::_queueMessage(const char* message, size_t len) { + if (!_client) + return false; + #ifdef ESP32 // length() is not thread-safe, thus acquiring the lock before this call.. std::lock_guard lock(_lockmq); @@ -198,7 +204,7 @@ void AsyncEventSourceClient::_queueMessage(const char* message, size_t len) { #elif defined(ESP32) log_e("Too many messages queued: deleting message"); #endif - return; + return false; } _messageQueue.emplace_back(message, len); @@ -206,6 +212,8 @@ void AsyncEventSourceClient::_queueMessage(const char* message, size_t len) { if (_client->canSend()) { _runQueue(); } + + return true; } void AsyncEventSourceClient::_onAck(size_t len __attribute__((unused)), uint32_t time __attribute__((unused))) { @@ -227,30 +235,31 @@ void AsyncEventSourceClient::_onPoll() { } void AsyncEventSourceClient::_onTimeout(uint32_t time __attribute__((unused))) { - _client->close(true); + if (_client) + _client->close(true); } void AsyncEventSourceClient::_onDisconnect() { - _client = NULL; + if (!_client) + return; + _client = nullptr; _server->_handleDisconnect(this); } void AsyncEventSourceClient::close() { - if (_client != NULL) + if (_client) _client->close(); } -void AsyncEventSourceClient::write(const char* message, size_t len) { - if (!connected()) - return; - _queueMessage(message, len); +bool AsyncEventSourceClient::write(const char* message, size_t len) { + return connected() && _queueMessage(message, len); } -void AsyncEventSourceClient::send(const char* message, const char* event, uint32_t id, uint32_t reconnect) { +bool AsyncEventSourceClient::send(const char* message, const char* event, uint32_t id, uint32_t reconnect) { if (!connected()) - return; + return false; String ev = generateEventMessage(message, event, id, reconnect); - _queueMessage(ev.c_str(), ev.length()); + return _queueMessage(ev.c_str(), ev.length()); } size_t AsyncEventSourceClient::packetsWaiting() const { @@ -261,6 +270,9 @@ size_t AsyncEventSourceClient::packetsWaiting() const { } void AsyncEventSourceClient::_runQueue() { + if (!_client) + return; + size_t total_bytes_written = 0; for (auto i = _messageQueue.begin(); i != _messageQueue.end(); ++i) { if (!i->sent()) { @@ -270,6 +282,7 @@ void AsyncEventSourceClient::_runQueue() { break; } } + if (total_bytes_written > 0) _client->send(); @@ -282,11 +295,6 @@ void AsyncEventSourceClient::_runQueue() { } } -// Handler -void AsyncEventSource::onConnect(ArEventHandlerFunction cb) { - _connectcb = cb; -} - void AsyncEventSource::authorizeConnect(ArAuthorizeConnectHandler cb) { AuthorizationMiddleware* m = new AuthorizationMiddleware(401, cb); m->_freeOnRemoval = true; @@ -308,6 +316,8 @@ void AsyncEventSource::_handleDisconnect(AsyncEventSourceClient* client) { #ifdef ESP32 std::lock_guard lock(_client_queue_lock); #endif + if (_disconnectcb) + _disconnectcb(client); for (auto i = _clients.begin(); i != _clients.end(); ++i) { if (i->get() == client) _clients.erase(i); @@ -346,17 +356,21 @@ size_t AsyncEventSource::avgPacketsWaiting() const { return ((aql) + (nConnectedClients / 2)) / (nConnectedClients); // round up } -void AsyncEventSource::send( +AsyncEventSource::SendStatus AsyncEventSource::send( const char* message, const char* event, uint32_t id, uint32_t reconnect) { String ev = generateEventMessage(message, event, id, reconnect); #ifdef ESP32 std::lock_guard lock(_client_queue_lock); #endif + size_t hits = 0; + size_t miss = 0; for (const auto& c : _clients) { - if (c->connected()) { - c->write(ev.c_str(), ev.length()); - } + if (c->write(ev.c_str(), ev.length())) + ++hits; + else + ++miss; } + return hits == 0 ? DISCARDED : (miss == 0 ? ENQUEUED : PARTIALLY_ENQUEUED); } size_t AsyncEventSource::count() const { @@ -371,11 +385,8 @@ size_t AsyncEventSource::count() const { return n_clients; } -bool AsyncEventSource::canHandle(AsyncWebServerRequest* request) { - if (request->method() != HTTP_GET || !request->url().equals(_url)) { - return false; - } - return true; +bool AsyncEventSource::canHandle(AsyncWebServerRequest* request) const { + return request->isSSE() && request->url().equals(_url); } void AsyncEventSource::handleRequest(AsyncWebServerRequest* request) { diff --git a/src/AsyncEventSource.h b/src/AsyncEventSource.h index 9fc4b7a..10d7901 100644 --- a/src/AsyncEventSource.h +++ b/src/AsyncEventSource.h @@ -81,7 +81,7 @@ class AsyncEventSourceClient { #ifdef ESP32 mutable std::mutex _lockmq; #endif - void _queueMessage(const char* message, size_t len); + bool _queueMessage(const char* message, size_t len); void _runQueue(); public: @@ -90,11 +90,11 @@ class AsyncEventSourceClient { AsyncClient* client() { return _client; } void close(); - void write(const char* message, size_t len); - void send(const String& message, const String& event, uint32_t id = 0, uint32_t reconnect = 0) { send(message.c_str(), event.c_str(), id, reconnect); } - void send(const String& message, const char* event, uint32_t id = 0, uint32_t reconnect = 0) { send(message.c_str(), event, id, reconnect); } - void send(const char* message, const char* event = NULL, uint32_t id = 0, uint32_t reconnect = 0); - bool connected() const { return (_client != NULL) && _client->connected(); } + bool write(const char* message, size_t len); + bool send(const String& message, const String& event, uint32_t id = 0, uint32_t reconnect = 0) { return send(message.c_str(), event.c_str(), id, reconnect); } + bool send(const String& message, const char* event, uint32_t id = 0, uint32_t reconnect = 0) { return send(message.c_str(), event, id, reconnect); } + bool send(const char* message, const char* event = NULL, uint32_t id = 0, uint32_t reconnect = 0); + bool connected() const { return _client && _client->connected(); } uint32_t lastId() const { return _lastId; } size_t packetsWaiting() const; @@ -114,19 +114,28 @@ class AsyncEventSource : public AsyncWebHandler { // since simultaneous access from different tasks is possible mutable std::mutex _client_queue_lock; #endif - ArEventHandlerFunction _connectcb{nullptr}; + ArEventHandlerFunction _connectcb = nullptr; + ArEventHandlerFunction _disconnectcb = nullptr; public: + typedef enum { + DISCARDED = 0, + ENQUEUED = 1, + PARTIALLY_ENQUEUED = 2, + } SendStatus; + AsyncEventSource(const String& url) : _url(url) {}; ~AsyncEventSource() { close(); }; const char* url() const { return _url.c_str(); } void close(); - void onConnect(ArEventHandlerFunction cb); + void onConnect(ArEventHandlerFunction cb) { _connectcb = cb; } + // The client pointer sent to the callback is only for reference purposes. DO NOT CALL ANY METHOD ON IT ! + void onDisconnect(ArEventHandlerFunction cb) { _disconnectcb = cb; } void authorizeConnect(ArAuthorizeConnectHandler cb); - void send(const String& message, const String& event, uint32_t id = 0, uint32_t reconnect = 0) { send(message.c_str(), event.c_str(), id, reconnect); } - void send(const String& message, const char* event, uint32_t id = 0, uint32_t reconnect = 0) { send(message.c_str(), event, id, reconnect); } - void send(const char* message, const char* event = NULL, uint32_t id = 0, uint32_t reconnect = 0); + SendStatus send(const String& message, const String& event, uint32_t id = 0, uint32_t reconnect = 0) { return send(message.c_str(), event.c_str(), id, reconnect); } + SendStatus send(const String& message, const char* event, uint32_t id = 0, uint32_t reconnect = 0) { return send(message.c_str(), event, id, reconnect); } + SendStatus 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; @@ -134,8 +143,8 @@ class AsyncEventSource : public AsyncWebHandler { // 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; + bool canHandle(AsyncWebServerRequest* request) const override final; + void handleRequest(AsyncWebServerRequest* request) override final; }; class AsyncEventSourceResponse : public AsyncWebServerResponse { diff --git a/src/AsyncJson.cpp b/src/AsyncJson.cpp index 7117a70..4295653 100644 --- a/src/AsyncJson.cpp +++ b/src/AsyncJson.cpp @@ -89,18 +89,14 @@ AsyncCallbackJsonWebHandler::AsyncCallbackJsonWebHandler(const String& uri, ArJs : _uri(uri), _method(HTTP_GET | HTTP_POST | HTTP_PUT | HTTP_PATCH), _onRequest(onRequest), _maxContentLength(16384) {} #endif -bool AsyncCallbackJsonWebHandler::canHandle(AsyncWebServerRequest* request) { - if (!_onRequest) - return false; - - WebRequestMethodComposite request_method = request->method(); - if (!(_method & request_method)) +bool AsyncCallbackJsonWebHandler::canHandle(AsyncWebServerRequest* request) const { + if (!_onRequest || !request->isHTTP() || !(_method & request->method())) return false; if (_uri.length() && (_uri != request->url() && !request->url().startsWith(_uri + "/"))) return false; - if (request_method != HTTP_GET && !request->contentType().equalsIgnoreCase(asyncsrv::T_application_json)) + if (request->method() != HTTP_GET && !request->contentType().equalsIgnoreCase(asyncsrv::T_application_json)) return false; return true; diff --git a/src/AsyncJson.h b/src/AsyncJson.h index 167364a..a54935a 100644 --- a/src/AsyncJson.h +++ b/src/AsyncJson.h @@ -119,11 +119,11 @@ class AsyncCallbackJsonWebHandler : public AsyncWebHandler { void setMaxContentLength(int maxContentLength) { _maxContentLength = maxContentLength; } void onRequest(ArJsonRequestHandlerFunction fn) { _onRequest = fn; } - virtual bool canHandle(AsyncWebServerRequest* request) override final; - virtual void handleRequest(AsyncWebServerRequest* request) override final; - virtual void handleUpload(__unused AsyncWebServerRequest* request, __unused const String& filename, __unused size_t index, __unused uint8_t* data, __unused size_t len, __unused bool final) override final {} - virtual void handleBody(AsyncWebServerRequest* request, uint8_t* data, size_t len, size_t index, size_t total) override final; - virtual bool isRequestHandlerTrivial() override final { return !_onRequest; } + bool canHandle(AsyncWebServerRequest* request) const override final; + void handleRequest(AsyncWebServerRequest* request) override final; + void handleUpload(__unused AsyncWebServerRequest* request, __unused const String& filename, __unused size_t index, __unused uint8_t* data, __unused size_t len, __unused bool final) override final {} + void handleBody(AsyncWebServerRequest* request, uint8_t* data, size_t len, size_t index, size_t total) override final; + bool isRequestHandlerTrivial() const override final { return !_onRequest; } }; #endif // ASYNC_JSON_SUPPORT == 1 diff --git a/src/AsyncMessagePack.cpp b/src/AsyncMessagePack.cpp index 076f558..85af671 100644 --- a/src/AsyncMessagePack.cpp +++ b/src/AsyncMessagePack.cpp @@ -44,18 +44,14 @@ AsyncCallbackMessagePackWebHandler::AsyncCallbackMessagePackWebHandler(const Str : _uri(uri), _method(HTTP_GET | HTTP_POST | HTTP_PUT | HTTP_PATCH), _onRequest(onRequest), _maxContentLength(16384) {} #endif -bool AsyncCallbackMessagePackWebHandler::canHandle(AsyncWebServerRequest* request) { - if (!_onRequest) - return false; - - WebRequestMethodComposite request_method = request->method(); - if (!(_method & request_method)) +bool AsyncCallbackMessagePackWebHandler::canHandle(AsyncWebServerRequest* request) const { + if (!_onRequest || !request->isHTTP() || !(_method & request->method())) return false; if (_uri.length() && (_uri != request->url() && !request->url().startsWith(_uri + "/"))) return false; - if (request_method != HTTP_GET && !request->contentType().equalsIgnoreCase(asyncsrv::T_application_msgpack)) + if (request->method() != HTTP_GET && !request->contentType().equalsIgnoreCase(asyncsrv::T_application_msgpack)) return false; return true; diff --git a/src/AsyncMessagePack.h b/src/AsyncMessagePack.h index 9ac5ee5..78fde92 100644 --- a/src/AsyncMessagePack.h +++ b/src/AsyncMessagePack.h @@ -92,11 +92,11 @@ class AsyncCallbackMessagePackWebHandler : public AsyncWebHandler { void setMaxContentLength(int maxContentLength) { _maxContentLength = maxContentLength; } void onRequest(ArMessagePackRequestHandlerFunction fn) { _onRequest = fn; } - virtual bool canHandle(AsyncWebServerRequest* request) override final; - virtual void handleRequest(AsyncWebServerRequest* request) override final; - virtual void handleUpload(__unused AsyncWebServerRequest* request, __unused const String& filename, __unused size_t index, __unused uint8_t* data, __unused size_t len, __unused bool final) override final {} - virtual void handleBody(AsyncWebServerRequest* request, uint8_t* data, size_t len, size_t index, size_t total) override final; - virtual bool isRequestHandlerTrivial() override final { return !_onRequest; } + bool canHandle(AsyncWebServerRequest* request) const override final; + void handleRequest(AsyncWebServerRequest* request) override final; + void handleUpload(__unused AsyncWebServerRequest* request, __unused const String& filename, __unused size_t index, __unused uint8_t* data, __unused size_t len, __unused bool final) override final {} + void handleBody(AsyncWebServerRequest* request, uint8_t* data, size_t len, size_t index, size_t total) override final; + bool isRequestHandlerTrivial() const override final { return !_onRequest; } }; #endif // ASYNC_MSG_PACK_SUPPORT == 1 diff --git a/src/AsyncWebSocket.cpp b/src/AsyncWebSocket.cpp index b040a9f..b89c0d5 100644 --- a/src/AsyncWebSocket.cpp +++ b/src/AsyncWebSocket.cpp @@ -39,7 +39,7 @@ using namespace asyncsrv; size_t webSocketSendFrameWindow(AsyncClient* client) { - if (!client->canSend()) + if (!client || !client->canSend()) return 0; size_t space = client->space(); if (space < 9) @@ -48,7 +48,7 @@ size_t webSocketSendFrameWindow(AsyncClient* client) { } size_t webSocketSendFrame(AsyncClient* client, bool final, uint8_t opcode, bool mask, uint8_t* data, size_t len) { - if (!client->canSend()) { + if (!client || !client->canSend()) { // Serial.println("SF 1"); return 0; } @@ -185,12 +185,12 @@ class AsyncWebSocketControl { _data = NULL; } - virtual ~AsyncWebSocketControl() { + ~AsyncWebSocketControl() { if (_data != NULL) free(_data); } - virtual bool finished() const { return _finished; } + bool finished() const { return _finished; } uint8_t opcode() { return _opcode; } uint8_t len() { return _len + 2; } size_t send(AsyncClient* client) { @@ -219,6 +219,9 @@ void AsyncWebSocketMessage::ack(size_t len, uint32_t time) { } size_t AsyncWebSocketMessage::send(AsyncClient* client) { + if (!client) + return 0; + if (_status != WS_MSG_SENDING) return 0; if (_acked < _ack) { @@ -343,7 +346,7 @@ void AsyncWebSocketClient::_onPoll() { #ifdef ESP32 std::unique_lock lock(_lock); #endif - if (_client->canSend() && (!_controlQueue.empty() || !_messageQueue.empty())) { + if (_client && _client->canSend() && (!_controlQueue.empty() || !_messageQueue.empty())) { _runQueue(); } else if (_keepAlivePeriod > 0 && (millis() - _lastMessageTime) >= _keepAlivePeriod && (_controlQueue.empty() && _messageQueue.empty())) { #ifdef ESP32 @@ -371,16 +374,13 @@ 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); + return (_messageQueue.size() >= WS_MAX_QUEUED_MESSAGES) || (_status != WS_CONNECTED); } size_t AsyncWebSocketClient::queueLen() const { #ifdef ESP32 std::lock_guard lock(_lock); #endif - return _messageQueue.size(); } @@ -391,38 +391,43 @@ bool AsyncWebSocketClient::canSend() const { return _messageQueue.size() < WS_MAX_QUEUED_MESSAGES; } -void AsyncWebSocketClient::_queueControl(uint8_t opcode, const uint8_t* data, size_t len, bool mask) { +bool 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::_queueMessage(AsyncWebSocketSharedBuffer buffer, uint8_t opcode, bool mask) { - if (!_client || buffer->size() == 0 || _status != WS_CONNECTED) - return; + return false; #ifdef ESP32 std::lock_guard lock(_lock); #endif + + _controlQueue.emplace_back(opcode, data, len, mask); + + if (_client && _client->canSend()) + _runQueue(); + + return true; +} + +bool AsyncWebSocketClient::_queueMessage(AsyncWebSocketSharedBuffer buffer, uint8_t opcode, bool mask) { + if (!_client || buffer->size() == 0 || _status != WS_CONNECTED) + return false; + +#ifdef ESP32 + std::lock_guard lock(_lock); +#endif + if (_messageQueue.size() >= WS_MAX_QUEUED_MESSAGES) { if (closeWhenFull) { + _status = WS_DISCONNECTED; + + if (_client) + _client->close(true); + #ifdef ESP8266 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"); @@ -430,13 +435,16 @@ void AsyncWebSocketClient::_queueMessage(AsyncWebSocketSharedBuffer buffer, uint log_e("Too many messages queued: discarding new message"); #endif } - return; - } else { - _messageQueue.emplace_back(buffer, opcode, mask); + + return false; } + _messageQueue.emplace_back(buffer, opcode, mask); + if (_client && _client->canSend()) _runQueue(); + + return true; } void AsyncWebSocketClient::close(uint16_t code, const char* message) { @@ -466,9 +474,8 @@ void AsyncWebSocketClient::close(uint16_t code, const char* message) { _queueControl(WS_DISCONNECT); } -void AsyncWebSocketClient::ping(const uint8_t* data, size_t len) { - if (_status == WS_CONNECTED) - _queueControl(WS_PING, data, len); +bool AsyncWebSocketClient::ping(const uint8_t* data, size_t len) { + return _status == WS_CONNECTED && _queueControl(WS_PING, data, len); } void AsyncWebSocketClient::_onError(int8_t) { @@ -476,6 +483,8 @@ void AsyncWebSocketClient::_onError(int8_t) { } void AsyncWebSocketClient::_onTimeout(uint32_t time) { + if (!_client) + return; // Serial.println("onTime"); (void)time; _client->close(true); @@ -483,7 +492,7 @@ void AsyncWebSocketClient::_onTimeout(uint32_t time) { void AsyncWebSocketClient::_onDisconnect() { // Serial.println("onDis"); - _client = NULL; + _client = nullptr; } void AsyncWebSocketClient::_onData(void* pbuf, size_t plen) { @@ -535,7 +544,7 @@ void AsyncWebSocketClient::_onData(void* pbuf, size_t plen) { } } if (datalen > 0) - _server->_handleEvent(this, WS_EVT_DATA, (void*)&_pinfo, (uint8_t*)data, datalen); + _server->_handleEvent(this, WS_EVT_DATA, (void*)&_pinfo, data, datalen); _pinfo.index += datalen; } else if ((datalen + _pinfo.index) == _pinfo.len) { @@ -550,18 +559,21 @@ void AsyncWebSocketClient::_onData(void* pbuf, size_t plen) { } if (_status == WS_DISCONNECTING) { _status = WS_DISCONNECTED; - _client->close(true); + if (_client) + _client->close(true); } else { _status = WS_DISCONNECTING; - _client->ackLater(); + if (_client) + _client->ackLater(); _queueControl(WS_DISCONNECT, data, datalen); } } else if (_pinfo.opcode == WS_PING) { + _server->_handleEvent(this, WS_EVT_PING, NULL, NULL, 0); _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) - _server->_handleEvent(this, WS_EVT_PONG, NULL, data, datalen); - } else if (_pinfo.opcode < 8) { // continuation or text/binary frame + _server->_handleEvent(this, WS_EVT_PONG, NULL, NULL, 0); + } else if (_pinfo.opcode < WS_DISCONNECT) { // continuation or text/binary frame _server->_handleEvent(this, WS_EVT_DATA, (void*)&_pinfo, data, datalen); if (_pinfo.final) _pinfo.num = 0; @@ -575,7 +587,7 @@ void AsyncWebSocketClient::_onData(void* pbuf, size_t plen) { } // restore byte as _handleEvent may have added a null terminator i.e., data[len] = 0; - if (datalen > 0) + if (datalen) data[datalen] = datalast; data += datalen; @@ -601,9 +613,9 @@ size_t AsyncWebSocketClient::printf(const char* format, ...) { len = vsnprintf(buffer, len + 1, format, arg); va_end(arg); - text(buffer, len); + bool enqueued = text(buffer, len); delete[] buffer; - return len; + return enqueued ? len : 0; } #ifdef ESP8266 @@ -625,9 +637,9 @@ size_t AsyncWebSocketClient::printf_P(PGM_P formatP, ...) { len = vsnprintf_P(buffer, len + 1, formatP, arg); va_end(arg); - text(buffer, len); + bool enqueued = text(buffer, len); delete[] buffer; - return len; + return enqueued ? len : 0; } #endif @@ -639,35 +651,37 @@ namespace { } } -void AsyncWebSocketClient::text(AsyncWebSocketMessageBuffer* buffer) { +bool AsyncWebSocketClient::text(AsyncWebSocketMessageBuffer* buffer) { + bool enqueued = false; if (buffer) { - text(std::move(buffer->_buffer)); + enqueued = text(std::move(buffer->_buffer)); delete buffer; } + return enqueued; } -void AsyncWebSocketClient::text(AsyncWebSocketSharedBuffer buffer) { - _queueMessage(buffer); +bool AsyncWebSocketClient::text(AsyncWebSocketSharedBuffer buffer) { + return _queueMessage(buffer); } -void AsyncWebSocketClient::text(const uint8_t* message, size_t len) { - text(makeSharedBuffer(message, len)); +bool AsyncWebSocketClient::text(const uint8_t* message, size_t len) { + return text(makeSharedBuffer(message, len)); } -void AsyncWebSocketClient::text(const char* message, size_t len) { - text((const uint8_t*)message, len); +bool AsyncWebSocketClient::text(const char* message, size_t len) { + return text((const uint8_t*)message, len); } -void AsyncWebSocketClient::text(const char* message) { - text(message, strlen(message)); +bool AsyncWebSocketClient::text(const char* message) { + return text(message, strlen(message)); } -void AsyncWebSocketClient::text(const String& message) { - text(message.c_str(), message.length()); +bool AsyncWebSocketClient::text(const String& message) { + return text(message.c_str(), message.length()); } #ifdef ESP8266 -void AsyncWebSocketClient::text(const __FlashStringHelper* data) { +bool AsyncWebSocketClient::text(const __FlashStringHelper* data) { PGM_P p = reinterpret_cast(data); size_t n = 0; @@ -678,51 +692,57 @@ void AsyncWebSocketClient::text(const __FlashStringHelper* data) { } char* message = (char*)malloc(n + 1); + bool enqueued = false; if (message) { memcpy_P(message, p, n); message[n] = 0; - text(message, n); + enqueued = text(message, n); free(message); } + return enqueued; } #endif // ESP8266 -void AsyncWebSocketClient::binary(AsyncWebSocketMessageBuffer* buffer) { +bool AsyncWebSocketClient::binary(AsyncWebSocketMessageBuffer* buffer) { + bool enqueued = false; if (buffer) { - binary(std::move(buffer->_buffer)); + enqueued = binary(std::move(buffer->_buffer)); delete buffer; } + return enqueued; } -void AsyncWebSocketClient::binary(AsyncWebSocketSharedBuffer buffer) { - _queueMessage(buffer, WS_BINARY); +bool AsyncWebSocketClient::binary(AsyncWebSocketSharedBuffer buffer) { + return _queueMessage(buffer, WS_BINARY); } -void AsyncWebSocketClient::binary(const uint8_t* message, size_t len) { - binary(makeSharedBuffer(message, len)); +bool AsyncWebSocketClient::binary(const uint8_t* message, size_t len) { + return binary(makeSharedBuffer(message, len)); } -void AsyncWebSocketClient::binary(const char* message, size_t len) { - binary((const uint8_t*)message, len); +bool AsyncWebSocketClient::binary(const char* message, size_t len) { + return binary((const uint8_t*)message, len); } -void AsyncWebSocketClient::binary(const char* message) { - binary(message, strlen(message)); +bool AsyncWebSocketClient::binary(const char* message) { + return binary(message, strlen(message)); } -void AsyncWebSocketClient::binary(const String& message) { - binary(message.c_str(), message.length()); +bool AsyncWebSocketClient::binary(const String& message) { + return binary(message.c_str(), message.length()); } #ifdef ESP8266 -void AsyncWebSocketClient::binary(const __FlashStringHelper* data, size_t len) { +bool AsyncWebSocketClient::binary(const __FlashStringHelper* data, size_t len) { PGM_P p = reinterpret_cast(data); char* message = (char*)malloc(len); + bool enqueued = false; if (message) { memcpy_P(message, p, len); - binary(message, len); + enqueued = binary(message, len); free(message); } + return enqueued; } #endif @@ -801,33 +821,38 @@ void AsyncWebSocket::cleanupClients(uint16_t maxClients) { } } -void AsyncWebSocket::ping(uint32_t id, const uint8_t* data, size_t len) { - if (AsyncWebSocketClient* c = client(id)) - c->ping(data, len); +bool AsyncWebSocket::ping(uint32_t id, const uint8_t* data, size_t len) { + AsyncWebSocketClient* c = client(id); + return c && c->ping(data, len); } -void AsyncWebSocket::pingAll(const uint8_t* data, size_t len) { +AsyncWebSocket::SendStatus AsyncWebSocket::pingAll(const uint8_t* data, size_t len) { + size_t hit = 0; + size_t miss = 0; for (auto& c : _clients) - if (c.status() == WS_CONNECTED) - c.ping(data, len); + if (c.status() == WS_CONNECTED && c.ping(data, len)) + hit++; + else + miss++; + return hit == 0 ? DISCARDED : (miss == 0 ? ENQUEUED : PARTIALLY_ENQUEUED); } -void AsyncWebSocket::text(uint32_t id, const uint8_t* message, size_t len) { - if (AsyncWebSocketClient* c = client(id)) - c->text(makeSharedBuffer(message, len)); +bool AsyncWebSocket::text(uint32_t id, const uint8_t* message, size_t len) { + AsyncWebSocketClient* c = client(id); + return c && c->text(makeSharedBuffer(message, len)); } -void AsyncWebSocket::text(uint32_t id, const char* message, size_t len) { - text(id, (const uint8_t*)message, len); +bool AsyncWebSocket::text(uint32_t id, const char* message, size_t len) { + return text(id, (const uint8_t*)message, len); } -void AsyncWebSocket::text(uint32_t id, const char* message) { - text(id, message, strlen(message)); +bool AsyncWebSocket::text(uint32_t id, const char* message) { + return text(id, message, strlen(message)); } -void AsyncWebSocket::text(uint32_t id, const String& message) { - text(id, message.c_str(), message.length()); +bool AsyncWebSocket::text(uint32_t id, const String& message) { + return text(id, message.c_str(), message.length()); } #ifdef ESP8266 -void AsyncWebSocket::text(uint32_t id, const __FlashStringHelper* data) { +bool AsyncWebSocket::text(uint32_t id, const __FlashStringHelper* data) { PGM_P p = reinterpret_cast(data); size_t n = 0; @@ -838,40 +863,44 @@ void AsyncWebSocket::text(uint32_t id, const __FlashStringHelper* data) { } char* message = (char*)malloc(n + 1); + bool enqueued = false; if (message) { memcpy_P(message, p, n); message[n] = 0; - text(id, message, n); + enqueued = text(id, message, n); free(message); } + return enqueued; } #endif // ESP8266 -void AsyncWebSocket::text(uint32_t id, AsyncWebSocketMessageBuffer* buffer) { +bool AsyncWebSocket::text(uint32_t id, AsyncWebSocketMessageBuffer* buffer) { + bool enqueued = false; if (buffer) { - text(id, std::move(buffer->_buffer)); + enqueued = text(id, std::move(buffer->_buffer)); delete buffer; } + return enqueued; } -void AsyncWebSocket::text(uint32_t id, AsyncWebSocketSharedBuffer buffer) { - if (AsyncWebSocketClient* c = client(id)) - c->text(buffer); +bool AsyncWebSocket::text(uint32_t id, AsyncWebSocketSharedBuffer buffer) { + AsyncWebSocketClient* c = client(id); + return c && c->text(buffer); } -void AsyncWebSocket::textAll(const uint8_t* message, size_t len) { - textAll(makeSharedBuffer(message, len)); +AsyncWebSocket::SendStatus AsyncWebSocket::textAll(const uint8_t* message, size_t len) { + return textAll(makeSharedBuffer(message, len)); } -void AsyncWebSocket::textAll(const char* message, size_t len) { - textAll((const uint8_t*)message, len); +AsyncWebSocket::SendStatus AsyncWebSocket::textAll(const char* message, size_t len) { + return textAll((const uint8_t*)message, len); } -void AsyncWebSocket::textAll(const char* message) { - textAll(message, strlen(message)); +AsyncWebSocket::SendStatus AsyncWebSocket::textAll(const char* message) { + return textAll(message, strlen(message)); } -void AsyncWebSocket::textAll(const String& message) { - textAll(message.c_str(), message.length()); +AsyncWebSocket::SendStatus AsyncWebSocket::textAll(const String& message) { + return textAll(message.c_str(), message.length()); } #ifdef ESP8266 -void AsyncWebSocket::textAll(const __FlashStringHelper* data) { +AsyncWebSocket::SendStatus AsyncWebSocket::textAll(const __FlashStringHelper* data) { PGM_P p = reinterpret_cast(data); size_t n = 0; @@ -882,99 +911,121 @@ void AsyncWebSocket::textAll(const __FlashStringHelper* data) { } char* message = (char*)malloc(n + 1); + AsyncWebSocket::SendStatus status = DISCARDED; if (message) { memcpy_P(message, p, n); message[n] = 0; - textAll(message, n); + status = textAll(message, n); free(message); } + return status; } #endif // ESP8266 -void AsyncWebSocket::textAll(AsyncWebSocketMessageBuffer* buffer) { +AsyncWebSocket::SendStatus AsyncWebSocket::textAll(AsyncWebSocketMessageBuffer* buffer) { + AsyncWebSocket::SendStatus status = DISCARDED; if (buffer) { - textAll(std::move(buffer->_buffer)); + status = textAll(std::move(buffer->_buffer)); delete buffer; } + return status; } -void AsyncWebSocket::textAll(AsyncWebSocketSharedBuffer buffer) { +AsyncWebSocket::SendStatus AsyncWebSocket::textAll(AsyncWebSocketSharedBuffer buffer) { + size_t hit = 0; + size_t miss = 0; for (auto& c : _clients) - if (c.status() == WS_CONNECTED) - c.text(buffer); + if (c.status() == WS_CONNECTED && c.text(buffer)) + hit++; + else + miss++; + return hit == 0 ? DISCARDED : (miss == 0 ? ENQUEUED : PARTIALLY_ENQUEUED); } -void AsyncWebSocket::binary(uint32_t id, const uint8_t* message, size_t len) { - if (AsyncWebSocketClient* c = client(id)) - c->binary(makeSharedBuffer(message, len)); +bool AsyncWebSocket::binary(uint32_t id, const uint8_t* message, size_t len) { + AsyncWebSocketClient* c = client(id); + return c && c->binary(makeSharedBuffer(message, len)); } -void AsyncWebSocket::binary(uint32_t id, const char* message, size_t len) { - binary(id, (const uint8_t*)message, len); +bool AsyncWebSocket::binary(uint32_t id, const char* message, size_t len) { + return binary(id, (const uint8_t*)message, len); } -void AsyncWebSocket::binary(uint32_t id, const char* message) { - binary(id, message, strlen(message)); +bool AsyncWebSocket::binary(uint32_t id, const char* message) { + return binary(id, message, strlen(message)); } -void AsyncWebSocket::binary(uint32_t id, const String& message) { - binary(id, message.c_str(), message.length()); +bool AsyncWebSocket::binary(uint32_t id, const String& message) { + return binary(id, message.c_str(), message.length()); } #ifdef ESP8266 -void AsyncWebSocket::binary(uint32_t id, const __FlashStringHelper* data, size_t len) { +bool AsyncWebSocket::binary(uint32_t id, const __FlashStringHelper* data, size_t len) { PGM_P p = reinterpret_cast(data); char* message = (char*)malloc(len); + bool enqueued = false; if (message) { memcpy_P(message, p, len); - binary(id, message, len); + enqueued = binary(id, message, len); free(message); } + return enqueued; } #endif // ESP8266 -void AsyncWebSocket::binary(uint32_t id, AsyncWebSocketMessageBuffer* buffer) { +bool AsyncWebSocket::binary(uint32_t id, AsyncWebSocketMessageBuffer* buffer) { + bool enqueued = false; if (buffer) { - binary(id, std::move(buffer->_buffer)); + enqueued = binary(id, std::move(buffer->_buffer)); delete buffer; } + return enqueued; } -void AsyncWebSocket::binary(uint32_t id, AsyncWebSocketSharedBuffer buffer) { - if (AsyncWebSocketClient* c = client(id)) - c->binary(buffer); +bool AsyncWebSocket::binary(uint32_t id, AsyncWebSocketSharedBuffer buffer) { + AsyncWebSocketClient* c = client(id); + return c && c->binary(buffer); } -void AsyncWebSocket::binaryAll(const uint8_t* message, size_t len) { - binaryAll(makeSharedBuffer(message, len)); +AsyncWebSocket::SendStatus AsyncWebSocket::binaryAll(const uint8_t* message, size_t len) { + return binaryAll(makeSharedBuffer(message, len)); } -void AsyncWebSocket::binaryAll(const char* message, size_t len) { - binaryAll((const uint8_t*)message, len); +AsyncWebSocket::SendStatus AsyncWebSocket::binaryAll(const char* message, size_t len) { + return binaryAll((const uint8_t*)message, len); } -void AsyncWebSocket::binaryAll(const char* message) { - binaryAll(message, strlen(message)); +AsyncWebSocket::SendStatus AsyncWebSocket::binaryAll(const char* message) { + return binaryAll(message, strlen(message)); } -void AsyncWebSocket::binaryAll(const String& message) { - binaryAll(message.c_str(), message.length()); +AsyncWebSocket::SendStatus AsyncWebSocket::binaryAll(const String& message) { + return binaryAll(message.c_str(), message.length()); } #ifdef ESP8266 -void AsyncWebSocket::binaryAll(const __FlashStringHelper* data, size_t len) { +AsyncWebSocket::SendStatus AsyncWebSocket::binaryAll(const __FlashStringHelper* data, size_t len) { PGM_P p = reinterpret_cast(data); char* message = (char*)malloc(len); + AsyncWebSocket::SendStatus status = DISCARDED; if (message) { memcpy_P(message, p, len); - binaryAll(message, len); + status = binaryAll(message, len); free(message); } + return status; } #endif // ESP8266 -void AsyncWebSocket::binaryAll(AsyncWebSocketMessageBuffer* buffer) { +AsyncWebSocket::SendStatus AsyncWebSocket::binaryAll(AsyncWebSocketMessageBuffer* buffer) { + AsyncWebSocket::SendStatus status = DISCARDED; if (buffer) { - binaryAll(std::move(buffer->_buffer)); + status = binaryAll(std::move(buffer->_buffer)); delete buffer; } + return status; } -void AsyncWebSocket::binaryAll(AsyncWebSocketSharedBuffer buffer) { +AsyncWebSocket::SendStatus AsyncWebSocket::binaryAll(AsyncWebSocketSharedBuffer buffer) { + size_t hit = 0; + size_t miss = 0; for (auto& c : _clients) - if (c.status() == WS_CONNECTED) - c.binary(buffer); + if (c.status() == WS_CONNECTED && c.binary(buffer)) + hit++; + else + miss++; + return hit == 0 ? DISCARDED : (miss == 0 ? ENQUEUED : PARTIALLY_ENQUEUED); } size_t AsyncWebSocket::printf(uint32_t id, const char* format, ...) { @@ -1007,9 +1058,9 @@ size_t AsyncWebSocket::printfAll(const char* format, ...) { len = vsnprintf(buffer, len + 1, format, arg); va_end(arg); - textAll(buffer, len); + AsyncWebSocket::SendStatus status = textAll(buffer, len); delete[] buffer; - return len; + return status == DISCARDED ? 0 : len; } #ifdef ESP8266 @@ -1043,9 +1094,9 @@ size_t AsyncWebSocket::printfAll_P(PGM_P formatP, ...) { len = vsnprintf_P(buffer, len + 1, formatP, arg); va_end(arg); - textAll(buffer, len); + AsyncWebSocket::SendStatus status = textAll(buffer, len); delete[] buffer; - return len; + return status == DISCARDED ? 0 : len; } #endif @@ -1071,14 +1122,8 @@ const char __WS_STR_UUID[] PROGMEM = {"258EAFA5-E914-47DA-95CA-C5AB0DC85B11"}; #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; - - if (request->method() != HTTP_GET || !request->url().equals(_url) || !request->isExpectedRequestedConnType(RCT_WS)) - return false; - - return true; +bool AsyncWebSocket::canHandle(AsyncWebServerRequest* request) const { + return _enabled && request->isWebSocketUpgrade() && request->url().equals(_url); } void AsyncWebSocket::handleRequest(AsyncWebServerRequest* request) { diff --git a/src/AsyncWebSocket.h b/src/AsyncWebSocket.h index 1da3d15..671c866 100644 --- a/src/AsyncWebSocket.h +++ b/src/AsyncWebSocket.h @@ -104,6 +104,7 @@ typedef enum { WS_MSG_SENDING, WS_MSG_ERROR } AwsMessageStatus; typedef enum { WS_EVT_CONNECT, WS_EVT_DISCONNECT, + WS_EVT_PING, WS_EVT_PONG, WS_EVT_ERROR, WS_EVT_DATA } AwsEventType; @@ -164,8 +165,8 @@ 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); + bool _queueControl(uint8_t opcode, const uint8_t* data = NULL, size_t len = 0, bool mask = false); + bool _queueMessage(AsyncWebSocketSharedBuffer buffer, uint8_t opcode = WS_TEXT, bool mask = false); void _runQueue(); void _clearQueue(); @@ -212,7 +213,7 @@ class AsyncWebSocketClient { // control frames void close(uint16_t code = 0, const char* message = NULL); - void ping(const uint8_t* data = NULL, size_t len = 0); + bool 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) { @@ -229,19 +230,19 @@ class AsyncWebSocketClient { size_t printf(const char* format, ...) __attribute__((format(printf, 2, 3))); - 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(AsyncWebSocketMessageBuffer* buffer); + bool text(AsyncWebSocketSharedBuffer buffer); + bool text(const uint8_t* message, size_t len); + bool text(const char* message, size_t len); + bool text(const char* message); + bool text(const String& message); + bool 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(AsyncWebSocketMessageBuffer* buffer); + bool binary(AsyncWebSocketSharedBuffer buffer); + bool binary(const uint8_t* message, size_t len); + bool binary(const char* message, size_t len); + bool binary(const char* message); + bool binary(const String& message); + bool binary(AsyncWebSocketMessageBuffer* buffer); bool canSend() const; @@ -255,8 +256,8 @@ class AsyncWebSocketClient { #ifdef ESP8266 size_t printf_P(PGM_P formatP, ...) __attribute__((format(printf, 2, 3))); - void text(const __FlashStringHelper* message); - void binary(const __FlashStringHelper* message, size_t len); + bool text(const __FlashStringHelper* message); + bool binary(const __FlashStringHelper* message, size_t len); #endif }; @@ -277,6 +278,12 @@ class AsyncWebSocket : public AsyncWebHandler { #endif public: + typedef enum { + DISCARDED = 0, + ENQUEUED = 1, + PARTIALLY_ENQUEUED = 2, + } SendStatus; + explicit AsyncWebSocket(const char* url) : _url(url), _cNextId(1), _enabled(true) {} AsyncWebSocket(const String& url) : _url(url), _cNextId(1), _enabled(true) {} ~AsyncWebSocket() {}; @@ -294,45 +301,45 @@ class AsyncWebSocket : public AsyncWebHandler { 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 + bool ping(uint32_t id, const uint8_t* data = NULL, size_t len = 0); + SendStatus 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, AsyncWebSocketMessageBuffer* buffer); - void text(uint32_t id, AsyncWebSocketSharedBuffer buffer); + bool text(uint32_t id, const uint8_t* message, size_t len); + bool text(uint32_t id, const char* message, size_t len); + bool text(uint32_t id, const char* message); + bool text(uint32_t id, const String& message); + bool text(uint32_t id, AsyncWebSocketMessageBuffer* buffer); + bool 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(AsyncWebSocketMessageBuffer* buffer); - void textAll(AsyncWebSocketSharedBuffer buffer); + SendStatus textAll(const uint8_t* message, size_t len); + SendStatus textAll(const char* message, size_t len); + SendStatus textAll(const char* message); + SendStatus textAll(const String& message); + SendStatus textAll(AsyncWebSocketMessageBuffer* buffer); + SendStatus 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, AsyncWebSocketMessageBuffer* buffer); - void binary(uint32_t id, AsyncWebSocketSharedBuffer buffer); + bool binary(uint32_t id, const uint8_t* message, size_t len); + bool binary(uint32_t id, const char* message, size_t len); + bool binary(uint32_t id, const char* message); + bool binary(uint32_t id, const String& message); + bool binary(uint32_t id, AsyncWebSocketMessageBuffer* buffer); + bool 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(AsyncWebSocketMessageBuffer* buffer); - void binaryAll(AsyncWebSocketSharedBuffer buffer); + SendStatus binaryAll(const uint8_t* message, size_t len); + SendStatus binaryAll(const char* message, size_t len); + SendStatus binaryAll(const char* message); + SendStatus binaryAll(const String& message); + SendStatus binaryAll(AsyncWebSocketMessageBuffer* buffer); + SendStatus 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))); #ifdef ESP8266 - void text(uint32_t id, const __FlashStringHelper* message); - void textAll(const __FlashStringHelper* message); - void binary(uint32_t id, const __FlashStringHelper* message, size_t len); - void binaryAll(const __FlashStringHelper* message, size_t len); + bool text(uint32_t id, const __FlashStringHelper* message); + SendStatus textAll(const __FlashStringHelper* message); + bool binary(uint32_t id, const __FlashStringHelper* message, size_t len); + SendStatus binaryAll(const __FlashStringHelper* message, size_t len); 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 @@ -344,8 +351,8 @@ class AsyncWebSocket : public AsyncWebHandler { 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; + bool canHandle(AsyncWebServerRequest* request) const override final; + void handleRequest(AsyncWebServerRequest* request) override final; // messagebuffer functions/objects. AsyncWebSocketMessageBuffer* makeBuffer(size_t size = 0); diff --git a/src/ChunkPrint.h b/src/ChunkPrint.h index 103d21e..47988e1 100644 --- a/src/ChunkPrint.h +++ b/src/ChunkPrint.h @@ -12,7 +12,6 @@ class ChunkPrint : public Print { public: ChunkPrint(uint8_t* destination, size_t from, size_t len); - virtual ~ChunkPrint() {} size_t write(uint8_t c); size_t write(const uint8_t* buffer, size_t size) { return this->Print::write(buffer, size); } }; diff --git a/src/ESPAsyncWebServer.h b/src/ESPAsyncWebServer.h index dfaef9c..e58a155 100644 --- a/src/ESPAsyncWebServer.h +++ b/src/ESPAsyncWebServer.h @@ -48,10 +48,10 @@ #include "literals.h" -#define ASYNCWEBSERVER_VERSION "3.3.7" +#define ASYNCWEBSERVER_VERSION "3.3.23" #define ASYNCWEBSERVER_VERSION_MAJOR 3 #define ASYNCWEBSERVER_VERSION_MINOR 3 -#define ASYNCWEBSERVER_VERSION_REVISION 7 +#define ASYNCWEBSERVER_VERSION_REVISION 23 #define ASYNCWEBSERVER_FORK_mathieucarbou #ifdef ASYNCWEBSERVER_REGEX @@ -140,7 +140,6 @@ class AsyncWebHeader { String _value; public: - AsyncWebHeader() = default; AsyncWebHeader(const AsyncWebHeader&) = default; AsyncWebHeader(const char* name, const char* value) : _name(name), _value(value) {} AsyncWebHeader(const String& name, const String& value) : _name(name), _value(value) {} @@ -166,11 +165,12 @@ typedef enum { RCT_NOT_USED = -1, // this enum is similar to Arduino WebServer's AsyncAuthType and PsychicHttp typedef enum { - AUTH_NONE = 0, - AUTH_BASIC, - AUTH_DIGEST, - AUTH_BEARER, - AUTH_OTHER, + AUTH_NONE = 0, // always allow + AUTH_BASIC = 1, + AUTH_DIGEST = 2, + AUTH_BEARER = 3, + AUTH_OTHER = 4, + AUTH_DENIED = 255, // always returns 401 } AsyncAuthType; typedef std::function AwsResponseFiller; @@ -264,23 +264,21 @@ class AsyncWebServerRequest { size_t contentLength() const { return _contentLength; } bool multipart() const { return _isMultipart; } -#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); + bool isExpectedRequestedConnType(RequestedConnectionType erct1, RequestedConnectionType erct2 = RCT_NOT_USED, RequestedConnectionType erct3 = RCT_NOT_USED) const; + bool isWebSocketUpgrade() const { return _method == HTTP_GET && isExpectedRequestedConnType(RCT_WS); } + bool isSSE() const { return _method == HTTP_GET && isExpectedRequestedConnType(RCT_EVENT); } + bool isHTTP() const { return isExpectedRequestedConnType(RCT_DEFAULT, RCT_HTTP); } 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* credentials, const char* realm = NULL, bool isHash = false); + bool authenticate(const char* hash) const; + bool authenticate(const char* username, const char* credentials, const char* realm = NULL, bool isHash = false) const; void requestAuthentication(const char* realm = nullptr, bool isDigest = true) { requestAuthentication(isDigest ? AsyncAuthType::AUTH_DIGEST : AsyncAuthType::AUTH_BASIC, realm); } void requestAuthentication(AsyncAuthType method, const char* realm = nullptr, const char* _authFailMsg = nullptr); @@ -523,9 +521,7 @@ using ArMiddlewareCallback = std::function& getHeaders() const { return _headers; } #ifndef ESP8266 @@ -794,7 +797,7 @@ class AsyncWebServerResponse { _assembleHead(buffer, version); return buffer; } - virtual void _assembleHead(String& buffer, uint8_t version); + void _assembleHead(String& buffer, uint8_t version); virtual bool _started() const; virtual bool _finished() const; diff --git a/src/Middleware.cpp b/src/Middleware.cpp index 1c36ef6..78cdfc8 100644 --- a/src/Middleware.cpp +++ b/src/Middleware.cpp @@ -66,7 +66,7 @@ void AuthenticationMiddleware::setPassword(const char* password) { void AuthenticationMiddleware::setPasswordHash(const char* hash) { _credentials = hash; - _hash = true; + _hash = _credentials.length(); _hasCreds = _username.length() && _credentials.length(); } @@ -95,13 +95,16 @@ bool AuthenticationMiddleware::generateHash() { } } -bool AuthenticationMiddleware::allowed(AsyncWebServerRequest* request) { +bool AuthenticationMiddleware::allowed(AsyncWebServerRequest* request) const { if (_authMethod == AsyncAuthType::AUTH_NONE) return true; - if (!_hasCreds) + if (_authMethod == AsyncAuthType::AUTH_DENIED) return false; + if (!_hasCreds) + return true; + return request->authenticate(_username.c_str(), _credentials.c_str(), _realm.c_str(), _hash); } @@ -192,16 +195,16 @@ void LoggingMiddleware::run(AsyncWebServerRequest* request, ArMiddlewareNext nex } void CorsMiddleware::addCORSHeaders(AsyncWebServerResponse* response) { - response->addHeader(F("Access-Control-Allow-Origin"), _origin.c_str()); - response->addHeader(F("Access-Control-Allow-Methods"), _methods.c_str()); - response->addHeader(F("Access-Control-Allow-Headers"), _headers.c_str()); - response->addHeader(F("Access-Control-Allow-Credentials"), _credentials ? F("true") : F("false")); - response->addHeader(F("Access-Control-Max-Age"), String(_maxAge).c_str()); + response->addHeader(asyncsrv::T_CORS_ACAO, _origin.c_str()); + response->addHeader(asyncsrv::T_CORS_ACAM, _methods.c_str()); + response->addHeader(asyncsrv::T_CORS_ACAH, _headers.c_str()); + response->addHeader(asyncsrv::T_CORS_ACAC, _credentials ? asyncsrv::T_TRUE : asyncsrv::T_FALSE); + response->addHeader(asyncsrv::T_CORS_ACMA, String(_maxAge).c_str()); } void CorsMiddleware::run(AsyncWebServerRequest* request, ArMiddlewareNext next) { // Origin header ? => CORS handling - if (request->hasHeader(F("Origin"))) { + if (request->hasHeader(asyncsrv::T_CORS_O)) { // check if this is a preflight request => handle it and return if (request->method() == HTTP_OPTIONS) { AsyncWebServerResponse* response = request->beginResponse(200); @@ -247,7 +250,7 @@ void RateLimitMiddleware::run(AsyncWebServerRequest* request, ArMiddlewareNext n next(); } else { AsyncWebServerResponse* response = request->beginResponse(429); - response->addHeader(F("Retry-After"), retryAfterSeconds); + response->addHeader(asyncsrv::T_retry_after, retryAfterSeconds); request->send(response); } } diff --git a/src/WebAuthentication.cpp b/src/WebAuthentication.cpp index 304f258..ebcc38e 100644 --- a/src/WebAuthentication.cpp +++ b/src/WebAuthentication.cpp @@ -137,11 +137,7 @@ String generateDigestHash(const char* username, const char* password, const char return in; } -#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"); diff --git a/src/WebAuthentication.h b/src/WebAuthentication.h index a35d551..68bed0c 100644 --- a/src/WebAuthentication.h +++ b/src/WebAuthentication.h @@ -28,10 +28,6 @@ bool checkBasicAuthentication(const char* header, const char* username, const ch 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); diff --git a/src/WebHandlerImpl.h b/src/WebHandlerImpl.h index cb43f80..0eaec65 100644 --- a/src/WebHandlerImpl.h +++ b/src/WebHandlerImpl.h @@ -34,8 +34,8 @@ class AsyncStaticWebHandler : public AsyncWebHandler { using FS = fs::FS; private: - bool _getFile(AsyncWebServerRequest* request); - bool _fileExists(AsyncWebServerRequest* request, const String& path); + bool _getFile(AsyncWebServerRequest* request) const; + bool _searchFile(AsyncWebServerRequest* request, const String& path); uint8_t _countBits(const uint8_t value) const; protected: @@ -47,13 +47,13 @@ class AsyncStaticWebHandler : public AsyncWebHandler { String _last_modified; AwsTemplateProcessor _callback; bool _isDir; - bool _gzipFirst; - uint8_t _gzipStats; + bool _tryGzipFirst = true; 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; + bool canHandle(AsyncWebServerRequest* request) const override final; + void handleRequest(AsyncWebServerRequest* request) override final; + AsyncStaticWebHandler& setTryGzipFirst(bool value); AsyncStaticWebHandler& setIsDir(bool isDir); AsyncStaticWebHandler& setDefaultFile(const char* filename); AsyncStaticWebHandler& setCacheControl(const char* cache_control); @@ -84,11 +84,11 @@ class AsyncCallbackWebHandler : public AsyncWebHandler { void onUpload(ArUploadHandlerFunction fn) { _onUpload = fn; } void onBody(ArBodyHandlerFunction fn) { _onBody = fn; } - virtual bool canHandle(AsyncWebServerRequest* request) override final; - virtual void handleRequest(AsyncWebServerRequest* request) override final; - virtual void handleUpload(AsyncWebServerRequest* request, const String& filename, size_t index, uint8_t* data, size_t len, bool final) override final; - virtual void handleBody(AsyncWebServerRequest* request, uint8_t* data, size_t len, size_t index, size_t total) override final; - virtual bool isRequestHandlerTrivial() override final { return !_onRequest; } + bool canHandle(AsyncWebServerRequest* request) const override final; + void handleRequest(AsyncWebServerRequest* request) override final; + void handleUpload(AsyncWebServerRequest* request, const String& filename, size_t index, uint8_t* data, size_t len, bool final) override final; + void handleBody(AsyncWebServerRequest* request, uint8_t* data, size_t len, size_t index, size_t total) override final; + bool isRequestHandlerTrivial() const override final { return !_onRequest; } }; #endif /* ASYNCWEBSERVERHANDLERIMPL_H_ */ diff --git a/src/WebHandlers.cpp b/src/WebHandlers.cpp index 7c3a7dc..335e1f2 100644 --- a/src/WebHandlers.cpp +++ b/src/WebHandlers.cpp @@ -27,7 +27,7 @@ AsyncWebHandler& AsyncWebHandler::setFilter(ArRequestFilterFunction fn) { _filter = fn; return *this; } -AsyncWebHandler& AsyncWebHandler::setAuthentication(const char* username, const char* password) { +AsyncWebHandler& AsyncWebHandler::setAuthentication(const char* username, const char* password, AsyncAuthType authMethod) { if (!_authMiddleware) { _authMiddleware = new AuthenticationMiddleware(); _authMiddleware->_freeOnRemoval = true; @@ -35,6 +35,7 @@ AsyncWebHandler& AsyncWebHandler::setAuthentication(const char* username, const } _authMiddleware->setUsername(username); _authMiddleware->setPassword(password); + _authMiddleware->setAuthType(authMethod); return *this; }; @@ -56,10 +57,11 @@ AsyncStaticWebHandler::AsyncStaticWebHandler(const char* uri, FS& fs, const char _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::setTryGzipFirst(bool value) { + _tryGzipFirst = value; + return *this; } AsyncStaticWebHandler& AsyncStaticWebHandler::setIsDir(bool isDir) { @@ -104,14 +106,11 @@ AsyncStaticWebHandler& AsyncStaticWebHandler::setLastModified() { 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)) { - return false; - } - return _getFile(request); +bool AsyncStaticWebHandler::canHandle(AsyncWebServerRequest* request) const { + return request->isHTTP() && request->method() == HTTP_GET && request->url().startsWith(_uri) && _getFile(request); } -bool AsyncStaticWebHandler::_getFile(AsyncWebServerRequest* request) { +bool AsyncStaticWebHandler::_getFile(AsyncWebServerRequest* request) const { // Remove the found uri String path = request->url().substring(_uri.length()); @@ -121,7 +120,7 @@ bool AsyncStaticWebHandler::_getFile(AsyncWebServerRequest* request) { path = _path + path; // Do we have a file or .gz file - if (!canSkipFileCheck && _fileExists(request, path)) + if (!canSkipFileCheck && const_cast(this)->_searchFile(request, path)) return true; // Can't handle if not default file @@ -133,7 +132,7 @@ bool AsyncStaticWebHandler::_getFile(AsyncWebServerRequest* request) { path += String('/'); path += _default_file; - return _fileExists(request, path); + return const_cast(this)->_searchFile(request, path); } #ifdef ESP32 @@ -142,13 +141,13 @@ bool AsyncStaticWebHandler::_getFile(AsyncWebServerRequest* request) { #define FILE_IS_REAL(f) (f == true) #endif -bool AsyncStaticWebHandler::_fileExists(AsyncWebServerRequest* request, const String& path) { +bool AsyncStaticWebHandler::_searchFile(AsyncWebServerRequest* request, const String& path) { bool fileFound = false; bool gzipFound = false; - String gzip = path + F(".gz"); + String gzip = path + T__gz; - if (_gzipFirst) { + if (_tryGzipFirst) { if (_fs.exists(gzip)) { request->_tempFile = _fs.open(gzip, fs::FileOpenMode::read); gzipFound = FILE_IS_REAL(request->_tempFile); @@ -180,15 +179,6 @@ bool AsyncStaticWebHandler::_fileExists(AsyncWebServerRequest* request, const St 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 } return found; @@ -260,11 +250,8 @@ void AsyncCallbackWebHandler::setUri(const String& uri) { _isRegex = uri.startsWith("^") && uri.endsWith("$"); } -bool AsyncCallbackWebHandler::canHandle(AsyncWebServerRequest* request) { - if (!_onRequest) - return false; - - if (!(_method & request->method())) +bool AsyncCallbackWebHandler::canHandle(AsyncWebServerRequest* request) const { + if (!_onRequest || !request->isHTTP() || !(_method & request->method())) return false; #ifdef ASYNCWEBSERVER_REGEX diff --git a/src/WebRequest.cpp b/src/WebRequest.cpp index 242a111..09d6102 100644 --- a/src/WebRequest.cpp +++ b/src/WebRequest.cpp @@ -49,9 +49,9 @@ AsyncWebServerRequest::~AsyncWebServerRequest() { _pathParams.clear(); - if (_response != NULL) { - delete _response; - } + AsyncWebServerResponse* r = _response; + _response = NULL; + delete r; if (_tempObject != NULL) { free(_tempObject); @@ -280,38 +280,40 @@ bool AsyncWebServerRequest::_parseReqHeader() { } } else if (name.equalsIgnoreCase(T_Content_Length)) { _contentLength = atoi(value.c_str()); - } else if (name.equalsIgnoreCase(T_EXPECT) && value == T_100_CONTINUE) { + } else if (name.equalsIgnoreCase(T_EXPECT) && value.equalsIgnoreCase(T_100_CONTINUE)) { _expectingContinue = true; } else if (name.equalsIgnoreCase(T_AUTH)) { - if (value.length() > 5 && value.substring(0, 5).equalsIgnoreCase(T_BASIC)) { - _authorization = value.substring(6); - _authMethod = AsyncAuthType::AUTH_BASIC; - } else if (value.length() > 6 && value.substring(0, 6).equalsIgnoreCase(T_DIGEST)) { - _authMethod = AsyncAuthType::AUTH_DIGEST; - _authorization = value.substring(7); - } else if (value.length() > 6 && value.substring(0, 6).equalsIgnoreCase(T_BEARER)) { - _authMethod = AsyncAuthType::AUTH_BEARER; - _authorization = value.substring(7); - } else { + int space = value.indexOf(' '); + if (space == -1) { _authorization = value; _authMethod = AsyncAuthType::AUTH_OTHER; - } - } else { - if (name.equalsIgnoreCase(T_UPGRADE) && value.equalsIgnoreCase(T_WS)) { - // WebSocket request can be uniquely identified by header: [Upgrade: websocket] - _reqconntype = RCT_WS; - } else if (name.equalsIgnoreCase(T_ACCEPT)) { - String lowcase(value); - lowcase.toLowerCase(); -#ifndef ESP8266 - const char* substr = std::strstr(lowcase.c_str(), T_text_event_stream); -#else - const char* substr = std::strstr(lowcase.c_str(), String(T_text_event_stream).c_str()); -#endif - if (substr != NULL) { - // WebEvent request can be uniquely identified by header: [Accept: text/event-stream] - _reqconntype = RCT_EVENT; + } else { + String method = value.substring(0, space); + if (method.equalsIgnoreCase(T_BASIC)) { + _authMethod = AsyncAuthType::AUTH_BASIC; + } else if (method.equalsIgnoreCase(T_DIGEST)) { + _authMethod = AsyncAuthType::AUTH_DIGEST; + } else if (method.equalsIgnoreCase(T_BEARER)) { + _authMethod = AsyncAuthType::AUTH_BEARER; + } else { + _authMethod = AsyncAuthType::AUTH_OTHER; } + _authorization = value.substring(space + 1); + } + } else if (name.equalsIgnoreCase(T_UPGRADE) && value.equalsIgnoreCase(T_WS)) { + // WebSocket request can be uniquely identified by header: [Upgrade: websocket] + _reqconntype = RCT_WS; + } else if (name.equalsIgnoreCase(T_ACCEPT)) { + String lowcase(value); + lowcase.toLowerCase(); +#ifndef ESP8266 + const char* substr = std::strstr(lowcase.c_str(), T_text_event_stream); +#else + const char* substr = std::strstr(lowcase.c_str(), String(T_text_event_stream).c_str()); +#endif + if (substr != NULL) { + // WebEvent request can be uniquely identified by header: [Accept: text/event-stream] + _reqconntype = RCT_EVENT; } } _headers.emplace_back(name, value); @@ -779,7 +781,7 @@ void AsyncWebServerRequest::redirect(const char* url, int code) { send(response); } -bool AsyncWebServerRequest::authenticate(const char* username, const char* password, const char* realm, bool passwordIsHash) { +bool AsyncWebServerRequest::authenticate(const char* username, const char* password, const char* realm, bool passwordIsHash) const { if (_authorization.length()) { if (_authMethod == AsyncAuthType::AUTH_DIGEST) return checkDigestAuthentication(_authorization.c_str(), methodToString(), username, password, realm, passwordIsHash, NULL, NULL, NULL); @@ -791,7 +793,7 @@ bool AsyncWebServerRequest::authenticate(const char* username, const char* passw return false; } -bool AsyncWebServerRequest::authenticate(const char* hash) { +bool AsyncWebServerRequest::authenticate(const char* hash) const { if (!_authorization.length() || hash == NULL) return false; @@ -831,7 +833,7 @@ void AsyncWebServerRequest::requestAuthentication(AsyncAuthType method, const ch break; } case AsyncAuthType::AUTH_DIGEST: { - constexpr size_t len = strlen(T_DIGEST_) + strlen(T_realm__) + strlen(T_auth_nonce) + 32 + strlen(T__opaque) + 32 + 1; + size_t len = strlen(T_DIGEST_) + strlen(T_realm__) + strlen(T_auth_nonce) + 32 + strlen(T__opaque) + 32 + 1; String header; header.reserve(len + strlen(realm)); header.concat(T_DIGEST_); @@ -938,7 +940,6 @@ String AsyncWebServerRequest::urlDecode(const String& text) const { return decoded; } -#ifndef ESP8266 const char* AsyncWebServerRequest::methodToString() const { if (_method == HTTP_ANY) return T_ANY; @@ -958,29 +959,7 @@ const char* AsyncWebServerRequest::methodToString() const { return T_OPTIONS; return T_UNKNOWN; } -#else // ESP8266 -const __FlashStringHelper* AsyncWebServerRequest::methodToString() const { - if (_method == HTTP_ANY) - return FPSTR(T_ANY); - if (_method & HTTP_GET) - return FPSTR(T_GET); - if (_method & HTTP_POST) - return FPSTR(T_POST); - if (_method & HTTP_DELETE) - return FPSTR(T_DELETE); - if (_method & HTTP_PUT) - return FPSTR(T_PUT); - if (_method & HTTP_PATCH) - return FPSTR(T_PATCH); - if (_method & HTTP_HEAD) - return FPSTR(T_HEAD); - if (_method & HTTP_OPTIONS) - return FPSTR(T_OPTIONS); - return FPSTR(T_UNKNOWN); -} -#endif // ESP8266 -#ifndef ESP8266 const char* AsyncWebServerRequest::requestedConnTypeToString() const { switch (_reqconntype) { case RCT_NOT_USED: @@ -997,32 +976,9 @@ const char* AsyncWebServerRequest::requestedConnTypeToString() const { return T_ERROR; } } -#else // ESP8266 -const __FlashStringHelper* AsyncWebServerRequest::requestedConnTypeToString() const { - switch (_reqconntype) { - case RCT_NOT_USED: - return FPSTR(T_RCT_NOT_USED); - case RCT_DEFAULT: - return FPSTR(T_RCT_DEFAULT); - case RCT_HTTP: - return FPSTR(T_RCT_HTTP); - case RCT_WS: - return FPSTR(T_RCT_WS); - case RCT_EVENT: - return FPSTR(T_RCT_EVENT); - default: - return FPSTR(T_ERROR); - } -} -#endif // ESP8266 -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 AsyncWebServerRequest::isExpectedRequestedConnType(RequestedConnectionType erct1, RequestedConnectionType erct2, RequestedConnectionType erct3) const { + return ((erct1 != RCT_NOT_USED) && (erct1 == _reqconntype)) || + ((erct2 != RCT_NOT_USED) && (erct2 == _reqconntype)) || + ((erct3 != RCT_NOT_USED) && (erct3 == _reqconntype)); } diff --git a/src/WebResponseImpl.h b/src/WebResponseImpl.h index a6f71bb..b58c5bb 100644 --- a/src/WebResponseImpl.h +++ b/src/WebResponseImpl.h @@ -26,9 +26,10 @@ #undef min #undef max #endif +#include "literals.h" +#include #include #include -#include "literals.h" // It is possible to restore these defines, but one can use _min and _max instead. Or std::min, std::max. @@ -39,9 +40,9 @@ class AsyncBasicResponse : public AsyncWebServerResponse { public: explicit AsyncBasicResponse(int code, const char* contentType = asyncsrv::empty, const char* content = asyncsrv::empty); AsyncBasicResponse(int code, const String& contentType, const String& content = emptyString) : AsyncBasicResponse(code, contentType.c_str(), content.c_str()) {} - void _respond(AsyncWebServerRequest* request); - size_t _ack(AsyncWebServerRequest* request, size_t len, uint32_t time); - bool _sourceValid() const { return true; } + void _respond(AsyncWebServerRequest* request) override final; + size_t _ack(AsyncWebServerRequest* request, size_t len, uint32_t time) override final; + bool _sourceValid() const override final { return true; } }; class AsyncAbstractResponse : public AsyncWebServerResponse { @@ -60,9 +61,10 @@ class AsyncAbstractResponse : public AsyncWebServerResponse { public: 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 ~AsyncAbstractResponse() {} + void _respond(AsyncWebServerRequest* request) override final; + size_t _ack(AsyncWebServerRequest* request, size_t len, uint32_t time) override final; + virtual bool _sourceValid() const { return false; } virtual size_t _fillBuffer(uint8_t* buf __attribute__((unused)), size_t maxLen __attribute__((unused))) { return 0; } }; @@ -85,9 +87,9 @@ class AsyncFileResponse : public AsyncAbstractResponse { AsyncFileResponse(FS& fs, const String& path, const String& contentType, bool download = false, AwsTemplateProcessor callback = nullptr) : AsyncFileResponse(fs, path, contentType.c_str(), download, callback) {} AsyncFileResponse(File content, const String& path, const char* contentType = asyncsrv::empty, bool download = false, AwsTemplateProcessor callback = nullptr); AsyncFileResponse(File content, const String& path, const String& contentType, bool download = false, AwsTemplateProcessor callack = nullptr) : AsyncFileResponse(content, path, contentType.c_str(), download, callack) {} - ~AsyncFileResponse(); - bool _sourceValid() const { return !!(_content); } - virtual size_t _fillBuffer(uint8_t* buf, size_t maxLen) override; + ~AsyncFileResponse() { _content.close(); } + bool _sourceValid() const override final { return !!(_content); } + size_t _fillBuffer(uint8_t* buf, size_t maxLen) override final; }; class AsyncStreamResponse : public AsyncAbstractResponse { @@ -97,8 +99,8 @@ class AsyncStreamResponse : public AsyncAbstractResponse { public: AsyncStreamResponse(Stream& stream, const char* contentType, size_t len, AwsTemplateProcessor callback = nullptr); AsyncStreamResponse(Stream& stream, const String& contentType, size_t len, AwsTemplateProcessor callback = nullptr) : AsyncStreamResponse(stream, contentType.c_str(), len, callback) {} - bool _sourceValid() const { return !!(_content); } - virtual size_t _fillBuffer(uint8_t* buf, size_t maxLen) override; + bool _sourceValid() const override final { return !!(_content); } + size_t _fillBuffer(uint8_t* buf, size_t maxLen) override final; }; class AsyncCallbackResponse : public AsyncAbstractResponse { @@ -109,8 +111,8 @@ class AsyncCallbackResponse : public AsyncAbstractResponse { public: AsyncCallbackResponse(const char* contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback = nullptr); AsyncCallbackResponse(const String& contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback = nullptr) : AsyncCallbackResponse(contentType.c_str(), len, callback, templateCallback) {} - bool _sourceValid() const { return !!(_content); } - virtual size_t _fillBuffer(uint8_t* buf, size_t maxLen) override; + bool _sourceValid() const override final { return !!(_content); } + size_t _fillBuffer(uint8_t* buf, size_t maxLen) override final; }; class AsyncChunkedResponse : public AsyncAbstractResponse { @@ -121,8 +123,8 @@ class AsyncChunkedResponse : public AsyncAbstractResponse { public: AsyncChunkedResponse(const char* contentType, AwsResponseFiller callback, AwsTemplateProcessor templateCallback = nullptr); AsyncChunkedResponse(const String& contentType, AwsResponseFiller callback, AwsTemplateProcessor templateCallback = nullptr) : AsyncChunkedResponse(contentType.c_str(), callback, templateCallback) {} - bool _sourceValid() const { return !!(_content); } - virtual size_t _fillBuffer(uint8_t* buf, size_t maxLen) override; + bool _sourceValid() const override final { return !!(_content); } + size_t _fillBuffer(uint8_t* buf, size_t maxLen) override final; }; class AsyncProgmemResponse : public AsyncAbstractResponse { @@ -133,22 +135,19 @@ class AsyncProgmemResponse : public AsyncAbstractResponse { public: AsyncProgmemResponse(int code, const char* 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) : AsyncProgmemResponse(code, contentType.c_str(), content, len, callback) {} - bool _sourceValid() const { return true; } - virtual size_t _fillBuffer(uint8_t* buf, size_t maxLen) override; + bool _sourceValid() const override final { return true; } + size_t _fillBuffer(uint8_t* buf, size_t maxLen) override final; }; -class cbuf; - class AsyncResponseStream : public AsyncAbstractResponse, public Print { private: - std::unique_ptr _content; + StreamString _content; public: AsyncResponseStream(const char* contentType, size_t bufferSize); AsyncResponseStream(const String& contentType, size_t bufferSize) : AsyncResponseStream(contentType.c_str(), bufferSize) {} - ~AsyncResponseStream(); - bool _sourceValid() const { return (_state < RESPONSE_END); } - virtual size_t _fillBuffer(uint8_t* buf, size_t maxLen) override; + bool _sourceValid() const override final { return (_state < RESPONSE_END); } + size_t _fillBuffer(uint8_t* buf, size_t maxLen) override final; 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 e7edf5e..7a26e92 100644 --- a/src/WebResponses.cpp +++ b/src/WebResponses.cpp @@ -20,7 +20,6 @@ */ #include "ESPAsyncWebServer.h" #include "WebResponseImpl.h" -#include "cbuf.h" using namespace asyncsrv; @@ -38,7 +37,6 @@ void* memchr(void* ptr, int ch, size_t count) { * */ -#ifndef ESP8266 const char* AsyncWebServerResponse::responseCodeToString(int code) { switch (code) { case 100: @@ -127,96 +125,6 @@ const char* AsyncWebServerResponse::responseCodeToString(int code) { return T_HTTP_CODE_ANY; } } -#else // ESP8266 -const __FlashStringHelper* AsyncWebServerResponse::responseCodeToString(int code) { - switch (code) { - case 100: - return FPSTR(T_HTTP_CODE_100); - case 101: - return FPSTR(T_HTTP_CODE_101); - case 200: - return FPSTR(T_HTTP_CODE_200); - case 201: - return FPSTR(T_HTTP_CODE_201); - case 202: - return FPSTR(T_HTTP_CODE_202); - case 203: - return FPSTR(T_HTTP_CODE_203); - case 204: - return FPSTR(T_HTTP_CODE_204); - case 205: - return FPSTR(T_HTTP_CODE_205); - case 206: - return FPSTR(T_HTTP_CODE_206); - case 300: - return FPSTR(T_HTTP_CODE_300); - case 301: - return FPSTR(T_HTTP_CODE_301); - case 302: - return FPSTR(T_HTTP_CODE_302); - case 303: - return FPSTR(T_HTTP_CODE_303); - case 304: - return FPSTR(T_HTTP_CODE_304); - case 305: - return FPSTR(T_HTTP_CODE_305); - case 307: - return FPSTR(T_HTTP_CODE_307); - case 400: - return FPSTR(T_HTTP_CODE_400); - case 401: - return FPSTR(T_HTTP_CODE_401); - case 402: - return FPSTR(T_HTTP_CODE_402); - case 403: - return FPSTR(T_HTTP_CODE_403); - case 404: - return FPSTR(T_HTTP_CODE_404); - case 405: - return FPSTR(T_HTTP_CODE_405); - case 406: - return FPSTR(T_HTTP_CODE_406); - case 407: - return FPSTR(T_HTTP_CODE_407); - case 408: - return FPSTR(T_HTTP_CODE_408); - case 409: - return FPSTR(T_HTTP_CODE_409); - case 410: - return FPSTR(T_HTTP_CODE_410); - case 411: - return FPSTR(T_HTTP_CODE_411); - case 412: - return FPSTR(T_HTTP_CODE_412); - case 413: - return FPSTR(T_HTTP_CODE_413); - case 414: - return FPSTR(T_HTTP_CODE_414); - case 415: - return FPSTR(T_HTTP_CODE_415); - case 416: - return FPSTR(T_HTTP_CODE_416); - case 417: - return FPSTR(T_HTTP_CODE_417); - case 429: - return FPSTR(T_HTTP_CODE_429); - case 500: - return FPSTR(T_HTTP_CODE_500); - case 501: - return FPSTR(T_HTTP_CODE_501); - case 502: - return FPSTR(T_HTTP_CODE_502); - case 503: - return FPSTR(T_HTTP_CODE_503); - case 504: - return FPSTR(T_HTTP_CODE_504); - case 505: - return FPSTR(T_HTTP_CODE_505); - default: - return FPSTR(T_HTTP_CODE_ANY); - } -} -#endif // ESP8266 AsyncWebServerResponse::AsyncWebServerResponse() : _code(0), _contentType(), _contentLength(0), _sendContentLength(true), _chunked(false), _headLength(0), _sentLength(0), _ackedLength(0), _writtenLength(0), _state(RESPONSE_SETUP) { @@ -225,8 +133,6 @@ AsyncWebServerResponse::AsyncWebServerResponse() } } -AsyncWebServerResponse::~AsyncWebServerResponse() = default; - void AsyncWebServerResponse::setCode(int code) { if (_state == RESPONSE_SETUP) _code = code; @@ -648,11 +554,6 @@ size_t AsyncAbstractResponse::_fillBufferAndProcessTemplates(uint8_t* data, size * File Response * */ -AsyncFileResponse::~AsyncFileResponse() { - if (_content) - _content.close(); -} - void AsyncFileResponse::_setContentTypeFromPath(const String& path) { #if HAVE_EXTERN_GET_Content_Type_FUNCTION #ifndef ESP8266 @@ -868,24 +769,17 @@ AsyncResponseStream::AsyncResponseStream(const char* contentType, size_t bufferS _code = 200; _contentLength = 0; _contentType = contentType; - _content = std::unique_ptr(new cbuf(bufferSize)); // std::make_unique(bufferSize); + _content.reserve(bufferSize); } -AsyncResponseStream::~AsyncResponseStream() = default; - size_t AsyncResponseStream::_fillBuffer(uint8_t* buf, size_t maxLen) { - return _content->read((char*)buf, maxLen); + return _content.readBytes((char*)buf, maxLen); } size_t AsyncResponseStream::write(const uint8_t* data, size_t len) { if (_started()) return 0; - - if (len > _content->room()) { - size_t needed = len - _content->room(); - _content->resizeAdd(needed); - } - size_t written = _content->write((const char*)data, len); + size_t written = _content.write(data, len); _contentLength += written; return written; } diff --git a/src/literals.h b/src/literals.h index 2300b4a..6782390 100644 --- a/src/literals.h +++ b/src/literals.h @@ -2,346 +2,181 @@ namespace asyncsrv { -static constexpr const char* empty = ""; + static constexpr const char* empty = ""; -#ifndef ESP8622 -static constexpr const char* T_100_CONTINUE = "100-continue"; -static constexpr const char* T_ACCEPT = "Accept"; -static constexpr const char* T_Accept_Ranges = "Accept-Ranges"; -static constexpr const char* T_app_xform_urlencoded = "application/x-www-form-urlencoded"; -static constexpr const char* T_AUTH = "Authorization"; -static constexpr const char* T_BASIC = "Basic"; -static constexpr const char* T_BASIC_REALM = "Basic realm=\""; -static constexpr const char* T_LOGIN_REQ = "Login Required"; -static constexpr const char* T_BODY = "body"; -static constexpr const char* T_Cache_Control = "Cache-Control"; -static constexpr const char* T_chunked = "chunked"; -static constexpr const char* T_close = "close"; -static constexpr const char* T_Connection = "Connection"; -static constexpr const char* T_Content_Disposition = "Content-Disposition"; -static constexpr const char* T_Content_Encoding = "Content-Encoding"; -static constexpr const char* T_Content_Length = "Content-Length"; -static constexpr const char* T_Content_Type = "Content-Type"; -static constexpr const char* T_Cookie = "Cookie"; -static constexpr const char* T_DIGEST = "Digest"; -static constexpr const char* T_DIGEST_ = "Digest "; -static constexpr const char* T_BEARER = "Bearer"; -static constexpr const char* T_ETag = "ETag"; -static constexpr const char* T_EXPECT = "Expect"; -static constexpr const char* T_HTTP_1_0 = "HTTP/1.0"; -static constexpr const char* T_HTTP_100_CONT = "HTTP/1.1 100 Continue\r\n\r\n"; -static constexpr const char* T_IMS = "If-Modified-Since"; -static constexpr const char* T_INM = "If-None-Match"; -static constexpr const char* T_keep_alive = "keep-alive"; -static constexpr const char* T_Last_Event_ID = "Last-Event-ID"; -static constexpr const char* T_Last_Modified = "Last-Modified"; -static constexpr const char* T_LOCATION = "Location"; -static constexpr const char* T_MULTIPART_ = "multipart/"; -static constexpr const char* T_no_cache = "no-cache"; -static constexpr const char* T_none = "none"; -static constexpr const char* T_UPGRADE = "Upgrade"; -static constexpr const char* T_WS = "websocket"; -static constexpr const char* T_WWW_AUTH = "WWW-Authenticate"; -static constexpr const char* T_Transfer_Encoding = "Transfer-Encoding"; + static constexpr const char* T__opaque = "\", opaque=\""; + static constexpr const char* T_100_CONTINUE = "100-continue"; + static constexpr const char* T_13 = "13"; + static constexpr const char* T_ACCEPT = "accept"; + static constexpr const char* T_Accept_Ranges = "accept-ranges"; + static constexpr const char* T_app_xform_urlencoded = "application/x-www-form-urlencoded"; + static constexpr const char* T_AUTH = "authorization"; + static constexpr const char* T_auth_nonce = "\", qop=\"auth\", nonce=\""; + static constexpr const char* T_BASIC = "basic"; + static constexpr const char* T_BASIC_REALM = "basic realm=\""; + static constexpr const char* T_BEARER = "bearer"; + static constexpr const char* T_BODY = "body"; + static constexpr const char* T_Cache_Control = "cache-control"; + static constexpr const char* T_chunked = "chunked"; + static constexpr const char* T_close = "close"; + static constexpr const char* T_cnonce = "cnonce"; + static constexpr const char* T_Connection = "connection"; + static constexpr const char* T_Content_Disposition = "content-disposition"; + static constexpr const char* T_Content_Encoding = "content-encoding"; + static constexpr const char* T_Content_Length = "content-length"; + static constexpr const char* T_Content_Type = "content-type"; + static constexpr const char* T_Cookie = "cookie"; + static constexpr const char* T_CORS_ACAC = "access-control-allow-credentials"; + static constexpr const char* T_CORS_ACAH = "access-control-allow-headers"; + static constexpr const char* T_CORS_ACAM = "access-control-allow-methods"; + static constexpr const char* T_CORS_ACAO = "access-control-allow-origin"; + static constexpr const char* T_CORS_ACMA = "access-control-max-age"; + static constexpr const char* T_CORS_O = "origin"; + static constexpr const char* T_data_ = "data: "; + static constexpr const char* T_DIGEST = "digest"; + static constexpr const char* T_DIGEST_ = "digest "; + static constexpr const char* T_ETag = "etag"; + static constexpr const char* T_event_ = "event: "; + static constexpr const char* T_EXPECT = "expect"; + static constexpr const char* T_FALSE = "false"; + static constexpr const char* T_filename = "filename"; + static constexpr const char* T_gzip = "gzip"; + static constexpr const char* T_Host = "host"; + static constexpr const char* T_HTTP_1_0 = "HTTP/1.0"; + static constexpr const char* T_HTTP_100_CONT = "HTTP/1.1 100 Continue\r\n\r\n"; + static constexpr const char* T_id__ = "id: "; + static constexpr const char* T_IMS = "if-modified-since"; + static constexpr const char* T_INM = "if-none-match"; + static constexpr const char* T_keep_alive = "keep-alive"; + static constexpr const char* T_Last_Event_ID = "last-event-id"; + static constexpr const char* T_Last_Modified = "last-modified"; + static constexpr const char* T_LOCATION = "location"; + static constexpr const char* T_LOGIN_REQ = "Login Required"; + static constexpr const char* T_MULTIPART_ = "multipart/"; + static constexpr const char* T_name = "name"; + static constexpr const char* T_nc = "nc"; + static constexpr const char* T_no_cache = "no-cache"; + static constexpr const char* T_nonce = "nonce"; + static constexpr const char* T_none = "none"; + static constexpr const char* T_opaque = "opaque"; + static constexpr const char* T_qop = "qop"; + static constexpr const char* T_realm = "realm"; + static constexpr const char* T_realm__ = "realm=\""; + static constexpr const char* T_response = "response"; + static constexpr const char* T_retry_ = "retry: "; + static constexpr const char* T_retry_after = "retry-after"; + static constexpr const char* T_rn = "\r\n"; + static constexpr const char* T_rnrn = "\r\n\r\n"; + static constexpr const char* T_Transfer_Encoding = "transfer-encoding"; + static constexpr const char* T_TRUE = "true"; + static constexpr const char* T_UPGRADE = "upgrade"; + static constexpr const char* T_uri = "uri"; + static constexpr const char* T_username = "username"; + static constexpr const char* T_WS = "websocket"; + static constexpr const char* T_WWW_AUTH = "www-authenticate"; -// HTTP Methods -static constexpr const char* T_ANY = "ANY"; -static constexpr const char* T_GET = "GET"; -static constexpr const char* T_POST = "POST"; -static constexpr const char* T_PUT = "PUT"; -static constexpr const char* T_DELETE = "DELETE"; -static constexpr const char* T_PATCH = "PATCH"; -static constexpr const char* T_HEAD = "HEAD"; -static constexpr const char* T_OPTIONS = "OPTIONS"; -static constexpr const char* T_UNKNOWN = "UNKNOWN"; + // HTTP Methods -// Req content types -static constexpr const char* T_RCT_NOT_USED = "RCT_NOT_USED"; -static constexpr const char* T_RCT_DEFAULT = "RCT_DEFAULT"; -static constexpr const char* T_RCT_HTTP = "RCT_HTTP"; -static constexpr const char* T_RCT_WS = "RCT_WS"; -static constexpr const char* T_RCT_EVENT = "RCT_EVENT"; -static constexpr const char* T_ERROR = "ERROR"; + static constexpr const char* T_ANY = "ANY"; + static constexpr const char* T_GET = "GET"; + static constexpr const char* T_POST = "POST"; + static constexpr const char* T_PUT = "PUT"; + static constexpr const char* T_DELETE = "DELETE"; + static constexpr const char* T_PATCH = "PATCH"; + static constexpr const char* T_HEAD = "HEAD"; + static constexpr const char* T_OPTIONS = "OPTIONS"; + static constexpr const char* T_UNKNOWN = "UNKNOWN"; -// extentions & MIME-Types -static constexpr const char* T__css = ".css"; -static constexpr const char* T__eot = ".eot"; -static constexpr const char* T__gif = ".gif"; -static constexpr const char* T__gz = ".gz"; -static constexpr const char* T__htm = ".htm"; -static constexpr const char* T__html = ".html"; -static constexpr const char* T__ico = ".ico"; -static constexpr const char* T__jpg = ".jpg"; -static constexpr const char* T__js = ".js"; -static constexpr const char* T__json = ".json"; -static constexpr const char* T__pdf = ".pdf"; -static constexpr const char* T__png = ".png"; -static constexpr const char* T__svg = ".svg"; -static constexpr const char* T__ttf = ".ttf"; -static constexpr const char* T__woff = ".woff"; -static constexpr const char* T__woff2 = ".woff2"; -static constexpr const char* T__xml = ".xml"; -static constexpr const char* T__zip = ".zip"; -static constexpr const char* T_application_javascript = "application/javascript"; -static constexpr const char* T_application_json = "application/json"; -static constexpr const char* T_application_msgpack = "application/msgpack"; -static constexpr const char* T_application_pdf = "application/pdf"; -static constexpr const char* T_application_x_gzip = "application/x-gzip"; -static constexpr const char* T_application_zip = "application/zip"; -static constexpr const char* T_font_eot = "font/eot"; -static constexpr const char* T_font_ttf = "font/ttf"; -static constexpr const char* T_font_woff = "font/woff"; -static constexpr const char* T_font_woff2 = "font/woff2"; -static constexpr const char* T_image_gif = "image/gif"; -static constexpr const char* T_image_jpeg = "image/jpeg"; -static constexpr const char* T_image_png = "image/png"; -static constexpr const char* T_image_svg_xml = "image/svg+xml"; -static constexpr const char* T_image_x_icon = "image/x-icon"; -static constexpr const char* T_text_css = "text/css"; -static constexpr const char* T_text_event_stream = "text/event-stream"; -static constexpr const char* T_text_html = "text/html"; -static constexpr const char* T_text_plain = "text/plain"; -static constexpr const char* T_text_xml = "text/xml"; + // Req content types + static constexpr const char* T_RCT_NOT_USED = "RCT_NOT_USED"; + static constexpr const char* T_RCT_DEFAULT = "RCT_DEFAULT"; + static constexpr const char* T_RCT_HTTP = "RCT_HTTP"; + static constexpr const char* T_RCT_WS = "RCT_WS"; + static constexpr const char* T_RCT_EVENT = "RCT_EVENT"; + static constexpr const char* T_ERROR = "ERROR"; -// Responce codes -static constexpr const char* T_HTTP_CODE_100 = "Continue"; -static constexpr const char* T_HTTP_CODE_101 = "Switching Protocols"; -static constexpr const char* T_HTTP_CODE_200 = "OK"; -static constexpr const char* T_HTTP_CODE_201 = "Created"; -static constexpr const char* T_HTTP_CODE_202 = "Accepted"; -static constexpr const char* T_HTTP_CODE_203 = "Non-Authoritative Information"; -static constexpr const char* T_HTTP_CODE_204 = "No Content"; -static constexpr const char* T_HTTP_CODE_205 = "Reset Content"; -static constexpr const char* T_HTTP_CODE_206 = "Partial Content"; -static constexpr const char* T_HTTP_CODE_300 = "Multiple Choices"; -static constexpr const char* T_HTTP_CODE_301 = "Moved Permanently"; -static constexpr const char* T_HTTP_CODE_302 = "Found"; -static constexpr const char* T_HTTP_CODE_303 = "See Other"; -static constexpr const char* T_HTTP_CODE_304 = "Not Modified"; -static constexpr const char* T_HTTP_CODE_305 = "Use Proxy"; -static constexpr const char* T_HTTP_CODE_307 = "Temporary Redirect"; -static constexpr const char* T_HTTP_CODE_400 = "Bad Request"; -static constexpr const char* T_HTTP_CODE_401 = "Unauthorized"; -static constexpr const char* T_HTTP_CODE_402 = "Payment Required"; -static constexpr const char* T_HTTP_CODE_403 = "Forbidden"; -static constexpr const char* T_HTTP_CODE_404 = "Not Found"; -static constexpr const char* T_HTTP_CODE_405 = "Method Not Allowed"; -static constexpr const char* T_HTTP_CODE_406 = "Not Acceptable"; -static constexpr const char* T_HTTP_CODE_407 = "Proxy Authentication Required"; -static constexpr const char* T_HTTP_CODE_408 = "Request Time-out"; -static constexpr const char* T_HTTP_CODE_409 = "Conflict"; -static constexpr const char* T_HTTP_CODE_410 = "Gone"; -static constexpr const char* T_HTTP_CODE_411 = "Length Required"; -static constexpr const char* T_HTTP_CODE_412 = "Precondition Failed"; -static constexpr const char* T_HTTP_CODE_413 = "Request Entity Too Large"; -static constexpr const char* T_HTTP_CODE_414 = "Request-URI Too Large"; -static constexpr const char* T_HTTP_CODE_415 = "Unsupported Media Type"; -static constexpr const char* T_HTTP_CODE_416 = "Requested range not satisfiable"; -static constexpr const char* T_HTTP_CODE_417 = "Expectation Failed"; -static constexpr const char* T_HTTP_CODE_429 = "Too Many Requests"; -static constexpr const char* T_HTTP_CODE_500 = "Internal Server Error"; -static constexpr const char* T_HTTP_CODE_501 = "Not Implemented"; -static constexpr const char* T_HTTP_CODE_502 = "Bad Gateway"; -static constexpr const char* T_HTTP_CODE_503 = "Service Unavailable"; -static constexpr const char* T_HTTP_CODE_504 = "Gateway Time-out"; -static constexpr const char* T_HTTP_CODE_505 = "HTTP Version not supported"; -static constexpr const char* T_HTTP_CODE_ANY = "Unknown code"; + // extentions & MIME-Types + static constexpr const char* T__css = ".css"; + static constexpr const char* T__eot = ".eot"; + static constexpr const char* T__gif = ".gif"; + static constexpr const char* T__gz = ".gz"; + static constexpr const char* T__htm = ".htm"; + static constexpr const char* T__html = ".html"; + static constexpr const char* T__ico = ".ico"; + static constexpr const char* T__jpg = ".jpg"; + static constexpr const char* T__js = ".js"; + static constexpr const char* T__json = ".json"; + static constexpr const char* T__pdf = ".pdf"; + static constexpr const char* T__png = ".png"; + static constexpr const char* T__svg = ".svg"; + static constexpr const char* T__ttf = ".ttf"; + static constexpr const char* T__woff = ".woff"; + static constexpr const char* T__woff2 = ".woff2"; + static constexpr const char* T__xml = ".xml"; + static constexpr const char* T__zip = ".zip"; + static constexpr const char* T_application_javascript = "application/javascript"; + static constexpr const char* T_application_json = "application/json"; + static constexpr const char* T_application_msgpack = "application/msgpack"; + static constexpr const char* T_application_pdf = "application/pdf"; + static constexpr const char* T_application_x_gzip = "application/x-gzip"; + static constexpr const char* T_application_zip = "application/zip"; + static constexpr const char* T_font_eot = "font/eot"; + static constexpr const char* T_font_ttf = "font/ttf"; + static constexpr const char* T_font_woff = "font/woff"; + static constexpr const char* T_font_woff2 = "font/woff2"; + static constexpr const char* T_image_gif = "image/gif"; + static constexpr const char* T_image_jpeg = "image/jpeg"; + static constexpr const char* T_image_png = "image/png"; + static constexpr const char* T_image_svg_xml = "image/svg+xml"; + static constexpr const char* T_image_x_icon = "image/x-icon"; + static constexpr const char* T_text_css = "text/css"; + static constexpr const char* T_text_event_stream = "text/event-stream"; + static constexpr const char* T_text_html = "text/html"; + static constexpr const char* T_text_plain = "text/plain"; + static constexpr const char* T_text_xml = "text/xml"; -// other -static constexpr const char* T__opaque = "\", opaque=\""; -static constexpr const char* T_13 = "13"; -static constexpr const char* T_auth_nonce = "\", qop=\"auth\", nonce=\""; -static constexpr const char* T_cnonce = "cnonce"; -static constexpr const char* T_data_ = "data: "; -static constexpr const char* T_event_ = "event: "; -static constexpr const char* T_filename = "filename"; -static constexpr const char* T_gzip = "gzip"; -static constexpr const char* T_Host = "Host"; -static constexpr const char* T_id__ = "id: "; -static constexpr const char* T_name = "name"; -static constexpr const char* T_nc = "nc"; -static constexpr const char* T_nonce = "nonce"; -static constexpr const char* T_opaque = "opaque"; -static constexpr const char* T_qop = "qop"; -static constexpr const char* T_realm = "realm"; -static constexpr const char* T_realm__ = "realm=\""; -static constexpr const char* T_response = "response"; -static constexpr const char* T_retry_ = "retry: "; -static constexpr const char* T_rn = "\r\n"; -static constexpr const char* T_rnrn = "\r\n\r\n"; -static constexpr const char* T_uri = "uri"; -static constexpr const char* T_username = "username"; + // Responce codes + static constexpr const char* T_HTTP_CODE_100 = "Continue"; + static constexpr const char* T_HTTP_CODE_101 = "Switching Protocols"; + static constexpr const char* T_HTTP_CODE_200 = "OK"; + static constexpr const char* T_HTTP_CODE_201 = "Created"; + static constexpr const char* T_HTTP_CODE_202 = "Accepted"; + static constexpr const char* T_HTTP_CODE_203 = "Non-Authoritative Information"; + static constexpr const char* T_HTTP_CODE_204 = "No Content"; + static constexpr const char* T_HTTP_CODE_205 = "Reset Content"; + static constexpr const char* T_HTTP_CODE_206 = "Partial Content"; + static constexpr const char* T_HTTP_CODE_300 = "Multiple Choices"; + static constexpr const char* T_HTTP_CODE_301 = "Moved Permanently"; + static constexpr const char* T_HTTP_CODE_302 = "Found"; + static constexpr const char* T_HTTP_CODE_303 = "See Other"; + static constexpr const char* T_HTTP_CODE_304 = "Not Modified"; + static constexpr const char* T_HTTP_CODE_305 = "Use Proxy"; + static constexpr const char* T_HTTP_CODE_307 = "Temporary Redirect"; + static constexpr const char* T_HTTP_CODE_400 = "Bad Request"; + static constexpr const char* T_HTTP_CODE_401 = "Unauthorized"; + static constexpr const char* T_HTTP_CODE_402 = "Payment Required"; + static constexpr const char* T_HTTP_CODE_403 = "Forbidden"; + static constexpr const char* T_HTTP_CODE_404 = "Not Found"; + static constexpr const char* T_HTTP_CODE_405 = "Method Not Allowed"; + static constexpr const char* T_HTTP_CODE_406 = "Not Acceptable"; + static constexpr const char* T_HTTP_CODE_407 = "Proxy Authentication Required"; + static constexpr const char* T_HTTP_CODE_408 = "Request Time-out"; + static constexpr const char* T_HTTP_CODE_409 = "Conflict"; + static constexpr const char* T_HTTP_CODE_410 = "Gone"; + static constexpr const char* T_HTTP_CODE_411 = "Length Required"; + static constexpr const char* T_HTTP_CODE_412 = "Precondition Failed"; + static constexpr const char* T_HTTP_CODE_413 = "Request Entity Too Large"; + static constexpr const char* T_HTTP_CODE_414 = "Request-URI Too Large"; + static constexpr const char* T_HTTP_CODE_415 = "Unsupported Media Type"; + static constexpr const char* T_HTTP_CODE_416 = "Requested range not satisfiable"; + static constexpr const char* T_HTTP_CODE_417 = "Expectation Failed"; + static constexpr const char* T_HTTP_CODE_429 = "Too Many Requests"; + static constexpr const char* T_HTTP_CODE_500 = "Internal Server Error"; + static constexpr const char* T_HTTP_CODE_501 = "Not Implemented"; + static constexpr const char* T_HTTP_CODE_502 = "Bad Gateway"; + static constexpr const char* T_HTTP_CODE_503 = "Service Unavailable"; + static constexpr const char* T_HTTP_CODE_504 = "Gateway Time-out"; + static constexpr const char* T_HTTP_CODE_505 = "HTTP Version not supported"; + static constexpr const char* T_HTTP_CODE_ANY = "Unknown code"; - -#else // ESP8622 - -static const char T_100_CONTINUE[] PROGMEM = "100-continue"; -static const char T_ACCEPT[] PROGMEM = "Accept"; -static const char T_Accept_Ranges[] PROGMEM = "Accept-Ranges"; -static const char T_app_xform_urlencoded[] PROGMEM = "application/x-www-form-urlencoded"; -static const char T_AUTH[] PROGMEM = "Authorization"; -static const char T_BASIC[] PROGMEM = "Basic"; -static const char T_BASIC_REALM[] PROGMEM = "Basic realm=\""; -static const char T_LOGIN_REQ[] PROGMEM = "Login Required"; -static const char T_BODY[] PROGMEM = "body"; -static const char T_Cache_Control[] PROGMEM = "Cache-Control"; -static const char T_chunked[] PROGMEM = "chunked"; -static const char T_close[] PROGMEM = "close"; -static const char T_Connection[] PROGMEM = "Connection"; -static const char T_Content_Disposition[] PROGMEM = "Content-Disposition"; -static const char T_Content_Encoding[] PROGMEM = "Content-Encoding"; -static const char T_Content_Length[] PROGMEM = "Content-Length"; -static const char T_Content_Type[] PROGMEM = "Content-Type"; -static const char T_Cookie[] PROGMEM = "Cookie"; -static const char T_DIGEST[] PROGMEM = "Digest"; -static const char T_DIGEST_[] PROGMEM = "Digest "; -static const char T_BEARER[] PROGMEM = "Bearer"; -static const char T_ETag[] PROGMEM = "ETag"; -static const char T_EXPECT[] PROGMEM = "Expect"; -static const char T_HTTP_1_0[] PROGMEM = "HTTP/1.0"; -static const char T_HTTP_100_CONT[] PROGMEM = "HTTP/1.1 100 Continue\r\n\r\n"; -static const char T_IMS[] PROGMEM = "If-Modified-Since"; -static const char T_INM[] PROGMEM = "If-None-Match"; -static const char T_keep_alive[] PROGMEM = "keep-alive"; -static const char T_Last_Event_ID[] PROGMEM = "Last-Event-ID"; -static const char T_Last_Modified[] PROGMEM = "Last-Modified"; -static const char T_LOCATION[] PROGMEM = "Location"; -static const char T_MULTIPART_[] PROGMEM = "multipart/"; -static const char T_no_cache[] PROGMEM = "no-cache"; -static const char T_none[] PROGMEM = "none"; -static const char T_UPGRADE[] PROGMEM = "Upgrade"; -static const char T_WS[] PROGMEM = "websocket"; -static const char T_WWW_AUTH[] PROGMEM = "WWW-Authenticate"; -static const char T_Transfer_Encoding[] PROGMEM = "Transfer-Encoding"; - -// HTTP Methods -static const char T_ANY[] PROGMEM = "ANY"; -static const char T_GET[] PROGMEM = "GET"; -static const char T_POST[] PROGMEM = "POST"; -static const char T_PUT[] PROGMEM = "PUT"; -static const char T_DELETE[] PROGMEM = "DELETE"; -static const char T_PATCH[] PROGMEM = "PATCH"; -static const char T_HEAD[] PROGMEM = "HEAD"; -static const char T_OPTIONS[] PROGMEM = "OPTIONS"; -static const char T_UNKNOWN[] PROGMEM = "UNKNOWN"; - -// Req content types -static const char T_RCT_NOT_USED[] PROGMEM = "RCT_NOT_USED"; -static const char T_RCT_DEFAULT[] PROGMEM = "RCT_DEFAULT"; -static const char T_RCT_HTTP[] PROGMEM = "RCT_HTTP"; -static const char T_RCT_WS[] PROGMEM = "RCT_WS"; -static const char T_RCT_EVENT[] PROGMEM = "RCT_EVENT"; -static const char T_ERROR[] PROGMEM = "ERROR"; - -// extentions & MIME-Types -static const char T__css[] PROGMEM = ".css"; -static const char T__eot[] PROGMEM = ".eot"; -static const char T__gif[] PROGMEM = ".gif"; -static const char T__gz[] PROGMEM = ".gz"; -static const char T__htm[] PROGMEM = ".htm"; -static const char T__html[] PROGMEM = ".html"; -static const char T__ico[] PROGMEM = ".ico"; -static const char T__jpg[] PROGMEM = ".jpg"; -static const char T__js[] PROGMEM = ".js"; -static const char T__json[] PROGMEM = ".json"; -static const char T__pdf[] PROGMEM = ".pdf"; -static const char T__png[] PROGMEM = ".png"; -static const char T__svg[] PROGMEM = ".svg"; -static const char T__ttf[] PROGMEM = ".ttf"; -static const char T__woff[] PROGMEM = ".woff"; -static const char T__woff2[] PROGMEM = ".woff2"; -static const char T__xml[] PROGMEM = ".xml"; -static const char T__zip[] PROGMEM = ".zip"; -static const char T_application_javascript[] PROGMEM = "application/javascript"; -static const char T_application_json[] PROGMEM = "application/json"; -static const char T_application_msgpack[] PROGMEM = "application/msgpack"; -static const char T_application_pdf[] PROGMEM = "application/pdf"; -static const char T_application_x_gzip[] PROGMEM = "application/x-gzip"; -static const char T_application_zip[] PROGMEM = "application/zip"; -static const char T_font_eot[] PROGMEM = "font/eot"; -static const char T_font_ttf[] PROGMEM = "font/ttf"; -static const char T_font_woff[] PROGMEM = "font/woff"; -static const char T_font_woff2[] PROGMEM = "font/woff2"; -static const char T_image_gif[] PROGMEM = "image/gif"; -static const char T_image_jpeg[] PROGMEM = "image/jpeg"; -static const char T_image_png[] PROGMEM = "image/png"; -static const char T_image_svg_xml[] PROGMEM = "image/svg+xml"; -static const char T_image_x_icon[] PROGMEM = "image/x-icon"; -static const char T_text_css[] PROGMEM = "text/css"; -static const char T_text_event_stream[] PROGMEM = "text/event-stream"; -static const char T_text_html[] PROGMEM = "text/html"; -static const char T_text_plain[] PROGMEM = "text/plain"; -static const char T_text_xml[] PROGMEM = "text/xml"; - -// Responce codes -static const char T_HTTP_CODE_100[] PROGMEM = "Continue"; -static const char T_HTTP_CODE_101[] PROGMEM = "Switching Protocols"; -static const char T_HTTP_CODE_200[] PROGMEM = "OK"; -static const char T_HTTP_CODE_201[] PROGMEM = "Created"; -static const char T_HTTP_CODE_202[] PROGMEM = "Accepted"; -static const char T_HTTP_CODE_203[] PROGMEM = "Non-Authoritative Information"; -static const char T_HTTP_CODE_204[] PROGMEM = "No Content"; -static const char T_HTTP_CODE_205[] PROGMEM = "Reset Content"; -static const char T_HTTP_CODE_206[] PROGMEM = "Partial Content"; -static const char T_HTTP_CODE_300[] PROGMEM = "Multiple Choices"; -static const char T_HTTP_CODE_301[] PROGMEM = "Moved Permanently"; -static const char T_HTTP_CODE_302[] PROGMEM = "Found"; -static const char T_HTTP_CODE_303[] PROGMEM = "See Other"; -static const char T_HTTP_CODE_304[] PROGMEM = "Not Modified"; -static const char T_HTTP_CODE_305[] PROGMEM = "Use Proxy"; -static const char T_HTTP_CODE_307[] PROGMEM = "Temporary Redirect"; -static const char T_HTTP_CODE_400[] PROGMEM = "Bad Request"; -static const char T_HTTP_CODE_401[] PROGMEM = "Unauthorized"; -static const char T_HTTP_CODE_402[] PROGMEM = "Payment Required"; -static const char T_HTTP_CODE_403[] PROGMEM = "Forbidden"; -static const char T_HTTP_CODE_404[] PROGMEM = "Not Found"; -static const char T_HTTP_CODE_405[] PROGMEM = "Method Not Allowed"; -static const char T_HTTP_CODE_406[] PROGMEM = "Not Acceptable"; -static const char T_HTTP_CODE_407[] PROGMEM = "Proxy Authentication Required"; -static const char T_HTTP_CODE_408[] PROGMEM = "Request Time-out"; -static const char T_HTTP_CODE_409[] PROGMEM = "Conflict"; -static const char T_HTTP_CODE_410[] PROGMEM = "Gone"; -static const char T_HTTP_CODE_411[] PROGMEM = "Length Required"; -static const char T_HTTP_CODE_412[] PROGMEM = "Precondition Failed"; -static const char T_HTTP_CODE_413[] PROGMEM = "Request Entity Too Large"; -static const char T_HTTP_CODE_414[] PROGMEM = "Request-URI Too Large"; -static const char T_HTTP_CODE_415[] PROGMEM = "Unsupported Media Type"; -static const char T_HTTP_CODE_416[] PROGMEM = "Requested range not satisfiable"; -static const char T_HTTP_CODE_417[] PROGMEM = "Expectation Failed"; -static const char T_HTTP_CODE_429[] PROGMEM = "Too Many Requests"; -static const char T_HTTP_CODE_500[] PROGMEM = "Internal Server Error"; -static const char T_HTTP_CODE_501[] PROGMEM = "Not Implemented"; -static const char T_HTTP_CODE_502[] PROGMEM = "Bad Gateway"; -static const char T_HTTP_CODE_503[] PROGMEM = "Service Unavailable"; -static const char T_HTTP_CODE_504[] PROGMEM = "Gateway Time-out"; -static const char T_HTTP_CODE_505[] PROGMEM = "HTTP Version not supported"; -static const char T_HTTP_CODE_ANY[] PROGMEM = "Unknown code"; - -// other -static const char T__opaque[] PROGMEM = "\", opaque=\""; -static const char T_13[] PROGMEM = "13"; -static const char T_auth_nonce[] PROGMEM = "\", qop=\"auth\", nonce=\""; -static const char T_cnonce[] PROGMEM = "cnonce"; -static const char T_data_[] PROGMEM = "data: "; -static const char T_event_[] PROGMEM = "event: "; -static const char T_filename[] PROGMEM = "filename"; -static const char T_gzip[] PROGMEM = "gzip"; -static const char T_Host[] PROGMEM = "Host"; -static const char T_id__[] PROGMEM = "id: "; -static const char T_name[] PROGMEM = "name"; -static const char T_nc[] PROGMEM = "nc"; -static const char T_nonce[] PROGMEM = "nonce"; -static const char T_opaque[] PROGMEM = "opaque"; -static const char T_qop[] PROGMEM = "qop"; -static const char T_realm[] PROGMEM = "realm"; -static const char T_realm__[] PROGMEM = "realm=\""; -static const char T_response[] PROGMEM = "response"; -static const char T_retry_[] PROGMEM = "retry: "; -static const char T_rn[] PROGMEM = "\r\n"; -static const char T_rnrn[] PROGMEM = "\r\n\r\n"; -static const char T_uri[] PROGMEM = "uri"; -static const char T_username[] PROGMEM = "username"; - -#endif // ESP8622 - -} // namespace asyncsrv {} +} // namespace asyncsrv {}