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)
[![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.
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
- Removed SPIFFSEditor
- Deployed in PlatformIO registry and Arduino IDE library manager
- CI
- Some code cleanup
- `SSE_MAX_QUEUED_MESSAGES` to control the maximum number of messages that can be queued for a SSE client
- `write()` function public in `AsyncEventSource.h`
- 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.
- `WS_MAX_QUEUED_MESSAGES`: control the maximum number of messages that can be queued for a Websocket client
- Resurrected `AsyncWebSocketMessageBuffer` and `makeBuffer()` in order to make the fork API-compatible with the original library from me-no-dev regarding WebSocket.
- [#5](https://github.com/mathieucarbou/ESPAsyncWebServer/pull/5) ([@vortigont](https://github.com/vortigont)): set real "Last-Modified" header based on file's LastWrite time
- [#13](https://github.com/mathieucarbou/ESPAsyncWebServer/pull/13) ([@tueddy](https://github.com/tueddy)): Compile with Arduino 3 (ESP-IDF 5.1)
- [#14](https://github.com/mathieucarbou/ESPAsyncWebServer/pull/14) ([@nilo85](https://github.com/nilo85)): Add support for Auth & GET requests in AsyncCallbackJsonWebHandler
- Added `setAuthentication(const String& username, const String& password)`
- Added `StreamConcat` example to show how to stream multiple files in one response
- Remove filename after inline in Content-Disposition header according to RFC2183
- Depends on `mathieucarbou/Async TCP @ ^3.1.4`
- Arduino 3 / ESP-IDF 5.1 compatibility
- Added all flavors of `binary()`, `text()`, `binaryAll()` and `textAll()` in `AsyncWebSocket`
- 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
- [#29](https://github.com/mathieucarbou/ESPAsyncWebServer/pull/29) ([@vortigont](https://github.com/vortigont)): Some websocket 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
- [@ayushsharma82](https://github.com/ayushsharma82) and [@mathieucarbou](https://github.com/mathieucarbou): Add RP2040 support ([#31](https://github.com/mathieucarbou/ESPAsyncWebServer/pull/31))
- [@mathieucarbou](https://github.com/mathieucarbou): `SSE_MAX_QUEUED_MESSAGES` to control the maximum number of messages that can be queued for a SSE client
- [@mathieucarbou](https://github.com/mathieucarbou): `write()` function public in `AsyncEventSource.h`
- [@mathieucarbou](https://github.com/mathieucarbou): `WS_MAX_QUEUED_MESSAGES`: control the maximum number of messages that can be queued for a Websocket client
- [@mathieucarbou](https://github.com/mathieucarbou): Added `setAuthentication(const String& username, const String& password)`
- [@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
- [@mathieucarbou](https://github.com/mathieucarbou): Added `StreamConcat` example to show how to stream multiple files in one response
- [@mathieucarbou](https://github.com/mathieucarbou): Added all flavors of `binary()`, `text()`, `binaryAll()` and `textAll()` in `AsyncWebSocket`
- [@mathieucarbou](https://github.com/mathieucarbou): Arduino 3 / ESP-IDF 5.1 compatibility
- [@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.
- [@mathieucarbou](https://github.com/mathieucarbou): CI
- [@mathieucarbou](https://github.com/mathieucarbou): Depends on `mathieucarbou/Async TCP @ ^3.1.4`
- [@mathieucarbou](https://github.com/mathieucarbou): Deployed in PlatformIO registry and Arduino IDE library manager
- [@mathieucarbou](https://github.com/mathieucarbou): Firmware size optimization: remove mbedtls dependency (accounts for 33KB in firmware)
- [@mathieucarbou](https://github.com/mathieucarbou): Made DEFAULT_MAX_SSE_CLIENTS customizable
- [@mathieucarbou](https://github.com/mathieucarbou): Made DEFAULT_MAX_WS_CLIENTS customizable
- [@mathieucarbou](https://github.com/mathieucarbou): Remove filename after inline in Content-Disposition header according to RFC2183
- [@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.
- [@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.
- [@mathieucarbou](https://github.com/mathieucarbou): Some code cleanup
- [@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
@ -76,17 +88,30 @@ void send(JsonDocument& doc) {
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
// Async TCP queue size
1. Set the running core to be on the same core of your application (usually core 1) `-D CONFIG_ASYNC_TCP_RUNNING_CORE=1`
2. Set the stack size appropriately with `-D CONFIG_ASYNC_TCP_STACK_SIZE=16384`.
The default value of `16384` might be too much for your project.
You can look at the [MycilaTaskMonitor](https://oss.carbou.me/MycilaTaskMonitor) project to monitor the stack usage.
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
// Async TCP async task core
-D CONFIG_ASYNC_TCP_RUNNING_CORE=1
// Async TCP async stac ksize
-D CONFIG_ASYNC_TCP_STACK_SIZE=8096
// WebSocket queue size
-D CONFIG_ASYNC_TCP_STACK_SIZE=4096
-D WS_MAX_QUEUED_MESSAGES=64
```

View File

@ -1,7 +1,7 @@
# bundle exec jekyll serve --host=0.0.0.0
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
plugins:
- 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)
[![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.
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
- Removed SPIFFSEditor
- Deployed in PlatformIO registry and Arduino IDE library manager
- CI
- Some code cleanup
- `SSE_MAX_QUEUED_MESSAGES` to control the maximum number of messages that can be queued for a SSE client
- `write()` function public in `AsyncEventSource.h`
- 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.
- `WS_MAX_QUEUED_MESSAGES`: control the maximum number of messages that can be queued for a Websocket client
- Resurrected `AsyncWebSocketMessageBuffer` and `makeBuffer()` in order to make the fork API-compatible with the original library from me-no-dev regarding WebSocket.
- [#5](https://github.com/mathieucarbou/ESPAsyncWebServer/pull/5) ([@vortigont](https://github.com/vortigont)): set real "Last-Modified" header based on file's LastWrite time
- [#13](https://github.com/mathieucarbou/ESPAsyncWebServer/pull/13) ([@tueddy](https://github.com/tueddy)): Compile with Arduino 3 (ESP-IDF 5.1)
- [#14](https://github.com/mathieucarbou/ESPAsyncWebServer/pull/14) ([@nilo85](https://github.com/nilo85)): Add support for Auth & GET requests in AsyncCallbackJsonWebHandler
- Added `setAuthentication(const String& username, const String& password)`
- Added `StreamConcat` example to show how to stream multiple files in one response
- Remove filename after inline in Content-Disposition header according to RFC2183
- Depends on `mathieucarbou/Async TCP @ ^3.1.4`
- Arduino 3 / ESP-IDF 5.1 compatibility
- Added all flavors of `binary()`, `text()`, `binaryAll()` and `textAll()` in `AsyncWebSocket`
- 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
- [#29](https://github.com/mathieucarbou/ESPAsyncWebServer/pull/29) ([@vortigont](https://github.com/vortigont)): Some websocket 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
- [@ayushsharma82](https://github.com/ayushsharma82) and [@mathieucarbou](https://github.com/mathieucarbou): Add RP2040 support ([#31](https://github.com/mathieucarbou/ESPAsyncWebServer/pull/31))
- [@mathieucarbou](https://github.com/mathieucarbou): `SSE_MAX_QUEUED_MESSAGES` to control the maximum number of messages that can be queued for a SSE client
- [@mathieucarbou](https://github.com/mathieucarbou): `write()` function public in `AsyncEventSource.h`
- [@mathieucarbou](https://github.com/mathieucarbou): `WS_MAX_QUEUED_MESSAGES`: control the maximum number of messages that can be queued for a Websocket client
- [@mathieucarbou](https://github.com/mathieucarbou): Added `setAuthentication(const String& username, const String& password)`
- [@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
- [@mathieucarbou](https://github.com/mathieucarbou): Added `StreamConcat` example to show how to stream multiple files in one response
- [@mathieucarbou](https://github.com/mathieucarbou): Added all flavors of `binary()`, `text()`, `binaryAll()` and `textAll()` in `AsyncWebSocket`
- [@mathieucarbou](https://github.com/mathieucarbou): Arduino 3 / ESP-IDF 5.1 compatibility
- [@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.
- [@mathieucarbou](https://github.com/mathieucarbou): CI
- [@mathieucarbou](https://github.com/mathieucarbou): Depends on `mathieucarbou/Async TCP @ ^3.1.4`
- [@mathieucarbou](https://github.com/mathieucarbou): Deployed in PlatformIO registry and Arduino IDE library manager
- [@mathieucarbou](https://github.com/mathieucarbou): Firmware size optimization: remove mbedtls dependency (accounts for 33KB in firmware)
- [@mathieucarbou](https://github.com/mathieucarbou): Made DEFAULT_MAX_SSE_CLIENTS customizable
- [@mathieucarbou](https://github.com/mathieucarbou): Made DEFAULT_MAX_WS_CLIENTS customizable
- [@mathieucarbou](https://github.com/mathieucarbou): Remove filename after inline in Content-Disposition header according to RFC2183
- [@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.
- [@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.
- [@mathieucarbou](https://github.com/mathieucarbou): Some code cleanup
- [@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
@ -76,17 +88,30 @@ void send(JsonDocument& doc) {
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
// Async TCP queue size
1. Set the running core to be on the same core of your application (usually core 1) `-D CONFIG_ASYNC_TCP_RUNNING_CORE=1`
2. Set the stack size appropriately with `-D CONFIG_ASYNC_TCP_STACK_SIZE=16384`.
The default value of `16384` might be too much for your project.
You can look at the [MycilaTaskMonitor](https://oss.carbou.me/MycilaTaskMonitor) project to monitor the stack usage.
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
// Async TCP async task core
-D CONFIG_ASYNC_TCP_RUNNING_CORE=1
// Async TCP async stac ksize
-D CONFIG_ASYNC_TCP_STACK_SIZE=8096
// WebSocket queue size
-D CONFIG_ASYNC_TCP_STACK_SIZE=4096
-D WS_MAX_QUEUED_MESSAGES=64
```

View File

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

View File

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

View File

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

View File

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

View File

@ -17,7 +17,11 @@ class StreamConcat : public Stream {
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 {
#endif
size_t count = _s1->readBytes(buffer, length);
return count > 0 ? count : _s2->readBytes(buffer, length);
}

View File

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

View File

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

View File

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

View File

@ -1,10 +1,10 @@
name=ESP Async WebServer
version=2.10.8
version=3.0.6
author=Me-No-Dev
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
category=Other
url=https://github.com/mathieucarbou/ESPAsyncWebServer
architectures=esp8266,esp32
architectures=*
license=LGPL-3.0

View File

@ -4,42 +4,57 @@ build_flags =
-Wall -Wextra
-D CONFIG_ARDUHAL_LOG_COLORS
-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
monitor_speed = 115200
monitor_filters = esp32_exception_decoder, log2file
[platformio]
lib_dir = .
src_dir = examples/CaptivePortal
; src_dir = examples/CaptivePortal
; src_dir = examples/SimpleServer
; src_dir = examples/StreamFiles
src_dir = examples/StreamFiles
; src_dir = examples/Filters
; src_dir = examples/Draft
[env:arduino]
platform = espressif32
board = esp32dev
lib_deps =
bblanchon/ArduinoJson @ 7.1.0
mathieucarbou/Async TCP @ ^3.1.4
[env:arduino-2]
platform = espressif32@6.7.0
board = esp32dev
lib_deps =
bblanchon/ArduinoJson @ 7.1.0
mathieucarbou/Async TCP @ ^3.1.4
[env:arduino-3]
platform = espressif32
platform_packages=
platformio/framework-arduinoespressif32 @ https://github.com/espressif/arduino-esp32.git#3.0.1
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 @ https://github.com/espressif/arduino-esp32.git#3.0.2
platformio/framework-arduinoespressif32-libs @ https://github.com/espressif/arduino-esp32/releases/download/3.0.2/esp32-arduino-libs-3.0.2.zip
board = esp32dev
lib_deps =
bblanchon/ArduinoJson @ 7.1.0
mathieucarbou/Async TCP @ ^3.1.4
[env:esp8266]
platform = espressif8266
board = huzzah
lib_deps =
bblanchon/ArduinoJson @ 7.0.4
bblanchon/ArduinoJson @ 7.1.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
*/
#include "Arduino.h"
#include "AsyncEventSource.h"
#ifndef ESP8266
#if defined(ESP32)
#include <rom/ets_sys.h>
#endif
#include "AsyncEventSource.h"
static String generateEventMessage(const char* message, const char* event, uint32_t id, uint32_t reconnect) {
String ev;
@ -110,8 +110,7 @@ static String generateEventMessage(const char *message, const char *event, uint3
// Message
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);
if (_data == nullptr) {
_len = 0;
@ -155,9 +154,7 @@ size_t AsyncEventSourceMessage::send(AsyncClient *client) {
// Client
AsyncEventSourceClient::AsyncEventSourceClient(AsyncWebServerRequest *request, AsyncEventSource *server)
: _messageQueue(LinkedList<AsyncEventSourceMessage *>([](AsyncEventSourceMessage *m){ delete m; }))
{
AsyncEventSourceClient::AsyncEventSourceClient(AsyncWebServerRequest* request, AsyncEventSource* server) {
_client = request->client();
_server = server;
_lastId = 0;
@ -177,56 +174,56 @@ AsyncEventSourceClient::AsyncEventSourceClient(AsyncWebServerRequest *request, A
}
AsyncEventSourceClient::~AsyncEventSourceClient() {
_lockmq.lock();
_messageQueue.free();
_lockmq.unlock();
#ifdef ESP32
std::lock_guard<std::mutex> lock(_lockmq);
#endif
_messageQueue.clear();
close();
}
void AsyncEventSourceClient::_queueMessage(AsyncEventSourceMessage *dataMessage){
if(dataMessage == NULL)
return;
if(!connected()){
delete dataMessage;
return;
}
void AsyncEventSourceClient::_queueMessage(const char* message, size_t len) {
#ifdef ESP32
// length() is not thread-safe, thus acquiring the lock before this call..
_lockmq.lock();
if(_messageQueue.length() >= SSE_MAX_QUEUED_MESSAGES){
std::lock_guard<std::mutex> lock(_lockmq);
#endif
if (_messageQueue.size() >= SSE_MAX_QUEUED_MESSAGES) {
#ifdef ESP8266
ets_printf(String(F("ERROR: Too many messages queued\n")).c_str());
#else
#elif defined(ESP32)
log_e("Too many messages queued: deleting message");
#endif
delete dataMessage;
} else {
_messageQueue.add(dataMessage);
return;
}
_messageQueue.emplace_back(message, len);
// runqueue trigger when new messages added
if (_client->canSend()) {
_runQueue();
}
}
_lockmq.unlock();
}
void AsyncEventSourceClient::_onAck(size_t len, uint32_t time) {
#ifdef ESP32
// Same here, acquiring the lock early
_lockmq.lock();
while(len && !_messageQueue.isEmpty()){
len = _messageQueue.front()->ack(len, time);
if(_messageQueue.front()->finished())
_messageQueue.remove(_messageQueue.front());
std::lock_guard<std::mutex> lock(_lockmq);
#endif
while (len && _messageQueue.size()) {
len = _messageQueue.front().ack(len, time);
if (_messageQueue.front().finished())
_messageQueue.pop_front();
}
_runQueue();
_lockmq.unlock();
}
void AsyncEventSourceClient::_onPoll() {
_lockmq.lock();
if(!_messageQueue.isEmpty()){
#ifdef ESP32
// Same here, acquiring the lock early
std::lock_guard<std::mutex> lock(_lockmq);
#endif
if (_messageQueue.size()) {
_runQueue();
}
_lockmq.unlock();
}
void AsyncEventSourceClient::_onTimeout(uint32_t time __attribute__((unused))) {
@ -244,47 +241,35 @@ void AsyncEventSourceClient::close(){
}
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) {
if (!connected())
return;
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 len;
_lockmq.lock();
len = _messageQueue.length();
_lockmq.unlock();
return len;
#ifdef ESP32
std::lock_guard<std::mutex> lock(_lockmq);
#endif
return _messageQueue.size();
}
void AsyncEventSourceClient::_runQueue() {
// Calls to this private method now already protected by _lockmq acquisition
// so no extra call of _lockmq.lock() here..
for (auto i = _messageQueue.begin(); i != _messageQueue.end(); ++i) {
// If it crashes here, iterator (i) has been invalidated as _messageQueue
// has been changed... (UL 2020-11-15: Not supposed to happen any more ;-) )
if (!(*i)->sent()) {
(*i)->send(_client);
for (auto& i : _messageQueue) {
if (!i.sent())
i.send(_client);
}
}
}
// 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) {
_connectcb = cb;
}
@ -294,34 +279,33 @@ void AsyncEventSource::authorizeConnect(ArAuthorizeConnectHandler cb){
}
void AsyncEventSource::_addClient(AsyncEventSourceClient* client) {
/*char * temp = (char *)malloc(2054);
if(temp != NULL){
memset(temp+1,' ',2048);
temp[0] = ':';
temp[2049] = '\r';
temp[2050] = '\n';
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 (!client)
return;
#ifdef ESP32
std::lock_guard<std::mutex> lock(_client_queue_lock);
#endif
_clients.emplace_back(client);
if (_connectcb)
_connectcb(client);
}
void AsyncEventSource::_handleDisconnect(AsyncEventSourceClient* client) {
AsyncWebLockGuard l(_client_queue_lock);
_clients.remove(client);
#ifdef ESP32
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() {
// While the whole loop is not done, the linked list is locked and so the
// iterator should remain valid even when AsyncEventSource::_handleDisconnect()
// 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) {
if (c->connected())
c->close();
@ -332,10 +316,12 @@ void AsyncEventSource::close(){
size_t AsyncEventSource::avgPacketsWaiting() const {
size_t aql = 0;
uint32_t nConnectedClients = 0;
AsyncWebLockGuard l(_client_queue_lock);
if (_clients.isEmpty()) {
#ifdef ESP32
std::lock_guard<std::mutex> lock(_client_queue_lock);
#endif
if (!_clients.size())
return 0;
}
for (const auto& c : _clients) {
if (c->connected()) {
aql += c->packetsWaiting();
@ -348,7 +334,9 @@ size_t AsyncEventSource::avgPacketsWaiting() const {
void AsyncEventSource::send(
const char* message, const char* event, uint32_t id, uint32_t 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) {
if (c->connected()) {
c->write(ev.c_str(), ev.length());
@ -357,11 +345,14 @@ void AsyncEventSource::send(
}
size_t AsyncEventSource::count() const {
size_t n_clients;
AsyncWebLockGuard l(_client_queue_lock);
n_clients = _clients.count_if([](AsyncEventSourceClient *c){
return c->connected();
});
#ifdef ESP32
std::lock_guard<std::mutex> lock(_client_queue_lock);
#endif
size_t n_clients{0};
for (const auto& i : _clients)
if (i->connected())
++n_clients;
return n_clients;
}
@ -409,4 +400,3 @@ size_t AsyncEventSourceResponse::_ack(AsyncWebServerRequest *request, size_t len
}
return 0;
}

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

@ -24,21 +24,26 @@
#include <Arduino.h>
#ifdef ESP32
#include <AsyncTCP.h>
#include <mutex>
#ifndef WS_MAX_QUEUED_MESSAGES
#define WS_MAX_QUEUED_MESSAGES 32
#endif
#else
#elif defined(ESP8266)
#include <ESPAsyncTCP.h>
#ifndef WS_MAX_QUEUED_MESSAGES
#define WS_MAX_QUEUED_MESSAGES 8
#endif
#elif defined(TARGET_RP2040)
#include <AsyncTCP_RP2040W.h>
#ifndef WS_MAX_QUEUED_MESSAGES
#define WS_MAX_QUEUED_MESSAGES 32
#endif
#endif
#include <ESPAsyncWebServer.h>
#include "AsyncWebSynchronization.h"
#include <list>
#include <deque>
#include <list>
#include <memory>
#ifdef ESP8266
@ -87,10 +92,23 @@ typedef struct {
uint64_t index;
} AwsFrameInfo;
typedef enum { WS_DISCONNECTED, WS_CONNECTED, WS_DISCONNECTING } AwsClientStatus;
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;
typedef enum { WS_DISCONNECTED,
WS_CONNECTED,
WS_DISCONNECTING } AwsClientStatus;
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 {
friend AsyncWebSocket;
@ -109,8 +127,7 @@ class AsyncWebSocketMessageBuffer {
size_t length() const { return _buffer->size(); }
};
class AsyncWebSocketMessage
{
class AsyncWebSocketMessage {
private:
AsyncWebSocketSharedBuffer _WSbuffer;
uint8_t _opcode{WS_TEXT};
@ -136,9 +153,9 @@ class AsyncWebSocketClient {
AsyncWebSocket* _server;
uint32_t _clientId;
AwsClientStatus _status;
AsyncWebLock _lock;
#ifdef ESP32
mutable std::mutex _lock;
#endif
std::deque<AsyncWebSocketControl> _controlQueue;
std::deque<AsyncWebSocketMessage> _messageQueue;
bool closeWhenFull = true;
@ -260,7 +277,9 @@ class AsyncWebSocket: public AsyncWebHandler {
AwsEventHandler _eventHandler{nullptr};
AwsHandshakeHandler _handshakeHandler;
bool _enabled;
AsyncWebLock _lock;
#ifdef ESP32
mutable std::mutex _lock;
#endif
public:
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 printfAll(const char* format, ...) __attribute__((format(printf, 2, 3)));
#ifndef ESP32
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)));
#endif
// event listener
void onEvent(AwsEventHandler handler) {
@ -347,7 +367,6 @@ class AsyncWebSocket: public AsyncWebHandler {
virtual bool canHandle(AsyncWebServerRequest* request) override final;
virtual void handleRequest(AsyncWebServerRequest* request) override final;
// messagebuffer functions/objects.
AsyncWebSocketMessageBuffer* makeBuffer(size_t size = 0);
AsyncWebSocketMessageBuffer* makeBuffer(const uint8_t* data, size_t size);
@ -360,6 +379,7 @@ class AsyncWebSocketResponse: public AsyncWebServerResponse {
private:
String _content;
AsyncWebSocket* _server;
public:
AsyncWebSocketResponse(const String& key, AsyncWebSocket* server);
void _respond(AsyncWebServerRequest* request);
@ -367,5 +387,4 @@ class AsyncWebSocketResponse: public AsyncWebServerResponse {
bool _sourceValid() const { return true; }
};
#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 "FS.h"
#include <functional>
#include <list>
#include <vector>
#include "FS.h"
#include "StringArray.h"
#ifdef ESP32
#include <WiFi.h>
#include <AsyncTCP.h>
#include <WiFi.h>
#elif defined(ESP8266)
#include <ESP8266WiFi.h>
#include <ESPAsyncTCP.h>
#elif defined(TARGET_RP2040)
#include <WiFi.h>
#include <AsyncTCP_RP2040W.h>
#include <http_parser.h>
#include <HTTP_Method.h>
#else
#error Platform not supported
#endif
#define ASYNCWEBSERVER_VERSION "2.10.8"
#define ASYNCWEBSERVER_VERSION_MAJOR 2
#define ASYNCWEBSERVER_VERSION_MINOR 10
#define ASYNCWEBSERVER_VERSION_REVISION 8
#define ASYNCWEBSERVER_VERSION "3.0.6"
#define ASYNCWEBSERVER_VERSION_MAJOR 3
#define ASYNCWEBSERVER_VERSION_MINOR 0
#define ASYNCWEBSERVER_VERSION_REVISION 6
#define ASYNCWEBSERVER_FORK_mathieucarbou
#ifdef ASYNCWEBSERVER_REGEX
@ -63,6 +66,9 @@ class AsyncStaticWebHandler;
class AsyncCallbackWebHandler;
class AsyncResponseStream;
#if defined (TARGET_RP2040)
typedef enum http_method WebRequestMethod;
#else
#ifndef WEBSERVER_H
typedef enum {
HTTP_GET = 0b00000001,
@ -75,6 +81,7 @@ typedef enum {
HTTP_ANY = 0b01111111,
} WebRequestMethod;
#endif
#endif
#ifndef HAVE_FS_FILE_OPEN_MODE
namespace fs {
@ -108,7 +115,6 @@ class AsyncWebParameter {
bool _isFile;
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) {}
const String& name() const { return _name; }
const String& value() const { return _value; }
@ -131,10 +137,12 @@ class AsyncWebHeader {
AsyncWebHeader(const AsyncWebHeader&) = default;
AsyncWebHeader(const String& name, const String& value) : _name(name), _value(value) {}
AsyncWebHeader(const String& data): _name(), _value(){
if(!data) return;
AsyncWebHeader(const String& data) {
if (!data)
return;
int index = data.indexOf(':');
if (index < 0) return;
if (index < 0)
return;
_name = data.substring(0, index);
_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
* */
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<String(const String&)> AwsTemplateProcessor;
@ -160,6 +173,7 @@ class AsyncWebServerRequest {
using FS = fs::FS;
friend class AsyncWebServer;
friend class AsyncCallbackWebHandler;
private:
AsyncClient* _client;
AsyncWebServer* _server;
@ -188,7 +202,7 @@ class AsyncWebServerRequest {
size_t _parsedLength;
std::list<AsyncWebHeader> _headers;
LinkedList<AsyncWebParameter *> _params;
std::list<AsyncWebParameter> _params;
std::vector<String> _pathParams;
uint8_t _multiParseState;
@ -210,7 +224,6 @@ class AsyncWebServerRequest {
void _onDisconnect();
void _onData(void* buf, size_t len);
void _addParam(AsyncWebParameter*);
void _addPathParam(const char* param);
bool _parseReqHead();
@ -239,8 +252,15 @@ class AsyncWebServerRequest {
const String& contentType() const { return _contentType; }
size_t contentLength() const { return _contentLength; }
bool multipart() const { return _isMultipart; }
#ifndef ESP8266
const char* methodToString() const;
const char* requestedConnTypeToString() const;
#else
const __FlashStringHelper* methodToString() const;
const __FlashStringHelper* requestedConnTypeToString() const;
#endif
RequestedConnectionType requestedConnType() const { return _reqconntype; }
bool isExpectedRequestedConnType(RequestedConnectionType erct1, RequestedConnectionType erct2 = RCT_NOT_USED, RequestedConnectionType erct3 = RCT_NOT_USED);
void onDisconnect(ArDisconnectHandler fn);
@ -253,63 +273,135 @@ class AsyncWebServerRequest {
void requestAuthentication(const char* realm = NULL, bool isDigest = true);
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(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(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(const String& contentType, size_t len, 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, 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(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(const String& contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback = nullptr);
AsyncWebServerResponse* beginChunkedResponse(const String& contentType, AwsResponseFiller callback, AwsTemplateProcessor templateCallback = nullptr);
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
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);
const AsyncWebHeader* getHeader(const String& name) const;
AsyncWebHeader* getHeader(const __FlashStringHelper * data);
// check if header exists
bool hasHeader(const char* name) const;
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;
AsyncWebHeader* getHeader(size_t num);
#endif
const AsyncWebHeader* getHeader(size_t num) const;
size_t params() const; // get arguments count
bool hasParam(const String& name, 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;
AsyncWebParameter* getParam(size_t num) const;
/**
* @brief Get the Request parameter by name
*
* @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
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)
#endif
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
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
#endif
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)
#endif
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
String urlDecode(const String& text) const;
};
@ -317,7 +409,7 @@ class AsyncWebServerRequest {
* 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);
@ -332,9 +424,10 @@ class AsyncWebRewrite {
String _from;
String _toUrl;
String _params;
ArRequestFilterFunction _filter;
ArRequestFilterFunction _filter{nullptr};
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('?');
if (index > 0) {
_params = _toUrl.substring(index + 1);
@ -342,7 +435,10 @@ class 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); }
const String& from(void) const { return _from; }
const String& toUrl(void) const { return _toUrl; }
@ -356,14 +452,26 @@ class AsyncWebRewrite {
class AsyncWebHandler {
protected:
ArRequestFilterFunction _filter;
ArRequestFilterFunction _filter{nullptr};
String _username;
String _password;
public:
AsyncWebHandler():_username(""), _password(""){}
AsyncWebHandler& setFilter(ArRequestFilterFunction fn) { _filter = fn; return *this; }
AsyncWebHandler& setAuthentication(const char *username, const char *password){ _username = String(username);_password = String(password); return *this; };
AsyncWebHandler& setAuthentication(const String& username, const String& password){ _username = username;_password = password; return *this; };
AsyncWebHandler() {}
AsyncWebHandler& setFilter(ArRequestFilterFunction fn) {
_filter = fn;
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); }
virtual ~AsyncWebHandler() {}
virtual bool canHandle(AsyncWebServerRequest* request __attribute__((unused))) {
@ -380,7 +488,12 @@ class AsyncWebHandler {
* */
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;
class AsyncWebServerResponse {
@ -397,6 +510,7 @@ class AsyncWebServerResponse {
size_t _writtenLength;
WebResponseState _state;
const char* _responseCodeToString(int code);
public:
static const __FlashStringHelper* responseCodeToString(int code);
@ -427,8 +541,8 @@ typedef std::function<void(AsyncWebServerRequest *request, uint8_t *data, size_t
class AsyncWebServer {
protected:
AsyncServer _server;
LinkedList<AsyncWebRewrite*> _rewrites;
LinkedList<AsyncWebHandler*> _handlers;
std::list<std::shared_ptr<AsyncWebRewrite>> _rewrites;
std::list<std::unique_ptr<AsyncWebHandler>> _handlers;
AsyncCallbackWebHandler* _catchAllHandler;
public:
@ -444,9 +558,46 @@ class AsyncWebServer {
#endif
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);
/**
* @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);
bool removeHandler(AsyncWebHandler* handler);
@ -493,9 +644,9 @@ public:
}
};
#include "WebResponseImpl.h"
#include "WebHandlerImpl.h"
#include "AsyncWebSocket.h"
#include "AsyncEventSource.h"
#include "AsyncWebSocket.h"
#include "WebHandlerImpl.h"
#include "WebResponseImpl.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 <libb64/cencode.h>
#ifdef ESP32
#if defined(ESP32) || defined(TARGET_RP2040)
#include <MD5Builder.h>
#else
#include "md5.h"
#endif
// Basic Auth hash = base64("username: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
#ifdef ESP32
#if defined(ESP32) || defined(TARGET_RP2040)
MD5Builder md5;
md5.begin();
md5.add(data, len);
@ -147,31 +146,36 @@ String requestDigestAuthentication(const char * realm){
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) {
// os_printf("AUTH FAIL: missing requred fields\n");
return false;
}
String myHeader = String(header);
String myHeader(header);
int nextBreak = myHeader.indexOf(',');
if (nextBreak < 0) {
// os_printf("AUTH FAIL: no variables\n");
return false;
}
String myUsername = String();
String myRealm = String();
String myNonce = String();
String myUri = String();
String myResponse = String();
String myQop = String();
String myNc = String();
String myCnonce = String();
String myUsername;
String myRealm;
String myNonce;
String myUri;
String myResponse;
String myQop;
String myNc;
String myCnonce;
myHeader += F(", ");
do {
String avLine = myHeader.substring(0, nextBreak);
String avLine(myHeader.substring(0, nextBreak));
avLine.trim();
myHeader = myHeader.substring(nextBreak + 1);
nextBreak = myHeader.indexOf(',');
@ -181,7 +185,7 @@ bool checkDigestAuthentication(const char * header, const __FlashStringHelper *m
// os_printf("AUTH FAIL: no = sign\n");
return false;
}
String varName = avLine.substring(0, eqSign);
String varName(avLine.substring(0, eqSign));
avLine = avLine.substring(eqSign + 1);
if (avLine.startsWith(String('"'))) {
avLine = avLine.substring(1, avLine.length() - 1);
@ -227,7 +231,7 @@ bool checkDigestAuthentication(const char * header, const __FlashStringHelper *m
}
} 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 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);
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);
#endif
// for storing hashed versions on the device that can be authenticated against
String generateDigestHash(const char* username, const char* password, const char* realm);

View File

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

View File

@ -22,11 +22,12 @@
#include "WebHandlerImpl.h"
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 '/'
if (_uri.length() == 0 || _uri[0] != '/') _uri = String('/') + _uri;
if (_path.length() == 0 || _path[0] != '/') _path = String('/') + _path;
if (_uri.length() == 0 || _uri[0] != '/')
_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.
// 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
// Notice that root will be "" not "/"
if (_uri[_uri.length()-1] == '/') _uri = _uri.substring(0, _uri.length()-1);
if (_path[_path.length()-1] == '/') _path = _path.substring(0, _path.length()-1);
if (_uri[_uri.length() - 1] == '/')
_uri = _uri.substring(0, _uri.length() - 1);
if (_path[_path.length() - 1] == '/')
_path = _path.substring(0, _path.length() - 1);
// Reset stats
_gzipFirst = false;
@ -85,10 +88,7 @@ AsyncStaticWebHandler& AsyncStaticWebHandler::setLastModified(){
}
#endif
bool AsyncStaticWebHandler::canHandle(AsyncWebServerRequest* request) {
if(request->method() != HTTP_GET
|| !request->url().startsWith(_uri)
|| !request->isExpectedRequestedConnType(RCT_DEFAULT, RCT_HTTP)
){
if (request->method() != HTTP_GET || !request->url().startsWith(_uri) || !request->isExpectedRequestedConnType(RCT_DEFAULT, RCT_HTTP)) {
return false;
}
if (_getFile(request)) {
@ -105,8 +105,7 @@ bool AsyncStaticWebHandler::canHandle(AsyncWebServerRequest *request){
return false;
}
bool AsyncStaticWebHandler::_getFile(AsyncWebServerRequest *request)
{
bool AsyncStaticWebHandler::_getFile(AsyncWebServerRequest* request) {
// Remove the found uri
String path = request->url().substring(_uri.length());
@ -137,8 +136,7 @@ bool AsyncStaticWebHandler::_getFile(AsyncWebServerRequest *request)
#define FILE_IS_REAL(f) (f == true)
#endif
bool AsyncStaticWebHandler::_fileExists(AsyncWebServerRequest *request, const String& path)
{
bool AsyncStaticWebHandler::_fileExists(AsyncWebServerRequest* request, const String& path) {
bool fileFound = false;
bool gzipFound = false;
@ -179,24 +177,26 @@ bool AsyncStaticWebHandler::_fileExists(AsyncWebServerRequest *request, const St
// Calculate gzip statistic
_gzipStats = (_gzipStats << 1) + (gzipFound ? 1 : 0);
if (_gzipStats == 0x00) _gzipFirst = false; // All files are not gzip
else if (_gzipStats == 0xFF) _gzipFirst = true; // All files are gzip
else _gzipFirst = _countBits(_gzipStats) > 4; // IF we have more gzip files - try gzip first
if (_gzipStats == 0x00)
_gzipFirst = false; // All files are not gzip
else if (_gzipStats == 0xFF)
_gzipFirst = true; // All files are gzip
else
_gzipFirst = _countBits(_gzipStats) > 4; // IF we have more gzip files - try gzip first
}
return found;
}
uint8_t AsyncStaticWebHandler::_countBits(const uint8_t value) const
{
uint8_t AsyncStaticWebHandler::_countBits(const uint8_t value) const {
uint8_t w = value;
uint8_t n;
for (n=0; w!=0; n++) w&=w-1;
for (n = 0; w != 0; n++)
w &= w - 1;
return n;
}
void AsyncStaticWebHandler::handleRequest(AsyncWebServerRequest *request)
{
void AsyncStaticWebHandler::handleRequest(AsyncWebServerRequest* request) {
// Get the filename from request->_tempObject and free it
String filename = String((char*)request->_tempObject);
free(request->_tempObject);
@ -206,8 +206,22 @@ void AsyncStaticWebHandler::handleRequest(AsyncWebServerRequest *request)
if (request->_tempFile == true) {
time_t lw = request->_tempFile.getLastWrite(); // get last file mod time (if supported by FS)
if (lw) setLastModified(gmtime(&lw));
String etag(lw ? lw : request->_tempFile.size()); // set etag to lastmod timestamp if available, otherwise to 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"))) {
request->_tempFile.close();
request->send(304); // Not modified

View File

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

View File

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

View File

@ -23,8 +23,7 @@
#include "cbuf.h"
// 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);
while (count--)
if (*p++ == static_cast<unsigned char>(ch))
@ -32,7 +31,6 @@ void* memchr(void* ptr, int ch, size_t count)
return nullptr;
}
/*
* Abstract Response
* */
@ -42,62 +40,93 @@ const char* AsyncWebServerResponse::_responseCodeToString(int code) {
const __FlashStringHelper* AsyncWebServerResponse::responseCodeToString(int code) {
switch (code) {
case 100: return F("Continue");
case 101: return F("Switching Protocols");
case 200: return F("OK");
case 201: return F("Created");
case 202: return F("Accepted");
case 203: return F("Non-Authoritative Information");
case 204: return F("No Content");
case 205: return F("Reset Content");
case 206: return F("Partial Content");
case 300: return F("Multiple Choices");
case 301: return F("Moved Permanently");
case 302: return F("Found");
case 303: return F("See Other");
case 304: return F("Not Modified");
case 305: return F("Use Proxy");
case 307: return F("Temporary Redirect");
case 400: return F("Bad Request");
case 401: return F("Unauthorized");
case 402: return F("Payment Required");
case 403: return F("Forbidden");
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("");
case 100:
return F("Continue");
case 101:
return F("Switching Protocols");
case 200:
return F("OK");
case 201:
return F("Created");
case 202:
return F("Accepted");
case 203:
return F("Non-Authoritative Information");
case 204:
return F("No Content");
case 205:
return F("Reset Content");
case 206:
return F("Partial Content");
case 300:
return F("Multiple Choices");
case 301:
return F("Moved Permanently");
case 302:
return F("Found");
case 303:
return F("See Other");
case 304:
return F("Not Modified");
case 305:
return F("Use Proxy");
case 307:
return F("Temporary Redirect");
case 400:
return F("Bad Request");
case 401:
return F("Unauthorized");
case 402:
return F("Payment Required");
case 403:
return F("Forbidden");
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()
: _code(0)
, _contentType()
, _contentLength(0)
, _sendContentLength(true)
, _chunked(false)
, _headLength(0)
, _sentLength(0)
, _ackedLength(0)
, _writtenLength(0)
, _state(RESPONSE_SETUP)
{
: _code(0), _contentType(), _contentLength(0), _sendContentLength(true), _chunked(false), _headLength(0), _sentLength(0), _ackedLength(0), _writtenLength(0), _state(RESPONSE_SETUP) {
for (const auto& header : DefaultHeaders::Instance()) {
_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::_failed() const { return _state == RESPONSE_FAILED; }
bool AsyncWebServerResponse::_sourceValid() const { return false; }
void AsyncWebServerResponse::_respond(AsyncWebServerRequest *request){ _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; }
void AsyncWebServerResponse::_respond(AsyncWebServerRequest* request) {
_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
@ -240,13 +277,11 @@ size_t AsyncBasicResponse::_ack(AsyncWebServerRequest *request, size_t len, uint
return 0;
}
/*
* 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
if (callback) {
_contentLength = 0;
@ -319,7 +354,8 @@ size_t AsyncAbstractResponse::_ack(AsyncWebServerRequest *request, size_t len, u
return 0;
}
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++] = '\n';
outLen += readLen;
@ -365,8 +401,7 @@ size_t AsyncAbstractResponse::_ack(AsyncWebServerRequest *request, size_t len, u
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
const size_t readFromCache = std::min(len, _cache.size());
if (readFromCache) {
@ -379,8 +414,7 @@ size_t AsyncAbstractResponse::_readDataFromCacheOrContent(uint8_t* data, const s
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)
return _fillBuffer(data, len);
@ -420,18 +454,15 @@ size_t AsyncAbstractResponse::_fillBufferAndProcessTemplates(uint8_t* data, size
// Copy remaining read-ahead data into cache
_cache.insert(_cache.begin(), pTemplateEnd + 1, buf + (&data[len - 1] - pTemplateStart) + readFromCacheOrContent);
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
_cache.insert(_cache.begin(), buf + (&data[len - 1] - pTemplateStart), buf + (&data[len - 1] - pTemplateStart) + readFromCacheOrContent);
++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;
}
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;
if (paramName.length()) {
// call callback and replace with result.
@ -473,7 +504,6 @@ size_t AsyncAbstractResponse::_fillBufferAndProcessTemplates(uint8_t* data, size
return len;
}
/*
* File Response
* */
@ -488,25 +518,44 @@ void AsyncFileResponse::_setContentType(const String& path){
extern const __FlashStringHelper* getContentType(const String& path);
_contentType = getContentType(path);
#else
if (path.endsWith(F(".html"))) _contentType = F("text/html");
else if (path.endsWith(F(".htm"))) _contentType = F("text/html");
else if (path.endsWith(F(".css"))) _contentType = F("text/css");
else if (path.endsWith(F(".json"))) _contentType = F("application/json");
else if (path.endsWith(F(".js"))) _contentType = F("application/javascript");
else if (path.endsWith(F(".png"))) _contentType = F("image/png");
else if (path.endsWith(F(".gif"))) _contentType = F("image/gif");
else if (path.endsWith(F(".jpg"))) _contentType = F("image/jpeg");
else if (path.endsWith(F(".ico"))) _contentType = F("image/x-icon");
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");
if (path.endsWith(F(".html")))
_contentType = F("text/html");
else if (path.endsWith(F(".htm")))
_contentType = F("text/html");
else if (path.endsWith(F(".css")))
_contentType = F("text/css");
else if (path.endsWith(F(".json")))
_contentType = F("application/json");
else if (path.endsWith(F(".js")))
_contentType = F("application/javascript");
else if (path.endsWith(F(".png")))
_contentType = F("image/png");
else if (path.endsWith(F(".gif")))
_contentType = F("image/gif");
else if (path.endsWith(F(".jpg")))
_contentType = F("image/jpeg");
else if (path.endsWith(F(".ico")))
_contentType = F("image/x-icon");
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
}
@ -667,13 +716,11 @@ size_t AsyncProgmemResponse::_fillBuffer(uint8_t *data, size_t len){
return left;
}
/*
* 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;
_contentLength = 0;
_contentType = contentType;

View File

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