Aktualizace na verzi 3.0.6
This commit is contained in:
parent
5c12efc75b
commit
e0375a1fb6
17
CMakeLists.txt
Normal file
17
CMakeLists.txt
Normal 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)
|
85
README.md
85
README.md
@ -4,34 +4,46 @@
|
|||||||
[![Continuous Integration](https://github.com/mathieucarbou/ESPAsyncWebServer/actions/workflows/ci.yml/badge.svg)](https://github.com/mathieucarbou/ESPAsyncWebServer/actions/workflows/ci.yml)
|
[![Continuous Integration](https://github.com/mathieucarbou/ESPAsyncWebServer/actions/workflows/ci.yml/badge.svg)](https://github.com/mathieucarbou/ESPAsyncWebServer/actions/workflows/ci.yml)
|
||||||
[![PlatformIO Registry](https://badges.registry.platformio.org/packages/mathieucarbou/library/ESP%20Async%20WebServer.svg)](https://registry.platformio.org/libraries/mathieucarbou/ESP%20Async%20WebServer)
|
[![PlatformIO Registry](https://badges.registry.platformio.org/packages/mathieucarbou/library/ESP%20Async%20WebServer.svg)](https://registry.platformio.org/libraries/mathieucarbou/ESP%20Async%20WebServer)
|
||||||
|
|
||||||
Asynchronous HTTP and WebSocket Server Library for ESP32.
|
Asynchronous HTTP and WebSocket Server Library for ESP32, ESP8266 and RP2040
|
||||||
Supports: WebSocket, SSE, Authentication, Arduino Json 7, File Upload, Static File serving, URL Rewrite, URL Redirect, etc.
|
Supports: WebSocket, SSE, Authentication, Arduino Json 7, File Upload, Static File serving, URL Rewrite, URL Redirect, etc.
|
||||||
|
|
||||||
This fork is based on [yubox-node-org/ESPAsyncWebServer](https://github.com/yubox-node-org/ESPAsyncWebServer) and includes all the concurrency fixes.
|
This fork is based on [yubox-node-org/ESPAsyncWebServer](https://github.com/yubox-node-org/ESPAsyncWebServer) and includes all the concurrency fixes.
|
||||||
|
|
||||||
## Changes in this fork
|
## Changes in this fork
|
||||||
|
|
||||||
- Removed SPIFFSEditor
|
- [@ayushsharma82](https://github.com/ayushsharma82) and [@mathieucarbou](https://github.com/mathieucarbou): Add RP2040 support ([#31](https://github.com/mathieucarbou/ESPAsyncWebServer/pull/31))
|
||||||
- Deployed in PlatformIO registry and Arduino IDE library manager
|
- [@mathieucarbou](https://github.com/mathieucarbou): `SSE_MAX_QUEUED_MESSAGES` to control the maximum number of messages that can be queued for a SSE client
|
||||||
- CI
|
- [@mathieucarbou](https://github.com/mathieucarbou): `write()` function public in `AsyncEventSource.h`
|
||||||
- Some code cleanup
|
- [@mathieucarbou](https://github.com/mathieucarbou): `WS_MAX_QUEUED_MESSAGES`: control the maximum number of messages that can be queued for a Websocket client
|
||||||
- `SSE_MAX_QUEUED_MESSAGES` to control the maximum number of messages that can be queued for a SSE client
|
- [@mathieucarbou](https://github.com/mathieucarbou): Added `setAuthentication(const String& username, const String& password)`
|
||||||
- `write()` function public in `AsyncEventSource.h`
|
- [@mathieucarbou](https://github.com/mathieucarbou): Added `setCloseClientOnQueueFull(bool)` which can be set on a client to either close the connection or discard messages but not close the connection when the queue is full
|
||||||
- Arduino Json 7 compatibility and backward compatible with 6 and 6 (changes in `AsyncJson.h`). The API to use Json has not changed. These are only internal changes.
|
- [@mathieucarbou](https://github.com/mathieucarbou): Added `StreamConcat` example to show how to stream multiple files in one response
|
||||||
- `WS_MAX_QUEUED_MESSAGES`: control the maximum number of messages that can be queued for a Websocket client
|
- [@mathieucarbou](https://github.com/mathieucarbou): Added all flavors of `binary()`, `text()`, `binaryAll()` and `textAll()` in `AsyncWebSocket`
|
||||||
- Resurrected `AsyncWebSocketMessageBuffer` and `makeBuffer()` in order to make the fork API-compatible with the original library from me-no-dev regarding WebSocket.
|
- [@mathieucarbou](https://github.com/mathieucarbou): Arduino 3 / ESP-IDF 5.1 compatibility
|
||||||
- [#5](https://github.com/mathieucarbou/ESPAsyncWebServer/pull/5) ([@vortigont](https://github.com/vortigont)): set real "Last-Modified" header based on file's LastWrite time
|
- [@mathieucarbou](https://github.com/mathieucarbou): Arduino Json 7 compatibility and backward compatible with 6 and 6 (changes in `AsyncJson.h`). The API to use Json has not changed. These are only internal changes.
|
||||||
- [#13](https://github.com/mathieucarbou/ESPAsyncWebServer/pull/13) ([@tueddy](https://github.com/tueddy)): Compile with Arduino 3 (ESP-IDF 5.1)
|
- [@mathieucarbou](https://github.com/mathieucarbou): CI
|
||||||
- [#14](https://github.com/mathieucarbou/ESPAsyncWebServer/pull/14) ([@nilo85](https://github.com/nilo85)): Add support for Auth & GET requests in AsyncCallbackJsonWebHandler
|
- [@mathieucarbou](https://github.com/mathieucarbou): Depends on `mathieucarbou/Async TCP @ ^3.1.4`
|
||||||
- Added `setAuthentication(const String& username, const String& password)`
|
- [@mathieucarbou](https://github.com/mathieucarbou): Deployed in PlatformIO registry and Arduino IDE library manager
|
||||||
- Added `StreamConcat` example to show how to stream multiple files in one response
|
- [@mathieucarbou](https://github.com/mathieucarbou): Firmware size optimization: remove mbedtls dependency (accounts for 33KB in firmware)
|
||||||
- Remove filename after inline in Content-Disposition header according to RFC2183
|
- [@mathieucarbou](https://github.com/mathieucarbou): Made DEFAULT_MAX_SSE_CLIENTS customizable
|
||||||
- Depends on `mathieucarbou/Async TCP @ ^3.1.4`
|
- [@mathieucarbou](https://github.com/mathieucarbou): Made DEFAULT_MAX_WS_CLIENTS customizable
|
||||||
- Arduino 3 / ESP-IDF 5.1 compatibility
|
- [@mathieucarbou](https://github.com/mathieucarbou): Remove filename after inline in Content-Disposition header according to RFC2183
|
||||||
- Added all flavors of `binary()`, `text()`, `binaryAll()` and `textAll()` in `AsyncWebSocket`
|
- [@mathieucarbou](https://github.com/mathieucarbou): Removed SPIFFSEditor to reduce library size and maintainance. SPIFF si also deprecated. If you need it, please copy the files from the original repository in your project. This fork focus on maintaining the server part and the SPIFFEditor is an application which has nothing to do inside a server library.
|
||||||
- Added `setCloseClientOnQueueFull(bool)` which can be set on a client to either close the connection or discard messages but not close the connection when the queue is full
|
- [@mathieucarbou](https://github.com/mathieucarbou): Resurrected `AsyncWebSocketMessageBuffer` and `makeBuffer()` in order to make the fork API-compatible with the original library from me-no-dev regarding WebSocket.
|
||||||
- [#29](https://github.com/mathieucarbou/ESPAsyncWebServer/pull/29) ([@vortigont](https://github.com/vortigont)): Some websocket code cleanup
|
- [@mathieucarbou](https://github.com/mathieucarbou): Some code cleanup
|
||||||
- Use `-D DEFAULT_MAX_WS_CLIENTS` to change the number of allows WebSocket clients and use `cleanupClients()` to help cleanup resources about dead clients
|
- [@mathieucarbou](https://github.com/mathieucarbou): Use `-D DEFAULT_MAX_WS_CLIENTS` to change the number of allows WebSocket clients and use `cleanupClients()` to help cleanup resources about dead clients
|
||||||
|
- [@nilo85](https://github.com/nilo85): Add support for Auth & GET requests in AsyncCallbackJsonWebHandler ([#14](https://github.com/mathieucarbou/ESPAsyncWebServer/pull/14))
|
||||||
|
- [@p0p-x](https://github.com/p0p-x): ESP IDF Compatibility (added back CMakeLists.txt) ([#32](https://github.com/mathieucarbou/ESPAsyncWebServer/pull/32))
|
||||||
|
- [@tueddy](https://github.com/tueddy): Compile with Arduino 3 (ESP-IDF 5.1) ([#13](https://github.com/mathieucarbou/ESPAsyncWebServer/pull/13))
|
||||||
|
- [@vortigont](https://github.com/vortigont): Set real "Last-Modified" header based on file's LastWrite time ([#5](https://github.com/mathieucarbou/ESPAsyncWebServer/pull/5))
|
||||||
|
- [@vortigont](https://github.com/vortigont): Some websocket code cleanup ([#29](https://github.com/mathieucarbou/ESPAsyncWebServer/pull/29))
|
||||||
|
- [@vortigont](https://github.com/vortigont): Refactor code - replace DYI structs with STL objects ([#39](https://github.com/mathieucarbou/ESPAsyncWebServer/pull/39))
|
||||||
|
|
||||||
|
## Dependencies:
|
||||||
|
|
||||||
|
- **ESP32**: `mathieucarbou/Async TCP @ ^3.1.4` (Arduino IDE: [https://github.com/mathieucarbou/AsyncTCP#v3.1.4](https://github.com/mathieucarbou/AsyncTCP/releases/tag/v3.1.4))
|
||||||
|
- **ESP8266**: `esphome/ESPAsyncTCP-esphome @ 2.0.0` (Arduino IDE: [https://github.com/mathieucarbou/esphome-ESPAsyncTCP#v2.0.0](https://github.com/mathieucarbou/esphome-ESPAsyncTCP/releases/tag/v2.0.0))
|
||||||
|
- **RP2040**: `khoih-prog/AsyncTCP_RP2040W @ 1.2.0` (Arduino IDE: [https://github.com/khoih-prog/AsyncTCP_RP2040W#v1.2.0](https://github.com/khoih-prog/AsyncTCP_RP2040W/releases/tag/v1.2.0))
|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
|
|
||||||
@ -76,17 +88,30 @@ void send(JsonDocument& doc) {
|
|||||||
|
|
||||||
I recommend to use the official API `AsyncWebSocketMessageBuffer` to retain further compatibility.
|
I recommend to use the official API `AsyncWebSocketMessageBuffer` to retain further compatibility.
|
||||||
|
|
||||||
## Stack size and queues
|
## Important recommendations
|
||||||
|
|
||||||
Here are some important flags to tweak depending on your needs:
|
Most of the crashes are caused by improper configuration of the library for the project.
|
||||||
|
Here are some recommendations to avoid them.
|
||||||
|
|
||||||
```cpp
|
1. Set the running core to be on the same core of your application (usually core 1) `-D CONFIG_ASYNC_TCP_RUNNING_CORE=1`
|
||||||
// Async TCP queue size
|
2. Set the stack size appropriately with `-D CONFIG_ASYNC_TCP_STACK_SIZE=16384`.
|
||||||
|
The default value of `16384` might be too much for your project.
|
||||||
|
You can look at the [MycilaTaskMonitor](https://oss.carbou.me/MycilaTaskMonitor) project to monitor the stack usage.
|
||||||
|
3. You can change **if you know what you are doing** the task priority with `-D CONFIG_ASYNC_TCP_PRIORITY=10`.
|
||||||
|
Default is `10`.
|
||||||
|
4. You can increase the queue size with `-D CONFIG_ASYNC_TCP_QUEUE_SIZE=128`.
|
||||||
|
Default is `64`.
|
||||||
|
5. You can decrease the maximum ack time `-D CONFIG_ASYNC_TCP_MAX_ACK_TIME=3000`.
|
||||||
|
Default is `5000`.
|
||||||
|
|
||||||
|
I personally use the following configuration in my projects because my WS messages can be big (up to 4k).
|
||||||
|
If you have smaller messages, you can increase `WS_MAX_QUEUED_MESSAGES` to 128.
|
||||||
|
|
||||||
|
```c++
|
||||||
|
-D CONFIG_ASYNC_TCP_MAX_ACK_TIME=3000
|
||||||
|
-D CONFIG_ASYNC_TCP_PRIORITY=10
|
||||||
-D CONFIG_ASYNC_TCP_QUEUE_SIZE=128
|
-D CONFIG_ASYNC_TCP_QUEUE_SIZE=128
|
||||||
// Async TCP async task core
|
|
||||||
-D CONFIG_ASYNC_TCP_RUNNING_CORE=1
|
-D CONFIG_ASYNC_TCP_RUNNING_CORE=1
|
||||||
// Async TCP async stac ksize
|
-D CONFIG_ASYNC_TCP_STACK_SIZE=4096
|
||||||
-D CONFIG_ASYNC_TCP_STACK_SIZE=8096
|
|
||||||
// WebSocket queue size
|
|
||||||
-D WS_MAX_QUEUED_MESSAGES=64
|
-D WS_MAX_QUEUED_MESSAGES=64
|
||||||
```
|
```
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
# bundle exec jekyll serve --host=0.0.0.0
|
# bundle exec jekyll serve --host=0.0.0.0
|
||||||
|
|
||||||
title: ESP Async WebServer
|
title: ESP Async WebServer
|
||||||
description: "Asynchronous HTTP and WebSocket Server Library for ESP32"
|
description: "Asynchronous HTTP and WebSocket Server Library for ESP32, ESP8266 and RP2040"
|
||||||
remote_theme: pages-themes/cayman@v0.2.0
|
remote_theme: pages-themes/cayman@v0.2.0
|
||||||
plugins:
|
plugins:
|
||||||
- jekyll-remote-theme
|
- jekyll-remote-theme
|
||||||
|
@ -4,34 +4,46 @@
|
|||||||
[![Continuous Integration](https://github.com/mathieucarbou/ESPAsyncWebServer/actions/workflows/ci.yml/badge.svg)](https://github.com/mathieucarbou/ESPAsyncWebServer/actions/workflows/ci.yml)
|
[![Continuous Integration](https://github.com/mathieucarbou/ESPAsyncWebServer/actions/workflows/ci.yml/badge.svg)](https://github.com/mathieucarbou/ESPAsyncWebServer/actions/workflows/ci.yml)
|
||||||
[![PlatformIO Registry](https://badges.registry.platformio.org/packages/mathieucarbou/library/ESP%20Async%20WebServer.svg)](https://registry.platformio.org/libraries/mathieucarbou/ESP%20Async%20WebServer)
|
[![PlatformIO Registry](https://badges.registry.platformio.org/packages/mathieucarbou/library/ESP%20Async%20WebServer.svg)](https://registry.platformio.org/libraries/mathieucarbou/ESP%20Async%20WebServer)
|
||||||
|
|
||||||
Asynchronous HTTP and WebSocket Server Library for ESP32.
|
Asynchronous HTTP and WebSocket Server Library for ESP32, ESP8266 and RP2040
|
||||||
Supports: WebSocket, SSE, Authentication, Arduino Json 7, File Upload, Static File serving, URL Rewrite, URL Redirect, etc.
|
Supports: WebSocket, SSE, Authentication, Arduino Json 7, File Upload, Static File serving, URL Rewrite, URL Redirect, etc.
|
||||||
|
|
||||||
This fork is based on [yubox-node-org/ESPAsyncWebServer](https://github.com/yubox-node-org/ESPAsyncWebServer) and includes all the concurrency fixes.
|
This fork is based on [yubox-node-org/ESPAsyncWebServer](https://github.com/yubox-node-org/ESPAsyncWebServer) and includes all the concurrency fixes.
|
||||||
|
|
||||||
## Changes in this fork
|
## Changes in this fork
|
||||||
|
|
||||||
- Removed SPIFFSEditor
|
- [@ayushsharma82](https://github.com/ayushsharma82) and [@mathieucarbou](https://github.com/mathieucarbou): Add RP2040 support ([#31](https://github.com/mathieucarbou/ESPAsyncWebServer/pull/31))
|
||||||
- Deployed in PlatformIO registry and Arduino IDE library manager
|
- [@mathieucarbou](https://github.com/mathieucarbou): `SSE_MAX_QUEUED_MESSAGES` to control the maximum number of messages that can be queued for a SSE client
|
||||||
- CI
|
- [@mathieucarbou](https://github.com/mathieucarbou): `write()` function public in `AsyncEventSource.h`
|
||||||
- Some code cleanup
|
- [@mathieucarbou](https://github.com/mathieucarbou): `WS_MAX_QUEUED_MESSAGES`: control the maximum number of messages that can be queued for a Websocket client
|
||||||
- `SSE_MAX_QUEUED_MESSAGES` to control the maximum number of messages that can be queued for a SSE client
|
- [@mathieucarbou](https://github.com/mathieucarbou): Added `setAuthentication(const String& username, const String& password)`
|
||||||
- `write()` function public in `AsyncEventSource.h`
|
- [@mathieucarbou](https://github.com/mathieucarbou): Added `setCloseClientOnQueueFull(bool)` which can be set on a client to either close the connection or discard messages but not close the connection when the queue is full
|
||||||
- Arduino Json 7 compatibility and backward compatible with 6 and 6 (changes in `AsyncJson.h`). The API to use Json has not changed. These are only internal changes.
|
- [@mathieucarbou](https://github.com/mathieucarbou): Added `StreamConcat` example to show how to stream multiple files in one response
|
||||||
- `WS_MAX_QUEUED_MESSAGES`: control the maximum number of messages that can be queued for a Websocket client
|
- [@mathieucarbou](https://github.com/mathieucarbou): Added all flavors of `binary()`, `text()`, `binaryAll()` and `textAll()` in `AsyncWebSocket`
|
||||||
- Resurrected `AsyncWebSocketMessageBuffer` and `makeBuffer()` in order to make the fork API-compatible with the original library from me-no-dev regarding WebSocket.
|
- [@mathieucarbou](https://github.com/mathieucarbou): Arduino 3 / ESP-IDF 5.1 compatibility
|
||||||
- [#5](https://github.com/mathieucarbou/ESPAsyncWebServer/pull/5) ([@vortigont](https://github.com/vortigont)): set real "Last-Modified" header based on file's LastWrite time
|
- [@mathieucarbou](https://github.com/mathieucarbou): Arduino Json 7 compatibility and backward compatible with 6 and 6 (changes in `AsyncJson.h`). The API to use Json has not changed. These are only internal changes.
|
||||||
- [#13](https://github.com/mathieucarbou/ESPAsyncWebServer/pull/13) ([@tueddy](https://github.com/tueddy)): Compile with Arduino 3 (ESP-IDF 5.1)
|
- [@mathieucarbou](https://github.com/mathieucarbou): CI
|
||||||
- [#14](https://github.com/mathieucarbou/ESPAsyncWebServer/pull/14) ([@nilo85](https://github.com/nilo85)): Add support for Auth & GET requests in AsyncCallbackJsonWebHandler
|
- [@mathieucarbou](https://github.com/mathieucarbou): Depends on `mathieucarbou/Async TCP @ ^3.1.4`
|
||||||
- Added `setAuthentication(const String& username, const String& password)`
|
- [@mathieucarbou](https://github.com/mathieucarbou): Deployed in PlatformIO registry and Arduino IDE library manager
|
||||||
- Added `StreamConcat` example to show how to stream multiple files in one response
|
- [@mathieucarbou](https://github.com/mathieucarbou): Firmware size optimization: remove mbedtls dependency (accounts for 33KB in firmware)
|
||||||
- Remove filename after inline in Content-Disposition header according to RFC2183
|
- [@mathieucarbou](https://github.com/mathieucarbou): Made DEFAULT_MAX_SSE_CLIENTS customizable
|
||||||
- Depends on `mathieucarbou/Async TCP @ ^3.1.4`
|
- [@mathieucarbou](https://github.com/mathieucarbou): Made DEFAULT_MAX_WS_CLIENTS customizable
|
||||||
- Arduino 3 / ESP-IDF 5.1 compatibility
|
- [@mathieucarbou](https://github.com/mathieucarbou): Remove filename after inline in Content-Disposition header according to RFC2183
|
||||||
- Added all flavors of `binary()`, `text()`, `binaryAll()` and `textAll()` in `AsyncWebSocket`
|
- [@mathieucarbou](https://github.com/mathieucarbou): Removed SPIFFSEditor to reduce library size and maintainance. SPIFF si also deprecated. If you need it, please copy the files from the original repository in your project. This fork focus on maintaining the server part and the SPIFFEditor is an application which has nothing to do inside a server library.
|
||||||
- Added `setCloseClientOnQueueFull(bool)` which can be set on a client to either close the connection or discard messages but not close the connection when the queue is full
|
- [@mathieucarbou](https://github.com/mathieucarbou): Resurrected `AsyncWebSocketMessageBuffer` and `makeBuffer()` in order to make the fork API-compatible with the original library from me-no-dev regarding WebSocket.
|
||||||
- [#29](https://github.com/mathieucarbou/ESPAsyncWebServer/pull/29) ([@vortigont](https://github.com/vortigont)): Some websocket code cleanup
|
- [@mathieucarbou](https://github.com/mathieucarbou): Some code cleanup
|
||||||
- Use `-D DEFAULT_MAX_WS_CLIENTS` to change the number of allows WebSocket clients and use `cleanupClients()` to help cleanup resources about dead clients
|
- [@mathieucarbou](https://github.com/mathieucarbou): Use `-D DEFAULT_MAX_WS_CLIENTS` to change the number of allows WebSocket clients and use `cleanupClients()` to help cleanup resources about dead clients
|
||||||
|
- [@nilo85](https://github.com/nilo85): Add support for Auth & GET requests in AsyncCallbackJsonWebHandler ([#14](https://github.com/mathieucarbou/ESPAsyncWebServer/pull/14))
|
||||||
|
- [@p0p-x](https://github.com/p0p-x): ESP IDF Compatibility (added back CMakeLists.txt) ([#32](https://github.com/mathieucarbou/ESPAsyncWebServer/pull/32))
|
||||||
|
- [@tueddy](https://github.com/tueddy): Compile with Arduino 3 (ESP-IDF 5.1) ([#13](https://github.com/mathieucarbou/ESPAsyncWebServer/pull/13))
|
||||||
|
- [@vortigont](https://github.com/vortigont): Set real "Last-Modified" header based on file's LastWrite time ([#5](https://github.com/mathieucarbou/ESPAsyncWebServer/pull/5))
|
||||||
|
- [@vortigont](https://github.com/vortigont): Some websocket code cleanup ([#29](https://github.com/mathieucarbou/ESPAsyncWebServer/pull/29))
|
||||||
|
- [@vortigont](https://github.com/vortigont): Refactor code - replace DYI structs with STL objects ([#39](https://github.com/mathieucarbou/ESPAsyncWebServer/pull/39))
|
||||||
|
|
||||||
|
## Dependencies:
|
||||||
|
|
||||||
|
- **ESP32**: `mathieucarbou/Async TCP @ ^3.1.4` (Arduino IDE: [https://github.com/mathieucarbou/AsyncTCP#v3.1.4](https://github.com/mathieucarbou/AsyncTCP/releases/tag/v3.1.4))
|
||||||
|
- **ESP8266**: `esphome/ESPAsyncTCP-esphome @ 2.0.0` (Arduino IDE: [https://github.com/mathieucarbou/esphome-ESPAsyncTCP#v2.0.0](https://github.com/mathieucarbou/esphome-ESPAsyncTCP/releases/tag/v2.0.0))
|
||||||
|
- **RP2040**: `khoih-prog/AsyncTCP_RP2040W @ 1.2.0` (Arduino IDE: [https://github.com/khoih-prog/AsyncTCP_RP2040W#v1.2.0](https://github.com/khoih-prog/AsyncTCP_RP2040W/releases/tag/v1.2.0))
|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
|
|
||||||
@ -76,17 +88,30 @@ void send(JsonDocument& doc) {
|
|||||||
|
|
||||||
I recommend to use the official API `AsyncWebSocketMessageBuffer` to retain further compatibility.
|
I recommend to use the official API `AsyncWebSocketMessageBuffer` to retain further compatibility.
|
||||||
|
|
||||||
## Stack size and queues
|
## Important recommendations
|
||||||
|
|
||||||
Here are some important flags to tweak depending on your needs:
|
Most of the crashes are caused by improper configuration of the library for the project.
|
||||||
|
Here are some recommendations to avoid them.
|
||||||
|
|
||||||
```cpp
|
1. Set the running core to be on the same core of your application (usually core 1) `-D CONFIG_ASYNC_TCP_RUNNING_CORE=1`
|
||||||
// Async TCP queue size
|
2. Set the stack size appropriately with `-D CONFIG_ASYNC_TCP_STACK_SIZE=16384`.
|
||||||
|
The default value of `16384` might be too much for your project.
|
||||||
|
You can look at the [MycilaTaskMonitor](https://oss.carbou.me/MycilaTaskMonitor) project to monitor the stack usage.
|
||||||
|
3. You can change **if you know what you are doing** the task priority with `-D CONFIG_ASYNC_TCP_PRIORITY=10`.
|
||||||
|
Default is `10`.
|
||||||
|
4. You can increase the queue size with `-D CONFIG_ASYNC_TCP_QUEUE_SIZE=128`.
|
||||||
|
Default is `64`.
|
||||||
|
5. You can decrease the maximum ack time `-D CONFIG_ASYNC_TCP_MAX_ACK_TIME=3000`.
|
||||||
|
Default is `5000`.
|
||||||
|
|
||||||
|
I personally use the following configuration in my projects because my WS messages can be big (up to 4k).
|
||||||
|
If you have smaller messages, you can increase `WS_MAX_QUEUED_MESSAGES` to 128.
|
||||||
|
|
||||||
|
```c++
|
||||||
|
-D CONFIG_ASYNC_TCP_MAX_ACK_TIME=3000
|
||||||
|
-D CONFIG_ASYNC_TCP_PRIORITY=10
|
||||||
-D CONFIG_ASYNC_TCP_QUEUE_SIZE=128
|
-D CONFIG_ASYNC_TCP_QUEUE_SIZE=128
|
||||||
// Async TCP async task core
|
|
||||||
-D CONFIG_ASYNC_TCP_RUNNING_CORE=1
|
-D CONFIG_ASYNC_TCP_RUNNING_CORE=1
|
||||||
// Async TCP async stac ksize
|
-D CONFIG_ASYNC_TCP_STACK_SIZE=4096
|
||||||
-D CONFIG_ASYNC_TCP_STACK_SIZE=8096
|
|
||||||
// WebSocket queue size
|
|
||||||
-D WS_MAX_QUEUED_MESSAGES=64
|
-D WS_MAX_QUEUED_MESSAGES=64
|
||||||
```
|
```
|
||||||
|
@ -1,10 +1,13 @@
|
|||||||
#include <DNSServer.h>
|
#include <DNSServer.h>
|
||||||
#ifdef ESP32
|
#ifdef ESP32
|
||||||
#include <WiFi.h>
|
#include <AsyncTCP.h>
|
||||||
#include <AsyncTCP.h>
|
#include <WiFi.h>
|
||||||
#elif defined(ESP8266)
|
#elif defined(ESP8266)
|
||||||
#include <ESP8266WiFi.h>
|
#include <ESP8266WiFi.h>
|
||||||
#include <ESPAsyncTCP.h>
|
#include <ESPAsyncTCP.h>
|
||||||
|
#elif defined(TARGET_RP2040)
|
||||||
|
#include <WebServer.h>
|
||||||
|
#include <WiFi.h>
|
||||||
#endif
|
#endif
|
||||||
#include "ESPAsyncWebServer.h"
|
#include "ESPAsyncWebServer.h"
|
||||||
|
|
||||||
@ -12,43 +15,43 @@ DNSServer dnsServer;
|
|||||||
AsyncWebServer server(80);
|
AsyncWebServer server(80);
|
||||||
|
|
||||||
class CaptiveRequestHandler : public AsyncWebHandler {
|
class CaptiveRequestHandler : public AsyncWebHandler {
|
||||||
public:
|
public:
|
||||||
CaptiveRequestHandler() {}
|
CaptiveRequestHandler() {}
|
||||||
virtual ~CaptiveRequestHandler() {}
|
virtual ~CaptiveRequestHandler() {}
|
||||||
|
|
||||||
bool canHandle(__unused AsyncWebServerRequest *request){
|
bool canHandle(__unused AsyncWebServerRequest* request) {
|
||||||
//request->addInterestingHeader("ANY");
|
// request->addInterestingHeader("ANY");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void handleRequest(AsyncWebServerRequest *request) {
|
void handleRequest(AsyncWebServerRequest* request) {
|
||||||
AsyncResponseStream *response = request->beginResponseStream("text/html");
|
AsyncResponseStream* response = request->beginResponseStream("text/html");
|
||||||
response->print("<!DOCTYPE html><html><head><title>Captive Portal</title></head><body>");
|
response->print("<!DOCTYPE html><html><head><title>Captive Portal</title></head><body>");
|
||||||
response->print("<p>This is out captive portal front page.</p>");
|
response->print("<p>This is out captive portal front page.</p>");
|
||||||
response->printf("<p>You were trying to reach: http://%s%s</p>", request->host().c_str(), request->url().c_str());
|
response->printf("<p>You were trying to reach: http://%s%s</p>", request->host().c_str(), request->url().c_str());
|
||||||
response->printf("<p>Try opening <a href='http://%s'>this link</a> instead</p>", WiFi.softAPIP().toString().c_str());
|
response->printf("<p>Try opening <a href='http://%s'>this link</a> instead</p>", WiFi.softAPIP().toString().c_str());
|
||||||
response->print("</body></html>");
|
response->print("</body></html>");
|
||||||
request->send(response);
|
request->send(response);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
void setup() {
|
||||||
void setup(){
|
|
||||||
Serial.begin(115200);
|
Serial.begin(115200);
|
||||||
Serial.println();
|
Serial.println();
|
||||||
Serial.println("Configuring access point...");
|
Serial.println("Configuring access point...");
|
||||||
|
|
||||||
if (!WiFi.softAP("esp-captive")) {
|
if (!WiFi.softAP("esp-captive")) {
|
||||||
Serial.println("Soft AP creation failed.");
|
Serial.println("Soft AP creation failed.");
|
||||||
while (1);
|
while (1)
|
||||||
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
dnsServer.start(53, "*", WiFi.softAPIP());
|
dnsServer.start(53, "*", WiFi.softAPIP());
|
||||||
server.addHandler(new CaptiveRequestHandler()).setFilter(ON_AP_FILTER);//only when requested from AP
|
server.addHandler(new CaptiveRequestHandler()).setFilter(ON_AP_FILTER); // only when requested from AP
|
||||||
//more handlers...
|
// more handlers...
|
||||||
server.begin();
|
server.begin();
|
||||||
}
|
}
|
||||||
|
|
||||||
void loop(){
|
void loop() {
|
||||||
dnsServer.processNextRequest();
|
dnsServer.processNextRequest();
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
|
#include "mbedtls/md5.h"
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#include <MD5Builder.h>
|
#include <MD5Builder.h>
|
||||||
#include "mbedtls/md5.h"
|
|
||||||
|
|
||||||
void setup() {
|
void setup() {
|
||||||
Serial.begin(115200);
|
Serial.begin(115200);
|
||||||
|
@ -2,11 +2,14 @@
|
|||||||
|
|
||||||
#include <DNSServer.h>
|
#include <DNSServer.h>
|
||||||
#ifdef ESP32
|
#ifdef ESP32
|
||||||
#include <AsyncTCP.h>
|
#include <AsyncTCP.h>
|
||||||
#include <WiFi.h>
|
#include <WiFi.h>
|
||||||
#elif defined(ESP8266)
|
#elif defined(ESP8266)
|
||||||
#include <ESP8266WiFi.h>
|
#include <ESP8266WiFi.h>
|
||||||
#include <ESPAsyncTCP.h>
|
#include <ESPAsyncTCP.h>
|
||||||
|
#elif defined(TARGET_RP2040)
|
||||||
|
#include <WebServer.h>
|
||||||
|
#include <WiFi.h>
|
||||||
#endif
|
#endif
|
||||||
#include "ESPAsyncWebServer.h"
|
#include "ESPAsyncWebServer.h"
|
||||||
|
|
||||||
|
@ -7,11 +7,14 @@
|
|||||||
|
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#ifdef ESP32
|
#ifdef ESP32
|
||||||
#include <WiFi.h>
|
#include <AsyncTCP.h>
|
||||||
#include <AsyncTCP.h>
|
#include <WiFi.h>
|
||||||
#elif defined(ESP8266)
|
#elif defined(ESP8266)
|
||||||
#include <ESP8266WiFi.h>
|
#include <ESP8266WiFi.h>
|
||||||
#include <ESPAsyncTCP.h>
|
#include <ESPAsyncTCP.h>
|
||||||
|
#elif defined(TARGET_RP2040)
|
||||||
|
#include <WebServer.h>
|
||||||
|
#include <WiFi.h>
|
||||||
#endif
|
#endif
|
||||||
#include <ESPAsyncWebServer.h>
|
#include <ESPAsyncWebServer.h>
|
||||||
|
|
||||||
@ -22,52 +25,52 @@ const char* password = "YOUR_PASSWORD";
|
|||||||
|
|
||||||
const char* PARAM_MESSAGE = "message";
|
const char* PARAM_MESSAGE = "message";
|
||||||
|
|
||||||
void notFound(AsyncWebServerRequest *request) {
|
void notFound(AsyncWebServerRequest* request) {
|
||||||
request->send(404, "text/plain", "Not found");
|
request->send(404, "text/plain", "Not found");
|
||||||
}
|
}
|
||||||
|
|
||||||
void setup() {
|
void setup() {
|
||||||
|
|
||||||
Serial.begin(115200);
|
Serial.begin(115200);
|
||||||
WiFi.mode(WIFI_STA);
|
WiFi.mode(WIFI_STA);
|
||||||
WiFi.begin(ssid, password);
|
WiFi.begin(ssid, password);
|
||||||
if (WiFi.waitForConnectResult() != WL_CONNECTED) {
|
if (WiFi.waitForConnectResult() != WL_CONNECTED) {
|
||||||
Serial.printf("WiFi Failed!\n");
|
Serial.printf("WiFi Failed!\n");
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Serial.print("IP Address: ");
|
||||||
|
Serial.println(WiFi.localIP());
|
||||||
|
|
||||||
|
server.on("/", HTTP_GET, [](AsyncWebServerRequest* request) {
|
||||||
|
request->send(200, "text/plain", "Hello, world");
|
||||||
|
});
|
||||||
|
|
||||||
|
// Send a GET request to <IP>/get?message=<message>
|
||||||
|
server.on("/get", HTTP_GET, [](AsyncWebServerRequest* request) {
|
||||||
|
String message;
|
||||||
|
if (request->hasParam(PARAM_MESSAGE)) {
|
||||||
|
message = request->getParam(PARAM_MESSAGE)->value();
|
||||||
|
} else {
|
||||||
|
message = "No message sent";
|
||||||
}
|
}
|
||||||
|
request->send(200, "text/plain", "Hello, GET: " + message);
|
||||||
|
});
|
||||||
|
|
||||||
Serial.print("IP Address: ");
|
// Send a POST request to <IP>/post with a form field message set to <message>
|
||||||
Serial.println(WiFi.localIP());
|
server.on("/post", HTTP_POST, [](AsyncWebServerRequest* request) {
|
||||||
|
String message;
|
||||||
|
if (request->hasParam(PARAM_MESSAGE, true)) {
|
||||||
|
message = request->getParam(PARAM_MESSAGE, true)->value();
|
||||||
|
} else {
|
||||||
|
message = "No message sent";
|
||||||
|
}
|
||||||
|
request->send(200, "text/plain", "Hello, POST: " + message);
|
||||||
|
});
|
||||||
|
|
||||||
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
|
server.onNotFound(notFound);
|
||||||
request->send(200, "text/plain", "Hello, world");
|
|
||||||
});
|
|
||||||
|
|
||||||
// Send a GET request to <IP>/get?message=<message>
|
server.begin();
|
||||||
server.on("/get", HTTP_GET, [] (AsyncWebServerRequest *request) {
|
|
||||||
String message;
|
|
||||||
if (request->hasParam(PARAM_MESSAGE)) {
|
|
||||||
message = request->getParam(PARAM_MESSAGE)->value();
|
|
||||||
} else {
|
|
||||||
message = "No message sent";
|
|
||||||
}
|
|
||||||
request->send(200, "text/plain", "Hello, GET: " + message);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Send a POST request to <IP>/post with a form field message set to <message>
|
|
||||||
server.on("/post", HTTP_POST, [](AsyncWebServerRequest *request){
|
|
||||||
String message;
|
|
||||||
if (request->hasParam(PARAM_MESSAGE, true)) {
|
|
||||||
message = request->getParam(PARAM_MESSAGE, true)->value();
|
|
||||||
} else {
|
|
||||||
message = "No message sent";
|
|
||||||
}
|
|
||||||
request->send(200, "text/plain", "Hello, POST: " + message);
|
|
||||||
});
|
|
||||||
|
|
||||||
server.onNotFound(notFound);
|
|
||||||
|
|
||||||
server.begin();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void loop() {
|
void loop() {
|
||||||
|
@ -17,7 +17,11 @@ class StreamConcat : public Stream {
|
|||||||
return c != -1 ? c : _s2->read();
|
return c != -1 ? c : _s2->read();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if defined(TARGET_RP2040)
|
||||||
|
size_t readBytes(char* buffer, size_t length) {
|
||||||
|
#else
|
||||||
size_t readBytes(char* buffer, size_t length) override {
|
size_t readBytes(char* buffer, size_t length) override {
|
||||||
|
#endif
|
||||||
size_t count = _s1->readBytes(buffer, length);
|
size_t count = _s1->readBytes(buffer, length);
|
||||||
return count > 0 ? count : _s2->readBytes(buffer, length);
|
return count > 0 ? count : _s2->readBytes(buffer, length);
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,14 @@
|
|||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#include <DNSServer.h>
|
#include <DNSServer.h>
|
||||||
#ifdef ESP32
|
#ifdef ESP32
|
||||||
#include <AsyncTCP.h>
|
#include <AsyncTCP.h>
|
||||||
#include <WiFi.h>
|
#include <WiFi.h>
|
||||||
#elif defined(ESP8266)
|
#elif defined(ESP8266)
|
||||||
#include <ESP8266WiFi.h>
|
#include <ESP8266WiFi.h>
|
||||||
#include <ESPAsyncTCP.h>
|
#include <ESPAsyncTCP.h>
|
||||||
|
#elif defined(TARGET_RP2040)
|
||||||
|
#include <WebServer.h>
|
||||||
|
#include <WiFi.h>
|
||||||
#endif
|
#endif
|
||||||
#include "StreamConcat.h"
|
#include "StreamConcat.h"
|
||||||
#include "StreamString.h"
|
#include "StreamString.h"
|
||||||
@ -42,7 +45,11 @@ void setup() {
|
|||||||
StreamConcat stream1(&header, &body);
|
StreamConcat stream1(&header, &body);
|
||||||
|
|
||||||
StreamString content;
|
StreamString content;
|
||||||
|
#if defined(TARGET_RP2040)
|
||||||
|
content.printf("FreeHeap: %d", rp2040.getFreeHeap());
|
||||||
|
#else
|
||||||
content.printf("FreeHeap: %" PRIu32, ESP.getFreeHeap());
|
content.printf("FreeHeap: %" PRIu32, ESP.getFreeHeap());
|
||||||
|
#endif
|
||||||
StreamConcat stream2 = StreamConcat(&stream1, &content);
|
StreamConcat stream2 = StreamConcat(&stream1, &content);
|
||||||
|
|
||||||
File footer = LittleFS.open("/footer.html", "r");
|
File footer = LittleFS.open("/footer.html", "r");
|
||||||
@ -67,7 +74,11 @@ void loop() {
|
|||||||
// dnsServer.processNextRequest();
|
// dnsServer.processNextRequest();
|
||||||
|
|
||||||
if (millis() - last > 2000) {
|
if (millis() - last > 2000) {
|
||||||
|
#if defined(TARGET_RP2040)
|
||||||
|
Serial.printf("FreeHeap: %d", rp2040.getFreeHeap());
|
||||||
|
#else
|
||||||
Serial.printf("FreeHeap: %" PRIu32, ESP.getFreeHeap());
|
Serial.printf("FreeHeap: %" PRIu32, ESP.getFreeHeap());
|
||||||
|
#endif
|
||||||
last = millis();
|
last = millis();
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -18,7 +18,11 @@ class StreamString : public Stream {
|
|||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if defined(TARGET_RP2040)
|
||||||
|
size_t readBytes(char* buffer, size_t length) {
|
||||||
|
#else
|
||||||
size_t readBytes(char* buffer, size_t length) override {
|
size_t readBytes(char* buffer, size_t length) override {
|
||||||
|
#endif
|
||||||
if (length > _buffer.length())
|
if (length > _buffer.length())
|
||||||
length = _buffer.length();
|
length = _buffer.length();
|
||||||
// Don't use _str.ToCharArray() because it inserts a terminator
|
// Don't use _str.ToCharArray() because it inserts a terminator
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "ESP Async WebServer",
|
"name": "ESP Async WebServer",
|
||||||
"version": "2.10.8",
|
"version": "3.0.6",
|
||||||
"description": "Asynchronous HTTP and WebSocket Server Library for ESP32. Supports: WebSocket, SSE, Authentication, Arduino Json 7, File Upload, Static File serving, URL Rewrite, URL Redirect, etc.",
|
"description": "Asynchronous HTTP and WebSocket Server Library for ESP32, ESP8266 and RP2040. Supports: WebSocket, SSE, Authentication, Arduino Json 7, File Upload, Static File serving, URL Rewrite, URL Redirect, etc.",
|
||||||
"keywords": "http,async,websocket,webserver",
|
"keywords": "http,async,websocket,webserver",
|
||||||
"homepage": "https://github.com/mathieucarbou/ESPAsyncWebServer",
|
"homepage": "https://github.com/mathieucarbou/ESPAsyncWebServer",
|
||||||
"repository": {
|
"repository": {
|
||||||
@ -21,7 +21,8 @@
|
|||||||
"frameworks": "arduino",
|
"frameworks": "arduino",
|
||||||
"platforms": [
|
"platforms": [
|
||||||
"espressif32",
|
"espressif32",
|
||||||
"espressif8266"
|
"espressif8266",
|
||||||
|
"raspberrypi"
|
||||||
],
|
],
|
||||||
"dependencies": [
|
"dependencies": [
|
||||||
{
|
{
|
||||||
@ -39,6 +40,12 @@
|
|||||||
{
|
{
|
||||||
"name": "Hash",
|
"name": "Hash",
|
||||||
"platforms": "espressif8266"
|
"platforms": "espressif8266"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"owner": "khoih-prog",
|
||||||
|
"name": "AsyncTCP_RP2040W",
|
||||||
|
"version": "^1.2.0",
|
||||||
|
"platforms": "raspberrypi"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"export": {
|
"export": {
|
||||||
@ -50,5 +57,8 @@
|
|||||||
"LICENSE",
|
"LICENSE",
|
||||||
"README.md"
|
"README.md"
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
"build": {
|
||||||
|
"libCompatMode": "strict"
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,10 +1,10 @@
|
|||||||
name=ESP Async WebServer
|
name=ESP Async WebServer
|
||||||
version=2.10.8
|
version=3.0.6
|
||||||
author=Me-No-Dev
|
author=Me-No-Dev
|
||||||
maintainer=Mathieu Carbou <mathieu.carbou@gmail.com>
|
maintainer=Mathieu Carbou <mathieu.carbou@gmail.com>
|
||||||
sentence=Asynchronous HTTP and WebSocket Server Library for ESP32
|
sentence=Asynchronous HTTP and WebSocket Server Library for ESP32, ESP8266 and RP2040
|
||||||
paragraph=Supports: WebSocket, SSE, Authentication, Arduino Json 7, File Upload, Static File serving, URL Rewrite, URL Redirect, etc
|
paragraph=Supports: WebSocket, SSE, Authentication, Arduino Json 7, File Upload, Static File serving, URL Rewrite, URL Redirect, etc
|
||||||
category=Other
|
category=Other
|
||||||
url=https://github.com/mathieucarbou/ESPAsyncWebServer
|
url=https://github.com/mathieucarbou/ESPAsyncWebServer
|
||||||
architectures=esp8266,esp32
|
architectures=*
|
||||||
license=LGPL-3.0
|
license=LGPL-3.0
|
||||||
|
@ -4,42 +4,57 @@ build_flags =
|
|||||||
-Wall -Wextra
|
-Wall -Wextra
|
||||||
-D CONFIG_ARDUHAL_LOG_COLORS
|
-D CONFIG_ARDUHAL_LOG_COLORS
|
||||||
-D CORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_VERBOSE
|
-D CORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_VERBOSE
|
||||||
lib_deps =
|
|
||||||
bblanchon/ArduinoJson @ 7.0.4
|
|
||||||
mathieucarbou/Async TCP @ ^3.1.4
|
|
||||||
; https://github.com/mathieucarbou/AsyncTCP
|
|
||||||
; https://github.com/me-no-dev/AsyncTCP
|
|
||||||
esphome/ESPAsyncTCP-esphome @ 2.0.0
|
|
||||||
upload_protocol = esptool
|
upload_protocol = esptool
|
||||||
monitor_speed = 115200
|
monitor_speed = 115200
|
||||||
monitor_filters = esp32_exception_decoder, log2file
|
monitor_filters = esp32_exception_decoder, log2file
|
||||||
|
|
||||||
[platformio]
|
[platformio]
|
||||||
lib_dir = .
|
lib_dir = .
|
||||||
src_dir = examples/CaptivePortal
|
; src_dir = examples/CaptivePortal
|
||||||
; src_dir = examples/SimpleServer
|
; src_dir = examples/SimpleServer
|
||||||
; src_dir = examples/StreamFiles
|
src_dir = examples/StreamFiles
|
||||||
; src_dir = examples/Filters
|
; src_dir = examples/Filters
|
||||||
; src_dir = examples/Draft
|
; src_dir = examples/Draft
|
||||||
|
|
||||||
[env:arduino]
|
[env:arduino]
|
||||||
platform = espressif32
|
platform = espressif32
|
||||||
board = esp32dev
|
board = esp32dev
|
||||||
|
lib_deps =
|
||||||
|
bblanchon/ArduinoJson @ 7.1.0
|
||||||
|
mathieucarbou/Async TCP @ ^3.1.4
|
||||||
|
|
||||||
[env:arduino-2]
|
[env:arduino-2]
|
||||||
platform = espressif32@6.7.0
|
platform = espressif32@6.7.0
|
||||||
board = esp32dev
|
board = esp32dev
|
||||||
|
lib_deps =
|
||||||
|
bblanchon/ArduinoJson @ 7.1.0
|
||||||
|
mathieucarbou/Async TCP @ ^3.1.4
|
||||||
|
|
||||||
[env:arduino-3]
|
[env:arduino-3]
|
||||||
platform = espressif32
|
platform = espressif32
|
||||||
platform_packages=
|
platform_packages=
|
||||||
platformio/framework-arduinoespressif32 @ https://github.com/espressif/arduino-esp32.git#3.0.1
|
platformio/framework-arduinoespressif32 @ https://github.com/espressif/arduino-esp32.git#3.0.2
|
||||||
platformio/framework-arduinoespressif32-libs @ https://github.com/espressif/arduino-esp32/releases/download/3.0.1/esp32-arduino-libs-3.0.1.zip
|
platformio/framework-arduinoespressif32-libs @ https://github.com/espressif/arduino-esp32/releases/download/3.0.2/esp32-arduino-libs-3.0.2.zip
|
||||||
board = esp32dev
|
board = esp32dev
|
||||||
|
lib_deps =
|
||||||
|
bblanchon/ArduinoJson @ 7.1.0
|
||||||
|
mathieucarbou/Async TCP @ ^3.1.4
|
||||||
|
|
||||||
[env:esp8266]
|
[env:esp8266]
|
||||||
platform = espressif8266
|
platform = espressif8266
|
||||||
board = huzzah
|
board = huzzah
|
||||||
lib_deps =
|
lib_deps =
|
||||||
bblanchon/ArduinoJson @ 7.0.4
|
bblanchon/ArduinoJson @ 7.1.0
|
||||||
esphome/ESPAsyncTCP-esphome @ 2.0.0
|
esphome/ESPAsyncTCP-esphome @ 2.0.0
|
||||||
|
|
||||||
|
; PlatformIO support for Raspberry Pi Pico is not official
|
||||||
|
; https://github.com/platformio/platform-raspberrypi/pull/36
|
||||||
|
; https://github.com/earlephilhower/arduino-pico/blob/master/docs/platformio.rst
|
||||||
|
; board settings: https://github.com/earlephilhower/arduino-pico/blob/master/tools/json/rpipico.json
|
||||||
|
[env:rpipicow]
|
||||||
|
upload_protocol = picotool
|
||||||
|
platform = https://github.com/maxgerhardt/platform-raspberrypi.git
|
||||||
|
board = rpipicow
|
||||||
|
lib_deps =
|
||||||
|
bblanchon/ArduinoJson @ 7.1.0
|
||||||
|
khoih-prog/AsyncTCP_RP2040W @ 1.2.0
|
||||||
|
@ -18,43 +18,43 @@
|
|||||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
*/
|
*/
|
||||||
#include "Arduino.h"
|
#include "Arduino.h"
|
||||||
#include "AsyncEventSource.h"
|
#if defined(ESP32)
|
||||||
#ifndef ESP8266
|
|
||||||
#include <rom/ets_sys.h>
|
#include <rom/ets_sys.h>
|
||||||
#endif
|
#endif
|
||||||
|
#include "AsyncEventSource.h"
|
||||||
|
|
||||||
static String generateEventMessage(const char *message, const char *event, uint32_t id, uint32_t reconnect){
|
static String generateEventMessage(const char* message, const char* event, uint32_t id, uint32_t reconnect) {
|
||||||
String ev;
|
String ev;
|
||||||
|
|
||||||
if(reconnect){
|
if (reconnect) {
|
||||||
ev += F("retry: ");
|
ev += F("retry: ");
|
||||||
ev += reconnect;
|
ev += reconnect;
|
||||||
ev += F("\r\n");
|
ev += F("\r\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
if(id){
|
if (id) {
|
||||||
ev += F("id: ");
|
ev += F("id: ");
|
||||||
ev += String(id);
|
ev += String(id);
|
||||||
ev += F("\r\n");
|
ev += F("\r\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
if(event != NULL){
|
if (event != NULL) {
|
||||||
ev += F("event: ");
|
ev += F("event: ");
|
||||||
ev += String(event);
|
ev += String(event);
|
||||||
ev += F("\r\n");
|
ev += F("\r\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
if(message != NULL){
|
if (message != NULL) {
|
||||||
size_t messageLen = strlen(message);
|
size_t messageLen = strlen(message);
|
||||||
char * lineStart = (char *)message;
|
char* lineStart = (char*)message;
|
||||||
char * lineEnd;
|
char* lineEnd;
|
||||||
do {
|
do {
|
||||||
char * nextN = strchr(lineStart, '\n');
|
char* nextN = strchr(lineStart, '\n');
|
||||||
char * nextR = strchr(lineStart, '\r');
|
char* nextR = strchr(lineStart, '\r');
|
||||||
if(nextN == NULL && nextR == NULL){
|
if (nextN == NULL && nextR == NULL) {
|
||||||
size_t llen = ((char *)message + messageLen) - lineStart;
|
size_t llen = ((char*)message + messageLen) - lineStart;
|
||||||
char * ldata = (char *)malloc(llen+1);
|
char* ldata = (char*)malloc(llen + 1);
|
||||||
if(ldata != NULL){
|
if (ldata != NULL) {
|
||||||
memcpy(ldata, lineStart, llen);
|
memcpy(ldata, lineStart, llen);
|
||||||
ldata[llen] = 0;
|
ldata[llen] = 0;
|
||||||
ev += F("data: ");
|
ev += F("data: ");
|
||||||
@ -62,24 +62,24 @@ static String generateEventMessage(const char *message, const char *event, uint3
|
|||||||
ev += F("\r\n\r\n");
|
ev += F("\r\n\r\n");
|
||||||
free(ldata);
|
free(ldata);
|
||||||
}
|
}
|
||||||
lineStart = (char *)message + messageLen;
|
lineStart = (char*)message + messageLen;
|
||||||
} else {
|
} else {
|
||||||
char * nextLine = NULL;
|
char* nextLine = NULL;
|
||||||
if(nextN != NULL && nextR != NULL){
|
if (nextN != NULL && nextR != NULL) {
|
||||||
if(nextR < nextN){
|
if (nextR < nextN) {
|
||||||
lineEnd = nextR;
|
lineEnd = nextR;
|
||||||
if(nextN == (nextR + 1))
|
if (nextN == (nextR + 1))
|
||||||
nextLine = nextN + 1;
|
nextLine = nextN + 1;
|
||||||
else
|
else
|
||||||
nextLine = nextR + 1;
|
nextLine = nextR + 1;
|
||||||
} else {
|
} else {
|
||||||
lineEnd = nextN;
|
lineEnd = nextN;
|
||||||
if(nextR == (nextN + 1))
|
if (nextR == (nextN + 1))
|
||||||
nextLine = nextR + 1;
|
nextLine = nextR + 1;
|
||||||
else
|
else
|
||||||
nextLine = nextN + 1;
|
nextLine = nextN + 1;
|
||||||
}
|
}
|
||||||
} else if(nextN != NULL){
|
} else if (nextN != NULL) {
|
||||||
lineEnd = nextN;
|
lineEnd = nextN;
|
||||||
nextLine = nextN + 1;
|
nextLine = nextN + 1;
|
||||||
} else {
|
} else {
|
||||||
@ -88,8 +88,8 @@ static String generateEventMessage(const char *message, const char *event, uint3
|
|||||||
}
|
}
|
||||||
|
|
||||||
size_t llen = lineEnd - lineStart;
|
size_t llen = lineEnd - lineStart;
|
||||||
char * ldata = (char *)malloc(llen+1);
|
char* ldata = (char*)malloc(llen + 1);
|
||||||
if(ldata != NULL){
|
if (ldata != NULL) {
|
||||||
memcpy(ldata, lineStart, llen);
|
memcpy(ldata, lineStart, llen);
|
||||||
ldata[llen] = 0;
|
ldata[llen] = 0;
|
||||||
ev += F("data: ");
|
ev += F("data: ");
|
||||||
@ -98,10 +98,10 @@ static String generateEventMessage(const char *message, const char *event, uint3
|
|||||||
free(ldata);
|
free(ldata);
|
||||||
}
|
}
|
||||||
lineStart = nextLine;
|
lineStart = nextLine;
|
||||||
if(lineStart == ((char *)message + messageLen))
|
if (lineStart == ((char*)message + messageLen))
|
||||||
ev += F("\r\n");
|
ev += F("\r\n");
|
||||||
}
|
}
|
||||||
} while(lineStart < ((char *)message + messageLen));
|
} while (lineStart < ((char*)message + messageLen));
|
||||||
}
|
}
|
||||||
|
|
||||||
return ev;
|
return ev;
|
||||||
@ -109,11 +109,10 @@ static String generateEventMessage(const char *message, const char *event, uint3
|
|||||||
|
|
||||||
// Message
|
// Message
|
||||||
|
|
||||||
AsyncEventSourceMessage::AsyncEventSourceMessage(const char * data, size_t len)
|
AsyncEventSourceMessage::AsyncEventSourceMessage(const char* data, size_t len)
|
||||||
: _data(nullptr), _len(len), _sent(0), _acked(0)
|
: _data(nullptr), _len(len), _sent(0), _acked(0) {
|
||||||
{
|
_data = (uint8_t*)malloc(_len + 1);
|
||||||
_data = (uint8_t*)malloc(_len+1);
|
if (_data == nullptr) {
|
||||||
if(_data == nullptr){
|
|
||||||
_len = 0;
|
_len = 0;
|
||||||
} else {
|
} else {
|
||||||
memcpy(_data, data, len);
|
memcpy(_data, data, len);
|
||||||
@ -122,18 +121,18 @@ AsyncEventSourceMessage::AsyncEventSourceMessage(const char * data, size_t len)
|
|||||||
}
|
}
|
||||||
|
|
||||||
AsyncEventSourceMessage::~AsyncEventSourceMessage() {
|
AsyncEventSourceMessage::~AsyncEventSourceMessage() {
|
||||||
if(_data != NULL)
|
if (_data != NULL)
|
||||||
free(_data);
|
free(_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t AsyncEventSourceMessage::ack(size_t len, uint32_t time) {
|
size_t AsyncEventSourceMessage::ack(size_t len, uint32_t time) {
|
||||||
(void)time;
|
(void)time;
|
||||||
// If the whole message is now acked...
|
// If the whole message is now acked...
|
||||||
if(_acked + len > _len){
|
if (_acked + len > _len) {
|
||||||
// Return the number of extra bytes acked (they will be carried on to the next message)
|
// Return the number of extra bytes acked (they will be carried on to the next message)
|
||||||
const size_t extra = _acked + len - _len;
|
const size_t extra = _acked + len - _len;
|
||||||
_acked = _len;
|
_acked = _len;
|
||||||
return extra;
|
return extra;
|
||||||
}
|
}
|
||||||
// Return that no extra bytes left.
|
// Return that no extra bytes left.
|
||||||
_acked += len;
|
_acked += len;
|
||||||
@ -142,188 +141,173 @@ size_t AsyncEventSourceMessage::ack(size_t len, uint32_t time) {
|
|||||||
|
|
||||||
// This could also return void as the return value is not used.
|
// This could also return void as the return value is not used.
|
||||||
// Leaving as-is for compatibility...
|
// Leaving as-is for compatibility...
|
||||||
size_t AsyncEventSourceMessage::send(AsyncClient *client) {
|
size_t AsyncEventSourceMessage::send(AsyncClient* client) {
|
||||||
if (_sent >= _len) {
|
if (_sent >= _len) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
const size_t len_to_send = _len - _sent;
|
const size_t len_to_send = _len - _sent;
|
||||||
auto position = reinterpret_cast<const char*>(_data + _sent);
|
auto position = reinterpret_cast<const char*>(_data + _sent);
|
||||||
const size_t sent_now = client->write(position, len_to_send);
|
const size_t sent_now = client->write(position, len_to_send);
|
||||||
_sent += sent_now;
|
_sent += sent_now;
|
||||||
return sent_now;
|
return sent_now;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Client
|
// Client
|
||||||
|
|
||||||
AsyncEventSourceClient::AsyncEventSourceClient(AsyncWebServerRequest *request, AsyncEventSource *server)
|
AsyncEventSourceClient::AsyncEventSourceClient(AsyncWebServerRequest* request, AsyncEventSource* server) {
|
||||||
: _messageQueue(LinkedList<AsyncEventSourceMessage *>([](AsyncEventSourceMessage *m){ delete m; }))
|
|
||||||
{
|
|
||||||
_client = request->client();
|
_client = request->client();
|
||||||
_server = server;
|
_server = server;
|
||||||
_lastId = 0;
|
_lastId = 0;
|
||||||
if(request->hasHeader(F("Last-Event-ID")))
|
if (request->hasHeader(F("Last-Event-ID")))
|
||||||
_lastId = atoi(request->getHeader(F("Last-Event-ID"))->value().c_str());
|
_lastId = atoi(request->getHeader(F("Last-Event-ID"))->value().c_str());
|
||||||
|
|
||||||
_client->setRxTimeout(0);
|
_client->setRxTimeout(0);
|
||||||
_client->onError(NULL, NULL);
|
_client->onError(NULL, NULL);
|
||||||
_client->onAck([](void *r, AsyncClient* c, size_t len, uint32_t time){ (void)c; ((AsyncEventSourceClient*)(r))->_onAck(len, time); }, this);
|
_client->onAck([](void* r, AsyncClient* c, size_t len, uint32_t time) { (void)c; ((AsyncEventSourceClient*)(r))->_onAck(len, time); }, this);
|
||||||
_client->onPoll([](void *r, AsyncClient* c){ (void)c; ((AsyncEventSourceClient*)(r))->_onPoll(); }, this);
|
_client->onPoll([](void* r, AsyncClient* c) { (void)c; ((AsyncEventSourceClient*)(r))->_onPoll(); }, this);
|
||||||
_client->onData(NULL, NULL);
|
_client->onData(NULL, NULL);
|
||||||
_client->onTimeout([this](void *r, AsyncClient* c __attribute__((unused)), uint32_t time){ ((AsyncEventSourceClient*)(r))->_onTimeout(time); }, this);
|
_client->onTimeout([this](void* r, AsyncClient* c __attribute__((unused)), uint32_t time) { ((AsyncEventSourceClient*)(r))->_onTimeout(time); }, this);
|
||||||
_client->onDisconnect([this](void *r, AsyncClient* c){ ((AsyncEventSourceClient*)(r))->_onDisconnect(); delete c; }, this);
|
_client->onDisconnect([this](void* r, AsyncClient* c) { ((AsyncEventSourceClient*)(r))->_onDisconnect(); delete c; }, this);
|
||||||
|
|
||||||
_server->_addClient(this);
|
_server->_addClient(this);
|
||||||
delete request;
|
delete request;
|
||||||
}
|
}
|
||||||
|
|
||||||
AsyncEventSourceClient::~AsyncEventSourceClient(){
|
AsyncEventSourceClient::~AsyncEventSourceClient() {
|
||||||
_lockmq.lock();
|
#ifdef ESP32
|
||||||
_messageQueue.free();
|
std::lock_guard<std::mutex> lock(_lockmq);
|
||||||
_lockmq.unlock();
|
#endif
|
||||||
|
_messageQueue.clear();
|
||||||
close();
|
close();
|
||||||
}
|
}
|
||||||
|
|
||||||
void AsyncEventSourceClient::_queueMessage(AsyncEventSourceMessage *dataMessage){
|
void AsyncEventSourceClient::_queueMessage(const char* message, size_t len) {
|
||||||
if(dataMessage == NULL)
|
#ifdef ESP32
|
||||||
return;
|
// length() is not thread-safe, thus acquiring the lock before this call..
|
||||||
if(!connected()){
|
std::lock_guard<std::mutex> lock(_lockmq);
|
||||||
delete dataMessage;
|
#endif
|
||||||
return;
|
|
||||||
}
|
if (_messageQueue.size() >= SSE_MAX_QUEUED_MESSAGES) {
|
||||||
//length() is not thread-safe, thus acquiring the lock before this call..
|
|
||||||
_lockmq.lock();
|
|
||||||
if(_messageQueue.length() >= SSE_MAX_QUEUED_MESSAGES){
|
|
||||||
#ifdef ESP8266
|
#ifdef ESP8266
|
||||||
ets_printf(String(F("ERROR: Too many messages queued\n")).c_str());
|
ets_printf(String(F("ERROR: Too many messages queued\n")).c_str());
|
||||||
#else
|
#elif defined(ESP32)
|
||||||
log_e("Too many messages queued: deleting message");
|
log_e("Too many messages queued: deleting message");
|
||||||
#endif
|
#endif
|
||||||
delete dataMessage;
|
return;
|
||||||
} else {
|
|
||||||
_messageQueue.add(dataMessage);
|
|
||||||
// runqueue trigger when new messages added
|
|
||||||
if(_client->canSend()) {
|
|
||||||
_runQueue();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
_lockmq.unlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
void AsyncEventSourceClient::_onAck(size_t len, uint32_t time){
|
_messageQueue.emplace_back(message, len);
|
||||||
// Same here, acquiring the lock early
|
// runqueue trigger when new messages added
|
||||||
_lockmq.lock();
|
if (_client->canSend()) {
|
||||||
while(len && !_messageQueue.isEmpty()){
|
|
||||||
len = _messageQueue.front()->ack(len, time);
|
|
||||||
if(_messageQueue.front()->finished())
|
|
||||||
_messageQueue.remove(_messageQueue.front());
|
|
||||||
}
|
|
||||||
_runQueue();
|
|
||||||
_lockmq.unlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
void AsyncEventSourceClient::_onPoll(){
|
|
||||||
_lockmq.lock();
|
|
||||||
if(!_messageQueue.isEmpty()){
|
|
||||||
_runQueue();
|
_runQueue();
|
||||||
}
|
}
|
||||||
_lockmq.unlock();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AsyncEventSourceClient::_onTimeout(uint32_t time __attribute__((unused))){
|
void AsyncEventSourceClient::_onAck(size_t len, uint32_t time) {
|
||||||
|
#ifdef ESP32
|
||||||
|
// Same here, acquiring the lock early
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AsyncEventSourceClient::_onPoll() {
|
||||||
|
#ifdef ESP32
|
||||||
|
// Same here, acquiring the lock early
|
||||||
|
std::lock_guard<std::mutex> lock(_lockmq);
|
||||||
|
#endif
|
||||||
|
if (_messageQueue.size()) {
|
||||||
|
_runQueue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AsyncEventSourceClient::_onTimeout(uint32_t time __attribute__((unused))) {
|
||||||
_client->close(true);
|
_client->close(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AsyncEventSourceClient::_onDisconnect(){
|
void AsyncEventSourceClient::_onDisconnect() {
|
||||||
_client = NULL;
|
_client = NULL;
|
||||||
_server->_handleDisconnect(this);
|
_server->_handleDisconnect(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AsyncEventSourceClient::close(){
|
void AsyncEventSourceClient::close() {
|
||||||
if(_client != NULL)
|
if (_client != NULL)
|
||||||
_client->close();
|
_client->close();
|
||||||
}
|
}
|
||||||
|
|
||||||
void AsyncEventSourceClient::write(const char * message, size_t len){
|
void AsyncEventSourceClient::write(const char* message, size_t len) {
|
||||||
_queueMessage(new AsyncEventSourceMessage(message, len));
|
if (!connected())
|
||||||
|
return;
|
||||||
|
_queueMessage(message, len);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AsyncEventSourceClient::send(const char *message, const char *event, uint32_t id, uint32_t reconnect){
|
void AsyncEventSourceClient::send(const char* message, const char* event, uint32_t id, uint32_t reconnect) {
|
||||||
|
if (!connected())
|
||||||
|
return;
|
||||||
String ev = generateEventMessage(message, event, id, reconnect);
|
String ev = generateEventMessage(message, event, id, reconnect);
|
||||||
_queueMessage(new AsyncEventSourceMessage(ev.c_str(), ev.length()));
|
_queueMessage(ev.c_str(), ev.length());
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t AsyncEventSourceClient::packetsWaiting() const {
|
size_t AsyncEventSourceClient::packetsWaiting() const {
|
||||||
size_t len;
|
#ifdef ESP32
|
||||||
_lockmq.lock();
|
std::lock_guard<std::mutex> lock(_lockmq);
|
||||||
len = _messageQueue.length();
|
#endif
|
||||||
_lockmq.unlock();
|
return _messageQueue.size();
|
||||||
return len;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AsyncEventSourceClient::_runQueue() {
|
void AsyncEventSourceClient::_runQueue() {
|
||||||
// Calls to this private method now already protected by _lockmq acquisition
|
// Calls to this private method now already protected by _lockmq acquisition
|
||||||
// so no extra call of _lockmq.lock() here..
|
// so no extra call of _lockmq.lock() here..
|
||||||
for (auto i = _messageQueue.begin(); i != _messageQueue.end(); ++i) {
|
for (auto& i : _messageQueue) {
|
||||||
// If it crashes here, iterator (i) has been invalidated as _messageQueue
|
if (!i.sent())
|
||||||
// has been changed... (UL 2020-11-15: Not supposed to happen any more ;-) )
|
i.send(_client);
|
||||||
if (!(*i)->sent()) {
|
|
||||||
(*i)->send(_client);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Handler
|
// Handler
|
||||||
|
void AsyncEventSource::onConnect(ArEventHandlerFunction cb) {
|
||||||
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;
|
_connectcb = cb;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AsyncEventSource::authorizeConnect(ArAuthorizeConnectHandler cb){
|
void AsyncEventSource::authorizeConnect(ArAuthorizeConnectHandler cb) {
|
||||||
_authorizeConnectHandler = cb;
|
_authorizeConnectHandler = cb;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AsyncEventSource::_addClient(AsyncEventSourceClient * client){
|
void AsyncEventSource::_addClient(AsyncEventSourceClient* client) {
|
||||||
/*char * temp = (char *)malloc(2054);
|
if (!client)
|
||||||
if(temp != NULL){
|
return;
|
||||||
memset(temp+1,' ',2048);
|
#ifdef ESP32
|
||||||
temp[0] = ':';
|
std::lock_guard<std::mutex> lock(_client_queue_lock);
|
||||||
temp[2049] = '\r';
|
#endif
|
||||||
temp[2050] = '\n';
|
_clients.emplace_back(client);
|
||||||
temp[2051] = '\r';
|
if (_connectcb)
|
||||||
temp[2052] = '\n';
|
|
||||||
temp[2053] = 0;
|
|
||||||
client->write((const char *)temp, 2053);
|
|
||||||
free(temp);
|
|
||||||
}*/
|
|
||||||
AsyncWebLockGuard l(_client_queue_lock);
|
|
||||||
_clients.add(client);
|
|
||||||
if(_connectcb)
|
|
||||||
_connectcb(client);
|
_connectcb(client);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AsyncEventSource::_handleDisconnect(AsyncEventSourceClient * client){
|
void AsyncEventSource::_handleDisconnect(AsyncEventSourceClient* client) {
|
||||||
AsyncWebLockGuard l(_client_queue_lock);
|
#ifdef ESP32
|
||||||
_clients.remove(client);
|
std::lock_guard<std::mutex> lock(_client_queue_lock);
|
||||||
|
#endif
|
||||||
|
for (auto i = _clients.begin(); i != _clients.end(); ++i) {
|
||||||
|
if (i->get() == client)
|
||||||
|
_clients.erase(i);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AsyncEventSource::close(){
|
void AsyncEventSource::close() {
|
||||||
// While the whole loop is not done, the linked list is locked and so the
|
// While the whole loop is not done, the linked list is locked and so the
|
||||||
// iterator should remain valid even when AsyncEventSource::_handleDisconnect()
|
// iterator should remain valid even when AsyncEventSource::_handleDisconnect()
|
||||||
// is called very early
|
// is called very early
|
||||||
AsyncWebLockGuard l(_client_queue_lock);
|
#ifdef ESP32
|
||||||
for(const auto &c: _clients){
|
std::lock_guard<std::mutex> lock(_client_queue_lock);
|
||||||
if(c->connected())
|
#endif
|
||||||
|
for (const auto& c : _clients) {
|
||||||
|
if (c->connected())
|
||||||
c->close();
|
c->close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -332,41 +316,48 @@ void AsyncEventSource::close(){
|
|||||||
size_t AsyncEventSource::avgPacketsWaiting() const {
|
size_t AsyncEventSource::avgPacketsWaiting() const {
|
||||||
size_t aql = 0;
|
size_t aql = 0;
|
||||||
uint32_t nConnectedClients = 0;
|
uint32_t nConnectedClients = 0;
|
||||||
AsyncWebLockGuard l(_client_queue_lock);
|
#ifdef ESP32
|
||||||
if (_clients.isEmpty()) {
|
std::lock_guard<std::mutex> lock(_client_queue_lock);
|
||||||
|
#endif
|
||||||
|
if (!_clients.size())
|
||||||
return 0;
|
return 0;
|
||||||
}
|
|
||||||
for(const auto &c: _clients){
|
for (const auto& c : _clients) {
|
||||||
if(c->connected()) {
|
if (c->connected()) {
|
||||||
aql += c->packetsWaiting();
|
aql += c->packetsWaiting();
|
||||||
++nConnectedClients;
|
++nConnectedClients;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ((aql) + (nConnectedClients/2)) / (nConnectedClients); // round up
|
return ((aql) + (nConnectedClients / 2)) / (nConnectedClients); // round up
|
||||||
}
|
}
|
||||||
|
|
||||||
void AsyncEventSource::send(
|
void AsyncEventSource::send(
|
||||||
const char *message, const char *event, uint32_t id, uint32_t reconnect){
|
const char* message, const char* event, uint32_t id, uint32_t reconnect) {
|
||||||
String ev = generateEventMessage(message, event, id, reconnect);
|
String ev = generateEventMessage(message, event, id, reconnect);
|
||||||
AsyncWebLockGuard l(_client_queue_lock);
|
#ifdef ESP32
|
||||||
for(const auto &c: _clients){
|
std::lock_guard<std::mutex> lock(_client_queue_lock);
|
||||||
if(c->connected()) {
|
#endif
|
||||||
|
for (const auto& c : _clients) {
|
||||||
|
if (c->connected()) {
|
||||||
c->write(ev.c_str(), ev.length());
|
c->write(ev.c_str(), ev.length());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t AsyncEventSource::count() const {
|
size_t AsyncEventSource::count() const {
|
||||||
size_t n_clients;
|
#ifdef ESP32
|
||||||
AsyncWebLockGuard l(_client_queue_lock);
|
std::lock_guard<std::mutex> lock(_client_queue_lock);
|
||||||
n_clients = _clients.count_if([](AsyncEventSourceClient *c){
|
#endif
|
||||||
return c->connected();
|
size_t n_clients{0};
|
||||||
});
|
for (const auto& i : _clients)
|
||||||
|
if (i->connected())
|
||||||
|
++n_clients;
|
||||||
|
|
||||||
return n_clients;
|
return n_clients;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AsyncEventSource::canHandle(AsyncWebServerRequest *request){
|
bool AsyncEventSource::canHandle(AsyncWebServerRequest* request) {
|
||||||
if(request->method() != HTTP_GET || !request->url().equals(_url)) {
|
if (request->method() != HTTP_GET || !request->url().equals(_url)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
request->addInterestingHeader(F("Last-Event-ID"));
|
request->addInterestingHeader(F("Last-Event-ID"));
|
||||||
@ -374,12 +365,12 @@ bool AsyncEventSource::canHandle(AsyncWebServerRequest *request){
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AsyncEventSource::handleRequest(AsyncWebServerRequest *request){
|
void AsyncEventSource::handleRequest(AsyncWebServerRequest* request) {
|
||||||
if((_username.length() && _password.length()) && !request->authenticate(_username.c_str(), _password.c_str())) {
|
if ((_username.length() && _password.length()) && !request->authenticate(_username.c_str(), _password.c_str())) {
|
||||||
return request->requestAuthentication();
|
return request->requestAuthentication();
|
||||||
}
|
}
|
||||||
if(_authorizeConnectHandler != NULL){
|
if (_authorizeConnectHandler != NULL) {
|
||||||
if(!_authorizeConnectHandler(request)){
|
if (!_authorizeConnectHandler(request)) {
|
||||||
return request->send(401);
|
return request->send(401);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -388,7 +379,7 @@ void AsyncEventSource::handleRequest(AsyncWebServerRequest *request){
|
|||||||
|
|
||||||
// Response
|
// Response
|
||||||
|
|
||||||
AsyncEventSourceResponse::AsyncEventSourceResponse(AsyncEventSource *server){
|
AsyncEventSourceResponse::AsyncEventSourceResponse(AsyncEventSource* server) {
|
||||||
_server = server;
|
_server = server;
|
||||||
_code = 200;
|
_code = 200;
|
||||||
_contentType = F("text/event-stream");
|
_contentType = F("text/event-stream");
|
||||||
@ -397,16 +388,15 @@ AsyncEventSourceResponse::AsyncEventSourceResponse(AsyncEventSource *server){
|
|||||||
addHeader(F("Connection"), F("keep-alive"));
|
addHeader(F("Connection"), F("keep-alive"));
|
||||||
}
|
}
|
||||||
|
|
||||||
void AsyncEventSourceResponse::_respond(AsyncWebServerRequest *request){
|
void AsyncEventSourceResponse::_respond(AsyncWebServerRequest* request) {
|
||||||
String out = _assembleHead(request->version());
|
String out = _assembleHead(request->version());
|
||||||
request->client()->write(out.c_str(), _headLength);
|
request->client()->write(out.c_str(), _headLength);
|
||||||
_state = RESPONSE_WAIT_ACK;
|
_state = RESPONSE_WAIT_ACK;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t AsyncEventSourceResponse::_ack(AsyncWebServerRequest *request, size_t len, uint32_t time __attribute__((unused))){
|
size_t AsyncEventSourceResponse::_ack(AsyncWebServerRequest* request, size_t len, uint32_t time __attribute__((unused))) {
|
||||||
if(len){
|
if (len) {
|
||||||
new AsyncEventSourceClient(request, _server);
|
new AsyncEventSourceClient(request, _server);
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -21,129 +21,138 @@
|
|||||||
#define ASYNCEVENTSOURCE_H_
|
#define ASYNCEVENTSOURCE_H_
|
||||||
|
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
|
#include <list>
|
||||||
#ifdef ESP32
|
#ifdef ESP32
|
||||||
#include <AsyncTCP.h>
|
#include <AsyncTCP.h>
|
||||||
#ifndef SSE_MAX_QUEUED_MESSAGES
|
#include <mutex>
|
||||||
#define SSE_MAX_QUEUED_MESSAGES 32
|
#ifndef SSE_MAX_QUEUED_MESSAGES
|
||||||
#endif
|
#define SSE_MAX_QUEUED_MESSAGES 32
|
||||||
#else
|
#endif
|
||||||
#include <ESPAsyncTCP.h>
|
#elif defined(ESP8266)
|
||||||
#ifndef SSE_MAX_QUEUED_MESSAGES
|
#include <ESPAsyncTCP.h>
|
||||||
#define SSE_MAX_QUEUED_MESSAGES 8
|
#ifndef SSE_MAX_QUEUED_MESSAGES
|
||||||
#endif
|
#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
|
#endif
|
||||||
|
|
||||||
#include <ESPAsyncWebServer.h>
|
#include <ESPAsyncWebServer.h>
|
||||||
|
|
||||||
#include "AsyncWebSynchronization.h"
|
|
||||||
|
|
||||||
#ifdef ESP8266
|
#ifdef ESP8266
|
||||||
#include <Hash.h>
|
#include <Hash.h>
|
||||||
#ifdef CRYPTO_HASH_h // include Hash.h from espressif framework if the first include was from the crypto library
|
#ifdef CRYPTO_HASH_h // include Hash.h from espressif framework if the first include was from the crypto library
|
||||||
#include <../src/Hash.h>
|
#include <../src/Hash.h>
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef DEFAULT_MAX_SSE_CLIENTS
|
#ifndef DEFAULT_MAX_SSE_CLIENTS
|
||||||
#ifdef ESP32
|
#ifdef ESP32
|
||||||
#define DEFAULT_MAX_SSE_CLIENTS 8
|
#define DEFAULT_MAX_SSE_CLIENTS 8
|
||||||
#else
|
#else
|
||||||
#define DEFAULT_MAX_SSE_CLIENTS 4
|
#define DEFAULT_MAX_SSE_CLIENTS 4
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
class AsyncEventSource;
|
class AsyncEventSource;
|
||||||
class AsyncEventSourceResponse;
|
class AsyncEventSourceResponse;
|
||||||
class AsyncEventSourceClient;
|
class AsyncEventSourceClient;
|
||||||
typedef std::function<void(AsyncEventSourceClient *client)> ArEventHandlerFunction;
|
using ArEventHandlerFunction = std::function<void(AsyncEventSourceClient* client)>;
|
||||||
typedef std::function<bool(AsyncWebServerRequest *request)> ArAuthorizeConnectHandler;
|
using ArAuthorizeConnectHandler = std::function<bool(AsyncWebServerRequest* request)>;
|
||||||
|
|
||||||
class AsyncEventSourceMessage {
|
class AsyncEventSourceMessage {
|
||||||
private:
|
private:
|
||||||
uint8_t * _data;
|
uint8_t* _data;
|
||||||
size_t _len;
|
size_t _len;
|
||||||
size_t _sent;
|
size_t _sent;
|
||||||
//size_t _ack;
|
// size_t _ack;
|
||||||
size_t _acked;
|
size_t _acked;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
AsyncEventSourceMessage(const char * data, size_t len);
|
AsyncEventSourceMessage(const char* data, size_t len);
|
||||||
~AsyncEventSourceMessage();
|
~AsyncEventSourceMessage();
|
||||||
size_t ack(size_t len, uint32_t time __attribute__((unused)));
|
size_t ack(size_t len, uint32_t time __attribute__((unused)));
|
||||||
size_t send(AsyncClient *client);
|
size_t send(AsyncClient* client);
|
||||||
bool finished(){ return _acked == _len; }
|
bool finished() { return _acked == _len; }
|
||||||
bool sent() { return _sent == _len; }
|
bool sent() { return _sent == _len; }
|
||||||
};
|
};
|
||||||
|
|
||||||
class AsyncEventSourceClient {
|
class AsyncEventSourceClient {
|
||||||
private:
|
private:
|
||||||
AsyncClient *_client;
|
AsyncClient* _client;
|
||||||
AsyncEventSource *_server;
|
AsyncEventSource* _server;
|
||||||
uint32_t _lastId;
|
uint32_t _lastId;
|
||||||
LinkedList<AsyncEventSourceMessage *> _messageQueue;
|
std::list<AsyncEventSourceMessage> _messageQueue;
|
||||||
// ArFi 2020-08-27 for protecting/serializing _messageQueue
|
#ifdef ESP32
|
||||||
AsyncPlainLock _lockmq;
|
mutable std::mutex _lockmq;
|
||||||
void _queueMessage(AsyncEventSourceMessage *dataMessage);
|
#endif
|
||||||
|
void _queueMessage(const char* message, size_t len);
|
||||||
void _runQueue();
|
void _runQueue();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
AsyncEventSourceClient(AsyncWebServerRequest* request, AsyncEventSource* server);
|
||||||
AsyncEventSourceClient(AsyncWebServerRequest *request, AsyncEventSource *server);
|
|
||||||
~AsyncEventSourceClient();
|
~AsyncEventSourceClient();
|
||||||
|
|
||||||
AsyncClient* client(){ return _client; }
|
AsyncClient* client() { return _client; }
|
||||||
void close();
|
void close();
|
||||||
void write(const char * message, size_t len);
|
void write(const char* message, size_t len);
|
||||||
void send(const char *message, const char *event=NULL, uint32_t id=0, uint32_t reconnect=0);
|
void send(const char* message, const char* event = NULL, uint32_t id = 0, uint32_t reconnect = 0);
|
||||||
bool connected() const { return (_client != NULL) && _client->connected(); }
|
bool connected() const { return (_client != NULL) && _client->connected(); }
|
||||||
uint32_t lastId() const { return _lastId; }
|
uint32_t lastId() const { return _lastId; }
|
||||||
size_t packetsWaiting() const;
|
size_t packetsWaiting() const;
|
||||||
|
|
||||||
//system callbacks (do not call)
|
// system callbacks (do not call)
|
||||||
void _onAck(size_t len, uint32_t time);
|
void _onAck(size_t len, uint32_t time);
|
||||||
void _onPoll();
|
void _onPoll();
|
||||||
void _onTimeout(uint32_t time);
|
void _onTimeout(uint32_t time);
|
||||||
void _onDisconnect();
|
void _onDisconnect();
|
||||||
};
|
};
|
||||||
|
|
||||||
class AsyncEventSource: public AsyncWebHandler {
|
class AsyncEventSource : public AsyncWebHandler {
|
||||||
private:
|
private:
|
||||||
String _url;
|
String _url;
|
||||||
LinkedList<AsyncEventSourceClient *> _clients;
|
std::list<std::unique_ptr<AsyncEventSourceClient>> _clients;
|
||||||
|
#ifdef ESP32
|
||||||
// Same as for individual messages, protect mutations of _clients list
|
// Same as for individual messages, protect mutations of _clients list
|
||||||
// since simultaneous access from different tasks is possible
|
// since simultaneous access from different tasks is possible
|
||||||
AsyncWebLock _client_queue_lock;
|
mutable std::mutex _client_queue_lock;
|
||||||
ArEventHandlerFunction _connectcb;
|
#endif
|
||||||
ArAuthorizeConnectHandler _authorizeConnectHandler;
|
ArEventHandlerFunction _connectcb{nullptr};
|
||||||
public:
|
ArAuthorizeConnectHandler _authorizeConnectHandler;
|
||||||
AsyncEventSource(const String& url);
|
|
||||||
~AsyncEventSource();
|
|
||||||
|
|
||||||
const char * url() const { return _url.c_str(); }
|
public:
|
||||||
|
AsyncEventSource(const String& url) : _url(url){};
|
||||||
|
~AsyncEventSource() { close(); };
|
||||||
|
|
||||||
|
const char* url() const { return _url.c_str(); }
|
||||||
void close();
|
void close();
|
||||||
void onConnect(ArEventHandlerFunction cb);
|
void onConnect(ArEventHandlerFunction cb);
|
||||||
void authorizeConnect(ArAuthorizeConnectHandler cb);
|
void authorizeConnect(ArAuthorizeConnectHandler cb);
|
||||||
void send(const char *message, const char *event=NULL, uint32_t id=0, uint32_t reconnect=0);
|
void send(const char* message, const char* event = NULL, uint32_t id = 0, uint32_t reconnect = 0);
|
||||||
// number of clients connected
|
// number of clients connected
|
||||||
size_t count() const;
|
size_t count() const;
|
||||||
size_t avgPacketsWaiting() const;
|
size_t avgPacketsWaiting() const;
|
||||||
|
|
||||||
//system callbacks (do not call)
|
// system callbacks (do not call)
|
||||||
void _addClient(AsyncEventSourceClient * client);
|
void _addClient(AsyncEventSourceClient* client);
|
||||||
void _handleDisconnect(AsyncEventSourceClient * client);
|
void _handleDisconnect(AsyncEventSourceClient* client);
|
||||||
virtual bool canHandle(AsyncWebServerRequest *request) override final;
|
virtual bool canHandle(AsyncWebServerRequest* request) override final;
|
||||||
virtual void handleRequest(AsyncWebServerRequest *request) override final;
|
virtual void handleRequest(AsyncWebServerRequest* request) override final;
|
||||||
};
|
};
|
||||||
|
|
||||||
class AsyncEventSourceResponse: public AsyncWebServerResponse {
|
class AsyncEventSourceResponse : public AsyncWebServerResponse {
|
||||||
private:
|
private:
|
||||||
String _content;
|
String _content;
|
||||||
AsyncEventSource *_server;
|
AsyncEventSource* _server;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
AsyncEventSourceResponse(AsyncEventSource *server);
|
AsyncEventSourceResponse(AsyncEventSource* server);
|
||||||
void _respond(AsyncWebServerRequest *request);
|
void _respond(AsyncWebServerRequest* request);
|
||||||
size_t _ack(AsyncWebServerRequest *request, size_t len, uint32_t time);
|
size_t _ack(AsyncWebServerRequest* request, size_t len, uint32_t time);
|
||||||
bool _sourceValid() const { return true; }
|
bool _sourceValid() const { return true; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
#endif /* ASYNCEVENTSOURCE_H_ */
|
#endif /* ASYNCEVENTSOURCE_H_ */
|
||||||
|
@ -39,9 +39,9 @@
|
|||||||
#include <Print.h>
|
#include <Print.h>
|
||||||
|
|
||||||
#if ARDUINOJSON_VERSION_MAJOR == 6
|
#if ARDUINOJSON_VERSION_MAJOR == 6
|
||||||
#ifndef DYNAMIC_JSON_DOCUMENT_SIZE
|
#ifndef DYNAMIC_JSON_DOCUMENT_SIZE
|
||||||
#define DYNAMIC_JSON_DOCUMENT_SIZE 1024
|
#define DYNAMIC_JSON_DOCUMENT_SIZE 1024
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
constexpr const char* JSON_MIMETYPE = "application/json";
|
constexpr const char* JSON_MIMETYPE = "application/json";
|
||||||
@ -120,7 +120,6 @@ class AsyncJsonResponse : public AsyncAbstractResponse {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
~AsyncJsonResponse() {}
|
|
||||||
JsonVariant& getRoot() { return _root; }
|
JsonVariant& getRoot() { return _root; }
|
||||||
bool _sourceValid() const { return _isValid; }
|
bool _sourceValid() const { return _isValid; }
|
||||||
size_t setLength() {
|
size_t setLength() {
|
||||||
@ -230,7 +229,7 @@ class AsyncCallbackJsonWebHandler : public AsyncWebHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
virtual void handleRequest(AsyncWebServerRequest* request) override final {
|
virtual void handleRequest(AsyncWebServerRequest* request) override final {
|
||||||
if((_username != "" && _password != "") && !request->authenticate(_username.c_str(), _password.c_str()))
|
if ((_username != "" && _password != "") && !request->authenticate(_username.c_str(), _password.c_str()))
|
||||||
return request->requestAuthentication();
|
return request->requestAuthentication();
|
||||||
if (_onRequest) {
|
if (_onRequest) {
|
||||||
if (request->method() == HTTP_GET) {
|
if (request->method() == HTTP_GET) {
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -23,37 +23,42 @@
|
|||||||
|
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#ifdef ESP32
|
#ifdef ESP32
|
||||||
#include <AsyncTCP.h>
|
#include <AsyncTCP.h>
|
||||||
#ifndef WS_MAX_QUEUED_MESSAGES
|
#include <mutex>
|
||||||
#define WS_MAX_QUEUED_MESSAGES 32
|
#ifndef WS_MAX_QUEUED_MESSAGES
|
||||||
#endif
|
#define WS_MAX_QUEUED_MESSAGES 32
|
||||||
#else
|
#endif
|
||||||
#include <ESPAsyncTCP.h>
|
#elif defined(ESP8266)
|
||||||
#ifndef WS_MAX_QUEUED_MESSAGES
|
#include <ESPAsyncTCP.h>
|
||||||
#define WS_MAX_QUEUED_MESSAGES 8
|
#ifndef WS_MAX_QUEUED_MESSAGES
|
||||||
#endif
|
#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
|
#endif
|
||||||
|
|
||||||
#include <ESPAsyncWebServer.h>
|
#include <ESPAsyncWebServer.h>
|
||||||
|
|
||||||
#include "AsyncWebSynchronization.h"
|
|
||||||
|
|
||||||
#include <list>
|
|
||||||
#include <deque>
|
#include <deque>
|
||||||
|
#include <list>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
#ifdef ESP8266
|
#ifdef ESP8266
|
||||||
#include <Hash.h>
|
#include <Hash.h>
|
||||||
#ifdef CRYPTO_HASH_h // include Hash.h from espressif framework if the first include was from the crypto library
|
#ifdef CRYPTO_HASH_h // include Hash.h from espressif framework if the first include was from the crypto library
|
||||||
#include <../src/Hash.h>
|
#include <../src/Hash.h>
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef DEFAULT_MAX_WS_CLIENTS
|
#ifndef DEFAULT_MAX_WS_CLIENTS
|
||||||
#ifdef ESP32
|
#ifdef ESP32
|
||||||
#define DEFAULT_MAX_WS_CLIENTS 8
|
#define DEFAULT_MAX_WS_CLIENTS 8
|
||||||
#else
|
#else
|
||||||
#define DEFAULT_MAX_WS_CLIENTS 4
|
#define DEFAULT_MAX_WS_CLIENTS 4
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
using AsyncWebSocketSharedBuffer = std::shared_ptr<std::vector<uint8_t>>;
|
using AsyncWebSocketSharedBuffer = std::shared_ptr<std::vector<uint8_t>>;
|
||||||
@ -87,20 +92,33 @@ typedef struct {
|
|||||||
uint64_t index;
|
uint64_t index;
|
||||||
} AwsFrameInfo;
|
} AwsFrameInfo;
|
||||||
|
|
||||||
typedef enum { WS_DISCONNECTED, WS_CONNECTED, WS_DISCONNECTING } AwsClientStatus;
|
typedef enum { WS_DISCONNECTED,
|
||||||
typedef enum { WS_CONTINUATION, WS_TEXT, WS_BINARY, WS_DISCONNECT = 0x08, WS_PING, WS_PONG } AwsFrameType;
|
WS_CONNECTED,
|
||||||
typedef enum { WS_MSG_SENDING, WS_MSG_SENT, WS_MSG_ERROR } AwsMessageStatus;
|
WS_DISCONNECTING } AwsClientStatus;
|
||||||
typedef enum { WS_EVT_CONNECT, WS_EVT_DISCONNECT, WS_EVT_PONG, WS_EVT_ERROR, WS_EVT_DATA } AwsEventType;
|
typedef enum { WS_CONTINUATION,
|
||||||
|
WS_TEXT,
|
||||||
|
WS_BINARY,
|
||||||
|
WS_DISCONNECT = 0x08,
|
||||||
|
WS_PING,
|
||||||
|
WS_PONG } AwsFrameType;
|
||||||
|
typedef enum { WS_MSG_SENDING,
|
||||||
|
WS_MSG_SENT,
|
||||||
|
WS_MSG_ERROR } AwsMessageStatus;
|
||||||
|
typedef enum { WS_EVT_CONNECT,
|
||||||
|
WS_EVT_DISCONNECT,
|
||||||
|
WS_EVT_PONG,
|
||||||
|
WS_EVT_ERROR,
|
||||||
|
WS_EVT_DATA } AwsEventType;
|
||||||
|
|
||||||
class AsyncWebSocketMessageBuffer {
|
class AsyncWebSocketMessageBuffer {
|
||||||
friend AsyncWebSocket;
|
friend AsyncWebSocket;
|
||||||
friend AsyncWebSocketClient;
|
friend AsyncWebSocketClient;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
AsyncWebSocketSharedBuffer _buffer;
|
AsyncWebSocketSharedBuffer _buffer;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
AsyncWebSocketMessageBuffer(){}
|
AsyncWebSocketMessageBuffer() {}
|
||||||
explicit AsyncWebSocketMessageBuffer(size_t size);
|
explicit AsyncWebSocketMessageBuffer(size_t size);
|
||||||
AsyncWebSocketMessageBuffer(const uint8_t* data, size_t size);
|
AsyncWebSocketMessageBuffer(const uint8_t* data, size_t size);
|
||||||
//~AsyncWebSocketMessageBuffer();
|
//~AsyncWebSocketMessageBuffer();
|
||||||
@ -109,9 +127,8 @@ class AsyncWebSocketMessageBuffer {
|
|||||||
size_t length() const { return _buffer->size(); }
|
size_t length() const { return _buffer->size(); }
|
||||||
};
|
};
|
||||||
|
|
||||||
class AsyncWebSocketMessage
|
class AsyncWebSocketMessage {
|
||||||
{
|
private:
|
||||||
private:
|
|
||||||
AsyncWebSocketSharedBuffer _WSbuffer;
|
AsyncWebSocketSharedBuffer _WSbuffer;
|
||||||
uint8_t _opcode{WS_TEXT};
|
uint8_t _opcode{WS_TEXT};
|
||||||
bool _mask{false};
|
bool _mask{false};
|
||||||
@ -120,25 +137,25 @@ private:
|
|||||||
size_t _ack{};
|
size_t _ack{};
|
||||||
size_t _acked{};
|
size_t _acked{};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
AsyncWebSocketMessage(AsyncWebSocketSharedBuffer buffer, uint8_t opcode=WS_TEXT, bool mask=false);
|
AsyncWebSocketMessage(AsyncWebSocketSharedBuffer buffer, uint8_t opcode = WS_TEXT, bool mask = false);
|
||||||
|
|
||||||
bool finished() const { return _status != WS_MSG_SENDING; }
|
bool finished() const { return _status != WS_MSG_SENDING; }
|
||||||
bool betweenFrames() const { return _acked == _ack; }
|
bool betweenFrames() const { return _acked == _ack; }
|
||||||
|
|
||||||
void ack(size_t len, uint32_t time);
|
void ack(size_t len, uint32_t time);
|
||||||
size_t send(AsyncClient *client);
|
size_t send(AsyncClient* client);
|
||||||
};
|
};
|
||||||
|
|
||||||
class AsyncWebSocketClient {
|
class AsyncWebSocketClient {
|
||||||
private:
|
private:
|
||||||
AsyncClient *_client;
|
AsyncClient* _client;
|
||||||
AsyncWebSocket *_server;
|
AsyncWebSocket* _server;
|
||||||
uint32_t _clientId;
|
uint32_t _clientId;
|
||||||
AwsClientStatus _status;
|
AwsClientStatus _status;
|
||||||
|
#ifdef ESP32
|
||||||
AsyncWebLock _lock;
|
mutable std::mutex _lock;
|
||||||
|
#endif
|
||||||
std::deque<AsyncWebSocketControl> _controlQueue;
|
std::deque<AsyncWebSocketControl> _controlQueue;
|
||||||
std::deque<AsyncWebSocketMessage> _messageQueue;
|
std::deque<AsyncWebSocketMessage> _messageQueue;
|
||||||
bool closeWhenFull = true;
|
bool closeWhenFull = true;
|
||||||
@ -149,223 +166,225 @@ class AsyncWebSocketClient {
|
|||||||
uint32_t _lastMessageTime;
|
uint32_t _lastMessageTime;
|
||||||
uint32_t _keepAlivePeriod;
|
uint32_t _keepAlivePeriod;
|
||||||
|
|
||||||
void _queueControl(uint8_t opcode, const uint8_t *data=NULL, size_t len=0, bool mask=false);
|
void _queueControl(uint8_t opcode, const uint8_t* data = NULL, size_t len = 0, bool mask = false);
|
||||||
void _queueMessage(AsyncWebSocketSharedBuffer buffer, uint8_t opcode=WS_TEXT, bool mask=false);
|
void _queueMessage(AsyncWebSocketSharedBuffer buffer, uint8_t opcode = WS_TEXT, bool mask = false);
|
||||||
void _runQueue();
|
void _runQueue();
|
||||||
void _clearQueue();
|
void _clearQueue();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void *_tempObject;
|
void* _tempObject;
|
||||||
|
|
||||||
AsyncWebSocketClient(AsyncWebServerRequest *request, AsyncWebSocket *server);
|
AsyncWebSocketClient(AsyncWebServerRequest* request, AsyncWebSocket* server);
|
||||||
~AsyncWebSocketClient();
|
~AsyncWebSocketClient();
|
||||||
|
|
||||||
//client id increments for the given server
|
// client id increments for the given server
|
||||||
uint32_t id() const { return _clientId; }
|
uint32_t id() const { return _clientId; }
|
||||||
AwsClientStatus status() const { return _status; }
|
AwsClientStatus status() const { return _status; }
|
||||||
AsyncClient* client() { return _client; }
|
AsyncClient* client() { return _client; }
|
||||||
const AsyncClient* client() const { return _client; }
|
const AsyncClient* client() const { return _client; }
|
||||||
AsyncWebSocket *server(){ return _server; }
|
AsyncWebSocket* server() { return _server; }
|
||||||
const AsyncWebSocket *server() const { return _server; }
|
const AsyncWebSocket* server() const { return _server; }
|
||||||
AwsFrameInfo const &pinfo() const { return _pinfo; }
|
AwsFrameInfo const& pinfo() const { return _pinfo; }
|
||||||
|
|
||||||
// - If "true" (default), the connection will be closed if the message queue is full.
|
// - If "true" (default), the connection will be closed if the message queue is full.
|
||||||
// This is the default behavior in yubox-node-org, which is not silently discarding messages but instead closes the connection.
|
// This is the default behavior in yubox-node-org, which is not silently discarding messages but instead closes the connection.
|
||||||
// The big issue with this behavior is that is can cause the UI to automatically re-create a new WS connection, which can be filled again,
|
// The big issue with this behavior is that is can cause the UI to automatically re-create a new WS connection, which can be filled again,
|
||||||
// and so on, causing a resource exhaustion.
|
// and so on, causing a resource exhaustion.
|
||||||
//
|
//
|
||||||
// - If "false", the incoming message will be discarded if the queue is full.
|
// - If "false", the incoming message will be discarded if the queue is full.
|
||||||
// This is the default behavior in the original ESPAsyncWebServer library from me-no-dev.
|
// This is the default behavior in the original ESPAsyncWebServer library from me-no-dev.
|
||||||
// This behavior allows the best performance at the expense of unreliable message delivery in case the queue is full (some messages may be lost).
|
// This behavior allows the best performance at the expense of unreliable message delivery in case the queue is full (some messages may be lost).
|
||||||
//
|
//
|
||||||
// - In any case, when the queue is full, a message is logged.
|
// - In any case, when the queue is full, a message is logged.
|
||||||
// - IT is recommended to use the methods queueIsFull(), availableForWriteAll(), availableForWrite(clientId) to check if the queue is full before sending a message.
|
// - IT is recommended to use the methods queueIsFull(), availableForWriteAll(), availableForWrite(clientId) to check if the queue is full before sending a message.
|
||||||
//
|
//
|
||||||
// Usage:
|
// Usage:
|
||||||
// - can be set in the onEvent listener when connecting (event type is: WS_EVT_CONNECT)
|
// - can be set in the onEvent listener when connecting (event type is: WS_EVT_CONNECT)
|
||||||
//
|
//
|
||||||
// Use cases:,
|
// Use cases:,
|
||||||
// - if using websocket to send logging messages, maybe some loss is acceptable.
|
// - if using websocket to send logging messages, maybe some loss is acceptable.
|
||||||
// - But if using websocket to send UI update messages, maybe the connection should be closed and the UI redrawn.
|
// - But if using websocket to send UI update messages, maybe the connection should be closed and the UI redrawn.
|
||||||
void setCloseClientOnQueueFull(bool close) { closeWhenFull = close; }
|
void setCloseClientOnQueueFull(bool close) { closeWhenFull = close; }
|
||||||
bool willCloseClientOnQueueFull() const { return closeWhenFull; }
|
bool willCloseClientOnQueueFull() const { return closeWhenFull; }
|
||||||
|
|
||||||
IPAddress remoteIP() const;
|
IPAddress remoteIP() const;
|
||||||
uint16_t remotePort() const;
|
uint16_t remotePort() const;
|
||||||
|
|
||||||
bool shouldBeDeleted() const { return !_client; }
|
bool shouldBeDeleted() const { return !_client; }
|
||||||
|
|
||||||
//control frames
|
// control frames
|
||||||
void close(uint16_t code=0, const char * message=NULL);
|
void close(uint16_t code = 0, const char* message = NULL);
|
||||||
void ping(const uint8_t *data=NULL, size_t len=0);
|
void ping(const uint8_t* data = NULL, size_t len = 0);
|
||||||
|
|
||||||
//set auto-ping period in seconds. disabled if zero (default)
|
// set auto-ping period in seconds. disabled if zero (default)
|
||||||
void keepAlivePeriod(uint16_t seconds){
|
void keepAlivePeriod(uint16_t seconds) {
|
||||||
_keepAlivePeriod = seconds * 1000;
|
_keepAlivePeriod = seconds * 1000;
|
||||||
}
|
}
|
||||||
uint16_t keepAlivePeriod(){
|
uint16_t keepAlivePeriod() {
|
||||||
return (uint16_t)(_keepAlivePeriod / 1000);
|
return (uint16_t)(_keepAlivePeriod / 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
//data packets
|
// data packets
|
||||||
void message(AsyncWebSocketSharedBuffer buffer, uint8_t opcode=WS_TEXT, bool mask=false) { _queueMessage(buffer, opcode, mask); }
|
void message(AsyncWebSocketSharedBuffer buffer, uint8_t opcode = WS_TEXT, bool mask = false) { _queueMessage(buffer, opcode, mask); }
|
||||||
bool queueIsFull() const;
|
bool queueIsFull() const;
|
||||||
size_t queueLen() const;
|
size_t queueLen() const;
|
||||||
|
|
||||||
size_t printf(const char *format, ...) __attribute__ ((format (printf, 2, 3)));
|
size_t printf(const char* format, ...) __attribute__((format(printf, 2, 3)));
|
||||||
#ifndef ESP32
|
#ifndef ESP32
|
||||||
size_t printf_P(PGM_P formatP, ...) __attribute__ ((format (printf, 2, 3)));
|
size_t printf_P(PGM_P formatP, ...) __attribute__((format(printf, 2, 3)));
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void text(AsyncWebSocketSharedBuffer buffer);
|
void text(AsyncWebSocketSharedBuffer buffer);
|
||||||
void text(const uint8_t *message, size_t len);
|
void text(const uint8_t* message, size_t len);
|
||||||
void text(const char *message, size_t len);
|
void text(const char* message, size_t len);
|
||||||
void text(const char *message);
|
void text(const char* message);
|
||||||
void text(const String &message);
|
void text(const String& message);
|
||||||
#ifndef ESP32
|
#ifndef ESP32
|
||||||
void text(const __FlashStringHelper *message);
|
void text(const __FlashStringHelper* message);
|
||||||
#endif // ESP32
|
#endif // ESP32
|
||||||
void text(AsyncWebSocketMessageBuffer *buffer);
|
void text(AsyncWebSocketMessageBuffer* buffer);
|
||||||
|
|
||||||
void binary(AsyncWebSocketSharedBuffer buffer);
|
void binary(AsyncWebSocketSharedBuffer buffer);
|
||||||
void binary(const uint8_t *message, size_t len);
|
void binary(const uint8_t* message, size_t len);
|
||||||
void binary(const char * message, size_t len);
|
void binary(const char* message, size_t len);
|
||||||
void binary(const char * message);
|
void binary(const char* message);
|
||||||
void binary(const String &message);
|
void binary(const String& message);
|
||||||
#ifndef ESP32
|
#ifndef ESP32
|
||||||
void binary(const __FlashStringHelper *message, size_t len);
|
void binary(const __FlashStringHelper* message, size_t len);
|
||||||
#endif // ESP32
|
#endif // ESP32
|
||||||
void binary(AsyncWebSocketMessageBuffer *buffer);
|
void binary(AsyncWebSocketMessageBuffer* buffer);
|
||||||
|
|
||||||
bool canSend() const;
|
bool canSend() const;
|
||||||
|
|
||||||
//system callbacks (do not call)
|
// system callbacks (do not call)
|
||||||
void _onAck(size_t len, uint32_t time);
|
void _onAck(size_t len, uint32_t time);
|
||||||
void _onError(int8_t);
|
void _onError(int8_t);
|
||||||
void _onPoll();
|
void _onPoll();
|
||||||
void _onTimeout(uint32_t time);
|
void _onTimeout(uint32_t time);
|
||||||
void _onDisconnect();
|
void _onDisconnect();
|
||||||
void _onData(void *pbuf, size_t plen);
|
void _onData(void* pbuf, size_t plen);
|
||||||
};
|
};
|
||||||
|
|
||||||
using AwsHandshakeHandler = std::function<bool(AsyncWebServerRequest *request)>;
|
using AwsHandshakeHandler = std::function<bool(AsyncWebServerRequest* request)>;
|
||||||
using AwsEventHandler = std::function<void(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventType type, void * arg, uint8_t *data, size_t len)>;
|
using AwsEventHandler = std::function<void(AsyncWebSocket* server, AsyncWebSocketClient* client, AwsEventType type, void* arg, uint8_t* data, size_t len)>;
|
||||||
|
|
||||||
//WebServer Handler implementation that plays the role of a socket server
|
// WebServer Handler implementation that plays the role of a socket server
|
||||||
class AsyncWebSocket: public AsyncWebHandler {
|
class AsyncWebSocket : public AsyncWebHandler {
|
||||||
private:
|
private:
|
||||||
String _url;
|
String _url;
|
||||||
std::list<AsyncWebSocketClient> _clients;
|
std::list<AsyncWebSocketClient> _clients;
|
||||||
uint32_t _cNextId;
|
uint32_t _cNextId;
|
||||||
AwsEventHandler _eventHandler{nullptr};
|
AwsEventHandler _eventHandler{nullptr};
|
||||||
AwsHandshakeHandler _handshakeHandler;
|
AwsHandshakeHandler _handshakeHandler;
|
||||||
bool _enabled;
|
bool _enabled;
|
||||||
AsyncWebLock _lock;
|
#ifdef ESP32
|
||||||
|
mutable std::mutex _lock;
|
||||||
|
#endif
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit AsyncWebSocket(const char* url) : _url(url) ,_cNextId(1), _enabled(true) {}
|
explicit AsyncWebSocket(const char* url) : _url(url), _cNextId(1), _enabled(true) {}
|
||||||
AsyncWebSocket(const String& url) :_url(url) ,_cNextId(1),_enabled(true) {}
|
AsyncWebSocket(const String& url) : _url(url), _cNextId(1), _enabled(true) {}
|
||||||
~AsyncWebSocket(){};
|
~AsyncWebSocket(){};
|
||||||
const char * url() const { return _url.c_str(); }
|
const char* url() const { return _url.c_str(); }
|
||||||
void enable(bool e){ _enabled = e; }
|
void enable(bool e) { _enabled = e; }
|
||||||
bool enabled() const { return _enabled; }
|
bool enabled() const { return _enabled; }
|
||||||
bool availableForWriteAll();
|
bool availableForWriteAll();
|
||||||
bool availableForWrite(uint32_t id);
|
bool availableForWrite(uint32_t id);
|
||||||
|
|
||||||
size_t count() const;
|
size_t count() const;
|
||||||
AsyncWebSocketClient * client(uint32_t id);
|
AsyncWebSocketClient* client(uint32_t id);
|
||||||
bool hasClient(uint32_t id){ return client(id) != nullptr; }
|
bool hasClient(uint32_t id) { return client(id) != nullptr; }
|
||||||
|
|
||||||
void close(uint32_t id, uint16_t code=0, const char * message=NULL);
|
void close(uint32_t id, uint16_t code = 0, const char* message = NULL);
|
||||||
void closeAll(uint16_t code=0, const char * message=NULL);
|
void closeAll(uint16_t code = 0, const char* message = NULL);
|
||||||
void cleanupClients(uint16_t maxClients = DEFAULT_MAX_WS_CLIENTS);
|
void cleanupClients(uint16_t maxClients = DEFAULT_MAX_WS_CLIENTS);
|
||||||
|
|
||||||
void ping(uint32_t id, const uint8_t *data=NULL, size_t len=0);
|
void ping(uint32_t id, const uint8_t* data = NULL, size_t len = 0);
|
||||||
void pingAll(const uint8_t *data=NULL, size_t len=0); // done
|
void pingAll(const uint8_t* data = NULL, size_t len = 0); // done
|
||||||
|
|
||||||
void text(uint32_t id, const uint8_t * message, size_t len);
|
void text(uint32_t id, const uint8_t* message, size_t len);
|
||||||
void text(uint32_t id, const char *message, size_t len);
|
void text(uint32_t id, const char* message, size_t len);
|
||||||
void text(uint32_t id, const char *message);
|
void text(uint32_t id, const char* message);
|
||||||
void text(uint32_t id, const String &message);
|
void text(uint32_t id, const String& message);
|
||||||
#ifndef ESP32
|
#ifndef ESP32
|
||||||
void text(uint32_t id, const __FlashStringHelper *message);
|
void text(uint32_t id, const __FlashStringHelper* message);
|
||||||
#endif // ESP32
|
#endif // ESP32
|
||||||
void text(uint32_t id, AsyncWebSocketMessageBuffer *buffer);
|
void text(uint32_t id, AsyncWebSocketMessageBuffer* buffer);
|
||||||
void text(uint32_t id, AsyncWebSocketSharedBuffer buffer);
|
void text(uint32_t id, AsyncWebSocketSharedBuffer buffer);
|
||||||
|
|
||||||
void textAll(const uint8_t *message, size_t len);
|
void textAll(const uint8_t* message, size_t len);
|
||||||
void textAll(const char * message, size_t len);
|
void textAll(const char* message, size_t len);
|
||||||
void textAll(const char * message);
|
void textAll(const char* message);
|
||||||
void textAll(const String &message);
|
void textAll(const String& message);
|
||||||
#ifndef ESP32
|
#ifndef ESP32
|
||||||
void textAll(const __FlashStringHelper *message);
|
void textAll(const __FlashStringHelper* message);
|
||||||
#endif // ESP32
|
#endif // ESP32
|
||||||
void textAll(AsyncWebSocketMessageBuffer *buffer);
|
void textAll(AsyncWebSocketMessageBuffer* buffer);
|
||||||
void textAll(AsyncWebSocketSharedBuffer buffer);
|
void textAll(AsyncWebSocketSharedBuffer buffer);
|
||||||
|
|
||||||
void binary(uint32_t id, const uint8_t *message, size_t len);
|
void binary(uint32_t id, const uint8_t* message, size_t len);
|
||||||
void binary(uint32_t id, const char *message, size_t len);
|
void binary(uint32_t id, const char* message, size_t len);
|
||||||
void binary(uint32_t id, const char *message);
|
void binary(uint32_t id, const char* message);
|
||||||
void binary(uint32_t id, const String &message);
|
void binary(uint32_t id, const String& message);
|
||||||
#ifndef ESP32
|
#ifndef ESP32
|
||||||
void binary(uint32_t id, const __FlashStringHelper *message, size_t len);
|
void binary(uint32_t id, const __FlashStringHelper* message, size_t len);
|
||||||
#endif // ESP32
|
#endif // ESP32
|
||||||
void binary(uint32_t id, AsyncWebSocketMessageBuffer *buffer);
|
void binary(uint32_t id, AsyncWebSocketMessageBuffer* buffer);
|
||||||
void binary(uint32_t id, AsyncWebSocketSharedBuffer buffer);
|
void binary(uint32_t id, AsyncWebSocketSharedBuffer buffer);
|
||||||
|
|
||||||
void binaryAll(const uint8_t *message, size_t len);
|
void binaryAll(const uint8_t* message, size_t len);
|
||||||
void binaryAll(const char *message, size_t len);
|
void binaryAll(const char* message, size_t len);
|
||||||
void binaryAll(const char *message);
|
void binaryAll(const char* message);
|
||||||
void binaryAll(const String &message);
|
void binaryAll(const String& message);
|
||||||
#ifndef ESP32
|
#ifndef ESP32
|
||||||
void binaryAll(const __FlashStringHelper *message, size_t len);
|
void binaryAll(const __FlashStringHelper* message, size_t len);
|
||||||
#endif // ESP32
|
#endif // ESP32
|
||||||
void binaryAll(AsyncWebSocketMessageBuffer *buffer);
|
void binaryAll(AsyncWebSocketMessageBuffer* buffer);
|
||||||
void binaryAll(AsyncWebSocketSharedBuffer buffer);
|
void binaryAll(AsyncWebSocketSharedBuffer buffer);
|
||||||
|
|
||||||
size_t printf(uint32_t id, const char *format, ...) __attribute__ ((format (printf, 3, 4)));
|
size_t printf(uint32_t id, const char* format, ...) __attribute__((format(printf, 3, 4)));
|
||||||
size_t printfAll(const char *format, ...) __attribute__ ((format (printf, 2, 3)));
|
size_t printfAll(const char* format, ...) __attribute__((format(printf, 2, 3)));
|
||||||
#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)));
|
|
||||||
|
|
||||||
//event listener
|
#ifndef ESP32
|
||||||
void onEvent(AwsEventHandler handler){
|
size_t printf_P(uint32_t id, PGM_P formatP, ...) __attribute__((format(printf, 3, 4)));
|
||||||
|
size_t printfAll_P(PGM_P formatP, ...) __attribute__((format(printf, 2, 3)));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// event listener
|
||||||
|
void onEvent(AwsEventHandler handler) {
|
||||||
_eventHandler = handler;
|
_eventHandler = handler;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handshake Handler
|
// Handshake Handler
|
||||||
void handleHandshake(AwsHandshakeHandler handler){
|
void handleHandshake(AwsHandshakeHandler handler) {
|
||||||
_handshakeHandler = handler;
|
_handshakeHandler = handler;
|
||||||
}
|
}
|
||||||
|
|
||||||
//system callbacks (do not call)
|
|
||||||
uint32_t _getNextId(){ return _cNextId++; }
|
|
||||||
AsyncWebSocketClient *_newClient(AsyncWebServerRequest *request);
|
|
||||||
void _handleEvent(AsyncWebSocketClient * client, AwsEventType type, void * arg, uint8_t *data, size_t len);
|
|
||||||
virtual bool canHandle(AsyncWebServerRequest *request) override final;
|
|
||||||
virtual void handleRequest(AsyncWebServerRequest *request) override final;
|
|
||||||
|
|
||||||
|
// system callbacks (do not call)
|
||||||
|
uint32_t _getNextId() { return _cNextId++; }
|
||||||
|
AsyncWebSocketClient* _newClient(AsyncWebServerRequest* request);
|
||||||
|
void _handleEvent(AsyncWebSocketClient* client, AwsEventType type, void* arg, uint8_t* data, size_t len);
|
||||||
|
virtual bool canHandle(AsyncWebServerRequest* request) override final;
|
||||||
|
virtual void handleRequest(AsyncWebServerRequest* request) override final;
|
||||||
|
|
||||||
// messagebuffer functions/objects.
|
// messagebuffer functions/objects.
|
||||||
AsyncWebSocketMessageBuffer * makeBuffer(size_t size = 0);
|
AsyncWebSocketMessageBuffer* makeBuffer(size_t size = 0);
|
||||||
AsyncWebSocketMessageBuffer * makeBuffer(const uint8_t * data, size_t size);
|
AsyncWebSocketMessageBuffer* makeBuffer(const uint8_t* data, size_t size);
|
||||||
|
|
||||||
const std::list<AsyncWebSocketClient> &getClients() const { return _clients; }
|
const std::list<AsyncWebSocketClient>& getClients() const { return _clients; }
|
||||||
};
|
};
|
||||||
|
|
||||||
//WebServer response to authenticate the socket and detach the tcp client from the web server request
|
// WebServer response to authenticate the socket and detach the tcp client from the web server request
|
||||||
class AsyncWebSocketResponse: public AsyncWebServerResponse {
|
class AsyncWebSocketResponse : public AsyncWebServerResponse {
|
||||||
private:
|
private:
|
||||||
String _content;
|
String _content;
|
||||||
AsyncWebSocket *_server;
|
AsyncWebSocket* _server;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
AsyncWebSocketResponse(const String& key, AsyncWebSocket *server);
|
AsyncWebSocketResponse(const String& key, AsyncWebSocket* server);
|
||||||
void _respond(AsyncWebServerRequest *request);
|
void _respond(AsyncWebServerRequest* request);
|
||||||
size_t _ack(AsyncWebServerRequest *request, size_t len, uint32_t time);
|
size_t _ack(AsyncWebServerRequest* request, size_t len, uint32_t time);
|
||||||
bool _sourceValid() const { return true; }
|
bool _sourceValid() const { return true; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
#endif /* ASYNCWEBSOCKET_H_ */
|
#endif /* ASYNCWEBSOCKET_H_ */
|
||||||
|
@ -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_
|
|
@ -23,33 +23,36 @@
|
|||||||
|
|
||||||
#include "Arduino.h"
|
#include "Arduino.h"
|
||||||
|
|
||||||
|
#include "FS.h"
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <list>
|
#include <list>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include "FS.h"
|
|
||||||
|
|
||||||
#include "StringArray.h"
|
|
||||||
|
|
||||||
#ifdef ESP32
|
#ifdef ESP32
|
||||||
#include <WiFi.h>
|
#include <AsyncTCP.h>
|
||||||
#include <AsyncTCP.h>
|
#include <WiFi.h>
|
||||||
#elif defined(ESP8266)
|
#elif defined(ESP8266)
|
||||||
#include <ESP8266WiFi.h>
|
#include <ESP8266WiFi.h>
|
||||||
#include <ESPAsyncTCP.h>
|
#include <ESPAsyncTCP.h>
|
||||||
|
#elif defined(TARGET_RP2040)
|
||||||
|
#include <WiFi.h>
|
||||||
|
#include <AsyncTCP_RP2040W.h>
|
||||||
|
#include <http_parser.h>
|
||||||
|
#include <HTTP_Method.h>
|
||||||
#else
|
#else
|
||||||
#error Platform not supported
|
#error Platform not supported
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define ASYNCWEBSERVER_VERSION "2.10.8"
|
#define ASYNCWEBSERVER_VERSION "3.0.6"
|
||||||
#define ASYNCWEBSERVER_VERSION_MAJOR 2
|
#define ASYNCWEBSERVER_VERSION_MAJOR 3
|
||||||
#define ASYNCWEBSERVER_VERSION_MINOR 10
|
#define ASYNCWEBSERVER_VERSION_MINOR 0
|
||||||
#define ASYNCWEBSERVER_VERSION_REVISION 8
|
#define ASYNCWEBSERVER_VERSION_REVISION 6
|
||||||
#define ASYNCWEBSERVER_FORK_mathieucarbou
|
#define ASYNCWEBSERVER_FORK_mathieucarbou
|
||||||
|
|
||||||
#ifdef ASYNCWEBSERVER_REGEX
|
#ifdef ASYNCWEBSERVER_REGEX
|
||||||
#define ASYNCWEBSERVER_REGEX_ATTRIBUTE
|
#define ASYNCWEBSERVER_REGEX_ATTRIBUTE
|
||||||
#else
|
#else
|
||||||
#define ASYNCWEBSERVER_REGEX_ATTRIBUTE __attribute__((warning("ASYNCWEBSERVER_REGEX not defined")))
|
#define ASYNCWEBSERVER_REGEX_ATTRIBUTE __attribute__((warning("ASYNCWEBSERVER_REGEX not defined")))
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
class AsyncWebServer;
|
class AsyncWebServer;
|
||||||
@ -63,33 +66,37 @@ class AsyncStaticWebHandler;
|
|||||||
class AsyncCallbackWebHandler;
|
class AsyncCallbackWebHandler;
|
||||||
class AsyncResponseStream;
|
class AsyncResponseStream;
|
||||||
|
|
||||||
#ifndef WEBSERVER_H
|
#if defined (TARGET_RP2040)
|
||||||
typedef enum {
|
typedef enum http_method WebRequestMethod;
|
||||||
HTTP_GET = 0b00000001,
|
#else
|
||||||
HTTP_POST = 0b00000010,
|
#ifndef WEBSERVER_H
|
||||||
HTTP_DELETE = 0b00000100,
|
typedef enum {
|
||||||
HTTP_PUT = 0b00001000,
|
HTTP_GET = 0b00000001,
|
||||||
HTTP_PATCH = 0b00010000,
|
HTTP_POST = 0b00000010,
|
||||||
HTTP_HEAD = 0b00100000,
|
HTTP_DELETE = 0b00000100,
|
||||||
HTTP_OPTIONS = 0b01000000,
|
HTTP_PUT = 0b00001000,
|
||||||
HTTP_ANY = 0b01111111,
|
HTTP_PATCH = 0b00010000,
|
||||||
} WebRequestMethod;
|
HTTP_HEAD = 0b00100000,
|
||||||
|
HTTP_OPTIONS = 0b01000000,
|
||||||
|
HTTP_ANY = 0b01111111,
|
||||||
|
} WebRequestMethod;
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef HAVE_FS_FILE_OPEN_MODE
|
#ifndef HAVE_FS_FILE_OPEN_MODE
|
||||||
namespace fs {
|
namespace fs {
|
||||||
class FileOpenMode {
|
class FileOpenMode {
|
||||||
public:
|
public:
|
||||||
static const char *read;
|
static const char* read;
|
||||||
static const char *write;
|
static const char* write;
|
||||||
static const char *append;
|
static const char* append;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
#else
|
#else
|
||||||
#include "FileOpenMode.h"
|
#include "FileOpenMode.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
//if this value is returned when asked for data, packet will not be sent and you will be asked for data again
|
// if this value is returned when asked for data, packet will not be sent and you will be asked for data again
|
||||||
#define RESPONSE_TRY_AGAIN 0xFFFFFFFF
|
#define RESPONSE_TRY_AGAIN 0xFFFFFFFF
|
||||||
|
|
||||||
typedef uint8_t WebRequestMethodComposite;
|
typedef uint8_t WebRequestMethodComposite;
|
||||||
@ -108,8 +115,7 @@ class AsyncWebParameter {
|
|||||||
bool _isFile;
|
bool _isFile;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
AsyncWebParameter(const String& name, const String& value, bool form = false, bool file = false, size_t size = 0) : _name(name), _value(value), _size(size), _isForm(form), _isFile(file) {}
|
||||||
AsyncWebParameter(const String& name, const String& value, bool form=false, bool file=false, size_t size=0): _name(name), _value(value), _size(size), _isForm(form), _isFile(file){}
|
|
||||||
const String& name() const { return _name; }
|
const String& name() const { return _name; }
|
||||||
const String& value() const { return _value; }
|
const String& value() const { return _value; }
|
||||||
size_t size() const { return _size; }
|
size_t size() const { return _size; }
|
||||||
@ -128,18 +134,20 @@ class AsyncWebHeader {
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
AsyncWebHeader() = default;
|
AsyncWebHeader() = default;
|
||||||
AsyncWebHeader(const AsyncWebHeader &) = default;
|
AsyncWebHeader(const AsyncWebHeader&) = default;
|
||||||
|
|
||||||
AsyncWebHeader(const String& name, const String& value): _name(name), _value(value){}
|
AsyncWebHeader(const String& name, const String& value) : _name(name), _value(value) {}
|
||||||
AsyncWebHeader(const String& data): _name(), _value(){
|
AsyncWebHeader(const String& data) {
|
||||||
if(!data) return;
|
if (!data)
|
||||||
|
return;
|
||||||
int index = data.indexOf(':');
|
int index = data.indexOf(':');
|
||||||
if (index < 0) return;
|
if (index < 0)
|
||||||
|
return;
|
||||||
_name = data.substring(0, index);
|
_name = data.substring(0, index);
|
||||||
_value = data.substring(index + 2);
|
_value = data.substring(index + 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
AsyncWebHeader &operator=(const AsyncWebHeader &) = default;
|
AsyncWebHeader& operator=(const AsyncWebHeader&) = default;
|
||||||
|
|
||||||
const String& name() const { return _name; }
|
const String& name() const { return _name; }
|
||||||
const String& value() const { return _value; }
|
const String& value() const { return _value; }
|
||||||
@ -150,16 +158,22 @@ class AsyncWebHeader {
|
|||||||
* REQUEST :: Each incoming Client is wrapped inside a Request and both live together until disconnect
|
* REQUEST :: Each incoming Client is wrapped inside a Request and both live together until disconnect
|
||||||
* */
|
* */
|
||||||
|
|
||||||
typedef enum { RCT_NOT_USED = -1, RCT_DEFAULT = 0, RCT_HTTP, RCT_WS, RCT_EVENT, RCT_MAX } RequestedConnectionType;
|
typedef enum { RCT_NOT_USED = -1,
|
||||||
|
RCT_DEFAULT = 0,
|
||||||
|
RCT_HTTP,
|
||||||
|
RCT_WS,
|
||||||
|
RCT_EVENT,
|
||||||
|
RCT_MAX } RequestedConnectionType;
|
||||||
|
|
||||||
typedef std::function<size_t(uint8_t*, size_t, size_t)> AwsResponseFiller;
|
typedef std::function<size_t(uint8_t*, size_t, size_t)> AwsResponseFiller;
|
||||||
typedef std::function<String(const String&)> AwsTemplateProcessor;
|
typedef std::function<String(const String&)> AwsTemplateProcessor;
|
||||||
|
|
||||||
class AsyncWebServerRequest {
|
class AsyncWebServerRequest {
|
||||||
using File = fs::File;
|
using File = fs::File;
|
||||||
using FS = fs::FS;
|
using FS = fs::FS;
|
||||||
friend class AsyncWebServer;
|
friend class AsyncWebServer;
|
||||||
friend class AsyncCallbackWebHandler;
|
friend class AsyncCallbackWebHandler;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
AsyncClient* _client;
|
AsyncClient* _client;
|
||||||
AsyncWebServer* _server;
|
AsyncWebServer* _server;
|
||||||
@ -188,7 +202,7 @@ class AsyncWebServerRequest {
|
|||||||
size_t _parsedLength;
|
size_t _parsedLength;
|
||||||
|
|
||||||
std::list<AsyncWebHeader> _headers;
|
std::list<AsyncWebHeader> _headers;
|
||||||
LinkedList<AsyncWebParameter *> _params;
|
std::list<AsyncWebParameter> _params;
|
||||||
std::vector<String> _pathParams;
|
std::vector<String> _pathParams;
|
||||||
|
|
||||||
uint8_t _multiParseState;
|
uint8_t _multiParseState;
|
||||||
@ -199,7 +213,7 @@ class AsyncWebServerRequest {
|
|||||||
String _itemFilename;
|
String _itemFilename;
|
||||||
String _itemType;
|
String _itemType;
|
||||||
String _itemValue;
|
String _itemValue;
|
||||||
uint8_t *_itemBuffer;
|
uint8_t* _itemBuffer;
|
||||||
size_t _itemBufferIndex;
|
size_t _itemBufferIndex;
|
||||||
bool _itemIsFile;
|
bool _itemIsFile;
|
||||||
|
|
||||||
@ -208,10 +222,9 @@ class AsyncWebServerRequest {
|
|||||||
void _onError(int8_t error);
|
void _onError(int8_t error);
|
||||||
void _onTimeout(uint32_t time);
|
void _onTimeout(uint32_t time);
|
||||||
void _onDisconnect();
|
void _onDisconnect();
|
||||||
void _onData(void *buf, size_t len);
|
void _onData(void* buf, size_t len);
|
||||||
|
|
||||||
void _addParam(AsyncWebParameter*);
|
void _addPathParam(const char* param);
|
||||||
void _addPathParam(const char *param);
|
|
||||||
|
|
||||||
bool _parseReqHead();
|
bool _parseReqHead();
|
||||||
bool _parseReqHeader();
|
bool _parseReqHeader();
|
||||||
@ -226,12 +239,12 @@ class AsyncWebServerRequest {
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
File _tempFile;
|
File _tempFile;
|
||||||
void *_tempObject;
|
void* _tempObject;
|
||||||
|
|
||||||
AsyncWebServerRequest(AsyncWebServer*, AsyncClient*);
|
AsyncWebServerRequest(AsyncWebServer*, AsyncClient*);
|
||||||
~AsyncWebServerRequest();
|
~AsyncWebServerRequest();
|
||||||
|
|
||||||
AsyncClient* client(){ return _client; }
|
AsyncClient* client() { return _client; }
|
||||||
uint8_t version() const { return _version; }
|
uint8_t version() const { return _version; }
|
||||||
WebRequestMethodComposite method() const { return _method; }
|
WebRequestMethodComposite method() const { return _method; }
|
||||||
const String& url() const { return _url; }
|
const String& url() const { return _url; }
|
||||||
@ -239,77 +252,156 @@ class AsyncWebServerRequest {
|
|||||||
const String& contentType() const { return _contentType; }
|
const String& contentType() const { return _contentType; }
|
||||||
size_t contentLength() const { return _contentLength; }
|
size_t contentLength() const { return _contentLength; }
|
||||||
bool multipart() const { return _isMultipart; }
|
bool multipart() const { return _isMultipart; }
|
||||||
const __FlashStringHelper *methodToString() const;
|
|
||||||
const __FlashStringHelper *requestedConnTypeToString() const;
|
#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; }
|
RequestedConnectionType requestedConnType() const { return _reqconntype; }
|
||||||
bool isExpectedRequestedConnType(RequestedConnectionType erct1, RequestedConnectionType erct2 = RCT_NOT_USED, RequestedConnectionType erct3 = RCT_NOT_USED);
|
bool isExpectedRequestedConnType(RequestedConnectionType erct1, RequestedConnectionType erct2 = RCT_NOT_USED, RequestedConnectionType erct3 = RCT_NOT_USED);
|
||||||
void onDisconnect (ArDisconnectHandler fn);
|
void onDisconnect(ArDisconnectHandler fn);
|
||||||
|
|
||||||
//hash is the string representation of:
|
// hash is the string representation of:
|
||||||
// base64(user:pass) for basic or
|
// base64(user:pass) for basic or
|
||||||
// user:realm:md5(user:realm:pass) for digest
|
// user:realm:md5(user:realm:pass) for digest
|
||||||
bool authenticate(const char * hash);
|
bool authenticate(const char* hash);
|
||||||
bool authenticate(const char * username, const char * password, const char * realm = NULL, bool passwordIsHash = false);
|
bool authenticate(const char* username, const char* password, const char* realm = NULL, bool passwordIsHash = false);
|
||||||
void requestAuthentication(const char * realm = NULL, bool isDigest = true);
|
void requestAuthentication(const char* realm = NULL, bool isDigest = true);
|
||||||
|
|
||||||
void setHandler(AsyncWebHandler *handler){ _handler = handler; }
|
void setHandler(AsyncWebHandler* handler) { _handler = handler; }
|
||||||
void addInterestingHeader(const String& name);
|
|
||||||
|
|
||||||
void redirect(const String& url);
|
/**
|
||||||
|
* @brief add header to collect from a response
|
||||||
|
*
|
||||||
|
* @param name
|
||||||
|
*/
|
||||||
|
void addInterestingHeader(const char* name);
|
||||||
|
void addInterestingHeader(const String& name) { return addInterestingHeader(name.c_str()); };
|
||||||
|
|
||||||
void send(AsyncWebServerResponse *response);
|
/**
|
||||||
void send(int code, const String& contentType=String(), const String& content=String());
|
* @brief issue 302 redirect response
|
||||||
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);
|
* @param url
|
||||||
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 redirect(const char* url);
|
||||||
void sendChunked(const String& contentType, AwsResponseFiller callback, AwsTemplateProcessor templateCallback=nullptr);
|
void redirect(const String& url) { return redirect(url.c_str()); };
|
||||||
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);
|
|
||||||
|
|
||||||
AsyncWebServerResponse *beginResponse(int code, const String& contentType=String(), const String& content=String());
|
void send(AsyncWebServerResponse* response);
|
||||||
AsyncWebServerResponse *beginResponse(FS &fs, const String& path, const String& contentType=String(), bool download=false, AwsTemplateProcessor callback=nullptr);
|
void send(int code, const String& contentType = String(), const String& content = String());
|
||||||
AsyncWebServerResponse *beginResponse(File content, const String& path, const String& contentType=String(), bool download=false, AwsTemplateProcessor callback=nullptr);
|
void send(int code, const String& contentType, const uint8_t* content, size_t len, AwsTemplateProcessor callback = nullptr);
|
||||||
AsyncWebServerResponse *beginResponse(Stream &stream, const String& contentType, size_t len, AwsTemplateProcessor callback=nullptr);
|
void send(int code, const String& contentType, PGM_P content, AwsTemplateProcessor callback = nullptr);
|
||||||
AsyncWebServerResponse *beginResponse(const String& contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback=nullptr);
|
void send(FS& fs, const String& path, const String& contentType = String(), bool download = false, AwsTemplateProcessor callback = nullptr);
|
||||||
AsyncWebServerResponse *beginChunkedResponse(const String& contentType, AwsResponseFiller callback, AwsTemplateProcessor templateCallback=nullptr);
|
void send(File content, const String& path, const String& contentType = String(), bool download = false, AwsTemplateProcessor callback = nullptr);
|
||||||
AsyncResponseStream *beginResponseStream(const String& contentType, size_t bufferSize=1460);
|
void send(Stream& stream, const String& contentType, size_t len, AwsTemplateProcessor callback = nullptr);
|
||||||
AsyncWebServerResponse *beginResponse_P(int code, const String& contentType, const uint8_t * content, size_t len, AwsTemplateProcessor callback=nullptr);
|
void send(const String& contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback = nullptr);
|
||||||
AsyncWebServerResponse *beginResponse_P(int code, const String& contentType, PGM_P content, AwsTemplateProcessor callback=nullptr);
|
void sendChunked(const String& contentType, AwsResponseFiller callback, AwsTemplateProcessor templateCallback = nullptr);
|
||||||
|
|
||||||
size_t headers() const; // get header count
|
[[deprecated("Replaced by send(...)")]]
|
||||||
bool hasHeader(const String& name) const; // check if header exists
|
void send_P(int code, const String& contentType, const uint8_t* content, size_t len, AwsTemplateProcessor callback = nullptr) {
|
||||||
bool hasHeader(const __FlashStringHelper * data) const; // check if header exists
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
AsyncWebHeader* getHeader(const String& name);
|
AsyncWebServerResponse* beginResponse(int code, const String& contentType = String(), const String& content = String());
|
||||||
const AsyncWebHeader* getHeader(const String& name) const;
|
AsyncWebServerResponse* beginResponse(int code, const String& contentType, const uint8_t* content, size_t len, AwsTemplateProcessor callback = nullptr);
|
||||||
AsyncWebHeader* getHeader(const __FlashStringHelper * data);
|
AsyncWebServerResponse* beginResponse(int code, const String& contentType, PGM_P content, AwsTemplateProcessor callback = nullptr);
|
||||||
const AsyncWebHeader* getHeader(const __FlashStringHelper * data) const;
|
AsyncWebServerResponse* beginResponse(FS& fs, const String& path, const String& contentType = String(), bool download = false, AwsTemplateProcessor callback = nullptr);
|
||||||
AsyncWebHeader* getHeader(size_t num);
|
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);
|
||||||
|
|
||||||
|
|
||||||
|
[[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
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
#endif
|
||||||
const AsyncWebHeader* getHeader(size_t num) const;
|
const AsyncWebHeader* getHeader(size_t num) const;
|
||||||
|
|
||||||
size_t params() const; // get arguments count
|
size_t params() const; // get arguments count
|
||||||
bool hasParam(const String& name, bool post=false, bool file=false) const;
|
bool hasParam(const String& name, bool post = false, bool file = false) const;
|
||||||
bool hasParam(const __FlashStringHelper * data, bool post=false, bool file=false) const;
|
bool hasParam(const __FlashStringHelper* data, bool post = false, bool file = false) const;
|
||||||
|
|
||||||
AsyncWebParameter* getParam(const String& name, bool post=false, bool file=false) const;
|
/**
|
||||||
AsyncWebParameter* getParam(const __FlashStringHelper * data, bool post, bool file) const;
|
* @brief Get the Request parameter by name
|
||||||
AsyncWebParameter* getParam(size_t num) const;
|
*
|
||||||
|
* @param name
|
||||||
|
* @param post
|
||||||
|
* @param file
|
||||||
|
* @return const AsyncWebParameter*
|
||||||
|
*/
|
||||||
|
const AsyncWebParameter* getParam(const char* name, bool post = false, bool file = false) const;
|
||||||
|
|
||||||
size_t args() const { return params(); } // get arguments count
|
const AsyncWebParameter* getParam(const String& name, bool post = false, bool file = false) const { return getParam(name.c_str(), post, file); };
|
||||||
const String& arg(const String& name) const; // get request argument value by name
|
#ifdef ESP8266
|
||||||
const String& arg(const __FlashStringHelper * data) const; // get request argument value by F(name)
|
const AsyncWebParameter* getParam(const __FlashStringHelper* data, bool post, bool file) const;
|
||||||
const String& arg(size_t i) const; // get request argument value by number
|
#endif
|
||||||
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 __FlashStringHelper * data) const; // check if F(argument) exists
|
* @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
|
||||||
|
|
||||||
|
// 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& 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 __FlashStringHelper * data) const;// get request header value by F(name)
|
const String& header(const char* name) const;
|
||||||
const String& header(size_t i) const; // get request header value by number
|
const String& header(const String& name) const { return header(name.c_str()); };
|
||||||
const String& headerName(size_t i) const; // get request header name by number
|
|
||||||
|
#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;
|
String urlDecode(const String& text) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -317,11 +409,11 @@ class AsyncWebServerRequest {
|
|||||||
* FILTER :: Callback to filter AsyncWebRewrite and AsyncWebHandler (done by the Server)
|
* FILTER :: Callback to filter AsyncWebRewrite and AsyncWebHandler (done by the Server)
|
||||||
* */
|
* */
|
||||||
|
|
||||||
typedef std::function<bool(AsyncWebServerRequest *request)> ArRequestFilterFunction;
|
using ArRequestFilterFunction = std::function<bool(AsyncWebServerRequest* request)>;
|
||||||
|
|
||||||
bool ON_STA_FILTER(AsyncWebServerRequest *request);
|
bool ON_STA_FILTER(AsyncWebServerRequest* request);
|
||||||
|
|
||||||
bool ON_AP_FILTER(AsyncWebServerRequest *request);
|
bool ON_AP_FILTER(AsyncWebServerRequest* request);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* REWRITE :: One instance can be handle any Request (done by the Server)
|
* REWRITE :: One instance can be handle any Request (done by the Server)
|
||||||
@ -332,22 +424,26 @@ class AsyncWebRewrite {
|
|||||||
String _from;
|
String _from;
|
||||||
String _toUrl;
|
String _toUrl;
|
||||||
String _params;
|
String _params;
|
||||||
ArRequestFilterFunction _filter;
|
ArRequestFilterFunction _filter{nullptr};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
AsyncWebRewrite(const char* from, const char* to): _from(from), _toUrl(to), _params(String()), _filter(NULL){
|
AsyncWebRewrite(const char* from, const char* to) : _from(from), _toUrl(to) {
|
||||||
int index = _toUrl.indexOf('?');
|
int index = _toUrl.indexOf('?');
|
||||||
if (index > 0) {
|
if (index > 0) {
|
||||||
_params = _toUrl.substring(index +1);
|
_params = _toUrl.substring(index + 1);
|
||||||
_toUrl = _toUrl.substring(0, index);
|
_toUrl = _toUrl.substring(0, index);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
virtual ~AsyncWebRewrite(){}
|
virtual ~AsyncWebRewrite() {}
|
||||||
AsyncWebRewrite& setFilter(ArRequestFilterFunction fn) { _filter = fn; return *this; }
|
AsyncWebRewrite& setFilter(ArRequestFilterFunction fn) {
|
||||||
bool filter(AsyncWebServerRequest *request) const { return _filter == NULL || _filter(request); }
|
_filter = fn;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
bool filter(AsyncWebServerRequest* request) const { return _filter == NULL || _filter(request); }
|
||||||
const String& from(void) const { return _from; }
|
const String& from(void) const { return _from; }
|
||||||
const String& toUrl(void) const { return _toUrl; }
|
const String& toUrl(void) const { return _toUrl; }
|
||||||
const String& params(void) const { return _params; }
|
const String& params(void) const { return _params; }
|
||||||
virtual bool match(AsyncWebServerRequest *request) { return from() == request->url() && filter(request); }
|
virtual bool match(AsyncWebServerRequest* request) { return from() == request->url() && filter(request); }
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -356,23 +452,35 @@ class AsyncWebRewrite {
|
|||||||
|
|
||||||
class AsyncWebHandler {
|
class AsyncWebHandler {
|
||||||
protected:
|
protected:
|
||||||
ArRequestFilterFunction _filter;
|
ArRequestFilterFunction _filter{nullptr};
|
||||||
String _username;
|
String _username;
|
||||||
String _password;
|
String _password;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
AsyncWebHandler():_username(""), _password(""){}
|
AsyncWebHandler() {}
|
||||||
AsyncWebHandler& setFilter(ArRequestFilterFunction fn) { _filter = fn; return *this; }
|
AsyncWebHandler& setFilter(ArRequestFilterFunction fn) {
|
||||||
AsyncWebHandler& setAuthentication(const char *username, const char *password){ _username = String(username);_password = String(password); return *this; };
|
_filter = fn;
|
||||||
AsyncWebHandler& setAuthentication(const String& username, const String& password){ _username = username;_password = password; return *this; };
|
return *this;
|
||||||
bool filter(AsyncWebServerRequest *request){ return _filter == NULL || _filter(request); }
|
}
|
||||||
virtual ~AsyncWebHandler(){}
|
AsyncWebHandler& setAuthentication(const char* username, const char* password) {
|
||||||
virtual bool canHandle(AsyncWebServerRequest *request __attribute__((unused))){
|
_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))) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
virtual void handleRequest(AsyncWebServerRequest *request __attribute__((unused))){}
|
virtual void handleRequest(AsyncWebServerRequest* request __attribute__((unused))) {}
|
||||||
virtual void handleUpload(AsyncWebServerRequest *request __attribute__((unused)), const String& filename __attribute__((unused)), size_t index __attribute__((unused)), uint8_t *data __attribute__((unused)), size_t len __attribute__((unused)), bool final __attribute__((unused))){}
|
virtual void handleUpload(AsyncWebServerRequest* request __attribute__((unused)), const String& filename __attribute__((unused)), size_t index __attribute__((unused)), uint8_t* data __attribute__((unused)), size_t len __attribute__((unused)), bool final __attribute__((unused))) {}
|
||||||
virtual void handleBody(AsyncWebServerRequest *request __attribute__((unused)), uint8_t *data __attribute__((unused)), size_t len __attribute__((unused)), size_t index __attribute__((unused)), size_t total __attribute__((unused))){}
|
virtual void handleBody(AsyncWebServerRequest* request __attribute__((unused)), uint8_t* data __attribute__((unused)), size_t len __attribute__((unused)), size_t index __attribute__((unused)), size_t total __attribute__((unused))) {}
|
||||||
virtual bool isRequestHandlerTrivial(){return true;}
|
virtual bool isRequestHandlerTrivial() { return true; }
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -380,7 +488,12 @@ class AsyncWebHandler {
|
|||||||
* */
|
* */
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
RESPONSE_SETUP, RESPONSE_HEADERS, RESPONSE_CONTENT, RESPONSE_WAIT_ACK, RESPONSE_END, RESPONSE_FAILED
|
RESPONSE_SETUP,
|
||||||
|
RESPONSE_HEADERS,
|
||||||
|
RESPONSE_CONTENT,
|
||||||
|
RESPONSE_WAIT_ACK,
|
||||||
|
RESPONSE_END,
|
||||||
|
RESPONSE_FAILED
|
||||||
} WebResponseState;
|
} WebResponseState;
|
||||||
|
|
||||||
class AsyncWebServerResponse {
|
class AsyncWebServerResponse {
|
||||||
@ -397,8 +510,9 @@ class AsyncWebServerResponse {
|
|||||||
size_t _writtenLength;
|
size_t _writtenLength;
|
||||||
WebResponseState _state;
|
WebResponseState _state;
|
||||||
const char* _responseCodeToString(int code);
|
const char* _responseCodeToString(int code);
|
||||||
public:
|
|
||||||
static const __FlashStringHelper *responseCodeToString(int code);
|
public:
|
||||||
|
static const __FlashStringHelper* responseCodeToString(int code);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
AsyncWebServerResponse();
|
AsyncWebServerResponse();
|
||||||
@ -412,23 +526,23 @@ public:
|
|||||||
virtual bool _finished() const;
|
virtual bool _finished() const;
|
||||||
virtual bool _failed() const;
|
virtual bool _failed() const;
|
||||||
virtual bool _sourceValid() const;
|
virtual bool _sourceValid() const;
|
||||||
virtual void _respond(AsyncWebServerRequest *request);
|
virtual void _respond(AsyncWebServerRequest* request);
|
||||||
virtual size_t _ack(AsyncWebServerRequest *request, size_t len, uint32_t time);
|
virtual size_t _ack(AsyncWebServerRequest* request, size_t len, uint32_t time);
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* SERVER :: One instance
|
* SERVER :: One instance
|
||||||
* */
|
* */
|
||||||
|
|
||||||
typedef std::function<void(AsyncWebServerRequest *request)> ArRequestHandlerFunction;
|
typedef std::function<void(AsyncWebServerRequest* request)> ArRequestHandlerFunction;
|
||||||
typedef std::function<void(AsyncWebServerRequest *request, const String& filename, size_t index, uint8_t *data, size_t len, bool final)> ArUploadHandlerFunction;
|
typedef std::function<void(AsyncWebServerRequest* request, const String& filename, size_t index, uint8_t* data, size_t len, bool final)> ArUploadHandlerFunction;
|
||||||
typedef std::function<void(AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total)> ArBodyHandlerFunction;
|
typedef std::function<void(AsyncWebServerRequest* request, uint8_t* data, size_t len, size_t index, size_t total)> ArBodyHandlerFunction;
|
||||||
|
|
||||||
class AsyncWebServer {
|
class AsyncWebServer {
|
||||||
protected:
|
protected:
|
||||||
AsyncServer _server;
|
AsyncServer _server;
|
||||||
LinkedList<AsyncWebRewrite*> _rewrites;
|
std::list<std::shared_ptr<AsyncWebRewrite>> _rewrites;
|
||||||
LinkedList<AsyncWebHandler*> _handlers;
|
std::list<std::unique_ptr<AsyncWebHandler>> _handlers;
|
||||||
AsyncCallbackWebHandler* _catchAllHandler;
|
AsyncCallbackWebHandler* _catchAllHandler;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@ -440,13 +554,50 @@ class AsyncWebServer {
|
|||||||
|
|
||||||
#if ASYNC_TCP_SSL_ENABLED
|
#if ASYNC_TCP_SSL_ENABLED
|
||||||
void onSslFileRequest(AcSSlFileHandler cb, void* arg);
|
void onSslFileRequest(AcSSlFileHandler cb, void* arg);
|
||||||
void beginSecure(const char *cert, const char *private_key_file, const char *password);
|
void beginSecure(const char* cert, const char* private_key_file, const char* password);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
AsyncWebRewrite& addRewrite(AsyncWebRewrite* rewrite);
|
AsyncWebRewrite& addRewrite(AsyncWebRewrite* rewrite);
|
||||||
bool removeRewrite(AsyncWebRewrite* rewrite);
|
|
||||||
|
/**
|
||||||
|
* @brief (compat) Add url rewrite rule by pointer
|
||||||
|
* a deep copy of the pounter object will be created,
|
||||||
|
* it is up to user to manage further lifetime of the object in argument
|
||||||
|
*
|
||||||
|
* @param rewrite pointer to rewrite object to copy setting from
|
||||||
|
* @return AsyncWebRewrite& reference to a newly created rewrite rule
|
||||||
|
*/
|
||||||
|
AsyncWebRewrite& addRewrite(std::shared_ptr<AsyncWebRewrite> rewrite);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief add url rewrite rule
|
||||||
|
*
|
||||||
|
* @param from
|
||||||
|
* @param to
|
||||||
|
* @return AsyncWebRewrite&
|
||||||
|
*/
|
||||||
AsyncWebRewrite& rewrite(const char* from, const char* to);
|
AsyncWebRewrite& rewrite(const char* from, const char* to);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief (compat) remove rewrite rule via referenced object
|
||||||
|
* this will NOT deallocate pointed object itself, internal rule with same from/to urls will be removed if any
|
||||||
|
* it's a compat method, better use `removeRewrite(const char* from, const char* to)`
|
||||||
|
* @param rewrite
|
||||||
|
* @return true
|
||||||
|
* @return false
|
||||||
|
*/
|
||||||
|
bool removeRewrite(AsyncWebRewrite* rewrite);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief remove rewrite rule
|
||||||
|
*
|
||||||
|
* @param from
|
||||||
|
* @param to
|
||||||
|
* @return true
|
||||||
|
* @return false
|
||||||
|
*/
|
||||||
|
bool removeRewrite(const char* from, const char* to);
|
||||||
|
|
||||||
AsyncWebHandler& addHandler(AsyncWebHandler* handler);
|
AsyncWebHandler& addHandler(AsyncWebHandler* handler);
|
||||||
bool removeHandler(AsyncWebHandler* handler);
|
bool removeHandler(AsyncWebHandler* handler);
|
||||||
|
|
||||||
@ -457,45 +608,45 @@ class AsyncWebServer {
|
|||||||
|
|
||||||
AsyncStaticWebHandler& serveStatic(const char* uri, fs::FS& fs, const char* path, const char* cache_control = NULL);
|
AsyncStaticWebHandler& serveStatic(const char* uri, fs::FS& fs, const char* path, const char* cache_control = NULL);
|
||||||
|
|
||||||
void onNotFound(ArRequestHandlerFunction fn); //called when handler is not assigned
|
void onNotFound(ArRequestHandlerFunction fn); // called when handler is not assigned
|
||||||
void onFileUpload(ArUploadHandlerFunction fn); //handle file uploads
|
void onFileUpload(ArUploadHandlerFunction fn); // handle file uploads
|
||||||
void onRequestBody(ArBodyHandlerFunction fn); //handle posts with plain body content (JSON often transmitted this way as a request)
|
void onRequestBody(ArBodyHandlerFunction fn); // handle posts with plain body content (JSON often transmitted this way as a request)
|
||||||
|
|
||||||
void reset(); //remove all writers and handlers, with onNotFound/onFileUpload/onRequestBody
|
void reset(); // remove all writers and handlers, with onNotFound/onFileUpload/onRequestBody
|
||||||
|
|
||||||
void _handleDisconnect(AsyncWebServerRequest *request);
|
void _handleDisconnect(AsyncWebServerRequest* request);
|
||||||
void _attachHandler(AsyncWebServerRequest *request);
|
void _attachHandler(AsyncWebServerRequest* request);
|
||||||
void _rewriteRequest(AsyncWebServerRequest *request);
|
void _rewriteRequest(AsyncWebServerRequest* request);
|
||||||
};
|
};
|
||||||
|
|
||||||
class DefaultHeaders {
|
class DefaultHeaders {
|
||||||
using headers_t = std::list<AsyncWebHeader>;
|
using headers_t = std::list<AsyncWebHeader>;
|
||||||
headers_t _headers;
|
headers_t _headers;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
DefaultHeaders() = default;
|
DefaultHeaders() = default;
|
||||||
|
|
||||||
using ConstIterator = headers_t::const_iterator;
|
using ConstIterator = headers_t::const_iterator;
|
||||||
|
|
||||||
void addHeader(const String& name, const String& value){
|
void addHeader(const String& name, const String& value) {
|
||||||
_headers.emplace_back(name, value);
|
_headers.emplace_back(name, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
ConstIterator begin() const { return _headers.begin(); }
|
ConstIterator begin() const { return _headers.begin(); }
|
||||||
ConstIterator end() const { return _headers.end(); }
|
ConstIterator end() const { return _headers.end(); }
|
||||||
|
|
||||||
DefaultHeaders(DefaultHeaders const &) = delete;
|
DefaultHeaders(DefaultHeaders const&) = delete;
|
||||||
DefaultHeaders &operator=(DefaultHeaders const &) = delete;
|
DefaultHeaders& operator=(DefaultHeaders const&) = delete;
|
||||||
|
|
||||||
static DefaultHeaders &Instance() {
|
static DefaultHeaders& Instance() {
|
||||||
static DefaultHeaders instance;
|
static DefaultHeaders instance;
|
||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
#include "WebResponseImpl.h"
|
|
||||||
#include "WebHandlerImpl.h"
|
|
||||||
#include "AsyncWebSocket.h"
|
|
||||||
#include "AsyncEventSource.h"
|
#include "AsyncEventSource.h"
|
||||||
|
#include "AsyncWebSocket.h"
|
||||||
|
#include "WebHandlerImpl.h"
|
||||||
|
#include "WebResponseImpl.h"
|
||||||
|
|
||||||
#endif /* _AsyncWebServer_H_ */
|
#endif /* _AsyncWebServer_H_ */
|
||||||
|
@ -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_ */
|
|
@ -20,41 +20,40 @@
|
|||||||
*/
|
*/
|
||||||
#include "WebAuthentication.h"
|
#include "WebAuthentication.h"
|
||||||
#include <libb64/cencode.h>
|
#include <libb64/cencode.h>
|
||||||
#ifdef ESP32
|
#if defined(ESP32) || defined(TARGET_RP2040)
|
||||||
#include <MD5Builder.h>
|
#include <MD5Builder.h>
|
||||||
#else
|
#else
|
||||||
#include "md5.h"
|
#include "md5.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
// Basic Auth hash = base64("username:password")
|
// Basic Auth hash = base64("username:password")
|
||||||
|
|
||||||
bool checkBasicAuthentication(const char * hash, const char * username, const char * password){
|
bool checkBasicAuthentication(const char* hash, const char* username, const char* password) {
|
||||||
if(username == NULL || password == NULL || hash == NULL)
|
if (username == NULL || password == NULL || hash == NULL)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
size_t toencodeLen = strlen(username)+strlen(password)+1;
|
size_t toencodeLen = strlen(username) + strlen(password) + 1;
|
||||||
size_t encodedLen = base64_encode_expected_len(toencodeLen);
|
size_t encodedLen = base64_encode_expected_len(toencodeLen);
|
||||||
if(strlen(hash) != encodedLen)
|
if (strlen(hash) != encodedLen)
|
||||||
// Fix from https://github.com/me-no-dev/ESPAsyncWebServer/issues/667
|
// Fix from https://github.com/me-no-dev/ESPAsyncWebServer/issues/667
|
||||||
#ifdef ARDUINO_ARCH_ESP32
|
#ifdef ARDUINO_ARCH_ESP32
|
||||||
if(strlen(hash) != encodedLen)
|
if (strlen(hash) != encodedLen)
|
||||||
#else
|
#else
|
||||||
if (strlen(hash) != encodedLen - 1)
|
if (strlen(hash) != encodedLen - 1)
|
||||||
#endif
|
#endif
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
char *toencode = new char[toencodeLen+1];
|
char* toencode = new char[toencodeLen + 1];
|
||||||
if(toencode == NULL){
|
if (toencode == NULL) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
char *encoded = new char[base64_encode_expected_len(toencodeLen)+1];
|
char* encoded = new char[base64_encode_expected_len(toencodeLen) + 1];
|
||||||
if(encoded == NULL){
|
if (encoded == NULL) {
|
||||||
delete[] toencode;
|
delete[] toencode;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
sprintf_P(toencode, PSTR("%s:%s"), username, password);
|
sprintf_P(toencode, PSTR("%s:%s"), username, password);
|
||||||
if(base64_encode_chars(toencode, toencodeLen, encoded) > 0 && memcmp(hash, encoded, encodedLen) == 0){
|
if (base64_encode_chars(toencode, toencodeLen, encoded) > 0 && memcmp(hash, encoded, encodedLen) == 0) {
|
||||||
delete[] toencode;
|
delete[] toencode;
|
||||||
delete[] encoded;
|
delete[] encoded;
|
||||||
return true;
|
return true;
|
||||||
@ -64,8 +63,8 @@ bool checkBasicAuthentication(const char * hash, const char * username, const ch
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool getMD5(uint8_t * data, uint16_t len, char * output){//33 bytes or more
|
static bool getMD5(uint8_t* data, uint16_t len, char* output) { // 33 bytes or more
|
||||||
#ifdef ESP32
|
#if defined(ESP32) || defined(TARGET_RP2040)
|
||||||
MD5Builder md5;
|
MD5Builder md5;
|
||||||
md5.begin();
|
md5.begin();
|
||||||
md5.add(data, len);
|
md5.add(data, len);
|
||||||
@ -73,9 +72,9 @@ static bool getMD5(uint8_t * data, uint16_t len, char * output){//33 bytes or mo
|
|||||||
md5.getChars(output);
|
md5.getChars(output);
|
||||||
#else
|
#else
|
||||||
md5_context_t _ctx;
|
md5_context_t _ctx;
|
||||||
|
|
||||||
uint8_t * _buf = (uint8_t*)malloc(16);
|
uint8_t* _buf = (uint8_t*)malloc(16);
|
||||||
if(_buf == NULL)
|
if (_buf == NULL)
|
||||||
return false;
|
return false;
|
||||||
memset(_buf, 0x00, 16);
|
memset(_buf, 0x00, 16);
|
||||||
|
|
||||||
@ -83,7 +82,7 @@ static bool getMD5(uint8_t * data, uint16_t len, char * output){//33 bytes or mo
|
|||||||
MD5Update(&_ctx, data, len);
|
MD5Update(&_ctx, data, len);
|
||||||
MD5Final(_buf, &_ctx);
|
MD5Final(_buf, &_ctx);
|
||||||
|
|
||||||
for(uint8_t i = 0; i < 16; i++) {
|
for (uint8_t i = 0; i < 16; i++) {
|
||||||
sprintf_P(output + (i * 2), PSTR("%02x"), _buf[i]);
|
sprintf_P(output + (i * 2), PSTR("%02x"), _buf[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -92,50 +91,50 @@ static bool getMD5(uint8_t * data, uint16_t len, char * output){//33 bytes or mo
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static String genRandomMD5(){
|
static String genRandomMD5() {
|
||||||
#ifdef ESP8266
|
#ifdef ESP8266
|
||||||
uint32_t r = RANDOM_REG32;
|
uint32_t r = RANDOM_REG32;
|
||||||
#else
|
#else
|
||||||
uint32_t r = rand();
|
uint32_t r = rand();
|
||||||
#endif
|
#endif
|
||||||
char * out = (char*)malloc(33);
|
char* out = (char*)malloc(33);
|
||||||
if(out == NULL || !getMD5((uint8_t*)(&r), 4, out))
|
if (out == NULL || !getMD5((uint8_t*)(&r), 4, out))
|
||||||
return emptyString;
|
return emptyString;
|
||||||
String res = String(out);
|
String res = String(out);
|
||||||
free(out);
|
free(out);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
static String stringMD5(const String& in){
|
static String stringMD5(const String& in) {
|
||||||
char * out = (char*)malloc(33);
|
char* out = (char*)malloc(33);
|
||||||
if(out == NULL || !getMD5((uint8_t*)(in.c_str()), in.length(), out))
|
if (out == NULL || !getMD5((uint8_t*)(in.c_str()), in.length(), out))
|
||||||
return emptyString;
|
return emptyString;
|
||||||
String res = String(out);
|
String res = String(out);
|
||||||
free(out);
|
free(out);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
String generateDigestHash(const char * username, const char * password, const char * realm){
|
String generateDigestHash(const char* username, const char* password, const char* realm) {
|
||||||
if(username == NULL || password == NULL || realm == NULL){
|
if (username == NULL || password == NULL || realm == NULL) {
|
||||||
return emptyString;
|
return emptyString;
|
||||||
}
|
}
|
||||||
char * out = (char*)malloc(33);
|
char* out = (char*)malloc(33);
|
||||||
String res = String(username);
|
String res = String(username);
|
||||||
res += ':';
|
res += ':';
|
||||||
res.concat(realm);
|
res.concat(realm);
|
||||||
res += ':';
|
res += ':';
|
||||||
String in = res;
|
String in = res;
|
||||||
in.concat(password);
|
in.concat(password);
|
||||||
if(out == NULL || !getMD5((uint8_t*)(in.c_str()), in.length(), out))
|
if (out == NULL || !getMD5((uint8_t*)(in.c_str()), in.length(), out))
|
||||||
return emptyString;
|
return emptyString;
|
||||||
res.concat(out);
|
res.concat(out);
|
||||||
free(out);
|
free(out);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
String requestDigestAuthentication(const char * realm){
|
String requestDigestAuthentication(const char* realm) {
|
||||||
String header = F("realm=\"");
|
String header = F("realm=\"");
|
||||||
if(realm == NULL)
|
if (realm == NULL)
|
||||||
header.concat(F("asyncesp"));
|
header.concat(F("asyncesp"));
|
||||||
else
|
else
|
||||||
header.concat(realm);
|
header.concat(realm);
|
||||||
@ -147,95 +146,100 @@ String requestDigestAuthentication(const char * realm){
|
|||||||
return header;
|
return header;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool checkDigestAuthentication(const char * header, const __FlashStringHelper *method, const char * username, const char * password, const char * realm, bool passwordIsHash, const char * nonce, const char * opaque, const char * uri){
|
#ifndef ESP8266
|
||||||
if(username == NULL || password == NULL || header == NULL || method == NULL){
|
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)
|
||||||
//os_printf("AUTH FAIL: missing requred fields\n");
|
#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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
String myHeader = String(header);
|
String myHeader(header);
|
||||||
int nextBreak = myHeader.indexOf(',');
|
int nextBreak = myHeader.indexOf(',');
|
||||||
if(nextBreak < 0){
|
if (nextBreak < 0) {
|
||||||
//os_printf("AUTH FAIL: no variables\n");
|
// os_printf("AUTH FAIL: no variables\n");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
String myUsername = String();
|
String myUsername;
|
||||||
String myRealm = String();
|
String myRealm;
|
||||||
String myNonce = String();
|
String myNonce;
|
||||||
String myUri = String();
|
String myUri;
|
||||||
String myResponse = String();
|
String myResponse;
|
||||||
String myQop = String();
|
String myQop;
|
||||||
String myNc = String();
|
String myNc;
|
||||||
String myCnonce = String();
|
String myCnonce;
|
||||||
|
|
||||||
myHeader += F(", ");
|
myHeader += F(", ");
|
||||||
do {
|
do {
|
||||||
String avLine = myHeader.substring(0, nextBreak);
|
String avLine(myHeader.substring(0, nextBreak));
|
||||||
avLine.trim();
|
avLine.trim();
|
||||||
myHeader = myHeader.substring(nextBreak+1);
|
myHeader = myHeader.substring(nextBreak + 1);
|
||||||
nextBreak = myHeader.indexOf(',');
|
nextBreak = myHeader.indexOf(',');
|
||||||
|
|
||||||
int eqSign = avLine.indexOf('=');
|
int eqSign = avLine.indexOf('=');
|
||||||
if(eqSign < 0){
|
if (eqSign < 0) {
|
||||||
//os_printf("AUTH FAIL: no = sign\n");
|
// os_printf("AUTH FAIL: no = sign\n");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
String varName = avLine.substring(0, eqSign);
|
String varName(avLine.substring(0, eqSign));
|
||||||
avLine = avLine.substring(eqSign + 1);
|
avLine = avLine.substring(eqSign + 1);
|
||||||
if(avLine.startsWith(String('"'))){
|
if (avLine.startsWith(String('"'))) {
|
||||||
avLine = avLine.substring(1, avLine.length() - 1);
|
avLine = avLine.substring(1, avLine.length() - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(varName.equals(F("username"))){
|
if (varName.equals(F("username"))) {
|
||||||
if(!avLine.equals(username)){
|
if (!avLine.equals(username)) {
|
||||||
//os_printf("AUTH FAIL: username\n");
|
// os_printf("AUTH FAIL: username\n");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
myUsername = avLine;
|
myUsername = avLine;
|
||||||
} else if(varName.equals(F("realm"))){
|
} else if (varName.equals(F("realm"))) {
|
||||||
if(realm != NULL && !avLine.equals(realm)){
|
if (realm != NULL && !avLine.equals(realm)) {
|
||||||
//os_printf("AUTH FAIL: realm\n");
|
// os_printf("AUTH FAIL: realm\n");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
myRealm = avLine;
|
myRealm = avLine;
|
||||||
} else if(varName.equals(F("nonce"))){
|
} else if (varName.equals(F("nonce"))) {
|
||||||
if(nonce != NULL && !avLine.equals(nonce)){
|
if (nonce != NULL && !avLine.equals(nonce)) {
|
||||||
//os_printf("AUTH FAIL: nonce\n");
|
// os_printf("AUTH FAIL: nonce\n");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
myNonce = avLine;
|
myNonce = avLine;
|
||||||
} else if(varName.equals(F("opaque"))){
|
} else if (varName.equals(F("opaque"))) {
|
||||||
if(opaque != NULL && !avLine.equals(opaque)){
|
if (opaque != NULL && !avLine.equals(opaque)) {
|
||||||
//os_printf("AUTH FAIL: opaque\n");
|
// os_printf("AUTH FAIL: opaque\n");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else if(varName.equals(F("uri"))){
|
} else if (varName.equals(F("uri"))) {
|
||||||
if(uri != NULL && !avLine.equals(uri)){
|
if (uri != NULL && !avLine.equals(uri)) {
|
||||||
//os_printf("AUTH FAIL: uri\n");
|
// os_printf("AUTH FAIL: uri\n");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
myUri = avLine;
|
myUri = avLine;
|
||||||
} else if(varName.equals(F("response"))){
|
} else if (varName.equals(F("response"))) {
|
||||||
myResponse = avLine;
|
myResponse = avLine;
|
||||||
} else if(varName.equals(F("qop"))){
|
} else if (varName.equals(F("qop"))) {
|
||||||
myQop = avLine;
|
myQop = avLine;
|
||||||
} else if(varName.equals(F("nc"))){
|
} else if (varName.equals(F("nc"))) {
|
||||||
myNc = avLine;
|
myNc = avLine;
|
||||||
} else if(varName.equals(F("cnonce"))){
|
} else if (varName.equals(F("cnonce"))) {
|
||||||
myCnonce = avLine;
|
myCnonce = avLine;
|
||||||
}
|
}
|
||||||
} while(nextBreak > 0);
|
} while (nextBreak > 0);
|
||||||
|
|
||||||
String ha1 = (passwordIsHash) ? String(password) : stringMD5(myUsername + ':' + myRealm + ':' + String(password));
|
String ha1 = (passwordIsHash) ? String(password) : stringMD5(myUsername + ':' + myRealm + ':' + password);
|
||||||
String ha2 = String(method) + ':' + myUri;
|
String ha2 = String(method) + ':' + myUri;
|
||||||
String response = ha1 + ':' + myNonce + ':' + myNc + ':' + myCnonce + ':' + myQop + ':' + stringMD5(ha2);
|
String response = ha1 + ':' + myNonce + ':' + myNc + ':' + myCnonce + ':' + myQop + ':' + stringMD5(ha2);
|
||||||
|
|
||||||
if(myResponse.equals(stringMD5(response))){
|
if (myResponse.equals(stringMD5(response))) {
|
||||||
//os_printf("AUTH SUCCESS\n");
|
// os_printf("AUTH SUCCESS\n");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
//os_printf("AUTH FAIL: password\n");
|
// os_printf("AUTH FAIL: password\n");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -24,11 +24,16 @@
|
|||||||
|
|
||||||
#include "Arduino.h"
|
#include "Arduino.h"
|
||||||
|
|
||||||
bool checkBasicAuthentication(const char * header, const char * username, const char * password);
|
bool checkBasicAuthentication(const char* header, const char* username, const char* password);
|
||||||
String requestDigestAuthentication(const char * realm);
|
String requestDigestAuthentication(const char* realm);
|
||||||
bool checkDigestAuthentication(const char * header, const __FlashStringHelper *method, const char * username, const char * password, const char * realm, bool passwordIsHash, const char * nonce, const char * opaque, const char * uri);
|
|
||||||
|
|
||||||
//for storing hashed versions on the device that can be authenticated against
|
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);
|
||||||
String generateDigestHash(const char * username, const char * password, const char * realm);
|
|
||||||
|
#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);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -23,19 +23,21 @@
|
|||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#ifdef ASYNCWEBSERVER_REGEX
|
#ifdef ASYNCWEBSERVER_REGEX
|
||||||
#include <regex>
|
#include <regex>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "stddef.h"
|
#include "stddef.h"
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
|
|
||||||
class AsyncStaticWebHandler: public AsyncWebHandler {
|
class AsyncStaticWebHandler : public AsyncWebHandler {
|
||||||
using File = fs::File;
|
using File = fs::File;
|
||||||
using FS = fs::FS;
|
using FS = fs::FS;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool _getFile(AsyncWebServerRequest *request);
|
bool _getFile(AsyncWebServerRequest* request);
|
||||||
bool _fileExists(AsyncWebServerRequest *request, const String& path);
|
bool _fileExists(AsyncWebServerRequest* request, const String& path);
|
||||||
uint8_t _countBits(const uint8_t value) const;
|
uint8_t _countBits(const uint8_t value) const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
FS _fs;
|
FS _fs;
|
||||||
String _uri;
|
String _uri;
|
||||||
@ -47,23 +49,27 @@ class AsyncStaticWebHandler: public AsyncWebHandler {
|
|||||||
bool _isDir;
|
bool _isDir;
|
||||||
bool _gzipFirst;
|
bool _gzipFirst;
|
||||||
uint8_t _gzipStats;
|
uint8_t _gzipStats;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
AsyncStaticWebHandler(const char* uri, FS& fs, const char* path, const char* cache_control);
|
AsyncStaticWebHandler(const char* uri, FS& fs, const char* path, const char* cache_control);
|
||||||
virtual bool canHandle(AsyncWebServerRequest *request) override final;
|
virtual bool canHandle(AsyncWebServerRequest* request) override final;
|
||||||
virtual void handleRequest(AsyncWebServerRequest *request) override final;
|
virtual void handleRequest(AsyncWebServerRequest* request) override final;
|
||||||
AsyncStaticWebHandler& setIsDir(bool isDir);
|
AsyncStaticWebHandler& setIsDir(bool isDir);
|
||||||
AsyncStaticWebHandler& setDefaultFile(const char* filename);
|
AsyncStaticWebHandler& setDefaultFile(const char* filename);
|
||||||
AsyncStaticWebHandler& setCacheControl(const char* cache_control);
|
AsyncStaticWebHandler& setCacheControl(const char* cache_control);
|
||||||
AsyncStaticWebHandler& setLastModified(const char* last_modified);
|
AsyncStaticWebHandler& setLastModified(const char* last_modified);
|
||||||
AsyncStaticWebHandler& setLastModified(struct tm* last_modified);
|
AsyncStaticWebHandler& setLastModified(struct tm* last_modified);
|
||||||
#ifdef ESP8266
|
#ifdef ESP8266
|
||||||
AsyncStaticWebHandler& setLastModified(time_t last_modified);
|
AsyncStaticWebHandler& setLastModified(time_t last_modified);
|
||||||
AsyncStaticWebHandler& setLastModified(); //sets to current time. Make sure sntp is runing and time is updated
|
AsyncStaticWebHandler& setLastModified(); // sets to current time. Make sure sntp is runing and time is updated
|
||||||
#endif
|
#endif
|
||||||
AsyncStaticWebHandler& setTemplateProcessor(AwsTemplateProcessor newCallback) {_callback = newCallback; return *this;}
|
AsyncStaticWebHandler& setTemplateProcessor(AwsTemplateProcessor newCallback) {
|
||||||
|
_callback = newCallback;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class AsyncCallbackWebHandler: public AsyncWebHandler {
|
class AsyncCallbackWebHandler : public AsyncWebHandler {
|
||||||
private:
|
private:
|
||||||
protected:
|
protected:
|
||||||
String _uri;
|
String _uri;
|
||||||
@ -72,23 +78,24 @@ class AsyncCallbackWebHandler: public AsyncWebHandler {
|
|||||||
ArUploadHandlerFunction _onUpload;
|
ArUploadHandlerFunction _onUpload;
|
||||||
ArBodyHandlerFunction _onBody;
|
ArBodyHandlerFunction _onBody;
|
||||||
bool _isRegex;
|
bool _isRegex;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
AsyncCallbackWebHandler() : _uri(), _method(HTTP_ANY), _onRequest(NULL), _onUpload(NULL), _onBody(NULL), _isRegex(false) {}
|
AsyncCallbackWebHandler() : _uri(), _method(HTTP_ANY), _onRequest(NULL), _onUpload(NULL), _onBody(NULL), _isRegex(false) {}
|
||||||
void setUri(const String& uri){
|
void setUri(const String& uri) {
|
||||||
_uri = uri;
|
_uri = uri;
|
||||||
_isRegex = uri.startsWith("^") && uri.endsWith("$");
|
_isRegex = uri.startsWith("^") && uri.endsWith("$");
|
||||||
}
|
}
|
||||||
void setMethod(WebRequestMethodComposite method){ _method = method; }
|
void setMethod(WebRequestMethodComposite method) { _method = method; }
|
||||||
void onRequest(ArRequestHandlerFunction fn){ _onRequest = fn; }
|
void onRequest(ArRequestHandlerFunction fn) { _onRequest = fn; }
|
||||||
void onUpload(ArUploadHandlerFunction fn){ _onUpload = fn; }
|
void onUpload(ArUploadHandlerFunction fn) { _onUpload = fn; }
|
||||||
void onBody(ArBodyHandlerFunction fn){ _onBody = fn; }
|
void onBody(ArBodyHandlerFunction fn) { _onBody = fn; }
|
||||||
|
|
||||||
virtual bool canHandle(AsyncWebServerRequest *request) override final{
|
virtual bool canHandle(AsyncWebServerRequest* request) override final {
|
||||||
|
|
||||||
if(!_onRequest)
|
if (!_onRequest)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if(!(_method & request->method()))
|
if (!(_method & request->method()))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
#ifdef ASYNCWEBSERVER_REGEX
|
#ifdef ASYNCWEBSERVER_REGEX
|
||||||
@ -96,56 +103,53 @@ class AsyncCallbackWebHandler: public AsyncWebHandler {
|
|||||||
std::regex pattern(_uri.c_str());
|
std::regex pattern(_uri.c_str());
|
||||||
std::smatch matches;
|
std::smatch matches;
|
||||||
std::string s(request->url().c_str());
|
std::string s(request->url().c_str());
|
||||||
if(std::regex_search(s, matches, pattern)) {
|
if (std::regex_search(s, matches, pattern)) {
|
||||||
for (size_t i = 1; i < matches.size(); ++i) { // start from 1
|
for (size_t i = 1; i < matches.size(); ++i) { // start from 1
|
||||||
request->_addPathParam(matches[i].str().c_str());
|
request->_addPathParam(matches[i].str().c_str());
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else
|
} else
|
||||||
#endif
|
#endif
|
||||||
if (_uri.length() && _uri.startsWith("/*.")) {
|
if (_uri.length() && _uri.startsWith("/*.")) {
|
||||||
String uriTemplate = String (_uri);
|
|
||||||
uriTemplate = uriTemplate.substring(uriTemplate.lastIndexOf("."));
|
|
||||||
if (!request->url().endsWith(uriTemplate))
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
if (_uri.length() && _uri.endsWith("*")) {
|
|
||||||
String uriTemplate = String(_uri);
|
String uriTemplate = String(_uri);
|
||||||
uriTemplate = uriTemplate.substring(0, uriTemplate.length() - 1);
|
uriTemplate = uriTemplate.substring(uriTemplate.lastIndexOf("."));
|
||||||
|
if (!request->url().endsWith(uriTemplate))
|
||||||
|
return false;
|
||||||
|
} else if (_uri.length() && _uri.endsWith("*")) {
|
||||||
|
String uriTemplate = String(_uri);
|
||||||
|
uriTemplate = uriTemplate.substring(0, uriTemplate.length() - 1);
|
||||||
if (!request->url().startsWith(uriTemplate))
|
if (!request->url().startsWith(uriTemplate))
|
||||||
return false;
|
return false;
|
||||||
}
|
} else if (_uri.length() && (_uri != request->url() && !request->url().startsWith(_uri + "/")))
|
||||||
else if(_uri.length() && (_uri != request->url() && !request->url().startsWith(_uri+"/")))
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
request->addInterestingHeader("ANY");
|
request->addInterestingHeader("ANY");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void handleRequest(AsyncWebServerRequest *request) override final {
|
virtual void handleRequest(AsyncWebServerRequest* request) override final {
|
||||||
if((_username != "" && _password != "") && !request->authenticate(_username.c_str(), _password.c_str()))
|
if ((_username != "" && _password != "") && !request->authenticate(_username.c_str(), _password.c_str()))
|
||||||
return request->requestAuthentication();
|
return request->requestAuthentication();
|
||||||
if(_onRequest)
|
if (_onRequest)
|
||||||
_onRequest(request);
|
_onRequest(request);
|
||||||
else
|
else
|
||||||
request->send(500);
|
request->send(500);
|
||||||
}
|
}
|
||||||
virtual void handleUpload(AsyncWebServerRequest *request, const String& filename, size_t index, uint8_t *data, size_t len, bool final) override final {
|
virtual void handleUpload(AsyncWebServerRequest* request, const String& filename, size_t index, uint8_t* data, size_t len, bool final) override final {
|
||||||
if((_username != "" && _password != "") && !request->authenticate(_username.c_str(), _password.c_str()))
|
if ((_username != "" && _password != "") && !request->authenticate(_username.c_str(), _password.c_str()))
|
||||||
return request->requestAuthentication();
|
return request->requestAuthentication();
|
||||||
if(_onUpload)
|
if (_onUpload)
|
||||||
_onUpload(request, filename, index, data, len, final);
|
_onUpload(request, filename, index, data, len, final);
|
||||||
}
|
}
|
||||||
virtual void handleBody(AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total) override final {
|
virtual void handleBody(AsyncWebServerRequest* request, uint8_t* data, size_t len, size_t index, size_t total) override final {
|
||||||
if((_username != "" && _password != "") && !request->authenticate(_username.c_str(), _password.c_str()))
|
if ((_username != "" && _password != "") && !request->authenticate(_username.c_str(), _password.c_str()))
|
||||||
return request->requestAuthentication();
|
return request->requestAuthentication();
|
||||||
if(_onBody)
|
if (_onBody)
|
||||||
_onBody(request, data, len, index, total);
|
_onBody(request, data, len, index, total);
|
||||||
}
|
}
|
||||||
virtual bool isRequestHandlerTrivial() override final {return _onRequest ? false : true;}
|
virtual bool isRequestHandlerTrivial() override final { return _onRequest ? false : true; }
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* ASYNCWEBSERVERHANDLERIMPL_H_ */
|
#endif /* ASYNCWEBSERVERHANDLERIMPL_H_ */
|
||||||
|
@ -22,73 +22,73 @@
|
|||||||
#include "WebHandlerImpl.h"
|
#include "WebHandlerImpl.h"
|
||||||
|
|
||||||
AsyncStaticWebHandler::AsyncStaticWebHandler(const char* uri, FS& fs, const char* path, const char* cache_control)
|
AsyncStaticWebHandler::AsyncStaticWebHandler(const char* uri, FS& fs, const char* path, const char* cache_control)
|
||||||
: _fs(fs), _uri(uri), _path(path), _default_file(F("index.htm")), _cache_control(cache_control), _last_modified(), _callback(nullptr)
|
: _fs(fs), _uri(uri), _path(path), _default_file(F("index.htm")), _cache_control(cache_control), _last_modified(), _callback(nullptr) {
|
||||||
{
|
|
||||||
// Ensure leading '/'
|
// Ensure leading '/'
|
||||||
if (_uri.length() == 0 || _uri[0] != '/') _uri = String('/') + _uri;
|
if (_uri.length() == 0 || _uri[0] != '/')
|
||||||
if (_path.length() == 0 || _path[0] != '/') _path = String('/') + _path;
|
_uri = String('/') + _uri;
|
||||||
|
if (_path.length() == 0 || _path[0] != '/')
|
||||||
|
_path = String('/') + _path;
|
||||||
|
|
||||||
// If path ends with '/' we assume a hint that this is a directory to improve performance.
|
// If path ends with '/' we assume a hint that this is a directory to improve performance.
|
||||||
// However - if it does not end with '/' we, can't assume a file, path can still be a directory.
|
// However - if it does not end with '/' we, can't assume a file, path can still be a directory.
|
||||||
_isDir = _path[_path.length()-1] == '/';
|
_isDir = _path[_path.length() - 1] == '/';
|
||||||
|
|
||||||
// Remove the trailing '/' so we can handle default file
|
// Remove the trailing '/' so we can handle default file
|
||||||
// Notice that root will be "" not "/"
|
// Notice that root will be "" not "/"
|
||||||
if (_uri[_uri.length()-1] == '/') _uri = _uri.substring(0, _uri.length()-1);
|
if (_uri[_uri.length() - 1] == '/')
|
||||||
if (_path[_path.length()-1] == '/') _path = _path.substring(0, _path.length()-1);
|
_uri = _uri.substring(0, _uri.length() - 1);
|
||||||
|
if (_path[_path.length() - 1] == '/')
|
||||||
|
_path = _path.substring(0, _path.length() - 1);
|
||||||
|
|
||||||
// Reset stats
|
// Reset stats
|
||||||
_gzipFirst = false;
|
_gzipFirst = false;
|
||||||
_gzipStats = 0xF8;
|
_gzipStats = 0xF8;
|
||||||
}
|
}
|
||||||
|
|
||||||
AsyncStaticWebHandler& AsyncStaticWebHandler::setIsDir(bool isDir){
|
AsyncStaticWebHandler& AsyncStaticWebHandler::setIsDir(bool isDir) {
|
||||||
_isDir = isDir;
|
_isDir = isDir;
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
AsyncStaticWebHandler& AsyncStaticWebHandler::setDefaultFile(const char* filename){
|
AsyncStaticWebHandler& AsyncStaticWebHandler::setDefaultFile(const char* filename) {
|
||||||
_default_file = String(filename);
|
_default_file = String(filename);
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
AsyncStaticWebHandler& AsyncStaticWebHandler::setCacheControl(const char* cache_control){
|
AsyncStaticWebHandler& AsyncStaticWebHandler::setCacheControl(const char* cache_control) {
|
||||||
_cache_control = String(cache_control);
|
_cache_control = String(cache_control);
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
AsyncStaticWebHandler& AsyncStaticWebHandler::setLastModified(const char* last_modified){
|
AsyncStaticWebHandler& AsyncStaticWebHandler::setLastModified(const char* last_modified) {
|
||||||
_last_modified = last_modified;
|
_last_modified = last_modified;
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
AsyncStaticWebHandler& AsyncStaticWebHandler::setLastModified(struct tm* last_modified){
|
AsyncStaticWebHandler& AsyncStaticWebHandler::setLastModified(struct tm* last_modified) {
|
||||||
auto formatP = PSTR("%a, %d %b %Y %H:%M:%S %Z");
|
auto formatP = PSTR("%a, %d %b %Y %H:%M:%S %Z");
|
||||||
char format[strlen_P(formatP) + 1];
|
char format[strlen_P(formatP) + 1];
|
||||||
strcpy_P(format, formatP);
|
strcpy_P(format, formatP);
|
||||||
|
|
||||||
char result[30];
|
char result[30];
|
||||||
strftime(result, sizeof(result), format, last_modified);
|
strftime(result, sizeof(result), format, last_modified);
|
||||||
return setLastModified((const char *)result);
|
return setLastModified((const char*)result);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef ESP8266
|
#ifdef ESP8266
|
||||||
AsyncStaticWebHandler& AsyncStaticWebHandler::setLastModified(time_t last_modified){
|
AsyncStaticWebHandler& AsyncStaticWebHandler::setLastModified(time_t last_modified) {
|
||||||
return setLastModified((struct tm *)gmtime(&last_modified));
|
return setLastModified((struct tm*)gmtime(&last_modified));
|
||||||
}
|
}
|
||||||
|
|
||||||
AsyncStaticWebHandler& AsyncStaticWebHandler::setLastModified(){
|
AsyncStaticWebHandler& AsyncStaticWebHandler::setLastModified() {
|
||||||
time_t last_modified;
|
time_t last_modified;
|
||||||
if(time(&last_modified) == 0) //time is not yet set
|
if (time(&last_modified) == 0) // time is not yet set
|
||||||
return *this;
|
return *this;
|
||||||
return setLastModified(last_modified);
|
return setLastModified(last_modified);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
bool AsyncStaticWebHandler::canHandle(AsyncWebServerRequest *request){
|
bool AsyncStaticWebHandler::canHandle(AsyncWebServerRequest* request) {
|
||||||
if(request->method() != HTTP_GET
|
if (request->method() != HTTP_GET || !request->url().startsWith(_uri) || !request->isExpectedRequestedConnType(RCT_DEFAULT, RCT_HTTP)) {
|
||||||
|| !request->url().startsWith(_uri)
|
|
||||||
|| !request->isExpectedRequestedConnType(RCT_DEFAULT, RCT_HTTP)
|
|
||||||
){
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (_getFile(request)) {
|
if (_getFile(request)) {
|
||||||
@ -96,7 +96,7 @@ bool AsyncStaticWebHandler::canHandle(AsyncWebServerRequest *request){
|
|||||||
if (_last_modified.length())
|
if (_last_modified.length())
|
||||||
request->addInterestingHeader(F("If-Modified-Since"));
|
request->addInterestingHeader(F("If-Modified-Since"));
|
||||||
|
|
||||||
if(_cache_control.length())
|
if (_cache_control.length())
|
||||||
request->addInterestingHeader(F("If-None-Match"));
|
request->addInterestingHeader(F("If-None-Match"));
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@ -105,13 +105,12 @@ bool AsyncStaticWebHandler::canHandle(AsyncWebServerRequest *request){
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AsyncStaticWebHandler::_getFile(AsyncWebServerRequest *request)
|
bool AsyncStaticWebHandler::_getFile(AsyncWebServerRequest* request) {
|
||||||
{
|
|
||||||
// Remove the found uri
|
// Remove the found uri
|
||||||
String path = request->url().substring(_uri.length());
|
String path = request->url().substring(_uri.length());
|
||||||
|
|
||||||
// We can skip the file check and look for default if request is to the root of a directory or that request path ends with '/'
|
// We can skip the file check and look for default if request is to the root of a directory or that request path ends with '/'
|
||||||
bool canSkipFileCheck = (_isDir && path.length() == 0) || (path.length() && path[path.length()-1] == '/');
|
bool canSkipFileCheck = (_isDir && path.length() == 0) || (path.length() && path[path.length() - 1] == '/');
|
||||||
|
|
||||||
path = _path + path;
|
path = _path + path;
|
||||||
|
|
||||||
@ -124,7 +123,7 @@ bool AsyncStaticWebHandler::_getFile(AsyncWebServerRequest *request)
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Try to add default file, ensure there is a trailing '/' ot the path.
|
// Try to add default file, ensure there is a trailing '/' ot the path.
|
||||||
if (path.length() == 0 || path[path.length()-1] != '/')
|
if (path.length() == 0 || path[path.length() - 1] != '/')
|
||||||
path += String('/');
|
path += String('/');
|
||||||
path += _default_file;
|
path += _default_file;
|
||||||
|
|
||||||
@ -132,39 +131,38 @@ bool AsyncStaticWebHandler::_getFile(AsyncWebServerRequest *request)
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ifdef ESP32
|
#ifdef ESP32
|
||||||
#define FILE_IS_REAL(f) (f == true && !f.isDirectory())
|
#define FILE_IS_REAL(f) (f == true && !f.isDirectory())
|
||||||
#else
|
#else
|
||||||
#define FILE_IS_REAL(f) (f == true)
|
#define FILE_IS_REAL(f) (f == true)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
bool AsyncStaticWebHandler::_fileExists(AsyncWebServerRequest *request, const String& path)
|
bool AsyncStaticWebHandler::_fileExists(AsyncWebServerRequest* request, const String& path) {
|
||||||
{
|
|
||||||
bool fileFound = false;
|
bool fileFound = false;
|
||||||
bool gzipFound = false;
|
bool gzipFound = false;
|
||||||
|
|
||||||
String gzip = path + F(".gz");
|
String gzip = path + F(".gz");
|
||||||
|
|
||||||
if (_gzipFirst) {
|
if (_gzipFirst) {
|
||||||
if (_fs.exists(gzip)) {
|
if (_fs.exists(gzip)) {
|
||||||
request->_tempFile = _fs.open(gzip, fs::FileOpenMode::read);
|
|
||||||
gzipFound = FILE_IS_REAL(request->_tempFile);
|
|
||||||
}
|
|
||||||
if (!gzipFound){
|
|
||||||
if (_fs.exists(path)) {
|
|
||||||
request->_tempFile = _fs.open(path, fs::FileOpenMode::read);
|
|
||||||
fileFound = FILE_IS_REAL(request->_tempFile);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (_fs.exists(path)) {
|
|
||||||
request->_tempFile = _fs.open(path, fs::FileOpenMode::read);
|
|
||||||
fileFound = FILE_IS_REAL(request->_tempFile);
|
|
||||||
}
|
|
||||||
if (!fileFound){
|
|
||||||
if (_fs.exists(gzip)) {
|
|
||||||
request->_tempFile = _fs.open(gzip, fs::FileOpenMode::read);
|
request->_tempFile = _fs.open(gzip, fs::FileOpenMode::read);
|
||||||
gzipFound = FILE_IS_REAL(request->_tempFile);
|
gzipFound = FILE_IS_REAL(request->_tempFile);
|
||||||
}
|
}
|
||||||
|
if (!gzipFound) {
|
||||||
|
if (_fs.exists(path)) {
|
||||||
|
request->_tempFile = _fs.open(path, fs::FileOpenMode::read);
|
||||||
|
fileFound = FILE_IS_REAL(request->_tempFile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (_fs.exists(path)) {
|
||||||
|
request->_tempFile = _fs.open(path, fs::FileOpenMode::read);
|
||||||
|
fileFound = FILE_IS_REAL(request->_tempFile);
|
||||||
|
}
|
||||||
|
if (!fileFound) {
|
||||||
|
if (_fs.exists(gzip)) {
|
||||||
|
request->_tempFile = _fs.open(gzip, fs::FileOpenMode::read);
|
||||||
|
gzipFound = FILE_IS_REAL(request->_tempFile);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -173,55 +171,71 @@ bool AsyncStaticWebHandler::_fileExists(AsyncWebServerRequest *request, const St
|
|||||||
if (found) {
|
if (found) {
|
||||||
// Extract the file name from the path and keep it in _tempObject
|
// Extract the file name from the path and keep it in _tempObject
|
||||||
size_t pathLen = path.length();
|
size_t pathLen = path.length();
|
||||||
char * _tempPath = (char*)malloc(pathLen+1);
|
char* _tempPath = (char*)malloc(pathLen + 1);
|
||||||
snprintf_P(_tempPath, pathLen+1, PSTR("%s"), path.c_str());
|
snprintf_P(_tempPath, pathLen + 1, PSTR("%s"), path.c_str());
|
||||||
request->_tempObject = (void*)_tempPath;
|
request->_tempObject = (void*)_tempPath;
|
||||||
|
|
||||||
// Calculate gzip statistic
|
// Calculate gzip statistic
|
||||||
_gzipStats = (_gzipStats << 1) + (gzipFound ? 1 : 0);
|
_gzipStats = (_gzipStats << 1) + (gzipFound ? 1 : 0);
|
||||||
if (_gzipStats == 0x00) _gzipFirst = false; // All files are not gzip
|
if (_gzipStats == 0x00)
|
||||||
else if (_gzipStats == 0xFF) _gzipFirst = true; // All files are gzip
|
_gzipFirst = false; // All files are not gzip
|
||||||
else _gzipFirst = _countBits(_gzipStats) > 4; // IF we have more gzip files - try gzip first
|
else if (_gzipStats == 0xFF)
|
||||||
|
_gzipFirst = true; // All files are gzip
|
||||||
|
else
|
||||||
|
_gzipFirst = _countBits(_gzipStats) > 4; // IF we have more gzip files - try gzip first
|
||||||
}
|
}
|
||||||
|
|
||||||
return found;
|
return found;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t AsyncStaticWebHandler::_countBits(const uint8_t value) const
|
uint8_t AsyncStaticWebHandler::_countBits(const uint8_t value) const {
|
||||||
{
|
|
||||||
uint8_t w = value;
|
uint8_t w = value;
|
||||||
uint8_t n;
|
uint8_t n;
|
||||||
for (n=0; w!=0; n++) w&=w-1;
|
for (n = 0; w != 0; n++)
|
||||||
|
w &= w - 1;
|
||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AsyncStaticWebHandler::handleRequest(AsyncWebServerRequest *request)
|
void AsyncStaticWebHandler::handleRequest(AsyncWebServerRequest* request) {
|
||||||
{
|
|
||||||
// Get the filename from request->_tempObject and free it
|
// Get the filename from request->_tempObject and free it
|
||||||
String filename = String((char*)request->_tempObject);
|
String filename = String((char*)request->_tempObject);
|
||||||
free(request->_tempObject);
|
free(request->_tempObject);
|
||||||
request->_tempObject = NULL;
|
request->_tempObject = NULL;
|
||||||
if((_username.length() && _password.length()) && !request->authenticate(_username.c_str(), _password.c_str()))
|
if ((_username.length() && _password.length()) && !request->authenticate(_username.c_str(), _password.c_str()))
|
||||||
return request->requestAuthentication();
|
return request->requestAuthentication();
|
||||||
|
|
||||||
if (request->_tempFile == true) {
|
if (request->_tempFile == true) {
|
||||||
time_t lw = request->_tempFile.getLastWrite(); // get last file mod time (if supported by FS)
|
time_t lw = request->_tempFile.getLastWrite(); // get last file mod time (if supported by FS)
|
||||||
if (lw) setLastModified(gmtime(&lw));
|
// set etag to lastmod timestamp if available, otherwise to size
|
||||||
String etag(lw ? lw : request->_tempFile.size()); // set etag to lastmod timestamp if available, otherwise to size
|
String etag;
|
||||||
|
if (lw) {
|
||||||
|
setLastModified(gmtime(&lw));
|
||||||
|
#if defined(TARGET_RP2040)
|
||||||
|
// time_t == long long int
|
||||||
|
const size_t len = 1 + 8 * sizeof(time_t);
|
||||||
|
char buf[len];
|
||||||
|
char* ret = lltoa(lw, buf, len, 10);
|
||||||
|
etag = ret ? String(ret) : String(request->_tempFile.size());
|
||||||
|
#else
|
||||||
|
etag = String(lw);
|
||||||
|
#endif
|
||||||
|
} else {
|
||||||
|
etag = String(request->_tempFile.size());
|
||||||
|
}
|
||||||
if (_last_modified.length() && _last_modified == request->header(F("If-Modified-Since"))) {
|
if (_last_modified.length() && _last_modified == request->header(F("If-Modified-Since"))) {
|
||||||
request->_tempFile.close();
|
request->_tempFile.close();
|
||||||
request->send(304); // Not modified
|
request->send(304); // Not modified
|
||||||
} else if (_cache_control.length() && request->hasHeader(F("If-None-Match")) && request->header(F("If-None-Match")).equals(etag)) {
|
} else if (_cache_control.length() && request->hasHeader(F("If-None-Match")) && request->header(F("If-None-Match")).equals(etag)) {
|
||||||
request->_tempFile.close();
|
request->_tempFile.close();
|
||||||
AsyncWebServerResponse * response = new AsyncBasicResponse(304); // Not modified
|
AsyncWebServerResponse* response = new AsyncBasicResponse(304); // Not modified
|
||||||
response->addHeader(F("Cache-Control"), _cache_control);
|
response->addHeader(F("Cache-Control"), _cache_control);
|
||||||
response->addHeader(F("ETag"), etag);
|
response->addHeader(F("ETag"), etag);
|
||||||
request->send(response);
|
request->send(response);
|
||||||
} else {
|
} else {
|
||||||
AsyncWebServerResponse * response = new AsyncFileResponse(request->_tempFile, filename, String(), false, _callback);
|
AsyncWebServerResponse* response = new AsyncFileResponse(request->_tempFile, filename, String(), false, _callback);
|
||||||
if (_last_modified.length())
|
if (_last_modified.length())
|
||||||
response->addHeader(F("Last-Modified"), _last_modified);
|
response->addHeader(F("Last-Modified"), _last_modified);
|
||||||
if (_cache_control.length()){
|
if (_cache_control.length()) {
|
||||||
response->addHeader(F("Cache-Control"), _cache_control);
|
response->addHeader(F("Cache-Control"), _cache_control);
|
||||||
response->addHeader(F("ETag"), etag);
|
response->addHeader(F("ETag"), etag);
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -22,115 +22,125 @@
|
|||||||
#define ASYNCWEBSERVERRESPONSEIMPL_H_
|
#define ASYNCWEBSERVERRESPONSEIMPL_H_
|
||||||
|
|
||||||
#ifdef Arduino_h
|
#ifdef Arduino_h
|
||||||
// arduino is not compatible with std::vector
|
// arduino is not compatible with std::vector
|
||||||
#undef min
|
#undef min
|
||||||
#undef max
|
#undef max
|
||||||
#endif
|
#endif
|
||||||
#include <vector>
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
// It is possible to restore these defines, but one can use _min and _max instead. Or std::min, std::max.
|
// It is possible to restore these defines, but one can use _min and _max instead. Or std::min, std::max.
|
||||||
|
|
||||||
class AsyncBasicResponse: public AsyncWebServerResponse {
|
class AsyncBasicResponse : public AsyncWebServerResponse {
|
||||||
private:
|
private:
|
||||||
String _content;
|
String _content;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
AsyncBasicResponse(int code, const String& contentType=String(), const String& content=String());
|
AsyncBasicResponse(int code, const String& contentType = String(), const String& content = String());
|
||||||
void _respond(AsyncWebServerRequest *request);
|
void _respond(AsyncWebServerRequest* request);
|
||||||
size_t _ack(AsyncWebServerRequest *request, size_t len, uint32_t time);
|
size_t _ack(AsyncWebServerRequest* request, size_t len, uint32_t time);
|
||||||
bool _sourceValid() const { return true; }
|
bool _sourceValid() const { return true; }
|
||||||
};
|
};
|
||||||
|
|
||||||
class AsyncAbstractResponse: public AsyncWebServerResponse {
|
class AsyncAbstractResponse : public AsyncWebServerResponse {
|
||||||
private:
|
private:
|
||||||
String _head;
|
String _head;
|
||||||
// Data is inserted into cache at begin().
|
// Data is inserted into cache at begin().
|
||||||
// This is inefficient with vector, but if we use some other container,
|
// This is inefficient with vector, but if we use some other container,
|
||||||
// we won't be able to access it as contiguous array of bytes when reading from it,
|
// we won't be able to access it as contiguous array of bytes when reading from it,
|
||||||
// so by gaining performance in one place, we'll lose it in another.
|
// so by gaining performance in one place, we'll lose it in another.
|
||||||
std::vector<uint8_t> _cache;
|
std::vector<uint8_t> _cache;
|
||||||
size_t _readDataFromCacheOrContent(uint8_t* data, const size_t len);
|
size_t _readDataFromCacheOrContent(uint8_t* data, const size_t len);
|
||||||
size_t _fillBufferAndProcessTemplates(uint8_t* buf, size_t maxLen);
|
size_t _fillBufferAndProcessTemplates(uint8_t* buf, size_t maxLen);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
AwsTemplateProcessor _callback;
|
AwsTemplateProcessor _callback;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
AsyncAbstractResponse(AwsTemplateProcessor callback=nullptr);
|
AsyncAbstractResponse(AwsTemplateProcessor callback = nullptr);
|
||||||
void _respond(AsyncWebServerRequest *request);
|
void _respond(AsyncWebServerRequest* request);
|
||||||
size_t _ack(AsyncWebServerRequest *request, size_t len, uint32_t time);
|
size_t _ack(AsyncWebServerRequest* request, size_t len, uint32_t time);
|
||||||
bool _sourceValid() const { return false; }
|
bool _sourceValid() const { return false; }
|
||||||
virtual size_t _fillBuffer(uint8_t *buf __attribute__((unused)), size_t maxLen __attribute__((unused))) { return 0; }
|
virtual size_t _fillBuffer(uint8_t* buf __attribute__((unused)), size_t maxLen __attribute__((unused))) { return 0; }
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifndef TEMPLATE_PLACEHOLDER
|
#ifndef TEMPLATE_PLACEHOLDER
|
||||||
#define TEMPLATE_PLACEHOLDER '%'
|
#define TEMPLATE_PLACEHOLDER '%'
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define TEMPLATE_PARAM_NAME_LENGTH 32
|
#define TEMPLATE_PARAM_NAME_LENGTH 32
|
||||||
class AsyncFileResponse: public AsyncAbstractResponse {
|
class AsyncFileResponse : public AsyncAbstractResponse {
|
||||||
using File = fs::File;
|
using File = fs::File;
|
||||||
using FS = fs::FS;
|
using FS = fs::FS;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
File _content;
|
File _content;
|
||||||
String _path;
|
String _path;
|
||||||
void _setContentType(const String& path);
|
void _setContentType(const String& path);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
AsyncFileResponse(FS &fs, const String& path, const String& contentType=String(), bool download=false, AwsTemplateProcessor callback=nullptr);
|
AsyncFileResponse(FS& fs, const String& path, const String& contentType = String(), bool download = false, AwsTemplateProcessor callback = nullptr);
|
||||||
AsyncFileResponse(File content, const String& path, const String& contentType=String(), bool download=false, AwsTemplateProcessor callback=nullptr);
|
AsyncFileResponse(File content, const String& path, const String& contentType = String(), bool download = false, AwsTemplateProcessor callback = nullptr);
|
||||||
~AsyncFileResponse();
|
~AsyncFileResponse();
|
||||||
bool _sourceValid() const { return !!(_content); }
|
bool _sourceValid() const { return !!(_content); }
|
||||||
virtual size_t _fillBuffer(uint8_t *buf, size_t maxLen) override;
|
virtual size_t _fillBuffer(uint8_t* buf, size_t maxLen) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class AsyncStreamResponse: public AsyncAbstractResponse {
|
class AsyncStreamResponse : public AsyncAbstractResponse {
|
||||||
private:
|
private:
|
||||||
Stream *_content;
|
Stream* _content;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
AsyncStreamResponse(Stream &stream, const String& contentType, size_t len, AwsTemplateProcessor callback=nullptr);
|
AsyncStreamResponse(Stream& stream, const String& contentType, size_t len, AwsTemplateProcessor callback = nullptr);
|
||||||
bool _sourceValid() const { return !!(_content); }
|
bool _sourceValid() const { return !!(_content); }
|
||||||
virtual size_t _fillBuffer(uint8_t *buf, size_t maxLen) override;
|
virtual size_t _fillBuffer(uint8_t* buf, size_t maxLen) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class AsyncCallbackResponse: public AsyncAbstractResponse {
|
class AsyncCallbackResponse : public AsyncAbstractResponse {
|
||||||
private:
|
private:
|
||||||
AwsResponseFiller _content;
|
AwsResponseFiller _content;
|
||||||
size_t _filledLength;
|
size_t _filledLength;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
AsyncCallbackResponse(const String& contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback=nullptr);
|
AsyncCallbackResponse(const String& contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback = nullptr);
|
||||||
bool _sourceValid() const { return !!(_content); }
|
bool _sourceValid() const { return !!(_content); }
|
||||||
virtual size_t _fillBuffer(uint8_t *buf, size_t maxLen) override;
|
virtual size_t _fillBuffer(uint8_t* buf, size_t maxLen) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class AsyncChunkedResponse: public AsyncAbstractResponse {
|
class AsyncChunkedResponse : public AsyncAbstractResponse {
|
||||||
private:
|
private:
|
||||||
AwsResponseFiller _content;
|
AwsResponseFiller _content;
|
||||||
size_t _filledLength;
|
size_t _filledLength;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
AsyncChunkedResponse(const String& contentType, AwsResponseFiller callback, AwsTemplateProcessor templateCallback=nullptr);
|
AsyncChunkedResponse(const String& contentType, AwsResponseFiller callback, AwsTemplateProcessor templateCallback = nullptr);
|
||||||
bool _sourceValid() const { return !!(_content); }
|
bool _sourceValid() const { return !!(_content); }
|
||||||
virtual size_t _fillBuffer(uint8_t *buf, size_t maxLen) override;
|
virtual size_t _fillBuffer(uint8_t* buf, size_t maxLen) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class AsyncProgmemResponse: public AsyncAbstractResponse {
|
class AsyncProgmemResponse : public AsyncAbstractResponse {
|
||||||
private:
|
private:
|
||||||
const uint8_t * _content;
|
const uint8_t* _content;
|
||||||
size_t _readLength;
|
size_t _readLength;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
AsyncProgmemResponse(int code, const String& contentType, const uint8_t * content, size_t len, AwsTemplateProcessor callback=nullptr);
|
AsyncProgmemResponse(int code, const String& contentType, const uint8_t* content, size_t len, AwsTemplateProcessor callback = nullptr);
|
||||||
bool _sourceValid() const { return true; }
|
bool _sourceValid() const { return true; }
|
||||||
virtual size_t _fillBuffer(uint8_t *buf, size_t maxLen) override;
|
virtual size_t _fillBuffer(uint8_t* buf, size_t maxLen) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class cbuf;
|
class cbuf;
|
||||||
|
|
||||||
class AsyncResponseStream: public AsyncAbstractResponse, public Print {
|
class AsyncResponseStream : public AsyncAbstractResponse, public Print {
|
||||||
private:
|
private:
|
||||||
std::unique_ptr<cbuf> _content;
|
std::unique_ptr<cbuf> _content;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
AsyncResponseStream(const String& contentType, size_t bufferSize);
|
AsyncResponseStream(const String& contentType, size_t bufferSize);
|
||||||
~AsyncResponseStream();
|
~AsyncResponseStream();
|
||||||
bool _sourceValid() const { return (_state < RESPONSE_END); }
|
bool _sourceValid() const { return (_state < RESPONSE_END); }
|
||||||
virtual size_t _fillBuffer(uint8_t *buf, size_t maxLen) override;
|
virtual size_t _fillBuffer(uint8_t* buf, size_t maxLen) override;
|
||||||
size_t write(const uint8_t *data, size_t len);
|
size_t write(const uint8_t* data, size_t len);
|
||||||
size_t write(uint8_t data);
|
size_t write(uint8_t data);
|
||||||
using Print::write;
|
using Print::write;
|
||||||
};
|
};
|
||||||
|
@ -23,111 +23,140 @@
|
|||||||
#include "cbuf.h"
|
#include "cbuf.h"
|
||||||
|
|
||||||
// Since ESP8266 does not link memchr by default, here's its implementation.
|
// Since ESP8266 does not link memchr by default, here's its implementation.
|
||||||
void* memchr(void* ptr, int ch, size_t count)
|
void* memchr(void* ptr, int ch, size_t count) {
|
||||||
{
|
|
||||||
unsigned char* p = static_cast<unsigned char*>(ptr);
|
unsigned char* p = static_cast<unsigned char*>(ptr);
|
||||||
while(count--)
|
while (count--)
|
||||||
if(*p++ == static_cast<unsigned char>(ch))
|
if (*p++ == static_cast<unsigned char>(ch))
|
||||||
return --p;
|
return --p;
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Abstract Response
|
* Abstract Response
|
||||||
* */
|
* */
|
||||||
const char* AsyncWebServerResponse::_responseCodeToString(int code) {
|
const char* AsyncWebServerResponse::_responseCodeToString(int code) {
|
||||||
return reinterpret_cast<const char *>(responseCodeToString(code));
|
return reinterpret_cast<const char*>(responseCodeToString(code));
|
||||||
}
|
}
|
||||||
|
|
||||||
const __FlashStringHelper *AsyncWebServerResponse::responseCodeToString(int code) {
|
const __FlashStringHelper* AsyncWebServerResponse::responseCodeToString(int code) {
|
||||||
switch (code) {
|
switch (code) {
|
||||||
case 100: return F("Continue");
|
case 100:
|
||||||
case 101: return F("Switching Protocols");
|
return F("Continue");
|
||||||
case 200: return F("OK");
|
case 101:
|
||||||
case 201: return F("Created");
|
return F("Switching Protocols");
|
||||||
case 202: return F("Accepted");
|
case 200:
|
||||||
case 203: return F("Non-Authoritative Information");
|
return F("OK");
|
||||||
case 204: return F("No Content");
|
case 201:
|
||||||
case 205: return F("Reset Content");
|
return F("Created");
|
||||||
case 206: return F("Partial Content");
|
case 202:
|
||||||
case 300: return F("Multiple Choices");
|
return F("Accepted");
|
||||||
case 301: return F("Moved Permanently");
|
case 203:
|
||||||
case 302: return F("Found");
|
return F("Non-Authoritative Information");
|
||||||
case 303: return F("See Other");
|
case 204:
|
||||||
case 304: return F("Not Modified");
|
return F("No Content");
|
||||||
case 305: return F("Use Proxy");
|
case 205:
|
||||||
case 307: return F("Temporary Redirect");
|
return F("Reset Content");
|
||||||
case 400: return F("Bad Request");
|
case 206:
|
||||||
case 401: return F("Unauthorized");
|
return F("Partial Content");
|
||||||
case 402: return F("Payment Required");
|
case 300:
|
||||||
case 403: return F("Forbidden");
|
return F("Multiple Choices");
|
||||||
case 404: return F("Not Found");
|
case 301:
|
||||||
case 405: return F("Method Not Allowed");
|
return F("Moved Permanently");
|
||||||
case 406: return F("Not Acceptable");
|
case 302:
|
||||||
case 407: return F("Proxy Authentication Required");
|
return F("Found");
|
||||||
case 408: return F("Request Time-out");
|
case 303:
|
||||||
case 409: return F("Conflict");
|
return F("See Other");
|
||||||
case 410: return F("Gone");
|
case 304:
|
||||||
case 411: return F("Length Required");
|
return F("Not Modified");
|
||||||
case 412: return F("Precondition Failed");
|
case 305:
|
||||||
case 413: return F("Request Entity Too Large");
|
return F("Use Proxy");
|
||||||
case 414: return F("Request-URI Too Large");
|
case 307:
|
||||||
case 415: return F("Unsupported Media Type");
|
return F("Temporary Redirect");
|
||||||
case 416: return F("Requested range not satisfiable");
|
case 400:
|
||||||
case 417: return F("Expectation Failed");
|
return F("Bad Request");
|
||||||
case 500: return F("Internal Server Error");
|
case 401:
|
||||||
case 501: return F("Not Implemented");
|
return F("Unauthorized");
|
||||||
case 502: return F("Bad Gateway");
|
case 402:
|
||||||
case 503: return F("Service Unavailable");
|
return F("Payment Required");
|
||||||
case 504: return F("Gateway Time-out");
|
case 403:
|
||||||
case 505: return F("HTTP Version not supported");
|
return F("Forbidden");
|
||||||
default: return F("");
|
case 404:
|
||||||
|
return F("Not Found");
|
||||||
|
case 405:
|
||||||
|
return F("Method Not Allowed");
|
||||||
|
case 406:
|
||||||
|
return F("Not Acceptable");
|
||||||
|
case 407:
|
||||||
|
return F("Proxy Authentication Required");
|
||||||
|
case 408:
|
||||||
|
return F("Request Time-out");
|
||||||
|
case 409:
|
||||||
|
return F("Conflict");
|
||||||
|
case 410:
|
||||||
|
return F("Gone");
|
||||||
|
case 411:
|
||||||
|
return F("Length Required");
|
||||||
|
case 412:
|
||||||
|
return F("Precondition Failed");
|
||||||
|
case 413:
|
||||||
|
return F("Request Entity Too Large");
|
||||||
|
case 414:
|
||||||
|
return F("Request-URI Too Large");
|
||||||
|
case 415:
|
||||||
|
return F("Unsupported Media Type");
|
||||||
|
case 416:
|
||||||
|
return F("Requested range not satisfiable");
|
||||||
|
case 417:
|
||||||
|
return F("Expectation Failed");
|
||||||
|
case 500:
|
||||||
|
return F("Internal Server Error");
|
||||||
|
case 501:
|
||||||
|
return F("Not Implemented");
|
||||||
|
case 502:
|
||||||
|
return F("Bad Gateway");
|
||||||
|
case 503:
|
||||||
|
return F("Service Unavailable");
|
||||||
|
case 504:
|
||||||
|
return F("Gateway Time-out");
|
||||||
|
case 505:
|
||||||
|
return F("HTTP Version not supported");
|
||||||
|
default:
|
||||||
|
return F("");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
AsyncWebServerResponse::AsyncWebServerResponse()
|
AsyncWebServerResponse::AsyncWebServerResponse()
|
||||||
: _code(0)
|
: _code(0), _contentType(), _contentLength(0), _sendContentLength(true), _chunked(false), _headLength(0), _sentLength(0), _ackedLength(0), _writtenLength(0), _state(RESPONSE_SETUP) {
|
||||||
, _contentType()
|
for (const auto& header : DefaultHeaders::Instance()) {
|
||||||
, _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);
|
_headers.emplace_back(header);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
AsyncWebServerResponse::~AsyncWebServerResponse() = default;
|
AsyncWebServerResponse::~AsyncWebServerResponse() = default;
|
||||||
|
|
||||||
void AsyncWebServerResponse::setCode(int code){
|
void AsyncWebServerResponse::setCode(int code) {
|
||||||
if(_state == RESPONSE_SETUP)
|
if (_state == RESPONSE_SETUP)
|
||||||
_code = code;
|
_code = code;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AsyncWebServerResponse::setContentLength(size_t len){
|
void AsyncWebServerResponse::setContentLength(size_t len) {
|
||||||
if(_state == RESPONSE_SETUP)
|
if (_state == RESPONSE_SETUP)
|
||||||
_contentLength = len;
|
_contentLength = len;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AsyncWebServerResponse::setContentType(const String& type){
|
void AsyncWebServerResponse::setContentType(const String& type) {
|
||||||
if(_state == RESPONSE_SETUP)
|
if (_state == RESPONSE_SETUP)
|
||||||
_contentType = type;
|
_contentType = type;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AsyncWebServerResponse::addHeader(const String& name, const String& value){
|
void AsyncWebServerResponse::addHeader(const String& name, const String& value) {
|
||||||
_headers.emplace_back(name, value);
|
_headers.emplace_back(name, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
String AsyncWebServerResponse::_assembleHead(uint8_t version){
|
String AsyncWebServerResponse::_assembleHead(uint8_t version) {
|
||||||
if(version){
|
if (version) {
|
||||||
addHeader(F("Accept-Ranges"), F("none"));
|
addHeader(F("Accept-Ranges"), F("none"));
|
||||||
if(_chunked)
|
if (_chunked)
|
||||||
addHeader(F("Transfer-Encoding"), F("chunked"));
|
addHeader(F("Transfer-Encoding"), F("chunked"));
|
||||||
}
|
}
|
||||||
String out = String();
|
String out = String();
|
||||||
@ -137,16 +166,16 @@ String AsyncWebServerResponse::_assembleHead(uint8_t version){
|
|||||||
snprintf_P(buf, bufSize, PSTR("HTTP/1.%d %d %s\r\n"), version, _code, _responseCodeToString(_code));
|
snprintf_P(buf, bufSize, PSTR("HTTP/1.%d %d %s\r\n"), version, _code, _responseCodeToString(_code));
|
||||||
out.concat(buf);
|
out.concat(buf);
|
||||||
|
|
||||||
if(_sendContentLength) {
|
if (_sendContentLength) {
|
||||||
snprintf_P(buf, bufSize, PSTR("Content-Length: %d\r\n"), _contentLength);
|
snprintf_P(buf, bufSize, PSTR("Content-Length: %d\r\n"), _contentLength);
|
||||||
out.concat(buf);
|
out.concat(buf);
|
||||||
}
|
}
|
||||||
if(_contentType.length()) {
|
if (_contentType.length()) {
|
||||||
snprintf_P(buf, bufSize, PSTR("Content-Type: %s\r\n"), _contentType.c_str());
|
snprintf_P(buf, bufSize, PSTR("Content-Type: %s\r\n"), _contentType.c_str());
|
||||||
out.concat(buf);
|
out.concat(buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
for(const auto& header: _headers){
|
for (const auto& header : _headers) {
|
||||||
snprintf_P(buf, bufSize, PSTR("%s: %s\r\n"), header.name().c_str(), header.value().c_str());
|
snprintf_P(buf, bufSize, PSTR("%s: %s\r\n"), header.name().c_str(), header.value().c_str());
|
||||||
out.concat(buf);
|
out.concat(buf);
|
||||||
}
|
}
|
||||||
@ -161,44 +190,52 @@ bool AsyncWebServerResponse::_started() const { return _state > RESPONSE_SETUP;
|
|||||||
bool AsyncWebServerResponse::_finished() const { return _state > RESPONSE_WAIT_ACK; }
|
bool AsyncWebServerResponse::_finished() const { return _state > RESPONSE_WAIT_ACK; }
|
||||||
bool AsyncWebServerResponse::_failed() const { return _state == RESPONSE_FAILED; }
|
bool AsyncWebServerResponse::_failed() const { return _state == RESPONSE_FAILED; }
|
||||||
bool AsyncWebServerResponse::_sourceValid() const { return false; }
|
bool AsyncWebServerResponse::_sourceValid() const { return false; }
|
||||||
void AsyncWebServerResponse::_respond(AsyncWebServerRequest *request){ _state = RESPONSE_END; request->client()->close(); }
|
void AsyncWebServerResponse::_respond(AsyncWebServerRequest* request) {
|
||||||
size_t AsyncWebServerResponse::_ack(AsyncWebServerRequest *request, size_t len, uint32_t time){ (void)request; (void)len; (void)time; return 0; }
|
_state = RESPONSE_END;
|
||||||
|
request->client()->close();
|
||||||
|
}
|
||||||
|
size_t AsyncWebServerResponse::_ack(AsyncWebServerRequest* request, size_t len, uint32_t time) {
|
||||||
|
(void)request;
|
||||||
|
(void)len;
|
||||||
|
(void)time;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* String/Code Response
|
* String/Code Response
|
||||||
* */
|
* */
|
||||||
AsyncBasicResponse::AsyncBasicResponse(int code, const String& contentType, const String& content){
|
AsyncBasicResponse::AsyncBasicResponse(int code, const String& contentType, const String& content) {
|
||||||
_code = code;
|
_code = code;
|
||||||
_content = content;
|
_content = content;
|
||||||
_contentType = contentType;
|
_contentType = contentType;
|
||||||
if(_content.length()){
|
if (_content.length()) {
|
||||||
_contentLength = _content.length();
|
_contentLength = _content.length();
|
||||||
if(!_contentType.length())
|
if (!_contentType.length())
|
||||||
_contentType = F("text/plain");
|
_contentType = F("text/plain");
|
||||||
}
|
}
|
||||||
addHeader(F("Connection"), F("close"));
|
addHeader(F("Connection"), F("close"));
|
||||||
}
|
}
|
||||||
|
|
||||||
void AsyncBasicResponse::_respond(AsyncWebServerRequest *request){
|
void AsyncBasicResponse::_respond(AsyncWebServerRequest* request) {
|
||||||
_state = RESPONSE_HEADERS;
|
_state = RESPONSE_HEADERS;
|
||||||
String out = _assembleHead(request->version());
|
String out = _assembleHead(request->version());
|
||||||
size_t outLen = out.length();
|
size_t outLen = out.length();
|
||||||
size_t space = request->client()->space();
|
size_t space = request->client()->space();
|
||||||
if(!_contentLength && space >= outLen){
|
if (!_contentLength && space >= outLen) {
|
||||||
_writtenLength += request->client()->write(out.c_str(), outLen);
|
_writtenLength += request->client()->write(out.c_str(), outLen);
|
||||||
_state = RESPONSE_WAIT_ACK;
|
_state = RESPONSE_WAIT_ACK;
|
||||||
} else if(_contentLength && space >= outLen + _contentLength){
|
} else if (_contentLength && space >= outLen + _contentLength) {
|
||||||
out += _content;
|
out += _content;
|
||||||
outLen += _contentLength;
|
outLen += _contentLength;
|
||||||
_writtenLength += request->client()->write(out.c_str(), outLen);
|
_writtenLength += request->client()->write(out.c_str(), outLen);
|
||||||
_state = RESPONSE_WAIT_ACK;
|
_state = RESPONSE_WAIT_ACK;
|
||||||
} else if(space && space < outLen){
|
} else if (space && space < outLen) {
|
||||||
String partial = out.substring(0, space);
|
String partial = out.substring(0, space);
|
||||||
_content = out.substring(space) + _content;
|
_content = out.substring(space) + _content;
|
||||||
_contentLength += outLen - space;
|
_contentLength += outLen - space;
|
||||||
_writtenLength += request->client()->write(partial.c_str(), partial.length());
|
_writtenLength += request->client()->write(partial.c_str(), partial.length());
|
||||||
_state = RESPONSE_CONTENT;
|
_state = RESPONSE_CONTENT;
|
||||||
} else if(space > outLen && space < (outLen + _contentLength)){
|
} else if (space > outLen && space < (outLen + _contentLength)) {
|
||||||
size_t shift = space - outLen;
|
size_t shift = space - outLen;
|
||||||
outLen += shift;
|
outLen += shift;
|
||||||
_sentLength += shift;
|
_sentLength += shift;
|
||||||
@ -213,58 +250,56 @@ void AsyncBasicResponse::_respond(AsyncWebServerRequest *request){
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t AsyncBasicResponse::_ack(AsyncWebServerRequest *request, size_t len, uint32_t time){
|
size_t AsyncBasicResponse::_ack(AsyncWebServerRequest* request, size_t len, uint32_t time) {
|
||||||
(void)time;
|
(void)time;
|
||||||
_ackedLength += len;
|
_ackedLength += len;
|
||||||
if(_state == RESPONSE_CONTENT){
|
if (_state == RESPONSE_CONTENT) {
|
||||||
size_t available = _contentLength - _sentLength;
|
size_t available = _contentLength - _sentLength;
|
||||||
size_t space = request->client()->space();
|
size_t space = request->client()->space();
|
||||||
//we can fit in this packet
|
// we can fit in this packet
|
||||||
if(space > available){
|
if (space > available) {
|
||||||
_writtenLength += request->client()->write(_content.c_str(), available);
|
_writtenLength += request->client()->write(_content.c_str(), available);
|
||||||
_content = String();
|
_content = String();
|
||||||
_state = RESPONSE_WAIT_ACK;
|
_state = RESPONSE_WAIT_ACK;
|
||||||
return available;
|
return available;
|
||||||
}
|
}
|
||||||
//send some data, the rest on ack
|
// send some data, the rest on ack
|
||||||
String out = _content.substring(0, space);
|
String out = _content.substring(0, space);
|
||||||
_content = _content.substring(space);
|
_content = _content.substring(space);
|
||||||
_sentLength += space;
|
_sentLength += space;
|
||||||
_writtenLength += request->client()->write(out.c_str(), space);
|
_writtenLength += request->client()->write(out.c_str(), space);
|
||||||
return space;
|
return space;
|
||||||
} else if(_state == RESPONSE_WAIT_ACK){
|
} else if (_state == RESPONSE_WAIT_ACK) {
|
||||||
if(_ackedLength >= _writtenLength){
|
if (_ackedLength >= _writtenLength) {
|
||||||
_state = RESPONSE_END;
|
_state = RESPONSE_END;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Abstract Response
|
* Abstract Response
|
||||||
* */
|
* */
|
||||||
|
|
||||||
AsyncAbstractResponse::AsyncAbstractResponse(AwsTemplateProcessor callback): _callback(callback)
|
AsyncAbstractResponse::AsyncAbstractResponse(AwsTemplateProcessor callback) : _callback(callback) {
|
||||||
{
|
|
||||||
// In case of template processing, we're unable to determine real response size
|
// In case of template processing, we're unable to determine real response size
|
||||||
if(callback) {
|
if (callback) {
|
||||||
_contentLength = 0;
|
_contentLength = 0;
|
||||||
_sendContentLength = false;
|
_sendContentLength = false;
|
||||||
_chunked = true;
|
_chunked = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AsyncAbstractResponse::_respond(AsyncWebServerRequest *request){
|
void AsyncAbstractResponse::_respond(AsyncWebServerRequest* request) {
|
||||||
addHeader(F("Connection"), F("close"));
|
addHeader(F("Connection"), F("close"));
|
||||||
_head = _assembleHead(request->version());
|
_head = _assembleHead(request->version());
|
||||||
_state = RESPONSE_HEADERS;
|
_state = RESPONSE_HEADERS;
|
||||||
_ack(request, 0, 0);
|
_ack(request, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t AsyncAbstractResponse::_ack(AsyncWebServerRequest *request, size_t len, uint32_t time){
|
size_t AsyncAbstractResponse::_ack(AsyncWebServerRequest* request, size_t len, uint32_t time) {
|
||||||
(void)time;
|
(void)time;
|
||||||
if(!_sourceValid()){
|
if (!_sourceValid()) {
|
||||||
_state = RESPONSE_FAILED;
|
_state = RESPONSE_FAILED;
|
||||||
request->client()->close();
|
request->client()->close();
|
||||||
return 0;
|
return 0;
|
||||||
@ -273,8 +308,8 @@ size_t AsyncAbstractResponse::_ack(AsyncWebServerRequest *request, size_t len, u
|
|||||||
size_t space = request->client()->space();
|
size_t space = request->client()->space();
|
||||||
|
|
||||||
size_t headLen = _head.length();
|
size_t headLen = _head.length();
|
||||||
if(_state == RESPONSE_HEADERS){
|
if (_state == RESPONSE_HEADERS) {
|
||||||
if(space >= headLen){
|
if (space >= headLen) {
|
||||||
_state = RESPONSE_CONTENT;
|
_state = RESPONSE_CONTENT;
|
||||||
space -= headLen;
|
space -= headLen;
|
||||||
} else {
|
} else {
|
||||||
@ -285,103 +320,102 @@ size_t AsyncAbstractResponse::_ack(AsyncWebServerRequest *request, size_t len, u
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(_state == RESPONSE_CONTENT){
|
if (_state == RESPONSE_CONTENT) {
|
||||||
size_t outLen;
|
size_t outLen;
|
||||||
if(_chunked){
|
if (_chunked) {
|
||||||
if(space <= 8){
|
if (space <= 8) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
outLen = space;
|
outLen = space;
|
||||||
} else if(!_sendContentLength){
|
} else if (!_sendContentLength) {
|
||||||
outLen = space;
|
outLen = space;
|
||||||
} else {
|
} else {
|
||||||
outLen = ((_contentLength - _sentLength) > space)?space:(_contentLength - _sentLength);
|
outLen = ((_contentLength - _sentLength) > space) ? space : (_contentLength - _sentLength);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t *buf = (uint8_t *)malloc(outLen+headLen);
|
uint8_t* buf = (uint8_t*)malloc(outLen + headLen);
|
||||||
if (!buf) {
|
if (!buf) {
|
||||||
// os_printf("_ack malloc %d failed\n", outLen+headLen);
|
// os_printf("_ack malloc %d failed\n", outLen+headLen);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(headLen){
|
if (headLen) {
|
||||||
memcpy(buf, _head.c_str(), _head.length());
|
memcpy(buf, _head.c_str(), _head.length());
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t readLen = 0;
|
size_t readLen = 0;
|
||||||
|
|
||||||
if(_chunked){
|
if (_chunked) {
|
||||||
// HTTP 1.1 allows leading zeros in chunk length. Or spaces may be added.
|
// HTTP 1.1 allows leading zeros in chunk length. Or spaces may be added.
|
||||||
// See RFC2616 sections 2, 3.6.1.
|
// See RFC2616 sections 2, 3.6.1.
|
||||||
readLen = _fillBufferAndProcessTemplates(buf+headLen+6, outLen - 8);
|
readLen = _fillBufferAndProcessTemplates(buf + headLen + 6, outLen - 8);
|
||||||
if(readLen == RESPONSE_TRY_AGAIN){
|
if (readLen == RESPONSE_TRY_AGAIN) {
|
||||||
free(buf);
|
free(buf);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
outLen = sprintf_P((char*)buf+headLen, PSTR("%x"), readLen) + headLen;
|
outLen = sprintf_P((char*)buf + headLen, PSTR("%x"), readLen) + headLen;
|
||||||
while(outLen < headLen + 4) buf[outLen++] = ' ';
|
while (outLen < headLen + 4)
|
||||||
|
buf[outLen++] = ' ';
|
||||||
buf[outLen++] = '\r';
|
buf[outLen++] = '\r';
|
||||||
buf[outLen++] = '\n';
|
buf[outLen++] = '\n';
|
||||||
outLen += readLen;
|
outLen += readLen;
|
||||||
buf[outLen++] = '\r';
|
buf[outLen++] = '\r';
|
||||||
buf[outLen++] = '\n';
|
buf[outLen++] = '\n';
|
||||||
} else {
|
} else {
|
||||||
readLen = _fillBufferAndProcessTemplates(buf+headLen, outLen);
|
readLen = _fillBufferAndProcessTemplates(buf + headLen, outLen);
|
||||||
if(readLen == RESPONSE_TRY_AGAIN){
|
if (readLen == RESPONSE_TRY_AGAIN) {
|
||||||
free(buf);
|
free(buf);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
outLen = readLen + headLen;
|
outLen = readLen + headLen;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(headLen){
|
if (headLen) {
|
||||||
_head = String();
|
_head = String();
|
||||||
}
|
}
|
||||||
|
|
||||||
if(outLen){
|
if (outLen) {
|
||||||
_writtenLength += request->client()->write((const char*)buf, outLen);
|
_writtenLength += request->client()->write((const char*)buf, outLen);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(_chunked){
|
if (_chunked) {
|
||||||
_sentLength += readLen;
|
_sentLength += readLen;
|
||||||
} else {
|
} else {
|
||||||
_sentLength += outLen - headLen;
|
_sentLength += outLen - headLen;
|
||||||
}
|
}
|
||||||
|
|
||||||
free(buf);
|
free(buf);
|
||||||
|
|
||||||
if((_chunked && readLen == 0) || (!_sendContentLength && outLen == 0) || (!_chunked && _sentLength == _contentLength)){
|
if ((_chunked && readLen == 0) || (!_sendContentLength && outLen == 0) || (!_chunked && _sentLength == _contentLength)) {
|
||||||
_state = RESPONSE_WAIT_ACK;
|
_state = RESPONSE_WAIT_ACK;
|
||||||
}
|
}
|
||||||
return outLen;
|
return outLen;
|
||||||
|
|
||||||
} else if(_state == RESPONSE_WAIT_ACK){
|
} else if (_state == RESPONSE_WAIT_ACK) {
|
||||||
if(!_sendContentLength || _ackedLength >= _writtenLength){
|
if (!_sendContentLength || _ackedLength >= _writtenLength) {
|
||||||
_state = RESPONSE_END;
|
_state = RESPONSE_END;
|
||||||
if(!_chunked && !_sendContentLength)
|
if (!_chunked && !_sendContentLength)
|
||||||
request->client()->close(true);
|
request->client()->close(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t AsyncAbstractResponse::_readDataFromCacheOrContent(uint8_t* data, const size_t len)
|
size_t AsyncAbstractResponse::_readDataFromCacheOrContent(uint8_t* data, const size_t len) {
|
||||||
{
|
// If we have something in cache, copy it to buffer
|
||||||
// If we have something in cache, copy it to buffer
|
const size_t readFromCache = std::min(len, _cache.size());
|
||||||
const size_t readFromCache = std::min(len, _cache.size());
|
if (readFromCache) {
|
||||||
if(readFromCache) {
|
memcpy(data, _cache.data(), readFromCache);
|
||||||
memcpy(data, _cache.data(), readFromCache);
|
_cache.erase(_cache.begin(), _cache.begin() + readFromCache);
|
||||||
_cache.erase(_cache.begin(), _cache.begin() + readFromCache);
|
}
|
||||||
}
|
// If we need to read more...
|
||||||
// If we need to read more...
|
const size_t needFromFile = len - readFromCache;
|
||||||
const size_t needFromFile = len - readFromCache;
|
const size_t readFromContent = _fillBuffer(data + readFromCache, needFromFile);
|
||||||
const size_t readFromContent = _fillBuffer(data + readFromCache, needFromFile);
|
return readFromCache + readFromContent;
|
||||||
return readFromCache + readFromContent;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t AsyncAbstractResponse::_fillBufferAndProcessTemplates(uint8_t* data, size_t len)
|
size_t AsyncAbstractResponse::_fillBufferAndProcessTemplates(uint8_t* data, size_t len) {
|
||||||
{
|
if (!_callback)
|
||||||
if(!_callback)
|
|
||||||
return _fillBuffer(data, len);
|
return _fillBuffer(data, len);
|
||||||
|
|
||||||
const size_t originalLen = len;
|
const size_t originalLen = len;
|
||||||
@ -389,16 +423,16 @@ size_t AsyncAbstractResponse::_fillBufferAndProcessTemplates(uint8_t* data, size
|
|||||||
// Now we've read 'len' bytes, either from cache or from file
|
// Now we've read 'len' bytes, either from cache or from file
|
||||||
// Search for template placeholders
|
// Search for template placeholders
|
||||||
uint8_t* pTemplateStart = data;
|
uint8_t* pTemplateStart = data;
|
||||||
while((pTemplateStart < &data[len]) && (pTemplateStart = (uint8_t*)memchr(pTemplateStart, TEMPLATE_PLACEHOLDER, &data[len - 1] - pTemplateStart + 1))) { // data[0] ... data[len - 1]
|
while ((pTemplateStart < &data[len]) && (pTemplateStart = (uint8_t*)memchr(pTemplateStart, TEMPLATE_PLACEHOLDER, &data[len - 1] - pTemplateStart + 1))) { // data[0] ... data[len - 1]
|
||||||
uint8_t* pTemplateEnd = (pTemplateStart < &data[len - 1]) ? (uint8_t*)memchr(pTemplateStart + 1, TEMPLATE_PLACEHOLDER, &data[len - 1] - pTemplateStart) : nullptr;
|
uint8_t* pTemplateEnd = (pTemplateStart < &data[len - 1]) ? (uint8_t*)memchr(pTemplateStart + 1, TEMPLATE_PLACEHOLDER, &data[len - 1] - pTemplateStart) : nullptr;
|
||||||
// temporary buffer to hold parameter name
|
// temporary buffer to hold parameter name
|
||||||
uint8_t buf[TEMPLATE_PARAM_NAME_LENGTH + 1];
|
uint8_t buf[TEMPLATE_PARAM_NAME_LENGTH + 1];
|
||||||
String paramName;
|
String paramName;
|
||||||
// If closing placeholder is found:
|
// If closing placeholder is found:
|
||||||
if(pTemplateEnd) {
|
if (pTemplateEnd) {
|
||||||
// prepare argument to callback
|
// prepare argument to callback
|
||||||
const size_t paramNameLength = std::min((size_t)sizeof(buf) - 1, (size_t)(pTemplateEnd - pTemplateStart - 1));
|
const size_t paramNameLength = std::min((size_t)sizeof(buf) - 1, (size_t)(pTemplateEnd - pTemplateStart - 1));
|
||||||
if(paramNameLength) {
|
if (paramNameLength) {
|
||||||
memcpy(buf, pTemplateStart + 1, paramNameLength);
|
memcpy(buf, pTemplateStart + 1, paramNameLength);
|
||||||
buf[paramNameLength] = 0;
|
buf[paramNameLength] = 0;
|
||||||
paramName = String(reinterpret_cast<char*>(buf));
|
paramName = String(reinterpret_cast<char*>(buf));
|
||||||
@ -408,32 +442,29 @@ size_t AsyncAbstractResponse::_fillBufferAndProcessTemplates(uint8_t* data, size
|
|||||||
len += _readDataFromCacheOrContent(&data[len - 1], 1) - 1;
|
len += _readDataFromCacheOrContent(&data[len - 1], 1) - 1;
|
||||||
++pTemplateStart;
|
++pTemplateStart;
|
||||||
}
|
}
|
||||||
} else if(&data[len - 1] - pTemplateStart + 1 < TEMPLATE_PARAM_NAME_LENGTH + 2) { // closing placeholder not found, check if it's in the remaining file data
|
} else if (&data[len - 1] - pTemplateStart + 1 < TEMPLATE_PARAM_NAME_LENGTH + 2) { // closing placeholder not found, check if it's in the remaining file data
|
||||||
memcpy(buf, pTemplateStart + 1, &data[len - 1] - pTemplateStart);
|
memcpy(buf, pTemplateStart + 1, &data[len - 1] - pTemplateStart);
|
||||||
const size_t readFromCacheOrContent = _readDataFromCacheOrContent(buf + (&data[len - 1] - pTemplateStart), TEMPLATE_PARAM_NAME_LENGTH + 2 - (&data[len - 1] - pTemplateStart + 1));
|
const size_t readFromCacheOrContent = _readDataFromCacheOrContent(buf + (&data[len - 1] - pTemplateStart), TEMPLATE_PARAM_NAME_LENGTH + 2 - (&data[len - 1] - pTemplateStart + 1));
|
||||||
if(readFromCacheOrContent) {
|
if (readFromCacheOrContent) {
|
||||||
pTemplateEnd = (uint8_t*)memchr(buf + (&data[len - 1] - pTemplateStart), TEMPLATE_PLACEHOLDER, readFromCacheOrContent);
|
pTemplateEnd = (uint8_t*)memchr(buf + (&data[len - 1] - pTemplateStart), TEMPLATE_PLACEHOLDER, readFromCacheOrContent);
|
||||||
if(pTemplateEnd) {
|
if (pTemplateEnd) {
|
||||||
// prepare argument to callback
|
// prepare argument to callback
|
||||||
*pTemplateEnd = 0;
|
*pTemplateEnd = 0;
|
||||||
paramName = String(reinterpret_cast<char*>(buf));
|
paramName = String(reinterpret_cast<char*>(buf));
|
||||||
// Copy remaining read-ahead data into cache
|
// Copy remaining read-ahead data into cache
|
||||||
_cache.insert(_cache.begin(), pTemplateEnd + 1, buf + (&data[len - 1] - pTemplateStart) + readFromCacheOrContent);
|
_cache.insert(_cache.begin(), pTemplateEnd + 1, buf + (&data[len - 1] - pTemplateStart) + readFromCacheOrContent);
|
||||||
pTemplateEnd = &data[len - 1];
|
pTemplateEnd = &data[len - 1];
|
||||||
}
|
} else // closing placeholder not found in file data, store found percent symbol as is and advance to the next position
|
||||||
else // closing placeholder not found in file data, store found percent symbol as is and advance to the next position
|
|
||||||
{
|
{
|
||||||
// but first, store read file data in cache
|
// but first, store read file data in cache
|
||||||
_cache.insert(_cache.begin(), buf + (&data[len - 1] - pTemplateStart), buf + (&data[len - 1] - pTemplateStart) + readFromCacheOrContent);
|
_cache.insert(_cache.begin(), buf + (&data[len - 1] - pTemplateStart), buf + (&data[len - 1] - pTemplateStart) + readFromCacheOrContent);
|
||||||
++pTemplateStart;
|
++pTemplateStart;
|
||||||
}
|
}
|
||||||
}
|
} else // closing placeholder not found in content data, store found percent symbol as is and advance to the next position
|
||||||
else // closing placeholder not found in content data, store found percent symbol as is and advance to the next position
|
|
||||||
++pTemplateStart;
|
++pTemplateStart;
|
||||||
}
|
} else // closing placeholder not found in content data, store found percent symbol as is and advance to the next position
|
||||||
else // closing placeholder not found in content data, store found percent symbol as is and advance to the next position
|
|
||||||
++pTemplateStart;
|
++pTemplateStart;
|
||||||
if(paramName.length()) {
|
if (paramName.length()) {
|
||||||
// call callback and replace with result.
|
// call callback and replace with result.
|
||||||
// Everything in range [pTemplateStart, pTemplateEnd] can be safely replaced with parameter value.
|
// Everything in range [pTemplateStart, pTemplateEnd] can be safely replaced with parameter value.
|
||||||
// Data after pTemplateEnd may need to be moved.
|
// Data after pTemplateEnd may need to be moved.
|
||||||
@ -445,21 +476,21 @@ size_t AsyncAbstractResponse::_fillBufferAndProcessTemplates(uint8_t* data, size
|
|||||||
const size_t numBytesCopied = std::min(pvlen, static_cast<unsigned int>(&data[originalLen - 1] - pTemplateStart + 1));
|
const size_t numBytesCopied = std::min(pvlen, static_cast<unsigned int>(&data[originalLen - 1] - pTemplateStart + 1));
|
||||||
// make room for param value
|
// make room for param value
|
||||||
// 1. move extra data to cache if parameter value is longer than placeholder AND if there is no room to store
|
// 1. move extra data to cache if parameter value is longer than placeholder AND if there is no room to store
|
||||||
if((pTemplateEnd + 1 < pTemplateStart + numBytesCopied) && (originalLen - (pTemplateStart + numBytesCopied - pTemplateEnd - 1) < len)) {
|
if ((pTemplateEnd + 1 < pTemplateStart + numBytesCopied) && (originalLen - (pTemplateStart + numBytesCopied - pTemplateEnd - 1) < len)) {
|
||||||
_cache.insert(_cache.begin(), &data[originalLen - (pTemplateStart + numBytesCopied - pTemplateEnd - 1)], &data[len]);
|
_cache.insert(_cache.begin(), &data[originalLen - (pTemplateStart + numBytesCopied - pTemplateEnd - 1)], &data[len]);
|
||||||
//2. parameter value is longer than placeholder text, push the data after placeholder which not saved into cache further to the end
|
// 2. parameter value is longer than placeholder text, push the data after placeholder which not saved into cache further to the end
|
||||||
memmove(pTemplateStart + numBytesCopied, pTemplateEnd + 1, &data[originalLen] - pTemplateStart - numBytesCopied);
|
memmove(pTemplateStart + numBytesCopied, pTemplateEnd + 1, &data[originalLen] - pTemplateStart - numBytesCopied);
|
||||||
len = originalLen; // fix issue with truncated data, not sure if it has any side effects
|
len = originalLen; // fix issue with truncated data, not sure if it has any side effects
|
||||||
} else if(pTemplateEnd + 1 != pTemplateStart + numBytesCopied)
|
} else if (pTemplateEnd + 1 != pTemplateStart + numBytesCopied)
|
||||||
//2. Either parameter value is shorter than placeholder text OR there is enough free space in buffer to fit.
|
// 2. Either parameter value is shorter than placeholder text OR there is enough free space in buffer to fit.
|
||||||
// Move the entire data after the placeholder
|
// Move the entire data after the placeholder
|
||||||
memmove(pTemplateStart + numBytesCopied, pTemplateEnd + 1, &data[len] - pTemplateEnd - 1);
|
memmove(pTemplateStart + numBytesCopied, pTemplateEnd + 1, &data[len] - pTemplateEnd - 1);
|
||||||
// 3. replace placeholder with actual value
|
// 3. replace placeholder with actual value
|
||||||
memcpy(pTemplateStart, pvstr, numBytesCopied);
|
memcpy(pTemplateStart, pvstr, numBytesCopied);
|
||||||
// If result is longer than buffer, copy the remainder into cache (this could happen only if placeholder text itself did not fit entirely in buffer)
|
// If result is longer than buffer, copy the remainder into cache (this could happen only if placeholder text itself did not fit entirely in buffer)
|
||||||
if(numBytesCopied < pvlen) {
|
if (numBytesCopied < pvlen) {
|
||||||
_cache.insert(_cache.begin(), pvstr + numBytesCopied, pvstr + pvlen);
|
_cache.insert(_cache.begin(), pvstr + numBytesCopied, pvstr + pvlen);
|
||||||
} else if(pTemplateStart + numBytesCopied < pTemplateEnd + 1) { // result is copied fully; if result is shorter than placeholder text...
|
} else if (pTemplateStart + numBytesCopied < pTemplateEnd + 1) { // result is copied fully; if result is shorter than placeholder text...
|
||||||
// there is some free room, fill it from cache
|
// there is some free room, fill it from cache
|
||||||
const size_t roomFreed = pTemplateEnd + 1 - pTemplateStart - numBytesCopied;
|
const size_t roomFreed = pTemplateEnd + 1 - pTemplateStart - numBytesCopied;
|
||||||
const size_t totalFreeRoom = originalLen - len + roomFreed;
|
const size_t totalFreeRoom = originalLen - len + roomFreed;
|
||||||
@ -473,48 +504,66 @@ size_t AsyncAbstractResponse::_fillBufferAndProcessTemplates(uint8_t* data, size
|
|||||||
return len;
|
return len;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* File Response
|
* File Response
|
||||||
* */
|
* */
|
||||||
|
|
||||||
AsyncFileResponse::~AsyncFileResponse(){
|
AsyncFileResponse::~AsyncFileResponse() {
|
||||||
if(_content)
|
if (_content)
|
||||||
_content.close();
|
_content.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
void AsyncFileResponse::_setContentType(const String& path){
|
void AsyncFileResponse::_setContentType(const String& path) {
|
||||||
#if HAVE_EXTERN_GET_CONTENT_TYPE_FUNCTION
|
#if HAVE_EXTERN_GET_CONTENT_TYPE_FUNCTION
|
||||||
extern const __FlashStringHelper *getContentType(const String &path);
|
extern const __FlashStringHelper* getContentType(const String& path);
|
||||||
_contentType = getContentType(path);
|
_contentType = getContentType(path);
|
||||||
#else
|
#else
|
||||||
if (path.endsWith(F(".html"))) _contentType = F("text/html");
|
if (path.endsWith(F(".html")))
|
||||||
else if (path.endsWith(F(".htm"))) _contentType = F("text/html");
|
_contentType = F("text/html");
|
||||||
else if (path.endsWith(F(".css"))) _contentType = F("text/css");
|
else if (path.endsWith(F(".htm")))
|
||||||
else if (path.endsWith(F(".json"))) _contentType = F("application/json");
|
_contentType = F("text/html");
|
||||||
else if (path.endsWith(F(".js"))) _contentType = F("application/javascript");
|
else if (path.endsWith(F(".css")))
|
||||||
else if (path.endsWith(F(".png"))) _contentType = F("image/png");
|
_contentType = F("text/css");
|
||||||
else if (path.endsWith(F(".gif"))) _contentType = F("image/gif");
|
else if (path.endsWith(F(".json")))
|
||||||
else if (path.endsWith(F(".jpg"))) _contentType = F("image/jpeg");
|
_contentType = F("application/json");
|
||||||
else if (path.endsWith(F(".ico"))) _contentType = F("image/x-icon");
|
else if (path.endsWith(F(".js")))
|
||||||
else if (path.endsWith(F(".svg"))) _contentType = F("image/svg+xml");
|
_contentType = F("application/javascript");
|
||||||
else if (path.endsWith(F(".eot"))) _contentType = F("font/eot");
|
else if (path.endsWith(F(".png")))
|
||||||
else if (path.endsWith(F(".woff"))) _contentType = F("font/woff");
|
_contentType = F("image/png");
|
||||||
else if (path.endsWith(F(".woff2"))) _contentType = F("font/woff2");
|
else if (path.endsWith(F(".gif")))
|
||||||
else if (path.endsWith(F(".ttf"))) _contentType = F("font/ttf");
|
_contentType = F("image/gif");
|
||||||
else if (path.endsWith(F(".xml"))) _contentType = F("text/xml");
|
else if (path.endsWith(F(".jpg")))
|
||||||
else if (path.endsWith(F(".pdf"))) _contentType = F("application/pdf");
|
_contentType = F("image/jpeg");
|
||||||
else if (path.endsWith(F(".zip"))) _contentType = F("application/zip");
|
else if (path.endsWith(F(".ico")))
|
||||||
else if(path.endsWith(F(".gz"))) _contentType = F("application/x-gzip");
|
_contentType = F("image/x-icon");
|
||||||
else _contentType = F("text/plain");
|
else if (path.endsWith(F(".svg")))
|
||||||
|
_contentType = F("image/svg+xml");
|
||||||
|
else if (path.endsWith(F(".eot")))
|
||||||
|
_contentType = F("font/eot");
|
||||||
|
else if (path.endsWith(F(".woff")))
|
||||||
|
_contentType = F("font/woff");
|
||||||
|
else if (path.endsWith(F(".woff2")))
|
||||||
|
_contentType = F("font/woff2");
|
||||||
|
else if (path.endsWith(F(".ttf")))
|
||||||
|
_contentType = F("font/ttf");
|
||||||
|
else if (path.endsWith(F(".xml")))
|
||||||
|
_contentType = F("text/xml");
|
||||||
|
else if (path.endsWith(F(".pdf")))
|
||||||
|
_contentType = F("application/pdf");
|
||||||
|
else if (path.endsWith(F(".zip")))
|
||||||
|
_contentType = F("application/zip");
|
||||||
|
else if (path.endsWith(F(".gz")))
|
||||||
|
_contentType = F("application/x-gzip");
|
||||||
|
else
|
||||||
|
_contentType = F("text/plain");
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
AsyncFileResponse::AsyncFileResponse(FS &fs, const String& path, const String& contentType, bool download, AwsTemplateProcessor callback): AsyncAbstractResponse(callback){
|
AsyncFileResponse::AsyncFileResponse(FS& fs, const String& path, const String& contentType, bool download, AwsTemplateProcessor callback) : AsyncAbstractResponse(callback) {
|
||||||
_code = 200;
|
_code = 200;
|
||||||
_path = path;
|
_path = path;
|
||||||
|
|
||||||
if(!download && !fs.exists(_path) && fs.exists(_path + F(".gz"))){
|
if (!download && !fs.exists(_path) && fs.exists(_path + F(".gz"))) {
|
||||||
_path = _path + F(".gz");
|
_path = _path + F(".gz");
|
||||||
addHeader(F("Content-Encoding"), F("gzip"));
|
addHeader(F("Content-Encoding"), F("gzip"));
|
||||||
_callback = nullptr; // Unable to process zipped templates
|
_callback = nullptr; // Unable to process zipped templates
|
||||||
@ -525,30 +574,30 @@ AsyncFileResponse::AsyncFileResponse(FS &fs, const String& path, const String& c
|
|||||||
_content = fs.open(_path, fs::FileOpenMode::read);
|
_content = fs.open(_path, fs::FileOpenMode::read);
|
||||||
_contentLength = _content.size();
|
_contentLength = _content.size();
|
||||||
|
|
||||||
if(contentType.length() == 0)
|
if (contentType.length() == 0)
|
||||||
_setContentType(path);
|
_setContentType(path);
|
||||||
else
|
else
|
||||||
_contentType = contentType;
|
_contentType = contentType;
|
||||||
|
|
||||||
int filenameStart = path.lastIndexOf('/') + 1;
|
int filenameStart = path.lastIndexOf('/') + 1;
|
||||||
char buf[26+path.length()-filenameStart];
|
char buf[26 + path.length() - filenameStart];
|
||||||
char* filename = (char*)path.c_str() + filenameStart;
|
char* filename = (char*)path.c_str() + filenameStart;
|
||||||
|
|
||||||
if(download) {
|
if (download) {
|
||||||
// set filename and force download
|
// set filename and force download
|
||||||
snprintf_P(buf, sizeof (buf), PSTR("attachment; filename=\"%s\""), filename);
|
snprintf_P(buf, sizeof(buf), PSTR("attachment; filename=\"%s\""), filename);
|
||||||
} else {
|
} else {
|
||||||
// set filename and force rendering
|
// set filename and force rendering
|
||||||
snprintf_P(buf, sizeof (buf), PSTR("inline"));
|
snprintf_P(buf, sizeof(buf), PSTR("inline"));
|
||||||
}
|
}
|
||||||
addHeader(F("Content-Disposition"), buf);
|
addHeader(F("Content-Disposition"), buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
AsyncFileResponse::AsyncFileResponse(File content, const String& path, const String& contentType, bool download, AwsTemplateProcessor callback): AsyncAbstractResponse(callback){
|
AsyncFileResponse::AsyncFileResponse(File content, const String& path, const String& contentType, bool download, AwsTemplateProcessor callback) : AsyncAbstractResponse(callback) {
|
||||||
_code = 200;
|
_code = 200;
|
||||||
_path = path;
|
_path = path;
|
||||||
|
|
||||||
if(!download && String(content.name()).endsWith(F(".gz")) && !path.endsWith(F(".gz"))){
|
if (!download && String(content.name()).endsWith(F(".gz")) && !path.endsWith(F(".gz"))) {
|
||||||
addHeader(F("Content-Encoding"), F("gzip"));
|
addHeader(F("Content-Encoding"), F("gzip"));
|
||||||
_callback = nullptr; // Unable to process gzipped templates
|
_callback = nullptr; // Unable to process gzipped templates
|
||||||
_sendContentLength = true;
|
_sendContentLength = true;
|
||||||
@ -558,24 +607,24 @@ AsyncFileResponse::AsyncFileResponse(File content, const String& path, const Str
|
|||||||
_content = content;
|
_content = content;
|
||||||
_contentLength = _content.size();
|
_contentLength = _content.size();
|
||||||
|
|
||||||
if(contentType.length() == 0)
|
if (contentType.length() == 0)
|
||||||
_setContentType(path);
|
_setContentType(path);
|
||||||
else
|
else
|
||||||
_contentType = contentType;
|
_contentType = contentType;
|
||||||
|
|
||||||
int filenameStart = path.lastIndexOf('/') + 1;
|
int filenameStart = path.lastIndexOf('/') + 1;
|
||||||
char buf[26+path.length()-filenameStart];
|
char buf[26 + path.length() - filenameStart];
|
||||||
char* filename = (char*)path.c_str() + filenameStart;
|
char* filename = (char*)path.c_str() + filenameStart;
|
||||||
|
|
||||||
if(download) {
|
if (download) {
|
||||||
snprintf_P(buf, sizeof (buf), PSTR("attachment; filename=\"%s\""), filename);
|
snprintf_P(buf, sizeof(buf), PSTR("attachment; filename=\"%s\""), filename);
|
||||||
} else {
|
} else {
|
||||||
snprintf_P(buf, sizeof (buf), PSTR("inline"));
|
snprintf_P(buf, sizeof(buf), PSTR("inline"));
|
||||||
}
|
}
|
||||||
addHeader(F("Content-Disposition"), buf);
|
addHeader(F("Content-Disposition"), buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t AsyncFileResponse::_fillBuffer(uint8_t *data, size_t len){
|
size_t AsyncFileResponse::_fillBuffer(uint8_t* data, size_t len) {
|
||||||
return _content.read(data, len);
|
return _content.read(data, len);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -583,18 +632,18 @@ size_t AsyncFileResponse::_fillBuffer(uint8_t *data, size_t len){
|
|||||||
* Stream Response
|
* Stream Response
|
||||||
* */
|
* */
|
||||||
|
|
||||||
AsyncStreamResponse::AsyncStreamResponse(Stream &stream, const String& contentType, size_t len, AwsTemplateProcessor callback): AsyncAbstractResponse(callback) {
|
AsyncStreamResponse::AsyncStreamResponse(Stream& stream, const String& contentType, size_t len, AwsTemplateProcessor callback) : AsyncAbstractResponse(callback) {
|
||||||
_code = 200;
|
_code = 200;
|
||||||
_content = &stream;
|
_content = &stream;
|
||||||
_contentLength = len;
|
_contentLength = len;
|
||||||
_contentType = contentType;
|
_contentType = contentType;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t AsyncStreamResponse::_fillBuffer(uint8_t *data, size_t len){
|
size_t AsyncStreamResponse::_fillBuffer(uint8_t* data, size_t len) {
|
||||||
size_t available = _content->available();
|
size_t available = _content->available();
|
||||||
size_t outLen = (available > len)?len:available;
|
size_t outLen = (available > len) ? len : available;
|
||||||
size_t i;
|
size_t i;
|
||||||
for(i=0;i<outLen;i++)
|
for (i = 0; i < outLen; i++)
|
||||||
data[i] = _content->read();
|
data[i] = _content->read();
|
||||||
return outLen;
|
return outLen;
|
||||||
}
|
}
|
||||||
@ -603,20 +652,20 @@ size_t AsyncStreamResponse::_fillBuffer(uint8_t *data, size_t len){
|
|||||||
* Callback Response
|
* Callback Response
|
||||||
* */
|
* */
|
||||||
|
|
||||||
AsyncCallbackResponse::AsyncCallbackResponse(const String& contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback): AsyncAbstractResponse(templateCallback) {
|
AsyncCallbackResponse::AsyncCallbackResponse(const String& contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback) : AsyncAbstractResponse(templateCallback) {
|
||||||
_code = 200;
|
_code = 200;
|
||||||
_content = callback;
|
_content = callback;
|
||||||
_contentLength = len;
|
_contentLength = len;
|
||||||
if(!len)
|
if (!len)
|
||||||
_sendContentLength = false;
|
_sendContentLength = false;
|
||||||
_contentType = contentType;
|
_contentType = contentType;
|
||||||
_filledLength = 0;
|
_filledLength = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t AsyncCallbackResponse::_fillBuffer(uint8_t *data, size_t len){
|
size_t AsyncCallbackResponse::_fillBuffer(uint8_t* data, size_t len) {
|
||||||
size_t ret = _content(data, len, _filledLength);
|
size_t ret = _content(data, len, _filledLength);
|
||||||
if(ret != RESPONSE_TRY_AGAIN){
|
if (ret != RESPONSE_TRY_AGAIN) {
|
||||||
_filledLength += ret;
|
_filledLength += ret;
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@ -625,7 +674,7 @@ size_t AsyncCallbackResponse::_fillBuffer(uint8_t *data, size_t len){
|
|||||||
* Chunked Response
|
* Chunked Response
|
||||||
* */
|
* */
|
||||||
|
|
||||||
AsyncChunkedResponse::AsyncChunkedResponse(const String& contentType, AwsResponseFiller callback, AwsTemplateProcessor processorCallback): AsyncAbstractResponse(processorCallback) {
|
AsyncChunkedResponse::AsyncChunkedResponse(const String& contentType, AwsResponseFiller callback, AwsTemplateProcessor processorCallback) : AsyncAbstractResponse(processorCallback) {
|
||||||
_code = 200;
|
_code = 200;
|
||||||
_content = callback;
|
_content = callback;
|
||||||
_contentLength = 0;
|
_contentLength = 0;
|
||||||
@ -635,10 +684,10 @@ AsyncChunkedResponse::AsyncChunkedResponse(const String& contentType, AwsRespons
|
|||||||
_filledLength = 0;
|
_filledLength = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t AsyncChunkedResponse::_fillBuffer(uint8_t *data, size_t len){
|
size_t AsyncChunkedResponse::_fillBuffer(uint8_t* data, size_t len) {
|
||||||
size_t ret = _content(data, len, _filledLength);
|
size_t ret = _content(data, len, _filledLength);
|
||||||
if(ret != RESPONSE_TRY_AGAIN){
|
if (ret != RESPONSE_TRY_AGAIN) {
|
||||||
_filledLength += ret;
|
_filledLength += ret;
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@ -647,7 +696,7 @@ size_t AsyncChunkedResponse::_fillBuffer(uint8_t *data, size_t len){
|
|||||||
* Progmem Response
|
* Progmem Response
|
||||||
* */
|
* */
|
||||||
|
|
||||||
AsyncProgmemResponse::AsyncProgmemResponse(int code, const String& contentType, const uint8_t * content, size_t len, AwsTemplateProcessor callback): AsyncAbstractResponse(callback) {
|
AsyncProgmemResponse::AsyncProgmemResponse(int code, const String& contentType, const uint8_t* content, size_t len, AwsTemplateProcessor callback) : AsyncAbstractResponse(callback) {
|
||||||
_code = code;
|
_code = code;
|
||||||
_content = content;
|
_content = content;
|
||||||
_contentType = contentType;
|
_contentType = contentType;
|
||||||
@ -655,7 +704,7 @@ AsyncProgmemResponse::AsyncProgmemResponse(int code, const String& contentType,
|
|||||||
_readLength = 0;
|
_readLength = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t AsyncProgmemResponse::_fillBuffer(uint8_t *data, size_t len){
|
size_t AsyncProgmemResponse::_fillBuffer(uint8_t* data, size_t len) {
|
||||||
size_t left = _contentLength - _readLength;
|
size_t left = _contentLength - _readLength;
|
||||||
if (left > len) {
|
if (left > len) {
|
||||||
memcpy_P(data, _content + _readLength, len);
|
memcpy_P(data, _content + _readLength, len);
|
||||||
@ -667,30 +716,28 @@ size_t AsyncProgmemResponse::_fillBuffer(uint8_t *data, size_t len){
|
|||||||
return left;
|
return left;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Response Stream (You can print/write/printf to it, up to the contentLen bytes)
|
* Response Stream (You can print/write/printf to it, up to the contentLen bytes)
|
||||||
* */
|
* */
|
||||||
|
|
||||||
AsyncResponseStream::AsyncResponseStream(const String& contentType, size_t bufferSize)
|
AsyncResponseStream::AsyncResponseStream(const String& contentType, size_t bufferSize) {
|
||||||
{
|
|
||||||
_code = 200;
|
_code = 200;
|
||||||
_contentLength = 0;
|
_contentLength = 0;
|
||||||
_contentType = contentType;
|
_contentType = contentType;
|
||||||
_content = std::unique_ptr<cbuf>(new cbuf(bufferSize)); //std::make_unique<cbuf>(bufferSize);
|
_content = std::unique_ptr<cbuf>(new cbuf(bufferSize)); // std::make_unique<cbuf>(bufferSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
AsyncResponseStream::~AsyncResponseStream() = default;
|
AsyncResponseStream::~AsyncResponseStream() = default;
|
||||||
|
|
||||||
size_t AsyncResponseStream::_fillBuffer(uint8_t *buf, size_t maxLen){
|
size_t AsyncResponseStream::_fillBuffer(uint8_t* buf, size_t maxLen) {
|
||||||
return _content->read((char*)buf, maxLen);
|
return _content->read((char*)buf, maxLen);
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t AsyncResponseStream::write(const uint8_t *data, size_t len){
|
size_t AsyncResponseStream::write(const uint8_t* data, size_t len) {
|
||||||
if(_started())
|
if (_started())
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if(len > _content->room()){
|
if (len > _content->room()) {
|
||||||
size_t needed = len - _content->room();
|
size_t needed = len - _content->room();
|
||||||
_content->resizeAdd(needed);
|
_content->resizeAdd(needed);
|
||||||
}
|
}
|
||||||
@ -699,6 +746,6 @@ size_t AsyncResponseStream::write(const uint8_t *data, size_t len){
|
|||||||
return written;
|
return written;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t AsyncResponseStream::write(uint8_t data){
|
size_t AsyncResponseStream::write(uint8_t data) {
|
||||||
return write(&data, 1);
|
return write(&data, 1);
|
||||||
}
|
}
|
||||||
|
@ -21,105 +21,126 @@
|
|||||||
#include "ESPAsyncWebServer.h"
|
#include "ESPAsyncWebServer.h"
|
||||||
#include "WebHandlerImpl.h"
|
#include "WebHandlerImpl.h"
|
||||||
|
|
||||||
bool ON_STA_FILTER(AsyncWebServerRequest *request) {
|
bool ON_STA_FILTER(AsyncWebServerRequest* request) {
|
||||||
return WiFi.localIP() == request->client()->localIP();
|
return WiFi.localIP() == request->client()->localIP();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ON_AP_FILTER(AsyncWebServerRequest *request) {
|
bool ON_AP_FILTER(AsyncWebServerRequest* request) {
|
||||||
return WiFi.localIP() != request->client()->localIP();
|
return WiFi.localIP() != request->client()->localIP();
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef HAVE_FS_FILE_OPEN_MODE
|
#ifndef HAVE_FS_FILE_OPEN_MODE
|
||||||
const char *fs::FileOpenMode::read = "r";
|
const char* fs::FileOpenMode::read = "r";
|
||||||
const char *fs::FileOpenMode::write = "w";
|
const char* fs::FileOpenMode::write = "w";
|
||||||
const char *fs::FileOpenMode::append = "a";
|
const char* fs::FileOpenMode::append = "a";
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
AsyncWebServer::AsyncWebServer(uint16_t port)
|
AsyncWebServer::AsyncWebServer(uint16_t port)
|
||||||
: _server(port)
|
: _server(port) {
|
||||||
, _rewrites(LinkedList<AsyncWebRewrite*>([](AsyncWebRewrite* r){ delete r; }))
|
|
||||||
, _handlers(LinkedList<AsyncWebHandler*>([](AsyncWebHandler* h){ delete h; }))
|
|
||||||
{
|
|
||||||
_catchAllHandler = new AsyncCallbackWebHandler();
|
_catchAllHandler = new AsyncCallbackWebHandler();
|
||||||
if(_catchAllHandler == NULL)
|
if (_catchAllHandler == NULL)
|
||||||
return;
|
return;
|
||||||
_server.onClient([](void *s, AsyncClient* c){
|
_server.onClient([](void* s, AsyncClient* c) {
|
||||||
if(c == NULL)
|
if (c == NULL)
|
||||||
return;
|
return;
|
||||||
c->setRxTimeout(3);
|
c->setRxTimeout(3);
|
||||||
AsyncWebServerRequest *r = new AsyncWebServerRequest((AsyncWebServer*)s, c);
|
AsyncWebServerRequest* r = new AsyncWebServerRequest((AsyncWebServer*)s, c);
|
||||||
if(r == NULL){
|
if (r == NULL) {
|
||||||
c->close(true);
|
c->close(true);
|
||||||
c->free();
|
c->free();
|
||||||
delete c;
|
delete c;
|
||||||
}
|
}
|
||||||
}, this);
|
},
|
||||||
|
this);
|
||||||
}
|
}
|
||||||
|
|
||||||
AsyncWebServer::~AsyncWebServer(){
|
AsyncWebServer::~AsyncWebServer() {
|
||||||
reset();
|
reset();
|
||||||
end();
|
end();
|
||||||
if(_catchAllHandler) delete _catchAllHandler;
|
if (_catchAllHandler)
|
||||||
|
delete _catchAllHandler;
|
||||||
}
|
}
|
||||||
|
|
||||||
AsyncWebRewrite& AsyncWebServer::addRewrite(AsyncWebRewrite* rewrite){
|
AsyncWebRewrite& AsyncWebServer::addRewrite(std::shared_ptr<AsyncWebRewrite> rewrite) {
|
||||||
_rewrites.add(rewrite);
|
_rewrites.emplace_back(rewrite);
|
||||||
return *rewrite;
|
return *_rewrites.back().get();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AsyncWebServer::removeRewrite(AsyncWebRewrite *rewrite){
|
AsyncWebRewrite& AsyncWebServer::addRewrite(AsyncWebRewrite* rewrite) {
|
||||||
return _rewrites.remove(rewrite);
|
_rewrites.emplace_back(rewrite);
|
||||||
|
return *_rewrites.back().get();
|
||||||
}
|
}
|
||||||
|
|
||||||
AsyncWebRewrite& AsyncWebServer::rewrite(const char* from, const char* to){
|
bool AsyncWebServer::removeRewrite(AsyncWebRewrite* rewrite) {
|
||||||
return addRewrite(new AsyncWebRewrite(from, to));
|
return removeRewrite(rewrite->from().c_str(), rewrite->toUrl().c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
AsyncWebHandler& AsyncWebServer::addHandler(AsyncWebHandler* handler){
|
bool AsyncWebServer::removeRewrite(const char* from, const char* to) {
|
||||||
_handlers.add(handler);
|
for (auto r = _rewrites.begin(); r != _rewrites.end(); ++r) {
|
||||||
return *handler;
|
if (r->get()->from() == from && r->get()->toUrl() == to) {
|
||||||
|
_rewrites.erase(r);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AsyncWebServer::removeHandler(AsyncWebHandler *handler){
|
AsyncWebRewrite& AsyncWebServer::rewrite(const char* from, const char* to) {
|
||||||
return _handlers.remove(handler);
|
_rewrites.emplace_back(std::make_shared<AsyncWebRewrite>(from, to));
|
||||||
|
return *_rewrites.back().get();
|
||||||
}
|
}
|
||||||
|
|
||||||
void AsyncWebServer::begin(){
|
AsyncWebHandler& AsyncWebServer::addHandler(AsyncWebHandler* handler) {
|
||||||
|
_handlers.emplace_back(handler);
|
||||||
|
return *(_handlers.back().get());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AsyncWebServer::removeHandler(AsyncWebHandler* handler) {
|
||||||
|
for (auto i = _handlers.begin(); i != _handlers.end(); ++i) {
|
||||||
|
if (i->get() == handler) {
|
||||||
|
_handlers.erase(i);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AsyncWebServer::begin() {
|
||||||
_server.setNoDelay(true);
|
_server.setNoDelay(true);
|
||||||
_server.begin();
|
_server.begin();
|
||||||
}
|
}
|
||||||
|
|
||||||
void AsyncWebServer::end(){
|
void AsyncWebServer::end() {
|
||||||
_server.end();
|
_server.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
#if ASYNC_TCP_SSL_ENABLED
|
#if ASYNC_TCP_SSL_ENABLED
|
||||||
void AsyncWebServer::onSslFileRequest(AcSSlFileHandler cb, void* arg){
|
void AsyncWebServer::onSslFileRequest(AcSSlFileHandler cb, void* arg) {
|
||||||
_server.onSslFileRequest(cb, arg);
|
_server.onSslFileRequest(cb, arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AsyncWebServer::beginSecure(const char *cert, const char *key, const char *password){
|
void AsyncWebServer::beginSecure(const char* cert, const char* key, const char* password) {
|
||||||
_server.beginSecure(cert, key, password);
|
_server.beginSecure(cert, key, password);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void AsyncWebServer::_handleDisconnect(AsyncWebServerRequest *request){
|
void AsyncWebServer::_handleDisconnect(AsyncWebServerRequest* request) {
|
||||||
delete request;
|
delete request;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AsyncWebServer::_rewriteRequest(AsyncWebServerRequest *request){
|
void AsyncWebServer::_rewriteRequest(AsyncWebServerRequest* request) {
|
||||||
for(const auto& r: _rewrites){
|
for (const auto& r : _rewrites) {
|
||||||
if (r->match(request)){
|
if (r->match(request)) {
|
||||||
request->_url = r->toUrl();
|
request->_url = r->toUrl();
|
||||||
request->_addGetParams(r->params());
|
request->_addGetParams(r->params());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AsyncWebServer::_attachHandler(AsyncWebServerRequest *request){
|
void AsyncWebServer::_attachHandler(AsyncWebServerRequest* request) {
|
||||||
for(const auto& h: _handlers){
|
for (auto& h : _handlers) {
|
||||||
if (h->filter(request) && h->canHandle(request)){
|
if (h->filter(request) && h->canHandle(request)) {
|
||||||
request->setHandler(h);
|
request->setHandler(h.get());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -128,8 +149,7 @@ void AsyncWebServer::_attachHandler(AsyncWebServerRequest *request){
|
|||||||
request->setHandler(_catchAllHandler);
|
request->setHandler(_catchAllHandler);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AsyncCallbackWebHandler& AsyncWebServer::on(const char* uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest, ArUploadHandlerFunction onUpload, ArBodyHandlerFunction onBody) {
|
||||||
AsyncCallbackWebHandler& AsyncWebServer::on(const char* uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest, ArUploadHandlerFunction onUpload, ArBodyHandlerFunction onBody){
|
|
||||||
AsyncCallbackWebHandler* handler = new AsyncCallbackWebHandler();
|
AsyncCallbackWebHandler* handler = new AsyncCallbackWebHandler();
|
||||||
handler->setUri(uri);
|
handler->setUri(uri);
|
||||||
handler->setMethod(method);
|
handler->setMethod(method);
|
||||||
@ -140,7 +160,7 @@ AsyncCallbackWebHandler& AsyncWebServer::on(const char* uri, WebRequestMethodCom
|
|||||||
return *handler;
|
return *handler;
|
||||||
}
|
}
|
||||||
|
|
||||||
AsyncCallbackWebHandler& AsyncWebServer::on(const char* uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest, ArUploadHandlerFunction onUpload){
|
AsyncCallbackWebHandler& AsyncWebServer::on(const char* uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest, ArUploadHandlerFunction onUpload) {
|
||||||
AsyncCallbackWebHandler* handler = new AsyncCallbackWebHandler();
|
AsyncCallbackWebHandler* handler = new AsyncCallbackWebHandler();
|
||||||
handler->setUri(uri);
|
handler->setUri(uri);
|
||||||
handler->setMethod(method);
|
handler->setMethod(method);
|
||||||
@ -150,7 +170,7 @@ AsyncCallbackWebHandler& AsyncWebServer::on(const char* uri, WebRequestMethodCom
|
|||||||
return *handler;
|
return *handler;
|
||||||
}
|
}
|
||||||
|
|
||||||
AsyncCallbackWebHandler& AsyncWebServer::on(const char* uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest){
|
AsyncCallbackWebHandler& AsyncWebServer::on(const char* uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest) {
|
||||||
AsyncCallbackWebHandler* handler = new AsyncCallbackWebHandler();
|
AsyncCallbackWebHandler* handler = new AsyncCallbackWebHandler();
|
||||||
handler->setUri(uri);
|
handler->setUri(uri);
|
||||||
handler->setMethod(method);
|
handler->setMethod(method);
|
||||||
@ -159,7 +179,7 @@ AsyncCallbackWebHandler& AsyncWebServer::on(const char* uri, WebRequestMethodCom
|
|||||||
return *handler;
|
return *handler;
|
||||||
}
|
}
|
||||||
|
|
||||||
AsyncCallbackWebHandler& AsyncWebServer::on(const char* uri, ArRequestHandlerFunction onRequest){
|
AsyncCallbackWebHandler& AsyncWebServer::on(const char* uri, ArRequestHandlerFunction onRequest) {
|
||||||
AsyncCallbackWebHandler* handler = new AsyncCallbackWebHandler();
|
AsyncCallbackWebHandler* handler = new AsyncCallbackWebHandler();
|
||||||
handler->setUri(uri);
|
handler->setUri(uri);
|
||||||
handler->onRequest(onRequest);
|
handler->onRequest(onRequest);
|
||||||
@ -167,32 +187,31 @@ AsyncCallbackWebHandler& AsyncWebServer::on(const char* uri, ArRequestHandlerFun
|
|||||||
return *handler;
|
return *handler;
|
||||||
}
|
}
|
||||||
|
|
||||||
AsyncStaticWebHandler& AsyncWebServer::serveStatic(const char* uri, fs::FS& fs, const char* path, const char* cache_control){
|
AsyncStaticWebHandler& AsyncWebServer::serveStatic(const char* uri, fs::FS& fs, const char* path, const char* cache_control) {
|
||||||
AsyncStaticWebHandler* handler = new AsyncStaticWebHandler(uri, fs, path, cache_control);
|
AsyncStaticWebHandler* handler = new AsyncStaticWebHandler(uri, fs, path, cache_control);
|
||||||
addHandler(handler);
|
addHandler(handler);
|
||||||
return *handler;
|
return *handler;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AsyncWebServer::onNotFound(ArRequestHandlerFunction fn){
|
void AsyncWebServer::onNotFound(ArRequestHandlerFunction fn) {
|
||||||
_catchAllHandler->onRequest(fn);
|
_catchAllHandler->onRequest(fn);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AsyncWebServer::onFileUpload(ArUploadHandlerFunction fn){
|
void AsyncWebServer::onFileUpload(ArUploadHandlerFunction fn) {
|
||||||
_catchAllHandler->onUpload(fn);
|
_catchAllHandler->onUpload(fn);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AsyncWebServer::onRequestBody(ArBodyHandlerFunction fn){
|
void AsyncWebServer::onRequestBody(ArBodyHandlerFunction fn) {
|
||||||
_catchAllHandler->onBody(fn);
|
_catchAllHandler->onBody(fn);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AsyncWebServer::reset(){
|
void AsyncWebServer::reset() {
|
||||||
_rewrites.free();
|
_rewrites.clear();
|
||||||
_handlers.free();
|
_handlers.clear();
|
||||||
|
|
||||||
if (_catchAllHandler != NULL){
|
if (_catchAllHandler != NULL) {
|
||||||
_catchAllHandler->onRequest(NULL);
|
_catchAllHandler->onRequest(NULL);
|
||||||
_catchAllHandler->onUpload(NULL);
|
_catchAllHandler->onUpload(NULL);
|
||||||
_catchAllHandler->onBody(NULL);
|
_catchAllHandler->onBody(NULL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user