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.
- [Coordinate and dependencies](#coordinate-and-dependencies)
- [Changes in this fork](#changes-in-this-fork)
- [Dependencies](#dependencies)
- [Performance](#performance)
- [Important recommendations](#important-recommendations)
- [`AsyncWebSocketMessageBuffer` and `makeBuffer()`](#asyncwebsocketmessagebuffer-and-makebuffer)
- [How to replace a response](#how-to-replace-a-response)
@ -25,20 +26,6 @@ This fork is based on [yubox-node-org/ESPAsyncWebServer](https://github.com/yubo
- [Migration to Middleware to improve performance and memory usage](#migration-to-middleware-to-improve-performance-and-memory-usage)
- [Original Documentation](#original-documentation)
## Coordinate and dependencies
**WARNING** The library name was changed from `ESP Async WebServer` to `ESPAsyncWebServer` as per the Arduino Lint recommendations.
```
mathieucarbou/ESPAsyncWebServer @ 3.3.7
```
Dependency:
- **ESP32**: `mathieucarbou/AsyncTCP @ 3.2.5` (Arduino IDE: [https://github.com/mathieucarbou/AsyncTCP#v3.2.5](https://github.com/mathieucarbou/AsyncTCP/releases))
- **ESP8266**: `esphome/ESPAsyncTCP-esphome @ 2.0.0` (Arduino IDE: [https://github.com/mathieucarbou/esphome-ESPAsyncTCP#v2.0.0](https://github.com/mathieucarbou/esphome-ESPAsyncTCP/releases/tag/v2.0.0))
- **RP2040**: `khoih-prog/AsyncTCP_RP2040W @ 1.2.0` (Arduino IDE: [https://github.com/khoih-prog/AsyncTCP_RP2040W#v1.2.0](https://github.com/khoih-prog/AsyncTCP_RP2040W/releases/tag/v1.2.0))
## Changes in this fork
- (bug) A lot of bug fixes
@ -55,6 +42,7 @@ Dependency:
- (feat) **Resumable download** support using HEAD and bytes range
- (feat) `StreamConcat` example to show how to stream multiple files in one response
- (feat) Removed ESPIDF Editor (this is not the role of a web server library to do that - get the source files from the original repos if required)
- (perf) [AsyncTCPSock](https://github.com/mathieucarbou/AsyncTCPSock) support: AsyncTCP can be ignored and AsyncTCPSock used instead
- (perf) `char*` overloads to avoid using `String`
- (perf) `DEFAULT_MAX_WS_CLIENTS` to change the number of allows WebSocket clients and use `cleanupClients()` to help cleanup resources about dead clients
- (perf) `setCloseClientOnQueueFull(bool)` which can be set on a client to either close the connection or discard messages but not close the connection when the queue is full
@ -64,6 +52,112 @@ Dependency:
- (perf) Lot of code cleanup and optimizations
- (perf) Performance improvements in terms of memory, speed and size
---
## WARNING: Important notes about future version 4.x
This ESPAsyncWebServer fork is now at version 3.x.
Next version 4.x will:
1. Drop support for ESP8266, which goes EOL in a few years. All ESP8266 boards can be replaced by equivalent ESP32 boards.
2. Drop support for Arduino 2.x and ESP-IDF 4.x. The library will be compatible with Arduino 3.x and ESP-IDF 5.x.
3. Drop support for ArduinoJson 5.x and 6.x. The library will be compatible with ArduinoJson 7.x.
So if you need one of these feature, you will have to stick with 3.x or another fork.
## Dependencies
**WARNING** The library name was changed from `ESP Async WebServer` to `ESPAsyncWebServer` as per the Arduino Lint recommendations, but its name had to stay `ESP Async WebServer` in Arduino Registry.
**PlatformIO / pioarduino:**
```ini
lib_compat_mode = strict
lib_ldf_mode = chain
lib_deps = mathieucarbou/ESPAsyncWebServer @ 3.3.23
```
**Dependencies:**
- **ESP32 with AsyncTCP**: `mathieucarbou/AsyncTCP @ 3.2.14`
Arduino IDE: [https://github.com/mathieucarbou/AsyncTCP#v3.2.14](https://github.com/mathieucarbou/AsyncTCP/releases)
- **ESP32 with AsyncTCPSock**: `https://github.com/mathieucarbou/AsyncTCPSock/archive/refs/tags/v1.0.3-dev.zip`
- **ESP8266**: `esphome/ESPAsyncTCP-esphome @ 2.0.0`
Arduino IDE: [https://github.com/mathieucarbou/esphome-ESPAsyncTCP#v2.0.0](https://github.com/mathieucarbou/esphome-ESPAsyncTCP/releases/tag/v2.0.0)
- **RP2040**: `khoih-prog/AsyncTCP_RP2040W @ 1.2.0`
Arduino IDE: [https://github.com/khoih-prog/AsyncTCP_RP2040W#v1.2.0](https://github.com/khoih-prog/AsyncTCP_RP2040W/releases/tag/v1.2.0)
**AsyncTCPSock**
AsyncTCPSock can be used instead of AsyncTCP by excluding AsyncTCP from the library dependencies and adding AsyncTCPSock instead:
```ini
lib_compat_mode = strict
lib_ldf_mode = chain
lib_deps =
; mathieucarbou/AsyncTCP @ 3.2.14
https://github.com/mathieucarbou/AsyncTCPSock/archive/refs/tags/v1.0.3-dev.zip
mathieucarbou/ESPAsyncWebServer @ 3.3.23
lib_ignore =
AsyncTCP
mathieucarbou/AsyncTCP
```
## Performance
Performance of `mathieucarbou/ESPAsyncWebServer @ 3.3.23`:
```bash
> brew install autocannon
> autocannon -c 10 -w 10 -d 20 http://192.168.4.1
```
With `mathieucarbou/AsyncTCP @ 3.2.14`
[![](https://mathieu.carbou.me/ESPAsyncWebServer/perf-c10.png)](https://mathieu.carbou.me/ESPAsyncWebServer/perf-c10.png)
With `https://github.com/mathieucarbou/AsyncTCPSock/archive/refs/tags/v1.0.3-dev.zip`:
[![](https://mathieu.carbou.me/ESPAsyncWebServer/perf-c10-asynctcpsock.png)](https://mathieu.carbou.me/ESPAsyncWebServer/perf-c10-asynctcpsock.png)
**SSE performance**
In the example, there is an endpoint `/events` with some comments showing how these metrics are calculated.
Test is running for 20 seconds with 10 connections.
```
// With AsyncTCP, with 10 workers: no message discarded from the queue
//
// Total: 1875 events, 468.75000000000000000000 events / second
// Total: 1870 events, 467.50000000000000000000 events / second
// Total: 1871 events, 467.75000000000000000000 events / second
// Total: 1875 events, 468.75000000000000000000 events / second
// Total: 1871 events, 467.75000000000000000000 events / second
// Total: 1805 events, 451.25000000000000000000 events / second
// Total: 1803 events, 450.75000000000000000000 events / second
// Total: 1873 events, 468.25000000000000000000 events / second
// Total: 1872 events, 468.00000000000000000000 events / second
// Total: 1805 events, 451.25000000000000000000 events / second
//
// With AsyncTCPSock, with 10 workers: no message discarded from the queue
//
// Total: 1242 events, 310.50000000000000000000 events / second
// Total: 1242 events, 310.50000000000000000000 events / second
// Total: 1242 events, 310.50000000000000000000 events / second
// Total: 1242 events, 310.50000000000000000000 events / second
// Total: 1181 events, 295.25000000000000000000 events / second
// Total: 1182 events, 295.50000000000000000000 events / second
// Total: 1240 events, 310.00000000000000000000 events / second
// Total: 1181 events, 295.25000000000000000000 events / second
// Total: 1181 events, 295.25000000000000000000 events / second
// Total: 1183 events, 295.75000000000000000000 events / second
```
## Important recommendations
Most of the crashes are caused by improper configuration of the library for the project.
@ -72,7 +166,7 @@ Here are some recommendations to avoid them.
1. Set the running core to be on the same core of your application (usually core 1) `-D CONFIG_ASYNC_TCP_RUNNING_CORE=1`
2. Set the stack size appropriately with `-D CONFIG_ASYNC_TCP_STACK_SIZE=16384`.
The default value of `16384` might be too much for your project.
You can look at the [MycilaTaskMonitor](https://oss.carbou.me/MycilaTaskMonitor) project to monitor the stack usage.
You can look at the [MycilaTaskMonitor](https://mathieu.carbou.me/MycilaTaskMonitor) project to monitor the stack usage.
3. You can change **if you know what you are doing** the task priority with `-D CONFIG_ASYNC_TCP_PRIORITY=10`.
Default is `10`.
4. You can increase the queue size with `-D CONFIG_ASYNC_TCP_QUEUE_SIZE=128`.
@ -528,7 +622,7 @@ Endpoints which consume JSON can use a special handler to get ready to use JSON
#include "ArduinoJson.h"
AsyncCallbackJsonWebHandler* handler = new AsyncCallbackJsonWebHandler("/rest/endpoint", [](AsyncWebServerRequest *request, JsonVariant &json) {
JsonObject& jsonObj = json.as<JsonObject>();
JsonObject jsonObj = json.as<JsonObject>();
// ...
});
server.addHandler(handler);
@ -1186,7 +1280,7 @@ For actual serving the file.
### Param Rewrite With Matching
It is possible to rewrite the request url with parameter matchg. Here is an example with one parameter:
It is possible to rewrite the request url with parameter match. Here is an example with one parameter:
Rewrite for example "/radio/{frequence}" -> "/radio?f={frequence}"
```cpp

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.
- [Coordinate and dependencies](#coordinate-and-dependencies)
- [Changes in this fork](#changes-in-this-fork)
- [Dependencies](#dependencies)
- [Performance](#performance)
- [Important recommendations](#important-recommendations)
- [`AsyncWebSocketMessageBuffer` and `makeBuffer()`](#asyncwebsocketmessagebuffer-and-makebuffer)
- [How to replace a response](#how-to-replace-a-response)
@ -25,20 +26,6 @@ This fork is based on [yubox-node-org/ESPAsyncWebServer](https://github.com/yubo
- [Migration to Middleware to improve performance and memory usage](#migration-to-middleware-to-improve-performance-and-memory-usage)
- [Original Documentation](#original-documentation)
## Coordinate and dependencies
**WARNING** The library name was changed from `ESP Async WebServer` to `ESPAsyncWebServer` as per the Arduino Lint recommendations.
```
mathieucarbou/ESPAsyncWebServer @ 3.3.7
```
Dependency:
- **ESP32**: `mathieucarbou/AsyncTCP @ 3.2.5` (Arduino IDE: [https://github.com/mathieucarbou/AsyncTCP#v3.2.5](https://github.com/mathieucarbou/AsyncTCP/releases))
- **ESP8266**: `esphome/ESPAsyncTCP-esphome @ 2.0.0` (Arduino IDE: [https://github.com/mathieucarbou/esphome-ESPAsyncTCP#v2.0.0](https://github.com/mathieucarbou/esphome-ESPAsyncTCP/releases/tag/v2.0.0))
- **RP2040**: `khoih-prog/AsyncTCP_RP2040W @ 1.2.0` (Arduino IDE: [https://github.com/khoih-prog/AsyncTCP_RP2040W#v1.2.0](https://github.com/khoih-prog/AsyncTCP_RP2040W/releases/tag/v1.2.0))
## Changes in this fork
- (bug) A lot of bug fixes
@ -55,6 +42,7 @@ Dependency:
- (feat) **Resumable download** support using HEAD and bytes range
- (feat) `StreamConcat` example to show how to stream multiple files in one response
- (feat) Removed ESPIDF Editor (this is not the role of a web server library to do that - get the source files from the original repos if required)
- (perf) [AsyncTCPSock](https://github.com/mathieucarbou/AsyncTCPSock) support: AsyncTCP can be ignored and AsyncTCPSock used instead
- (perf) `char*` overloads to avoid using `String`
- (perf) `DEFAULT_MAX_WS_CLIENTS` to change the number of allows WebSocket clients and use `cleanupClients()` to help cleanup resources about dead clients
- (perf) `setCloseClientOnQueueFull(bool)` which can be set on a client to either close the connection or discard messages but not close the connection when the queue is full
@ -64,6 +52,112 @@ Dependency:
- (perf) Lot of code cleanup and optimizations
- (perf) Performance improvements in terms of memory, speed and size
---
## WARNING: Important notes about future version 4.x
This ESPAsyncWebServer fork is now at version 3.x.
Next version 4.x will:
1. Drop support for ESP8266, which goes EOL in a few years. All ESP8266 boards can be replaced by equivalent ESP32 boards.
2. Drop support for Arduino 2.x and ESP-IDF 4.x. The library will be compatible with Arduino 3.x and ESP-IDF 5.x.
3. Drop support for ArduinoJson 5.x and 6.x. The library will be compatible with ArduinoJson 7.x.
So if you need one of these feature, you will have to stick with 3.x or another fork.
## Dependencies
**WARNING** The library name was changed from `ESP Async WebServer` to `ESPAsyncWebServer` as per the Arduino Lint recommendations, but its name had to stay `ESP Async WebServer` in Arduino Registry.
**PlatformIO / pioarduino:**
```ini
lib_compat_mode = strict
lib_ldf_mode = chain
lib_deps = mathieucarbou/ESPAsyncWebServer @ 3.3.23
```
**Dependencies:**
- **ESP32 with AsyncTCP**: `mathieucarbou/AsyncTCP @ 3.2.14`
Arduino IDE: [https://github.com/mathieucarbou/AsyncTCP#v3.2.14](https://github.com/mathieucarbou/AsyncTCP/releases)
- **ESP32 with AsyncTCPSock**: `https://github.com/mathieucarbou/AsyncTCPSock/archive/refs/tags/v1.0.3-dev.zip`
- **ESP8266**: `esphome/ESPAsyncTCP-esphome @ 2.0.0`
Arduino IDE: [https://github.com/mathieucarbou/esphome-ESPAsyncTCP#v2.0.0](https://github.com/mathieucarbou/esphome-ESPAsyncTCP/releases/tag/v2.0.0)
- **RP2040**: `khoih-prog/AsyncTCP_RP2040W @ 1.2.0`
Arduino IDE: [https://github.com/khoih-prog/AsyncTCP_RP2040W#v1.2.0](https://github.com/khoih-prog/AsyncTCP_RP2040W/releases/tag/v1.2.0)
**AsyncTCPSock**
AsyncTCPSock can be used instead of AsyncTCP by excluding AsyncTCP from the library dependencies and adding AsyncTCPSock instead:
```ini
lib_compat_mode = strict
lib_ldf_mode = chain
lib_deps =
; mathieucarbou/AsyncTCP @ 3.2.14
https://github.com/mathieucarbou/AsyncTCPSock/archive/refs/tags/v1.0.3-dev.zip
mathieucarbou/ESPAsyncWebServer @ 3.3.23
lib_ignore =
AsyncTCP
mathieucarbou/AsyncTCP
```
## Performance
Performance of `mathieucarbou/ESPAsyncWebServer @ 3.3.23`:
```bash
> brew install autocannon
> autocannon -c 10 -w 10 -d 20 http://192.168.4.1
```
With `mathieucarbou/AsyncTCP @ 3.2.14`
[![](https://mathieu.carbou.me/ESPAsyncWebServer/perf-c10.png)](https://mathieu.carbou.me/ESPAsyncWebServer/perf-c10.png)
With `https://github.com/mathieucarbou/AsyncTCPSock/archive/refs/tags/v1.0.3-dev.zip`:
[![](https://mathieu.carbou.me/ESPAsyncWebServer/perf-c10-asynctcpsock.png)](https://mathieu.carbou.me/ESPAsyncWebServer/perf-c10-asynctcpsock.png)
**SSE performance**
In the example, there is an endpoint `/events` with some comments showing how these metrics are calculated.
Test is running for 20 seconds with 10 connections.
```
// With AsyncTCP, with 10 workers: no message discarded from the queue
//
// Total: 1875 events, 468.75000000000000000000 events / second
// Total: 1870 events, 467.50000000000000000000 events / second
// Total: 1871 events, 467.75000000000000000000 events / second
// Total: 1875 events, 468.75000000000000000000 events / second
// Total: 1871 events, 467.75000000000000000000 events / second
// Total: 1805 events, 451.25000000000000000000 events / second
// Total: 1803 events, 450.75000000000000000000 events / second
// Total: 1873 events, 468.25000000000000000000 events / second
// Total: 1872 events, 468.00000000000000000000 events / second
// Total: 1805 events, 451.25000000000000000000 events / second
//
// With AsyncTCPSock, with 10 workers: no message discarded from the queue
//
// Total: 1242 events, 310.50000000000000000000 events / second
// Total: 1242 events, 310.50000000000000000000 events / second
// Total: 1242 events, 310.50000000000000000000 events / second
// Total: 1242 events, 310.50000000000000000000 events / second
// Total: 1181 events, 295.25000000000000000000 events / second
// Total: 1182 events, 295.50000000000000000000 events / second
// Total: 1240 events, 310.00000000000000000000 events / second
// Total: 1181 events, 295.25000000000000000000 events / second
// Total: 1181 events, 295.25000000000000000000 events / second
// Total: 1183 events, 295.75000000000000000000 events / second
```
## Important recommendations
Most of the crashes are caused by improper configuration of the library for the project.
@ -72,7 +166,7 @@ Here are some recommendations to avoid them.
1. Set the running core to be on the same core of your application (usually core 1) `-D CONFIG_ASYNC_TCP_RUNNING_CORE=1`
2. Set the stack size appropriately with `-D CONFIG_ASYNC_TCP_STACK_SIZE=16384`.
The default value of `16384` might be too much for your project.
You can look at the [MycilaTaskMonitor](https://oss.carbou.me/MycilaTaskMonitor) project to monitor the stack usage.
You can look at the [MycilaTaskMonitor](https://mathieu.carbou.me/MycilaTaskMonitor) project to monitor the stack usage.
3. You can change **if you know what you are doing** the task priority with `-D CONFIG_ASYNC_TCP_PRIORITY=10`.
Default is `10`.
4. You can increase the queue size with `-D CONFIG_ASYNC_TCP_QUEUE_SIZE=128`.
@ -1186,7 +1280,7 @@ For actual serving the file.
### Param Rewrite With Matching
It is possible to rewrite the request url with parameter matchg. Here is an example with one parameter:
It is possible to rewrite the request url with parameter match. Here is an example with one parameter:
Rewrite for example "/radio/{frequence}" -> "/radio?f={frequence}"
```cpp

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 {
public:
CaptiveRequestHandler() {}
virtual ~CaptiveRequestHandler() {}
bool canHandle(__unused AsyncWebServerRequest* request) {
bool canHandle(__unused AsyncWebServerRequest* request) const override {
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 {
public:
CaptiveRequestHandler() {}
virtual ~CaptiveRequestHandler() {}
bool canHandle(__unused AsyncWebServerRequest* request) {
bool canHandle(__unused AsyncWebServerRequest* request) const override {
return true;
}
void handleRequest(AsyncWebServerRequest* request) {
void handleRequest(AsyncWebServerRequest* request) override {
AsyncResponseStream* response = request->beginResponseStream("text/html");
response->print("<!DOCTYPE html><html><head><title>Captive Portal</title></head><body>");
response->print("<p>This is out captive portal front page.</p>");

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>
#if ASYNC_JSON_SUPPORT == 1
#if __has_include("ArduinoJson.h")
#include <ArduinoJson.h>
#include <AsyncJson.h>
#include <AsyncMessagePack.h>
@ -27,6 +27,80 @@
#include <LittleFS.h>
const char* htmlContent PROGMEM = R"(
<!DOCTYPE html>
<html>
<head>
<title>Sample HTML</title>
</head>
<body>
<h1>Hello, World!</h1>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod
rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper
arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit
accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi.
Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo
dapibus elit, id varius sem dui id lacus.</p>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod
rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper
arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit
accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi.
Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo
dapibus elit, id varius sem dui id lacus.</p>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod
rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper
arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit
accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi.
Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo
dapibus elit, id varius sem dui id lacus.</p>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod
rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper
arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit
accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi.
Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo
dapibus elit, id varius sem dui id lacus.</p>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod
rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper
arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit
accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi.
Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo
dapibus elit, id varius sem dui id lacus.</p>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod
rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper
arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit
accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi.
Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo
dapibus elit, id varius sem dui id lacus.</p>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod
rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper
arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit
accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi.
Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo
dapibus elit, id varius sem dui id lacus.</p>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod
rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper
arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit
accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi.
Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo
dapibus elit, id varius sem dui id lacus.</p>
</body>
</html>
)";
const size_t htmlContentLength = strlen_P(htmlContent);
const char* staticContent PROGMEM = R"(
<!DOCTYPE html>
<html>
<head>
<title>Sample HTML</title>
</head>
<body>
<h1>Hello, %IP%</h1>
</body>
</html>
)";
AsyncWebServer server(80);
AsyncEventSource events("/events");
AsyncWebSocket ws("/ws");
@ -111,11 +185,14 @@ void notFound(AsyncWebServerRequest* request) {
request->send(404, "text/plain", "Not found");
}
#if ASYNC_JSON_SUPPORT == 1
#if __has_include("ArduinoJson.h")
AsyncCallbackJsonWebHandler* jsonHandler = new AsyncCallbackJsonWebHandler("/json2");
AsyncCallbackMessagePackWebHandler* msgPackHandler = new AsyncCallbackMessagePackWebHandler("/msgpack2");
#endif
static const char characters[] = "0123456789ABCDEF";
static size_t charactersIndex = 0;
void setup() {
Serial.begin(115200);
@ -134,6 +211,32 @@ void setup() {
WiFi.softAP("esp-captive");
#endif
#ifdef ESP32
LittleFS.begin(true);
#else
LittleFS.begin();
#endif
{
File f = LittleFS.open("/index.txt", "w");
if (f) {
for (size_t c = 0; c < sizeof(characters); c++) {
for (size_t i = 0; i < 1024; i++) {
f.print(characters[c]);
}
}
f.close();
}
}
{
File f = LittleFS.open("/index.html", "w");
if (f) {
f.print(staticContent);
f.close();
}
}
// curl -v -X GET http://192.168.4.1/handler-not-sending-response
server.on("/handler-not-sending-response", HTTP_GET, [](AsyncWebServerRequest* request) {
// handler forgot to send a response to the client => 501 Not Implemented
@ -215,16 +318,18 @@ void setup() {
headerFree.keep("X-Keep-Me");
headerFree.keep("host");
// global middleware
server.addMiddleware(&requestLogger);
server.addMiddlewares({&rateLimit, &cors, &headerFilter});
cors.setOrigin("http://192.168.4.1");
cors.setMethods("POST, GET, OPTIONS, DELETE");
cors.setHeaders("X-Custom-Header");
cors.setAllowCredentials(false);
cors.setMaxAge(600);
#ifndef PERF_TEST
// global middleware
server.addMiddleware(&requestLogger);
server.addMiddlewares({&rateLimit, &cors, &headerFilter});
#endif
// Test CORS preflight request
// curl -v -X OPTIONS -H "origin: http://192.168.4.1" http://192.168.4.1/middleware/cors
server.on("/middleware/cors", HTTP_GET, [](AsyncWebServerRequest* request) {
@ -299,12 +404,103 @@ void setup() {
request->redirect("/");
});
// PERF TEST:
// > brew install autocannon
// > autocannon -c 10 -w 10 -d 20 http://192.168.4.1
server.on("/", HTTP_GET, [](AsyncWebServerRequest* request) {
request->send(200, "text/plain", "Hello, world");
request->send(200, "text/html", htmlContent);
});
server.on("/file", HTTP_GET, [](AsyncWebServerRequest* request) {
request->send(LittleFS, "/index.html");
// curl -v -X GET http://192.168.4.1/index.txt
server.serveStatic("/index.txt", LittleFS, "/index.txt");
// curl -v -X GET http://192.168.4.1/index-private.txt
server.serveStatic("/index-private.txt", LittleFS, "/index.txt").setAuthentication("admin", "admin");
// ServeStatic static is used to serve static output which never changes over time.
// This special endpoints automatyically adds caching headers.
// If a template processor is used, it must enure that the outputed content will always be the ame over time and never changes.
// Otherwise, do not use serveStatic.
// Example below: IP never changes.
// curl -v -X GET http://192.168.4.1/index-static.html
server.serveStatic("/index-static.html", LittleFS, "/index.html").setTemplateProcessor([](const String& var) -> String {
if (var == "IP") {
// for CI, commented out since H2 board doesn ot support WiFi
// return WiFi.localIP().toString();
// return WiFi.softAPIP().toString();
return "127.0.0..1";
}
return emptyString;
});
// to serve a template with dynamic content (output changes over time), use normal
// Example below: content changes over tinme do not use serveStatic.
// curl -v -X GET http://192.168.4.1/index-dynamic.html
server.on("/index-dynamic.html", HTTP_GET, [](AsyncWebServerRequest* request) {
request->send(LittleFS, "/index.html", "text/html", false, [](const String& var) -> String {
if (var == "IP")
return String(random(0, 1000));
return emptyString;
});
});
// Issue #14: assert failed: tcp_update_rcv_ann_wnd (needs help to test fix)
// > curl -v http://192.168.4.1/issue-14
pinMode(4, OUTPUT);
server.on("/issue-14", HTTP_GET, [](AsyncWebServerRequest* request) {
digitalWrite(4, HIGH);
request->send(LittleFS, "/index.txt", "text/pain");
delay(500);
digitalWrite(4, LOW);
});
/*
Chunked encoding test: sends 16k of characters.
curl -N -v -X GET -H "origin: http://192.168.4.1" http://192.168.4.1/chunk
*/
server.on("/chunk", HTTP_HEAD | HTTP_GET, [](AsyncWebServerRequest* request) {
AsyncWebServerResponse* response = request->beginChunkedResponse("text/html", [](uint8_t* buffer, size_t maxLen, size_t index) -> size_t {
if (index >= 16384)
return 0;
memset(buffer, characters[charactersIndex], maxLen);
charactersIndex = (charactersIndex + 1) % sizeof(characters);
return maxLen;
});
request->send(response);
});
// curl -N -v -X GET http://192.168.4.1/chunked.html --output -
// curl -N -v -X GET -H "if-none-match: 4272" http://192.168.4.1/chunked.html --output -
server.on("/chunked.html", HTTP_GET, [](AsyncWebServerRequest* request) {
String len = String(htmlContentLength);
if (request->header(asyncsrv::T_INM) == len) {
request->send(304);
return;
}
AsyncWebServerResponse* response = request->beginChunkedResponse("text/html", [](uint8_t* buffer, size_t maxLen, size_t index) -> size_t {
Serial.printf("%u / %u\n", index, htmlContentLength);
// finished ?
if (htmlContentLength <= index) {
Serial.println("finished");
return 0;
}
// serve a maximum of 1024 or maxLen bytes of the remaining content
const int chunkSize = min((size_t)1024, min(maxLen, htmlContentLength - index));
Serial.printf("sending: %u\n", chunkSize);
memcpy(buffer, htmlContent + index, chunkSize);
return chunkSize;
});
response->addHeader(asyncsrv::T_Cache_Control, "public,max-age=60");
response->addHeader(asyncsrv::T_ETag, len);
request->send(response);
});
/*
@ -353,14 +549,12 @@ void setup() {
request->send(200, "text/plain", "Hello, POST: " + message);
});
#if ASYNC_JSON_SUPPORT == 1
#if __has_include("ArduinoJson.h")
// JSON
// receives JSON and sends JSON
jsonHandler->onRequest([](AsyncWebServerRequest* request, JsonVariant& json) {
// JsonObject jsonObj = json.as<JsonObject>();
// ...
// sends JSON
// curl -v -X GET http://192.168.4.1/json1
server.on("/json1", HTTP_GET, [](AsyncWebServerRequest* request) {
AsyncJsonResponse* response = new AsyncJsonResponse();
JsonObject root = response->getRoot().to<JsonObject>();
root["hello"] = "world";
@ -368,11 +562,24 @@ void setup() {
request->send(response);
});
// sends JSON
server.on("/json1", HTTP_GET, [](AsyncWebServerRequest* request) {
// curl -v -X GET http://192.168.4.1/json2
server.on("/json2", HTTP_GET, [](AsyncWebServerRequest* request) {
AsyncResponseStream* response = request->beginResponseStream("application/json");
JsonDocument doc;
JsonObject root = doc.to<JsonObject>();
root["foo"] = "bar";
serializeJson(root, *response);
request->send(response);
});
// curl -v -X POST -H 'Content-Type: application/json' -d '{"name":"You"}' http://192.168.4.1/json2
// curl -v -X PUT -H 'Content-Type: application/json' -d '{"name":"You"}' http://192.168.4.1/json2
jsonHandler->setMethod(HTTP_POST | HTTP_PUT);
jsonHandler->onRequest([](AsyncWebServerRequest* request, JsonVariant& json) {
serializeJson(json, Serial);
AsyncJsonResponse* response = new AsyncJsonResponse();
JsonObject root = response->getRoot().to<JsonObject>();
root["hello"] = "world";
root["hello"] = json.as<JsonObject>()["name"];
response->setLength();
request->send(response);
});
@ -436,10 +643,69 @@ void setup() {
}
});
// SSS endpoints
// sends a message every 10 ms
//
// go to http://192.168.4.1/sse
// > curl -v -N -H "Accept: text/event-stream" http://192.168.4.1/events
//
// some perf tests:
// launch 16 concurrent workers for 30 seconds
// > for i in {1..16}; do ( count=$(gtimeout 30 curl -s -N -H "Accept: text/event-stream" http://192.168.4.1/events 2>&1 | grep -c "^data:"); echo "Total: $count events, $(echo "$count / 4" | bc -l) events / second" ) & done;
//
// With AsyncTCP, with 16 workers: a lot of Too many messages queued: deleting message
//
// Total: 119 events, 29.75000000000000000000 events / second
// Total: 727 events, 181.75000000000000000000 events / second
// Total: 1386 events, 346.50000000000000000000 events / second
// Total: 1385 events, 346.25000000000000000000 events / second
// Total: 1276 events, 319.00000000000000000000 events / second
// Total: 1411 events, 352.75000000000000000000 events / second
// Total: 1276 events, 319.00000000000000000000 events / second
// Total: 1333 events, 333.25000000000000000000 events / second
// Total: 1250 events, 312.50000000000000000000 events / second
// Total: 1275 events, 318.75000000000000000000 events / second
// Total: 1271 events, 317.75000000000000000000 events / second
// Total: 1271 events, 317.75000000000000000000 events / second
// Total: 1254 events, 313.50000000000000000000 events / second
// Total: 1251 events, 312.75000000000000000000 events / second
// Total: 1254 events, 313.50000000000000000000 events / second
// Total: 1262 events, 315.50000000000000000000 events / second
//
// With AsyncTCP, with 10 workers:
//
// Total: 1875 events, 468.75000000000000000000 events / second
// Total: 1870 events, 467.50000000000000000000 events / second
// Total: 1871 events, 467.75000000000000000000 events / second
// Total: 1875 events, 468.75000000000000000000 events / second
// Total: 1871 events, 467.75000000000000000000 events / second
// Total: 1805 events, 451.25000000000000000000 events / second
// Total: 1803 events, 450.75000000000000000000 events / second
// Total: 1873 events, 468.25000000000000000000 events / second
// Total: 1872 events, 468.00000000000000000000 events / second
// Total: 1805 events, 451.25000000000000000000 events / second
//
// With AsyncTCPSock, with 16 workers: ESP32 CRASH !!!
//
// With AsyncTCPSock, with 10 workers:
//
// Total: 1242 events, 310.50000000000000000000 events / second
// Total: 1242 events, 310.50000000000000000000 events / second
// Total: 1242 events, 310.50000000000000000000 events / second
// Total: 1242 events, 310.50000000000000000000 events / second
// Total: 1181 events, 295.25000000000000000000 events / second
// Total: 1182 events, 295.50000000000000000000 events / second
// Total: 1240 events, 310.00000000000000000000 events / second
// Total: 1181 events, 295.25000000000000000000 events / second
// Total: 1181 events, 295.25000000000000000000 events / second
// Total: 1183 events, 295.75000000000000000000 events / second
//
server.addHandler(&events);
// Run: websocat ws://192.168.4.1/ws
server.addHandler(&ws);
#if ASYNC_JSON_SUPPORT == 1
#if __has_include("ArduinoJson.h")
server.addHandler(jsonHandler);
server.addHandler(msgPackHandler);
#endif
@ -450,7 +716,7 @@ void setup() {
}
uint32_t lastSSE = 0;
uint32_t deltaSSE = 5;
uint32_t deltaSSE = 10;
uint32_t lastWS = 0;
uint32_t deltaWS = 100;
@ -463,9 +729,8 @@ void loop() {
}
if (now - lastWS >= deltaWS) {
ws.printfAll("kp%.4f", (10.0 / 3.0));
// ws.getClients
for (auto& client : ws.getClients()) {
client.text("kp%.4f", (10.0 / 3.0));
client.printf("kp%.4f", (10.0 / 3.0));
}
lastWS = millis();
}

View File

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

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",
"version": "3.3.7",
"version": "3.3.23",
"description": "Asynchronous HTTP and WebSocket Server Library for ESP32, ESP8266 and RP2040. Supports: WebSocket, SSE, Authentication, Arduino Json 7, File Upload, Static File serving, URL Rewrite, URL Redirect, etc.",
"keywords": "http,async,websocket,webserver",
"homepage": "https://github.com/mathieucarbou/ESPAsyncWebServer",
@ -28,7 +28,7 @@
{
"owner": "mathieucarbou",
"name": "AsyncTCP",
"version": "^3.2.5",
"version": "^3.2.14",
"platforms": "espressif32"
},
{

View File

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

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

View File

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

View File

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

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) {}
#endif
bool AsyncCallbackJsonWebHandler::canHandle(AsyncWebServerRequest* request) {
if (!_onRequest)
return false;
WebRequestMethodComposite request_method = request->method();
if (!(_method & request_method))
bool AsyncCallbackJsonWebHandler::canHandle(AsyncWebServerRequest* request) const {
if (!_onRequest || !request->isHTTP() || !(_method & request->method()))
return false;
if (_uri.length() && (_uri != request->url() && !request->url().startsWith(_uri + "/")))
return false;
if (request_method != HTTP_GET && !request->contentType().equalsIgnoreCase(asyncsrv::T_application_json))
if (request->method() != HTTP_GET && !request->contentType().equalsIgnoreCase(asyncsrv::T_application_json))
return false;
return true;

View File

@ -119,11 +119,11 @@ class AsyncCallbackJsonWebHandler : public AsyncWebHandler {
void setMaxContentLength(int maxContentLength) { _maxContentLength = maxContentLength; }
void onRequest(ArJsonRequestHandlerFunction fn) { _onRequest = fn; }
virtual bool canHandle(AsyncWebServerRequest* request) override final;
virtual void handleRequest(AsyncWebServerRequest* request) override final;
virtual void handleUpload(__unused AsyncWebServerRequest* request, __unused const String& filename, __unused size_t index, __unused uint8_t* data, __unused size_t len, __unused bool final) override final {}
virtual void handleBody(AsyncWebServerRequest* request, uint8_t* data, size_t len, size_t index, size_t total) override final;
virtual bool isRequestHandlerTrivial() override final { return !_onRequest; }
bool canHandle(AsyncWebServerRequest* request) const override final;
void handleRequest(AsyncWebServerRequest* request) override final;
void handleUpload(__unused AsyncWebServerRequest* request, __unused const String& filename, __unused size_t index, __unused uint8_t* data, __unused size_t len, __unused bool final) override final {}
void handleBody(AsyncWebServerRequest* request, uint8_t* data, size_t len, size_t index, size_t total) override final;
bool isRequestHandlerTrivial() const override final { return !_onRequest; }
};
#endif // ASYNC_JSON_SUPPORT == 1

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) {}
#endif
bool AsyncCallbackMessagePackWebHandler::canHandle(AsyncWebServerRequest* request) {
if (!_onRequest)
return false;
WebRequestMethodComposite request_method = request->method();
if (!(_method & request_method))
bool AsyncCallbackMessagePackWebHandler::canHandle(AsyncWebServerRequest* request) const {
if (!_onRequest || !request->isHTTP() || !(_method & request->method()))
return false;
if (_uri.length() && (_uri != request->url() && !request->url().startsWith(_uri + "/")))
return false;
if (request_method != HTTP_GET && !request->contentType().equalsIgnoreCase(asyncsrv::T_application_msgpack))
if (request->method() != HTTP_GET && !request->contentType().equalsIgnoreCase(asyncsrv::T_application_msgpack))
return false;
return true;

View File

@ -92,11 +92,11 @@ class AsyncCallbackMessagePackWebHandler : public AsyncWebHandler {
void setMaxContentLength(int maxContentLength) { _maxContentLength = maxContentLength; }
void onRequest(ArMessagePackRequestHandlerFunction fn) { _onRequest = fn; }
virtual bool canHandle(AsyncWebServerRequest* request) override final;
virtual void handleRequest(AsyncWebServerRequest* request) override final;
virtual void handleUpload(__unused AsyncWebServerRequest* request, __unused const String& filename, __unused size_t index, __unused uint8_t* data, __unused size_t len, __unused bool final) override final {}
virtual void handleBody(AsyncWebServerRequest* request, uint8_t* data, size_t len, size_t index, size_t total) override final;
virtual bool isRequestHandlerTrivial() override final { return !_onRequest; }
bool canHandle(AsyncWebServerRequest* request) const override final;
void handleRequest(AsyncWebServerRequest* request) override final;
void handleUpload(__unused AsyncWebServerRequest* request, __unused const String& filename, __unused size_t index, __unused uint8_t* data, __unused size_t len, __unused bool final) override final {}
void handleBody(AsyncWebServerRequest* request, uint8_t* data, size_t len, size_t index, size_t total) override final;
bool isRequestHandlerTrivial() const override final { return !_onRequest; }
};
#endif // ASYNC_MSG_PACK_SUPPORT == 1

View File

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

View File

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

View File

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

View File

@ -48,10 +48,10 @@
#include "literals.h"
#define ASYNCWEBSERVER_VERSION "3.3.7"
#define ASYNCWEBSERVER_VERSION "3.3.23"
#define ASYNCWEBSERVER_VERSION_MAJOR 3
#define ASYNCWEBSERVER_VERSION_MINOR 3
#define ASYNCWEBSERVER_VERSION_REVISION 7
#define ASYNCWEBSERVER_VERSION_REVISION 23
#define ASYNCWEBSERVER_FORK_mathieucarbou
#ifdef ASYNCWEBSERVER_REGEX
@ -140,7 +140,6 @@ class AsyncWebHeader {
String _value;
public:
AsyncWebHeader() = default;
AsyncWebHeader(const AsyncWebHeader&) = default;
AsyncWebHeader(const char* name, const char* value) : _name(name), _value(value) {}
AsyncWebHeader(const String& name, const String& value) : _name(name), _value(value) {}
@ -166,11 +165,12 @@ typedef enum { RCT_NOT_USED = -1,
// this enum is similar to Arduino WebServer's AsyncAuthType and PsychicHttp
typedef enum {
AUTH_NONE = 0,
AUTH_BASIC,
AUTH_DIGEST,
AUTH_BEARER,
AUTH_OTHER,
AUTH_NONE = 0, // always allow
AUTH_BASIC = 1,
AUTH_DIGEST = 2,
AUTH_BEARER = 3,
AUTH_OTHER = 4,
AUTH_DENIED = 255, // always returns 401
} AsyncAuthType;
typedef std::function<size_t(uint8_t*, size_t, size_t)> AwsResponseFiller;
@ -264,23 +264,21 @@ class AsyncWebServerRequest {
size_t contentLength() const { return _contentLength; }
bool multipart() const { return _isMultipart; }
#ifndef ESP8266
const char* methodToString() const;
const char* requestedConnTypeToString() const;
#else
const __FlashStringHelper* methodToString() const;
const __FlashStringHelper* requestedConnTypeToString() const;
#endif
RequestedConnectionType requestedConnType() const { return _reqconntype; }
bool isExpectedRequestedConnType(RequestedConnectionType erct1, RequestedConnectionType erct2 = RCT_NOT_USED, RequestedConnectionType erct3 = RCT_NOT_USED);
bool isExpectedRequestedConnType(RequestedConnectionType erct1, RequestedConnectionType erct2 = RCT_NOT_USED, RequestedConnectionType erct3 = RCT_NOT_USED) const;
bool isWebSocketUpgrade() const { return _method == HTTP_GET && isExpectedRequestedConnType(RCT_WS); }
bool isSSE() const { return _method == HTTP_GET && isExpectedRequestedConnType(RCT_EVENT); }
bool isHTTP() const { return isExpectedRequestedConnType(RCT_DEFAULT, RCT_HTTP); }
void onDisconnect(ArDisconnectHandler fn);
// hash is the string representation of:
// base64(user:pass) for basic or
// user:realm:md5(user:realm:pass) for digest
bool authenticate(const char* hash);
bool authenticate(const char* username, const char* credentials, const char* realm = NULL, bool isHash = false);
bool authenticate(const char* hash) const;
bool authenticate(const char* username, const char* credentials, const char* realm = NULL, bool isHash = false) const;
void requestAuthentication(const char* realm = nullptr, bool isDigest = true) { requestAuthentication(isDigest ? AsyncAuthType::AUTH_DIGEST : AsyncAuthType::AUTH_BASIC, realm); }
void requestAuthentication(AsyncAuthType method, const char* realm = nullptr, const char* _authFailMsg = nullptr);
@ -523,9 +521,7 @@ using ArMiddlewareCallback = std::function<void(AsyncWebServerRequest* request,
class AsyncMiddleware {
public:
virtual ~AsyncMiddleware() {}
virtual void run(__unused AsyncWebServerRequest* request, __unused ArMiddlewareNext next) {
return next();
};
virtual void run(__unused AsyncWebServerRequest* request, __unused ArMiddlewareNext next) { return next(); };
private:
friend class AsyncWebHandler;
@ -547,7 +543,7 @@ class AsyncMiddlewareFunction : public AsyncMiddleware {
// For internal use only: super class to add/remove middleware to server or handlers
class AsyncMiddlewareChain {
public:
virtual ~AsyncMiddlewareChain();
~AsyncMiddlewareChain();
void addMiddleware(ArMiddlewareCallback fn);
void addMiddleware(AsyncMiddleware* middleware);
@ -570,13 +566,26 @@ class AuthenticationMiddleware : public AsyncMiddleware {
void setRealm(const char* realm) { _realm = realm; }
void setAuthFailureMessage(const char* message) { _authFailMsg = message; }
// set the authentication method to use
// default is AUTH_NONE: no authentication required
// AUTH_BASIC: basic authentication
// AUTH_DIGEST: digest authentication
// AUTH_BEARER: bearer token authentication
// AUTH_OTHER: other authentication method
// AUTH_DENIED: always return 401 Unauthorized
// if a method is set but no username or password is set, authentication will be ignored
void setAuthType(AsyncAuthType authMethod) { _authMethod = authMethod; }
// precompute and store the hash value based on the username, realm, and authMethod
// precompute and store the hash value based on the username, password, realm.
// can be used for DIGEST and BASIC to avoid recomputing the hash for each request.
// returns true if the hash was successfully generated and replaced
bool generateHash();
bool allowed(AsyncWebServerRequest* request);
// returns true if the username and password (or hash) are set
bool hasCredentials() const { return _hasCreds; }
bool allowed(AsyncWebServerRequest* request) const;
void run(AsyncWebServerRequest* request, ArMiddlewareNext next);
@ -634,7 +643,7 @@ class LoggingMiddleware : public AsyncMiddleware {
public:
void setOutput(Print& output) { _out = &output; }
void setEnabled(bool enabled) { _enabled = enabled; }
bool isEnabled() { return _enabled && _out; }
bool isEnabled() const { return _enabled && _out; }
void run(AsyncWebServerRequest* request, ArMiddlewareNext next);
@ -722,18 +731,16 @@ class AsyncWebHandler : public AsyncMiddlewareChain {
public:
AsyncWebHandler() {}
AsyncWebHandler& setFilter(ArRequestFilterFunction fn);
AsyncWebHandler& setAuthentication(const char* username, const char* password);
AsyncWebHandler& setAuthentication(const String& username, const String& password) { return setAuthentication(username.c_str(), password.c_str()); };
bool filter(AsyncWebServerRequest* request) { return _filter == NULL || _filter(request); }
virtual ~AsyncWebHandler() {}
virtual bool canHandle(AsyncWebServerRequest* request __attribute__((unused))) {
return false;
}
AsyncWebHandler& setFilter(ArRequestFilterFunction fn);
AsyncWebHandler& setAuthentication(const char* username, const char* password, AsyncAuthType authMethod = AsyncAuthType::AUTH_DIGEST);
AsyncWebHandler& setAuthentication(const String& username, const String& password, AsyncAuthType authMethod = AsyncAuthType::AUTH_DIGEST) { return setAuthentication(username.c_str(), password.c_str(), authMethod); };
bool filter(AsyncWebServerRequest* request) { return _filter == NULL || _filter(request); }
virtual bool canHandle(AsyncWebServerRequest* request __attribute__((unused))) const { return false; }
virtual void handleRequest(__unused AsyncWebServerRequest* request) {}
virtual void handleUpload(__unused AsyncWebServerRequest* request, __unused const String& filename, __unused size_t index, __unused uint8_t* data, __unused size_t len, __unused bool final) {}
virtual void handleBody(__unused AsyncWebServerRequest* request, __unused uint8_t* data, __unused size_t len, __unused size_t index, __unused size_t total) {}
virtual bool isRequestHandlerTrivial() { return true; }
virtual bool isRequestHandlerTrivial() const { return true; }
};
/*
@ -764,26 +771,22 @@ class AsyncWebServerResponse {
WebResponseState _state;
public:
#ifndef ESP8266
static const char* responseCodeToString(int code);
#else
static const __FlashStringHelper* responseCodeToString(int code);
#endif
public:
AsyncWebServerResponse();
virtual ~AsyncWebServerResponse();
virtual void setCode(int code);
virtual ~AsyncWebServerResponse() {}
void setCode(int code);
int code() const { return _code; }
virtual void setContentLength(size_t len);
void setContentLength(size_t len);
void setContentType(const String& type) { setContentType(type.c_str()); }
virtual void setContentType(const char* type);
virtual bool addHeader(const char* name, const char* value, bool replaceExisting = true);
void setContentType(const char* type);
bool addHeader(const char* name, const char* value, bool replaceExisting = true);
bool addHeader(const String& name, const String& value, bool replaceExisting = true) { return addHeader(name.c_str(), value.c_str(), replaceExisting); }
bool addHeader(const char* name, long value, bool replaceExisting = true) { return addHeader(name, String(value), replaceExisting); }
bool addHeader(const String& name, long value, bool replaceExisting = true) { return addHeader(name.c_str(), value, replaceExisting); }
virtual bool removeHeader(const char* name);
virtual const AsyncWebHeader* getHeader(const char* name) const;
bool removeHeader(const char* name);
const AsyncWebHeader* getHeader(const char* name) const;
const std::list<AsyncWebHeader>& getHeaders() const { return _headers; }
#ifndef ESP8266
@ -794,7 +797,7 @@ class AsyncWebServerResponse {
_assembleHead(buffer, version);
return buffer;
}
virtual void _assembleHead(String& buffer, uint8_t version);
void _assembleHead(String& buffer, uint8_t version);
virtual bool _started() const;
virtual bool _finished() const;

View File

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

View File

@ -137,11 +137,7 @@ String generateDigestHash(const char* username, const char* password, const char
return in;
}
#ifndef ESP8266
bool checkDigestAuthentication(const char* header, const char* method, const char* username, const char* password, const char* realm, bool passwordIsHash, const char* nonce, const char* opaque, const char* uri)
#else
bool checkDigestAuthentication(const char* header, const __FlashStringHelper* method, const char* username, const char* password, const char* realm, bool passwordIsHash, const char* nonce, const char* opaque, const char* uri)
#endif
{
if (username == NULL || password == NULL || header == NULL || method == NULL) {
// os_printf("AUTH FAIL: missing requred fields\n");

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);
#ifdef ESP8266
bool checkDigestAuthentication(const char* header, const __FlashStringHelper* method, const char* username, const char* password, const char* realm, bool passwordIsHash, const char* nonce, const char* opaque, const char* uri);
#endif
// for storing hashed versions on the device that can be authenticated against
String generateDigestHash(const char* username, const char* password, const char* realm);

View File

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

View File

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

View File

@ -49,9 +49,9 @@ AsyncWebServerRequest::~AsyncWebServerRequest() {
_pathParams.clear();
if (_response != NULL) {
delete _response;
}
AsyncWebServerResponse* r = _response;
_response = NULL;
delete r;
if (_tempObject != NULL) {
free(_tempObject);
@ -280,38 +280,40 @@ bool AsyncWebServerRequest::_parseReqHeader() {
}
} else if (name.equalsIgnoreCase(T_Content_Length)) {
_contentLength = atoi(value.c_str());
} else if (name.equalsIgnoreCase(T_EXPECT) && value == T_100_CONTINUE) {
} else if (name.equalsIgnoreCase(T_EXPECT) && value.equalsIgnoreCase(T_100_CONTINUE)) {
_expectingContinue = true;
} else if (name.equalsIgnoreCase(T_AUTH)) {
if (value.length() > 5 && value.substring(0, 5).equalsIgnoreCase(T_BASIC)) {
_authorization = value.substring(6);
_authMethod = AsyncAuthType::AUTH_BASIC;
} else if (value.length() > 6 && value.substring(0, 6).equalsIgnoreCase(T_DIGEST)) {
_authMethod = AsyncAuthType::AUTH_DIGEST;
_authorization = value.substring(7);
} else if (value.length() > 6 && value.substring(0, 6).equalsIgnoreCase(T_BEARER)) {
_authMethod = AsyncAuthType::AUTH_BEARER;
_authorization = value.substring(7);
} else {
int space = value.indexOf(' ');
if (space == -1) {
_authorization = value;
_authMethod = AsyncAuthType::AUTH_OTHER;
}
} else {
if (name.equalsIgnoreCase(T_UPGRADE) && value.equalsIgnoreCase(T_WS)) {
// WebSocket request can be uniquely identified by header: [Upgrade: websocket]
_reqconntype = RCT_WS;
} else if (name.equalsIgnoreCase(T_ACCEPT)) {
String lowcase(value);
lowcase.toLowerCase();
#ifndef ESP8266
const char* substr = std::strstr(lowcase.c_str(), T_text_event_stream);
#else
const char* substr = std::strstr(lowcase.c_str(), String(T_text_event_stream).c_str());
#endif
if (substr != NULL) {
// WebEvent request can be uniquely identified by header: [Accept: text/event-stream]
_reqconntype = RCT_EVENT;
} else {
String method = value.substring(0, space);
if (method.equalsIgnoreCase(T_BASIC)) {
_authMethod = AsyncAuthType::AUTH_BASIC;
} else if (method.equalsIgnoreCase(T_DIGEST)) {
_authMethod = AsyncAuthType::AUTH_DIGEST;
} else if (method.equalsIgnoreCase(T_BEARER)) {
_authMethod = AsyncAuthType::AUTH_BEARER;
} else {
_authMethod = AsyncAuthType::AUTH_OTHER;
}
_authorization = value.substring(space + 1);
}
} else if (name.equalsIgnoreCase(T_UPGRADE) && value.equalsIgnoreCase(T_WS)) {
// WebSocket request can be uniquely identified by header: [Upgrade: websocket]
_reqconntype = RCT_WS;
} else if (name.equalsIgnoreCase(T_ACCEPT)) {
String lowcase(value);
lowcase.toLowerCase();
#ifndef ESP8266
const char* substr = std::strstr(lowcase.c_str(), T_text_event_stream);
#else
const char* substr = std::strstr(lowcase.c_str(), String(T_text_event_stream).c_str());
#endif
if (substr != NULL) {
// WebEvent request can be uniquely identified by header: [Accept: text/event-stream]
_reqconntype = RCT_EVENT;
}
}
_headers.emplace_back(name, value);
@ -779,7 +781,7 @@ void AsyncWebServerRequest::redirect(const char* url, int code) {
send(response);
}
bool AsyncWebServerRequest::authenticate(const char* username, const char* password, const char* realm, bool passwordIsHash) {
bool AsyncWebServerRequest::authenticate(const char* username, const char* password, const char* realm, bool passwordIsHash) const {
if (_authorization.length()) {
if (_authMethod == AsyncAuthType::AUTH_DIGEST)
return checkDigestAuthentication(_authorization.c_str(), methodToString(), username, password, realm, passwordIsHash, NULL, NULL, NULL);
@ -791,7 +793,7 @@ bool AsyncWebServerRequest::authenticate(const char* username, const char* passw
return false;
}
bool AsyncWebServerRequest::authenticate(const char* hash) {
bool AsyncWebServerRequest::authenticate(const char* hash) const {
if (!_authorization.length() || hash == NULL)
return false;
@ -831,7 +833,7 @@ void AsyncWebServerRequest::requestAuthentication(AsyncAuthType method, const ch
break;
}
case AsyncAuthType::AUTH_DIGEST: {
constexpr size_t len = strlen(T_DIGEST_) + strlen(T_realm__) + strlen(T_auth_nonce) + 32 + strlen(T__opaque) + 32 + 1;
size_t len = strlen(T_DIGEST_) + strlen(T_realm__) + strlen(T_auth_nonce) + 32 + strlen(T__opaque) + 32 + 1;
String header;
header.reserve(len + strlen(realm));
header.concat(T_DIGEST_);
@ -938,7 +940,6 @@ String AsyncWebServerRequest::urlDecode(const String& text) const {
return decoded;
}
#ifndef ESP8266
const char* AsyncWebServerRequest::methodToString() const {
if (_method == HTTP_ANY)
return T_ANY;
@ -958,29 +959,7 @@ const char* AsyncWebServerRequest::methodToString() const {
return T_OPTIONS;
return T_UNKNOWN;
}
#else // ESP8266
const __FlashStringHelper* AsyncWebServerRequest::methodToString() const {
if (_method == HTTP_ANY)
return FPSTR(T_ANY);
if (_method & HTTP_GET)
return FPSTR(T_GET);
if (_method & HTTP_POST)
return FPSTR(T_POST);
if (_method & HTTP_DELETE)
return FPSTR(T_DELETE);
if (_method & HTTP_PUT)
return FPSTR(T_PUT);
if (_method & HTTP_PATCH)
return FPSTR(T_PATCH);
if (_method & HTTP_HEAD)
return FPSTR(T_HEAD);
if (_method & HTTP_OPTIONS)
return FPSTR(T_OPTIONS);
return FPSTR(T_UNKNOWN);
}
#endif // ESP8266
#ifndef ESP8266
const char* AsyncWebServerRequest::requestedConnTypeToString() const {
switch (_reqconntype) {
case RCT_NOT_USED:
@ -997,32 +976,9 @@ const char* AsyncWebServerRequest::requestedConnTypeToString() const {
return T_ERROR;
}
}
#else // ESP8266
const __FlashStringHelper* AsyncWebServerRequest::requestedConnTypeToString() const {
switch (_reqconntype) {
case RCT_NOT_USED:
return FPSTR(T_RCT_NOT_USED);
case RCT_DEFAULT:
return FPSTR(T_RCT_DEFAULT);
case RCT_HTTP:
return FPSTR(T_RCT_HTTP);
case RCT_WS:
return FPSTR(T_RCT_WS);
case RCT_EVENT:
return FPSTR(T_RCT_EVENT);
default:
return FPSTR(T_ERROR);
}
}
#endif // ESP8266
bool AsyncWebServerRequest::isExpectedRequestedConnType(RequestedConnectionType erct1, RequestedConnectionType erct2, RequestedConnectionType erct3) {
bool res = false;
if ((erct1 != RCT_NOT_USED) && (erct1 == _reqconntype))
res = true;
if ((erct2 != RCT_NOT_USED) && (erct2 == _reqconntype))
res = true;
if ((erct3 != RCT_NOT_USED) && (erct3 == _reqconntype))
res = true;
return res;
bool AsyncWebServerRequest::isExpectedRequestedConnType(RequestedConnectionType erct1, RequestedConnectionType erct2, RequestedConnectionType erct3) const {
return ((erct1 != RCT_NOT_USED) && (erct1 == _reqconntype)) ||
((erct2 != RCT_NOT_USED) && (erct2 == _reqconntype)) ||
((erct3 != RCT_NOT_USED) && (erct3 == _reqconntype));
}

View File

@ -26,9 +26,10 @@
#undef min
#undef max
#endif
#include "literals.h"
#include <StreamString.h>
#include <memory>
#include <vector>
#include "literals.h"
// It is possible to restore these defines, but one can use _min and _max instead. Or std::min, std::max.
@ -39,9 +40,9 @@ class AsyncBasicResponse : public AsyncWebServerResponse {
public:
explicit AsyncBasicResponse(int code, const char* contentType = asyncsrv::empty, const char* content = asyncsrv::empty);
AsyncBasicResponse(int code, const String& contentType, const String& content = emptyString) : AsyncBasicResponse(code, contentType.c_str(), content.c_str()) {}
void _respond(AsyncWebServerRequest* request);
size_t _ack(AsyncWebServerRequest* request, size_t len, uint32_t time);
bool _sourceValid() const { return true; }
void _respond(AsyncWebServerRequest* request) override final;
size_t _ack(AsyncWebServerRequest* request, size_t len, uint32_t time) override final;
bool _sourceValid() const override final { return true; }
};
class AsyncAbstractResponse : public AsyncWebServerResponse {
@ -60,9 +61,10 @@ class AsyncAbstractResponse : public AsyncWebServerResponse {
public:
AsyncAbstractResponse(AwsTemplateProcessor callback = nullptr);
void _respond(AsyncWebServerRequest* request);
size_t _ack(AsyncWebServerRequest* request, size_t len, uint32_t time);
bool _sourceValid() const { return false; }
virtual ~AsyncAbstractResponse() {}
void _respond(AsyncWebServerRequest* request) override final;
size_t _ack(AsyncWebServerRequest* request, size_t len, uint32_t time) override final;
virtual bool _sourceValid() const { return false; }
virtual size_t _fillBuffer(uint8_t* buf __attribute__((unused)), size_t maxLen __attribute__((unused))) { return 0; }
};
@ -85,9 +87,9 @@ class AsyncFileResponse : public AsyncAbstractResponse {
AsyncFileResponse(FS& fs, const String& path, const String& contentType, bool download = false, AwsTemplateProcessor callback = nullptr) : AsyncFileResponse(fs, path, contentType.c_str(), download, callback) {}
AsyncFileResponse(File content, const String& path, const char* contentType = asyncsrv::empty, bool download = false, AwsTemplateProcessor callback = nullptr);
AsyncFileResponse(File content, const String& path, const String& contentType, bool download = false, AwsTemplateProcessor callack = nullptr) : AsyncFileResponse(content, path, contentType.c_str(), download, callack) {}
~AsyncFileResponse();
bool _sourceValid() const { return !!(_content); }
virtual size_t _fillBuffer(uint8_t* buf, size_t maxLen) override;
~AsyncFileResponse() { _content.close(); }
bool _sourceValid() const override final { return !!(_content); }
size_t _fillBuffer(uint8_t* buf, size_t maxLen) override final;
};
class AsyncStreamResponse : public AsyncAbstractResponse {
@ -97,8 +99,8 @@ class AsyncStreamResponse : public AsyncAbstractResponse {
public:
AsyncStreamResponse(Stream& stream, const char* contentType, size_t len, AwsTemplateProcessor callback = nullptr);
AsyncStreamResponse(Stream& stream, const String& contentType, size_t len, AwsTemplateProcessor callback = nullptr) : AsyncStreamResponse(stream, contentType.c_str(), len, callback) {}
bool _sourceValid() const { return !!(_content); }
virtual size_t _fillBuffer(uint8_t* buf, size_t maxLen) override;
bool _sourceValid() const override final { return !!(_content); }
size_t _fillBuffer(uint8_t* buf, size_t maxLen) override final;
};
class AsyncCallbackResponse : public AsyncAbstractResponse {
@ -109,8 +111,8 @@ class AsyncCallbackResponse : public AsyncAbstractResponse {
public:
AsyncCallbackResponse(const char* contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback = nullptr);
AsyncCallbackResponse(const String& contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback = nullptr) : AsyncCallbackResponse(contentType.c_str(), len, callback, templateCallback) {}
bool _sourceValid() const { return !!(_content); }
virtual size_t _fillBuffer(uint8_t* buf, size_t maxLen) override;
bool _sourceValid() const override final { return !!(_content); }
size_t _fillBuffer(uint8_t* buf, size_t maxLen) override final;
};
class AsyncChunkedResponse : public AsyncAbstractResponse {
@ -121,8 +123,8 @@ class AsyncChunkedResponse : public AsyncAbstractResponse {
public:
AsyncChunkedResponse(const char* contentType, AwsResponseFiller callback, AwsTemplateProcessor templateCallback = nullptr);
AsyncChunkedResponse(const String& contentType, AwsResponseFiller callback, AwsTemplateProcessor templateCallback = nullptr) : AsyncChunkedResponse(contentType.c_str(), callback, templateCallback) {}
bool _sourceValid() const { return !!(_content); }
virtual size_t _fillBuffer(uint8_t* buf, size_t maxLen) override;
bool _sourceValid() const override final { return !!(_content); }
size_t _fillBuffer(uint8_t* buf, size_t maxLen) override final;
};
class AsyncProgmemResponse : public AsyncAbstractResponse {
@ -133,22 +135,19 @@ class AsyncProgmemResponse : public AsyncAbstractResponse {
public:
AsyncProgmemResponse(int code, const char* contentType, const uint8_t* content, size_t len, AwsTemplateProcessor callback = nullptr);
AsyncProgmemResponse(int code, const String& contentType, const uint8_t* content, size_t len, AwsTemplateProcessor callback = nullptr) : AsyncProgmemResponse(code, contentType.c_str(), content, len, callback) {}
bool _sourceValid() const { return true; }
virtual size_t _fillBuffer(uint8_t* buf, size_t maxLen) override;
bool _sourceValid() const override final { return true; }
size_t _fillBuffer(uint8_t* buf, size_t maxLen) override final;
};
class cbuf;
class AsyncResponseStream : public AsyncAbstractResponse, public Print {
private:
std::unique_ptr<cbuf> _content;
StreamString _content;
public:
AsyncResponseStream(const char* contentType, size_t bufferSize);
AsyncResponseStream(const String& contentType, size_t bufferSize) : AsyncResponseStream(contentType.c_str(), bufferSize) {}
~AsyncResponseStream();
bool _sourceValid() const { return (_state < RESPONSE_END); }
virtual size_t _fillBuffer(uint8_t* buf, size_t maxLen) override;
bool _sourceValid() const override final { return (_state < RESPONSE_END); }
size_t _fillBuffer(uint8_t* buf, size_t maxLen) override final;
size_t write(const uint8_t* data, size_t len);
size_t write(uint8_t data);
using Print::write;

View File

@ -20,7 +20,6 @@
*/
#include "ESPAsyncWebServer.h"
#include "WebResponseImpl.h"
#include "cbuf.h"
using namespace asyncsrv;
@ -38,7 +37,6 @@ void* memchr(void* ptr, int ch, size_t count) {
*
*/
#ifndef ESP8266
const char* AsyncWebServerResponse::responseCodeToString(int code) {
switch (code) {
case 100:
@ -127,96 +125,6 @@ const char* AsyncWebServerResponse::responseCodeToString(int code) {
return T_HTTP_CODE_ANY;
}
}
#else // ESP8266
const __FlashStringHelper* AsyncWebServerResponse::responseCodeToString(int code) {
switch (code) {
case 100:
return FPSTR(T_HTTP_CODE_100);
case 101:
return FPSTR(T_HTTP_CODE_101);
case 200:
return FPSTR(T_HTTP_CODE_200);
case 201:
return FPSTR(T_HTTP_CODE_201);
case 202:
return FPSTR(T_HTTP_CODE_202);
case 203:
return FPSTR(T_HTTP_CODE_203);
case 204:
return FPSTR(T_HTTP_CODE_204);
case 205:
return FPSTR(T_HTTP_CODE_205);
case 206:
return FPSTR(T_HTTP_CODE_206);
case 300:
return FPSTR(T_HTTP_CODE_300);
case 301:
return FPSTR(T_HTTP_CODE_301);
case 302:
return FPSTR(T_HTTP_CODE_302);
case 303:
return FPSTR(T_HTTP_CODE_303);
case 304:
return FPSTR(T_HTTP_CODE_304);
case 305:
return FPSTR(T_HTTP_CODE_305);
case 307:
return FPSTR(T_HTTP_CODE_307);
case 400:
return FPSTR(T_HTTP_CODE_400);
case 401:
return FPSTR(T_HTTP_CODE_401);
case 402:
return FPSTR(T_HTTP_CODE_402);
case 403:
return FPSTR(T_HTTP_CODE_403);
case 404:
return FPSTR(T_HTTP_CODE_404);
case 405:
return FPSTR(T_HTTP_CODE_405);
case 406:
return FPSTR(T_HTTP_CODE_406);
case 407:
return FPSTR(T_HTTP_CODE_407);
case 408:
return FPSTR(T_HTTP_CODE_408);
case 409:
return FPSTR(T_HTTP_CODE_409);
case 410:
return FPSTR(T_HTTP_CODE_410);
case 411:
return FPSTR(T_HTTP_CODE_411);
case 412:
return FPSTR(T_HTTP_CODE_412);
case 413:
return FPSTR(T_HTTP_CODE_413);
case 414:
return FPSTR(T_HTTP_CODE_414);
case 415:
return FPSTR(T_HTTP_CODE_415);
case 416:
return FPSTR(T_HTTP_CODE_416);
case 417:
return FPSTR(T_HTTP_CODE_417);
case 429:
return FPSTR(T_HTTP_CODE_429);
case 500:
return FPSTR(T_HTTP_CODE_500);
case 501:
return FPSTR(T_HTTP_CODE_501);
case 502:
return FPSTR(T_HTTP_CODE_502);
case 503:
return FPSTR(T_HTTP_CODE_503);
case 504:
return FPSTR(T_HTTP_CODE_504);
case 505:
return FPSTR(T_HTTP_CODE_505);
default:
return FPSTR(T_HTTP_CODE_ANY);
}
}
#endif // ESP8266
AsyncWebServerResponse::AsyncWebServerResponse()
: _code(0), _contentType(), _contentLength(0), _sendContentLength(true), _chunked(false), _headLength(0), _sentLength(0), _ackedLength(0), _writtenLength(0), _state(RESPONSE_SETUP) {
@ -225,8 +133,6 @@ AsyncWebServerResponse::AsyncWebServerResponse()
}
}
AsyncWebServerResponse::~AsyncWebServerResponse() = default;
void AsyncWebServerResponse::setCode(int code) {
if (_state == RESPONSE_SETUP)
_code = code;
@ -648,11 +554,6 @@ size_t AsyncAbstractResponse::_fillBufferAndProcessTemplates(uint8_t* data, size
* File Response
* */
AsyncFileResponse::~AsyncFileResponse() {
if (_content)
_content.close();
}
void AsyncFileResponse::_setContentTypeFromPath(const String& path) {
#if HAVE_EXTERN_GET_Content_Type_FUNCTION
#ifndef ESP8266
@ -868,24 +769,17 @@ AsyncResponseStream::AsyncResponseStream(const char* contentType, size_t bufferS
_code = 200;
_contentLength = 0;
_contentType = contentType;
_content = std::unique_ptr<cbuf>(new cbuf(bufferSize)); // std::make_unique<cbuf>(bufferSize);
_content.reserve(bufferSize);
}
AsyncResponseStream::~AsyncResponseStream() = default;
size_t AsyncResponseStream::_fillBuffer(uint8_t* buf, size_t maxLen) {
return _content->read((char*)buf, maxLen);
return _content.readBytes((char*)buf, maxLen);
}
size_t AsyncResponseStream::write(const uint8_t* data, size_t len) {
if (_started())
return 0;
if (len > _content->room()) {
size_t needed = len - _content->room();
_content->resizeAdd(needed);
}
size_t written = _content->write((const char*)data, len);
size_t written = _content.write(data, len);
_contentLength += written;
return written;
}

View File

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