Aktualizace na verzi 3.3.23

This commit is contained in:
Pavel Brychta 2024-12-05 10:21:12 +01:00
parent 2333497adc
commit 7c828c70d8
36 changed files with 1436 additions and 1204 deletions

130
README.md
View File

@ -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. 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) - [Changes in this fork](#changes-in-this-fork)
- [Dependencies](#dependencies)
- [Performance](#performance)
- [Important recommendations](#important-recommendations) - [Important recommendations](#important-recommendations)
- [`AsyncWebSocketMessageBuffer` and `makeBuffer()`](#asyncwebsocketmessagebuffer-and-makebuffer) - [`AsyncWebSocketMessageBuffer` and `makeBuffer()`](#asyncwebsocketmessagebuffer-and-makebuffer)
- [How to replace a response](#how-to-replace-a-response) - [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) - [Migration to Middleware to improve performance and memory usage](#migration-to-middleware-to-improve-performance-and-memory-usage)
- [Original Documentation](#original-documentation) - [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 ## Changes in this fork
- (bug) A lot of bug fixes - (bug) A lot of bug fixes
@ -55,6 +42,7 @@ Dependency:
- (feat) **Resumable download** support using HEAD and bytes range - (feat) **Resumable download** support using HEAD and bytes range
- (feat) `StreamConcat` example to show how to stream multiple files in one response - (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) - (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) `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) `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 - (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) Lot of code cleanup and optimizations
- (perf) Performance improvements in terms of memory, speed and size - (perf) Performance improvements in terms of memory, speed and size
---
## WARNING: Important notes about future version 4.x
This ESPAsyncWebServer fork is now at version 3.x.
Next version 4.x will:
1. Drop support for ESP8266, which goes EOL in a few years. All ESP8266 boards can be replaced by equivalent ESP32 boards.
2. Drop support for Arduino 2.x and ESP-IDF 4.x. The library will be compatible with Arduino 3.x and ESP-IDF 5.x.
3. Drop support for ArduinoJson 5.x and 6.x. The library will be compatible with ArduinoJson 7.x.
So if you need one of these feature, you will have to stick with 3.x or another fork.
## Dependencies
**WARNING** The library name was changed from `ESP Async WebServer` to `ESPAsyncWebServer` as per the Arduino Lint recommendations, but its name had to stay `ESP Async WebServer` in Arduino Registry.
**PlatformIO / pioarduino:**
```ini
lib_compat_mode = strict
lib_ldf_mode = chain
lib_deps = mathieucarbou/ESPAsyncWebServer @ 3.3.23
```
**Dependencies:**
- **ESP32 with AsyncTCP**: `mathieucarbou/AsyncTCP @ 3.2.14`
Arduino IDE: [https://github.com/mathieucarbou/AsyncTCP#v3.2.14](https://github.com/mathieucarbou/AsyncTCP/releases)
- **ESP32 with AsyncTCPSock**: `https://github.com/mathieucarbou/AsyncTCPSock/archive/refs/tags/v1.0.3-dev.zip`
- **ESP8266**: `esphome/ESPAsyncTCP-esphome @ 2.0.0`
Arduino IDE: [https://github.com/mathieucarbou/esphome-ESPAsyncTCP#v2.0.0](https://github.com/mathieucarbou/esphome-ESPAsyncTCP/releases/tag/v2.0.0)
- **RP2040**: `khoih-prog/AsyncTCP_RP2040W @ 1.2.0`
Arduino IDE: [https://github.com/khoih-prog/AsyncTCP_RP2040W#v1.2.0](https://github.com/khoih-prog/AsyncTCP_RP2040W/releases/tag/v1.2.0)
**AsyncTCPSock**
AsyncTCPSock can be used instead of AsyncTCP by excluding AsyncTCP from the library dependencies and adding AsyncTCPSock instead:
```ini
lib_compat_mode = strict
lib_ldf_mode = chain
lib_deps =
; mathieucarbou/AsyncTCP @ 3.2.14
https://github.com/mathieucarbou/AsyncTCPSock/archive/refs/tags/v1.0.3-dev.zip
mathieucarbou/ESPAsyncWebServer @ 3.3.23
lib_ignore =
AsyncTCP
mathieucarbou/AsyncTCP
```
## Performance
Performance of `mathieucarbou/ESPAsyncWebServer @ 3.3.23`:
```bash
> brew install autocannon
> autocannon -c 10 -w 10 -d 20 http://192.168.4.1
```
With `mathieucarbou/AsyncTCP @ 3.2.14`
[![](https://mathieu.carbou.me/ESPAsyncWebServer/perf-c10.png)](https://mathieu.carbou.me/ESPAsyncWebServer/perf-c10.png)
With `https://github.com/mathieucarbou/AsyncTCPSock/archive/refs/tags/v1.0.3-dev.zip`:
[![](https://mathieu.carbou.me/ESPAsyncWebServer/perf-c10-asynctcpsock.png)](https://mathieu.carbou.me/ESPAsyncWebServer/perf-c10-asynctcpsock.png)
**SSE performance**
In the example, there is an endpoint `/events` with some comments showing how these metrics are calculated.
Test is running for 20 seconds with 10 connections.
```
// With AsyncTCP, with 10 workers: no message discarded from the queue
//
// Total: 1875 events, 468.75000000000000000000 events / second
// Total: 1870 events, 467.50000000000000000000 events / second
// Total: 1871 events, 467.75000000000000000000 events / second
// Total: 1875 events, 468.75000000000000000000 events / second
// Total: 1871 events, 467.75000000000000000000 events / second
// Total: 1805 events, 451.25000000000000000000 events / second
// Total: 1803 events, 450.75000000000000000000 events / second
// Total: 1873 events, 468.25000000000000000000 events / second
// Total: 1872 events, 468.00000000000000000000 events / second
// Total: 1805 events, 451.25000000000000000000 events / second
//
// With AsyncTCPSock, with 10 workers: no message discarded from the queue
//
// Total: 1242 events, 310.50000000000000000000 events / second
// Total: 1242 events, 310.50000000000000000000 events / second
// Total: 1242 events, 310.50000000000000000000 events / second
// Total: 1242 events, 310.50000000000000000000 events / second
// Total: 1181 events, 295.25000000000000000000 events / second
// Total: 1182 events, 295.50000000000000000000 events / second
// Total: 1240 events, 310.00000000000000000000 events / second
// Total: 1181 events, 295.25000000000000000000 events / second
// Total: 1181 events, 295.25000000000000000000 events / second
// Total: 1183 events, 295.75000000000000000000 events / second
```
## Important recommendations ## Important recommendations
Most of the crashes are caused by improper configuration of the library for the project. 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` 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`. 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. 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`. 3. You can change **if you know what you are doing** the task priority with `-D CONFIG_ASYNC_TCP_PRIORITY=10`.
Default is `10`. Default is `10`.
4. You can increase the queue size with `-D CONFIG_ASYNC_TCP_QUEUE_SIZE=128`. 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" #include "ArduinoJson.h"
AsyncCallbackJsonWebHandler* handler = new AsyncCallbackJsonWebHandler("/rest/endpoint", [](AsyncWebServerRequest *request, JsonVariant &json) { AsyncCallbackJsonWebHandler* handler = new AsyncCallbackJsonWebHandler("/rest/endpoint", [](AsyncWebServerRequest *request, JsonVariant &json) {
JsonObject& jsonObj = json.as<JsonObject>(); JsonObject jsonObj = json.as<JsonObject>();
// ... // ...
}); });
server.addHandler(handler); server.addHandler(handler);
@ -1186,7 +1280,7 @@ For actual serving the file.
### Param Rewrite With Matching ### 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}" Rewrite for example "/radio/{frequence}" -> "/radio?f={frequence}"
```cpp ```cpp

View File

@ -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. 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) - [Changes in this fork](#changes-in-this-fork)
- [Dependencies](#dependencies)
- [Performance](#performance)
- [Important recommendations](#important-recommendations) - [Important recommendations](#important-recommendations)
- [`AsyncWebSocketMessageBuffer` and `makeBuffer()`](#asyncwebsocketmessagebuffer-and-makebuffer) - [`AsyncWebSocketMessageBuffer` and `makeBuffer()`](#asyncwebsocketmessagebuffer-and-makebuffer)
- [How to replace a response](#how-to-replace-a-response) - [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) - [Migration to Middleware to improve performance and memory usage](#migration-to-middleware-to-improve-performance-and-memory-usage)
- [Original Documentation](#original-documentation) - [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 ## Changes in this fork
- (bug) A lot of bug fixes - (bug) A lot of bug fixes
@ -55,6 +42,7 @@ Dependency:
- (feat) **Resumable download** support using HEAD and bytes range - (feat) **Resumable download** support using HEAD and bytes range
- (feat) `StreamConcat` example to show how to stream multiple files in one response - (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) - (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) `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) `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 - (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) Lot of code cleanup and optimizations
- (perf) Performance improvements in terms of memory, speed and size - (perf) Performance improvements in terms of memory, speed and size
---
## WARNING: Important notes about future version 4.x
This ESPAsyncWebServer fork is now at version 3.x.
Next version 4.x will:
1. Drop support for ESP8266, which goes EOL in a few years. All ESP8266 boards can be replaced by equivalent ESP32 boards.
2. Drop support for Arduino 2.x and ESP-IDF 4.x. The library will be compatible with Arduino 3.x and ESP-IDF 5.x.
3. Drop support for ArduinoJson 5.x and 6.x. The library will be compatible with ArduinoJson 7.x.
So if you need one of these feature, you will have to stick with 3.x or another fork.
## Dependencies
**WARNING** The library name was changed from `ESP Async WebServer` to `ESPAsyncWebServer` as per the Arduino Lint recommendations, but its name had to stay `ESP Async WebServer` in Arduino Registry.
**PlatformIO / pioarduino:**
```ini
lib_compat_mode = strict
lib_ldf_mode = chain
lib_deps = mathieucarbou/ESPAsyncWebServer @ 3.3.23
```
**Dependencies:**
- **ESP32 with AsyncTCP**: `mathieucarbou/AsyncTCP @ 3.2.14`
Arduino IDE: [https://github.com/mathieucarbou/AsyncTCP#v3.2.14](https://github.com/mathieucarbou/AsyncTCP/releases)
- **ESP32 with AsyncTCPSock**: `https://github.com/mathieucarbou/AsyncTCPSock/archive/refs/tags/v1.0.3-dev.zip`
- **ESP8266**: `esphome/ESPAsyncTCP-esphome @ 2.0.0`
Arduino IDE: [https://github.com/mathieucarbou/esphome-ESPAsyncTCP#v2.0.0](https://github.com/mathieucarbou/esphome-ESPAsyncTCP/releases/tag/v2.0.0)
- **RP2040**: `khoih-prog/AsyncTCP_RP2040W @ 1.2.0`
Arduino IDE: [https://github.com/khoih-prog/AsyncTCP_RP2040W#v1.2.0](https://github.com/khoih-prog/AsyncTCP_RP2040W/releases/tag/v1.2.0)
**AsyncTCPSock**
AsyncTCPSock can be used instead of AsyncTCP by excluding AsyncTCP from the library dependencies and adding AsyncTCPSock instead:
```ini
lib_compat_mode = strict
lib_ldf_mode = chain
lib_deps =
; mathieucarbou/AsyncTCP @ 3.2.14
https://github.com/mathieucarbou/AsyncTCPSock/archive/refs/tags/v1.0.3-dev.zip
mathieucarbou/ESPAsyncWebServer @ 3.3.23
lib_ignore =
AsyncTCP
mathieucarbou/AsyncTCP
```
## Performance
Performance of `mathieucarbou/ESPAsyncWebServer @ 3.3.23`:
```bash
> brew install autocannon
> autocannon -c 10 -w 10 -d 20 http://192.168.4.1
```
With `mathieucarbou/AsyncTCP @ 3.2.14`
[![](https://mathieu.carbou.me/ESPAsyncWebServer/perf-c10.png)](https://mathieu.carbou.me/ESPAsyncWebServer/perf-c10.png)
With `https://github.com/mathieucarbou/AsyncTCPSock/archive/refs/tags/v1.0.3-dev.zip`:
[![](https://mathieu.carbou.me/ESPAsyncWebServer/perf-c10-asynctcpsock.png)](https://mathieu.carbou.me/ESPAsyncWebServer/perf-c10-asynctcpsock.png)
**SSE performance**
In the example, there is an endpoint `/events` with some comments showing how these metrics are calculated.
Test is running for 20 seconds with 10 connections.
```
// With AsyncTCP, with 10 workers: no message discarded from the queue
//
// Total: 1875 events, 468.75000000000000000000 events / second
// Total: 1870 events, 467.50000000000000000000 events / second
// Total: 1871 events, 467.75000000000000000000 events / second
// Total: 1875 events, 468.75000000000000000000 events / second
// Total: 1871 events, 467.75000000000000000000 events / second
// Total: 1805 events, 451.25000000000000000000 events / second
// Total: 1803 events, 450.75000000000000000000 events / second
// Total: 1873 events, 468.25000000000000000000 events / second
// Total: 1872 events, 468.00000000000000000000 events / second
// Total: 1805 events, 451.25000000000000000000 events / second
//
// With AsyncTCPSock, with 10 workers: no message discarded from the queue
//
// Total: 1242 events, 310.50000000000000000000 events / second
// Total: 1242 events, 310.50000000000000000000 events / second
// Total: 1242 events, 310.50000000000000000000 events / second
// Total: 1242 events, 310.50000000000000000000 events / second
// Total: 1181 events, 295.25000000000000000000 events / second
// Total: 1182 events, 295.50000000000000000000 events / second
// Total: 1240 events, 310.00000000000000000000 events / second
// Total: 1181 events, 295.25000000000000000000 events / second
// Total: 1181 events, 295.25000000000000000000 events / second
// Total: 1183 events, 295.75000000000000000000 events / second
```
## Important recommendations ## Important recommendations
Most of the crashes are caused by improper configuration of the library for the project. 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` 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`. 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. 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`. 3. You can change **if you know what you are doing** the task priority with `-D CONFIG_ASYNC_TCP_PRIORITY=10`.
Default is `10`. Default is `10`.
4. You can increase the queue size with `-D CONFIG_ASYNC_TCP_QUEUE_SIZE=128`. 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 ### 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}" Rewrite for example "/radio/{frequence}" -> "/radio?f={frequence}"
```cpp ```cpp

Binary file not shown.

After

Width:  |  Height:  |  Size: 310 KiB

BIN
docs/perf-c10.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 295 KiB

View File

@ -16,10 +16,7 @@ AsyncWebServer server(80);
class CaptiveRequestHandler : public AsyncWebHandler { class CaptiveRequestHandler : public AsyncWebHandler {
public: public:
CaptiveRequestHandler() {} bool canHandle(__unused AsyncWebServerRequest* request) const override {
virtual ~CaptiveRequestHandler() {}
bool canHandle(__unused AsyncWebServerRequest* request) {
return true; return true;
} }

View File

@ -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() {
}

View File

@ -18,14 +18,11 @@ AsyncWebServer server(80);
class CaptiveRequestHandler : public AsyncWebHandler { class CaptiveRequestHandler : public AsyncWebHandler {
public: public:
CaptiveRequestHandler() {} bool canHandle(__unused AsyncWebServerRequest* request) const override {
virtual ~CaptiveRequestHandler() {}
bool canHandle(__unused AsyncWebServerRequest* request) {
return true; return true;
} }
void handleRequest(AsyncWebServerRequest* request) { void handleRequest(AsyncWebServerRequest* request) override {
AsyncResponseStream* response = request->beginResponseStream("text/html"); AsyncResponseStream* response = request->beginResponseStream("text/html");
response->print("<!DOCTYPE html><html><head><title>Captive Portal</title></head><body>"); response->print("<!DOCTYPE html><html><head><title>Captive Portal</title></head><body>");
response->print("<p>This is out captive portal front page.</p>"); response->print("<p>This is out captive portal front page.</p>");

View 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);
}

View 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';
// }
// }
}

View File

@ -19,7 +19,7 @@
#include <ESPAsyncWebServer.h> #include <ESPAsyncWebServer.h>
#if ASYNC_JSON_SUPPORT == 1 #if __has_include("ArduinoJson.h")
#include <ArduinoJson.h> #include <ArduinoJson.h>
#include <AsyncJson.h> #include <AsyncJson.h>
#include <AsyncMessagePack.h> #include <AsyncMessagePack.h>
@ -27,6 +27,80 @@
#include <LittleFS.h> #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); AsyncWebServer server(80);
AsyncEventSource events("/events"); AsyncEventSource events("/events");
AsyncWebSocket ws("/ws"); AsyncWebSocket ws("/ws");
@ -111,11 +185,14 @@ void notFound(AsyncWebServerRequest* request) {
request->send(404, "text/plain", "Not found"); request->send(404, "text/plain", "Not found");
} }
#if ASYNC_JSON_SUPPORT == 1 #if __has_include("ArduinoJson.h")
AsyncCallbackJsonWebHandler* jsonHandler = new AsyncCallbackJsonWebHandler("/json2"); AsyncCallbackJsonWebHandler* jsonHandler = new AsyncCallbackJsonWebHandler("/json2");
AsyncCallbackMessagePackWebHandler* msgPackHandler = new AsyncCallbackMessagePackWebHandler("/msgpack2"); AsyncCallbackMessagePackWebHandler* msgPackHandler = new AsyncCallbackMessagePackWebHandler("/msgpack2");
#endif #endif
static const char characters[] = "0123456789ABCDEF";
static size_t charactersIndex = 0;
void setup() { void setup() {
Serial.begin(115200); Serial.begin(115200);
@ -134,6 +211,32 @@ void setup() {
WiFi.softAP("esp-captive"); WiFi.softAP("esp-captive");
#endif #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 // curl -v -X GET http://192.168.4.1/handler-not-sending-response
server.on("/handler-not-sending-response", HTTP_GET, [](AsyncWebServerRequest* request) { server.on("/handler-not-sending-response", HTTP_GET, [](AsyncWebServerRequest* request) {
// handler forgot to send a response to the client => 501 Not Implemented // 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("X-Keep-Me");
headerFree.keep("host"); headerFree.keep("host");
// global middleware
server.addMiddleware(&requestLogger);
server.addMiddlewares({&rateLimit, &cors, &headerFilter});
cors.setOrigin("http://192.168.4.1"); cors.setOrigin("http://192.168.4.1");
cors.setMethods("POST, GET, OPTIONS, DELETE"); cors.setMethods("POST, GET, OPTIONS, DELETE");
cors.setHeaders("X-Custom-Header"); cors.setHeaders("X-Custom-Header");
cors.setAllowCredentials(false); cors.setAllowCredentials(false);
cors.setMaxAge(600); cors.setMaxAge(600);
#ifndef PERF_TEST
// global middleware
server.addMiddleware(&requestLogger);
server.addMiddlewares({&rateLimit, &cors, &headerFilter});
#endif
// Test CORS preflight request // Test CORS preflight request
// curl -v -X OPTIONS -H "origin: http://192.168.4.1" http://192.168.4.1/middleware/cors // 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) { server.on("/middleware/cors", HTTP_GET, [](AsyncWebServerRequest* request) {
@ -299,12 +404,103 @@ void setup() {
request->redirect("/"); 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) { 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) { // curl -v -X GET http://192.168.4.1/index.txt
request->send(LittleFS, "/index.html"); 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); request->send(200, "text/plain", "Hello, POST: " + message);
}); });
#if ASYNC_JSON_SUPPORT == 1 #if __has_include("ArduinoJson.h")
// JSON // JSON
// receives JSON and sends JSON // sends JSON
jsonHandler->onRequest([](AsyncWebServerRequest* request, JsonVariant& json) { // curl -v -X GET http://192.168.4.1/json1
// JsonObject jsonObj = json.as<JsonObject>(); server.on("/json1", HTTP_GET, [](AsyncWebServerRequest* request) {
// ...
AsyncJsonResponse* response = new AsyncJsonResponse(); AsyncJsonResponse* response = new AsyncJsonResponse();
JsonObject root = response->getRoot().to<JsonObject>(); JsonObject root = response->getRoot().to<JsonObject>();
root["hello"] = "world"; root["hello"] = "world";
@ -368,11 +562,24 @@ void setup() {
request->send(response); request->send(response);
}); });
// sends JSON // curl -v -X GET http://192.168.4.1/json2
server.on("/json1", HTTP_GET, [](AsyncWebServerRequest* request) { 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(); AsyncJsonResponse* response = new AsyncJsonResponse();
JsonObject root = response->getRoot().to<JsonObject>(); JsonObject root = response->getRoot().to<JsonObject>();
root["hello"] = "world"; root["hello"] = json.as<JsonObject>()["name"];
response->setLength(); response->setLength();
request->send(response); 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); server.addHandler(&events);
// Run: websocat ws://192.168.4.1/ws
server.addHandler(&ws); server.addHandler(&ws);
#if ASYNC_JSON_SUPPORT == 1 #if __has_include("ArduinoJson.h")
server.addHandler(jsonHandler); server.addHandler(jsonHandler);
server.addHandler(msgPackHandler); server.addHandler(msgPackHandler);
#endif #endif
@ -450,7 +716,7 @@ void setup() {
} }
uint32_t lastSSE = 0; uint32_t lastSSE = 0;
uint32_t deltaSSE = 5; uint32_t deltaSSE = 10;
uint32_t lastWS = 0; uint32_t lastWS = 0;
uint32_t deltaWS = 100; uint32_t deltaWS = 100;
@ -463,9 +729,8 @@ void loop() {
} }
if (now - lastWS >= deltaWS) { if (now - lastWS >= deltaWS) {
ws.printfAll("kp%.4f", (10.0 / 3.0)); ws.printfAll("kp%.4f", (10.0 / 3.0));
// ws.getClients
for (auto& client : 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(); lastWS = millis();
} }

View File

@ -10,11 +10,13 @@
#include <WebServer.h> #include <WebServer.h>
#include <WiFi.h> #include <WiFi.h>
#endif #endif
#include "StreamConcat.h"
#include "StreamString.h" #include <StreamString.h>
#include <ESPAsyncWebServer.h> #include <ESPAsyncWebServer.h>
#include <LittleFS.h> #include <LittleFS.h>
#include "StreamConcat.h"
DNSServer dnsServer; DNSServer dnsServer;
AsyncWebServer server(80); AsyncWebServer server(80);

View File

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

View File

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

View File

@ -1,6 +1,6 @@
{ {
"name": "ESPAsyncWebServer", "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.", "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", "keywords": "http,async,websocket,webserver",
"homepage": "https://github.com/mathieucarbou/ESPAsyncWebServer", "homepage": "https://github.com/mathieucarbou/ESPAsyncWebServer",
@ -28,7 +28,7 @@
{ {
"owner": "mathieucarbou", "owner": "mathieucarbou",
"name": "AsyncTCP", "name": "AsyncTCP",
"version": "^3.2.5", "version": "^3.2.14",
"platforms": "espressif32" "platforms": "espressif32"
}, },
{ {

View File

@ -1,5 +1,6 @@
name=ESPAsyncWebServer name=ESP Async WebServer
version=3.3.7 includes=ESPAsyncWebServer.h
version=3.3.23
author=Me-No-Dev author=Me-No-Dev
maintainer=Mathieu Carbou <mathieu.carbou@gmail.com> maintainer=Mathieu Carbou <mathieu.carbou@gmail.com>
sentence=Asynchronous HTTP and WebSocket Server Library for ESP32, ESP8266 and RP2040 sentence=Asynchronous HTTP and WebSocket Server Library for ESP32, ESP8266 and RP2040

7
partitions-4MB.csv Normal file
View 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 # Name Type SubType Offset Size Flags
2 nvs data nvs 36K 20K
3 otadata data ota 56K 8K
4 app0 app ota_0 64K 1856K
5 app1 app ota_1 1920K 1856K
6 spiffs data spiffs 3776K 256K
7 coredump data coredump 4032K 64K

View File

@ -1,16 +1,17 @@
[platformio] [platformio]
default_envs = arduino-2, arduino-3, arduino-310rc1, esp8266, raspberrypi default_envs = arduino-2, arduino-3, arduino-310, esp8266, raspberrypi
lib_dir = . lib_dir = .
; src_dir = examples/CaptivePortal ; src_dir = examples/CaptivePortal
src_dir = examples/SimpleServer ; src_dir = examples/SimpleServer
; src_dir = examples/StreamFiles ; src_dir = examples/StreamFiles
; src_dir = examples/Filters ; src_dir = examples/Filters
; src_dir = examples/Draft ; src_dir = examples/Issue85
; src_dir = examples/issues/Issue14 src_dir = examples/Issue162
[env] [env]
framework = arduino framework = arduino
build_flags = build_flags =
-Og
-Wall -Wextra -Wall -Wextra
-Wno-unused-parameter -Wno-unused-parameter
-D CONFIG_ARDUHAL_LOG_COLORS -D CONFIG_ARDUHAL_LOG_COLORS
@ -23,12 +24,17 @@ build_flags =
upload_protocol = esptool upload_protocol = esptool
monitor_speed = 115200 monitor_speed = 115200
monitor_filters = esp32_exception_decoder, log2file monitor_filters = esp32_exception_decoder, log2file
; monitor_filters = esp8266_exception_decoder, log2file
lib_compat_mode = strict
lib_ldf_mode = chain
lib_deps = lib_deps =
; bblanchon/ArduinoJson @ 5.13.4 ; bblanchon/ArduinoJson @ 5.13.4
; bblanchon/ArduinoJson @ 6.21.5 ; bblanchon/ArduinoJson @ 6.21.5
bblanchon/ArduinoJson @ 7.2.0 bblanchon/ArduinoJson @ 7.2.1
mathieucarbou/AsyncTCP @ 3.2.5 mathieucarbou/AsyncTCP @ 3.2.14
board = esp32dev board = esp32dev
board_build.partitions = partitions-4MB.csv
board_build.filesystem = littlefs
[env:arduino-2] [env:arduino-2]
platform = espressif32@6.9.0 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-s3-devkitc-1
; board = esp32-c6-devkitc-1 ; board = esp32-c6-devkitc-1
lib_deps = lib_deps =
mathieucarbou/AsyncTCP @ 3.2.5 mathieucarbou/AsyncTCP @ 3.2.14
[env:arduino-310rc1] [env:arduino-310]
platform = https://github.com/pioarduino/platform-espressif32/releases/download/53.03.10-rc1/platform-espressif32.zip platform = https://github.com/pioarduino/platform-espressif32/releases/download/53.03.10-rc3/platform-espressif32.zip
; board = esp32-s3-devkitc-1 ; board = esp32-s3-devkitc-1
; board = esp32-c6-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] [env:esp8266]
platform = espressif8266 platform = espressif8266
board = huzzah ; board = huzzah
; board = d1_mini board = d1_mini
lib_deps = lib_deps =
bblanchon/ArduinoJson @ 7.2.0 bblanchon/ArduinoJson @ 7.2.1
esphome/ESPAsyncTCP-esphome @ 2.0.0 esphome/ESPAsyncTCP-esphome @ 2.0.0
; PlatformIO support for Raspberry Pi Pico is not official
; https://github.com/platformio/platform-raspberrypi/pull/36
; https://github.com/earlephilhower/arduino-pico/blob/master/docs/platformio.rst
; board settings: https://github.com/earlephilhower/arduino-pico/blob/master/tools/json/rpipico.json
[env:raspberrypi] [env:raspberrypi]
upload_protocol = picotool platform = https://github.com/maxgerhardt/platform-raspberrypi.git
; platform = raspberrypi
platform = https://github.com/maxgerhardt/platform-raspberrypi.git#f2687073f73d554c9db41f29b4769fd9703f4e55
board = rpipicow board = rpipicow
lib_deps = lib_deps =
bblanchon/ArduinoJson @ 7.2.0 bblanchon/ArduinoJson @ 7.2.1
khoih-prog/AsyncTCP_RP2040W @ 1.2.0 khoih-prog/AsyncTCP_RP2040W @ 1.2.0
lib_ignore =
lwIP_ESPHost
build_flags = ${env.build_flags} build_flags = ${env.build_flags}
-Wno-missing-field-initializers -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 platform = https://github.com/pioarduino/platform-espressif32/releases/download/51.03.05/platform-espressif32.zip
board = ${sysenv.PIO_BOARD} board = ${sysenv.PIO_BOARD}
lib_deps = lib_deps =
mathieucarbou/AsyncTCP @ 3.2.5 mathieucarbou/AsyncTCP @ 3.2.14
[env:ci-arduino-310rc1] [env:ci-arduino-310]
platform = https://github.com/pioarduino/platform-espressif32/releases/download/53.03.10-rc1/platform-espressif32.zip platform = https://github.com/pioarduino/platform-espressif32/releases/download/53.03.10-rc3/platform-espressif32.zip
board = ${sysenv.PIO_BOARD} board = ${sysenv.PIO_BOARD}
[env:ci-esp8266] [env:ci-esp8266]
platform = espressif8266 platform = espressif8266
board = ${sysenv.PIO_BOARD} board = ${sysenv.PIO_BOARD}
lib_deps = lib_deps =
bblanchon/ArduinoJson @ 7.2.0 bblanchon/ArduinoJson @ 7.2.1
esphome/ESPAsyncTCP-esphome @ 2.0.0 esphome/ESPAsyncTCP-esphome @ 2.0.0
[env:ci-raspberrypi] [env:ci-raspberrypi]
; platform = raspberrypi platform = https://github.com/maxgerhardt/platform-raspberrypi.git
platform = https://github.com/maxgerhardt/platform-raspberrypi.git#f2687073f73d554c9db41f29b4769fd9703f4e55
board = ${sysenv.PIO_BOARD} board = ${sysenv.PIO_BOARD}
lib_deps = lib_deps =
bblanchon/ArduinoJson @ 7.2.0 bblanchon/ArduinoJson @ 7.2.1
khoih-prog/AsyncTCP_RP2040W @ 1.2.0 khoih-prog/AsyncTCP_RP2040W @ 1.2.0
lib_ignore =
lwIP_ESPHost
build_flags = ${env.build_flags} build_flags = ${env.build_flags}
-Wno-missing-field-initializers -Wno-missing-field-initializers

View File

@ -141,6 +141,9 @@ size_t AsyncEventSourceMessage::ack(size_t len, __attribute__((unused)) uint32_t
} }
size_t AsyncEventSourceMessage::write(AsyncClient* client) { size_t AsyncEventSourceMessage::write(AsyncClient* client) {
if (!client)
return 0;
if (_sent >= _len || !client->canSend()) { if (_sent >= _len || !client->canSend()) {
return 0; return 0;
} }
@ -186,7 +189,10 @@ AsyncEventSourceClient::~AsyncEventSourceClient() {
close(); 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 #ifdef ESP32
// length() is not thread-safe, thus acquiring the lock before this call.. // length() is not thread-safe, thus acquiring the lock before this call..
std::lock_guard<std::mutex> lock(_lockmq); std::lock_guard<std::mutex> lock(_lockmq);
@ -198,7 +204,7 @@ void AsyncEventSourceClient::_queueMessage(const char* message, size_t len) {
#elif defined(ESP32) #elif defined(ESP32)
log_e("Too many messages queued: deleting message"); log_e("Too many messages queued: deleting message");
#endif #endif
return; return false;
} }
_messageQueue.emplace_back(message, len); _messageQueue.emplace_back(message, len);
@ -206,6 +212,8 @@ void AsyncEventSourceClient::_queueMessage(const char* message, size_t len) {
if (_client->canSend()) { if (_client->canSend()) {
_runQueue(); _runQueue();
} }
return true;
} }
void AsyncEventSourceClient::_onAck(size_t len __attribute__((unused)), uint32_t time __attribute__((unused))) { 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))) { void AsyncEventSourceClient::_onTimeout(uint32_t time __attribute__((unused))) {
_client->close(true); if (_client)
_client->close(true);
} }
void AsyncEventSourceClient::_onDisconnect() { void AsyncEventSourceClient::_onDisconnect() {
_client = NULL; if (!_client)
return;
_client = nullptr;
_server->_handleDisconnect(this); _server->_handleDisconnect(this);
} }
void AsyncEventSourceClient::close() { void AsyncEventSourceClient::close() {
if (_client != NULL) if (_client)
_client->close(); _client->close();
} }
void AsyncEventSourceClient::write(const char* message, size_t len) { bool AsyncEventSourceClient::write(const char* message, size_t len) {
if (!connected()) return connected() && _queueMessage(message, len);
return;
_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()) if (!connected())
return; return false;
String ev = generateEventMessage(message, event, id, reconnect); 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 { size_t AsyncEventSourceClient::packetsWaiting() const {
@ -261,6 +270,9 @@ size_t AsyncEventSourceClient::packetsWaiting() const {
} }
void AsyncEventSourceClient::_runQueue() { void AsyncEventSourceClient::_runQueue() {
if (!_client)
return;
size_t total_bytes_written = 0; size_t total_bytes_written = 0;
for (auto i = _messageQueue.begin(); i != _messageQueue.end(); ++i) { for (auto i = _messageQueue.begin(); i != _messageQueue.end(); ++i) {
if (!i->sent()) { if (!i->sent()) {
@ -270,6 +282,7 @@ void AsyncEventSourceClient::_runQueue() {
break; break;
} }
} }
if (total_bytes_written > 0) if (total_bytes_written > 0)
_client->send(); _client->send();
@ -282,11 +295,6 @@ void AsyncEventSourceClient::_runQueue() {
} }
} }
// Handler
void AsyncEventSource::onConnect(ArEventHandlerFunction cb) {
_connectcb = cb;
}
void AsyncEventSource::authorizeConnect(ArAuthorizeConnectHandler cb) { void AsyncEventSource::authorizeConnect(ArAuthorizeConnectHandler cb) {
AuthorizationMiddleware* m = new AuthorizationMiddleware(401, cb); AuthorizationMiddleware* m = new AuthorizationMiddleware(401, cb);
m->_freeOnRemoval = true; m->_freeOnRemoval = true;
@ -308,6 +316,8 @@ void AsyncEventSource::_handleDisconnect(AsyncEventSourceClient* client) {
#ifdef ESP32 #ifdef ESP32
std::lock_guard<std::mutex> lock(_client_queue_lock); std::lock_guard<std::mutex> lock(_client_queue_lock);
#endif #endif
if (_disconnectcb)
_disconnectcb(client);
for (auto i = _clients.begin(); i != _clients.end(); ++i) { for (auto i = _clients.begin(); i != _clients.end(); ++i) {
if (i->get() == client) if (i->get() == client)
_clients.erase(i); _clients.erase(i);
@ -346,17 +356,21 @@ size_t AsyncEventSource::avgPacketsWaiting() const {
return ((aql) + (nConnectedClients / 2)) / (nConnectedClients); // round up 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) { const char* message, const char* event, uint32_t id, uint32_t reconnect) {
String ev = generateEventMessage(message, event, id, reconnect); String ev = generateEventMessage(message, event, id, reconnect);
#ifdef ESP32 #ifdef ESP32
std::lock_guard<std::mutex> lock(_client_queue_lock); std::lock_guard<std::mutex> lock(_client_queue_lock);
#endif #endif
size_t hits = 0;
size_t miss = 0;
for (const auto& c : _clients) { for (const auto& c : _clients) {
if (c->connected()) { if (c->write(ev.c_str(), ev.length()))
c->write(ev.c_str(), ev.length()); ++hits;
} else
++miss;
} }
return hits == 0 ? DISCARDED : (miss == 0 ? ENQUEUED : PARTIALLY_ENQUEUED);
} }
size_t AsyncEventSource::count() const { size_t AsyncEventSource::count() const {
@ -371,11 +385,8 @@ size_t AsyncEventSource::count() const {
return n_clients; return n_clients;
} }
bool AsyncEventSource::canHandle(AsyncWebServerRequest* request) { bool AsyncEventSource::canHandle(AsyncWebServerRequest* request) const {
if (request->method() != HTTP_GET || !request->url().equals(_url)) { return request->isSSE() && request->url().equals(_url);
return false;
}
return true;
} }
void AsyncEventSource::handleRequest(AsyncWebServerRequest* request) { void AsyncEventSource::handleRequest(AsyncWebServerRequest* request) {

View File

@ -81,7 +81,7 @@ class AsyncEventSourceClient {
#ifdef ESP32 #ifdef ESP32
mutable std::mutex _lockmq; mutable std::mutex _lockmq;
#endif #endif
void _queueMessage(const char* message, size_t len); bool _queueMessage(const char* message, size_t len);
void _runQueue(); void _runQueue();
public: public:
@ -90,11 +90,11 @@ class AsyncEventSourceClient {
AsyncClient* client() { return _client; } AsyncClient* client() { return _client; }
void close(); void close();
void write(const char* message, size_t len); bool 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); } 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); }
void send(const String& message, const char* event, uint32_t id = 0, uint32_t reconnect = 0) { send(message.c_str(), event, 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); }
void send(const char* message, const char* event = NULL, uint32_t id = 0, uint32_t reconnect = 0); bool send(const char* message, const char* event = NULL, uint32_t id = 0, uint32_t reconnect = 0);
bool connected() const { return (_client != NULL) && _client->connected(); } bool connected() const { return _client && _client->connected(); }
uint32_t lastId() const { return _lastId; } uint32_t lastId() const { return _lastId; }
size_t packetsWaiting() const; size_t packetsWaiting() const;
@ -114,19 +114,28 @@ class AsyncEventSource : public AsyncWebHandler {
// since simultaneous access from different tasks is possible // since simultaneous access from different tasks is possible
mutable std::mutex _client_queue_lock; mutable std::mutex _client_queue_lock;
#endif #endif
ArEventHandlerFunction _connectcb{nullptr}; ArEventHandlerFunction _connectcb = nullptr;
ArEventHandlerFunction _disconnectcb = nullptr;
public: public:
typedef enum {
DISCARDED = 0,
ENQUEUED = 1,
PARTIALLY_ENQUEUED = 2,
} SendStatus;
AsyncEventSource(const String& url) : _url(url) {}; AsyncEventSource(const String& url) : _url(url) {};
~AsyncEventSource() { close(); }; ~AsyncEventSource() { close(); };
const char* url() const { return _url.c_str(); } const char* url() const { return _url.c_str(); }
void close(); 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 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); } 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); }
void send(const String& message, const char* event, uint32_t id = 0, uint32_t reconnect = 0) { send(message.c_str(), event, 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); }
void send(const char* message, const char* event = NULL, uint32_t id = 0, uint32_t reconnect = 0); SendStatus send(const char* message, const char* event = NULL, uint32_t id = 0, uint32_t reconnect = 0);
// number of clients connected // number of clients connected
size_t count() const; size_t count() const;
size_t avgPacketsWaiting() const; size_t avgPacketsWaiting() const;
@ -134,8 +143,8 @@ class AsyncEventSource : public AsyncWebHandler {
// system callbacks (do not call) // system callbacks (do not call)
void _addClient(AsyncEventSourceClient* client); void _addClient(AsyncEventSourceClient* client);
void _handleDisconnect(AsyncEventSourceClient* client); void _handleDisconnect(AsyncEventSourceClient* client);
virtual bool canHandle(AsyncWebServerRequest* request) override final; bool canHandle(AsyncWebServerRequest* request) const override final;
virtual void handleRequest(AsyncWebServerRequest* request) override final; void handleRequest(AsyncWebServerRequest* request) override final;
}; };
class AsyncEventSourceResponse : public AsyncWebServerResponse { class AsyncEventSourceResponse : public AsyncWebServerResponse {

View File

@ -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) {} : _uri(uri), _method(HTTP_GET | HTTP_POST | HTTP_PUT | HTTP_PATCH), _onRequest(onRequest), _maxContentLength(16384) {}
#endif #endif
bool AsyncCallbackJsonWebHandler::canHandle(AsyncWebServerRequest* request) { bool AsyncCallbackJsonWebHandler::canHandle(AsyncWebServerRequest* request) const {
if (!_onRequest) if (!_onRequest || !request->isHTTP() || !(_method & request->method()))
return false;
WebRequestMethodComposite request_method = request->method();
if (!(_method & request_method))
return false; return false;
if (_uri.length() && (_uri != request->url() && !request->url().startsWith(_uri + "/"))) if (_uri.length() && (_uri != request->url() && !request->url().startsWith(_uri + "/")))
return false; 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 false;
return true; return true;

View File

@ -119,11 +119,11 @@ class AsyncCallbackJsonWebHandler : public AsyncWebHandler {
void setMaxContentLength(int maxContentLength) { _maxContentLength = maxContentLength; } void setMaxContentLength(int maxContentLength) { _maxContentLength = maxContentLength; }
void onRequest(ArJsonRequestHandlerFunction fn) { _onRequest = fn; } void onRequest(ArJsonRequestHandlerFunction fn) { _onRequest = fn; }
virtual bool canHandle(AsyncWebServerRequest* request) override final; bool canHandle(AsyncWebServerRequest* request) const override final;
virtual void handleRequest(AsyncWebServerRequest* request) override final; 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 {} 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; 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 isRequestHandlerTrivial() const override final { return !_onRequest; }
}; };
#endif // ASYNC_JSON_SUPPORT == 1 #endif // ASYNC_JSON_SUPPORT == 1

View File

@ -44,18 +44,14 @@ AsyncCallbackMessagePackWebHandler::AsyncCallbackMessagePackWebHandler(const Str
: _uri(uri), _method(HTTP_GET | HTTP_POST | HTTP_PUT | HTTP_PATCH), _onRequest(onRequest), _maxContentLength(16384) {} : _uri(uri), _method(HTTP_GET | HTTP_POST | HTTP_PUT | HTTP_PATCH), _onRequest(onRequest), _maxContentLength(16384) {}
#endif #endif
bool AsyncCallbackMessagePackWebHandler::canHandle(AsyncWebServerRequest* request) { bool AsyncCallbackMessagePackWebHandler::canHandle(AsyncWebServerRequest* request) const {
if (!_onRequest) if (!_onRequest || !request->isHTTP() || !(_method & request->method()))
return false;
WebRequestMethodComposite request_method = request->method();
if (!(_method & request_method))
return false; return false;
if (_uri.length() && (_uri != request->url() && !request->url().startsWith(_uri + "/"))) if (_uri.length() && (_uri != request->url() && !request->url().startsWith(_uri + "/")))
return false; 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 false;
return true; return true;

View File

@ -92,11 +92,11 @@ class AsyncCallbackMessagePackWebHandler : public AsyncWebHandler {
void setMaxContentLength(int maxContentLength) { _maxContentLength = maxContentLength; } void setMaxContentLength(int maxContentLength) { _maxContentLength = maxContentLength; }
void onRequest(ArMessagePackRequestHandlerFunction fn) { _onRequest = fn; } void onRequest(ArMessagePackRequestHandlerFunction fn) { _onRequest = fn; }
virtual bool canHandle(AsyncWebServerRequest* request) override final; bool canHandle(AsyncWebServerRequest* request) const override final;
virtual void handleRequest(AsyncWebServerRequest* request) override final; 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 {} 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; 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 isRequestHandlerTrivial() const override final { return !_onRequest; }
}; };
#endif // ASYNC_MSG_PACK_SUPPORT == 1 #endif // ASYNC_MSG_PACK_SUPPORT == 1

View File

@ -39,7 +39,7 @@
using namespace asyncsrv; using namespace asyncsrv;
size_t webSocketSendFrameWindow(AsyncClient* client) { size_t webSocketSendFrameWindow(AsyncClient* client) {
if (!client->canSend()) if (!client || !client->canSend())
return 0; return 0;
size_t space = client->space(); size_t space = client->space();
if (space < 9) 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) { 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"); // Serial.println("SF 1");
return 0; return 0;
} }
@ -185,12 +185,12 @@ class AsyncWebSocketControl {
_data = NULL; _data = NULL;
} }
virtual ~AsyncWebSocketControl() { ~AsyncWebSocketControl() {
if (_data != NULL) if (_data != NULL)
free(_data); free(_data);
} }
virtual bool finished() const { return _finished; } bool finished() const { return _finished; }
uint8_t opcode() { return _opcode; } uint8_t opcode() { return _opcode; }
uint8_t len() { return _len + 2; } uint8_t len() { return _len + 2; }
size_t send(AsyncClient* client) { size_t send(AsyncClient* client) {
@ -219,6 +219,9 @@ void AsyncWebSocketMessage::ack(size_t len, uint32_t time) {
} }
size_t AsyncWebSocketMessage::send(AsyncClient* client) { size_t AsyncWebSocketMessage::send(AsyncClient* client) {
if (!client)
return 0;
if (_status != WS_MSG_SENDING) if (_status != WS_MSG_SENDING)
return 0; return 0;
if (_acked < _ack) { if (_acked < _ack) {
@ -343,7 +346,7 @@ void AsyncWebSocketClient::_onPoll() {
#ifdef ESP32 #ifdef ESP32
std::unique_lock<std::mutex> lock(_lock); std::unique_lock<std::mutex> lock(_lock);
#endif #endif
if (_client->canSend() && (!_controlQueue.empty() || !_messageQueue.empty())) { if (_client && _client->canSend() && (!_controlQueue.empty() || !_messageQueue.empty())) {
_runQueue(); _runQueue();
} else if (_keepAlivePeriod > 0 && (millis() - _lastMessageTime) >= _keepAlivePeriod && (_controlQueue.empty() && _messageQueue.empty())) { } else if (_keepAlivePeriod > 0 && (millis() - _lastMessageTime) >= _keepAlivePeriod && (_controlQueue.empty() && _messageQueue.empty())) {
#ifdef ESP32 #ifdef ESP32
@ -371,16 +374,13 @@ bool AsyncWebSocketClient::queueIsFull() const {
#ifdef ESP32 #ifdef ESP32
std::lock_guard<std::mutex> lock(_lock); std::lock_guard<std::mutex> lock(_lock);
#endif #endif
size_t size = _messageQueue.size(); return (_messageQueue.size() >= WS_MAX_QUEUED_MESSAGES) || (_status != WS_CONNECTED);
;
return (size >= WS_MAX_QUEUED_MESSAGES) || (_status != WS_CONNECTED);
} }
size_t AsyncWebSocketClient::queueLen() const { size_t AsyncWebSocketClient::queueLen() const {
#ifdef ESP32 #ifdef ESP32
std::lock_guard<std::mutex> lock(_lock); std::lock_guard<std::mutex> lock(_lock);
#endif #endif
return _messageQueue.size(); return _messageQueue.size();
} }
@ -391,38 +391,43 @@ bool AsyncWebSocketClient::canSend() const {
return _messageQueue.size() < WS_MAX_QUEUED_MESSAGES; 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) if (!_client)
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();
}
void AsyncWebSocketClient::_queueMessage(AsyncWebSocketSharedBuffer buffer, uint8_t opcode, bool mask) {
if (!_client || buffer->size() == 0 || _status != WS_CONNECTED)
return;
#ifdef ESP32 #ifdef ESP32
std::lock_guard<std::mutex> lock(_lock); std::lock_guard<std::mutex> lock(_lock);
#endif #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 (_messageQueue.size() >= WS_MAX_QUEUED_MESSAGES) {
if (closeWhenFull) { if (closeWhenFull) {
_status = WS_DISCONNECTED;
if (_client)
_client->close(true);
#ifdef ESP8266 #ifdef ESP8266
ets_printf("AsyncWebSocketClient::_queueMessage: Too many messages queued: closing connection\n"); ets_printf("AsyncWebSocketClient::_queueMessage: Too many messages queued: closing connection\n");
#elif defined(ESP32) #elif defined(ESP32)
log_e("Too many messages queued: closing connection"); log_e("Too many messages queued: closing connection");
#endif #endif
_status = WS_DISCONNECTED;
if (_client)
_client->close(true);
} else { } else {
#ifdef ESP8266 #ifdef ESP8266
ets_printf("AsyncWebSocketClient::_queueMessage: Too many messages queued: discarding new message\n"); 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"); log_e("Too many messages queued: discarding new message");
#endif #endif
} }
return;
} else { return false;
_messageQueue.emplace_back(buffer, opcode, mask);
} }
_messageQueue.emplace_back(buffer, opcode, mask);
if (_client && _client->canSend()) if (_client && _client->canSend())
_runQueue(); _runQueue();
return true;
} }
void AsyncWebSocketClient::close(uint16_t code, const char* message) { 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); _queueControl(WS_DISCONNECT);
} }
void AsyncWebSocketClient::ping(const uint8_t* data, size_t len) { bool AsyncWebSocketClient::ping(const uint8_t* data, size_t len) {
if (_status == WS_CONNECTED) return _status == WS_CONNECTED && _queueControl(WS_PING, data, len);
_queueControl(WS_PING, data, len);
} }
void AsyncWebSocketClient::_onError(int8_t) { void AsyncWebSocketClient::_onError(int8_t) {
@ -476,6 +483,8 @@ void AsyncWebSocketClient::_onError(int8_t) {
} }
void AsyncWebSocketClient::_onTimeout(uint32_t time) { void AsyncWebSocketClient::_onTimeout(uint32_t time) {
if (!_client)
return;
// Serial.println("onTime"); // Serial.println("onTime");
(void)time; (void)time;
_client->close(true); _client->close(true);
@ -483,7 +492,7 @@ void AsyncWebSocketClient::_onTimeout(uint32_t time) {
void AsyncWebSocketClient::_onDisconnect() { void AsyncWebSocketClient::_onDisconnect() {
// Serial.println("onDis"); // Serial.println("onDis");
_client = NULL; _client = nullptr;
} }
void AsyncWebSocketClient::_onData(void* pbuf, size_t plen) { void AsyncWebSocketClient::_onData(void* pbuf, size_t plen) {
@ -535,7 +544,7 @@ void AsyncWebSocketClient::_onData(void* pbuf, size_t plen) {
} }
} }
if (datalen > 0) 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; _pinfo.index += datalen;
} else if ((datalen + _pinfo.index) == _pinfo.len) { } else if ((datalen + _pinfo.index) == _pinfo.len) {
@ -550,18 +559,21 @@ void AsyncWebSocketClient::_onData(void* pbuf, size_t plen) {
} }
if (_status == WS_DISCONNECTING) { if (_status == WS_DISCONNECTING) {
_status = WS_DISCONNECTED; _status = WS_DISCONNECTED;
_client->close(true); if (_client)
_client->close(true);
} else { } else {
_status = WS_DISCONNECTING; _status = WS_DISCONNECTING;
_client->ackLater(); if (_client)
_client->ackLater();
_queueControl(WS_DISCONNECT, data, datalen); _queueControl(WS_DISCONNECT, data, datalen);
} }
} else if (_pinfo.opcode == WS_PING) { } else if (_pinfo.opcode == WS_PING) {
_server->_handleEvent(this, WS_EVT_PING, NULL, NULL, 0);
_queueControl(WS_PONG, data, datalen); _queueControl(WS_PONG, data, datalen);
} else if (_pinfo.opcode == WS_PONG) { } else if (_pinfo.opcode == WS_PONG) {
if (datalen != AWSC_PING_PAYLOAD_LEN || memcmp(AWSC_PING_PAYLOAD, data, AWSC_PING_PAYLOAD_LEN) != 0) 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); _server->_handleEvent(this, WS_EVT_PONG, NULL, NULL, 0);
} else if (_pinfo.opcode < 8) { // continuation or text/binary frame } else if (_pinfo.opcode < WS_DISCONNECT) { // continuation or text/binary frame
_server->_handleEvent(this, WS_EVT_DATA, (void*)&_pinfo, data, datalen); _server->_handleEvent(this, WS_EVT_DATA, (void*)&_pinfo, data, datalen);
if (_pinfo.final) if (_pinfo.final)
_pinfo.num = 0; _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; // 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] = datalast;
data += datalen; data += datalen;
@ -601,9 +613,9 @@ size_t AsyncWebSocketClient::printf(const char* format, ...) {
len = vsnprintf(buffer, len + 1, format, arg); len = vsnprintf(buffer, len + 1, format, arg);
va_end(arg); va_end(arg);
text(buffer, len); bool enqueued = text(buffer, len);
delete[] buffer; delete[] buffer;
return len; return enqueued ? len : 0;
} }
#ifdef ESP8266 #ifdef ESP8266
@ -625,9 +637,9 @@ size_t AsyncWebSocketClient::printf_P(PGM_P formatP, ...) {
len = vsnprintf_P(buffer, len + 1, formatP, arg); len = vsnprintf_P(buffer, len + 1, formatP, arg);
va_end(arg); va_end(arg);
text(buffer, len); bool enqueued = text(buffer, len);
delete[] buffer; delete[] buffer;
return len; return enqueued ? len : 0;
} }
#endif #endif
@ -639,35 +651,37 @@ namespace {
} }
} }
void AsyncWebSocketClient::text(AsyncWebSocketMessageBuffer* buffer) { bool AsyncWebSocketClient::text(AsyncWebSocketMessageBuffer* buffer) {
bool enqueued = false;
if (buffer) { if (buffer) {
text(std::move(buffer->_buffer)); enqueued = text(std::move(buffer->_buffer));
delete buffer; delete buffer;
} }
return enqueued;
} }
void AsyncWebSocketClient::text(AsyncWebSocketSharedBuffer buffer) { bool AsyncWebSocketClient::text(AsyncWebSocketSharedBuffer buffer) {
_queueMessage(buffer); return _queueMessage(buffer);
} }
void AsyncWebSocketClient::text(const uint8_t* message, size_t len) { bool AsyncWebSocketClient::text(const uint8_t* message, size_t len) {
text(makeSharedBuffer(message, len)); return text(makeSharedBuffer(message, len));
} }
void AsyncWebSocketClient::text(const char* message, size_t len) { bool AsyncWebSocketClient::text(const char* message, size_t len) {
text((const uint8_t*)message, len); return text((const uint8_t*)message, len);
} }
void AsyncWebSocketClient::text(const char* message) { bool AsyncWebSocketClient::text(const char* message) {
text(message, strlen(message)); return text(message, strlen(message));
} }
void AsyncWebSocketClient::text(const String& message) { bool AsyncWebSocketClient::text(const String& message) {
text(message.c_str(), message.length()); return text(message.c_str(), message.length());
} }
#ifdef ESP8266 #ifdef ESP8266
void AsyncWebSocketClient::text(const __FlashStringHelper* data) { bool AsyncWebSocketClient::text(const __FlashStringHelper* data) {
PGM_P p = reinterpret_cast<PGM_P>(data); PGM_P p = reinterpret_cast<PGM_P>(data);
size_t n = 0; size_t n = 0;
@ -678,51 +692,57 @@ void AsyncWebSocketClient::text(const __FlashStringHelper* data) {
} }
char* message = (char*)malloc(n + 1); char* message = (char*)malloc(n + 1);
bool enqueued = false;
if (message) { if (message) {
memcpy_P(message, p, n); memcpy_P(message, p, n);
message[n] = 0; message[n] = 0;
text(message, n); enqueued = text(message, n);
free(message); free(message);
} }
return enqueued;
} }
#endif // ESP8266 #endif // ESP8266
void AsyncWebSocketClient::binary(AsyncWebSocketMessageBuffer* buffer) { bool AsyncWebSocketClient::binary(AsyncWebSocketMessageBuffer* buffer) {
bool enqueued = false;
if (buffer) { if (buffer) {
binary(std::move(buffer->_buffer)); enqueued = binary(std::move(buffer->_buffer));
delete buffer; delete buffer;
} }
return enqueued;
} }
void AsyncWebSocketClient::binary(AsyncWebSocketSharedBuffer buffer) { bool AsyncWebSocketClient::binary(AsyncWebSocketSharedBuffer buffer) {
_queueMessage(buffer, WS_BINARY); return _queueMessage(buffer, WS_BINARY);
} }
void AsyncWebSocketClient::binary(const uint8_t* message, size_t len) { bool AsyncWebSocketClient::binary(const uint8_t* message, size_t len) {
binary(makeSharedBuffer(message, len)); return binary(makeSharedBuffer(message, len));
} }
void AsyncWebSocketClient::binary(const char* message, size_t len) { bool AsyncWebSocketClient::binary(const char* message, size_t len) {
binary((const uint8_t*)message, len); return binary((const uint8_t*)message, len);
} }
void AsyncWebSocketClient::binary(const char* message) { bool AsyncWebSocketClient::binary(const char* message) {
binary(message, strlen(message)); return binary(message, strlen(message));
} }
void AsyncWebSocketClient::binary(const String& message) { bool AsyncWebSocketClient::binary(const String& message) {
binary(message.c_str(), message.length()); return binary(message.c_str(), message.length());
} }
#ifdef ESP8266 #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); PGM_P p = reinterpret_cast<PGM_P>(data);
char* message = (char*)malloc(len); char* message = (char*)malloc(len);
bool enqueued = false;
if (message) { if (message) {
memcpy_P(message, p, len); memcpy_P(message, p, len);
binary(message, len); enqueued = binary(message, len);
free(message); free(message);
} }
return enqueued;
} }
#endif #endif
@ -801,33 +821,38 @@ void AsyncWebSocket::cleanupClients(uint16_t maxClients) {
} }
} }
void AsyncWebSocket::ping(uint32_t id, const uint8_t* data, size_t len) { bool AsyncWebSocket::ping(uint32_t id, const uint8_t* data, size_t len) {
if (AsyncWebSocketClient* c = client(id)) AsyncWebSocketClient* c = client(id);
c->ping(data, len); 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) for (auto& c : _clients)
if (c.status() == WS_CONNECTED) if (c.status() == WS_CONNECTED && c.ping(data, len))
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) { bool AsyncWebSocket::text(uint32_t id, const uint8_t* message, size_t len) {
if (AsyncWebSocketClient* c = client(id)) AsyncWebSocketClient* c = client(id);
c->text(makeSharedBuffer(message, len)); return c && c->text(makeSharedBuffer(message, len));
} }
void AsyncWebSocket::text(uint32_t id, const char* message, size_t len) { bool AsyncWebSocket::text(uint32_t id, const char* message, size_t len) {
text(id, (const uint8_t*)message, len); return text(id, (const uint8_t*)message, len);
} }
void AsyncWebSocket::text(uint32_t id, const char* message) { bool AsyncWebSocket::text(uint32_t id, const char* message) {
text(id, message, strlen(message)); return text(id, message, strlen(message));
} }
void AsyncWebSocket::text(uint32_t id, const String& message) { bool AsyncWebSocket::text(uint32_t id, const String& message) {
text(id, message.c_str(), message.length()); return text(id, message.c_str(), message.length());
} }
#ifdef ESP8266 #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); PGM_P p = reinterpret_cast<PGM_P>(data);
size_t n = 0; size_t n = 0;
@ -838,40 +863,44 @@ void AsyncWebSocket::text(uint32_t id, const __FlashStringHelper* data) {
} }
char* message = (char*)malloc(n + 1); char* message = (char*)malloc(n + 1);
bool enqueued = false;
if (message) { if (message) {
memcpy_P(message, p, n); memcpy_P(message, p, n);
message[n] = 0; message[n] = 0;
text(id, message, n); enqueued = text(id, message, n);
free(message); free(message);
} }
return enqueued;
} }
#endif // ESP8266 #endif // ESP8266
void AsyncWebSocket::text(uint32_t id, AsyncWebSocketMessageBuffer* buffer) { bool AsyncWebSocket::text(uint32_t id, AsyncWebSocketMessageBuffer* buffer) {
bool enqueued = false;
if (buffer) { if (buffer) {
text(id, std::move(buffer->_buffer)); enqueued = text(id, std::move(buffer->_buffer));
delete buffer; delete buffer;
} }
return enqueued;
} }
void AsyncWebSocket::text(uint32_t id, AsyncWebSocketSharedBuffer buffer) { bool AsyncWebSocket::text(uint32_t id, AsyncWebSocketSharedBuffer buffer) {
if (AsyncWebSocketClient* c = client(id)) AsyncWebSocketClient* c = client(id);
c->text(buffer); return c && c->text(buffer);
} }
void AsyncWebSocket::textAll(const uint8_t* message, size_t len) { AsyncWebSocket::SendStatus AsyncWebSocket::textAll(const uint8_t* message, size_t len) {
textAll(makeSharedBuffer(message, len)); return textAll(makeSharedBuffer(message, len));
} }
void AsyncWebSocket::textAll(const char* message, size_t len) { AsyncWebSocket::SendStatus AsyncWebSocket::textAll(const char* message, size_t len) {
textAll((const uint8_t*)message, len); return textAll((const uint8_t*)message, len);
} }
void AsyncWebSocket::textAll(const char* message) { AsyncWebSocket::SendStatus AsyncWebSocket::textAll(const char* message) {
textAll(message, strlen(message)); return textAll(message, strlen(message));
} }
void AsyncWebSocket::textAll(const String& message) { AsyncWebSocket::SendStatus AsyncWebSocket::textAll(const String& message) {
textAll(message.c_str(), message.length()); return textAll(message.c_str(), message.length());
} }
#ifdef ESP8266 #ifdef ESP8266
void AsyncWebSocket::textAll(const __FlashStringHelper* data) { AsyncWebSocket::SendStatus AsyncWebSocket::textAll(const __FlashStringHelper* data) {
PGM_P p = reinterpret_cast<PGM_P>(data); PGM_P p = reinterpret_cast<PGM_P>(data);
size_t n = 0; size_t n = 0;
@ -882,99 +911,121 @@ void AsyncWebSocket::textAll(const __FlashStringHelper* data) {
} }
char* message = (char*)malloc(n + 1); char* message = (char*)malloc(n + 1);
AsyncWebSocket::SendStatus status = DISCARDED;
if (message) { if (message) {
memcpy_P(message, p, n); memcpy_P(message, p, n);
message[n] = 0; message[n] = 0;
textAll(message, n); status = textAll(message, n);
free(message); free(message);
} }
return status;
} }
#endif // ESP8266 #endif // ESP8266
void AsyncWebSocket::textAll(AsyncWebSocketMessageBuffer* buffer) { AsyncWebSocket::SendStatus AsyncWebSocket::textAll(AsyncWebSocketMessageBuffer* buffer) {
AsyncWebSocket::SendStatus status = DISCARDED;
if (buffer) { if (buffer) {
textAll(std::move(buffer->_buffer)); status = textAll(std::move(buffer->_buffer));
delete 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) for (auto& c : _clients)
if (c.status() == WS_CONNECTED) if (c.status() == WS_CONNECTED && c.text(buffer))
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) { bool AsyncWebSocket::binary(uint32_t id, const uint8_t* message, size_t len) {
if (AsyncWebSocketClient* c = client(id)) AsyncWebSocketClient* c = client(id);
c->binary(makeSharedBuffer(message, len)); return c && c->binary(makeSharedBuffer(message, len));
} }
void AsyncWebSocket::binary(uint32_t id, const char* message, size_t len) { bool AsyncWebSocket::binary(uint32_t id, const char* message, size_t len) {
binary(id, (const uint8_t*)message, len); return binary(id, (const uint8_t*)message, len);
} }
void AsyncWebSocket::binary(uint32_t id, const char* message) { bool AsyncWebSocket::binary(uint32_t id, const char* message) {
binary(id, message, strlen(message)); return binary(id, message, strlen(message));
} }
void AsyncWebSocket::binary(uint32_t id, const String& message) { bool AsyncWebSocket::binary(uint32_t id, const String& message) {
binary(id, message.c_str(), message.length()); return binary(id, message.c_str(), message.length());
} }
#ifdef ESP8266 #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); PGM_P p = reinterpret_cast<PGM_P>(data);
char* message = (char*)malloc(len); char* message = (char*)malloc(len);
bool enqueued = false;
if (message) { if (message) {
memcpy_P(message, p, len); memcpy_P(message, p, len);
binary(id, message, len); enqueued = binary(id, message, len);
free(message); free(message);
} }
return enqueued;
} }
#endif // ESP8266 #endif // ESP8266
void AsyncWebSocket::binary(uint32_t id, AsyncWebSocketMessageBuffer* buffer) { bool AsyncWebSocket::binary(uint32_t id, AsyncWebSocketMessageBuffer* buffer) {
bool enqueued = false;
if (buffer) { if (buffer) {
binary(id, std::move(buffer->_buffer)); enqueued = binary(id, std::move(buffer->_buffer));
delete buffer; delete buffer;
} }
return enqueued;
} }
void AsyncWebSocket::binary(uint32_t id, AsyncWebSocketSharedBuffer buffer) { bool AsyncWebSocket::binary(uint32_t id, AsyncWebSocketSharedBuffer buffer) {
if (AsyncWebSocketClient* c = client(id)) AsyncWebSocketClient* c = client(id);
c->binary(buffer); return c && c->binary(buffer);
} }
void AsyncWebSocket::binaryAll(const uint8_t* message, size_t len) { AsyncWebSocket::SendStatus AsyncWebSocket::binaryAll(const uint8_t* message, size_t len) {
binaryAll(makeSharedBuffer(message, len)); return binaryAll(makeSharedBuffer(message, len));
} }
void AsyncWebSocket::binaryAll(const char* message, size_t len) { AsyncWebSocket::SendStatus AsyncWebSocket::binaryAll(const char* message, size_t len) {
binaryAll((const uint8_t*)message, len); return binaryAll((const uint8_t*)message, len);
} }
void AsyncWebSocket::binaryAll(const char* message) { AsyncWebSocket::SendStatus AsyncWebSocket::binaryAll(const char* message) {
binaryAll(message, strlen(message)); return binaryAll(message, strlen(message));
} }
void AsyncWebSocket::binaryAll(const String& message) { AsyncWebSocket::SendStatus AsyncWebSocket::binaryAll(const String& message) {
binaryAll(message.c_str(), message.length()); return binaryAll(message.c_str(), message.length());
} }
#ifdef ESP8266 #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); PGM_P p = reinterpret_cast<PGM_P>(data);
char* message = (char*)malloc(len); char* message = (char*)malloc(len);
AsyncWebSocket::SendStatus status = DISCARDED;
if (message) { if (message) {
memcpy_P(message, p, len); memcpy_P(message, p, len);
binaryAll(message, len); status = binaryAll(message, len);
free(message); free(message);
} }
return status;
} }
#endif // ESP8266 #endif // ESP8266
void AsyncWebSocket::binaryAll(AsyncWebSocketMessageBuffer* buffer) { AsyncWebSocket::SendStatus AsyncWebSocket::binaryAll(AsyncWebSocketMessageBuffer* buffer) {
AsyncWebSocket::SendStatus status = DISCARDED;
if (buffer) { if (buffer) {
binaryAll(std::move(buffer->_buffer)); status = binaryAll(std::move(buffer->_buffer));
delete 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) for (auto& c : _clients)
if (c.status() == WS_CONNECTED) if (c.status() == WS_CONNECTED && c.binary(buffer))
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, ...) { 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); len = vsnprintf(buffer, len + 1, format, arg);
va_end(arg); va_end(arg);
textAll(buffer, len); AsyncWebSocket::SendStatus status = textAll(buffer, len);
delete[] buffer; delete[] buffer;
return len; return status == DISCARDED ? 0 : len;
} }
#ifdef ESP8266 #ifdef ESP8266
@ -1043,9 +1094,9 @@ size_t AsyncWebSocket::printfAll_P(PGM_P formatP, ...) {
len = vsnprintf_P(buffer, len + 1, formatP, arg); len = vsnprintf_P(buffer, len + 1, formatP, arg);
va_end(arg); va_end(arg);
textAll(buffer, len); AsyncWebSocket::SendStatus status = textAll(buffer, len);
delete[] buffer; delete[] buffer;
return len; return status == DISCARDED ? 0 : len;
} }
#endif #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_ACCEPT FPSTR(__WS_STR_ACCEPT)
#define WS_STR_UUID FPSTR(__WS_STR_UUID) #define WS_STR_UUID FPSTR(__WS_STR_UUID)
bool AsyncWebSocket::canHandle(AsyncWebServerRequest* request) { bool AsyncWebSocket::canHandle(AsyncWebServerRequest* request) const {
if (!_enabled) return _enabled && request->isWebSocketUpgrade() && request->url().equals(_url);
return false;
if (request->method() != HTTP_GET || !request->url().equals(_url) || !request->isExpectedRequestedConnType(RCT_WS))
return false;
return true;
} }
void AsyncWebSocket::handleRequest(AsyncWebServerRequest* request) { void AsyncWebSocket::handleRequest(AsyncWebServerRequest* request) {

View File

@ -104,6 +104,7 @@ typedef enum { WS_MSG_SENDING,
WS_MSG_ERROR } AwsMessageStatus; WS_MSG_ERROR } AwsMessageStatus;
typedef enum { WS_EVT_CONNECT, typedef enum { WS_EVT_CONNECT,
WS_EVT_DISCONNECT, WS_EVT_DISCONNECT,
WS_EVT_PING,
WS_EVT_PONG, WS_EVT_PONG,
WS_EVT_ERROR, WS_EVT_ERROR,
WS_EVT_DATA } AwsEventType; WS_EVT_DATA } AwsEventType;
@ -164,8 +165,8 @@ class AsyncWebSocketClient {
uint32_t _lastMessageTime; uint32_t _lastMessageTime;
uint32_t _keepAlivePeriod; uint32_t _keepAlivePeriod;
void _queueControl(uint8_t opcode, const uint8_t* data = NULL, size_t len = 0, bool mask = false); bool _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 _queueMessage(AsyncWebSocketSharedBuffer buffer, uint8_t opcode = WS_TEXT, bool mask = false);
void _runQueue(); void _runQueue();
void _clearQueue(); void _clearQueue();
@ -212,7 +213,7 @@ class AsyncWebSocketClient {
// control frames // control frames
void close(uint16_t code = 0, const char* message = NULL); void close(uint16_t code = 0, const char* message = NULL);
void ping(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) // set auto-ping period in seconds. disabled if zero (default)
void keepAlivePeriod(uint16_t seconds) { void keepAlivePeriod(uint16_t seconds) {
@ -229,19 +230,19 @@ class AsyncWebSocketClient {
size_t printf(const char* format, ...) __attribute__((format(printf, 2, 3))); size_t printf(const char* format, ...) __attribute__((format(printf, 2, 3)));
void text(AsyncWebSocketSharedBuffer buffer); bool text(AsyncWebSocketSharedBuffer buffer);
void text(const uint8_t* message, size_t len); bool text(const uint8_t* message, size_t len);
void text(const char* message, size_t len); bool text(const char* message, size_t len);
void text(const char* message); bool text(const char* message);
void text(const String& message); bool text(const String& message);
void text(AsyncWebSocketMessageBuffer* buffer); bool text(AsyncWebSocketMessageBuffer* buffer);
void binary(AsyncWebSocketSharedBuffer buffer); bool binary(AsyncWebSocketSharedBuffer buffer);
void binary(const uint8_t* message, size_t len); bool binary(const uint8_t* message, size_t len);
void binary(const char* message, size_t len); bool binary(const char* message, size_t len);
void binary(const char* message); bool binary(const char* message);
void binary(const String& message); bool binary(const String& message);
void binary(AsyncWebSocketMessageBuffer* buffer); bool binary(AsyncWebSocketMessageBuffer* buffer);
bool canSend() const; bool canSend() const;
@ -255,8 +256,8 @@ class AsyncWebSocketClient {
#ifdef ESP8266 #ifdef ESP8266
size_t printf_P(PGM_P formatP, ...) __attribute__((format(printf, 2, 3))); size_t printf_P(PGM_P formatP, ...) __attribute__((format(printf, 2, 3)));
void text(const __FlashStringHelper* message); bool text(const __FlashStringHelper* message);
void binary(const __FlashStringHelper* message, size_t len); bool binary(const __FlashStringHelper* message, size_t len);
#endif #endif
}; };
@ -277,6 +278,12 @@ class AsyncWebSocket : public AsyncWebHandler {
#endif #endif
public: public:
typedef enum {
DISCARDED = 0,
ENQUEUED = 1,
PARTIALLY_ENQUEUED = 2,
} SendStatus;
explicit AsyncWebSocket(const char* url) : _url(url), _cNextId(1), _enabled(true) {} explicit AsyncWebSocket(const char* url) : _url(url), _cNextId(1), _enabled(true) {}
AsyncWebSocket(const String& url) : _url(url), _cNextId(1), _enabled(true) {} AsyncWebSocket(const String& url) : _url(url), _cNextId(1), _enabled(true) {}
~AsyncWebSocket() {}; ~AsyncWebSocket() {};
@ -294,45 +301,45 @@ class AsyncWebSocket : public AsyncWebHandler {
void closeAll(uint16_t code = 0, const char* message = NULL); void closeAll(uint16_t code = 0, const char* message = NULL);
void cleanupClients(uint16_t maxClients = DEFAULT_MAX_WS_CLIENTS); void cleanupClients(uint16_t maxClients = DEFAULT_MAX_WS_CLIENTS);
void ping(uint32_t id, const uint8_t* data = NULL, size_t len = 0); bool 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 SendStatus pingAll(const uint8_t* data = NULL, size_t len = 0); // done
void text(uint32_t id, const uint8_t* message, size_t len); bool text(uint32_t id, const uint8_t* message, size_t len);
void text(uint32_t id, const char* message, size_t len); bool text(uint32_t id, const char* message, size_t len);
void text(uint32_t id, const char* message); bool text(uint32_t id, const char* message);
void text(uint32_t id, const String& message); bool text(uint32_t id, const String& message);
void text(uint32_t id, AsyncWebSocketMessageBuffer* buffer); bool text(uint32_t id, AsyncWebSocketMessageBuffer* buffer);
void text(uint32_t id, AsyncWebSocketSharedBuffer buffer); bool text(uint32_t id, AsyncWebSocketSharedBuffer buffer);
void textAll(const uint8_t* message, size_t len); SendStatus textAll(const uint8_t* message, size_t len);
void textAll(const char* message, size_t len); SendStatus textAll(const char* message, size_t len);
void textAll(const char* message); SendStatus textAll(const char* message);
void textAll(const String& message); SendStatus textAll(const String& message);
void textAll(AsyncWebSocketMessageBuffer* buffer); SendStatus textAll(AsyncWebSocketMessageBuffer* buffer);
void textAll(AsyncWebSocketSharedBuffer buffer); SendStatus textAll(AsyncWebSocketSharedBuffer buffer);
void binary(uint32_t id, const uint8_t* message, size_t len); bool binary(uint32_t id, const uint8_t* message, size_t len);
void binary(uint32_t id, const char* message, size_t len); bool binary(uint32_t id, const char* message, size_t len);
void binary(uint32_t id, const char* message); bool binary(uint32_t id, const char* message);
void binary(uint32_t id, const String& message); bool binary(uint32_t id, const String& message);
void binary(uint32_t id, AsyncWebSocketMessageBuffer* buffer); bool binary(uint32_t id, AsyncWebSocketMessageBuffer* buffer);
void binary(uint32_t id, AsyncWebSocketSharedBuffer buffer); bool binary(uint32_t id, AsyncWebSocketSharedBuffer buffer);
void binaryAll(const uint8_t* message, size_t len); SendStatus binaryAll(const uint8_t* message, size_t len);
void binaryAll(const char* message, size_t len); SendStatus binaryAll(const char* message, size_t len);
void binaryAll(const char* message); SendStatus binaryAll(const char* message);
void binaryAll(const String& message); SendStatus binaryAll(const String& message);
void binaryAll(AsyncWebSocketMessageBuffer* buffer); SendStatus binaryAll(AsyncWebSocketMessageBuffer* buffer);
void binaryAll(AsyncWebSocketSharedBuffer buffer); SendStatus binaryAll(AsyncWebSocketSharedBuffer buffer);
size_t printf(uint32_t id, const char* format, ...) __attribute__((format(printf, 3, 4))); size_t printf(uint32_t id, const char* format, ...) __attribute__((format(printf, 3, 4)));
size_t printfAll(const char* format, ...) __attribute__((format(printf, 2, 3))); size_t printfAll(const char* format, ...) __attribute__((format(printf, 2, 3)));
#ifdef ESP8266 #ifdef ESP8266
void text(uint32_t id, const __FlashStringHelper* message); bool text(uint32_t id, const __FlashStringHelper* message);
void textAll(const __FlashStringHelper* message); SendStatus textAll(const __FlashStringHelper* message);
void binary(uint32_t id, const __FlashStringHelper* message, size_t len); bool binary(uint32_t id, const __FlashStringHelper* message, size_t len);
void binaryAll(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 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))); size_t printfAll_P(PGM_P formatP, ...) __attribute__((format(printf, 2, 3)));
#endif #endif
@ -344,8 +351,8 @@ class AsyncWebSocket : public AsyncWebHandler {
uint32_t _getNextId() { return _cNextId++; } uint32_t _getNextId() { return _cNextId++; }
AsyncWebSocketClient* _newClient(AsyncWebServerRequest* request); AsyncWebSocketClient* _newClient(AsyncWebServerRequest* request);
void _handleEvent(AsyncWebSocketClient* client, AwsEventType type, void* arg, uint8_t* data, size_t len); void _handleEvent(AsyncWebSocketClient* client, AwsEventType type, void* arg, uint8_t* data, size_t len);
virtual bool canHandle(AsyncWebServerRequest* request) override final; bool canHandle(AsyncWebServerRequest* request) const override final;
virtual void handleRequest(AsyncWebServerRequest* request) override final; void handleRequest(AsyncWebServerRequest* request) override final;
// messagebuffer functions/objects. // messagebuffer functions/objects.
AsyncWebSocketMessageBuffer* makeBuffer(size_t size = 0); AsyncWebSocketMessageBuffer* makeBuffer(size_t size = 0);

View File

@ -12,7 +12,6 @@ class ChunkPrint : public Print {
public: public:
ChunkPrint(uint8_t* destination, size_t from, size_t len); ChunkPrint(uint8_t* destination, size_t from, size_t len);
virtual ~ChunkPrint() {}
size_t write(uint8_t c); size_t write(uint8_t c);
size_t write(const uint8_t* buffer, size_t size) { return this->Print::write(buffer, size); } size_t write(const uint8_t* buffer, size_t size) { return this->Print::write(buffer, size); }
}; };

View File

@ -48,10 +48,10 @@
#include "literals.h" #include "literals.h"
#define ASYNCWEBSERVER_VERSION "3.3.7" #define ASYNCWEBSERVER_VERSION "3.3.23"
#define ASYNCWEBSERVER_VERSION_MAJOR 3 #define ASYNCWEBSERVER_VERSION_MAJOR 3
#define ASYNCWEBSERVER_VERSION_MINOR 3 #define ASYNCWEBSERVER_VERSION_MINOR 3
#define ASYNCWEBSERVER_VERSION_REVISION 7 #define ASYNCWEBSERVER_VERSION_REVISION 23
#define ASYNCWEBSERVER_FORK_mathieucarbou #define ASYNCWEBSERVER_FORK_mathieucarbou
#ifdef ASYNCWEBSERVER_REGEX #ifdef ASYNCWEBSERVER_REGEX
@ -140,7 +140,6 @@ class AsyncWebHeader {
String _value; String _value;
public: public:
AsyncWebHeader() = default;
AsyncWebHeader(const AsyncWebHeader&) = default; AsyncWebHeader(const AsyncWebHeader&) = default;
AsyncWebHeader(const char* name, const char* value) : _name(name), _value(value) {} AsyncWebHeader(const char* name, const char* value) : _name(name), _value(value) {}
AsyncWebHeader(const String& name, const String& 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 // this enum is similar to Arduino WebServer's AsyncAuthType and PsychicHttp
typedef enum { typedef enum {
AUTH_NONE = 0, AUTH_NONE = 0, // always allow
AUTH_BASIC, AUTH_BASIC = 1,
AUTH_DIGEST, AUTH_DIGEST = 2,
AUTH_BEARER, AUTH_BEARER = 3,
AUTH_OTHER, AUTH_OTHER = 4,
AUTH_DENIED = 255, // always returns 401
} AsyncAuthType; } AsyncAuthType;
typedef std::function<size_t(uint8_t*, size_t, size_t)> AwsResponseFiller; typedef std::function<size_t(uint8_t*, size_t, size_t)> AwsResponseFiller;
@ -264,23 +264,21 @@ class AsyncWebServerRequest {
size_t contentLength() const { return _contentLength; } size_t contentLength() const { return _contentLength; }
bool multipart() const { return _isMultipart; } bool multipart() const { return _isMultipart; }
#ifndef ESP8266
const char* methodToString() const; const char* methodToString() const;
const char* requestedConnTypeToString() const; const char* requestedConnTypeToString() const;
#else
const __FlashStringHelper* methodToString() const;
const __FlashStringHelper* requestedConnTypeToString() const;
#endif
RequestedConnectionType requestedConnType() const { return _reqconntype; } 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); void onDisconnect(ArDisconnectHandler fn);
// hash is the string representation of: // hash is the string representation of:
// base64(user:pass) for basic or // base64(user:pass) for basic or
// user:realm:md5(user:realm:pass) for digest // user:realm:md5(user:realm:pass) for digest
bool authenticate(const char* hash); bool authenticate(const char* hash) const;
bool authenticate(const char* username, const char* credentials, const char* realm = NULL, bool isHash = false); 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(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); 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 { class AsyncMiddleware {
public: public:
virtual ~AsyncMiddleware() {} virtual ~AsyncMiddleware() {}
virtual void run(__unused AsyncWebServerRequest* request, __unused ArMiddlewareNext next) { virtual void run(__unused AsyncWebServerRequest* request, __unused ArMiddlewareNext next) { return next(); };
return next();
};
private: private:
friend class AsyncWebHandler; 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 // For internal use only: super class to add/remove middleware to server or handlers
class AsyncMiddlewareChain { class AsyncMiddlewareChain {
public: public:
virtual ~AsyncMiddlewareChain(); ~AsyncMiddlewareChain();
void addMiddleware(ArMiddlewareCallback fn); void addMiddleware(ArMiddlewareCallback fn);
void addMiddleware(AsyncMiddleware* middleware); void addMiddleware(AsyncMiddleware* middleware);
@ -570,13 +566,26 @@ class AuthenticationMiddleware : public AsyncMiddleware {
void setRealm(const char* realm) { _realm = realm; } void setRealm(const char* realm) { _realm = realm; }
void setAuthFailureMessage(const char* message) { _authFailMsg = message; } 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; } 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 // returns true if the hash was successfully generated and replaced
bool generateHash(); 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); void run(AsyncWebServerRequest* request, ArMiddlewareNext next);
@ -634,7 +643,7 @@ class LoggingMiddleware : public AsyncMiddleware {
public: public:
void setOutput(Print& output) { _out = &output; } void setOutput(Print& output) { _out = &output; }
void setEnabled(bool enabled) { _enabled = enabled; } void setEnabled(bool enabled) { _enabled = enabled; }
bool isEnabled() { return _enabled && _out; } bool isEnabled() const { return _enabled && _out; }
void run(AsyncWebServerRequest* request, ArMiddlewareNext next); void run(AsyncWebServerRequest* request, ArMiddlewareNext next);
@ -722,18 +731,16 @@ class AsyncWebHandler : public AsyncMiddlewareChain {
public: public:
AsyncWebHandler() {} 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 ~AsyncWebHandler() {}
virtual bool canHandle(AsyncWebServerRequest* request __attribute__((unused))) { AsyncWebHandler& setFilter(ArRequestFilterFunction fn);
return false; 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 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 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 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; WebResponseState _state;
public: public:
#ifndef ESP8266
static const char* responseCodeToString(int code); static const char* responseCodeToString(int code);
#else
static const __FlashStringHelper* responseCodeToString(int code);
#endif
public: public:
AsyncWebServerResponse(); AsyncWebServerResponse();
virtual ~AsyncWebServerResponse(); virtual ~AsyncWebServerResponse() {}
virtual void setCode(int code); void setCode(int code);
int code() const { return _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()); } void setContentType(const String& type) { setContentType(type.c_str()); }
virtual void setContentType(const char* type); void setContentType(const char* type);
virtual bool addHeader(const char* name, const char* value, bool replaceExisting = true); 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 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 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); } bool addHeader(const String& name, long value, bool replaceExisting = true) { return addHeader(name.c_str(), value, replaceExisting); }
virtual bool removeHeader(const char* name); bool removeHeader(const char* name);
virtual const AsyncWebHeader* getHeader(const char* name) const; const AsyncWebHeader* getHeader(const char* name) const;
const std::list<AsyncWebHeader>& getHeaders() const { return _headers; } const std::list<AsyncWebHeader>& getHeaders() const { return _headers; }
#ifndef ESP8266 #ifndef ESP8266
@ -794,7 +797,7 @@ class AsyncWebServerResponse {
_assembleHead(buffer, version); _assembleHead(buffer, version);
return buffer; return buffer;
} }
virtual void _assembleHead(String& buffer, uint8_t version); void _assembleHead(String& buffer, uint8_t version);
virtual bool _started() const; virtual bool _started() const;
virtual bool _finished() const; virtual bool _finished() const;

View File

@ -66,7 +66,7 @@ void AuthenticationMiddleware::setPassword(const char* password) {
void AuthenticationMiddleware::setPasswordHash(const char* hash) { void AuthenticationMiddleware::setPasswordHash(const char* hash) {
_credentials = hash; _credentials = hash;
_hash = true; _hash = _credentials.length();
_hasCreds = _username.length() && _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) if (_authMethod == AsyncAuthType::AUTH_NONE)
return true; return true;
if (!_hasCreds) if (_authMethod == AsyncAuthType::AUTH_DENIED)
return false; return false;
if (!_hasCreds)
return true;
return request->authenticate(_username.c_str(), _credentials.c_str(), _realm.c_str(), _hash); 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) { void CorsMiddleware::addCORSHeaders(AsyncWebServerResponse* response) {
response->addHeader(F("Access-Control-Allow-Origin"), _origin.c_str()); response->addHeader(asyncsrv::T_CORS_ACAO, _origin.c_str());
response->addHeader(F("Access-Control-Allow-Methods"), _methods.c_str()); response->addHeader(asyncsrv::T_CORS_ACAM, _methods.c_str());
response->addHeader(F("Access-Control-Allow-Headers"), _headers.c_str()); response->addHeader(asyncsrv::T_CORS_ACAH, _headers.c_str());
response->addHeader(F("Access-Control-Allow-Credentials"), _credentials ? F("true") : F("false")); response->addHeader(asyncsrv::T_CORS_ACAC, _credentials ? asyncsrv::T_TRUE : asyncsrv::T_FALSE);
response->addHeader(F("Access-Control-Max-Age"), String(_maxAge).c_str()); response->addHeader(asyncsrv::T_CORS_ACMA, String(_maxAge).c_str());
} }
void CorsMiddleware::run(AsyncWebServerRequest* request, ArMiddlewareNext next) { void CorsMiddleware::run(AsyncWebServerRequest* request, ArMiddlewareNext next) {
// Origin header ? => CORS handling // 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 // check if this is a preflight request => handle it and return
if (request->method() == HTTP_OPTIONS) { if (request->method() == HTTP_OPTIONS) {
AsyncWebServerResponse* response = request->beginResponse(200); AsyncWebServerResponse* response = request->beginResponse(200);
@ -247,7 +250,7 @@ void RateLimitMiddleware::run(AsyncWebServerRequest* request, ArMiddlewareNext n
next(); next();
} else { } else {
AsyncWebServerResponse* response = request->beginResponse(429); AsyncWebServerResponse* response = request->beginResponse(429);
response->addHeader(F("Retry-After"), retryAfterSeconds); response->addHeader(asyncsrv::T_retry_after, retryAfterSeconds);
request->send(response); request->send(response);
} }
} }

View File

@ -137,11 +137,7 @@ String generateDigestHash(const char* username, const char* password, const char
return in; 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) 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) { if (username == NULL || password == NULL || header == NULL || method == NULL) {
// os_printf("AUTH FAIL: missing requred fields\n"); // os_printf("AUTH FAIL: missing requred fields\n");

View File

@ -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); 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 // for storing hashed versions on the device that can be authenticated against
String generateDigestHash(const char* username, const char* password, const char* realm); String generateDigestHash(const char* username, const char* password, const char* realm);

View File

@ -34,8 +34,8 @@ class AsyncStaticWebHandler : public AsyncWebHandler {
using FS = fs::FS; using FS = fs::FS;
private: private:
bool _getFile(AsyncWebServerRequest* request); bool _getFile(AsyncWebServerRequest* request) const;
bool _fileExists(AsyncWebServerRequest* request, const String& path); bool _searchFile(AsyncWebServerRequest* request, const String& path);
uint8_t _countBits(const uint8_t value) const; uint8_t _countBits(const uint8_t value) const;
protected: protected:
@ -47,13 +47,13 @@ class AsyncStaticWebHandler : public AsyncWebHandler {
String _last_modified; String _last_modified;
AwsTemplateProcessor _callback; AwsTemplateProcessor _callback;
bool _isDir; bool _isDir;
bool _gzipFirst; bool _tryGzipFirst = true;
uint8_t _gzipStats;
public: public:
AsyncStaticWebHandler(const char* uri, FS& fs, const char* path, const char* cache_control); AsyncStaticWebHandler(const char* uri, FS& fs, const char* path, const char* cache_control);
virtual bool canHandle(AsyncWebServerRequest* request) override final; bool canHandle(AsyncWebServerRequest* request) const override final;
virtual void handleRequest(AsyncWebServerRequest* request) override final; void handleRequest(AsyncWebServerRequest* request) override final;
AsyncStaticWebHandler& setTryGzipFirst(bool value);
AsyncStaticWebHandler& setIsDir(bool isDir); AsyncStaticWebHandler& setIsDir(bool isDir);
AsyncStaticWebHandler& setDefaultFile(const char* filename); AsyncStaticWebHandler& setDefaultFile(const char* filename);
AsyncStaticWebHandler& setCacheControl(const char* cache_control); AsyncStaticWebHandler& setCacheControl(const char* cache_control);
@ -84,11 +84,11 @@ class AsyncCallbackWebHandler : public AsyncWebHandler {
void onUpload(ArUploadHandlerFunction fn) { _onUpload = fn; } void onUpload(ArUploadHandlerFunction fn) { _onUpload = fn; }
void onBody(ArBodyHandlerFunction fn) { _onBody = fn; } void onBody(ArBodyHandlerFunction fn) { _onBody = fn; }
virtual bool canHandle(AsyncWebServerRequest* request) override final; bool canHandle(AsyncWebServerRequest* request) const override final;
virtual void handleRequest(AsyncWebServerRequest* request) override final; 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; 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; 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 isRequestHandlerTrivial() const override final { return !_onRequest; }
}; };
#endif /* ASYNCWEBSERVERHANDLERIMPL_H_ */ #endif /* ASYNCWEBSERVERHANDLERIMPL_H_ */

View File

@ -27,7 +27,7 @@ AsyncWebHandler& AsyncWebHandler::setFilter(ArRequestFilterFunction fn) {
_filter = fn; _filter = fn;
return *this; return *this;
} }
AsyncWebHandler& AsyncWebHandler::setAuthentication(const char* username, const char* password) { AsyncWebHandler& AsyncWebHandler::setAuthentication(const char* username, const char* password, AsyncAuthType authMethod) {
if (!_authMiddleware) { if (!_authMiddleware) {
_authMiddleware = new AuthenticationMiddleware(); _authMiddleware = new AuthenticationMiddleware();
_authMiddleware->_freeOnRemoval = true; _authMiddleware->_freeOnRemoval = true;
@ -35,6 +35,7 @@ AsyncWebHandler& AsyncWebHandler::setAuthentication(const char* username, const
} }
_authMiddleware->setUsername(username); _authMiddleware->setUsername(username);
_authMiddleware->setPassword(password); _authMiddleware->setPassword(password);
_authMiddleware->setAuthType(authMethod);
return *this; return *this;
}; };
@ -56,10 +57,11 @@ AsyncStaticWebHandler::AsyncStaticWebHandler(const char* uri, FS& fs, const char
_uri = _uri.substring(0, _uri.length() - 1); _uri = _uri.substring(0, _uri.length() - 1);
if (_path[_path.length() - 1] == '/') if (_path[_path.length() - 1] == '/')
_path = _path.substring(0, _path.length() - 1); _path = _path.substring(0, _path.length() - 1);
}
// Reset stats AsyncStaticWebHandler& AsyncStaticWebHandler::setTryGzipFirst(bool value) {
_gzipFirst = false; _tryGzipFirst = value;
_gzipStats = 0xF8; return *this;
} }
AsyncStaticWebHandler& AsyncStaticWebHandler::setIsDir(bool isDir) { AsyncStaticWebHandler& AsyncStaticWebHandler::setIsDir(bool isDir) {
@ -104,14 +106,11 @@ AsyncStaticWebHandler& AsyncStaticWebHandler::setLastModified() {
return setLastModified(last_modified); return setLastModified(last_modified);
} }
#endif #endif
bool AsyncStaticWebHandler::canHandle(AsyncWebServerRequest* request) { bool AsyncStaticWebHandler::canHandle(AsyncWebServerRequest* request) const {
if (request->method() != HTTP_GET || !request->url().startsWith(_uri) || !request->isExpectedRequestedConnType(RCT_DEFAULT, RCT_HTTP)) { return request->isHTTP() && request->method() == HTTP_GET && request->url().startsWith(_uri) && _getFile(request);
return false;
}
return _getFile(request);
} }
bool AsyncStaticWebHandler::_getFile(AsyncWebServerRequest* request) { bool AsyncStaticWebHandler::_getFile(AsyncWebServerRequest* request) const {
// Remove the found uri // Remove the found uri
String path = request->url().substring(_uri.length()); String path = request->url().substring(_uri.length());
@ -121,7 +120,7 @@ bool AsyncStaticWebHandler::_getFile(AsyncWebServerRequest* request) {
path = _path + path; path = _path + path;
// Do we have a file or .gz file // Do we have a file or .gz file
if (!canSkipFileCheck && _fileExists(request, path)) if (!canSkipFileCheck && const_cast<AsyncStaticWebHandler*>(this)->_searchFile(request, path))
return true; return true;
// Can't handle if not default file // Can't handle if not default file
@ -133,7 +132,7 @@ bool AsyncStaticWebHandler::_getFile(AsyncWebServerRequest* request) {
path += String('/'); path += String('/');
path += _default_file; path += _default_file;
return _fileExists(request, path); return const_cast<AsyncStaticWebHandler*>(this)->_searchFile(request, path);
} }
#ifdef ESP32 #ifdef ESP32
@ -142,13 +141,13 @@ bool AsyncStaticWebHandler::_getFile(AsyncWebServerRequest* request) {
#define FILE_IS_REAL(f) (f == true) #define FILE_IS_REAL(f) (f == true)
#endif #endif
bool AsyncStaticWebHandler::_fileExists(AsyncWebServerRequest* request, const String& path) { bool AsyncStaticWebHandler::_searchFile(AsyncWebServerRequest* request, const String& path) {
bool fileFound = false; bool fileFound = false;
bool gzipFound = false; bool gzipFound = false;
String gzip = path + F(".gz"); String gzip = path + T__gz;
if (_gzipFirst) { if (_tryGzipFirst) {
if (_fs.exists(gzip)) { if (_fs.exists(gzip)) {
request->_tempFile = _fs.open(gzip, fs::FileOpenMode::read); request->_tempFile = _fs.open(gzip, fs::FileOpenMode::read);
gzipFound = FILE_IS_REAL(request->_tempFile); gzipFound = FILE_IS_REAL(request->_tempFile);
@ -180,15 +179,6 @@ bool AsyncStaticWebHandler::_fileExists(AsyncWebServerRequest* request, const St
char* _tempPath = (char*)malloc(pathLen + 1); char* _tempPath = (char*)malloc(pathLen + 1);
snprintf_P(_tempPath, pathLen + 1, PSTR("%s"), path.c_str()); snprintf_P(_tempPath, pathLen + 1, PSTR("%s"), path.c_str());
request->_tempObject = (void*)_tempPath; 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; return found;
@ -260,11 +250,8 @@ void AsyncCallbackWebHandler::setUri(const String& uri) {
_isRegex = uri.startsWith("^") && uri.endsWith("$"); _isRegex = uri.startsWith("^") && uri.endsWith("$");
} }
bool AsyncCallbackWebHandler::canHandle(AsyncWebServerRequest* request) { bool AsyncCallbackWebHandler::canHandle(AsyncWebServerRequest* request) const {
if (!_onRequest) if (!_onRequest || !request->isHTTP() || !(_method & request->method()))
return false;
if (!(_method & request->method()))
return false; return false;
#ifdef ASYNCWEBSERVER_REGEX #ifdef ASYNCWEBSERVER_REGEX

View File

@ -49,9 +49,9 @@ AsyncWebServerRequest::~AsyncWebServerRequest() {
_pathParams.clear(); _pathParams.clear();
if (_response != NULL) { AsyncWebServerResponse* r = _response;
delete _response; _response = NULL;
} delete r;
if (_tempObject != NULL) { if (_tempObject != NULL) {
free(_tempObject); free(_tempObject);
@ -280,38 +280,40 @@ bool AsyncWebServerRequest::_parseReqHeader() {
} }
} else if (name.equalsIgnoreCase(T_Content_Length)) { } else if (name.equalsIgnoreCase(T_Content_Length)) {
_contentLength = atoi(value.c_str()); _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; _expectingContinue = true;
} else if (name.equalsIgnoreCase(T_AUTH)) { } else if (name.equalsIgnoreCase(T_AUTH)) {
if (value.length() > 5 && value.substring(0, 5).equalsIgnoreCase(T_BASIC)) { int space = value.indexOf(' ');
_authorization = value.substring(6); if (space == -1) {
_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 {
_authorization = value; _authorization = value;
_authMethod = AsyncAuthType::AUTH_OTHER; _authMethod = AsyncAuthType::AUTH_OTHER;
} } else {
} else { String method = value.substring(0, space);
if (name.equalsIgnoreCase(T_UPGRADE) && value.equalsIgnoreCase(T_WS)) { if (method.equalsIgnoreCase(T_BASIC)) {
// WebSocket request can be uniquely identified by header: [Upgrade: websocket] _authMethod = AsyncAuthType::AUTH_BASIC;
_reqconntype = RCT_WS; } else if (method.equalsIgnoreCase(T_DIGEST)) {
} else if (name.equalsIgnoreCase(T_ACCEPT)) { _authMethod = AsyncAuthType::AUTH_DIGEST;
String lowcase(value); } else if (method.equalsIgnoreCase(T_BEARER)) {
lowcase.toLowerCase(); _authMethod = AsyncAuthType::AUTH_BEARER;
#ifndef ESP8266 } else {
const char* substr = std::strstr(lowcase.c_str(), T_text_event_stream); _authMethod = AsyncAuthType::AUTH_OTHER;
#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;
} }
_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); _headers.emplace_back(name, value);
@ -779,7 +781,7 @@ void AsyncWebServerRequest::redirect(const char* url, int code) {
send(response); 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 (_authorization.length()) {
if (_authMethod == AsyncAuthType::AUTH_DIGEST) if (_authMethod == AsyncAuthType::AUTH_DIGEST)
return checkDigestAuthentication(_authorization.c_str(), methodToString(), username, password, realm, passwordIsHash, NULL, NULL, NULL); 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; return false;
} }
bool AsyncWebServerRequest::authenticate(const char* hash) { bool AsyncWebServerRequest::authenticate(const char* hash) const {
if (!_authorization.length() || hash == NULL) if (!_authorization.length() || hash == NULL)
return false; return false;
@ -831,7 +833,7 @@ void AsyncWebServerRequest::requestAuthentication(AsyncAuthType method, const ch
break; break;
} }
case AsyncAuthType::AUTH_DIGEST: { 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; String header;
header.reserve(len + strlen(realm)); header.reserve(len + strlen(realm));
header.concat(T_DIGEST_); header.concat(T_DIGEST_);
@ -938,7 +940,6 @@ String AsyncWebServerRequest::urlDecode(const String& text) const {
return decoded; return decoded;
} }
#ifndef ESP8266
const char* AsyncWebServerRequest::methodToString() const { const char* AsyncWebServerRequest::methodToString() const {
if (_method == HTTP_ANY) if (_method == HTTP_ANY)
return T_ANY; return T_ANY;
@ -958,29 +959,7 @@ const char* AsyncWebServerRequest::methodToString() const {
return T_OPTIONS; return T_OPTIONS;
return T_UNKNOWN; 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 { const char* AsyncWebServerRequest::requestedConnTypeToString() const {
switch (_reqconntype) { switch (_reqconntype) {
case RCT_NOT_USED: case RCT_NOT_USED:
@ -997,32 +976,9 @@ const char* AsyncWebServerRequest::requestedConnTypeToString() const {
return T_ERROR; 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 AsyncWebServerRequest::isExpectedRequestedConnType(RequestedConnectionType erct1, RequestedConnectionType erct2, RequestedConnectionType erct3) const {
bool res = false; return ((erct1 != RCT_NOT_USED) && (erct1 == _reqconntype)) ||
if ((erct1 != RCT_NOT_USED) && (erct1 == _reqconntype)) ((erct2 != RCT_NOT_USED) && (erct2 == _reqconntype)) ||
res = true; ((erct3 != RCT_NOT_USED) && (erct3 == _reqconntype));
if ((erct2 != RCT_NOT_USED) && (erct2 == _reqconntype))
res = true;
if ((erct3 != RCT_NOT_USED) && (erct3 == _reqconntype))
res = true;
return res;
} }

View File

@ -26,9 +26,10 @@
#undef min #undef min
#undef max #undef max
#endif #endif
#include "literals.h"
#include <StreamString.h>
#include <memory> #include <memory>
#include <vector> #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. // 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: public:
explicit AsyncBasicResponse(int code, const char* contentType = asyncsrv::empty, const char* content = asyncsrv::empty); 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()) {} AsyncBasicResponse(int code, const String& contentType, const String& content = emptyString) : AsyncBasicResponse(code, contentType.c_str(), content.c_str()) {}
void _respond(AsyncWebServerRequest* request); void _respond(AsyncWebServerRequest* request) override final;
size_t _ack(AsyncWebServerRequest* request, size_t len, uint32_t time); size_t _ack(AsyncWebServerRequest* request, size_t len, uint32_t time) override final;
bool _sourceValid() const { return true; } bool _sourceValid() const override final { return true; }
}; };
class AsyncAbstractResponse : public AsyncWebServerResponse { class AsyncAbstractResponse : public AsyncWebServerResponse {
@ -60,9 +61,10 @@ class AsyncAbstractResponse : public AsyncWebServerResponse {
public: public:
AsyncAbstractResponse(AwsTemplateProcessor callback = nullptr); AsyncAbstractResponse(AwsTemplateProcessor callback = nullptr);
void _respond(AsyncWebServerRequest* request); virtual ~AsyncAbstractResponse() {}
size_t _ack(AsyncWebServerRequest* request, size_t len, uint32_t time); void _respond(AsyncWebServerRequest* request) override final;
bool _sourceValid() const { return false; } 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; } 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(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 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(File content, const String& path, const String& contentType, bool download = false, AwsTemplateProcessor callack = nullptr) : AsyncFileResponse(content, path, contentType.c_str(), download, callack) {}
~AsyncFileResponse(); ~AsyncFileResponse() { _content.close(); }
bool _sourceValid() const { return !!(_content); } bool _sourceValid() const override final { return !!(_content); }
virtual size_t _fillBuffer(uint8_t* buf, size_t maxLen) override; size_t _fillBuffer(uint8_t* buf, size_t maxLen) override final;
}; };
class AsyncStreamResponse : public AsyncAbstractResponse { class AsyncStreamResponse : public AsyncAbstractResponse {
@ -97,8 +99,8 @@ class AsyncStreamResponse : public AsyncAbstractResponse {
public: public:
AsyncStreamResponse(Stream& stream, const char* contentType, size_t len, AwsTemplateProcessor callback = nullptr); 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) {} AsyncStreamResponse(Stream& stream, const String& contentType, size_t len, AwsTemplateProcessor callback = nullptr) : AsyncStreamResponse(stream, contentType.c_str(), len, callback) {}
bool _sourceValid() const { return !!(_content); } bool _sourceValid() const override final { return !!(_content); }
virtual size_t _fillBuffer(uint8_t* buf, size_t maxLen) override; size_t _fillBuffer(uint8_t* buf, size_t maxLen) override final;
}; };
class AsyncCallbackResponse : public AsyncAbstractResponse { class AsyncCallbackResponse : public AsyncAbstractResponse {
@ -109,8 +111,8 @@ class AsyncCallbackResponse : public AsyncAbstractResponse {
public: public:
AsyncCallbackResponse(const char* contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback = nullptr); 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) {} AsyncCallbackResponse(const String& contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback = nullptr) : AsyncCallbackResponse(contentType.c_str(), len, callback, templateCallback) {}
bool _sourceValid() const { return !!(_content); } bool _sourceValid() const override final { return !!(_content); }
virtual size_t _fillBuffer(uint8_t* buf, size_t maxLen) override; size_t _fillBuffer(uint8_t* buf, size_t maxLen) override final;
}; };
class AsyncChunkedResponse : public AsyncAbstractResponse { class AsyncChunkedResponse : public AsyncAbstractResponse {
@ -121,8 +123,8 @@ class AsyncChunkedResponse : public AsyncAbstractResponse {
public: public:
AsyncChunkedResponse(const char* contentType, AwsResponseFiller callback, AwsTemplateProcessor templateCallback = nullptr); AsyncChunkedResponse(const char* contentType, AwsResponseFiller callback, AwsTemplateProcessor templateCallback = nullptr);
AsyncChunkedResponse(const String& contentType, AwsResponseFiller callback, AwsTemplateProcessor templateCallback = nullptr) : AsyncChunkedResponse(contentType.c_str(), callback, templateCallback) {} AsyncChunkedResponse(const String& contentType, AwsResponseFiller callback, AwsTemplateProcessor templateCallback = nullptr) : AsyncChunkedResponse(contentType.c_str(), callback, templateCallback) {}
bool _sourceValid() const { return !!(_content); } bool _sourceValid() const override final { return !!(_content); }
virtual size_t _fillBuffer(uint8_t* buf, size_t maxLen) override; size_t _fillBuffer(uint8_t* buf, size_t maxLen) override final;
}; };
class AsyncProgmemResponse : public AsyncAbstractResponse { class AsyncProgmemResponse : public AsyncAbstractResponse {
@ -133,22 +135,19 @@ class AsyncProgmemResponse : public AsyncAbstractResponse {
public: public:
AsyncProgmemResponse(int code, const char* contentType, const uint8_t* content, size_t len, AwsTemplateProcessor callback = nullptr); 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) {} 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; } bool _sourceValid() const override final { return true; }
virtual size_t _fillBuffer(uint8_t* buf, size_t maxLen) override; size_t _fillBuffer(uint8_t* buf, size_t maxLen) override final;
}; };
class cbuf;
class AsyncResponseStream : public AsyncAbstractResponse, public Print { class AsyncResponseStream : public AsyncAbstractResponse, public Print {
private: private:
std::unique_ptr<cbuf> _content; StreamString _content;
public: public:
AsyncResponseStream(const char* contentType, size_t bufferSize); AsyncResponseStream(const char* contentType, size_t bufferSize);
AsyncResponseStream(const String& contentType, size_t bufferSize) : AsyncResponseStream(contentType.c_str(), bufferSize) {} AsyncResponseStream(const String& contentType, size_t bufferSize) : AsyncResponseStream(contentType.c_str(), bufferSize) {}
~AsyncResponseStream(); bool _sourceValid() const override final { return (_state < RESPONSE_END); }
bool _sourceValid() const { return (_state < RESPONSE_END); } size_t _fillBuffer(uint8_t* buf, size_t maxLen) override final;
virtual size_t _fillBuffer(uint8_t* buf, size_t maxLen) override;
size_t write(const uint8_t* data, size_t len); size_t write(const uint8_t* data, size_t len);
size_t write(uint8_t data); size_t write(uint8_t data);
using Print::write; using Print::write;

View File

@ -20,7 +20,6 @@
*/ */
#include "ESPAsyncWebServer.h" #include "ESPAsyncWebServer.h"
#include "WebResponseImpl.h" #include "WebResponseImpl.h"
#include "cbuf.h"
using namespace asyncsrv; using namespace asyncsrv;
@ -38,7 +37,6 @@ void* memchr(void* ptr, int ch, size_t count) {
* *
*/ */
#ifndef ESP8266
const char* AsyncWebServerResponse::responseCodeToString(int code) { const char* AsyncWebServerResponse::responseCodeToString(int code) {
switch (code) { switch (code) {
case 100: case 100:
@ -127,96 +125,6 @@ const char* AsyncWebServerResponse::responseCodeToString(int code) {
return T_HTTP_CODE_ANY; 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() AsyncWebServerResponse::AsyncWebServerResponse()
: _code(0), _contentType(), _contentLength(0), _sendContentLength(true), _chunked(false), _headLength(0), _sentLength(0), _ackedLength(0), _writtenLength(0), _state(RESPONSE_SETUP) { : _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) { void AsyncWebServerResponse::setCode(int code) {
if (_state == RESPONSE_SETUP) if (_state == RESPONSE_SETUP)
_code = code; _code = code;
@ -648,11 +554,6 @@ size_t AsyncAbstractResponse::_fillBufferAndProcessTemplates(uint8_t* data, size
* File Response * File Response
* */ * */
AsyncFileResponse::~AsyncFileResponse() {
if (_content)
_content.close();
}
void AsyncFileResponse::_setContentTypeFromPath(const String& path) { void AsyncFileResponse::_setContentTypeFromPath(const String& path) {
#if HAVE_EXTERN_GET_Content_Type_FUNCTION #if HAVE_EXTERN_GET_Content_Type_FUNCTION
#ifndef ESP8266 #ifndef ESP8266
@ -868,24 +769,17 @@ AsyncResponseStream::AsyncResponseStream(const char* contentType, size_t bufferS
_code = 200; _code = 200;
_contentLength = 0; _contentLength = 0;
_contentType = contentType; _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) { 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) { size_t AsyncResponseStream::write(const uint8_t* data, size_t len) {
if (_started()) if (_started())
return 0; return 0;
size_t written = _content.write(data, len);
if (len > _content->room()) {
size_t needed = len - _content->room();
_content->resizeAdd(needed);
}
size_t written = _content->write((const char*)data, len);
_contentLength += written; _contentLength += written;
return written; return written;
} }

View File

@ -2,346 +2,181 @@
namespace asyncsrv { namespace asyncsrv {
static constexpr const char* empty = ""; static constexpr const char* empty = "";
#ifndef ESP8622 static constexpr const char* T__opaque = "\", opaque=\"";
static constexpr const char* T_100_CONTINUE = "100-continue"; static constexpr const char* T_100_CONTINUE = "100-continue";
static constexpr const char* T_ACCEPT = "Accept"; static constexpr const char* T_13 = "13";
static constexpr const char* T_Accept_Ranges = "Accept-Ranges"; static constexpr const char* T_ACCEPT = "accept";
static constexpr const char* T_app_xform_urlencoded = "application/x-www-form-urlencoded"; static constexpr const char* T_Accept_Ranges = "accept-ranges";
static constexpr const char* T_AUTH = "Authorization"; static constexpr const char* T_app_xform_urlencoded = "application/x-www-form-urlencoded";
static constexpr const char* T_BASIC = "Basic"; static constexpr const char* T_AUTH = "authorization";
static constexpr const char* T_BASIC_REALM = "Basic realm=\""; static constexpr const char* T_auth_nonce = "\", qop=\"auth\", nonce=\"";
static constexpr const char* T_LOGIN_REQ = "Login Required"; static constexpr const char* T_BASIC = "basic";
static constexpr const char* T_BODY = "body"; static constexpr const char* T_BASIC_REALM = "basic realm=\"";
static constexpr const char* T_Cache_Control = "Cache-Control"; static constexpr const char* T_BEARER = "bearer";
static constexpr const char* T_chunked = "chunked"; static constexpr const char* T_BODY = "body";
static constexpr const char* T_close = "close"; static constexpr const char* T_Cache_Control = "cache-control";
static constexpr const char* T_Connection = "Connection"; static constexpr const char* T_chunked = "chunked";
static constexpr const char* T_Content_Disposition = "Content-Disposition"; static constexpr const char* T_close = "close";
static constexpr const char* T_Content_Encoding = "Content-Encoding"; static constexpr const char* T_cnonce = "cnonce";
static constexpr const char* T_Content_Length = "Content-Length"; static constexpr const char* T_Connection = "connection";
static constexpr const char* T_Content_Type = "Content-Type"; static constexpr const char* T_Content_Disposition = "content-disposition";
static constexpr const char* T_Cookie = "Cookie"; static constexpr const char* T_Content_Encoding = "content-encoding";
static constexpr const char* T_DIGEST = "Digest"; static constexpr const char* T_Content_Length = "content-length";
static constexpr const char* T_DIGEST_ = "Digest "; static constexpr const char* T_Content_Type = "content-type";
static constexpr const char* T_BEARER = "Bearer"; static constexpr const char* T_Cookie = "cookie";
static constexpr const char* T_ETag = "ETag"; static constexpr const char* T_CORS_ACAC = "access-control-allow-credentials";
static constexpr const char* T_EXPECT = "Expect"; static constexpr const char* T_CORS_ACAH = "access-control-allow-headers";
static constexpr const char* T_HTTP_1_0 = "HTTP/1.0"; static constexpr const char* T_CORS_ACAM = "access-control-allow-methods";
static constexpr const char* T_HTTP_100_CONT = "HTTP/1.1 100 Continue\r\n\r\n"; static constexpr const char* T_CORS_ACAO = "access-control-allow-origin";
static constexpr const char* T_IMS = "If-Modified-Since"; static constexpr const char* T_CORS_ACMA = "access-control-max-age";
static constexpr const char* T_INM = "If-None-Match"; static constexpr const char* T_CORS_O = "origin";
static constexpr const char* T_keep_alive = "keep-alive"; static constexpr const char* T_data_ = "data: ";
static constexpr const char* T_Last_Event_ID = "Last-Event-ID"; static constexpr const char* T_DIGEST = "digest";
static constexpr const char* T_Last_Modified = "Last-Modified"; static constexpr const char* T_DIGEST_ = "digest ";
static constexpr const char* T_LOCATION = "Location"; static constexpr const char* T_ETag = "etag";
static constexpr const char* T_MULTIPART_ = "multipart/"; static constexpr const char* T_event_ = "event: ";
static constexpr const char* T_no_cache = "no-cache"; static constexpr const char* T_EXPECT = "expect";
static constexpr const char* T_none = "none"; static constexpr const char* T_FALSE = "false";
static constexpr const char* T_UPGRADE = "Upgrade"; static constexpr const char* T_filename = "filename";
static constexpr const char* T_WS = "websocket"; static constexpr const char* T_gzip = "gzip";
static constexpr const char* T_WWW_AUTH = "WWW-Authenticate"; static constexpr const char* T_Host = "host";
static constexpr const char* T_Transfer_Encoding = "Transfer-Encoding"; 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 // 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";
// Req content types static constexpr const char* T_ANY = "ANY";
static constexpr const char* T_RCT_NOT_USED = "RCT_NOT_USED"; static constexpr const char* T_GET = "GET";
static constexpr const char* T_RCT_DEFAULT = "RCT_DEFAULT"; static constexpr const char* T_POST = "POST";
static constexpr const char* T_RCT_HTTP = "RCT_HTTP"; static constexpr const char* T_PUT = "PUT";
static constexpr const char* T_RCT_WS = "RCT_WS"; static constexpr const char* T_DELETE = "DELETE";
static constexpr const char* T_RCT_EVENT = "RCT_EVENT"; static constexpr const char* T_PATCH = "PATCH";
static constexpr const char* T_ERROR = "ERROR"; static constexpr const char* T_HEAD = "HEAD";
static constexpr const char* T_OPTIONS = "OPTIONS";
static constexpr const char* T_UNKNOWN = "UNKNOWN";
// extentions & MIME-Types // Req content types
static constexpr const char* T__css = ".css"; static constexpr const char* T_RCT_NOT_USED = "RCT_NOT_USED";
static constexpr const char* T__eot = ".eot"; static constexpr const char* T_RCT_DEFAULT = "RCT_DEFAULT";
static constexpr const char* T__gif = ".gif"; static constexpr const char* T_RCT_HTTP = "RCT_HTTP";
static constexpr const char* T__gz = ".gz"; static constexpr const char* T_RCT_WS = "RCT_WS";
static constexpr const char* T__htm = ".htm"; static constexpr const char* T_RCT_EVENT = "RCT_EVENT";
static constexpr const char* T__html = ".html"; static constexpr const char* T_ERROR = "ERROR";
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";
// Responce codes // extentions & MIME-Types
static constexpr const char* T_HTTP_CODE_100 = "Continue"; static constexpr const char* T__css = ".css";
static constexpr const char* T_HTTP_CODE_101 = "Switching Protocols"; static constexpr const char* T__eot = ".eot";
static constexpr const char* T_HTTP_CODE_200 = "OK"; static constexpr const char* T__gif = ".gif";
static constexpr const char* T_HTTP_CODE_201 = "Created"; static constexpr const char* T__gz = ".gz";
static constexpr const char* T_HTTP_CODE_202 = "Accepted"; static constexpr const char* T__htm = ".htm";
static constexpr const char* T_HTTP_CODE_203 = "Non-Authoritative Information"; static constexpr const char* T__html = ".html";
static constexpr const char* T_HTTP_CODE_204 = "No Content"; static constexpr const char* T__ico = ".ico";
static constexpr const char* T_HTTP_CODE_205 = "Reset Content"; static constexpr const char* T__jpg = ".jpg";
static constexpr const char* T_HTTP_CODE_206 = "Partial Content"; static constexpr const char* T__js = ".js";
static constexpr const char* T_HTTP_CODE_300 = "Multiple Choices"; static constexpr const char* T__json = ".json";
static constexpr const char* T_HTTP_CODE_301 = "Moved Permanently"; static constexpr const char* T__pdf = ".pdf";
static constexpr const char* T_HTTP_CODE_302 = "Found"; static constexpr const char* T__png = ".png";
static constexpr const char* T_HTTP_CODE_303 = "See Other"; static constexpr const char* T__svg = ".svg";
static constexpr const char* T_HTTP_CODE_304 = "Not Modified"; static constexpr const char* T__ttf = ".ttf";
static constexpr const char* T_HTTP_CODE_305 = "Use Proxy"; static constexpr const char* T__woff = ".woff";
static constexpr const char* T_HTTP_CODE_307 = "Temporary Redirect"; static constexpr const char* T__woff2 = ".woff2";
static constexpr const char* T_HTTP_CODE_400 = "Bad Request"; static constexpr const char* T__xml = ".xml";
static constexpr const char* T_HTTP_CODE_401 = "Unauthorized"; static constexpr const char* T__zip = ".zip";
static constexpr const char* T_HTTP_CODE_402 = "Payment Required"; static constexpr const char* T_application_javascript = "application/javascript";
static constexpr const char* T_HTTP_CODE_403 = "Forbidden"; static constexpr const char* T_application_json = "application/json";
static constexpr const char* T_HTTP_CODE_404 = "Not Found"; static constexpr const char* T_application_msgpack = "application/msgpack";
static constexpr const char* T_HTTP_CODE_405 = "Method Not Allowed"; static constexpr const char* T_application_pdf = "application/pdf";
static constexpr const char* T_HTTP_CODE_406 = "Not Acceptable"; static constexpr const char* T_application_x_gzip = "application/x-gzip";
static constexpr const char* T_HTTP_CODE_407 = "Proxy Authentication Required"; static constexpr const char* T_application_zip = "application/zip";
static constexpr const char* T_HTTP_CODE_408 = "Request Time-out"; static constexpr const char* T_font_eot = "font/eot";
static constexpr const char* T_HTTP_CODE_409 = "Conflict"; static constexpr const char* T_font_ttf = "font/ttf";
static constexpr const char* T_HTTP_CODE_410 = "Gone"; static constexpr const char* T_font_woff = "font/woff";
static constexpr const char* T_HTTP_CODE_411 = "Length Required"; static constexpr const char* T_font_woff2 = "font/woff2";
static constexpr const char* T_HTTP_CODE_412 = "Precondition Failed"; static constexpr const char* T_image_gif = "image/gif";
static constexpr const char* T_HTTP_CODE_413 = "Request Entity Too Large"; static constexpr const char* T_image_jpeg = "image/jpeg";
static constexpr const char* T_HTTP_CODE_414 = "Request-URI Too Large"; static constexpr const char* T_image_png = "image/png";
static constexpr const char* T_HTTP_CODE_415 = "Unsupported Media Type"; static constexpr const char* T_image_svg_xml = "image/svg+xml";
static constexpr const char* T_HTTP_CODE_416 = "Requested range not satisfiable"; static constexpr const char* T_image_x_icon = "image/x-icon";
static constexpr const char* T_HTTP_CODE_417 = "Expectation Failed"; static constexpr const char* T_text_css = "text/css";
static constexpr const char* T_HTTP_CODE_429 = "Too Many Requests"; static constexpr const char* T_text_event_stream = "text/event-stream";
static constexpr const char* T_HTTP_CODE_500 = "Internal Server Error"; static constexpr const char* T_text_html = "text/html";
static constexpr const char* T_HTTP_CODE_501 = "Not Implemented"; static constexpr const char* T_text_plain = "text/plain";
static constexpr const char* T_HTTP_CODE_502 = "Bad Gateway"; static constexpr const char* T_text_xml = "text/xml";
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";
// other // Responce codes
static constexpr const char* T__opaque = "\", opaque=\""; static constexpr const char* T_HTTP_CODE_100 = "Continue";
static constexpr const char* T_13 = "13"; static constexpr const char* T_HTTP_CODE_101 = "Switching Protocols";
static constexpr const char* T_auth_nonce = "\", qop=\"auth\", nonce=\""; static constexpr const char* T_HTTP_CODE_200 = "OK";
static constexpr const char* T_cnonce = "cnonce"; static constexpr const char* T_HTTP_CODE_201 = "Created";
static constexpr const char* T_data_ = "data: "; static constexpr const char* T_HTTP_CODE_202 = "Accepted";
static constexpr const char* T_event_ = "event: "; static constexpr const char* T_HTTP_CODE_203 = "Non-Authoritative Information";
static constexpr const char* T_filename = "filename"; static constexpr const char* T_HTTP_CODE_204 = "No Content";
static constexpr const char* T_gzip = "gzip"; static constexpr const char* T_HTTP_CODE_205 = "Reset Content";
static constexpr const char* T_Host = "Host"; static constexpr const char* T_HTTP_CODE_206 = "Partial Content";
static constexpr const char* T_id__ = "id: "; static constexpr const char* T_HTTP_CODE_300 = "Multiple Choices";
static constexpr const char* T_name = "name"; static constexpr const char* T_HTTP_CODE_301 = "Moved Permanently";
static constexpr const char* T_nc = "nc"; static constexpr const char* T_HTTP_CODE_302 = "Found";
static constexpr const char* T_nonce = "nonce"; static constexpr const char* T_HTTP_CODE_303 = "See Other";
static constexpr const char* T_opaque = "opaque"; static constexpr const char* T_HTTP_CODE_304 = "Not Modified";
static constexpr const char* T_qop = "qop"; static constexpr const char* T_HTTP_CODE_305 = "Use Proxy";
static constexpr const char* T_realm = "realm"; static constexpr const char* T_HTTP_CODE_307 = "Temporary Redirect";
static constexpr const char* T_realm__ = "realm=\""; static constexpr const char* T_HTTP_CODE_400 = "Bad Request";
static constexpr const char* T_response = "response"; static constexpr const char* T_HTTP_CODE_401 = "Unauthorized";
static constexpr const char* T_retry_ = "retry: "; static constexpr const char* T_HTTP_CODE_402 = "Payment Required";
static constexpr const char* T_rn = "\r\n"; static constexpr const char* T_HTTP_CODE_403 = "Forbidden";
static constexpr const char* T_rnrn = "\r\n\r\n"; static constexpr const char* T_HTTP_CODE_404 = "Not Found";
static constexpr const char* T_uri = "uri"; static constexpr const char* T_HTTP_CODE_405 = "Method Not Allowed";
static constexpr const char* T_username = "username"; 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";
} // namespace asyncsrv {}
#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 {}