Aktualizace na verzi 3.0.6

This commit is contained in:
Pavel Brychta 2024-07-02 20:06:33 +02:00
parent 5c12efc75b
commit e0375a1fb6
30 changed files with 2862 additions and 2879 deletions

17
CMakeLists.txt Normal file
View File

@ -0,0 +1,17 @@
set(COMPONENT_SRCDIRS
"src"
)
set(COMPONENT_ADD_INCLUDEDIRS
"src"
)
set(COMPONENT_REQUIRES
"arduino-esp32"
"AsyncTCP"
)
register_component()
target_compile_definitions(${COMPONENT_TARGET} PUBLIC -DESP32)
target_compile_options(${COMPONENT_TARGET} PRIVATE -fno-rtti)

View File

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

View File

@ -1,7 +1,7 @@
# bundle exec jekyll serve --host=0.0.0.0 # bundle exec jekyll serve --host=0.0.0.0
title: ESP Async WebServer title: ESP Async WebServer
description: "Asynchronous HTTP and WebSocket Server Library for ESP32" description: "Asynchronous HTTP and WebSocket Server Library for ESP32, ESP8266 and RP2040"
remote_theme: pages-themes/cayman@v0.2.0 remote_theme: pages-themes/cayman@v0.2.0
plugins: plugins:
- jekyll-remote-theme - jekyll-remote-theme

View File

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

View File

@ -1,10 +1,13 @@
#include <DNSServer.h> #include <DNSServer.h>
#ifdef ESP32 #ifdef ESP32
#include <WiFi.h>
#include <AsyncTCP.h> #include <AsyncTCP.h>
#include <WiFi.h>
#elif defined(ESP8266) #elif defined(ESP8266)
#include <ESP8266WiFi.h> #include <ESP8266WiFi.h>
#include <ESPAsyncTCP.h> #include <ESPAsyncTCP.h>
#elif defined(TARGET_RP2040)
#include <WebServer.h>
#include <WiFi.h>
#endif #endif
#include "ESPAsyncWebServer.h" #include "ESPAsyncWebServer.h"
@ -32,7 +35,6 @@ public:
} }
}; };
void setup() { void setup() {
Serial.begin(115200); Serial.begin(115200);
Serial.println(); Serial.println();
@ -40,7 +42,8 @@ void setup(){
if (!WiFi.softAP("esp-captive")) { if (!WiFi.softAP("esp-captive")) {
Serial.println("Soft AP creation failed."); Serial.println("Soft AP creation failed.");
while (1); while (1)
;
} }
dnsServer.start(53, "*", WiFi.softAPIP()); dnsServer.start(53, "*", WiFi.softAPIP());

View File

@ -1,6 +1,6 @@
#include "mbedtls/md5.h"
#include <Arduino.h> #include <Arduino.h>
#include <MD5Builder.h> #include <MD5Builder.h>
#include "mbedtls/md5.h"
void setup() { void setup() {
Serial.begin(115200); Serial.begin(115200);

View File

@ -7,6 +7,9 @@
#elif defined(ESP8266) #elif defined(ESP8266)
#include <ESP8266WiFi.h> #include <ESP8266WiFi.h>
#include <ESPAsyncTCP.h> #include <ESPAsyncTCP.h>
#elif defined(TARGET_RP2040)
#include <WebServer.h>
#include <WiFi.h>
#endif #endif
#include "ESPAsyncWebServer.h" #include "ESPAsyncWebServer.h"

View File

@ -7,11 +7,14 @@
#include <Arduino.h> #include <Arduino.h>
#ifdef ESP32 #ifdef ESP32
#include <WiFi.h>
#include <AsyncTCP.h> #include <AsyncTCP.h>
#include <WiFi.h>
#elif defined(ESP8266) #elif defined(ESP8266)
#include <ESP8266WiFi.h> #include <ESP8266WiFi.h>
#include <ESPAsyncTCP.h> #include <ESPAsyncTCP.h>
#elif defined(TARGET_RP2040)
#include <WebServer.h>
#include <WiFi.h>
#endif #endif
#include <ESPAsyncWebServer.h> #include <ESPAsyncWebServer.h>

View File

@ -17,7 +17,11 @@ class StreamConcat : public Stream {
return c != -1 ? c : _s2->read(); return c != -1 ? c : _s2->read();
} }
#if defined(TARGET_RP2040)
size_t readBytes(char* buffer, size_t length) {
#else
size_t readBytes(char* buffer, size_t length) override { size_t readBytes(char* buffer, size_t length) override {
#endif
size_t count = _s1->readBytes(buffer, length); size_t count = _s1->readBytes(buffer, length);
return count > 0 ? count : _s2->readBytes(buffer, length); return count > 0 ? count : _s2->readBytes(buffer, length);
} }

View File

@ -6,6 +6,9 @@
#elif defined(ESP8266) #elif defined(ESP8266)
#include <ESP8266WiFi.h> #include <ESP8266WiFi.h>
#include <ESPAsyncTCP.h> #include <ESPAsyncTCP.h>
#elif defined(TARGET_RP2040)
#include <WebServer.h>
#include <WiFi.h>
#endif #endif
#include "StreamConcat.h" #include "StreamConcat.h"
#include "StreamString.h" #include "StreamString.h"
@ -42,7 +45,11 @@ void setup() {
StreamConcat stream1(&header, &body); StreamConcat stream1(&header, &body);
StreamString content; StreamString content;
#if defined(TARGET_RP2040)
content.printf("FreeHeap: %d", rp2040.getFreeHeap());
#else
content.printf("FreeHeap: %" PRIu32, ESP.getFreeHeap()); content.printf("FreeHeap: %" PRIu32, ESP.getFreeHeap());
#endif
StreamConcat stream2 = StreamConcat(&stream1, &content); StreamConcat stream2 = StreamConcat(&stream1, &content);
File footer = LittleFS.open("/footer.html", "r"); File footer = LittleFS.open("/footer.html", "r");
@ -67,7 +74,11 @@ void loop() {
// dnsServer.processNextRequest(); // dnsServer.processNextRequest();
if (millis() - last > 2000) { if (millis() - last > 2000) {
#if defined(TARGET_RP2040)
Serial.printf("FreeHeap: %d", rp2040.getFreeHeap());
#else
Serial.printf("FreeHeap: %" PRIu32, ESP.getFreeHeap()); Serial.printf("FreeHeap: %" PRIu32, ESP.getFreeHeap());
#endif
last = millis(); last = millis();
} }
} }

View File

@ -18,7 +18,11 @@ class StreamString : public Stream {
return c; return c;
} }
#if defined(TARGET_RP2040)
size_t readBytes(char* buffer, size_t length) {
#else
size_t readBytes(char* buffer, size_t length) override { size_t readBytes(char* buffer, size_t length) override {
#endif
if (length > _buffer.length()) if (length > _buffer.length())
length = _buffer.length(); length = _buffer.length();
// Don't use _str.ToCharArray() because it inserts a terminator // Don't use _str.ToCharArray() because it inserts a terminator

View File

@ -1,7 +1,7 @@
{ {
"name": "ESP Async WebServer", "name": "ESP Async WebServer",
"version": "2.10.8", "version": "3.0.6",
"description": "Asynchronous HTTP and WebSocket Server Library for ESP32. Supports: WebSocket, SSE, Authentication, Arduino Json 7, File Upload, Static File serving, URL Rewrite, URL Redirect, etc.", "description": "Asynchronous HTTP and WebSocket Server Library for ESP32, ESP8266 and RP2040. Supports: WebSocket, SSE, Authentication, Arduino Json 7, File Upload, Static File serving, URL Rewrite, URL Redirect, etc.",
"keywords": "http,async,websocket,webserver", "keywords": "http,async,websocket,webserver",
"homepage": "https://github.com/mathieucarbou/ESPAsyncWebServer", "homepage": "https://github.com/mathieucarbou/ESPAsyncWebServer",
"repository": { "repository": {
@ -21,7 +21,8 @@
"frameworks": "arduino", "frameworks": "arduino",
"platforms": [ "platforms": [
"espressif32", "espressif32",
"espressif8266" "espressif8266",
"raspberrypi"
], ],
"dependencies": [ "dependencies": [
{ {
@ -39,6 +40,12 @@
{ {
"name": "Hash", "name": "Hash",
"platforms": "espressif8266" "platforms": "espressif8266"
},
{
"owner": "khoih-prog",
"name": "AsyncTCP_RP2040W",
"version": "^1.2.0",
"platforms": "raspberrypi"
} }
], ],
"export": { "export": {
@ -50,5 +57,8 @@
"LICENSE", "LICENSE",
"README.md" "README.md"
] ]
},
"build": {
"libCompatMode": "strict"
} }
} }

View File

@ -1,10 +1,10 @@
name=ESP Async WebServer name=ESP Async WebServer
version=2.10.8 version=3.0.6
author=Me-No-Dev author=Me-No-Dev
maintainer=Mathieu Carbou <mathieu.carbou@gmail.com> maintainer=Mathieu Carbou <mathieu.carbou@gmail.com>
sentence=Asynchronous HTTP and WebSocket Server Library for ESP32 sentence=Asynchronous HTTP and WebSocket Server Library for ESP32, ESP8266 and RP2040
paragraph=Supports: WebSocket, SSE, Authentication, Arduino Json 7, File Upload, Static File serving, URL Rewrite, URL Redirect, etc paragraph=Supports: WebSocket, SSE, Authentication, Arduino Json 7, File Upload, Static File serving, URL Rewrite, URL Redirect, etc
category=Other category=Other
url=https://github.com/mathieucarbou/ESPAsyncWebServer url=https://github.com/mathieucarbou/ESPAsyncWebServer
architectures=esp8266,esp32 architectures=*
license=LGPL-3.0 license=LGPL-3.0

View File

@ -4,42 +4,57 @@ build_flags =
-Wall -Wextra -Wall -Wextra
-D CONFIG_ARDUHAL_LOG_COLORS -D CONFIG_ARDUHAL_LOG_COLORS
-D CORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_VERBOSE -D CORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_VERBOSE
lib_deps =
bblanchon/ArduinoJson @ 7.0.4
mathieucarbou/Async TCP @ ^3.1.4
; https://github.com/mathieucarbou/AsyncTCP
; https://github.com/me-no-dev/AsyncTCP
esphome/ESPAsyncTCP-esphome @ 2.0.0
upload_protocol = esptool upload_protocol = esptool
monitor_speed = 115200 monitor_speed = 115200
monitor_filters = esp32_exception_decoder, log2file monitor_filters = esp32_exception_decoder, log2file
[platformio] [platformio]
lib_dir = . lib_dir = .
src_dir = examples/CaptivePortal ; src_dir = examples/CaptivePortal
; src_dir = examples/SimpleServer ; src_dir = examples/SimpleServer
; src_dir = examples/StreamFiles src_dir = examples/StreamFiles
; src_dir = examples/Filters ; src_dir = examples/Filters
; src_dir = examples/Draft ; src_dir = examples/Draft
[env:arduino] [env:arduino]
platform = espressif32 platform = espressif32
board = esp32dev board = esp32dev
lib_deps =
bblanchon/ArduinoJson @ 7.1.0
mathieucarbou/Async TCP @ ^3.1.4
[env:arduino-2] [env:arduino-2]
platform = espressif32@6.7.0 platform = espressif32@6.7.0
board = esp32dev board = esp32dev
lib_deps =
bblanchon/ArduinoJson @ 7.1.0
mathieucarbou/Async TCP @ ^3.1.4
[env:arduino-3] [env:arduino-3]
platform = espressif32 platform = espressif32
platform_packages= platform_packages=
platformio/framework-arduinoespressif32 @ https://github.com/espressif/arduino-esp32.git#3.0.1 platformio/framework-arduinoespressif32 @ https://github.com/espressif/arduino-esp32.git#3.0.2
platformio/framework-arduinoespressif32-libs @ https://github.com/espressif/arduino-esp32/releases/download/3.0.1/esp32-arduino-libs-3.0.1.zip platformio/framework-arduinoespressif32-libs @ https://github.com/espressif/arduino-esp32/releases/download/3.0.2/esp32-arduino-libs-3.0.2.zip
board = esp32dev board = esp32dev
lib_deps =
bblanchon/ArduinoJson @ 7.1.0
mathieucarbou/Async TCP @ ^3.1.4
[env:esp8266] [env:esp8266]
platform = espressif8266 platform = espressif8266
board = huzzah board = huzzah
lib_deps = lib_deps =
bblanchon/ArduinoJson @ 7.0.4 bblanchon/ArduinoJson @ 7.1.0
esphome/ESPAsyncTCP-esphome @ 2.0.0 esphome/ESPAsyncTCP-esphome @ 2.0.0
; PlatformIO support for Raspberry Pi Pico is not official
; https://github.com/platformio/platform-raspberrypi/pull/36
; https://github.com/earlephilhower/arduino-pico/blob/master/docs/platformio.rst
; board settings: https://github.com/earlephilhower/arduino-pico/blob/master/tools/json/rpipico.json
[env:rpipicow]
upload_protocol = picotool
platform = https://github.com/maxgerhardt/platform-raspberrypi.git
board = rpipicow
lib_deps =
bblanchon/ArduinoJson @ 7.1.0
khoih-prog/AsyncTCP_RP2040W @ 1.2.0

View File

@ -18,10 +18,10 @@
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/ */
#include "Arduino.h" #include "Arduino.h"
#include "AsyncEventSource.h" #if defined(ESP32)
#ifndef ESP8266
#include <rom/ets_sys.h> #include <rom/ets_sys.h>
#endif #endif
#include "AsyncEventSource.h"
static String generateEventMessage(const char* message, const char* event, uint32_t id, uint32_t reconnect) { static String generateEventMessage(const char* message, const char* event, uint32_t id, uint32_t reconnect) {
String ev; String ev;
@ -110,8 +110,7 @@ static String generateEventMessage(const char *message, const char *event, uint3
// Message // Message
AsyncEventSourceMessage::AsyncEventSourceMessage(const char* data, size_t len) AsyncEventSourceMessage::AsyncEventSourceMessage(const char* data, size_t len)
: _data(nullptr), _len(len), _sent(0), _acked(0) : _data(nullptr), _len(len), _sent(0), _acked(0) {
{
_data = (uint8_t*)malloc(_len + 1); _data = (uint8_t*)malloc(_len + 1);
if (_data == nullptr) { if (_data == nullptr) {
_len = 0; _len = 0;
@ -155,9 +154,7 @@ size_t AsyncEventSourceMessage::send(AsyncClient *client) {
// Client // Client
AsyncEventSourceClient::AsyncEventSourceClient(AsyncWebServerRequest *request, AsyncEventSource *server) AsyncEventSourceClient::AsyncEventSourceClient(AsyncWebServerRequest* request, AsyncEventSource* server) {
: _messageQueue(LinkedList<AsyncEventSourceMessage *>([](AsyncEventSourceMessage *m){ delete m; }))
{
_client = request->client(); _client = request->client();
_server = server; _server = server;
_lastId = 0; _lastId = 0;
@ -177,56 +174,56 @@ AsyncEventSourceClient::AsyncEventSourceClient(AsyncWebServerRequest *request, A
} }
AsyncEventSourceClient::~AsyncEventSourceClient() { AsyncEventSourceClient::~AsyncEventSourceClient() {
_lockmq.lock(); #ifdef ESP32
_messageQueue.free(); std::lock_guard<std::mutex> lock(_lockmq);
_lockmq.unlock(); #endif
_messageQueue.clear();
close(); close();
} }
void AsyncEventSourceClient::_queueMessage(AsyncEventSourceMessage *dataMessage){ void AsyncEventSourceClient::_queueMessage(const char* message, size_t len) {
if(dataMessage == NULL) #ifdef ESP32
return;
if(!connected()){
delete dataMessage;
return;
}
// length() is not thread-safe, thus acquiring the lock before this call.. // length() is not thread-safe, thus acquiring the lock before this call..
_lockmq.lock(); std::lock_guard<std::mutex> lock(_lockmq);
if(_messageQueue.length() >= SSE_MAX_QUEUED_MESSAGES){ #endif
if (_messageQueue.size() >= SSE_MAX_QUEUED_MESSAGES) {
#ifdef ESP8266 #ifdef ESP8266
ets_printf(String(F("ERROR: Too many messages queued\n")).c_str()); ets_printf(String(F("ERROR: Too many messages queued\n")).c_str());
#else #elif defined(ESP32)
log_e("Too many messages queued: deleting message"); log_e("Too many messages queued: deleting message");
#endif #endif
delete dataMessage; return;
} else { }
_messageQueue.add(dataMessage);
_messageQueue.emplace_back(message, len);
// runqueue trigger when new messages added // runqueue trigger when new messages added
if (_client->canSend()) { if (_client->canSend()) {
_runQueue(); _runQueue();
} }
} }
_lockmq.unlock();
}
void AsyncEventSourceClient::_onAck(size_t len, uint32_t time) { void AsyncEventSourceClient::_onAck(size_t len, uint32_t time) {
#ifdef ESP32
// Same here, acquiring the lock early // Same here, acquiring the lock early
_lockmq.lock(); std::lock_guard<std::mutex> lock(_lockmq);
while(len && !_messageQueue.isEmpty()){ #endif
len = _messageQueue.front()->ack(len, time); while (len && _messageQueue.size()) {
if(_messageQueue.front()->finished()) len = _messageQueue.front().ack(len, time);
_messageQueue.remove(_messageQueue.front()); if (_messageQueue.front().finished())
_messageQueue.pop_front();
} }
_runQueue(); _runQueue();
_lockmq.unlock();
} }
void AsyncEventSourceClient::_onPoll() { void AsyncEventSourceClient::_onPoll() {
_lockmq.lock(); #ifdef ESP32
if(!_messageQueue.isEmpty()){ // Same here, acquiring the lock early
std::lock_guard<std::mutex> lock(_lockmq);
#endif
if (_messageQueue.size()) {
_runQueue(); _runQueue();
} }
_lockmq.unlock();
} }
void AsyncEventSourceClient::_onTimeout(uint32_t time __attribute__((unused))) { void AsyncEventSourceClient::_onTimeout(uint32_t time __attribute__((unused))) {
@ -244,47 +241,35 @@ void AsyncEventSourceClient::close(){
} }
void AsyncEventSourceClient::write(const char* message, size_t len) { void AsyncEventSourceClient::write(const char* message, size_t len) {
_queueMessage(new AsyncEventSourceMessage(message, len)); if (!connected())
return;
_queueMessage(message, len);
} }
void AsyncEventSourceClient::send(const char* message, const char* event, uint32_t id, uint32_t reconnect) { void AsyncEventSourceClient::send(const char* message, const char* event, uint32_t id, uint32_t reconnect) {
if (!connected())
return;
String ev = generateEventMessage(message, event, id, reconnect); String ev = generateEventMessage(message, event, id, reconnect);
_queueMessage(new AsyncEventSourceMessage(ev.c_str(), ev.length())); _queueMessage(ev.c_str(), ev.length());
} }
size_t AsyncEventSourceClient::packetsWaiting() const { size_t AsyncEventSourceClient::packetsWaiting() const {
size_t len; #ifdef ESP32
_lockmq.lock(); std::lock_guard<std::mutex> lock(_lockmq);
len = _messageQueue.length(); #endif
_lockmq.unlock(); return _messageQueue.size();
return len;
} }
void AsyncEventSourceClient::_runQueue() { void AsyncEventSourceClient::_runQueue() {
// Calls to this private method now already protected by _lockmq acquisition // Calls to this private method now already protected by _lockmq acquisition
// so no extra call of _lockmq.lock() here.. // so no extra call of _lockmq.lock() here..
for (auto i = _messageQueue.begin(); i != _messageQueue.end(); ++i) { for (auto& i : _messageQueue) {
// If it crashes here, iterator (i) has been invalidated as _messageQueue if (!i.sent())
// has been changed... (UL 2020-11-15: Not supposed to happen any more ;-) ) i.send(_client);
if (!(*i)->sent()) {
(*i)->send(_client);
} }
} }
}
// Handler // Handler
AsyncEventSource::AsyncEventSource(const String& url)
: _url(url)
, _clients(LinkedList<AsyncEventSourceClient *>([](AsyncEventSourceClient *c){ delete c; }))
, _connectcb(NULL)
{}
AsyncEventSource::~AsyncEventSource(){
close();
}
void AsyncEventSource::onConnect(ArEventHandlerFunction cb) { void AsyncEventSource::onConnect(ArEventHandlerFunction cb) {
_connectcb = cb; _connectcb = cb;
} }
@ -294,34 +279,33 @@ void AsyncEventSource::authorizeConnect(ArAuthorizeConnectHandler cb){
} }
void AsyncEventSource::_addClient(AsyncEventSourceClient* client) { void AsyncEventSource::_addClient(AsyncEventSourceClient* client) {
/*char * temp = (char *)malloc(2054); if (!client)
if(temp != NULL){ return;
memset(temp+1,' ',2048); #ifdef ESP32
temp[0] = ':'; std::lock_guard<std::mutex> lock(_client_queue_lock);
temp[2049] = '\r'; #endif
temp[2050] = '\n'; _clients.emplace_back(client);
temp[2051] = '\r';
temp[2052] = '\n';
temp[2053] = 0;
client->write((const char *)temp, 2053);
free(temp);
}*/
AsyncWebLockGuard l(_client_queue_lock);
_clients.add(client);
if (_connectcb) if (_connectcb)
_connectcb(client); _connectcb(client);
} }
void AsyncEventSource::_handleDisconnect(AsyncEventSourceClient* client) { void AsyncEventSource::_handleDisconnect(AsyncEventSourceClient* client) {
AsyncWebLockGuard l(_client_queue_lock); #ifdef ESP32
_clients.remove(client); std::lock_guard<std::mutex> lock(_client_queue_lock);
#endif
for (auto i = _clients.begin(); i != _clients.end(); ++i) {
if (i->get() == client)
_clients.erase(i);
}
} }
void AsyncEventSource::close() { void AsyncEventSource::close() {
// While the whole loop is not done, the linked list is locked and so the // While the whole loop is not done, the linked list is locked and so the
// iterator should remain valid even when AsyncEventSource::_handleDisconnect() // iterator should remain valid even when AsyncEventSource::_handleDisconnect()
// is called very early // is called very early
AsyncWebLockGuard l(_client_queue_lock); #ifdef ESP32
std::lock_guard<std::mutex> lock(_client_queue_lock);
#endif
for (const auto& c : _clients) { for (const auto& c : _clients) {
if (c->connected()) if (c->connected())
c->close(); c->close();
@ -332,10 +316,12 @@ void AsyncEventSource::close(){
size_t AsyncEventSource::avgPacketsWaiting() const { size_t AsyncEventSource::avgPacketsWaiting() const {
size_t aql = 0; size_t aql = 0;
uint32_t nConnectedClients = 0; uint32_t nConnectedClients = 0;
AsyncWebLockGuard l(_client_queue_lock); #ifdef ESP32
if (_clients.isEmpty()) { std::lock_guard<std::mutex> lock(_client_queue_lock);
#endif
if (!_clients.size())
return 0; return 0;
}
for (const auto& c : _clients) { for (const auto& c : _clients) {
if (c->connected()) { if (c->connected()) {
aql += c->packetsWaiting(); aql += c->packetsWaiting();
@ -348,7 +334,9 @@ size_t AsyncEventSource::avgPacketsWaiting() const {
void AsyncEventSource::send( void AsyncEventSource::send(
const char* message, const char* event, uint32_t id, uint32_t reconnect) { const char* message, const char* event, uint32_t id, uint32_t reconnect) {
String ev = generateEventMessage(message, event, id, reconnect); String ev = generateEventMessage(message, event, id, reconnect);
AsyncWebLockGuard l(_client_queue_lock); #ifdef ESP32
std::lock_guard<std::mutex> lock(_client_queue_lock);
#endif
for (const auto& c : _clients) { for (const auto& c : _clients) {
if (c->connected()) { if (c->connected()) {
c->write(ev.c_str(), ev.length()); c->write(ev.c_str(), ev.length());
@ -357,11 +345,14 @@ void AsyncEventSource::send(
} }
size_t AsyncEventSource::count() const { size_t AsyncEventSource::count() const {
size_t n_clients; #ifdef ESP32
AsyncWebLockGuard l(_client_queue_lock); std::lock_guard<std::mutex> lock(_client_queue_lock);
n_clients = _clients.count_if([](AsyncEventSourceClient *c){ #endif
return c->connected(); size_t n_clients{0};
}); for (const auto& i : _clients)
if (i->connected())
++n_clients;
return n_clients; return n_clients;
} }
@ -409,4 +400,3 @@ size_t AsyncEventSourceResponse::_ack(AsyncWebServerRequest *request, size_t len
} }
return 0; return 0;
} }

View File

@ -21,22 +21,27 @@
#define ASYNCEVENTSOURCE_H_ #define ASYNCEVENTSOURCE_H_
#include <Arduino.h> #include <Arduino.h>
#include <list>
#ifdef ESP32 #ifdef ESP32
#include <AsyncTCP.h> #include <AsyncTCP.h>
#include <mutex>
#ifndef SSE_MAX_QUEUED_MESSAGES #ifndef SSE_MAX_QUEUED_MESSAGES
#define SSE_MAX_QUEUED_MESSAGES 32 #define SSE_MAX_QUEUED_MESSAGES 32
#endif #endif
#else #elif defined(ESP8266)
#include <ESPAsyncTCP.h> #include <ESPAsyncTCP.h>
#ifndef SSE_MAX_QUEUED_MESSAGES #ifndef SSE_MAX_QUEUED_MESSAGES
#define SSE_MAX_QUEUED_MESSAGES 8 #define SSE_MAX_QUEUED_MESSAGES 8
#endif #endif
#elif defined(TARGET_RP2040)
#include <AsyncTCP_RP2040W.h>
#ifndef SSE_MAX_QUEUED_MESSAGES
#define SSE_MAX_QUEUED_MESSAGES 32
#endif
#endif #endif
#include <ESPAsyncWebServer.h> #include <ESPAsyncWebServer.h>
#include "AsyncWebSynchronization.h"
#ifdef ESP8266 #ifdef ESP8266
#include <Hash.h> #include <Hash.h>
#ifdef CRYPTO_HASH_h // include Hash.h from espressif framework if the first include was from the crypto library #ifdef CRYPTO_HASH_h // include Hash.h from espressif framework if the first include was from the crypto library
@ -55,8 +60,8 @@
class AsyncEventSource; class AsyncEventSource;
class AsyncEventSourceResponse; class AsyncEventSourceResponse;
class AsyncEventSourceClient; class AsyncEventSourceClient;
typedef std::function<void(AsyncEventSourceClient *client)> ArEventHandlerFunction; using ArEventHandlerFunction = std::function<void(AsyncEventSourceClient* client)>;
typedef std::function<bool(AsyncWebServerRequest *request)> ArAuthorizeConnectHandler; using ArAuthorizeConnectHandler = std::function<bool(AsyncWebServerRequest* request)>;
class AsyncEventSourceMessage { class AsyncEventSourceMessage {
private: private:
@ -65,6 +70,7 @@ class AsyncEventSourceMessage {
size_t _sent; size_t _sent;
// size_t _ack; // size_t _ack;
size_t _acked; size_t _acked;
public: public:
AsyncEventSourceMessage(const char* data, size_t len); AsyncEventSourceMessage(const char* data, size_t len);
~AsyncEventSourceMessage(); ~AsyncEventSourceMessage();
@ -79,14 +85,14 @@ class AsyncEventSourceClient {
AsyncClient* _client; AsyncClient* _client;
AsyncEventSource* _server; AsyncEventSource* _server;
uint32_t _lastId; uint32_t _lastId;
LinkedList<AsyncEventSourceMessage *> _messageQueue; std::list<AsyncEventSourceMessage> _messageQueue;
// ArFi 2020-08-27 for protecting/serializing _messageQueue #ifdef ESP32
AsyncPlainLock _lockmq; mutable std::mutex _lockmq;
void _queueMessage(AsyncEventSourceMessage *dataMessage); #endif
void _queueMessage(const char* message, size_t len);
void _runQueue(); void _runQueue();
public: public:
AsyncEventSourceClient(AsyncWebServerRequest* request, AsyncEventSource* server); AsyncEventSourceClient(AsyncWebServerRequest* request, AsyncEventSource* server);
~AsyncEventSourceClient(); ~AsyncEventSourceClient();
@ -108,15 +114,18 @@ class AsyncEventSourceClient {
class AsyncEventSource : public AsyncWebHandler { class AsyncEventSource : public AsyncWebHandler {
private: private:
String _url; String _url;
LinkedList<AsyncEventSourceClient *> _clients; std::list<std::unique_ptr<AsyncEventSourceClient>> _clients;
#ifdef ESP32
// Same as for individual messages, protect mutations of _clients list // Same as for individual messages, protect mutations of _clients list
// since simultaneous access from different tasks is possible // since simultaneous access from different tasks is possible
AsyncWebLock _client_queue_lock; mutable std::mutex _client_queue_lock;
ArEventHandlerFunction _connectcb; #endif
ArEventHandlerFunction _connectcb{nullptr};
ArAuthorizeConnectHandler _authorizeConnectHandler; ArAuthorizeConnectHandler _authorizeConnectHandler;
public: public:
AsyncEventSource(const String& url); AsyncEventSource(const String& url) : _url(url){};
~AsyncEventSource(); ~AsyncEventSource() { close(); };
const char* url() const { return _url.c_str(); } const char* url() const { return _url.c_str(); }
void close(); void close();
@ -138,6 +147,7 @@ class AsyncEventSourceResponse: public AsyncWebServerResponse {
private: private:
String _content; String _content;
AsyncEventSource* _server; AsyncEventSource* _server;
public: public:
AsyncEventSourceResponse(AsyncEventSource* server); AsyncEventSourceResponse(AsyncEventSource* server);
void _respond(AsyncWebServerRequest* request); void _respond(AsyncWebServerRequest* request);
@ -145,5 +155,4 @@ class AsyncEventSourceResponse: public AsyncWebServerResponse {
bool _sourceValid() const { return true; } bool _sourceValid() const { return true; }
}; };
#endif /* ASYNCEVENTSOURCE_H_ */ #endif /* ASYNCEVENTSOURCE_H_ */

View File

@ -120,7 +120,6 @@ class AsyncJsonResponse : public AsyncAbstractResponse {
} }
#endif #endif
~AsyncJsonResponse() {}
JsonVariant& getRoot() { return _root; } JsonVariant& getRoot() { return _root; }
bool _sourceValid() const { return _isValid; } bool _sourceValid() const { return _isValid; }
size_t setLength() { size_t setLength() {

File diff suppressed because it is too large Load Diff

View File

@ -24,21 +24,26 @@
#include <Arduino.h> #include <Arduino.h>
#ifdef ESP32 #ifdef ESP32
#include <AsyncTCP.h> #include <AsyncTCP.h>
#include <mutex>
#ifndef WS_MAX_QUEUED_MESSAGES #ifndef WS_MAX_QUEUED_MESSAGES
#define WS_MAX_QUEUED_MESSAGES 32 #define WS_MAX_QUEUED_MESSAGES 32
#endif #endif
#else #elif defined(ESP8266)
#include <ESPAsyncTCP.h> #include <ESPAsyncTCP.h>
#ifndef WS_MAX_QUEUED_MESSAGES #ifndef WS_MAX_QUEUED_MESSAGES
#define WS_MAX_QUEUED_MESSAGES 8 #define WS_MAX_QUEUED_MESSAGES 8
#endif #endif
#elif defined(TARGET_RP2040)
#include <AsyncTCP_RP2040W.h>
#ifndef WS_MAX_QUEUED_MESSAGES
#define WS_MAX_QUEUED_MESSAGES 32
#endif #endif
#endif
#include <ESPAsyncWebServer.h> #include <ESPAsyncWebServer.h>
#include "AsyncWebSynchronization.h"
#include <list>
#include <deque> #include <deque>
#include <list>
#include <memory> #include <memory>
#ifdef ESP8266 #ifdef ESP8266
@ -87,10 +92,23 @@ typedef struct {
uint64_t index; uint64_t index;
} AwsFrameInfo; } AwsFrameInfo;
typedef enum { WS_DISCONNECTED, WS_CONNECTED, WS_DISCONNECTING } AwsClientStatus; typedef enum { WS_DISCONNECTED,
typedef enum { WS_CONTINUATION, WS_TEXT, WS_BINARY, WS_DISCONNECT = 0x08, WS_PING, WS_PONG } AwsFrameType; WS_CONNECTED,
typedef enum { WS_MSG_SENDING, WS_MSG_SENT, WS_MSG_ERROR } AwsMessageStatus; WS_DISCONNECTING } AwsClientStatus;
typedef enum { WS_EVT_CONNECT, WS_EVT_DISCONNECT, WS_EVT_PONG, WS_EVT_ERROR, WS_EVT_DATA } AwsEventType; typedef enum { WS_CONTINUATION,
WS_TEXT,
WS_BINARY,
WS_DISCONNECT = 0x08,
WS_PING,
WS_PONG } AwsFrameType;
typedef enum { WS_MSG_SENDING,
WS_MSG_SENT,
WS_MSG_ERROR } AwsMessageStatus;
typedef enum { WS_EVT_CONNECT,
WS_EVT_DISCONNECT,
WS_EVT_PONG,
WS_EVT_ERROR,
WS_EVT_DATA } AwsEventType;
class AsyncWebSocketMessageBuffer { class AsyncWebSocketMessageBuffer {
friend AsyncWebSocket; friend AsyncWebSocket;
@ -109,8 +127,7 @@ class AsyncWebSocketMessageBuffer {
size_t length() const { return _buffer->size(); } size_t length() const { return _buffer->size(); }
}; };
class AsyncWebSocketMessage class AsyncWebSocketMessage {
{
private: private:
AsyncWebSocketSharedBuffer _WSbuffer; AsyncWebSocketSharedBuffer _WSbuffer;
uint8_t _opcode{WS_TEXT}; uint8_t _opcode{WS_TEXT};
@ -136,9 +153,9 @@ class AsyncWebSocketClient {
AsyncWebSocket* _server; AsyncWebSocket* _server;
uint32_t _clientId; uint32_t _clientId;
AwsClientStatus _status; AwsClientStatus _status;
#ifdef ESP32
AsyncWebLock _lock; mutable std::mutex _lock;
#endif
std::deque<AsyncWebSocketControl> _controlQueue; std::deque<AsyncWebSocketControl> _controlQueue;
std::deque<AsyncWebSocketMessage> _messageQueue; std::deque<AsyncWebSocketMessage> _messageQueue;
bool closeWhenFull = true; bool closeWhenFull = true;
@ -260,7 +277,9 @@ class AsyncWebSocket: public AsyncWebHandler {
AwsEventHandler _eventHandler{nullptr}; AwsEventHandler _eventHandler{nullptr};
AwsHandshakeHandler _handshakeHandler; AwsHandshakeHandler _handshakeHandler;
bool _enabled; bool _enabled;
AsyncWebLock _lock; #ifdef ESP32
mutable std::mutex _lock;
#endif
public: public:
explicit AsyncWebSocket(const char* url) : _url(url), _cNextId(1), _enabled(true) {} explicit AsyncWebSocket(const char* url) : _url(url), _cNextId(1), _enabled(true) {}
@ -325,10 +344,11 @@ class AsyncWebSocket: public AsyncWebHandler {
size_t printf(uint32_t id, const char* format, ...) __attribute__((format(printf, 3, 4))); size_t printf(uint32_t id, const char* format, ...) __attribute__((format(printf, 3, 4)));
size_t printfAll(const char* format, ...) __attribute__((format(printf, 2, 3))); size_t printfAll(const char* format, ...) __attribute__((format(printf, 2, 3)));
#ifndef ESP32 #ifndef ESP32
size_t printf_P(uint32_t id, PGM_P formatP, ...) __attribute__((format(printf, 3, 4))); size_t printf_P(uint32_t id, PGM_P formatP, ...) __attribute__((format(printf, 3, 4)));
#endif
size_t printfAll_P(PGM_P formatP, ...) __attribute__((format(printf, 2, 3))); size_t printfAll_P(PGM_P formatP, ...) __attribute__((format(printf, 2, 3)));
#endif
// event listener // event listener
void onEvent(AwsEventHandler handler) { void onEvent(AwsEventHandler handler) {
@ -347,7 +367,6 @@ class AsyncWebSocket: public AsyncWebHandler {
virtual bool canHandle(AsyncWebServerRequest* request) override final; virtual bool canHandle(AsyncWebServerRequest* request) override final;
virtual void handleRequest(AsyncWebServerRequest* request) override final; virtual void handleRequest(AsyncWebServerRequest* request) override final;
// messagebuffer functions/objects. // messagebuffer functions/objects.
AsyncWebSocketMessageBuffer* makeBuffer(size_t size = 0); AsyncWebSocketMessageBuffer* makeBuffer(size_t size = 0);
AsyncWebSocketMessageBuffer* makeBuffer(const uint8_t* data, size_t size); AsyncWebSocketMessageBuffer* makeBuffer(const uint8_t* data, size_t size);
@ -360,6 +379,7 @@ class AsyncWebSocketResponse: public AsyncWebServerResponse {
private: private:
String _content; String _content;
AsyncWebSocket* _server; AsyncWebSocket* _server;
public: public:
AsyncWebSocketResponse(const String& key, AsyncWebSocket* server); AsyncWebSocketResponse(const String& key, AsyncWebSocket* server);
void _respond(AsyncWebServerRequest* request); void _respond(AsyncWebServerRequest* request);
@ -367,5 +387,4 @@ class AsyncWebSocketResponse: public AsyncWebServerResponse {
bool _sourceValid() const { return true; } bool _sourceValid() const { return true; }
}; };
#endif /* ASYNCWEBSOCKET_H_ */ #endif /* ASYNCWEBSOCKET_H_ */

View File

@ -1,134 +0,0 @@
#ifndef ASYNCWEBSYNCHRONIZATION_H_
#define ASYNCWEBSYNCHRONIZATION_H_
// Synchronisation is only available on ESP32, as the ESP8266 isn't using FreeRTOS by default
#include <ESPAsyncWebServer.h>
#ifdef ESP32
// This is the ESP32 version of the Sync Lock, using the FreeRTOS Semaphore
// Modified 'AsyncWebLock' to just only use mutex since pxCurrentTCB is not
// always available. According to example by Arjan Filius, changed name,
// added unimplemented version for ESP8266
class AsyncPlainLock
{
private:
SemaphoreHandle_t _lock;
public:
AsyncPlainLock() {
_lock = xSemaphoreCreateBinary();
// In this fails, the system is likely that much out of memory that
// we should abort anyways. If assertions are disabled, nothing is lost..
assert(_lock);
xSemaphoreGive(_lock);
}
~AsyncPlainLock() {
vSemaphoreDelete(_lock);
}
bool lock() const {
xSemaphoreTake(_lock, portMAX_DELAY);
return true;
}
void unlock() const {
xSemaphoreGive(_lock);
}
};
// This is the ESP32 version of the Sync Lock, using the FreeRTOS Semaphore
class AsyncWebLock
{
private:
SemaphoreHandle_t _lock;
mutable TaskHandle_t _lockedBy{};
public:
AsyncWebLock()
{
_lock = xSemaphoreCreateBinary();
// In this fails, the system is likely that much out of memory that
// we should abort anyways. If assertions are disabled, nothing is lost..
assert(_lock);
_lockedBy = NULL;
xSemaphoreGive(_lock);
}
~AsyncWebLock() {
vSemaphoreDelete(_lock);
}
bool lock() const {
const auto currentTask = xTaskGetCurrentTaskHandle();
if (_lockedBy != currentTask) {
xSemaphoreTake(_lock, portMAX_DELAY);
_lockedBy = currentTask;
return true;
}
return false;
}
void unlock() const {
_lockedBy = NULL;
xSemaphoreGive(_lock);
}
};
#else
// This is the 8266 version of the Sync Lock which is currently unimplemented
class AsyncWebLock
{
public:
AsyncWebLock() {
}
~AsyncWebLock() {
}
bool lock() const {
return false;
}
void unlock() const {
}
};
// Same for AsyncPlainLock, for ESP8266 this is just the unimplemented version above.
using AsyncPlainLock = AsyncWebLock;
#endif
class AsyncWebLockGuard
{
private:
const AsyncWebLock *_lock;
public:
AsyncWebLockGuard(const AsyncWebLock &l) {
if (l.lock()) {
_lock = &l;
} else {
_lock = NULL;
}
}
~AsyncWebLockGuard() {
if (_lock) {
_lock->unlock();
}
}
void unlock() {
if (_lock) {
_lock->unlock();
_lock = NULL;
}
}
};
#endif // ASYNCWEBSYNCHRONIZATION_H_

View File

@ -23,27 +23,30 @@
#include "Arduino.h" #include "Arduino.h"
#include "FS.h"
#include <functional> #include <functional>
#include <list> #include <list>
#include <vector> #include <vector>
#include "FS.h"
#include "StringArray.h"
#ifdef ESP32 #ifdef ESP32
#include <WiFi.h>
#include <AsyncTCP.h> #include <AsyncTCP.h>
#include <WiFi.h>
#elif defined(ESP8266) #elif defined(ESP8266)
#include <ESP8266WiFi.h> #include <ESP8266WiFi.h>
#include <ESPAsyncTCP.h> #include <ESPAsyncTCP.h>
#elif defined(TARGET_RP2040)
#include <WiFi.h>
#include <AsyncTCP_RP2040W.h>
#include <http_parser.h>
#include <HTTP_Method.h>
#else #else
#error Platform not supported #error Platform not supported
#endif #endif
#define ASYNCWEBSERVER_VERSION "2.10.8" #define ASYNCWEBSERVER_VERSION "3.0.6"
#define ASYNCWEBSERVER_VERSION_MAJOR 2 #define ASYNCWEBSERVER_VERSION_MAJOR 3
#define ASYNCWEBSERVER_VERSION_MINOR 10 #define ASYNCWEBSERVER_VERSION_MINOR 0
#define ASYNCWEBSERVER_VERSION_REVISION 8 #define ASYNCWEBSERVER_VERSION_REVISION 6
#define ASYNCWEBSERVER_FORK_mathieucarbou #define ASYNCWEBSERVER_FORK_mathieucarbou
#ifdef ASYNCWEBSERVER_REGEX #ifdef ASYNCWEBSERVER_REGEX
@ -63,6 +66,9 @@ class AsyncStaticWebHandler;
class AsyncCallbackWebHandler; class AsyncCallbackWebHandler;
class AsyncResponseStream; class AsyncResponseStream;
#if defined (TARGET_RP2040)
typedef enum http_method WebRequestMethod;
#else
#ifndef WEBSERVER_H #ifndef WEBSERVER_H
typedef enum { typedef enum {
HTTP_GET = 0b00000001, HTTP_GET = 0b00000001,
@ -75,6 +81,7 @@ typedef enum {
HTTP_ANY = 0b01111111, HTTP_ANY = 0b01111111,
} WebRequestMethod; } WebRequestMethod;
#endif #endif
#endif
#ifndef HAVE_FS_FILE_OPEN_MODE #ifndef HAVE_FS_FILE_OPEN_MODE
namespace fs { namespace fs {
@ -108,7 +115,6 @@ class AsyncWebParameter {
bool _isFile; bool _isFile;
public: public:
AsyncWebParameter(const String& name, const String& value, bool form = false, bool file = false, size_t size = 0) : _name(name), _value(value), _size(size), _isForm(form), _isFile(file) {} AsyncWebParameter(const String& name, const String& value, bool form = false, bool file = false, size_t size = 0) : _name(name), _value(value), _size(size), _isForm(form), _isFile(file) {}
const String& name() const { return _name; } const String& name() const { return _name; }
const String& value() const { return _value; } const String& value() const { return _value; }
@ -131,10 +137,12 @@ class AsyncWebHeader {
AsyncWebHeader(const AsyncWebHeader&) = default; AsyncWebHeader(const AsyncWebHeader&) = default;
AsyncWebHeader(const String& name, const String& value) : _name(name), _value(value) {} AsyncWebHeader(const String& name, const String& value) : _name(name), _value(value) {}
AsyncWebHeader(const String& data): _name(), _value(){ AsyncWebHeader(const String& data) {
if(!data) return; if (!data)
return;
int index = data.indexOf(':'); int index = data.indexOf(':');
if (index < 0) return; if (index < 0)
return;
_name = data.substring(0, index); _name = data.substring(0, index);
_value = data.substring(index + 2); _value = data.substring(index + 2);
} }
@ -150,7 +158,12 @@ class AsyncWebHeader {
* REQUEST :: Each incoming Client is wrapped inside a Request and both live together until disconnect * REQUEST :: Each incoming Client is wrapped inside a Request and both live together until disconnect
* */ * */
typedef enum { RCT_NOT_USED = -1, RCT_DEFAULT = 0, RCT_HTTP, RCT_WS, RCT_EVENT, RCT_MAX } RequestedConnectionType; typedef enum { RCT_NOT_USED = -1,
RCT_DEFAULT = 0,
RCT_HTTP,
RCT_WS,
RCT_EVENT,
RCT_MAX } RequestedConnectionType;
typedef std::function<size_t(uint8_t*, size_t, size_t)> AwsResponseFiller; typedef std::function<size_t(uint8_t*, size_t, size_t)> AwsResponseFiller;
typedef std::function<String(const String&)> AwsTemplateProcessor; typedef std::function<String(const String&)> AwsTemplateProcessor;
@ -160,6 +173,7 @@ class AsyncWebServerRequest {
using FS = fs::FS; using FS = fs::FS;
friend class AsyncWebServer; friend class AsyncWebServer;
friend class AsyncCallbackWebHandler; friend class AsyncCallbackWebHandler;
private: private:
AsyncClient* _client; AsyncClient* _client;
AsyncWebServer* _server; AsyncWebServer* _server;
@ -188,7 +202,7 @@ class AsyncWebServerRequest {
size_t _parsedLength; size_t _parsedLength;
std::list<AsyncWebHeader> _headers; std::list<AsyncWebHeader> _headers;
LinkedList<AsyncWebParameter *> _params; std::list<AsyncWebParameter> _params;
std::vector<String> _pathParams; std::vector<String> _pathParams;
uint8_t _multiParseState; uint8_t _multiParseState;
@ -210,7 +224,6 @@ class AsyncWebServerRequest {
void _onDisconnect(); void _onDisconnect();
void _onData(void* buf, size_t len); void _onData(void* buf, size_t len);
void _addParam(AsyncWebParameter*);
void _addPathParam(const char* param); void _addPathParam(const char* param);
bool _parseReqHead(); bool _parseReqHead();
@ -239,8 +252,15 @@ class AsyncWebServerRequest {
const String& contentType() const { return _contentType; } const String& contentType() const { return _contentType; }
size_t contentLength() const { return _contentLength; } size_t contentLength() const { return _contentLength; }
bool multipart() const { return _isMultipart; } bool multipart() const { return _isMultipart; }
#ifndef ESP8266
const char* methodToString() const;
const char* requestedConnTypeToString() const;
#else
const __FlashStringHelper* methodToString() const; const __FlashStringHelper* methodToString() const;
const __FlashStringHelper* requestedConnTypeToString() const; const __FlashStringHelper* requestedConnTypeToString() const;
#endif
RequestedConnectionType requestedConnType() const { return _reqconntype; } RequestedConnectionType requestedConnType() const { return _reqconntype; }
bool isExpectedRequestedConnType(RequestedConnectionType erct1, RequestedConnectionType erct2 = RCT_NOT_USED, RequestedConnectionType erct3 = RCT_NOT_USED); bool isExpectedRequestedConnType(RequestedConnectionType erct1, RequestedConnectionType erct2 = RCT_NOT_USED, RequestedConnectionType erct3 = RCT_NOT_USED);
void onDisconnect(ArDisconnectHandler fn); void onDisconnect(ArDisconnectHandler fn);
@ -253,63 +273,135 @@ class AsyncWebServerRequest {
void requestAuthentication(const char* realm = NULL, bool isDigest = true); void requestAuthentication(const char* realm = NULL, bool isDigest = true);
void setHandler(AsyncWebHandler* handler) { _handler = handler; } void setHandler(AsyncWebHandler* handler) { _handler = handler; }
void addInterestingHeader(const String& name);
void redirect(const String& url); /**
* @brief add header to collect from a response
*
* @param name
*/
void addInterestingHeader(const char* name);
void addInterestingHeader(const String& name) { return addInterestingHeader(name.c_str()); };
/**
* @brief issue 302 redirect response
*
* @param url
*/
void redirect(const char* url);
void redirect(const String& url) { return redirect(url.c_str()); };
void send(AsyncWebServerResponse* response); void send(AsyncWebServerResponse* response);
void send(int code, const String& contentType = String(), const String& content = String()); void send(int code, const String& contentType = String(), const String& content = String());
void send(int code, const String& contentType, const uint8_t* content, size_t len, AwsTemplateProcessor callback = nullptr);
void send(int code, const String& contentType, PGM_P content, AwsTemplateProcessor callback = nullptr);
void send(FS& fs, const String& path, const String& contentType = String(), bool download = false, AwsTemplateProcessor callback = nullptr); void send(FS& fs, const String& path, const String& contentType = String(), bool download = false, AwsTemplateProcessor callback = nullptr);
void send(File content, const String& path, const String& contentType = String(), bool download = false, AwsTemplateProcessor callback = nullptr); void send(File content, const String& path, const String& contentType = String(), bool download = false, AwsTemplateProcessor callback = nullptr);
void send(Stream& stream, const String& contentType, size_t len, AwsTemplateProcessor callback = nullptr); void send(Stream& stream, const String& contentType, size_t len, AwsTemplateProcessor callback = nullptr);
void send(const String& contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback = nullptr); void send(const String& contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback = nullptr);
void sendChunked(const String& contentType, AwsResponseFiller callback, AwsTemplateProcessor templateCallback = nullptr); void sendChunked(const String& contentType, AwsResponseFiller callback, AwsTemplateProcessor templateCallback = nullptr);
void send_P(int code, const String& contentType, const uint8_t * content, size_t len, AwsTemplateProcessor callback=nullptr);
void send_P(int code, const String& contentType, PGM_P content, AwsTemplateProcessor callback=nullptr); [[deprecated("Replaced by send(...)")]]
void send_P(int code, const String& contentType, const uint8_t* content, size_t len, AwsTemplateProcessor callback = nullptr) {
send(code, contentType, content, len, callback);
}
[[deprecated("Replaced by send(...)")]]
void send_P(int code, const String& contentType, PGM_P content, AwsTemplateProcessor callback = nullptr) {
send(code, contentType, content, callback);
}
AsyncWebServerResponse* beginResponse(int code, const String& contentType = String(), const String& content = String()); AsyncWebServerResponse* beginResponse(int code, const String& contentType = String(), const String& content = String());
AsyncWebServerResponse* beginResponse(int code, const String& contentType, const uint8_t* content, size_t len, AwsTemplateProcessor callback = nullptr);
AsyncWebServerResponse* beginResponse(int code, const String& contentType, PGM_P content, AwsTemplateProcessor callback = nullptr);
AsyncWebServerResponse* beginResponse(FS& fs, const String& path, const String& contentType = String(), bool download = false, AwsTemplateProcessor callback = nullptr); AsyncWebServerResponse* beginResponse(FS& fs, const String& path, const String& contentType = String(), bool download = false, AwsTemplateProcessor callback = nullptr);
AsyncWebServerResponse* beginResponse(File content, const String& path, const String& contentType = String(), bool download = false, AwsTemplateProcessor callback = nullptr); AsyncWebServerResponse* beginResponse(File content, const String& path, const String& contentType = String(), bool download = false, AwsTemplateProcessor callback = nullptr);
AsyncWebServerResponse* beginResponse(Stream& stream, const String& contentType, size_t len, AwsTemplateProcessor callback = nullptr); AsyncWebServerResponse* beginResponse(Stream& stream, const String& contentType, size_t len, AwsTemplateProcessor callback = nullptr);
AsyncWebServerResponse* beginResponse(const String& contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback = nullptr); AsyncWebServerResponse* beginResponse(const String& contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback = nullptr);
AsyncWebServerResponse* beginChunkedResponse(const String& contentType, AwsResponseFiller callback, AwsTemplateProcessor templateCallback = nullptr); AsyncWebServerResponse* beginChunkedResponse(const String& contentType, AwsResponseFiller callback, AwsTemplateProcessor templateCallback = nullptr);
AsyncResponseStream* beginResponseStream(const String& contentType, size_t bufferSize = 1460); AsyncResponseStream* beginResponseStream(const String& contentType, size_t bufferSize = 1460);
AsyncWebServerResponse *beginResponse_P(int code, const String& contentType, const uint8_t * content, size_t len, AwsTemplateProcessor callback=nullptr);
AsyncWebServerResponse *beginResponse_P(int code, const String& contentType, PGM_P content, AwsTemplateProcessor callback=nullptr);
[[deprecated("Replaced by beginResponse(...)")]]
AsyncWebServerResponse* beginResponse_P(int code, const String& contentType, const uint8_t* content, size_t len, AwsTemplateProcessor callback = nullptr) {
return beginResponse(code, contentType, content, len, callback);
}
[[deprecated("Replaced by beginResponse(...)")]]
AsyncWebServerResponse* beginResponse_P(int code, const String& contentType, PGM_P content, AwsTemplateProcessor callback = nullptr) {
return beginResponse(code, contentType, content, callback);
}
size_t headers() const; // get header count size_t headers() const; // get header count
bool hasHeader(const String& name) const; // check if header exists
bool hasHeader(const __FlashStringHelper * data) const; // check if header exists
AsyncWebHeader* getHeader(const String& name); // check if header exists
const AsyncWebHeader* getHeader(const String& name) const; bool hasHeader(const char* name) const;
AsyncWebHeader* getHeader(const __FlashStringHelper * data); bool hasHeader(const String& name) const { return hasHeader(name.c_str()); };
#ifdef ESP8266
bool hasHeader(const __FlashStringHelper* data) const; // check if header exists
#endif
const AsyncWebHeader* getHeader(const char* name) const;
const AsyncWebHeader* getHeader(const String& name) const { return getHeader(name.c_str()); };
#ifdef ESP8266
const AsyncWebHeader* getHeader(const __FlashStringHelper* data) const; const AsyncWebHeader* getHeader(const __FlashStringHelper* data) const;
AsyncWebHeader* getHeader(size_t num); #endif
const AsyncWebHeader* getHeader(size_t num) const; const AsyncWebHeader* getHeader(size_t num) const;
size_t params() const; // get arguments count size_t params() const; // get arguments count
bool hasParam(const String& name, bool post = false, bool file = false) const; bool hasParam(const String& name, bool post = false, bool file = false) const;
bool hasParam(const __FlashStringHelper* data, bool post = false, bool file = false) const; bool hasParam(const __FlashStringHelper* data, bool post = false, bool file = false) const;
AsyncWebParameter* getParam(const String& name, bool post=false, bool file=false) const; /**
AsyncWebParameter* getParam(const __FlashStringHelper * data, bool post, bool file) const; * @brief Get the Request parameter by name
AsyncWebParameter* getParam(size_t num) const; *
* @param name
* @param post
* @param file
* @return const AsyncWebParameter*
*/
const AsyncWebParameter* getParam(const char* name, bool post = false, bool file = false) const;
const AsyncWebParameter* getParam(const String& name, bool post = false, bool file = false) const { return getParam(name.c_str(), post, file); };
#ifdef ESP8266
const AsyncWebParameter* getParam(const __FlashStringHelper* data, bool post, bool file) const;
#endif
/**
* @brief Get request parameter by number
* i.e., n-th parameter
* @param num
* @return const AsyncWebParameter*
*/
const AsyncWebParameter* getParam(size_t num) const;
size_t args() const { return params(); } // get arguments count size_t args() const { return params(); } // get arguments count
const String& arg(const String& name) const; // get request argument value by name
// get request argument value by name
const String& arg(const char* name) const;
// get request argument value by name
const String& arg(const String& name) const { return arg(name.c_str()); };
#ifdef ESP8266
const String& arg(const __FlashStringHelper* data) const; // get request argument value by F(name) const String& arg(const __FlashStringHelper* data) const; // get request argument value by F(name)
#endif
const String& arg(size_t i) const; // get request argument value by number const String& arg(size_t i) const; // get request argument value by number
const String& argName(size_t i) const; // get request argument name by number const String& argName(size_t i) const; // get request argument name by number
bool hasArg(const char* name) const; // check if argument exists bool hasArg(const char* name) const; // check if argument exists
bool hasArg(const String& name) const { return hasArg(name.c_str()); };
#ifdef ESP8266
bool hasArg(const __FlashStringHelper* data) const; // check if F(argument) exists bool hasArg(const __FlashStringHelper* data) const; // check if F(argument) exists
#endif
const String& ASYNCWEBSERVER_REGEX_ATTRIBUTE pathArg(size_t i) const; const String& ASYNCWEBSERVER_REGEX_ATTRIBUTE pathArg(size_t i) const;
const String& header(const char* name) const;// get request header value by name // get request header value by name
const String& header(const char* name) const;
const String& header(const String& name) const { return header(name.c_str()); };
#ifdef ESP8266
const String& header(const __FlashStringHelper* data) const; // get request header value by F(name) const String& header(const __FlashStringHelper* data) const; // get request header value by F(name)
#endif
const String& header(size_t i) const; // get request header value by number const String& header(size_t i) const; // get request header value by number
const String& headerName(size_t i) const; // get request header name by number const String& headerName(size_t i) const; // get request header name by number
String urlDecode(const String& text) const; String urlDecode(const String& text) const;
}; };
@ -317,7 +409,7 @@ class AsyncWebServerRequest {
* FILTER :: Callback to filter AsyncWebRewrite and AsyncWebHandler (done by the Server) * FILTER :: Callback to filter AsyncWebRewrite and AsyncWebHandler (done by the Server)
* */ * */
typedef std::function<bool(AsyncWebServerRequest *request)> ArRequestFilterFunction; using ArRequestFilterFunction = std::function<bool(AsyncWebServerRequest* request)>;
bool ON_STA_FILTER(AsyncWebServerRequest* request); bool ON_STA_FILTER(AsyncWebServerRequest* request);
@ -332,9 +424,10 @@ class AsyncWebRewrite {
String _from; String _from;
String _toUrl; String _toUrl;
String _params; String _params;
ArRequestFilterFunction _filter; ArRequestFilterFunction _filter{nullptr};
public: public:
AsyncWebRewrite(const char* from, const char* to): _from(from), _toUrl(to), _params(String()), _filter(NULL){ AsyncWebRewrite(const char* from, const char* to) : _from(from), _toUrl(to) {
int index = _toUrl.indexOf('?'); int index = _toUrl.indexOf('?');
if (index > 0) { if (index > 0) {
_params = _toUrl.substring(index + 1); _params = _toUrl.substring(index + 1);
@ -342,7 +435,10 @@ class AsyncWebRewrite {
} }
} }
virtual ~AsyncWebRewrite() {} virtual ~AsyncWebRewrite() {}
AsyncWebRewrite& setFilter(ArRequestFilterFunction fn) { _filter = fn; return *this; } AsyncWebRewrite& setFilter(ArRequestFilterFunction fn) {
_filter = fn;
return *this;
}
bool filter(AsyncWebServerRequest* request) const { return _filter == NULL || _filter(request); } bool filter(AsyncWebServerRequest* request) const { return _filter == NULL || _filter(request); }
const String& from(void) const { return _from; } const String& from(void) const { return _from; }
const String& toUrl(void) const { return _toUrl; } const String& toUrl(void) const { return _toUrl; }
@ -356,14 +452,26 @@ class AsyncWebRewrite {
class AsyncWebHandler { class AsyncWebHandler {
protected: protected:
ArRequestFilterFunction _filter; ArRequestFilterFunction _filter{nullptr};
String _username; String _username;
String _password; String _password;
public: public:
AsyncWebHandler():_username(""), _password(""){} AsyncWebHandler() {}
AsyncWebHandler& setFilter(ArRequestFilterFunction fn) { _filter = fn; return *this; } AsyncWebHandler& setFilter(ArRequestFilterFunction fn) {
AsyncWebHandler& setAuthentication(const char *username, const char *password){ _username = String(username);_password = String(password); return *this; }; _filter = fn;
AsyncWebHandler& setAuthentication(const String& username, const String& password){ _username = username;_password = password; return *this; }; return *this;
}
AsyncWebHandler& setAuthentication(const char* username, const char* password) {
_username = username;
_password = password;
return *this;
};
AsyncWebHandler& setAuthentication(const String& username, const String& password) {
_username = username;
_password = password;
return *this;
};
bool filter(AsyncWebServerRequest* request) { return _filter == NULL || _filter(request); } bool filter(AsyncWebServerRequest* request) { return _filter == NULL || _filter(request); }
virtual ~AsyncWebHandler() {} virtual ~AsyncWebHandler() {}
virtual bool canHandle(AsyncWebServerRequest* request __attribute__((unused))) { virtual bool canHandle(AsyncWebServerRequest* request __attribute__((unused))) {
@ -380,7 +488,12 @@ class AsyncWebHandler {
* */ * */
typedef enum { typedef enum {
RESPONSE_SETUP, RESPONSE_HEADERS, RESPONSE_CONTENT, RESPONSE_WAIT_ACK, RESPONSE_END, RESPONSE_FAILED RESPONSE_SETUP,
RESPONSE_HEADERS,
RESPONSE_CONTENT,
RESPONSE_WAIT_ACK,
RESPONSE_END,
RESPONSE_FAILED
} WebResponseState; } WebResponseState;
class AsyncWebServerResponse { class AsyncWebServerResponse {
@ -397,6 +510,7 @@ class AsyncWebServerResponse {
size_t _writtenLength; size_t _writtenLength;
WebResponseState _state; WebResponseState _state;
const char* _responseCodeToString(int code); const char* _responseCodeToString(int code);
public: public:
static const __FlashStringHelper* responseCodeToString(int code); static const __FlashStringHelper* responseCodeToString(int code);
@ -427,8 +541,8 @@ typedef std::function<void(AsyncWebServerRequest *request, uint8_t *data, size_t
class AsyncWebServer { class AsyncWebServer {
protected: protected:
AsyncServer _server; AsyncServer _server;
LinkedList<AsyncWebRewrite*> _rewrites; std::list<std::shared_ptr<AsyncWebRewrite>> _rewrites;
LinkedList<AsyncWebHandler*> _handlers; std::list<std::unique_ptr<AsyncWebHandler>> _handlers;
AsyncCallbackWebHandler* _catchAllHandler; AsyncCallbackWebHandler* _catchAllHandler;
public: public:
@ -444,9 +558,46 @@ class AsyncWebServer {
#endif #endif
AsyncWebRewrite& addRewrite(AsyncWebRewrite* rewrite); AsyncWebRewrite& addRewrite(AsyncWebRewrite* rewrite);
bool removeRewrite(AsyncWebRewrite* rewrite);
/**
* @brief (compat) Add url rewrite rule by pointer
* a deep copy of the pounter object will be created,
* it is up to user to manage further lifetime of the object in argument
*
* @param rewrite pointer to rewrite object to copy setting from
* @return AsyncWebRewrite& reference to a newly created rewrite rule
*/
AsyncWebRewrite& addRewrite(std::shared_ptr<AsyncWebRewrite> rewrite);
/**
* @brief add url rewrite rule
*
* @param from
* @param to
* @return AsyncWebRewrite&
*/
AsyncWebRewrite& rewrite(const char* from, const char* to); AsyncWebRewrite& rewrite(const char* from, const char* to);
/**
* @brief (compat) remove rewrite rule via referenced object
* this will NOT deallocate pointed object itself, internal rule with same from/to urls will be removed if any
* it's a compat method, better use `removeRewrite(const char* from, const char* to)`
* @param rewrite
* @return true
* @return false
*/
bool removeRewrite(AsyncWebRewrite* rewrite);
/**
* @brief remove rewrite rule
*
* @param from
* @param to
* @return true
* @return false
*/
bool removeRewrite(const char* from, const char* to);
AsyncWebHandler& addHandler(AsyncWebHandler* handler); AsyncWebHandler& addHandler(AsyncWebHandler* handler);
bool removeHandler(AsyncWebHandler* handler); bool removeHandler(AsyncWebHandler* handler);
@ -493,9 +644,9 @@ public:
} }
}; };
#include "WebResponseImpl.h"
#include "WebHandlerImpl.h"
#include "AsyncWebSocket.h"
#include "AsyncEventSource.h" #include "AsyncEventSource.h"
#include "AsyncWebSocket.h"
#include "WebHandlerImpl.h"
#include "WebResponseImpl.h"
#endif /* _AsyncWebServer_H_ */ #endif /* _AsyncWebServer_H_ */

View File

@ -1,174 +0,0 @@
/*
Asynchronous WebServer library for Espressif MCUs
Copyright (c) 2016 Hristo Gochkov. All rights reserved.
This file is part of the esp8266 core for Arduino environment.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef STRINGARRAY_H_
#define STRINGARRAY_H_
#include "stddef.h"
#include "WString.h"
template <typename T>
class LinkedListNode {
T _value;
public:
LinkedListNode<T>* next;
LinkedListNode(const T val): _value(val), next(nullptr) {}
~LinkedListNode(){}
const T& value() const { return _value; };
T& value(){ return _value; }
};
template <typename T, template<typename> class Item = LinkedListNode>
class LinkedList {
public:
typedef Item<T> ItemType;
typedef std::function<void(const T&)> OnRemove;
typedef std::function<bool(const T&)> Predicate;
private:
ItemType* _root;
OnRemove _onRemove;
class Iterator {
ItemType* _node;
public:
Iterator(ItemType* current = nullptr) : _node(current) {}
Iterator(const Iterator& i) : _node(i._node) {}
Iterator& operator ++() { _node = _node->next; return *this; }
bool operator != (const Iterator& i) const { return _node != i._node; }
const T& operator * () const { return _node->value(); }
const T* operator -> () const { return &_node->value(); }
};
public:
typedef const Iterator ConstIterator;
ConstIterator begin() const { return ConstIterator(_root); }
ConstIterator end() const { return ConstIterator(nullptr); }
LinkedList(OnRemove onRemove) : _root(nullptr), _onRemove(onRemove) {}
~LinkedList(){}
void add(const T& t){
auto it = new ItemType(t);
if(!_root){
_root = it;
} else {
auto i = _root;
while(i->next) i = i->next;
i->next = it;
}
}
T& front() const {
return _root->value();
}
bool isEmpty() const {
return _root == nullptr;
}
size_t length() const {
size_t i = 0;
auto it = _root;
while(it){
i++;
it = it->next;
}
return i;
}
size_t count_if(Predicate predicate) const {
size_t i = 0;
auto it = _root;
while(it){
if (!predicate){
i++;
}
else if (predicate(it->value())) {
i++;
}
it = it->next;
}
return i;
}
const T* nth(size_t N) const {
size_t i = 0;
auto it = _root;
while(it){
if(i++ == N)
return &(it->value());
it = it->next;
}
return nullptr;
}
bool remove(const T& t){
auto it = _root;
auto pit = _root;
while(it){
if(it->value() == t){
if(it == _root){
_root = _root->next;
} else {
pit->next = it->next;
}
if (_onRemove) {
_onRemove(it->value());
}
delete it;
return true;
}
pit = it;
it = it->next;
}
return false;
}
bool remove_first(Predicate predicate){
auto it = _root;
auto pit = _root;
while(it){
if(predicate(it->value())){
if(it == _root){
_root = _root->next;
} else {
pit->next = it->next;
}
if (_onRemove) {
_onRemove(it->value());
}
delete it;
return true;
}
pit = it;
it = it->next;
}
return false;
}
void free(){
while(_root != nullptr){
auto it = _root;
_root = _root->next;
if (_onRemove) {
_onRemove(it->value());
}
delete it;
}
_root = nullptr;
}
};
#endif /* STRINGARRAY_H_ */

View File

@ -20,13 +20,12 @@
*/ */
#include "WebAuthentication.h" #include "WebAuthentication.h"
#include <libb64/cencode.h> #include <libb64/cencode.h>
#ifdef ESP32 #if defined(ESP32) || defined(TARGET_RP2040)
#include <MD5Builder.h> #include <MD5Builder.h>
#else #else
#include "md5.h" #include "md5.h"
#endif #endif
// Basic Auth hash = base64("username:password") // Basic Auth hash = base64("username:password")
bool checkBasicAuthentication(const char* hash, const char* username, const char* password) { bool checkBasicAuthentication(const char* hash, const char* username, const char* password) {
@ -65,7 +64,7 @@ bool checkBasicAuthentication(const char * hash, const char * username, const ch
} }
static bool getMD5(uint8_t* data, uint16_t len, char* output) { // 33 bytes or more static bool getMD5(uint8_t* data, uint16_t len, char* output) { // 33 bytes or more
#ifdef ESP32 #if defined(ESP32) || defined(TARGET_RP2040)
MD5Builder md5; MD5Builder md5;
md5.begin(); md5.begin();
md5.add(data, len); md5.add(data, len);
@ -147,31 +146,36 @@ String requestDigestAuthentication(const char * realm){
return header; return header;
} }
bool checkDigestAuthentication(const char * header, const __FlashStringHelper *method, const char * username, const char * password, const char * realm, bool passwordIsHash, const char * nonce, const char * opaque, const char * uri){ #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) { if (username == NULL || password == NULL || header == NULL || method == NULL) {
// os_printf("AUTH FAIL: missing requred fields\n"); // os_printf("AUTH FAIL: missing requred fields\n");
return false; return false;
} }
String myHeader = String(header); String myHeader(header);
int nextBreak = myHeader.indexOf(','); int nextBreak = myHeader.indexOf(',');
if (nextBreak < 0) { if (nextBreak < 0) {
// os_printf("AUTH FAIL: no variables\n"); // os_printf("AUTH FAIL: no variables\n");
return false; return false;
} }
String myUsername = String(); String myUsername;
String myRealm = String(); String myRealm;
String myNonce = String(); String myNonce;
String myUri = String(); String myUri;
String myResponse = String(); String myResponse;
String myQop = String(); String myQop;
String myNc = String(); String myNc;
String myCnonce = String(); String myCnonce;
myHeader += F(", "); myHeader += F(", ");
do { do {
String avLine = myHeader.substring(0, nextBreak); String avLine(myHeader.substring(0, nextBreak));
avLine.trim(); avLine.trim();
myHeader = myHeader.substring(nextBreak + 1); myHeader = myHeader.substring(nextBreak + 1);
nextBreak = myHeader.indexOf(','); nextBreak = myHeader.indexOf(',');
@ -181,7 +185,7 @@ bool checkDigestAuthentication(const char * header, const __FlashStringHelper *m
// os_printf("AUTH FAIL: no = sign\n"); // os_printf("AUTH FAIL: no = sign\n");
return false; return false;
} }
String varName = avLine.substring(0, eqSign); String varName(avLine.substring(0, eqSign));
avLine = avLine.substring(eqSign + 1); avLine = avLine.substring(eqSign + 1);
if (avLine.startsWith(String('"'))) { if (avLine.startsWith(String('"'))) {
avLine = avLine.substring(1, avLine.length() - 1); avLine = avLine.substring(1, avLine.length() - 1);
@ -227,7 +231,7 @@ bool checkDigestAuthentication(const char * header, const __FlashStringHelper *m
} }
} while (nextBreak > 0); } while (nextBreak > 0);
String ha1 = (passwordIsHash) ? String(password) : stringMD5(myUsername + ':' + myRealm + ':' + String(password)); String ha1 = (passwordIsHash) ? String(password) : stringMD5(myUsername + ':' + myRealm + ':' + password);
String ha2 = String(method) + ':' + myUri; String ha2 = String(method) + ':' + myUri;
String response = ha1 + ':' + myNonce + ':' + myNc + ':' + myCnonce + ':' + myQop + ':' + stringMD5(ha2); String response = ha1 + ':' + myNonce + ':' + myNc + ':' + myCnonce + ':' + myQop + ':' + stringMD5(ha2);

View File

@ -26,7 +26,12 @@
bool checkBasicAuthentication(const char* header, const char* username, const char* password); bool checkBasicAuthentication(const char* header, const char* username, const char* password);
String requestDigestAuthentication(const char* realm); String requestDigestAuthentication(const char* realm);
bool checkDigestAuthentication(const char* header, const char* method, const char* username, const char* password, const char* realm, bool passwordIsHash, const char* nonce, const char* opaque, const char* uri);
#ifdef ESP8266
bool checkDigestAuthentication(const char* header, const __FlashStringHelper* method, const char* username, const char* password, const char* realm, bool passwordIsHash, const char* nonce, const char* opaque, const char* uri); bool checkDigestAuthentication(const char* header, const __FlashStringHelper* method, const char* username, const char* password, const char* realm, bool passwordIsHash, const char* nonce, const char* opaque, const char* uri);
#endif
// for storing hashed versions on the device that can be authenticated against // for storing hashed versions on the device that can be authenticated against
String generateDigestHash(const char* username, const char* password, const char* realm); String generateDigestHash(const char* username, const char* password, const char* realm);

View File

@ -32,10 +32,12 @@
class AsyncStaticWebHandler : public AsyncWebHandler { class AsyncStaticWebHandler : public AsyncWebHandler {
using File = fs::File; using File = fs::File;
using FS = fs::FS; using FS = fs::FS;
private: private:
bool _getFile(AsyncWebServerRequest* request); bool _getFile(AsyncWebServerRequest* request);
bool _fileExists(AsyncWebServerRequest* request, const String& path); bool _fileExists(AsyncWebServerRequest* request, const String& path);
uint8_t _countBits(const uint8_t value) const; uint8_t _countBits(const uint8_t value) const;
protected: protected:
FS _fs; FS _fs;
String _uri; String _uri;
@ -47,6 +49,7 @@ class AsyncStaticWebHandler: public AsyncWebHandler {
bool _isDir; bool _isDir;
bool _gzipFirst; bool _gzipFirst;
uint8_t _gzipStats; uint8_t _gzipStats;
public: public:
AsyncStaticWebHandler(const char* uri, FS& fs, const char* path, const char* cache_control); AsyncStaticWebHandler(const char* uri, FS& fs, const char* path, const char* cache_control);
virtual bool canHandle(AsyncWebServerRequest* request) override final; virtual bool canHandle(AsyncWebServerRequest* request) override final;
@ -60,7 +63,10 @@ class AsyncStaticWebHandler: public AsyncWebHandler {
AsyncStaticWebHandler& setLastModified(time_t last_modified); AsyncStaticWebHandler& setLastModified(time_t last_modified);
AsyncStaticWebHandler& setLastModified(); // sets to current time. Make sure sntp is runing and time is updated AsyncStaticWebHandler& setLastModified(); // sets to current time. Make sure sntp is runing and time is updated
#endif #endif
AsyncStaticWebHandler& setTemplateProcessor(AwsTemplateProcessor newCallback) {_callback = newCallback; return *this;} AsyncStaticWebHandler& setTemplateProcessor(AwsTemplateProcessor newCallback) {
_callback = newCallback;
return *this;
}
}; };
class AsyncCallbackWebHandler : public AsyncWebHandler { class AsyncCallbackWebHandler : public AsyncWebHandler {
@ -72,6 +78,7 @@ class AsyncCallbackWebHandler: public AsyncWebHandler {
ArUploadHandlerFunction _onUpload; ArUploadHandlerFunction _onUpload;
ArBodyHandlerFunction _onBody; ArBodyHandlerFunction _onBody;
bool _isRegex; bool _isRegex;
public: public:
AsyncCallbackWebHandler() : _uri(), _method(HTTP_ANY), _onRequest(NULL), _onUpload(NULL), _onBody(NULL), _isRegex(false) {} AsyncCallbackWebHandler() : _uri(), _method(HTTP_ANY), _onRequest(NULL), _onUpload(NULL), _onBody(NULL), _isRegex(false) {}
void setUri(const String& uri) { void setUri(const String& uri) {
@ -110,15 +117,12 @@ class AsyncCallbackWebHandler: public AsyncWebHandler {
uriTemplate = uriTemplate.substring(uriTemplate.lastIndexOf(".")); uriTemplate = uriTemplate.substring(uriTemplate.lastIndexOf("."));
if (!request->url().endsWith(uriTemplate)) if (!request->url().endsWith(uriTemplate))
return false; return false;
} } else if (_uri.length() && _uri.endsWith("*")) {
else
if (_uri.length() && _uri.endsWith("*")) {
String uriTemplate = String(_uri); String uriTemplate = String(_uri);
uriTemplate = uriTemplate.substring(0, uriTemplate.length() - 1); uriTemplate = uriTemplate.substring(0, uriTemplate.length() - 1);
if (!request->url().startsWith(uriTemplate)) if (!request->url().startsWith(uriTemplate))
return false; return false;
} } else if (_uri.length() && (_uri != request->url() && !request->url().startsWith(_uri + "/")))
else if(_uri.length() && (_uri != request->url() && !request->url().startsWith(_uri+"/")))
return false; return false;
request->addInterestingHeader("ANY"); request->addInterestingHeader("ANY");

View File

@ -22,11 +22,12 @@
#include "WebHandlerImpl.h" #include "WebHandlerImpl.h"
AsyncStaticWebHandler::AsyncStaticWebHandler(const char* uri, FS& fs, const char* path, const char* cache_control) AsyncStaticWebHandler::AsyncStaticWebHandler(const char* uri, FS& fs, const char* path, const char* cache_control)
: _fs(fs), _uri(uri), _path(path), _default_file(F("index.htm")), _cache_control(cache_control), _last_modified(), _callback(nullptr) : _fs(fs), _uri(uri), _path(path), _default_file(F("index.htm")), _cache_control(cache_control), _last_modified(), _callback(nullptr) {
{
// Ensure leading '/' // Ensure leading '/'
if (_uri.length() == 0 || _uri[0] != '/') _uri = String('/') + _uri; if (_uri.length() == 0 || _uri[0] != '/')
if (_path.length() == 0 || _path[0] != '/') _path = String('/') + _path; _uri = String('/') + _uri;
if (_path.length() == 0 || _path[0] != '/')
_path = String('/') + _path;
// If path ends with '/' we assume a hint that this is a directory to improve performance. // If path ends with '/' we assume a hint that this is a directory to improve performance.
// However - if it does not end with '/' we, can't assume a file, path can still be a directory. // However - if it does not end with '/' we, can't assume a file, path can still be a directory.
@ -34,8 +35,10 @@ AsyncStaticWebHandler::AsyncStaticWebHandler(const char* uri, FS& fs, const char
// Remove the trailing '/' so we can handle default file // Remove the trailing '/' so we can handle default file
// Notice that root will be "" not "/" // Notice that root will be "" not "/"
if (_uri[_uri.length()-1] == '/') _uri = _uri.substring(0, _uri.length()-1); if (_uri[_uri.length() - 1] == '/')
if (_path[_path.length()-1] == '/') _path = _path.substring(0, _path.length()-1); _uri = _uri.substring(0, _uri.length() - 1);
if (_path[_path.length() - 1] == '/')
_path = _path.substring(0, _path.length() - 1);
// Reset stats // Reset stats
_gzipFirst = false; _gzipFirst = false;
@ -85,10 +88,7 @@ AsyncStaticWebHandler& AsyncStaticWebHandler::setLastModified(){
} }
#endif #endif
bool AsyncStaticWebHandler::canHandle(AsyncWebServerRequest* request) { bool AsyncStaticWebHandler::canHandle(AsyncWebServerRequest* request) {
if(request->method() != HTTP_GET if (request->method() != HTTP_GET || !request->url().startsWith(_uri) || !request->isExpectedRequestedConnType(RCT_DEFAULT, RCT_HTTP)) {
|| !request->url().startsWith(_uri)
|| !request->isExpectedRequestedConnType(RCT_DEFAULT, RCT_HTTP)
){
return false; return false;
} }
if (_getFile(request)) { if (_getFile(request)) {
@ -105,8 +105,7 @@ bool AsyncStaticWebHandler::canHandle(AsyncWebServerRequest *request){
return false; return false;
} }
bool AsyncStaticWebHandler::_getFile(AsyncWebServerRequest *request) bool AsyncStaticWebHandler::_getFile(AsyncWebServerRequest* request) {
{
// Remove the found uri // Remove the found uri
String path = request->url().substring(_uri.length()); String path = request->url().substring(_uri.length());
@ -137,8 +136,7 @@ bool AsyncStaticWebHandler::_getFile(AsyncWebServerRequest *request)
#define FILE_IS_REAL(f) (f == true) #define FILE_IS_REAL(f) (f == true)
#endif #endif
bool AsyncStaticWebHandler::_fileExists(AsyncWebServerRequest *request, const String& path) bool AsyncStaticWebHandler::_fileExists(AsyncWebServerRequest* request, const String& path) {
{
bool fileFound = false; bool fileFound = false;
bool gzipFound = false; bool gzipFound = false;
@ -179,24 +177,26 @@ bool AsyncStaticWebHandler::_fileExists(AsyncWebServerRequest *request, const St
// Calculate gzip statistic // Calculate gzip statistic
_gzipStats = (_gzipStats << 1) + (gzipFound ? 1 : 0); _gzipStats = (_gzipStats << 1) + (gzipFound ? 1 : 0);
if (_gzipStats == 0x00) _gzipFirst = false; // All files are not gzip if (_gzipStats == 0x00)
else if (_gzipStats == 0xFF) _gzipFirst = true; // All files are gzip _gzipFirst = false; // All files are not gzip
else _gzipFirst = _countBits(_gzipStats) > 4; // IF we have more gzip files - try gzip first else if (_gzipStats == 0xFF)
_gzipFirst = true; // All files are gzip
else
_gzipFirst = _countBits(_gzipStats) > 4; // IF we have more gzip files - try gzip first
} }
return found; return found;
} }
uint8_t AsyncStaticWebHandler::_countBits(const uint8_t value) const uint8_t AsyncStaticWebHandler::_countBits(const uint8_t value) const {
{
uint8_t w = value; uint8_t w = value;
uint8_t n; uint8_t n;
for (n=0; w!=0; n++) w&=w-1; for (n = 0; w != 0; n++)
w &= w - 1;
return n; return n;
} }
void AsyncStaticWebHandler::handleRequest(AsyncWebServerRequest *request) void AsyncStaticWebHandler::handleRequest(AsyncWebServerRequest* request) {
{
// Get the filename from request->_tempObject and free it // Get the filename from request->_tempObject and free it
String filename = String((char*)request->_tempObject); String filename = String((char*)request->_tempObject);
free(request->_tempObject); free(request->_tempObject);
@ -206,8 +206,22 @@ void AsyncStaticWebHandler::handleRequest(AsyncWebServerRequest *request)
if (request->_tempFile == true) { if (request->_tempFile == true) {
time_t lw = request->_tempFile.getLastWrite(); // get last file mod time (if supported by FS) time_t lw = request->_tempFile.getLastWrite(); // get last file mod time (if supported by FS)
if (lw) setLastModified(gmtime(&lw)); // set etag to lastmod timestamp if available, otherwise to size
String etag(lw ? lw : request->_tempFile.size()); // set etag to lastmod timestamp if available, otherwise to size String etag;
if (lw) {
setLastModified(gmtime(&lw));
#if defined(TARGET_RP2040)
// time_t == long long int
const size_t len = 1 + 8 * sizeof(time_t);
char buf[len];
char* ret = lltoa(lw, buf, len, 10);
etag = ret ? String(ret) : String(request->_tempFile.size());
#else
etag = String(lw);
#endif
} else {
etag = String(request->_tempFile.size());
}
if (_last_modified.length() && _last_modified == request->header(F("If-Modified-Since"))) { if (_last_modified.length() && _last_modified == request->header(F("If-Modified-Since"))) {
request->_tempFile.close(); request->_tempFile.close();
request->send(304); // Not modified request->send(304); // Not modified

View File

@ -19,8 +19,8 @@
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/ */
#include "ESPAsyncWebServer.h" #include "ESPAsyncWebServer.h"
#include "WebResponseImpl.h"
#include "WebAuthentication.h" #include "WebAuthentication.h"
#include "WebResponseImpl.h"
#ifndef ESP8266 #ifndef ESP8266
#define os_strlen strlen #define os_strlen strlen
@ -28,43 +28,14 @@
#define __is_param_char(c) ((c) && ((c) != '{') && ((c) != '[') && ((c) != '&') && ((c) != '=')) #define __is_param_char(c) ((c) && ((c) != '{') && ((c) != '[') && ((c) != '&') && ((c) != '='))
enum { PARSE_REQ_START, PARSE_REQ_HEADERS, PARSE_REQ_BODY, PARSE_REQ_END, PARSE_REQ_FAIL }; enum { PARSE_REQ_START,
PARSE_REQ_HEADERS,
PARSE_REQ_BODY,
PARSE_REQ_END,
PARSE_REQ_FAIL };
AsyncWebServerRequest::AsyncWebServerRequest(AsyncWebServer* s, AsyncClient* c) AsyncWebServerRequest::AsyncWebServerRequest(AsyncWebServer* s, AsyncClient* c)
: _client(c) : _client(c), _server(s), _handler(NULL), _response(NULL), _temp(), _parseState(0), _version(0), _method(HTTP_ANY), _url(), _host(), _contentType(), _boundary(), _authorization(), _reqconntype(RCT_HTTP), _isDigest(false), _isMultipart(false), _isPlainPost(false), _expectingContinue(false), _contentLength(0), _parsedLength(0), _multiParseState(0), _boundaryPosition(0), _itemStartIndex(0), _itemSize(0), _itemName(), _itemFilename(), _itemType(), _itemValue(), _itemBuffer(0), _itemBufferIndex(0), _itemIsFile(false), _tempObject(NULL) {
, _server(s)
, _handler(NULL)
, _response(NULL)
, _temp()
, _parseState(0)
, _version(0)
, _method(HTTP_ANY)
, _url()
, _host()
, _contentType()
, _boundary()
, _authorization()
, _reqconntype(RCT_HTTP)
, _isDigest(false)
, _isMultipart(false)
, _isPlainPost(false)
, _expectingContinue(false)
, _contentLength(0)
, _parsedLength(0)
, _params(LinkedList<AsyncWebParameter *>([](AsyncWebParameter *p){ delete p; }))
, _multiParseState(0)
, _boundaryPosition(0)
, _itemStartIndex(0)
, _itemSize(0)
, _itemName()
, _itemFilename()
, _itemType()
, _itemValue()
, _itemBuffer(0)
, _itemBufferIndex(0)
, _itemIsFile(false)
, _tempObject(NULL)
{
c->onError([](void* r, AsyncClient* c, int8_t error) { (void)c; AsyncWebServerRequest *req = (AsyncWebServerRequest*)r; req->_onError(error); }, this); c->onError([](void* r, AsyncClient* c, int8_t error) { (void)c; AsyncWebServerRequest *req = (AsyncWebServerRequest*)r; req->_onError(error); }, this);
c->onAck([](void* r, AsyncClient* c, size_t len, uint32_t time) { (void)c; AsyncWebServerRequest *req = (AsyncWebServerRequest*)r; req->_onAck(len, time); }, this); c->onAck([](void* r, AsyncClient* c, size_t len, uint32_t time) { (void)c; AsyncWebServerRequest *req = (AsyncWebServerRequest*)r; req->_onAck(len, time); }, this);
c->onDisconnect([](void* r, AsyncClient* c) { AsyncWebServerRequest *req = (AsyncWebServerRequest*)r; req->_onDisconnect(); delete c; }, this); c->onDisconnect([](void* r, AsyncClient* c) { AsyncWebServerRequest *req = (AsyncWebServerRequest*)r; req->_onDisconnect(); delete c; }, this);
@ -76,7 +47,6 @@ AsyncWebServerRequest::AsyncWebServerRequest(AsyncWebServer* s, AsyncClient* c)
AsyncWebServerRequest::~AsyncWebServerRequest() { AsyncWebServerRequest::~AsyncWebServerRequest() {
_headers.clear(); _headers.clear();
_params.free();
_pathParams.clear(); _pathParams.clear();
_interestingHeaders.clear(); _interestingHeaders.clear();
@ -96,7 +66,6 @@ AsyncWebServerRequest::~AsyncWebServerRequest(){
if (_itemBuffer) { if (_itemBuffer) {
free(_itemBuffer); free(_itemBuffer);
} }
} }
void AsyncWebServerRequest::_onData(void* buf, size_t len) { void AsyncWebServerRequest::_onData(void* buf, size_t len) {
@ -148,7 +117,8 @@ void AsyncWebServerRequest::_onData(void *buf, size_t len){
_isPlainPost = true; _isPlainPost = true;
} else if (_contentType == F("text/plain") && __is_param_char(((char*)buf)[0])) { } else if (_contentType == F("text/plain") && __is_param_char(((char*)buf)[0])) {
size_t i = 0; size_t i = 0;
while (i<len && __is_param_char(((char*)buf)[i++])); while (i < len && __is_param_char(((char*)buf)[i++]))
;
if (i < len && ((char*)buf)[i - 1] == '=') { if (i < len && ((char*)buf)[i - 1] == '=') {
_isPlainPost = true; _isPlainPost = true;
} }
@ -156,7 +126,8 @@ void AsyncWebServerRequest::_onData(void *buf, size_t len){
} }
if (!_isPlainPost) { if (!_isPlainPost) {
// check if authenticated before calling the body // check if authenticated before calling the body
if(_handler) _handler->handleBody(this, (uint8_t*)buf, len, _parsedLength, _contentLength); if (_handler)
_handler->handleBody(this, (uint8_t*)buf, len, _parsedLength, _contentLength);
_parsedLength += len; _parsedLength += len;
} else if (needParse) { } else if (needParse) {
size_t i; size_t i;
@ -171,8 +142,10 @@ void AsyncWebServerRequest::_onData(void *buf, size_t len){
if (_parsedLength == _contentLength) { if (_parsedLength == _contentLength) {
_parseState = PARSE_REQ_END; _parseState = PARSE_REQ_END;
// check if authenticated before calling handleRequest and request auth instead // check if authenticated before calling handleRequest and request auth instead
if(_handler) _handler->handleRequest(this); if (_handler)
else send(501); _handler->handleRequest(this);
else
send(501);
} }
} }
break; break;
@ -180,16 +153,13 @@ void AsyncWebServerRequest::_onData(void *buf, size_t len){
} }
void AsyncWebServerRequest::_removeNotInterestingHeaders() { void AsyncWebServerRequest::_removeNotInterestingHeaders() {
if (std::any_of(std::begin(_interestingHeaders), std::end(_interestingHeaders), if (std::any_of(std::begin(_interestingHeaders), std::end(_interestingHeaders), [](const String& str) { return str.equalsIgnoreCase(F("ANY")); }))
[](const String &str){ return str.equalsIgnoreCase(F("ANY")); }))
return; // nothing to do return; // nothing to do
for(auto iter = std::begin(_headers); iter != std::end(_headers); ) for (auto iter = std::begin(_headers); iter != std::end(_headers);) {
{
const auto name = iter->name(); const auto name = iter->name();
if (std::none_of(std::begin(_interestingHeaders), std::end(_interestingHeaders), if (std::none_of(std::begin(_interestingHeaders), std::end(_interestingHeaders), [&name](const String& str) { return str.equalsIgnoreCase(name); }))
[&name](const String &str){ return str.equalsIgnoreCase(name); }))
iter = _headers.erase(iter); iter = _headers.erase(iter);
else else
iter++; iter++;
@ -248,10 +218,6 @@ void AsyncWebServerRequest::_onDisconnect(){
_server->_handleDisconnect(this); _server->_handleDisconnect(this);
} }
void AsyncWebServerRequest::_addParam(AsyncWebParameter *p){
_params.add(p);
}
void AsyncWebServerRequest::_addPathParam(const char* p) { void AsyncWebServerRequest::_addPathParam(const char* p) {
_pathParams.emplace_back(p); _pathParams.emplace_back(p);
} }
@ -260,12 +226,14 @@ void AsyncWebServerRequest::_addGetParams(const String& params){
size_t start = 0; size_t start = 0;
while (start < params.length()) { while (start < params.length()) {
int end = params.indexOf('&', start); int end = params.indexOf('&', start);
if (end < 0) end = params.length(); if (end < 0)
end = params.length();
int equal = params.indexOf('=', start); int equal = params.indexOf('=', start);
if (equal < 0 || equal > end) equal = end; if (equal < 0 || equal > end)
String name = params.substring(start, equal); equal = end;
String value = equal + 1 < end ? params.substring(equal + 1, end) : String(); String name(params.substring(start, equal));
_addParam(new AsyncWebParameter(urlDecode(name), urlDecode(value))); String value(equal + 1 < end ? params.substring(equal + 1, end) : String());
_params.emplace_back(urlDecode(name), urlDecode(value));
start = end + 1; start = end + 1;
} }
} }
@ -315,17 +283,19 @@ bool strContains(const String &src, const String &find, bool mindcase = true) {
const int slen = src.length(); const int slen = src.length();
const int flen = find.length(); const int flen = find.length();
if (slen < flen) return false; if (slen < flen)
return false;
while (pos <= (slen - flen)) { while (pos <= (slen - flen)) {
for (i = 0; i < flen; i++) { for (i = 0; i < flen; i++) {
if (mindcase) { if (mindcase) {
if (src[pos+i] != find[i]) i = flen + 1; // no match if (src[pos + i] != find[i])
} i = flen + 1; // no match
else if (tolower(src[pos+i]) != tolower(find[i])) { } else if (tolower(src[pos + i]) != tolower(find[i])) {
i = flen + 1; // no match i = flen + 1; // no match
} }
} }
if (i == flen) return true; if (i == flen)
return true;
pos++; pos++;
} }
return false; return false;
@ -383,7 +353,7 @@ void AsyncWebServerRequest::_parsePlainPostChar(uint8_t data){
name = _temp.substring(0, _temp.indexOf('=')); name = _temp.substring(0, _temp.indexOf('='));
value = _temp.substring(_temp.indexOf('=') + 1); value = _temp.substring(_temp.indexOf('=') + 1);
} }
_addParam(new AsyncWebParameter(urlDecode(name), urlDecode(value), true)); _params.emplace_back(urlDecode(name), urlDecode(value), true);
_temp = String(); _temp = String();
} }
} }
@ -414,7 +384,14 @@ enum {
}; };
void AsyncWebServerRequest::_parseMultipartPostByte(uint8_t data, bool last) { void AsyncWebServerRequest::_parseMultipartPostByte(uint8_t data, bool last) {
#define itemWriteByte(b) do { _itemSize++; if(_itemIsFile) _handleUploadByte(b, last); else _itemValue+=(char)(b); } while(0) #define itemWriteByte(b) \
do { \
_itemSize++; \
if (_itemIsFile) \
_handleUploadByte(b, last); \
else \
_itemValue += (char)(b); \
} while (0)
if (!_parsedLength) { if (!_parsedLength) {
_multiParseState = EXPECT_BOUNDARY; _multiParseState = EXPECT_BOUNDARY;
@ -500,21 +477,27 @@ void AsyncWebServerRequest::_parseMultipartPostByte(uint8_t data, bool last){
} else if (_multiParseState == EXPECT_FEED1) { } else if (_multiParseState == EXPECT_FEED1) {
if (data != '\n') { if (data != '\n') {
_multiParseState = WAIT_FOR_RETURN1; _multiParseState = WAIT_FOR_RETURN1;
itemWriteByte('\r'); _parseMultipartPostByte(data, last); itemWriteByte('\r');
_parseMultipartPostByte(data, last);
} else { } else {
_multiParseState = EXPECT_DASH1; _multiParseState = EXPECT_DASH1;
} }
} else if (_multiParseState == EXPECT_DASH1) { } else if (_multiParseState == EXPECT_DASH1) {
if (data != '-') { if (data != '-') {
_multiParseState = WAIT_FOR_RETURN1; _multiParseState = WAIT_FOR_RETURN1;
itemWriteByte('\r'); itemWriteByte('\n'); _parseMultipartPostByte(data, last); itemWriteByte('\r');
itemWriteByte('\n');
_parseMultipartPostByte(data, last);
} else { } else {
_multiParseState = EXPECT_DASH2; _multiParseState = EXPECT_DASH2;
} }
} else if (_multiParseState == EXPECT_DASH2) { } else if (_multiParseState == EXPECT_DASH2) {
if (data != '-') { if (data != '-') {
_multiParseState = WAIT_FOR_RETURN1; _multiParseState = WAIT_FOR_RETURN1;
itemWriteByte('\r'); itemWriteByte('\n'); itemWriteByte('-'); _parseMultipartPostByte(data, last); itemWriteByte('\r');
itemWriteByte('\n');
itemWriteByte('-');
_parseMultipartPostByte(data, last);
} else { } else {
_multiParseState = BOUNDARY_OR_DATA; _multiParseState = BOUNDARY_OR_DATA;
_boundaryPosition = 0; _boundaryPosition = 0;
@ -522,7 +505,10 @@ void AsyncWebServerRequest::_parseMultipartPostByte(uint8_t data, bool last){
} else if (_multiParseState == BOUNDARY_OR_DATA) { } else if (_multiParseState == BOUNDARY_OR_DATA) {
if (_boundaryPosition < _boundary.length() && _boundary.c_str()[_boundaryPosition] != data) { if (_boundaryPosition < _boundary.length() && _boundary.c_str()[_boundaryPosition] != data) {
_multiParseState = WAIT_FOR_RETURN1; _multiParseState = WAIT_FOR_RETURN1;
itemWriteByte('\r'); itemWriteByte('\n'); itemWriteByte('-'); itemWriteByte('-'); itemWriteByte('\r');
itemWriteByte('\n');
itemWriteByte('-');
itemWriteByte('-');
uint8_t i; uint8_t i;
for (i = 0; i < _boundaryPosition; i++) for (i = 0; i < _boundaryPosition; i++)
itemWriteByte(_boundary.c_str()[i]); itemWriteByte(_boundary.c_str()[i]);
@ -530,13 +516,14 @@ void AsyncWebServerRequest::_parseMultipartPostByte(uint8_t data, bool last){
} else if (_boundaryPosition == _boundary.length() - 1) { } else if (_boundaryPosition == _boundary.length() - 1) {
_multiParseState = DASH3_OR_RETURN2; _multiParseState = DASH3_OR_RETURN2;
if (!_itemIsFile) { if (!_itemIsFile) {
_addParam(new AsyncWebParameter(_itemName, _itemValue, true)); _params.emplace_back(_itemName, _itemValue, true);
} else { } else {
if (_itemSize) { if (_itemSize) {
// check if authenticated before calling the upload // check if authenticated before calling the upload
if(_handler) _handler->handleUpload(this, _itemFilename, _itemSize - _itemBufferIndex, _itemBuffer, _itemBufferIndex, true); if (_handler)
_handler->handleUpload(this, _itemFilename, _itemSize - _itemBufferIndex, _itemBuffer, _itemBufferIndex, true);
_itemBufferIndex = 0; _itemBufferIndex = 0;
_addParam(new AsyncWebParameter(_itemName, _itemFilename, true, true, _itemSize)); _params.emplace_back(_itemName, _itemFilename, true, true, _itemSize);
} }
free(_itemBuffer); free(_itemBuffer);
_itemBuffer = NULL; _itemBuffer = NULL;
@ -556,8 +543,13 @@ void AsyncWebServerRequest::_parseMultipartPostByte(uint8_t data, bool last){
_multiParseState = PARSING_FINISHED; _multiParseState = PARSING_FINISHED;
} else { } else {
_multiParseState = WAIT_FOR_RETURN1; _multiParseState = WAIT_FOR_RETURN1;
itemWriteByte('\r'); itemWriteByte('\n'); itemWriteByte('-'); itemWriteByte('-'); itemWriteByte('\r');
uint8_t i; for(i=0; i<_boundary.length(); i++) itemWriteByte(_boundary.c_str()[i]); itemWriteByte('\n');
itemWriteByte('-');
itemWriteByte('-');
uint8_t i;
for (i = 0; i < _boundary.length(); i++)
itemWriteByte(_boundary.c_str()[i]);
_parseMultipartPostByte(data, last); _parseMultipartPostByte(data, last);
} }
} else if (_multiParseState == EXPECT_FEED2) { } else if (_multiParseState == EXPECT_FEED2) {
@ -566,9 +558,15 @@ void AsyncWebServerRequest::_parseMultipartPostByte(uint8_t data, bool last){
_itemIsFile = false; _itemIsFile = false;
} else { } else {
_multiParseState = WAIT_FOR_RETURN1; _multiParseState = WAIT_FOR_RETURN1;
itemWriteByte('\r'); itemWriteByte('\n'); itemWriteByte('-'); itemWriteByte('-'); itemWriteByte('\r');
uint8_t i; for(i=0; i<_boundary.length(); i++) itemWriteByte(_boundary.c_str()[i]); itemWriteByte('\n');
itemWriteByte('\r'); _parseMultipartPostByte(data, last); itemWriteByte('-');
itemWriteByte('-');
uint8_t i;
for (i = 0; i < _boundary.length(); i++)
itemWriteByte(_boundary.c_str()[i]);
itemWriteByte('\r');
_parseMultipartPostByte(data, last);
} }
} }
} }
@ -600,10 +598,13 @@ void AsyncWebServerRequest::_parseLine(){
_parseState = PARSE_REQ_BODY; _parseState = PARSE_REQ_BODY;
} else { } else {
_parseState = PARSE_REQ_END; _parseState = PARSE_REQ_END;
if(_handler) _handler->handleRequest(this); if (_handler)
else send(501); _handler->handleRequest(this);
else
send(501);
} }
} else _parseReqHeader(); } else
_parseReqHeader();
} }
} }
@ -611,7 +612,7 @@ size_t AsyncWebServerRequest::headers() const{
return _headers.size(); return _headers.size();
} }
bool AsyncWebServerRequest::hasHeader(const String& name) const { bool AsyncWebServerRequest::hasHeader(const char* name) const {
for (const auto& h : _headers) { for (const auto& h : _headers) {
if (h.name().equalsIgnoreCase(name)) { if (h.name().equalsIgnoreCase(name)) {
return true; return true;
@ -620,44 +621,19 @@ bool AsyncWebServerRequest::hasHeader(const String& name) const {
return false; return false;
} }
#ifdef ESP8266
bool AsyncWebServerRequest::hasHeader(const __FlashStringHelper* data) const { bool AsyncWebServerRequest::hasHeader(const __FlashStringHelper* data) const {
return hasHeader(String(data)); return hasHeader(String(data));
} }
#endif
AsyncWebHeader* AsyncWebServerRequest::getHeader(const String& name) { const AsyncWebHeader* AsyncWebServerRequest::getHeader(const char* name) const {
auto iter = std::find_if(std::begin(_headers), std::end(_headers), auto iter = std::find_if(std::begin(_headers), std::end(_headers), [&name](const AsyncWebHeader& header) { return header.name().equalsIgnoreCase(name); });
[&name](const AsyncWebHeader &header){ return header.name().equalsIgnoreCase(name); });
if (iter == std::end(_headers)) return (iter == std::end(_headers)) ? nullptr : &(*iter);
return nullptr;
return &(*iter);
}
const AsyncWebHeader* AsyncWebServerRequest::getHeader(const String& name) const {
auto iter = std::find_if(std::begin(_headers), std::end(_headers),
[&name](const AsyncWebHeader &header){ return header.name().equalsIgnoreCase(name); });
if (iter == std::end(_headers))
return nullptr;
return &(*iter);
}
AsyncWebHeader* AsyncWebServerRequest::getHeader(const __FlashStringHelper * data) {
PGM_P p = reinterpret_cast<PGM_P>(data);
size_t n = strlen_P(p);
char * name = (char*) malloc(n+1);
if (name) {
strcpy_P(name, p);
AsyncWebHeader* result = getHeader( String(name));
free(name);
return result;
} else {
return nullptr;
}
} }
#ifdef ESP8266
const AsyncWebHeader* AsyncWebServerRequest::getHeader(const __FlashStringHelper* data) const { const AsyncWebHeader* AsyncWebServerRequest::getHeader(const __FlashStringHelper* data) const {
PGM_P p = reinterpret_cast<PGM_P>(data); PGM_P p = reinterpret_cast<PGM_P>(data);
size_t n = strlen_P(p); size_t n = strlen_P(p);
@ -671,26 +647,21 @@ const AsyncWebHeader* AsyncWebServerRequest::getHeader(const __FlashStringHelper
return nullptr; return nullptr;
} }
} }
#endif
AsyncWebHeader* AsyncWebServerRequest::getHeader(size_t num) {
if (num >= _headers.size())
return nullptr;
return &(*std::next(std::begin(_headers), num));
}
const AsyncWebHeader* AsyncWebServerRequest::getHeader(size_t num) const { const AsyncWebHeader* AsyncWebServerRequest::getHeader(size_t num) const {
if (num >= _headers.size()) if (num >= _headers.size())
return nullptr; return nullptr;
return &(*std::next(std::begin(_headers), num)); return &(*std::next(_headers.cbegin(), num));
} }
size_t AsyncWebServerRequest::params() const { size_t AsyncWebServerRequest::params() const {
return _params.length(); return _params.size();
} }
bool AsyncWebServerRequest::hasParam(const String& name, bool post, bool file) const { bool AsyncWebServerRequest::hasParam(const String& name, bool post, bool file) const {
for (const auto& p : _params) { for (const auto& p : _params) {
if(p->name() == name && p->isPost() == post && p->isFile() == file){ if (p.name() == name && p.isPost() == post && p.isFile() == file) {
return true; return true;
} }
} }
@ -701,52 +672,44 @@ bool AsyncWebServerRequest::hasParam(const __FlashStringHelper * data, bool post
return hasParam(String(data).c_str(), post, file); return hasParam(String(data).c_str(), post, file);
} }
AsyncWebParameter* AsyncWebServerRequest::getParam(const String& name, bool post, bool file) const { const AsyncWebParameter* AsyncWebServerRequest::getParam(const char* name, bool post, bool file) const {
for (const auto& p : _params) { for (const auto& p : _params) {
if(p->name() == name && p->isPost() == post && p->isFile() == file){ if (p.name() == name && p.isPost() == post && p.isFile() == file) {
return p; return &p;
} }
} }
return nullptr; return nullptr;
} }
AsyncWebParameter* AsyncWebServerRequest::getParam(const __FlashStringHelper * data, bool post, bool file) const { #ifdef ESP8266
return getParam(String(data).c_str(), post, file); const AsyncWebParameter* AsyncWebServerRequest::getParam(const __FlashStringHelper* data, bool post, bool file) const {
return getParam(String(data), post, file);
}
#endif
const AsyncWebParameter* AsyncWebServerRequest::getParam(size_t num) const {
if (num >= _params.size())
return nullptr;
return &(*std::next(_params.cbegin(), num));
} }
AsyncWebParameter* AsyncWebServerRequest::getParam(size_t num) const { void AsyncWebServerRequest::addInterestingHeader(const char* name) {
auto param = _params.nth(num); if (std::none_of(std::begin(_interestingHeaders), std::end(_interestingHeaders), [&name](const String& str) { return str.equalsIgnoreCase(name); }))
return param ? *param : nullptr; _interestingHeaders.emplace_back(name);
}
void AsyncWebServerRequest::addInterestingHeader(const String& name){
if(std::none_of(std::begin(_interestingHeaders), std::end(_interestingHeaders),
[&name](const String &str){ return str.equalsIgnoreCase(name); }))
_interestingHeaders.push_back(name);
}
void AsyncWebServerRequest::send(AsyncWebServerResponse *response){
_response = response;
if(_response == NULL){
_client->close(true);
_onDisconnect();
return;
}
if(!_response->_sourceValid()){
delete response;
_response = NULL;
send(500);
}
else {
_client->setRxTimeout(0);
_response->_respond(this);
}
} }
AsyncWebServerResponse* AsyncWebServerRequest::beginResponse(int code, const String& contentType, const String& content) { AsyncWebServerResponse* AsyncWebServerRequest::beginResponse(int code, const String& contentType, const String& content) {
return new AsyncBasicResponse(code, contentType, content); return new AsyncBasicResponse(code, contentType, content);
} }
AsyncWebServerResponse* AsyncWebServerRequest::beginResponse(int code, const String& contentType, const uint8_t* content, size_t len, AwsTemplateProcessor callback) {
return new AsyncProgmemResponse(code, contentType, content, len, callback);
}
AsyncWebServerResponse* AsyncWebServerRequest::beginResponse(int code, const String& contentType, PGM_P content, AwsTemplateProcessor callback) {
return new AsyncProgmemResponse(code, contentType, (const uint8_t*)content, strlen_P(content), callback);
}
AsyncWebServerResponse* AsyncWebServerRequest::beginResponse(FS& fs, const String& path, const String& contentType, bool download, AwsTemplateProcessor callback) { AsyncWebServerResponse* AsyncWebServerRequest::beginResponse(FS& fs, const String& path, const String& contentType, bool download, AwsTemplateProcessor callback) {
if (fs.exists(path) || (!download && fs.exists(path + F(".gz")))) if (fs.exists(path) || (!download && fs.exists(path + F(".gz"))))
return new AsyncFileResponse(fs, path, contentType, download, callback); return new AsyncFileResponse(fs, path, contentType, download, callback);
@ -777,28 +740,47 @@ AsyncResponseStream * AsyncWebServerRequest::beginResponseStream(const String& c
return new AsyncResponseStream(contentType, bufferSize); return new AsyncResponseStream(contentType, bufferSize);
} }
AsyncWebServerResponse * AsyncWebServerRequest::beginResponse_P(int code, const String& contentType, const uint8_t * content, size_t len, AwsTemplateProcessor callback){ void AsyncWebServerRequest::send(AsyncWebServerResponse* response) {
return new AsyncProgmemResponse(code, contentType, content, len, callback); _response = response;
if (_response == NULL) {
_client->close(true);
_onDisconnect();
return;
}
if (!_response->_sourceValid()) {
delete response;
_response = NULL;
send(500);
} else {
_client->setRxTimeout(0);
_response->_respond(this);
} }
AsyncWebServerResponse * AsyncWebServerRequest::beginResponse_P(int code, const String& contentType, PGM_P content, AwsTemplateProcessor callback){
return beginResponse_P(code, contentType, (const uint8_t *)content, strlen_P(content), callback);
} }
void AsyncWebServerRequest::send(int code, const String& contentType, const String& content) { void AsyncWebServerRequest::send(int code, const String& contentType, const String& content) {
send(beginResponse(code, contentType, content)); send(beginResponse(code, contentType, content));
} }
void AsyncWebServerRequest::send(int code, const String& contentType, const uint8_t* content, size_t len, AwsTemplateProcessor callback) {
send(beginResponse(code, contentType, content, len, callback));
}
void AsyncWebServerRequest::send(int code, const String& contentType, PGM_P content, AwsTemplateProcessor callback) {
send(beginResponse(code, contentType, content, callback));
}
void AsyncWebServerRequest::send(FS& fs, const String& path, const String& contentType, bool download, AwsTemplateProcessor callback) { void AsyncWebServerRequest::send(FS& fs, const String& path, const String& contentType, bool download, AwsTemplateProcessor callback) {
if (fs.exists(path) || (!download && fs.exists(path + F(".gz")))) { if (fs.exists(path) || (!download && fs.exists(path + F(".gz")))) {
send(beginResponse(fs, path, contentType, download, callback)); send(beginResponse(fs, path, contentType, download, callback));
} else send(404); } else
send(404);
} }
void AsyncWebServerRequest::send(File content, const String& path, const String& contentType, bool download, AwsTemplateProcessor callback) { void AsyncWebServerRequest::send(File content, const String& path, const String& contentType, bool download, AwsTemplateProcessor callback) {
if (content == true) { if (content == true) {
send(beginResponse(content, path, contentType, download, callback)); send(beginResponse(content, path, contentType, download, callback));
} else send(404); } else
send(404);
} }
void AsyncWebServerRequest::send(Stream& stream, const String& contentType, size_t len, AwsTemplateProcessor callback) { void AsyncWebServerRequest::send(Stream& stream, const String& contentType, size_t len, AwsTemplateProcessor callback) {
@ -813,15 +795,7 @@ void AsyncWebServerRequest::sendChunked(const String& contentType, AwsResponseFi
send(beginChunkedResponse(contentType, callback, templateCallback)); send(beginChunkedResponse(contentType, callback, templateCallback));
} }
void AsyncWebServerRequest::send_P(int code, const String& contentType, const uint8_t * content, size_t len, AwsTemplateProcessor callback){ void AsyncWebServerRequest::redirect(const char* url) {
send(beginResponse_P(code, contentType, content, len, callback));
}
void AsyncWebServerRequest::send_P(int code, const String& contentType, PGM_P content, AwsTemplateProcessor callback){
send(beginResponse_P(code, contentType, content, callback));
}
void AsyncWebServerRequest::redirect(const String& url){
AsyncWebServerResponse* response = beginResponse(302); AsyncWebServerResponse* response = beginResponse(302);
response->addHeader(F("Location"), url); response->addHeader(F("Location"), url);
send(response); send(response);
@ -880,30 +854,33 @@ void AsyncWebServerRequest::requestAuthentication(const char * realm, bool isDig
bool AsyncWebServerRequest::hasArg(const char* name) const { bool AsyncWebServerRequest::hasArg(const char* name) const {
for (const auto& arg : _params) { for (const auto& arg : _params) {
if(arg->name() == name){ if (arg.name() == name) {
return true; return true;
} }
} }
return false; return false;
} }
#ifdef ESP8266
bool AsyncWebServerRequest::hasArg(const __FlashStringHelper* data) const { bool AsyncWebServerRequest::hasArg(const __FlashStringHelper* data) const {
return hasArg(String(data).c_str()); return hasArg(String(data).c_str());
} }
#endif
const String& AsyncWebServerRequest::arg(const char* name) const {
const String& AsyncWebServerRequest::arg(const String& name) const {
for (const auto& arg : _params) { for (const auto& arg : _params) {
if(arg->name() == name){ if (arg.name() == name) {
return arg->value(); return arg.value();
} }
} }
return emptyString; return emptyString;
} }
#ifdef ESP8266
const String& AsyncWebServerRequest::arg(const __FlashStringHelper* data) const { const String& AsyncWebServerRequest::arg(const __FlashStringHelper* data) const {
return arg(String(data).c_str()); return arg(String(data).c_str());
} }
#endif
const String& AsyncWebServerRequest::arg(size_t i) const { const String& AsyncWebServerRequest::arg(size_t i) const {
return getParam(i)->value(); return getParam(i)->value();
@ -918,14 +895,15 @@ const String& AsyncWebServerRequest::pathArg(size_t i) const {
} }
const String& AsyncWebServerRequest::header(const char* name) const { const String& AsyncWebServerRequest::header(const char* name) const {
const AsyncWebHeader* h = getHeader(String(name)); const AsyncWebHeader* h = getHeader(name);
return h ? h->value() : emptyString; return h ? h->value() : emptyString;
} }
#ifdef ESP8266
const String& AsyncWebServerRequest::header(const __FlashStringHelper* data) const { const String& AsyncWebServerRequest::header(const __FlashStringHelper* data) const {
return header(String(data).c_str()); return header(String(data).c_str());
}; };
#endif
const String& AsyncWebServerRequest::header(size_t i) const { const String& AsyncWebServerRequest::header(size_t i) const {
const AsyncWebHeader* h = getHeader(i); const AsyncWebHeader* h = getHeader(i);
@ -941,7 +919,7 @@ String AsyncWebServerRequest::urlDecode(const String& text) const {
char temp[] = "0x00"; char temp[] = "0x00";
unsigned int len = text.length(); unsigned int len = text.length();
unsigned int i = 0; unsigned int i = 0;
String decoded = String(); String decoded;
decoded.reserve(len); // Allocate the string internal buffer - never longer from source text decoded.reserve(len); // Allocate the string internal buffer - never longer from source text
while (i < len) { while (i < len) {
char decodedChar; char decodedChar;
@ -960,34 +938,91 @@ String AsyncWebServerRequest::urlDecode(const String& text) const {
return decoded; return decoded;
} }
#ifndef ESP8266
const char* AsyncWebServerRequest::methodToString() const {
if (_method == HTTP_ANY)
return "ANY";
if (_method & HTTP_GET)
return "GET";
if (_method & HTTP_POST)
return "POST";
if (_method & HTTP_DELETE)
return "DELETE";
if (_method & HTTP_PUT)
return "PUT";
if (_method & HTTP_PATCH)
return "PATCH";
if (_method & HTTP_HEAD)
return "HEAD";
if (_method & HTTP_OPTIONS)
return "OPTIONS";
return "UNKNOWN";
}
const char* AsyncWebServerRequest::requestedConnTypeToString() const {
switch (_reqconntype) {
case RCT_NOT_USED:
return "RCT_NOT_USED";
case RCT_DEFAULT:
return "RCT_DEFAULT";
case RCT_HTTP:
return "RCT_HTTP";
case RCT_WS:
return "RCT_WS";
case RCT_EVENT:
return "RCT_EVENT";
default:
return "ERROR";
}
}
#endif
#ifdef ESP8266
const __FlashStringHelper* AsyncWebServerRequest::methodToString() const { const __FlashStringHelper* AsyncWebServerRequest::methodToString() const {
if(_method == HTTP_ANY) return F("ANY"); if (_method == HTTP_ANY)
else if(_method & HTTP_GET) return F("GET"); return F("ANY");
else if(_method & HTTP_POST) return F("POST"); else if (_method & HTTP_GET)
else if(_method & HTTP_DELETE) return F("DELETE"); return F("GET");
else if(_method & HTTP_PUT) return F("PUT"); else if (_method & HTTP_POST)
else if(_method & HTTP_PATCH) return F("PATCH"); return F("POST");
else if(_method & HTTP_HEAD) return F("HEAD"); else if (_method & HTTP_DELETE)
else if(_method & HTTP_OPTIONS) return F("OPTIONS"); return F("DELETE");
else if (_method & HTTP_PUT)
return F("PUT");
else if (_method & HTTP_PATCH)
return F("PATCH");
else if (_method & HTTP_HEAD)
return F("HEAD");
else if (_method & HTTP_OPTIONS)
return F("OPTIONS");
return F("UNKNOWN"); return F("UNKNOWN");
} }
const __FlashStringHelper* AsyncWebServerRequest::requestedConnTypeToString() const { const __FlashStringHelper* AsyncWebServerRequest::requestedConnTypeToString() const {
switch (_reqconntype) { switch (_reqconntype) {
case RCT_NOT_USED: return F("RCT_NOT_USED"); case RCT_NOT_USED:
case RCT_DEFAULT: return F("RCT_DEFAULT"); return F("RCT_NOT_USED");
case RCT_HTTP: return F("RCT_HTTP"); case RCT_DEFAULT:
case RCT_WS: return F("RCT_WS"); return F("RCT_DEFAULT");
case RCT_EVENT: return F("RCT_EVENT"); case RCT_HTTP:
default: return F("ERROR"); return F("RCT_HTTP");
case RCT_WS:
return F("RCT_WS");
case RCT_EVENT:
return F("RCT_EVENT");
default:
return F("ERROR");
} }
} }
#endif
bool AsyncWebServerRequest::isExpectedRequestedConnType(RequestedConnectionType erct1, RequestedConnectionType erct2, RequestedConnectionType erct3) { bool AsyncWebServerRequest::isExpectedRequestedConnType(RequestedConnectionType erct1, RequestedConnectionType erct2, RequestedConnectionType erct3) {
bool res = false; bool res = false;
if ((erct1 != RCT_NOT_USED) && (erct1 == _reqconntype)) res = true; if ((erct1 != RCT_NOT_USED) && (erct1 == _reqconntype))
if ((erct2 != RCT_NOT_USED) && (erct2 == _reqconntype)) res = true; res = true;
if ((erct3 != RCT_NOT_USED) && (erct3 == _reqconntype)) res = true; if ((erct2 != RCT_NOT_USED) && (erct2 == _reqconntype))
res = true;
if ((erct3 != RCT_NOT_USED) && (erct3 == _reqconntype))
res = true;
return res; return res;
} }

View File

@ -26,14 +26,15 @@
#undef min #undef min
#undef max #undef max
#endif #endif
#include <vector>
#include <memory> #include <memory>
#include <vector>
// It is possible to restore these defines, but one can use _min and _max instead. Or std::min, std::max. // It is possible to restore these defines, but one can use _min and _max instead. Or std::min, std::max.
class AsyncBasicResponse : public AsyncWebServerResponse { class AsyncBasicResponse : public AsyncWebServerResponse {
private: private:
String _content; String _content;
public: public:
AsyncBasicResponse(int code, const String& contentType = String(), const String& content = String()); AsyncBasicResponse(int code, const String& contentType = String(), const String& content = String());
void _respond(AsyncWebServerRequest* request); void _respond(AsyncWebServerRequest* request);
@ -51,8 +52,10 @@ class AsyncAbstractResponse: public AsyncWebServerResponse {
std::vector<uint8_t> _cache; std::vector<uint8_t> _cache;
size_t _readDataFromCacheOrContent(uint8_t* data, const size_t len); size_t _readDataFromCacheOrContent(uint8_t* data, const size_t len);
size_t _fillBufferAndProcessTemplates(uint8_t* buf, size_t maxLen); size_t _fillBufferAndProcessTemplates(uint8_t* buf, size_t maxLen);
protected: protected:
AwsTemplateProcessor _callback; AwsTemplateProcessor _callback;
public: public:
AsyncAbstractResponse(AwsTemplateProcessor callback = nullptr); AsyncAbstractResponse(AwsTemplateProcessor callback = nullptr);
void _respond(AsyncWebServerRequest* request); void _respond(AsyncWebServerRequest* request);
@ -69,10 +72,12 @@ class AsyncAbstractResponse: public AsyncWebServerResponse {
class AsyncFileResponse : public AsyncAbstractResponse { class AsyncFileResponse : public AsyncAbstractResponse {
using File = fs::File; using File = fs::File;
using FS = fs::FS; using FS = fs::FS;
private: private:
File _content; File _content;
String _path; String _path;
void _setContentType(const String& path); void _setContentType(const String& path);
public: public:
AsyncFileResponse(FS& fs, const String& path, const String& contentType = String(), bool download = false, AwsTemplateProcessor callback = nullptr); AsyncFileResponse(FS& fs, const String& path, const String& contentType = String(), bool download = false, AwsTemplateProcessor callback = nullptr);
AsyncFileResponse(File content, const String& path, const String& contentType = String(), bool download = false, AwsTemplateProcessor callback = nullptr); AsyncFileResponse(File content, const String& path, const String& contentType = String(), bool download = false, AwsTemplateProcessor callback = nullptr);
@ -84,6 +89,7 @@ class AsyncFileResponse: public AsyncAbstractResponse {
class AsyncStreamResponse : public AsyncAbstractResponse { class AsyncStreamResponse : public AsyncAbstractResponse {
private: private:
Stream* _content; Stream* _content;
public: public:
AsyncStreamResponse(Stream& stream, const String& contentType, size_t len, AwsTemplateProcessor callback = nullptr); AsyncStreamResponse(Stream& stream, const String& contentType, size_t len, AwsTemplateProcessor callback = nullptr);
bool _sourceValid() const { return !!(_content); } bool _sourceValid() const { return !!(_content); }
@ -94,6 +100,7 @@ class AsyncCallbackResponse: public AsyncAbstractResponse {
private: private:
AwsResponseFiller _content; AwsResponseFiller _content;
size_t _filledLength; size_t _filledLength;
public: public:
AsyncCallbackResponse(const String& contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback = nullptr); AsyncCallbackResponse(const String& contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback = nullptr);
bool _sourceValid() const { return !!(_content); } bool _sourceValid() const { return !!(_content); }
@ -104,6 +111,7 @@ class AsyncChunkedResponse: public AsyncAbstractResponse {
private: private:
AwsResponseFiller _content; AwsResponseFiller _content;
size_t _filledLength; size_t _filledLength;
public: public:
AsyncChunkedResponse(const String& contentType, AwsResponseFiller callback, AwsTemplateProcessor templateCallback = nullptr); AsyncChunkedResponse(const String& contentType, AwsResponseFiller callback, AwsTemplateProcessor templateCallback = nullptr);
bool _sourceValid() const { return !!(_content); } bool _sourceValid() const { return !!(_content); }
@ -114,6 +122,7 @@ class AsyncProgmemResponse: public AsyncAbstractResponse {
private: private:
const uint8_t* _content; const uint8_t* _content;
size_t _readLength; size_t _readLength;
public: public:
AsyncProgmemResponse(int code, const String& contentType, const uint8_t* content, size_t len, AwsTemplateProcessor callback = nullptr); AsyncProgmemResponse(int code, const String& contentType, const uint8_t* content, size_t len, AwsTemplateProcessor callback = nullptr);
bool _sourceValid() const { return true; } bool _sourceValid() const { return true; }
@ -125,6 +134,7 @@ class cbuf;
class AsyncResponseStream : public AsyncAbstractResponse, public Print { class AsyncResponseStream : public AsyncAbstractResponse, public Print {
private: private:
std::unique_ptr<cbuf> _content; std::unique_ptr<cbuf> _content;
public: public:
AsyncResponseStream(const String& contentType, size_t bufferSize); AsyncResponseStream(const String& contentType, size_t bufferSize);
~AsyncResponseStream(); ~AsyncResponseStream();

View File

@ -23,8 +23,7 @@
#include "cbuf.h" #include "cbuf.h"
// Since ESP8266 does not link memchr by default, here's its implementation. // Since ESP8266 does not link memchr by default, here's its implementation.
void* memchr(void* ptr, int ch, size_t count) void* memchr(void* ptr, int ch, size_t count) {
{
unsigned char* p = static_cast<unsigned char*>(ptr); unsigned char* p = static_cast<unsigned char*>(ptr);
while (count--) while (count--)
if (*p++ == static_cast<unsigned char>(ch)) if (*p++ == static_cast<unsigned char>(ch))
@ -32,7 +31,6 @@ void* memchr(void* ptr, int ch, size_t count)
return nullptr; return nullptr;
} }
/* /*
* Abstract Response * Abstract Response
* */ * */
@ -42,62 +40,93 @@ const char* AsyncWebServerResponse::_responseCodeToString(int code) {
const __FlashStringHelper* AsyncWebServerResponse::responseCodeToString(int code) { const __FlashStringHelper* AsyncWebServerResponse::responseCodeToString(int code) {
switch (code) { switch (code) {
case 100: return F("Continue"); case 100:
case 101: return F("Switching Protocols"); return F("Continue");
case 200: return F("OK"); case 101:
case 201: return F("Created"); return F("Switching Protocols");
case 202: return F("Accepted"); case 200:
case 203: return F("Non-Authoritative Information"); return F("OK");
case 204: return F("No Content"); case 201:
case 205: return F("Reset Content"); return F("Created");
case 206: return F("Partial Content"); case 202:
case 300: return F("Multiple Choices"); return F("Accepted");
case 301: return F("Moved Permanently"); case 203:
case 302: return F("Found"); return F("Non-Authoritative Information");
case 303: return F("See Other"); case 204:
case 304: return F("Not Modified"); return F("No Content");
case 305: return F("Use Proxy"); case 205:
case 307: return F("Temporary Redirect"); return F("Reset Content");
case 400: return F("Bad Request"); case 206:
case 401: return F("Unauthorized"); return F("Partial Content");
case 402: return F("Payment Required"); case 300:
case 403: return F("Forbidden"); return F("Multiple Choices");
case 404: return F("Not Found"); case 301:
case 405: return F("Method Not Allowed"); return F("Moved Permanently");
case 406: return F("Not Acceptable"); case 302:
case 407: return F("Proxy Authentication Required"); return F("Found");
case 408: return F("Request Time-out"); case 303:
case 409: return F("Conflict"); return F("See Other");
case 410: return F("Gone"); case 304:
case 411: return F("Length Required"); return F("Not Modified");
case 412: return F("Precondition Failed"); case 305:
case 413: return F("Request Entity Too Large"); return F("Use Proxy");
case 414: return F("Request-URI Too Large"); case 307:
case 415: return F("Unsupported Media Type"); return F("Temporary Redirect");
case 416: return F("Requested range not satisfiable"); case 400:
case 417: return F("Expectation Failed"); return F("Bad Request");
case 500: return F("Internal Server Error"); case 401:
case 501: return F("Not Implemented"); return F("Unauthorized");
case 502: return F("Bad Gateway"); case 402:
case 503: return F("Service Unavailable"); return F("Payment Required");
case 504: return F("Gateway Time-out"); case 403:
case 505: return F("HTTP Version not supported"); return F("Forbidden");
default: return F(""); case 404:
return F("Not Found");
case 405:
return F("Method Not Allowed");
case 406:
return F("Not Acceptable");
case 407:
return F("Proxy Authentication Required");
case 408:
return F("Request Time-out");
case 409:
return F("Conflict");
case 410:
return F("Gone");
case 411:
return F("Length Required");
case 412:
return F("Precondition Failed");
case 413:
return F("Request Entity Too Large");
case 414:
return F("Request-URI Too Large");
case 415:
return F("Unsupported Media Type");
case 416:
return F("Requested range not satisfiable");
case 417:
return F("Expectation Failed");
case 500:
return F("Internal Server Error");
case 501:
return F("Not Implemented");
case 502:
return F("Bad Gateway");
case 503:
return F("Service Unavailable");
case 504:
return F("Gateway Time-out");
case 505:
return F("HTTP Version not supported");
default:
return F("");
} }
} }
AsyncWebServerResponse::AsyncWebServerResponse() AsyncWebServerResponse::AsyncWebServerResponse()
: _code(0) : _code(0), _contentType(), _contentLength(0), _sendContentLength(true), _chunked(false), _headLength(0), _sentLength(0), _ackedLength(0), _writtenLength(0), _state(RESPONSE_SETUP) {
, _contentType()
, _contentLength(0)
, _sendContentLength(true)
, _chunked(false)
, _headLength(0)
, _sentLength(0)
, _ackedLength(0)
, _writtenLength(0)
, _state(RESPONSE_SETUP)
{
for (const auto& header : DefaultHeaders::Instance()) { for (const auto& header : DefaultHeaders::Instance()) {
_headers.emplace_back(header); _headers.emplace_back(header);
} }
@ -161,8 +190,16 @@ bool AsyncWebServerResponse::_started() const { return _state > RESPONSE_SETUP;
bool AsyncWebServerResponse::_finished() const { return _state > RESPONSE_WAIT_ACK; } bool AsyncWebServerResponse::_finished() const { return _state > RESPONSE_WAIT_ACK; }
bool AsyncWebServerResponse::_failed() const { return _state == RESPONSE_FAILED; } bool AsyncWebServerResponse::_failed() const { return _state == RESPONSE_FAILED; }
bool AsyncWebServerResponse::_sourceValid() const { return false; } bool AsyncWebServerResponse::_sourceValid() const { return false; }
void AsyncWebServerResponse::_respond(AsyncWebServerRequest *request){ _state = RESPONSE_END; request->client()->close(); } void AsyncWebServerResponse::_respond(AsyncWebServerRequest* request) {
size_t AsyncWebServerResponse::_ack(AsyncWebServerRequest *request, size_t len, uint32_t time){ (void)request; (void)len; (void)time; return 0; } _state = RESPONSE_END;
request->client()->close();
}
size_t AsyncWebServerResponse::_ack(AsyncWebServerRequest* request, size_t len, uint32_t time) {
(void)request;
(void)len;
(void)time;
return 0;
}
/* /*
* String/Code Response * String/Code Response
@ -240,13 +277,11 @@ size_t AsyncBasicResponse::_ack(AsyncWebServerRequest *request, size_t len, uint
return 0; return 0;
} }
/* /*
* Abstract Response * Abstract Response
* */ * */
AsyncAbstractResponse::AsyncAbstractResponse(AwsTemplateProcessor callback): _callback(callback) AsyncAbstractResponse::AsyncAbstractResponse(AwsTemplateProcessor callback) : _callback(callback) {
{
// In case of template processing, we're unable to determine real response size // In case of template processing, we're unable to determine real response size
if (callback) { if (callback) {
_contentLength = 0; _contentLength = 0;
@ -319,7 +354,8 @@ size_t AsyncAbstractResponse::_ack(AsyncWebServerRequest *request, size_t len, u
return 0; return 0;
} }
outLen = sprintf_P((char*)buf + headLen, PSTR("%x"), readLen) + headLen; outLen = sprintf_P((char*)buf + headLen, PSTR("%x"), readLen) + headLen;
while(outLen < headLen + 4) buf[outLen++] = ' '; while (outLen < headLen + 4)
buf[outLen++] = ' ';
buf[outLen++] = '\r'; buf[outLen++] = '\r';
buf[outLen++] = '\n'; buf[outLen++] = '\n';
outLen += readLen; outLen += readLen;
@ -365,8 +401,7 @@ size_t AsyncAbstractResponse::_ack(AsyncWebServerRequest *request, size_t len, u
return 0; return 0;
} }
size_t AsyncAbstractResponse::_readDataFromCacheOrContent(uint8_t* data, const size_t len) size_t AsyncAbstractResponse::_readDataFromCacheOrContent(uint8_t* data, const size_t len) {
{
// If we have something in cache, copy it to buffer // If we have something in cache, copy it to buffer
const size_t readFromCache = std::min(len, _cache.size()); const size_t readFromCache = std::min(len, _cache.size());
if (readFromCache) { if (readFromCache) {
@ -379,8 +414,7 @@ size_t AsyncAbstractResponse::_readDataFromCacheOrContent(uint8_t* data, const s
return readFromCache + readFromContent; return readFromCache + readFromContent;
} }
size_t AsyncAbstractResponse::_fillBufferAndProcessTemplates(uint8_t* data, size_t len) size_t AsyncAbstractResponse::_fillBufferAndProcessTemplates(uint8_t* data, size_t len) {
{
if (!_callback) if (!_callback)
return _fillBuffer(data, len); return _fillBuffer(data, len);
@ -420,18 +454,15 @@ size_t AsyncAbstractResponse::_fillBufferAndProcessTemplates(uint8_t* data, size
// Copy remaining read-ahead data into cache // Copy remaining read-ahead data into cache
_cache.insert(_cache.begin(), pTemplateEnd + 1, buf + (&data[len - 1] - pTemplateStart) + readFromCacheOrContent); _cache.insert(_cache.begin(), pTemplateEnd + 1, buf + (&data[len - 1] - pTemplateStart) + readFromCacheOrContent);
pTemplateEnd = &data[len - 1]; pTemplateEnd = &data[len - 1];
} } else // closing placeholder not found in file data, store found percent symbol as is and advance to the next position
else // closing placeholder not found in file data, store found percent symbol as is and advance to the next position
{ {
// but first, store read file data in cache // but first, store read file data in cache
_cache.insert(_cache.begin(), buf + (&data[len - 1] - pTemplateStart), buf + (&data[len - 1] - pTemplateStart) + readFromCacheOrContent); _cache.insert(_cache.begin(), buf + (&data[len - 1] - pTemplateStart), buf + (&data[len - 1] - pTemplateStart) + readFromCacheOrContent);
++pTemplateStart; ++pTemplateStart;
} }
} } else // closing placeholder not found in content data, store found percent symbol as is and advance to the next position
else // closing placeholder not found in content data, store found percent symbol as is and advance to the next position
++pTemplateStart; ++pTemplateStart;
} } else // closing placeholder not found in content data, store found percent symbol as is and advance to the next position
else // closing placeholder not found in content data, store found percent symbol as is and advance to the next position
++pTemplateStart; ++pTemplateStart;
if (paramName.length()) { if (paramName.length()) {
// call callback and replace with result. // call callback and replace with result.
@ -473,7 +504,6 @@ size_t AsyncAbstractResponse::_fillBufferAndProcessTemplates(uint8_t* data, size
return len; return len;
} }
/* /*
* File Response * File Response
* */ * */
@ -488,25 +518,44 @@ void AsyncFileResponse::_setContentType(const String& path){
extern const __FlashStringHelper* getContentType(const String& path); extern const __FlashStringHelper* getContentType(const String& path);
_contentType = getContentType(path); _contentType = getContentType(path);
#else #else
if (path.endsWith(F(".html"))) _contentType = F("text/html"); if (path.endsWith(F(".html")))
else if (path.endsWith(F(".htm"))) _contentType = F("text/html"); _contentType = F("text/html");
else if (path.endsWith(F(".css"))) _contentType = F("text/css"); else if (path.endsWith(F(".htm")))
else if (path.endsWith(F(".json"))) _contentType = F("application/json"); _contentType = F("text/html");
else if (path.endsWith(F(".js"))) _contentType = F("application/javascript"); else if (path.endsWith(F(".css")))
else if (path.endsWith(F(".png"))) _contentType = F("image/png"); _contentType = F("text/css");
else if (path.endsWith(F(".gif"))) _contentType = F("image/gif"); else if (path.endsWith(F(".json")))
else if (path.endsWith(F(".jpg"))) _contentType = F("image/jpeg"); _contentType = F("application/json");
else if (path.endsWith(F(".ico"))) _contentType = F("image/x-icon"); else if (path.endsWith(F(".js")))
else if (path.endsWith(F(".svg"))) _contentType = F("image/svg+xml"); _contentType = F("application/javascript");
else if (path.endsWith(F(".eot"))) _contentType = F("font/eot"); else if (path.endsWith(F(".png")))
else if (path.endsWith(F(".woff"))) _contentType = F("font/woff"); _contentType = F("image/png");
else if (path.endsWith(F(".woff2"))) _contentType = F("font/woff2"); else if (path.endsWith(F(".gif")))
else if (path.endsWith(F(".ttf"))) _contentType = F("font/ttf"); _contentType = F("image/gif");
else if (path.endsWith(F(".xml"))) _contentType = F("text/xml"); else if (path.endsWith(F(".jpg")))
else if (path.endsWith(F(".pdf"))) _contentType = F("application/pdf"); _contentType = F("image/jpeg");
else if (path.endsWith(F(".zip"))) _contentType = F("application/zip"); else if (path.endsWith(F(".ico")))
else if(path.endsWith(F(".gz"))) _contentType = F("application/x-gzip"); _contentType = F("image/x-icon");
else _contentType = F("text/plain"); else if (path.endsWith(F(".svg")))
_contentType = F("image/svg+xml");
else if (path.endsWith(F(".eot")))
_contentType = F("font/eot");
else if (path.endsWith(F(".woff")))
_contentType = F("font/woff");
else if (path.endsWith(F(".woff2")))
_contentType = F("font/woff2");
else if (path.endsWith(F(".ttf")))
_contentType = F("font/ttf");
else if (path.endsWith(F(".xml")))
_contentType = F("text/xml");
else if (path.endsWith(F(".pdf")))
_contentType = F("application/pdf");
else if (path.endsWith(F(".zip")))
_contentType = F("application/zip");
else if (path.endsWith(F(".gz")))
_contentType = F("application/x-gzip");
else
_contentType = F("text/plain");
#endif #endif
} }
@ -667,13 +716,11 @@ size_t AsyncProgmemResponse::_fillBuffer(uint8_t *data, size_t len){
return left; return left;
} }
/* /*
* Response Stream (You can print/write/printf to it, up to the contentLen bytes) * Response Stream (You can print/write/printf to it, up to the contentLen bytes)
* */ * */
AsyncResponseStream::AsyncResponseStream(const String& contentType, size_t bufferSize) AsyncResponseStream::AsyncResponseStream(const String& contentType, size_t bufferSize) {
{
_code = 200; _code = 200;
_contentLength = 0; _contentLength = 0;
_contentType = contentType; _contentType = contentType;

View File

@ -36,10 +36,7 @@ const char *fs::FileOpenMode::append = "a";
#endif #endif
AsyncWebServer::AsyncWebServer(uint16_t port) AsyncWebServer::AsyncWebServer(uint16_t port)
: _server(port) : _server(port) {
, _rewrites(LinkedList<AsyncWebRewrite*>([](AsyncWebRewrite* r){ delete r; }))
, _handlers(LinkedList<AsyncWebHandler*>([](AsyncWebHandler* h){ delete h; }))
{
_catchAllHandler = new AsyncCallbackWebHandler(); _catchAllHandler = new AsyncCallbackWebHandler();
if (_catchAllHandler == NULL) if (_catchAllHandler == NULL)
return; return;
@ -53,35 +50,59 @@ AsyncWebServer::AsyncWebServer(uint16_t port)
c->free(); c->free();
delete c; delete c;
} }
}, this); },
this);
} }
AsyncWebServer::~AsyncWebServer() { AsyncWebServer::~AsyncWebServer() {
reset(); reset();
end(); end();
if(_catchAllHandler) delete _catchAllHandler; if (_catchAllHandler)
delete _catchAllHandler;
}
AsyncWebRewrite& AsyncWebServer::addRewrite(std::shared_ptr<AsyncWebRewrite> rewrite) {
_rewrites.emplace_back(rewrite);
return *_rewrites.back().get();
} }
AsyncWebRewrite& AsyncWebServer::addRewrite(AsyncWebRewrite* rewrite) { AsyncWebRewrite& AsyncWebServer::addRewrite(AsyncWebRewrite* rewrite) {
_rewrites.add(rewrite); _rewrites.emplace_back(rewrite);
return *rewrite; return *_rewrites.back().get();
} }
bool AsyncWebServer::removeRewrite(AsyncWebRewrite* rewrite) { bool AsyncWebServer::removeRewrite(AsyncWebRewrite* rewrite) {
return _rewrites.remove(rewrite); return removeRewrite(rewrite->from().c_str(), rewrite->toUrl().c_str());
}
bool AsyncWebServer::removeRewrite(const char* from, const char* to) {
for (auto r = _rewrites.begin(); r != _rewrites.end(); ++r) {
if (r->get()->from() == from && r->get()->toUrl() == to) {
_rewrites.erase(r);
return true;
}
}
return false;
} }
AsyncWebRewrite& AsyncWebServer::rewrite(const char* from, const char* to) { AsyncWebRewrite& AsyncWebServer::rewrite(const char* from, const char* to) {
return addRewrite(new AsyncWebRewrite(from, to)); _rewrites.emplace_back(std::make_shared<AsyncWebRewrite>(from, to));
return *_rewrites.back().get();
} }
AsyncWebHandler& AsyncWebServer::addHandler(AsyncWebHandler* handler) { AsyncWebHandler& AsyncWebServer::addHandler(AsyncWebHandler* handler) {
_handlers.add(handler); _handlers.emplace_back(handler);
return *handler; return *(_handlers.back().get());
} }
bool AsyncWebServer::removeHandler(AsyncWebHandler* handler) { bool AsyncWebServer::removeHandler(AsyncWebHandler* handler) {
return _handlers.remove(handler); for (auto i = _handlers.begin(); i != _handlers.end(); ++i) {
if (i->get() == handler) {
_handlers.erase(i);
return true;
}
}
return false;
} }
void AsyncWebServer::begin() { void AsyncWebServer::begin() {
@ -117,9 +138,9 @@ void AsyncWebServer::_rewriteRequest(AsyncWebServerRequest *request){
} }
void AsyncWebServer::_attachHandler(AsyncWebServerRequest* request) { void AsyncWebServer::_attachHandler(AsyncWebServerRequest* request) {
for(const auto& h: _handlers){ for (auto& h : _handlers) {
if (h->filter(request) && h->canHandle(request)) { if (h->filter(request) && h->canHandle(request)) {
request->setHandler(h); request->setHandler(h.get());
return; return;
} }
} }
@ -128,7 +149,6 @@ void AsyncWebServer::_attachHandler(AsyncWebServerRequest *request){
request->setHandler(_catchAllHandler); request->setHandler(_catchAllHandler);
} }
AsyncCallbackWebHandler& AsyncWebServer::on(const char* uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest, ArUploadHandlerFunction onUpload, ArBodyHandlerFunction onBody) { AsyncCallbackWebHandler& AsyncWebServer::on(const char* uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest, ArUploadHandlerFunction onUpload, ArBodyHandlerFunction onBody) {
AsyncCallbackWebHandler* handler = new AsyncCallbackWebHandler(); AsyncCallbackWebHandler* handler = new AsyncCallbackWebHandler();
handler->setUri(uri); handler->setUri(uri);
@ -186,8 +206,8 @@ void AsyncWebServer::onRequestBody(ArBodyHandlerFunction fn){
} }
void AsyncWebServer::reset() { void AsyncWebServer::reset() {
_rewrites.free(); _rewrites.clear();
_handlers.free(); _handlers.clear();
if (_catchAllHandler != NULL) { if (_catchAllHandler != NULL) {
_catchAllHandler->onRequest(NULL); _catchAllHandler->onRequest(NULL);
@ -195,4 +215,3 @@ void AsyncWebServer::reset(){
_catchAllHandler->onBody(NULL); _catchAllHandler->onBody(NULL);
} }
} }