Aktualizace na verzi 3.3.23
This commit is contained in:
		
							
								
								
									
										130
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										130
									
								
								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) | ||||
|  | ||||
| With `https://github.com/mathieucarbou/AsyncTCPSock/archive/refs/tags/v1.0.3-dev.zip`: | ||||
|  | ||||
| [](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>(); | ||||
|   JsonObject jsonObj = json.as<JsonObject>(); | ||||
|   // ... | ||||
| }); | ||||
| 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 | ||||
|   | ||||
							
								
								
									
										128
									
								
								docs/index.md
									
									
									
									
									
								
							
							
						
						
									
										128
									
								
								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) | ||||
|  | ||||
| With `https://github.com/mathieucarbou/AsyncTCPSock/archive/refs/tags/v1.0.3-dev.zip`: | ||||
|  | ||||
| [](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 | ||||
|   | ||||
							
								
								
									
										
											BIN
										
									
								
								docs/perf-c10-asynctcpsock.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								docs/perf-c10-asynctcpsock.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 310 KiB | 
							
								
								
									
										
											BIN
										
									
								
								docs/perf-c10.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								docs/perf-c10.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 295 KiB | 
| @@ -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; | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -1,37 +0,0 @@ | ||||
| #include "mbedtls/md5.h" | ||||
| #include <Arduino.h> | ||||
| #include <MD5Builder.h> | ||||
|  | ||||
| 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() { | ||||
| } | ||||
| @@ -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("<!DOCTYPE html><html><head><title>Captive Portal</title></head><body>"); | ||||
|       response->print("<p>This is out captive portal front page.</p>"); | ||||
|   | ||||
							
								
								
									
										84
									
								
								examples/Issue162/Issue162.ino
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										84
									
								
								examples/Issue162/Issue162.ino
									
									
									
									
									
										Normal file
									
								
							| @@ -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 <Arduino.h> | ||||
| #ifdef ESP8266 | ||||
|   #include <ESP8266WiFi.h> | ||||
| #endif | ||||
| #ifdef ESP32 | ||||
|   #include <WiFi.h> | ||||
| #endif | ||||
| #include <ESPAsyncWebServer.h> | ||||
|  | ||||
| #include <list> | ||||
| 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( | ||||
|     <!DOCTYPE html> | ||||
|         <html> | ||||
|         <head></head> | ||||
|         <script>  | ||||
|             let ws = new WebSocket("ws://" + window.location.host + "/ws"); | ||||
|                      | ||||
|             document.addEventListener("DOMContentLoaded", function () { | ||||
|                 ws.onopen = function () { | ||||
|                     console.log("WebSocket connected"); | ||||
|                     }; | ||||
|                 }); | ||||
|                          | ||||
|             function closeAllWsClients() { | ||||
|               fetch("/close_all_ws_clients", { | ||||
|                   method : "POST", | ||||
|               }); | ||||
|             }; | ||||
|         </script> | ||||
|         <body> | ||||
|             <button onclick = "closeAllWsClients()" style = "width: 200px"> ws close all</button><p></p>   | ||||
|           </body> | ||||
|         </html> | ||||
|   )rawliteral"); | ||||
|   }); | ||||
|  | ||||
|   server.begin(); | ||||
| } | ||||
|  | ||||
| void loop() { | ||||
|   vTaskDelete(NULL); | ||||
| } | ||||
							
								
								
									
										127
									
								
								examples/Issue85/Issue85.ino
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										127
									
								
								examples/Issue85/Issue85.ino
									
									
									
									
									
										Normal file
									
								
							| @@ -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 <Arduino.h> | ||||
| #ifdef ESP8266 | ||||
|   #include <ESP8266WiFi.h> | ||||
| #endif | ||||
| #ifdef ESP32 | ||||
|   #include <WiFi.h> | ||||
| #endif | ||||
| #include <ESPAsyncWebServer.h> | ||||
|  | ||||
| #include <list> | ||||
|  | ||||
| size_t msgCount = 0; | ||||
| size_t window = 100; | ||||
| std::list<uint32_t> 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'; | ||||
|   //   } | ||||
|   // } | ||||
| } | ||||
| @@ -19,7 +19,7 @@ | ||||
|  | ||||
| #include <ESPAsyncWebServer.h> | ||||
|  | ||||
| #if ASYNC_JSON_SUPPORT == 1 | ||||
| #if __has_include("ArduinoJson.h") | ||||
|   #include <ArduinoJson.h> | ||||
|   #include <AsyncJson.h> | ||||
|   #include <AsyncMessagePack.h> | ||||
| @@ -27,6 +27,80 @@ | ||||
|  | ||||
| #include <LittleFS.h> | ||||
|  | ||||
| const char* htmlContent PROGMEM = R"( | ||||
| <!DOCTYPE html> | ||||
| <html> | ||||
| <head> | ||||
|     <title>Sample HTML</title> | ||||
| </head> | ||||
| <body> | ||||
|     <h1>Hello, World!</h1> | ||||
|     <p>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.</p> | ||||
|     <p>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.</p> | ||||
|     <p>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.</p> | ||||
|     <p>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.</p> | ||||
|     <p>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.</p> | ||||
|     <p>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.</p> | ||||
|     <p>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.</p> | ||||
|     <p>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.</p> | ||||
| </body> | ||||
| </html> | ||||
| )"; | ||||
|  | ||||
| const size_t htmlContentLength = strlen_P(htmlContent); | ||||
|  | ||||
| const char* staticContent PROGMEM = R"( | ||||
| <!DOCTYPE html> | ||||
| <html> | ||||
| <head> | ||||
|     <title>Sample HTML</title> | ||||
| </head> | ||||
| <body> | ||||
|     <h1>Hello, %IP%</h1> | ||||
| </body> | ||||
| </html> | ||||
| )"; | ||||
|  | ||||
| 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<JsonObject>(); | ||||
|     // ... | ||||
|  | ||||
|   // 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<JsonObject>(); | ||||
|     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<JsonObject>(); | ||||
|     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<JsonObject>(); | ||||
|     root["hello"] = "world"; | ||||
|     root["hello"] = json.as<JsonObject>()["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(); | ||||
|   } | ||||
|   | ||||
| @@ -10,11 +10,13 @@ | ||||
|   #include <WebServer.h> | ||||
|   #include <WiFi.h> | ||||
| #endif | ||||
| #include "StreamConcat.h" | ||||
| #include "StreamString.h" | ||||
|  | ||||
| #include <StreamString.h> | ||||
| #include <ESPAsyncWebServer.h> | ||||
| #include <LittleFS.h> | ||||
|  | ||||
| #include "StreamConcat.h" | ||||
|  | ||||
| DNSServer dnsServer; | ||||
| AsyncWebServer server(80); | ||||
|  | ||||
|   | ||||
| @@ -1,40 +0,0 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include <Stream.h> | ||||
|  | ||||
| class StreamString : public Stream { | ||||
|   public: | ||||
|     size_t write(const uint8_t* p, size_t n) override { return _buffer.concat(reinterpret_cast<const char*>(p), n) ? n : 0; } | ||||
|     size_t write(uint8_t c) override { return _buffer.concat(static_cast<char>(c)) ? 1 : 0; } | ||||
|     void flush() override {} | ||||
|  | ||||
|     int available() override { return static_cast<int>(_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<unsigned int>(length)); | ||||
|       return length; | ||||
|     } | ||||
|  | ||||
|     int peek() override { return _buffer.length() > 0 ? _buffer[0] : -1; } | ||||
|  | ||||
|     const String& buffer() const { return _buffer; } | ||||
|  | ||||
|   private: | ||||
|     String _buffer; | ||||
| }; | ||||
| @@ -1,107 +0,0 @@ | ||||
| #include <DNSServer.h> | ||||
| #ifdef ESP32 | ||||
|   #include <AsyncTCP.h> | ||||
|   #include <WiFi.h> | ||||
| #elif defined(ESP8266) | ||||
|   #include <ESP8266WiFi.h> | ||||
|   #include <ESPAsyncTCP.h> | ||||
| #elif defined(TARGET_RP2040) | ||||
|   #include <WebServer.h> | ||||
|   #include <WiFi.h> | ||||
| #endif | ||||
|  | ||||
| #include "ESPAsyncWebServer.h" | ||||
|  | ||||
| const char appWebPage[] PROGMEM = R"rawliteral( | ||||
| <body> | ||||
| <button id="button1" onclick="fetch('/button1');">Relay1</button> | ||||
| <script> | ||||
|   const evtSource = new EventSource("/events"); | ||||
|   button1 = document.getElementById("button1"); | ||||
|   evtSource.addEventListener('state', (e) => { | ||||
|       const data = JSON.parse(e.data); | ||||
|       console.log('Event Source data: ', data); | ||||
|       if (data.button1) { | ||||
|           button1.style.backgroundColor = "green"; | ||||
|       } | ||||
|       else { | ||||
|           button1.style.backgroundColor = "red"; | ||||
|       } | ||||
|   }); | ||||
| </script> | ||||
| </body> | ||||
| )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(); | ||||
|   } | ||||
| } | ||||
| @@ -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" | ||||
|     }, | ||||
|     { | ||||
|   | ||||
| @@ -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 <mathieu.carbou@gmail.com> | ||||
| 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 | ||||
							
								
								
									
										7
									
								
								partitions-4MB.csv
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								partitions-4MB.csv
									
									
									
									
									
										Normal file
									
								
							| @@ -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   , | ||||
| 
 | 
| @@ -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 | ||||
|   | ||||
| @@ -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<std::mutex> 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<std::mutex> 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<std::mutex> 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) { | ||||
|   | ||||
| @@ -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 { | ||||
|   | ||||
| @@ -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; | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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; | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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<std::mutex> 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<std::mutex> 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<std::mutex> 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<std::mutex> 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<std::mutex> 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<std::mutex> 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<PGM_P>(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<PGM_P>(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<PGM_P>(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<PGM_P>(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<PGM_P>(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<PGM_P>(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) { | ||||
|   | ||||
| @@ -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); | ||||
|   | ||||
| @@ -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); } | ||||
| }; | ||||
|   | ||||
| @@ -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<size_t(uint8_t*, size_t, size_t)> 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<void(AsyncWebServerRequest* request, | ||||
| class AsyncMiddleware { | ||||
|   public: | ||||
|     virtual ~AsyncMiddleware() {} | ||||
|     virtual void run(__unused AsyncWebServerRequest* request, __unused ArMiddlewareNext next) { | ||||
|       return next(); | ||||
|     }; | ||||
|     virtual void run(__unused AsyncWebServerRequest* request, __unused ArMiddlewareNext next) { return next(); }; | ||||
|  | ||||
|   private: | ||||
|     friend class AsyncWebHandler; | ||||
| @@ -547,7 +543,7 @@ class AsyncMiddlewareFunction : public AsyncMiddleware { | ||||
| // For internal use only: super class to add/remove middleware to server or handlers | ||||
| class AsyncMiddlewareChain { | ||||
|   public: | ||||
|     virtual ~AsyncMiddlewareChain(); | ||||
|     ~AsyncMiddlewareChain(); | ||||
|  | ||||
|     void addMiddleware(ArMiddlewareCallback fn); | ||||
|     void addMiddleware(AsyncMiddleware* middleware); | ||||
| @@ -570,13 +566,26 @@ class AuthenticationMiddleware : public AsyncMiddleware { | ||||
|  | ||||
|     void setRealm(const char* realm) { _realm = realm; } | ||||
|     void setAuthFailureMessage(const char* message) { _authFailMsg = message; } | ||||
|  | ||||
|     // set the authentication method to use | ||||
|     // default is AUTH_NONE: no authentication required | ||||
|     // AUTH_BASIC: basic authentication | ||||
|     // AUTH_DIGEST: digest authentication | ||||
|     // AUTH_BEARER: bearer token authentication | ||||
|     // AUTH_OTHER: other authentication method | ||||
|     // AUTH_DENIED: always return 401 Unauthorized | ||||
|     // if a method is set but no username or password is set, authentication will be ignored | ||||
|     void setAuthType(AsyncAuthType authMethod) { _authMethod = authMethod; } | ||||
|  | ||||
|     // precompute and store the hash value based on the username, realm, and authMethod | ||||
|     // precompute and store the hash value based on the username, password, realm. | ||||
|     // can be used for DIGEST and BASIC to avoid recomputing the hash for each request. | ||||
|     // returns true if the hash was successfully generated and replaced | ||||
|     bool generateHash(); | ||||
|  | ||||
|     bool allowed(AsyncWebServerRequest* request); | ||||
|     // returns true if the username and password (or hash) are set | ||||
|     bool hasCredentials() const { return _hasCreds; } | ||||
|  | ||||
|     bool allowed(AsyncWebServerRequest* request) const; | ||||
|  | ||||
|     void run(AsyncWebServerRequest* request, ArMiddlewareNext next); | ||||
|  | ||||
| @@ -634,7 +643,7 @@ class LoggingMiddleware : public AsyncMiddleware { | ||||
|   public: | ||||
|     void setOutput(Print& output) { _out = &output; } | ||||
|     void setEnabled(bool enabled) { _enabled = enabled; } | ||||
|     bool isEnabled() { return _enabled && _out; } | ||||
|     bool isEnabled() const { return _enabled && _out; } | ||||
|  | ||||
|     void run(AsyncWebServerRequest* request, ArMiddlewareNext next); | ||||
|  | ||||
| @@ -722,18 +731,16 @@ class AsyncWebHandler : public AsyncMiddlewareChain { | ||||
|  | ||||
|   public: | ||||
|     AsyncWebHandler() {} | ||||
|     AsyncWebHandler& setFilter(ArRequestFilterFunction fn); | ||||
|     AsyncWebHandler& setAuthentication(const char* username, const char* password); | ||||
|     AsyncWebHandler& setAuthentication(const String& username, const String& password) { return setAuthentication(username.c_str(), password.c_str()); }; | ||||
|     bool filter(AsyncWebServerRequest* request) { return _filter == NULL || _filter(request); } | ||||
|     virtual ~AsyncWebHandler() {} | ||||
|     virtual bool canHandle(AsyncWebServerRequest* request __attribute__((unused))) { | ||||
|       return false; | ||||
|     } | ||||
|     AsyncWebHandler& setFilter(ArRequestFilterFunction fn); | ||||
|     AsyncWebHandler& setAuthentication(const char* username, const char* password, AsyncAuthType authMethod = AsyncAuthType::AUTH_DIGEST); | ||||
|     AsyncWebHandler& setAuthentication(const String& username, const String& password, AsyncAuthType authMethod = AsyncAuthType::AUTH_DIGEST) { return setAuthentication(username.c_str(), password.c_str(), authMethod); }; | ||||
|     bool filter(AsyncWebServerRequest* request) { return _filter == NULL || _filter(request); } | ||||
|     virtual bool canHandle(AsyncWebServerRequest* request __attribute__((unused))) const { return false; } | ||||
|     virtual void handleRequest(__unused AsyncWebServerRequest* request) {} | ||||
|     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) {} | ||||
|     virtual void handleBody(__unused AsyncWebServerRequest* request, __unused uint8_t* data, __unused size_t len, __unused size_t index, __unused size_t total) {} | ||||
|     virtual bool isRequestHandlerTrivial() { return true; } | ||||
|     virtual bool isRequestHandlerTrivial() const { return true; } | ||||
| }; | ||||
|  | ||||
| /* | ||||
| @@ -764,26 +771,22 @@ class AsyncWebServerResponse { | ||||
|     WebResponseState _state; | ||||
|  | ||||
|   public: | ||||
| #ifndef ESP8266 | ||||
|     static const char* responseCodeToString(int code); | ||||
| #else | ||||
|     static const __FlashStringHelper* responseCodeToString(int code); | ||||
| #endif | ||||
|  | ||||
|   public: | ||||
|     AsyncWebServerResponse(); | ||||
|     virtual ~AsyncWebServerResponse(); | ||||
|     virtual void setCode(int code); | ||||
|     virtual ~AsyncWebServerResponse() {} | ||||
|     void setCode(int code); | ||||
|     int code() const { return _code; } | ||||
|     virtual void setContentLength(size_t len); | ||||
|     void setContentLength(size_t len); | ||||
|     void setContentType(const String& type) { setContentType(type.c_str()); } | ||||
|     virtual void setContentType(const char* type); | ||||
|     virtual bool addHeader(const char* name, const char* value, bool replaceExisting = true); | ||||
|     void setContentType(const char* type); | ||||
|     bool addHeader(const char* name, const char* value, bool replaceExisting = true); | ||||
|     bool addHeader(const String& name, const String& value, bool replaceExisting = true) { return addHeader(name.c_str(), value.c_str(), replaceExisting); } | ||||
|     bool addHeader(const char* name, long value, bool replaceExisting = true) { return addHeader(name, String(value), replaceExisting); } | ||||
|     bool addHeader(const String& name, long value, bool replaceExisting = true) { return addHeader(name.c_str(), value, replaceExisting); } | ||||
|     virtual bool removeHeader(const char* name); | ||||
|     virtual const AsyncWebHeader* getHeader(const char* name) const; | ||||
|     bool removeHeader(const char* name); | ||||
|     const AsyncWebHeader* getHeader(const char* name) const; | ||||
|     const std::list<AsyncWebHeader>& 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; | ||||
|   | ||||
| @@ -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); | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -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"); | ||||
|   | ||||
| @@ -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); | ||||
|  | ||||
|   | ||||
| @@ -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_ */ | ||||
|   | ||||
| @@ -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<AsyncStaticWebHandler*>(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<AsyncStaticWebHandler*>(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 | ||||
|   | ||||
| @@ -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)); | ||||
| } | ||||
|   | ||||
| @@ -26,9 +26,10 @@ | ||||
|   #undef min | ||||
|   #undef max | ||||
| #endif | ||||
| #include "literals.h" | ||||
| #include <StreamString.h> | ||||
| #include <memory> | ||||
| #include <vector> | ||||
| #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<cbuf> _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; | ||||
|   | ||||
| @@ -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<cbuf>(new cbuf(bufferSize)); // std::make_unique<cbuf>(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; | ||||
| } | ||||
|   | ||||
							
								
								
									
										507
									
								
								src/literals.h
									
									
									
									
									
								
							
							
						
						
									
										507
									
								
								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 {} | ||||
|   | ||||
		Reference in New Issue
	
	Block a user