Verze 3.6.0 z noveho repozitare https://github.com/ESP32Async/ESPAsyncWebServer
This commit is contained in:
parent
9dca4f5dee
commit
d55bb23fef
151
README.md
151
README.md
@ -1,32 +1,44 @@
|
|||||||
|
![https://avatars.githubusercontent.com/u/195753706?s=96&v=4](https://avatars.githubusercontent.com/u/195753706?s=96&v=4)
|
||||||
|
|
||||||
# ESPAsyncWebServer
|
# ESPAsyncWebServer
|
||||||
|
|
||||||
[![Latest Release](https://img.shields.io/github/release/mathieucarbou/ESPAsyncWebServer.svg)](https://GitHub.com/mathieucarbou/ESPAsyncWebServer/releases/)
|
[![Latest Release](https://img.shields.io/github/release/ESP32Async/ESPAsyncWebServer.svg)](https://GitHub.com/ESP32Async/ESPAsyncWebServer/releases/)
|
||||||
[![PlatformIO Registry](https://badges.registry.platformio.org/packages/mathieucarbou/library/ESPAsyncWebServer.svg)](https://registry.platformio.org/libraries/mathieucarbou/ESPAsyncWebServer)
|
[![PlatformIO Registry](https://badges.registry.platformio.org/packages/ESP32Async/library/ESPAsyncWebServer.svg)](https://registry.platformio.org/libraries/ESP32Async/ESPAsyncWebServer)
|
||||||
|
|
||||||
[![License: LGPL 3.0](https://img.shields.io/badge/License-LGPL%203.0-yellow.svg)](https://opensource.org/license/lgpl-3-0/)
|
[![License: LGPL 3.0](https://img.shields.io/badge/License-LGPL%203.0-yellow.svg)](https://opensource.org/license/lgpl-3-0/)
|
||||||
[![Contributor Covenant](https://img.shields.io/badge/Contributor%20Covenant-2.1-4baaaa.svg)](code_of_conduct.md)
|
[![Contributor Covenant](https://img.shields.io/badge/Contributor%20Covenant-2.1-4baaaa.svg)](code_of_conduct.md)
|
||||||
|
|
||||||
[![Build](https://github.com/mathieucarbou/ESPAsyncWebServer/actions/workflows/ci.yml/badge.svg)](https://github.com/mathieucarbou/ESPAsyncWebServer/actions/workflows/ci.yml)
|
[![Build](https://github.com/ESP32Async/ESPAsyncWebServer/actions/workflows/ci.yml/badge.svg)](https://github.com/ESP32Async/ESPAsyncWebServer/actions/workflows/ci.yml)
|
||||||
[![GitHub latest commit](https://badgen.net/github/last-commit/mathieucarbou/ESPAsyncWebServer)](https://GitHub.com/mathieucarbou/ESPAsyncWebServer/commit/)
|
[![GitHub latest commit](https://badgen.net/github/last-commit/ESP32Async/ESPAsyncWebServer)](https://GitHub.com/ESP32Async/ESPAsyncWebServer/commit/)
|
||||||
[![Gitpod Ready-to-Code](https://img.shields.io/badge/Gitpod-Ready--to--Code-blue?logo=gitpod)](https://gitpod.io/#https://github.com/mathieucarbou/ESPAsyncWebServer)
|
[![Gitpod Ready-to-Code](https://img.shields.io/badge/Gitpod-Ready--to--Code-blue?logo=gitpod)](https://gitpod.io/#https://github.com/ESP32Async/ESPAsyncWebServer)
|
||||||
|
|
||||||
|
Project moved to [ESP32Async](https://github.com/organizations/ESP32Async) organization at [https://github.com/ESP32Async/ESPAsyncWebServer](https://github.com/ESP32Async/ESPAsyncWebServer)
|
||||||
|
|
||||||
|
Discord Server: [https://discord.gg/X7zpGdyUcY](https://discord.gg/X7zpGdyUcY)
|
||||||
|
|
||||||
|
Please see the new links:
|
||||||
|
|
||||||
|
- `ESP32Async/ESPAsyncWebServer @ 3.6.0` (ESP32, ESP8266, RP2040)
|
||||||
|
- `ESP32Async/AsyncTCP @ 3.3.2` (ESP32)
|
||||||
|
- `ESP32Async/ESPAsyncTCP @ 2.0.0` (ESP8266)
|
||||||
|
- `https://github.com/ESP32Async/AsyncTCPSock/archive/refs/tags/v1.0.3-dev.zip` (AsyncTCP alternative for ESP32)
|
||||||
|
- `khoih-prog/AsyncTCP_RP2040W @ 1.2.0` (RP2040)
|
||||||
|
|
||||||
Asynchronous HTTP and WebSocket Server Library for ESP32, ESP8266 and RP2040
|
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.
|
- [Changes in this repository](#changes-in-this-repository)
|
||||||
|
|
||||||
- [Changes in this fork](#changes-in-this-fork)
|
|
||||||
- [Dependencies](#dependencies)
|
- [Dependencies](#dependencies)
|
||||||
- [Performance](#performance)
|
- [Performance](#performance)
|
||||||
- [Important recommendations](#important-recommendations)
|
- [Important recommendations for build options](#important-recommendations-for-build-options)
|
||||||
- [`AsyncWebSocketMessageBuffer` and `makeBuffer()`](#asyncwebsocketmessagebuffer-and-makebuffer)
|
- [`AsyncWebSocketMessageBuffer` and `makeBuffer()`](#asyncwebsocketmessagebuffer-and-makebuffer)
|
||||||
- [How to replace a response](#how-to-replace-a-response)
|
- [How to replace a response](#how-to-replace-a-response)
|
||||||
- [How to use Middleware](#how-to-use-middleware)
|
- [How to use Middleware](#how-to-use-middleware)
|
||||||
- [How to use authentication with AuthenticationMiddleware](#how-to-use-authentication-with-authenticationmiddleware)
|
- [How to use authentication with AsyncAuthenticationMiddleware](#how-to-use-authentication-with-authenticationmiddleware)
|
||||||
- [Migration to Middleware to improve performance and memory usage](#migration-to-middleware-to-improve-performance-and-memory-usage)
|
- [Migration to Middleware to improve performance and memory usage](#migration-to-middleware-to-improve-performance-and-memory-usage)
|
||||||
- [Original Documentation](#original-documentation)
|
- [Original Documentation](#original-documentation)
|
||||||
|
|
||||||
## Changes in this fork
|
## Changes in this repository
|
||||||
|
|
||||||
- (bug) A lot of bug fixes
|
- (bug) A lot of bug fixes
|
||||||
- (ci) Better CI with a complete matrix of Arduino versions and boards
|
- (ci) Better CI with a complete matrix of Arduino versions and boards
|
||||||
@ -42,12 +54,13 @@ This fork is based on [yubox-node-org/ESPAsyncWebServer](https://github.com/yubo
|
|||||||
- (feat) **Resumable download** support using HEAD and bytes range
|
- (feat) **Resumable download** support using HEAD and bytes range
|
||||||
- (feat) `StreamConcat` example to show how to stream multiple files in one response
|
- (feat) `StreamConcat` example to show how to stream multiple files in one response
|
||||||
- (feat) Removed ESPIDF Editor (this is not the role of a web server library to do that - get the source files from the original repos if required)
|
- (feat) Removed ESPIDF Editor (this is not the role of a web server library to do that - get the source files from the original repos if required)
|
||||||
- (perf) [AsyncTCPSock](https://github.com/mathieucarbou/AsyncTCPSock) support: AsyncTCP can be ignored and AsyncTCPSock used instead
|
- (perf) [AsyncTCPSock](https://github.com/ESP32Async/AsyncTCPSock) support: AsyncTCP can be ignored and AsyncTCPSock used instead
|
||||||
- (perf) `char*` overloads to avoid using `String`
|
- (perf) `char*` overloads to avoid using `String`
|
||||||
- (perf) `DEFAULT_MAX_WS_CLIENTS` to change the number of allows WebSocket clients and use `cleanupClients()` to help cleanup resources about dead clients
|
- (perf) `DEFAULT_MAX_WS_CLIENTS` to change the number of allows WebSocket clients and use `cleanupClients()` to help cleanup resources about dead clients
|
||||||
- (perf) `setCloseClientOnQueueFull(bool)` which can be set on a client to either close the connection or discard messages but not close the connection when the queue is full
|
- (perf) `setCloseClientOnQueueFull(bool)` which can be set on a client to either close the connection or discard messages but not close the connection when the queue is full
|
||||||
- (perf) `SSE_MAX_QUEUED_MESSAGES` to control the maximum number of messages that can be queued for a SSE client
|
- (perf) `SSE_MAX_QUEUED_MESSAGES` to control the maximum number of messages that can be queued for a SSE client
|
||||||
- (perf) `WS_MAX_QUEUED_MESSAGES`: control the maximum number of messages that can be queued for a Websocket client
|
- (perf) `WS_MAX_QUEUED_MESSAGES`: control the maximum number of messages that can be queued for a Websocket client
|
||||||
|
- (perf) in-flight buffer control and queue congestion avoidance to help to improve parallel connections handling, high volume data transfers and mitigate poor implemeneted slow user-code callbacks delayes on connctions handling
|
||||||
- (perf) Code size improvements
|
- (perf) Code size improvements
|
||||||
- (perf) Lot of code cleanup and optimizations
|
- (perf) Lot of code cleanup and optimizations
|
||||||
- (perf) Performance improvements in terms of memory, speed and size
|
- (perf) Performance improvements in terms of memory, speed and size
|
||||||
@ -56,37 +69,44 @@ This fork is based on [yubox-node-org/ESPAsyncWebServer](https://github.com/yubo
|
|||||||
|
|
||||||
## WARNING: Important notes about future version 4.x
|
## WARNING: Important notes about future version 4.x
|
||||||
|
|
||||||
This ESPAsyncWebServer fork is now at version 3.x.
|
This ESPAsyncWebServer fork is now at version 3.x, where we try to keep the API compatibility with original project as much as possible.
|
||||||
|
|
||||||
Next version 4.x will:
|
We plan on creating a next major 4.x version that will:
|
||||||
|
|
||||||
1. Drop support for ESP8266, which goes EOL in a few years. All ESP8266 boards can be replaced by equivalent ESP32 boards.
|
1. Drop support for ESP8266, which goes EOL in a few years
|
||||||
2. Drop support for Arduino 2.x and ESP-IDF 4.x. The library will be compatible with Arduino 3.x and ESP-IDF 5.x.
|
2. Drop support for Arduino 2.x and ESP-IDF 4.x. The library will be compatible with latest Arduino and ESP-IDF
|
||||||
3. Drop support for ArduinoJson 5.x and 6.x. The library will be compatible with ArduinoJson 7.x.
|
3. Drop support for ArduinoJson 5.x and 6.x. The library will be compatible with latest ArduinoJson
|
||||||
|
|
||||||
So if you need one of these feature, you will have to stick with 3.x or another fork.
|
So if you need one of these feature, you will have to stick with the current 3.x.
|
||||||
|
All releases we do will not cease to exist: all 3.x releases will stay in the release page.
|
||||||
|
That is why we have tags and a release cycle.
|
||||||
|
|
||||||
|
Maintaining a library for ESP8266 and RP2040 has a real cost and clearly what we see is that most users helping are on ESP32.
|
||||||
|
|
||||||
|
If you are an ESP8266 user and want to help improve current 3.x, you are more than welcomed to contribute to this community effort.
|
||||||
|
|
||||||
## Dependencies
|
## Dependencies
|
||||||
|
|
||||||
**WARNING** The library name was changed from `ESP Async WebServer` to `ESPAsyncWebServer` as per the Arduino Lint recommendations, but its name had to stay `ESP Async WebServer` in Arduino Registry.
|
> [!WARNING]
|
||||||
|
> The library name was changed from `ESP Async WebServer` to `ESPAsyncWebServer` as per the Arduino Lint recommendations, but its name had to stay `ESP Async WebServer` in Arduino Registry.
|
||||||
|
|
||||||
**PlatformIO / pioarduino:**
|
**PlatformIO / pioarduino:**
|
||||||
|
|
||||||
```ini
|
```ini
|
||||||
lib_compat_mode = strict
|
lib_compat_mode = strict
|
||||||
lib_ldf_mode = chain
|
lib_ldf_mode = chain
|
||||||
lib_deps = mathieucarbou/ESPAsyncWebServer @ 3.4.5
|
lib_deps = ESP32Async/ESPAsyncWebServer @ 3.6.0
|
||||||
```
|
```
|
||||||
|
|
||||||
**Dependencies:**
|
**Dependencies:**
|
||||||
|
|
||||||
- **ESP32 with AsyncTCP**: `mathieucarbou/AsyncTCP @ 3.3.1`
|
- **ESP32 with AsyncTCP**: `ESP32Async/AsyncTCP @ 3.3.2`
|
||||||
Arduino IDE: [https://github.com/mathieucarbou/AsyncTCP#v3.3.1](https://github.com/mathieucarbou/AsyncTCP/releases)
|
Arduino IDE: [https://github.com/ESP32Async/AsyncTCP#v3.3.2](https://github.com/ESP32Async/AsyncTCP/releases)
|
||||||
|
|
||||||
- **ESP32 with AsyncTCPSock**: `https://github.com/mathieucarbou/AsyncTCPSock/archive/refs/tags/v1.0.3-dev.zip`
|
- **ESP32 with AsyncTCPSock**: `https://github.com/ESP32Async/AsyncTCPSock/archive/refs/tags/v1.0.3-dev.zip`
|
||||||
|
|
||||||
- **ESP8266**: `esphome/ESPAsyncTCP-esphome @ 2.0.0`
|
- **ESP8266**: `ESP32Async/ESPAsyncTCP @ 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)
|
Arduino IDE: [https://github.com/ESP32Async/ESPAsyncTCP#v2.0.0](https://github.com/ESP32Async/ESPAsyncTCP/releases/tag/v2.0.0)
|
||||||
|
|
||||||
- **RP2040**: `khoih-prog/AsyncTCP_RP2040W @ 1.2.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)
|
Arduino IDE: [https://github.com/khoih-prog/AsyncTCP_RP2040W#v1.2.0](https://github.com/khoih-prog/AsyncTCP_RP2040W/releases/tag/v1.2.0)
|
||||||
@ -99,30 +119,30 @@ AsyncTCPSock can be used instead of AsyncTCP by excluding AsyncTCP from the libr
|
|||||||
lib_compat_mode = strict
|
lib_compat_mode = strict
|
||||||
lib_ldf_mode = chain
|
lib_ldf_mode = chain
|
||||||
lib_deps =
|
lib_deps =
|
||||||
; mathieucarbou/AsyncTCP @ 3.3.1
|
; ESP32Async/AsyncTCP @ 3.3.2
|
||||||
https://github.com/mathieucarbou/AsyncTCPSock/archive/refs/tags/v1.0.3-dev.zip
|
https://github.com/ESP32Async/AsyncTCPSock/archive/refs/tags/v1.0.3-dev.zip
|
||||||
mathieucarbou/ESPAsyncWebServer @ 3.4.5
|
ESP32Async/ESPAsyncWebServer @ 3.6.0
|
||||||
lib_ignore =
|
lib_ignore =
|
||||||
AsyncTCP
|
AsyncTCP
|
||||||
mathieucarbou/AsyncTCP
|
ESP32Async/AsyncTCP
|
||||||
```
|
```
|
||||||
|
|
||||||
## Performance
|
## Performance
|
||||||
|
|
||||||
Performance of `mathieucarbou/ESPAsyncWebServer @ 3.4.5`:
|
Performance of `ESP32Async/ESPAsyncWebServer @ 3.6.0`:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
> brew install autocannon
|
> brew install autocannon
|
||||||
> autocannon -c 10 -w 10 -d 20 http://192.168.4.1
|
> autocannon -c 10 -w 10 -d 20 http://192.168.4.1
|
||||||
```
|
```
|
||||||
|
|
||||||
With `mathieucarbou/AsyncTCP @ 3.3.1`
|
With `ESP32Async/AsyncTCP @ 3.3.2`
|
||||||
|
|
||||||
[![](https://mathieu.carbou.me/ESPAsyncWebServer/perf-c10.png)](https://mathieu.carbou.me/ESPAsyncWebServer/perf-c10.png)
|
<img width="629" alt="perf-c10" src="https://github.com/user-attachments/assets/b4b7f953-c24d-4e04-8d87-ba3f26805737" />
|
||||||
|
|
||||||
With `https://github.com/mathieucarbou/AsyncTCPSock/archive/refs/tags/v1.0.3-dev.zip`:
|
With `https://github.com/ESP32Async/AsyncTCPSock/archive/refs/tags/v1.0.3-dev.zip`:
|
||||||
|
|
||||||
[![](https://mathieu.carbou.me/ESPAsyncWebServer/perf-c10-asynctcpsock.png)](https://mathieu.carbou.me/ESPAsyncWebServer/perf-c10-asynctcpsock.png)
|
<img width="654" alt="perf-c10-asynctcpsock" src="https://github.com/user-attachments/assets/0dacf133-ca47-40be-939b-e6f60fc95b81" />
|
||||||
|
|
||||||
**SSE performance**
|
**SSE performance**
|
||||||
|
|
||||||
@ -158,26 +178,41 @@ Test is running for 20 seconds with 10 connections.
|
|||||||
// Total: 2038 events, 509.50 events / second
|
// Total: 2038 events, 509.50 events / second
|
||||||
```
|
```
|
||||||
|
|
||||||
## Important recommendations
|
## Important recommendations for build options
|
||||||
|
|
||||||
Most of the crashes are caused by improper configuration of the library for the project.
|
Most of the crashes are caused by improper use or configuration of the AsyncTCP library used for the project.
|
||||||
Here are some recommendations to avoid them.
|
Here are some recommendations to avoid them and build-time flags you can change.
|
||||||
|
|
||||||
|
`CONFIG_ASYNC_TCP_MAX_ACK_TIME` - defines a timeout for TCP connection to be considered alive when waiting for data.
|
||||||
|
In some bad network conditions you might consider increasing it.
|
||||||
|
|
||||||
|
`CONFIG_ASYNC_TCP_QUEUE_SIZE` - defines the length of the queue for events related to connections handling.
|
||||||
|
Both the server and AsyncTCP library in this fork were optimized to control the queue automatically. Do NOT try blindly increasing the queue size, it does not help you in a way you might think it is. If you receive debug messages about queue throttling, try to optimize your server callbaks code to execute as fast as possible.
|
||||||
|
Read #165 thread, it might give you some hints.
|
||||||
|
|
||||||
|
`CONFIG_ASYNC_TCP_RUNNING_CORE` - CPU core thread affinity that runs the queue events handling and executes server callbacks. Default is ANY core, so it means that for dualcore SoCs both cores could handle server activities. If your server's code is too heavy and unoptimized or you see that sometimes
|
||||||
|
server might affect other network activities, you might consider to bind it to the same core that runs Arduino code (1) to minimize affect on radio part. Otherwise you can leave the default to let RTOS decide where to run the thread based on priority
|
||||||
|
|
||||||
|
`CONFIG_ASYNC_TCP_STACK_SIZE` - stack size for the thread that runs sever events and callbacks. Default is 16k that is a way too much waste for well-defined short async code or simple static file handling. You might want to cosider reducing it to 4-8k to same RAM usage. If you do not know what this is or not sure about your callback code demands - leave it as default, should be enough even for very hungry callbacks in most cases.
|
||||||
|
|
||||||
|
> [!NOTE]
|
||||||
|
> This relates to ESP32 only, ESP8266 uses different ESPAsyncTCP lib that does not has this build options
|
||||||
|
|
||||||
I personally use the following configuration in my projects:
|
I personally use the following configuration in my projects:
|
||||||
|
|
||||||
```c++
|
```c++
|
||||||
-D CONFIG_ASYNC_TCP_MAX_ACK_TIME=5000 // (keep default)
|
-D CONFIG_ASYNC_TCP_MAX_ACK_TIME=5000 // (keep default)
|
||||||
-D CONFIG_ASYNC_TCP_PRIORITY=10 // (keep default)
|
-D CONFIG_ASYNC_TCP_PRIORITY=10 // (keep default)
|
||||||
-D CONFIG_ASYNC_TCP_QUEUE_SIZE=64 // (keep default)
|
-D CONFIG_ASYNC_TCP_QUEUE_SIZE=64 // (keep default)
|
||||||
-D CONFIG_ASYNC_TCP_RUNNING_CORE=1 // force async_tcp task to be on same core as the app (default is core 0)
|
-D CONFIG_ASYNC_TCP_RUNNING_CORE=1 // force async_tcp task to be on same core as Arduino app (default is any core)
|
||||||
-D CONFIG_ASYNC_TCP_STACK_SIZE=4096 // reduce the stack size (default is 16K)
|
-D CONFIG_ASYNC_TCP_STACK_SIZE=4096 // reduce the stack size (default is 16K)
|
||||||
```
|
```
|
||||||
|
|
||||||
## `AsyncWebSocketMessageBuffer` and `makeBuffer()`
|
## `AsyncWebSocketMessageBuffer` and `makeBuffer()`
|
||||||
|
|
||||||
The fork from `yubox-node-org` introduces some breaking API changes compared to the original library, especially regarding the use of `std::shared_ptr<std::vector<uint8_t>>` for WebSocket.
|
The fork from [yubox-node-org](https://github.com/yubox-node-org/ESPAsyncWebServer) introduces some breaking API changes compared to the original library, especially regarding the use of `std::shared_ptr<std::vector<uint8_t>>` for WebSocket.
|
||||||
|
|
||||||
This fork is compatible with the original library from `me-no-dev` regarding WebSocket, and wraps the optimizations done by `yubox-node-org` in the `AsyncWebSocketMessageBuffer` class.
|
This fork is compatible with the original library from [me-no-dev](https://github.com/me-no-dev/ESPAsyncWebServer) regarding WebSocket, and wraps the optimizations done by `yubox-node-org` in the `AsyncWebSocketMessageBuffer` class.
|
||||||
So you have the choice of which API to use.
|
So you have the choice of which API to use.
|
||||||
|
|
||||||
Here are examples for serializing a Json document in a websocket message buffer:
|
Here are examples for serializing a Json document in a websocket message buffer:
|
||||||
@ -230,7 +265,7 @@ Middleware is a way to intercept requests to perform some operations on them, li
|
|||||||
Middleware can either be attached to individual handlers, attached at the server level (thus applied to all handlers), or both.
|
Middleware can either be attached to individual handlers, attached at the server level (thus applied to all handlers), or both.
|
||||||
They will be executed in the order they are attached, and they can stop the request processing by sending a response themselves.
|
They will be executed in the order they are attached, and they can stop the request processing by sending a response themselves.
|
||||||
|
|
||||||
You can have a look at the [SimpleServer.ino](https://github.com/mathieucarbou/ESPAsyncWebServer/blob/main/examples/SimpleServer/SimpleServer.ino) example for some use cases.
|
You can have a look at the [SimpleServer.ino](https://github.com/ESP32Async/ESPAsyncWebServer/blob/main/examples/SimpleServer/SimpleServer.ino) example for some use cases.
|
||||||
|
|
||||||
For example, such middleware would handle authentication and set some attributes on the request to make them available for the next middleware and for the handler which will process the request.
|
For example, such middleware would handle authentication and set some attributes on the request to make them available for the next middleware and for the handler which will process the request.
|
||||||
|
|
||||||
@ -253,24 +288,24 @@ AsyncMiddlewareFunction complexAuth([](AsyncWebServerRequest* request, ArMiddlew
|
|||||||
**Here are the list of available middlewares:**
|
**Here are the list of available middlewares:**
|
||||||
|
|
||||||
- `AsyncMiddlewareFunction`: can convert a lambda function (`ArMiddlewareCallback`) to a middleware
|
- `AsyncMiddlewareFunction`: can convert a lambda function (`ArMiddlewareCallback`) to a middleware
|
||||||
- `AuthenticationMiddleware`: to handle basic/digest authentication globally or per handler
|
- `AsyncAuthenticationMiddleware`: to handle basic/digest authentication globally or per handler
|
||||||
- `AuthorizationMiddleware`: to handle authorization globally or per handler
|
- `AsyncAuthorizationMiddleware`: to handle authorization globally or per handler
|
||||||
- `CorsMiddleware`: to handle CORS preflight request globally or per handler
|
- `AsyncCorsMiddleware`: to handle CORS preflight request globally or per handler
|
||||||
- `HeaderFilterMiddleware`: to filter out headers from the request
|
- `AsyncHeaderFilterMiddleware`: to filter out headers from the request
|
||||||
- `HeaderFreeMiddleware`: to only keep some headers from the request, and remove the others
|
- `AsyncHeaderFreeMiddleware`: to only keep some headers from the request, and remove the others
|
||||||
- `LoggerMiddleware`: to log requests globally or per handler with the same pattern as curl. Will also record request processing time
|
- `LoggerMiddleware`: to log requests globally or per handler with the same pattern as curl. Will also record request processing time
|
||||||
- `RateLimitMiddleware`: to limit the number of requests on a windows of time globally or per handler
|
- `AsyncRateLimitMiddleware`: to limit the number of requests on a windows of time globally or per handler
|
||||||
|
|
||||||
## How to use authentication with AuthenticationMiddleware
|
## How to use authentication with AsyncAuthenticationMiddleware
|
||||||
|
|
||||||
Do not use the `setUsername()` and `setPassword()` methods on the hanlders anymore.
|
Do not use the `setUsername()` and `setPassword()` methods on the hanlders anymore.
|
||||||
They are deprecated.
|
They are deprecated.
|
||||||
These methods were causing a copy of the username and password for each handler, which is not efficient.
|
These methods were causing a copy of the username and password for each handler, which is not efficient.
|
||||||
|
|
||||||
Now, you can use the `AuthenticationMiddleware` to handle authentication globally or per handler.
|
Now, you can use the `AsyncAuthenticationMiddleware` to handle authentication globally or per handler.
|
||||||
|
|
||||||
```c++
|
```c++
|
||||||
AuthenticationMiddleware authMiddleware;
|
AsyncAuthenticationMiddleware authMiddleware;
|
||||||
|
|
||||||
// [...]
|
// [...]
|
||||||
|
|
||||||
@ -292,14 +327,14 @@ myHandler.addMiddleware(&authMiddleware); // add authentication to a specific ha
|
|||||||
|
|
||||||
## Migration to Middleware to improve performance and memory usage
|
## Migration to Middleware to improve performance and memory usage
|
||||||
|
|
||||||
- `AsyncEventSource.authorizeConnect(...)` => do not use this method anymore: add a common `AuthorizationMiddleware` to the handler or server, and make sure to add it AFTER the `AuthenticationMiddleware` if you use authentication.
|
- `AsyncEventSource.authorizeConnect(...)` => do not use this method anymore: add a common `AsyncAuthorizationMiddleware` to the handler or server, and make sure to add it AFTER the `AsyncAuthenticationMiddleware` if you use authentication.
|
||||||
- `AsyncWebHandler.setAuthentication(...)` => do not use this method anymore: add a common `AuthenticationMiddleware` to the handler or server
|
- `AsyncWebHandler.setAuthentication(...)` => do not use this method anymore: add a common `AsyncAuthenticationMiddleware` to the handler or server
|
||||||
- `ArUploadHandlerFunction` and `ArBodyHandlerFunction` => these callbacks receiving body data and upload and not calling anymore the authentication code for performance reasons.
|
- `ArUploadHandlerFunction` and `ArBodyHandlerFunction` => these callbacks receiving body data and upload and not calling anymore the authentication code for performance reasons.
|
||||||
These callbacks can be called multiple times during request parsing, so this is up to the user to now call the `AuthenticationMiddleware.allowed(request)` if needed and ideally when the method is called for the first time.
|
These callbacks can be called multiple times during request parsing, so this is up to the user to now call the `AsyncAuthenticationMiddleware.allowed(request)` if needed and ideally when the method is called for the first time.
|
||||||
These callbacks are also not triggering the whole middleware chain since they are not part of the request processing workflow (they are not the final handler).
|
These callbacks are also not triggering the whole middleware chain since they are not part of the request processing workflow (they are not the final handler).
|
||||||
|
|
||||||
## Original Documentation
|
## Original Documentation
|
||||||
|
<!-- no toc -->
|
||||||
- [Why should you care](#why-should-you-care)
|
- [Why should you care](#why-should-you-care)
|
||||||
- [Important things to remember](#important-things-to-remember)
|
- [Important things to remember](#important-things-to-remember)
|
||||||
- [Principles of operation](#principles-of-operation)
|
- [Principles of operation](#principles-of-operation)
|
||||||
|
@ -1,8 +0,0 @@
|
|||||||
# bundle exec jekyll serve --host=0.0.0.0
|
|
||||||
|
|
||||||
title: ESPAsyncWebServer
|
|
||||||
description: "Asynchronous HTTP and WebSocket Server Library for ESP32, ESP8266 and RP2040"
|
|
||||||
remote_theme: pages-themes/cayman@v0.2.0
|
|
||||||
plugins:
|
|
||||||
- jekyll-remote-theme
|
|
||||||
|
|
1966
docs/index.md
1966
docs/index.md
File diff suppressed because it is too large
Load Diff
BIN
docs/logo.png
Normal file
BIN
docs/logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 479 KiB |
BIN
docs/logo.webp
Normal file
BIN
docs/logo.webp
Normal file
Binary file not shown.
After Width: | Height: | Size: 131 KiB |
@ -1,4 +1,4 @@
|
|||||||
// Reproduced issue https://github.com/mathieucarbou/ESPAsyncWebServer/issues/26
|
// Reproduced issue https://github.com/ESP32Async/ESPAsyncWebServer/issues/26
|
||||||
|
|
||||||
#include <DNSServer.h>
|
#include <DNSServer.h>
|
||||||
#ifdef ESP32
|
#ifdef ESP32
|
||||||
|
@ -110,27 +110,27 @@ AsyncWebSocket ws("/ws");
|
|||||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
// log incoming requests
|
// log incoming requests
|
||||||
LoggingMiddleware requestLogger;
|
AsyncLoggingMiddleware requestLogger;
|
||||||
|
|
||||||
// CORS
|
// CORS
|
||||||
CorsMiddleware cors;
|
AsyncCorsMiddleware cors;
|
||||||
|
|
||||||
// maximum 5 requests per 10 seconds
|
// maximum 5 requests per 10 seconds
|
||||||
RateLimitMiddleware rateLimit;
|
AsyncRateLimitMiddleware rateLimit;
|
||||||
|
|
||||||
// filter out specific headers from the incoming request
|
// filter out specific headers from the incoming request
|
||||||
HeaderFilterMiddleware headerFilter;
|
AsyncHeaderFilterMiddleware headerFilter;
|
||||||
|
|
||||||
// remove all headers from the incoming request except the ones provided in the constructor
|
// remove all headers from the incoming request except the ones provided in the constructor
|
||||||
HeaderFreeMiddleware headerFree;
|
AsyncHeaderFreeMiddleware headerFree;
|
||||||
|
|
||||||
// basicAuth
|
// basicAuth
|
||||||
AuthenticationMiddleware basicAuth;
|
AsyncAuthenticationMiddleware basicAuth;
|
||||||
AuthenticationMiddleware basicAuthHash;
|
AsyncAuthenticationMiddleware basicAuthHash;
|
||||||
|
|
||||||
// simple digest authentication
|
// simple digest authentication
|
||||||
AuthenticationMiddleware digestAuth;
|
AsyncAuthenticationMiddleware digestAuth;
|
||||||
AuthenticationMiddleware digestAuthHash;
|
AsyncAuthenticationMiddleware digestAuthHash;
|
||||||
|
|
||||||
// complex authentication which adds request attributes for the next middlewares and handler
|
// complex authentication which adds request attributes for the next middlewares and handler
|
||||||
AsyncMiddlewareFunction complexAuth([](AsyncWebServerRequest* request, ArMiddlewareNext next) {
|
AsyncMiddlewareFunction complexAuth([](AsyncWebServerRequest* request, ArMiddlewareNext next) {
|
||||||
@ -145,7 +145,7 @@ AsyncMiddlewareFunction complexAuth([](AsyncWebServerRequest* request, ArMiddlew
|
|||||||
request->getResponse()->addHeader("X-Rate-Limit", "200");
|
request->getResponse()->addHeader("X-Rate-Limit", "200");
|
||||||
});
|
});
|
||||||
|
|
||||||
AuthorizationMiddleware authz([](AsyncWebServerRequest* request) { return request->getAttribute("role") == "staff"; });
|
AsyncAuthorizationMiddleware authz([](AsyncWebServerRequest* request) { return request->getAttribute("role") == "staff"; });
|
||||||
|
|
||||||
int wsClients = 0;
|
int wsClients = 0;
|
||||||
|
|
||||||
|
@ -1,22 +1,18 @@
|
|||||||
{
|
{
|
||||||
"name": "ESPAsyncWebServer",
|
"name": "ESPAsyncWebServer",
|
||||||
"version": "3.4.5",
|
"version": "3.6.0",
|
||||||
"description": "Asynchronous HTTP and WebSocket Server Library for ESP32, ESP8266 and RP2040. Supports: WebSocket, SSE, Authentication, Arduino Json 7, File Upload, Static File serving, URL Rewrite, URL Redirect, etc.",
|
"description": "Asynchronous HTTP and WebSocket Server Library for ESP32, ESP8266 and RP2040. Supports: WebSocket, SSE, Authentication, Arduino Json 7, File Upload, Static File serving, URL Rewrite, URL Redirect, etc.",
|
||||||
"keywords": "http,async,websocket,webserver",
|
"keywords": "http,async,websocket,webserver",
|
||||||
"homepage": "https://github.com/mathieucarbou/ESPAsyncWebServer",
|
"homepage": "https://github.com/ESP32Async/ESPAsyncWebServer",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/mathieucarbou/ESPAsyncWebServer.git"
|
"url": "https://github.com/ESP32Async/ESPAsyncWebServer.git"
|
||||||
|
},
|
||||||
|
"authors":
|
||||||
|
{
|
||||||
|
"name": "ESP32Async",
|
||||||
|
"maintainer": true
|
||||||
},
|
},
|
||||||
"authors": [
|
|
||||||
{
|
|
||||||
"name": "Hristo Gochkov"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Mathieu Carbou",
|
|
||||||
"maintainer": true
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"license": "LGPL-3.0",
|
"license": "LGPL-3.0",
|
||||||
"frameworks": "arduino",
|
"frameworks": "arduino",
|
||||||
"platforms": [
|
"platforms": [
|
||||||
@ -26,14 +22,14 @@
|
|||||||
],
|
],
|
||||||
"dependencies": [
|
"dependencies": [
|
||||||
{
|
{
|
||||||
"owner": "mathieucarbou",
|
"owner": "ESP32Async",
|
||||||
"name": "AsyncTCP",
|
"name": "AsyncTCP",
|
||||||
"version": "^3.3.1",
|
"version": "^3.3.2",
|
||||||
"platforms": "espressif32"
|
"platforms": "espressif32"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"owner": "esphome",
|
"owner": "ESP32Async",
|
||||||
"name": "ESPAsyncTCP-esphome",
|
"name": "ESPAsyncTCP",
|
||||||
"version": "^2.0.0",
|
"version": "^2.0.0",
|
||||||
"platforms": "espressif8266"
|
"platforms": "espressif8266"
|
||||||
},
|
},
|
||||||
@ -57,8 +53,5 @@
|
|||||||
"LICENSE",
|
"LICENSE",
|
||||||
"README.md"
|
"README.md"
|
||||||
]
|
]
|
||||||
},
|
|
||||||
"build": {
|
|
||||||
"libCompatMode": "strict"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,11 +1,11 @@
|
|||||||
name=ESP Async WebServer
|
name=ESP Async WebServer
|
||||||
includes=ESPAsyncWebServer.h
|
includes=ESPAsyncWebServer.h
|
||||||
version=3.4.5
|
version=3.6.0
|
||||||
author=Me-No-Dev
|
author=ESP32Async
|
||||||
maintainer=Mathieu Carbou <mathieu.carbou@gmail.com>
|
maintainer=ESP32Async
|
||||||
sentence=Asynchronous HTTP and WebSocket Server Library for ESP32, ESP8266 and RP2040
|
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/ESP32Async/ESPAsyncWebServer
|
||||||
architectures=*
|
architectures=*
|
||||||
license=LGPL-3.0
|
license=LGPL-3.0
|
@ -1,5 +1,5 @@
|
|||||||
[platformio]
|
[platformio]
|
||||||
default_envs = arduino-2, arduino-3, arduino-310, esp8266, raspberrypi
|
default_envs = arduino-2, arduino-3, arduino-311, esp8266, raspberrypi
|
||||||
lib_dir = .
|
lib_dir = .
|
||||||
; src_dir = examples/CaptivePortal
|
; src_dir = examples/CaptivePortal
|
||||||
src_dir = examples/SimpleServer
|
src_dir = examples/SimpleServer
|
||||||
@ -30,8 +30,8 @@ lib_ldf_mode = chain
|
|||||||
lib_deps =
|
lib_deps =
|
||||||
; bblanchon/ArduinoJson @ 5.13.4
|
; bblanchon/ArduinoJson @ 5.13.4
|
||||||
; bblanchon/ArduinoJson @ 6.21.5
|
; bblanchon/ArduinoJson @ 6.21.5
|
||||||
bblanchon/ArduinoJson @ 7.2.1
|
bblanchon/ArduinoJson @ 7.3.0
|
||||||
mathieucarbou/AsyncTCP @ 3.3.1
|
ESP32Async/AsyncTCP @ 3.3.2
|
||||||
board = esp32dev
|
board = esp32dev
|
||||||
board_build.partitions = partitions-4MB.csv
|
board_build.partitions = partitions-4MB.csv
|
||||||
board_build.filesystem = littlefs
|
board_build.filesystem = littlefs
|
||||||
@ -49,23 +49,23 @@ platform = https://github.com/pioarduino/platform-espressif32/releases/download/
|
|||||||
; board = esp32-s3-devkitc-1
|
; board = esp32-s3-devkitc-1
|
||||||
; board = esp32-c6-devkitc-1
|
; board = esp32-c6-devkitc-1
|
||||||
lib_deps =
|
lib_deps =
|
||||||
mathieucarbou/AsyncTCP @ 3.3.1
|
ESP32Async/AsyncTCP @ 3.3.2
|
||||||
|
|
||||||
[env:arduino-310]
|
[env:arduino-311]
|
||||||
platform = https://github.com/pioarduino/platform-espressif32/releases/download/53.03.10/platform-espressif32.zip
|
platform = https://github.com/pioarduino/platform-espressif32/releases/download/53.03.11/platform-espressif32.zip
|
||||||
; board = esp32-s3-devkitc-1
|
; board = esp32-s3-devkitc-1
|
||||||
; board = esp32-c6-devkitc-1
|
; board = esp32-c6-devkitc-1
|
||||||
; board = esp32-h2-devkitm-1
|
; board = esp32-h2-devkitm-1
|
||||||
|
|
||||||
[env:perf-test-AsyncTCP]
|
[env:perf-test-AsyncTCP]
|
||||||
platform = https://github.com/pioarduino/platform-espressif32/releases/download/53.03.10/platform-espressif32.zip
|
platform = https://github.com/pioarduino/platform-espressif32/releases/download/53.03.11/platform-espressif32.zip
|
||||||
build_flags = ${env.build_flags}
|
build_flags = ${env.build_flags}
|
||||||
-D PERF_TEST=1
|
-D PERF_TEST=1
|
||||||
|
|
||||||
[env:perf-test-AsyncTCPSock]
|
[env:perf-test-AsyncTCPSock]
|
||||||
platform = https://github.com/pioarduino/platform-espressif32/releases/download/53.03.10/platform-espressif32.zip
|
platform = https://github.com/pioarduino/platform-espressif32/releases/download/53.03.11/platform-espressif32.zip
|
||||||
lib_deps =
|
lib_deps =
|
||||||
https://github.com/mathieucarbou/AsyncTCPSock/archive/refs/tags/v1.0.3-dev.zip
|
https://github.com/ESP32Async/AsyncTCPSock/archive/refs/tags/v1.0.3-dev.zip
|
||||||
build_flags = ${env.build_flags}
|
build_flags = ${env.build_flags}
|
||||||
-D PERF_TEST=1
|
-D PERF_TEST=1
|
||||||
|
|
||||||
@ -74,14 +74,15 @@ platform = espressif8266
|
|||||||
; board = huzzah
|
; board = huzzah
|
||||||
board = d1_mini
|
board = d1_mini
|
||||||
lib_deps =
|
lib_deps =
|
||||||
bblanchon/ArduinoJson @ 7.2.1
|
bblanchon/ArduinoJson @ 7.3.0
|
||||||
esphome/ESPAsyncTCP-esphome @ 2.0.0
|
ESP32Async/ESPAsyncTCP @ 2.0.0
|
||||||
|
|
||||||
[env:raspberrypi]
|
[env:raspberrypi]
|
||||||
platform = https://github.com/maxgerhardt/platform-raspberrypi.git
|
platform = https://github.com/maxgerhardt/platform-raspberrypi.git
|
||||||
board = rpipicow
|
board = rpipicow
|
||||||
|
board_build.core = earlephilhower
|
||||||
lib_deps =
|
lib_deps =
|
||||||
bblanchon/ArduinoJson @ 7.2.1
|
bblanchon/ArduinoJson @ 7.3.0
|
||||||
khoih-prog/AsyncTCP_RP2040W @ 1.2.0
|
khoih-prog/AsyncTCP_RP2040W @ 1.2.0
|
||||||
lib_ignore =
|
lib_ignore =
|
||||||
lwIP_ESPHost
|
lwIP_ESPHost
|
||||||
@ -102,24 +103,25 @@ board = ${sysenv.PIO_BOARD}
|
|||||||
platform = https://github.com/pioarduino/platform-espressif32/releases/download/51.03.05/platform-espressif32.zip
|
platform = https://github.com/pioarduino/platform-espressif32/releases/download/51.03.05/platform-espressif32.zip
|
||||||
board = ${sysenv.PIO_BOARD}
|
board = ${sysenv.PIO_BOARD}
|
||||||
lib_deps =
|
lib_deps =
|
||||||
mathieucarbou/AsyncTCP @ 3.3.1
|
ESP32Async/AsyncTCP @ 3.3.2
|
||||||
|
|
||||||
[env:ci-arduino-310]
|
[env:ci-arduino-311]
|
||||||
platform = https://github.com/pioarduino/platform-espressif32/releases/download/53.03.10/platform-espressif32.zip
|
platform = https://github.com/pioarduino/platform-espressif32/releases/download/53.03.11/platform-espressif32.zip
|
||||||
board = ${sysenv.PIO_BOARD}
|
board = ${sysenv.PIO_BOARD}
|
||||||
|
|
||||||
[env:ci-esp8266]
|
[env:ci-esp8266]
|
||||||
platform = espressif8266
|
platform = espressif8266
|
||||||
board = ${sysenv.PIO_BOARD}
|
board = ${sysenv.PIO_BOARD}
|
||||||
lib_deps =
|
lib_deps =
|
||||||
bblanchon/ArduinoJson @ 7.2.1
|
bblanchon/ArduinoJson @ 7.3.0
|
||||||
esphome/ESPAsyncTCP-esphome @ 2.0.0
|
ESP32Async/ESPAsyncTCP @ 2.0.0
|
||||||
|
|
||||||
[env:ci-raspberrypi]
|
[env:ci-raspberrypi]
|
||||||
platform = https://github.com/maxgerhardt/platform-raspberrypi.git
|
platform = https://github.com/maxgerhardt/platform-raspberrypi.git
|
||||||
board = ${sysenv.PIO_BOARD}
|
board = ${sysenv.PIO_BOARD}
|
||||||
|
board_build.core = earlephilhower
|
||||||
lib_deps =
|
lib_deps =
|
||||||
bblanchon/ArduinoJson @ 7.2.1
|
bblanchon/ArduinoJson @ 7.3.0
|
||||||
khoih-prog/AsyncTCP_RP2040W @ 1.2.0
|
khoih-prog/AsyncTCP_RP2040W @ 1.2.0
|
||||||
lib_ignore =
|
lib_ignore =
|
||||||
lwIP_ESPHost
|
lwIP_ESPHost
|
||||||
|
@ -332,7 +332,7 @@ void AsyncEventSourceClient::set_max_inflight_bytes(size_t value) {
|
|||||||
/* AsyncEventSource */
|
/* AsyncEventSource */
|
||||||
|
|
||||||
void AsyncEventSource::authorizeConnect(ArAuthorizeConnectHandler cb) {
|
void AsyncEventSource::authorizeConnect(ArAuthorizeConnectHandler cb) {
|
||||||
AuthorizationMiddleware* m = new AuthorizationMiddleware(401, cb);
|
AsyncAuthorizationMiddleware* m = new AsyncAuthorizationMiddleware(401, cb);
|
||||||
m->_freeOnRemoval = true;
|
m->_freeOnRemoval = true;
|
||||||
addMiddleware(m);
|
addMiddleware(m);
|
||||||
}
|
}
|
||||||
|
@ -595,7 +595,7 @@ void AsyncWebSocketClient::_onData(void* pbuf, size_t plen) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// restore byte as _handleEvent may have added a null terminator i.e., data[len] = 0;
|
// restore byte as _handleEvent may have added a null terminator i.e., data[len] = 0;
|
||||||
if (data && datalen > 0)
|
if (datalen)
|
||||||
data[datalen] = datalast;
|
data[datalen] = datalast;
|
||||||
|
|
||||||
data += datalen;
|
data += datalen;
|
||||||
|
@ -48,11 +48,11 @@
|
|||||||
|
|
||||||
#include "literals.h"
|
#include "literals.h"
|
||||||
|
|
||||||
#define ASYNCWEBSERVER_VERSION "3.4.5"
|
#define ASYNCWEBSERVER_VERSION "3.6.0"
|
||||||
#define ASYNCWEBSERVER_VERSION_MAJOR 3
|
#define ASYNCWEBSERVER_VERSION_MAJOR 3
|
||||||
#define ASYNCWEBSERVER_VERSION_MINOR 4
|
#define ASYNCWEBSERVER_VERSION_MINOR 6
|
||||||
#define ASYNCWEBSERVER_VERSION_REVISION 5
|
#define ASYNCWEBSERVER_VERSION_REVISION 0
|
||||||
#define ASYNCWEBSERVER_FORK_mathieucarbou
|
#define ASYNCWEBSERVER_FORK_ESP32Async
|
||||||
|
|
||||||
#ifdef ASYNCWEBSERVER_REGEX
|
#ifdef ASYNCWEBSERVER_REGEX
|
||||||
#define ASYNCWEBSERVER_REGEX_ATTRIBUTE
|
#define ASYNCWEBSERVER_REGEX_ATTRIBUTE
|
||||||
@ -285,12 +285,12 @@ class AsyncWebServerRequest {
|
|||||||
void setHandler(AsyncWebHandler* handler) { _handler = handler; }
|
void setHandler(AsyncWebHandler* handler) { _handler = handler; }
|
||||||
|
|
||||||
#ifndef ESP8266
|
#ifndef ESP8266
|
||||||
[[deprecated("All headers are now collected. Use removeHeader(name) or HeaderFreeMiddleware if you really need to free some headers.")]]
|
[[deprecated("All headers are now collected. Use removeHeader(name) or AsyncHeaderFreeMiddleware if you really need to free some headers.")]]
|
||||||
#endif
|
#endif
|
||||||
void addInterestingHeader(__unused const char* name) {
|
void addInterestingHeader(__unused const char* name) {
|
||||||
}
|
}
|
||||||
#ifndef ESP8266
|
#ifndef ESP8266
|
||||||
[[deprecated("All headers are now collected. Use removeHeader(name) or HeaderFreeMiddleware if you really need to free some headers.")]]
|
[[deprecated("All headers are now collected. Use removeHeader(name) or AsyncHeaderFreeMiddleware if you really need to free some headers.")]]
|
||||||
#endif
|
#endif
|
||||||
void addInterestingHeader(__unused const String& name) {
|
void addInterestingHeader(__unused const String& name) {
|
||||||
}
|
}
|
||||||
@ -557,8 +557,8 @@ class AsyncMiddlewareChain {
|
|||||||
std::list<AsyncMiddleware*> _middlewares;
|
std::list<AsyncMiddleware*> _middlewares;
|
||||||
};
|
};
|
||||||
|
|
||||||
// AuthenticationMiddleware is a middleware that checks if the request is authenticated
|
// AsyncAuthenticationMiddleware is a middleware that checks if the request is authenticated
|
||||||
class AuthenticationMiddleware : public AsyncMiddleware {
|
class AsyncAuthenticationMiddleware : public AsyncMiddleware {
|
||||||
public:
|
public:
|
||||||
void setUsername(const char* username);
|
void setUsername(const char* username);
|
||||||
void setPassword(const char* password);
|
void setPassword(const char* password);
|
||||||
@ -601,11 +601,11 @@ class AuthenticationMiddleware : public AsyncMiddleware {
|
|||||||
};
|
};
|
||||||
|
|
||||||
using ArAuthorizeFunction = std::function<bool(AsyncWebServerRequest* request)>;
|
using ArAuthorizeFunction = std::function<bool(AsyncWebServerRequest* request)>;
|
||||||
// AuthorizationMiddleware is a middleware that checks if the request is authorized
|
// AsyncAuthorizationMiddleware is a middleware that checks if the request is authorized
|
||||||
class AuthorizationMiddleware : public AsyncMiddleware {
|
class AsyncAuthorizationMiddleware : public AsyncMiddleware {
|
||||||
public:
|
public:
|
||||||
AuthorizationMiddleware(ArAuthorizeFunction authorizeConnectHandler) : _code(403), _authz(authorizeConnectHandler) {}
|
AsyncAuthorizationMiddleware(ArAuthorizeFunction authorizeConnectHandler) : _code(403), _authz(authorizeConnectHandler) {}
|
||||||
AuthorizationMiddleware(int code, ArAuthorizeFunction authorizeConnectHandler) : _code(code), _authz(authorizeConnectHandler) {}
|
AsyncAuthorizationMiddleware(int code, ArAuthorizeFunction authorizeConnectHandler) : _code(code), _authz(authorizeConnectHandler) {}
|
||||||
|
|
||||||
void run(AsyncWebServerRequest* request, ArMiddlewareNext next) { return _authz && !_authz(request) ? request->send(_code) : next(); }
|
void run(AsyncWebServerRequest* request, ArMiddlewareNext next) { return _authz && !_authz(request) ? request->send(_code) : next(); }
|
||||||
|
|
||||||
@ -615,7 +615,7 @@ class AuthorizationMiddleware : public AsyncMiddleware {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// remove all headers from the incoming request except the ones provided in the constructor
|
// remove all headers from the incoming request except the ones provided in the constructor
|
||||||
class HeaderFreeMiddleware : public AsyncMiddleware {
|
class AsyncHeaderFreeMiddleware : public AsyncMiddleware {
|
||||||
public:
|
public:
|
||||||
void keep(const char* name) { _toKeep.push_back(name); }
|
void keep(const char* name) { _toKeep.push_back(name); }
|
||||||
void unKeep(const char* name) { _toKeep.erase(std::remove(_toKeep.begin(), _toKeep.end(), name), _toKeep.end()); }
|
void unKeep(const char* name) { _toKeep.erase(std::remove(_toKeep.begin(), _toKeep.end(), name), _toKeep.end()); }
|
||||||
@ -627,7 +627,7 @@ class HeaderFreeMiddleware : public AsyncMiddleware {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// filter out specific headers from the incoming request
|
// filter out specific headers from the incoming request
|
||||||
class HeaderFilterMiddleware : public AsyncMiddleware {
|
class AsyncHeaderFilterMiddleware : public AsyncMiddleware {
|
||||||
public:
|
public:
|
||||||
void filter(const char* name) { _toRemove.push_back(name); }
|
void filter(const char* name) { _toRemove.push_back(name); }
|
||||||
void unFilter(const char* name) { _toRemove.erase(std::remove(_toRemove.begin(), _toRemove.end(), name), _toRemove.end()); }
|
void unFilter(const char* name) { _toRemove.erase(std::remove(_toRemove.begin(), _toRemove.end(), name), _toRemove.end()); }
|
||||||
@ -639,7 +639,7 @@ class HeaderFilterMiddleware : public AsyncMiddleware {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// curl-like logging of incoming requests
|
// curl-like logging of incoming requests
|
||||||
class LoggingMiddleware : public AsyncMiddleware {
|
class AsyncLoggingMiddleware : public AsyncMiddleware {
|
||||||
public:
|
public:
|
||||||
void setOutput(Print& output) { _out = &output; }
|
void setOutput(Print& output) { _out = &output; }
|
||||||
void setEnabled(bool enabled) { _enabled = enabled; }
|
void setEnabled(bool enabled) { _enabled = enabled; }
|
||||||
@ -653,7 +653,7 @@ class LoggingMiddleware : public AsyncMiddleware {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// CORS Middleware
|
// CORS Middleware
|
||||||
class CorsMiddleware : public AsyncMiddleware {
|
class AsyncCorsMiddleware : public AsyncMiddleware {
|
||||||
public:
|
public:
|
||||||
void setOrigin(const char* origin) { _origin = origin; }
|
void setOrigin(const char* origin) { _origin = origin; }
|
||||||
void setMethods(const char* methods) { _methods = methods; }
|
void setMethods(const char* methods) { _methods = methods; }
|
||||||
@ -674,7 +674,7 @@ class CorsMiddleware : public AsyncMiddleware {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Rate limit Middleware
|
// Rate limit Middleware
|
||||||
class RateLimitMiddleware : public AsyncMiddleware {
|
class AsyncRateLimitMiddleware : public AsyncMiddleware {
|
||||||
public:
|
public:
|
||||||
void setMaxRequests(size_t maxRequests) { _maxRequests = maxRequests; }
|
void setMaxRequests(size_t maxRequests) { _maxRequests = maxRequests; }
|
||||||
void setWindowSize(uint32_t seconds) { _windowSizeMillis = seconds * 1000; }
|
void setWindowSize(uint32_t seconds) { _windowSizeMillis = seconds * 1000; }
|
||||||
@ -727,7 +727,7 @@ class AsyncWebRewrite {
|
|||||||
class AsyncWebHandler : public AsyncMiddlewareChain {
|
class AsyncWebHandler : public AsyncMiddlewareChain {
|
||||||
protected:
|
protected:
|
||||||
ArRequestFilterFunction _filter = nullptr;
|
ArRequestFilterFunction _filter = nullptr;
|
||||||
AuthenticationMiddleware* _authMiddleware = nullptr;
|
AsyncAuthenticationMiddleware* _authMiddleware = nullptr;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
AsyncWebHandler() {}
|
AsyncWebHandler() {}
|
||||||
|
@ -53,24 +53,24 @@ void AsyncMiddlewareChain::_runChain(AsyncWebServerRequest* request, ArMiddlewar
|
|||||||
return next();
|
return next();
|
||||||
}
|
}
|
||||||
|
|
||||||
void AuthenticationMiddleware::setUsername(const char* username) {
|
void AsyncAuthenticationMiddleware::setUsername(const char* username) {
|
||||||
_username = username;
|
_username = username;
|
||||||
_hasCreds = _username.length() && _credentials.length();
|
_hasCreds = _username.length() && _credentials.length();
|
||||||
}
|
}
|
||||||
|
|
||||||
void AuthenticationMiddleware::setPassword(const char* password) {
|
void AsyncAuthenticationMiddleware::setPassword(const char* password) {
|
||||||
_credentials = password;
|
_credentials = password;
|
||||||
_hash = false;
|
_hash = false;
|
||||||
_hasCreds = _username.length() && _credentials.length();
|
_hasCreds = _username.length() && _credentials.length();
|
||||||
}
|
}
|
||||||
|
|
||||||
void AuthenticationMiddleware::setPasswordHash(const char* hash) {
|
void AsyncAuthenticationMiddleware::setPasswordHash(const char* hash) {
|
||||||
_credentials = hash;
|
_credentials = hash;
|
||||||
_hash = _credentials.length();
|
_hash = _credentials.length();
|
||||||
_hasCreds = _username.length() && _credentials.length();
|
_hasCreds = _username.length() && _credentials.length();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AuthenticationMiddleware::generateHash() {
|
bool AsyncAuthenticationMiddleware::generateHash() {
|
||||||
// ensure we have all the necessary data
|
// ensure we have all the necessary data
|
||||||
if (!_hasCreds)
|
if (!_hasCreds)
|
||||||
return false;
|
return false;
|
||||||
@ -95,7 +95,7 @@ bool AuthenticationMiddleware::generateHash() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AuthenticationMiddleware::allowed(AsyncWebServerRequest* request) const {
|
bool AsyncAuthenticationMiddleware::allowed(AsyncWebServerRequest* request) const {
|
||||||
if (_authMethod == AsyncAuthType::AUTH_NONE)
|
if (_authMethod == AsyncAuthType::AUTH_NONE)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
@ -108,11 +108,11 @@ bool AuthenticationMiddleware::allowed(AsyncWebServerRequest* request) const {
|
|||||||
return request->authenticate(_username.c_str(), _credentials.c_str(), _realm.c_str(), _hash);
|
return request->authenticate(_username.c_str(), _credentials.c_str(), _realm.c_str(), _hash);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AuthenticationMiddleware::run(AsyncWebServerRequest* request, ArMiddlewareNext next) {
|
void AsyncAuthenticationMiddleware::run(AsyncWebServerRequest* request, ArMiddlewareNext next) {
|
||||||
return allowed(request) ? next() : request->requestAuthentication(_authMethod, _realm.c_str(), _authFailMsg.c_str());
|
return allowed(request) ? next() : request->requestAuthentication(_authMethod, _realm.c_str(), _authFailMsg.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
void HeaderFreeMiddleware::run(AsyncWebServerRequest* request, ArMiddlewareNext next) {
|
void AsyncHeaderFreeMiddleware::run(AsyncWebServerRequest* request, ArMiddlewareNext next) {
|
||||||
std::vector<const char*> reqHeaders;
|
std::vector<const char*> reqHeaders;
|
||||||
request->getHeaderNames(reqHeaders);
|
request->getHeaderNames(reqHeaders);
|
||||||
for (const char* h : reqHeaders) {
|
for (const char* h : reqHeaders) {
|
||||||
@ -130,13 +130,13 @@ void HeaderFreeMiddleware::run(AsyncWebServerRequest* request, ArMiddlewareNext
|
|||||||
next();
|
next();
|
||||||
}
|
}
|
||||||
|
|
||||||
void HeaderFilterMiddleware::run(AsyncWebServerRequest* request, ArMiddlewareNext next) {
|
void AsyncHeaderFilterMiddleware::run(AsyncWebServerRequest* request, ArMiddlewareNext next) {
|
||||||
for (auto it = _toRemove.begin(); it != _toRemove.end(); ++it)
|
for (auto it = _toRemove.begin(); it != _toRemove.end(); ++it)
|
||||||
request->removeHeader(*it);
|
request->removeHeader(*it);
|
||||||
next();
|
next();
|
||||||
}
|
}
|
||||||
|
|
||||||
void LoggingMiddleware::run(AsyncWebServerRequest* request, ArMiddlewareNext next) {
|
void AsyncLoggingMiddleware::run(AsyncWebServerRequest* request, ArMiddlewareNext next) {
|
||||||
if (!isEnabled()) {
|
if (!isEnabled()) {
|
||||||
next();
|
next();
|
||||||
return;
|
return;
|
||||||
@ -194,7 +194,7 @@ void LoggingMiddleware::run(AsyncWebServerRequest* request, ArMiddlewareNext nex
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CorsMiddleware::addCORSHeaders(AsyncWebServerResponse* response) {
|
void AsyncCorsMiddleware::addCORSHeaders(AsyncWebServerResponse* response) {
|
||||||
response->addHeader(asyncsrv::T_CORS_ACAO, _origin.c_str());
|
response->addHeader(asyncsrv::T_CORS_ACAO, _origin.c_str());
|
||||||
response->addHeader(asyncsrv::T_CORS_ACAM, _methods.c_str());
|
response->addHeader(asyncsrv::T_CORS_ACAM, _methods.c_str());
|
||||||
response->addHeader(asyncsrv::T_CORS_ACAH, _headers.c_str());
|
response->addHeader(asyncsrv::T_CORS_ACAH, _headers.c_str());
|
||||||
@ -202,7 +202,7 @@ void CorsMiddleware::addCORSHeaders(AsyncWebServerResponse* response) {
|
|||||||
response->addHeader(asyncsrv::T_CORS_ACMA, String(_maxAge).c_str());
|
response->addHeader(asyncsrv::T_CORS_ACMA, String(_maxAge).c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
void CorsMiddleware::run(AsyncWebServerRequest* request, ArMiddlewareNext next) {
|
void AsyncCorsMiddleware::run(AsyncWebServerRequest* request, ArMiddlewareNext next) {
|
||||||
// Origin header ? => CORS handling
|
// Origin header ? => CORS handling
|
||||||
if (request->hasHeader(asyncsrv::T_CORS_O)) {
|
if (request->hasHeader(asyncsrv::T_CORS_O)) {
|
||||||
// check if this is a preflight request => handle it and return
|
// check if this is a preflight request => handle it and return
|
||||||
@ -226,7 +226,7 @@ void CorsMiddleware::run(AsyncWebServerRequest* request, ArMiddlewareNext next)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RateLimitMiddleware::isRequestAllowed(uint32_t& retryAfterSeconds) {
|
bool AsyncRateLimitMiddleware::isRequestAllowed(uint32_t& retryAfterSeconds) {
|
||||||
uint32_t now = millis();
|
uint32_t now = millis();
|
||||||
|
|
||||||
while (!_requestTimes.empty() && _requestTimes.front() <= now - _windowSizeMillis)
|
while (!_requestTimes.empty() && _requestTimes.front() <= now - _windowSizeMillis)
|
||||||
@ -244,7 +244,7 @@ bool RateLimitMiddleware::isRequestAllowed(uint32_t& retryAfterSeconds) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void RateLimitMiddleware::run(AsyncWebServerRequest* request, ArMiddlewareNext next) {
|
void AsyncRateLimitMiddleware::run(AsyncWebServerRequest* request, ArMiddlewareNext next) {
|
||||||
uint32_t retryAfterSeconds;
|
uint32_t retryAfterSeconds;
|
||||||
if (isRequestAllowed(retryAfterSeconds)) {
|
if (isRequestAllowed(retryAfterSeconds)) {
|
||||||
next();
|
next();
|
||||||
|
@ -57,12 +57,19 @@ class AsyncStaticWebHandler : public AsyncWebHandler {
|
|||||||
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);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set the Last-Modified time for the object
|
||||||
|
*
|
||||||
|
* @param last_modified
|
||||||
|
* @return AsyncStaticWebHandler&
|
||||||
|
*/
|
||||||
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
|
|
||||||
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
|
// sets to current time. Make sure sntp is runing and time is updated
|
||||||
#endif
|
AsyncStaticWebHandler& setLastModified();
|
||||||
|
|
||||||
AsyncStaticWebHandler& setTemplateProcessor(AwsTemplateProcessor newCallback);
|
AsyncStaticWebHandler& setTemplateProcessor(AwsTemplateProcessor newCallback);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -29,7 +29,7 @@ AsyncWebHandler& AsyncWebHandler::setFilter(ArRequestFilterFunction fn) {
|
|||||||
}
|
}
|
||||||
AsyncWebHandler& AsyncWebHandler::setAuthentication(const char* username, const char* password, AsyncAuthType authMethod) {
|
AsyncWebHandler& AsyncWebHandler::setAuthentication(const char* username, const char* password, AsyncAuthType authMethod) {
|
||||||
if (!_authMiddleware) {
|
if (!_authMiddleware) {
|
||||||
_authMiddleware = new AuthenticationMiddleware();
|
_authMiddleware = new AsyncAuthenticationMiddleware();
|
||||||
_authMiddleware->_freeOnRemoval = true;
|
_authMiddleware->_freeOnRemoval = true;
|
||||||
addMiddleware(_authMiddleware);
|
addMiddleware(_authMiddleware);
|
||||||
}
|
}
|
||||||
@ -70,12 +70,12 @@ AsyncStaticWebHandler& AsyncStaticWebHandler::setIsDir(bool isDir) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
AsyncStaticWebHandler& AsyncStaticWebHandler::setDefaultFile(const char* filename) {
|
AsyncStaticWebHandler& AsyncStaticWebHandler::setDefaultFile(const char* filename) {
|
||||||
_default_file = String(filename);
|
_default_file = 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 = cache_control;
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -85,16 +85,20 @@ AsyncStaticWebHandler& AsyncStaticWebHandler::setLastModified(const char* last_m
|
|||||||
}
|
}
|
||||||
|
|
||||||
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");
|
char result[30];
|
||||||
|
#ifdef ESP8266
|
||||||
|
auto formatP = PSTR("%a, %d %b %Y %H:%M:%S GMT");
|
||||||
char format[strlen_P(formatP) + 1];
|
char format[strlen_P(formatP) + 1];
|
||||||
strcpy_P(format, formatP);
|
strcpy_P(format, formatP);
|
||||||
|
#else
|
||||||
|
static constexpr const char* format = "%a, %d %b %Y %H:%M:%S GMT";
|
||||||
|
#endif
|
||||||
|
|
||||||
char result[30];
|
|
||||||
strftime(result, sizeof(result), format, last_modified);
|
strftime(result, sizeof(result), format, last_modified);
|
||||||
return setLastModified((const char*)result);
|
_last_modified = result;
|
||||||
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
#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));
|
||||||
}
|
}
|
||||||
@ -105,7 +109,7 @@ AsyncStaticWebHandler& AsyncStaticWebHandler::setLastModified() {
|
|||||||
return *this;
|
return *this;
|
||||||
return setLastModified(last_modified);
|
return setLastModified(last_modified);
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
bool AsyncStaticWebHandler::canHandle(AsyncWebServerRequest* request) const {
|
bool AsyncStaticWebHandler::canHandle(AsyncWebServerRequest* request) const {
|
||||||
return request->isHTTP() && request->method() == HTTP_GET && request->url().startsWith(_uri) && _getFile(request);
|
return request->isHTTP() && request->method() == HTTP_GET && request->url().startsWith(_uri) && _getFile(request);
|
||||||
}
|
}
|
||||||
@ -194,50 +198,59 @@ uint8_t AsyncStaticWebHandler::_countBits(const uint8_t value) const {
|
|||||||
|
|
||||||
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((char*)request->_tempObject);
|
||||||
free(request->_tempObject);
|
free(request->_tempObject);
|
||||||
request->_tempObject = NULL;
|
request->_tempObject = NULL;
|
||||||
|
|
||||||
if (request->_tempFile == true) {
|
if (request->_tempFile != true){
|
||||||
|
request->send(404);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
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)
|
||||||
// set etag to lastmod timestamp if available, otherwise to size
|
// set etag to lastmod timestamp if available, otherwise to size
|
||||||
String etag;
|
String etag;
|
||||||
if (lw) {
|
if (lw) {
|
||||||
setLastModified(gmtime(&lw));
|
setLastModified(lw);
|
||||||
#if defined(TARGET_RP2040)
|
#if defined(TARGET_RP2040)
|
||||||
// time_t == long long int
|
// time_t == long long int
|
||||||
const size_t len = 1 + 8 * sizeof(time_t);
|
constexpr size_t len = 1 + 8 * sizeof(time_t);
|
||||||
char buf[len];
|
char buf[len];
|
||||||
char* ret = lltoa(lw, buf, len, 10);
|
char* ret = lltoa(lw ^ request->_tempFile.size(), buf, len, 10);
|
||||||
etag = ret ? String(ret) : String(request->_tempFile.size());
|
etag = ret ? String(ret) : String(request->_tempFile.size());
|
||||||
#else
|
#else
|
||||||
etag = String(lw);
|
etag = lw ^ request->_tempFile.size(); // etag combines file size and lastmod timestamp
|
||||||
#endif
|
#endif
|
||||||
} else {
|
} else {
|
||||||
etag = String(request->_tempFile.size());
|
etag = request->_tempFile.size();
|
||||||
}
|
}
|
||||||
if (_last_modified.length() && _last_modified == request->header(T_IMS)) {
|
|
||||||
|
bool not_modified = false;
|
||||||
|
|
||||||
|
// if-none-match has precedence over if-modified-since
|
||||||
|
if (request->hasHeader(T_INM))
|
||||||
|
not_modified = request->header(T_INM).equals(etag);
|
||||||
|
else if (_last_modified.length())
|
||||||
|
not_modified = request->header(T_IMS).equals(_last_modified);
|
||||||
|
|
||||||
|
AsyncWebServerResponse* response;
|
||||||
|
|
||||||
|
if (not_modified){
|
||||||
request->_tempFile.close();
|
request->_tempFile.close();
|
||||||
request->send(304); // Not modified
|
response = new AsyncBasicResponse(304); // Not modified
|
||||||
} else if (_cache_control.length() && request->hasHeader(T_INM) && request->header(T_INM).equals(etag)) {
|
|
||||||
request->_tempFile.close();
|
|
||||||
AsyncWebServerResponse* response = new AsyncBasicResponse(304); // Not modified
|
|
||||||
response->addHeader(T_Cache_Control, _cache_control.c_str());
|
|
||||||
response->addHeader(T_ETag, etag.c_str());
|
|
||||||
request->send(response);
|
|
||||||
} else {
|
} else {
|
||||||
AsyncWebServerResponse* response = new AsyncFileResponse(request->_tempFile, filename, String(), false, _callback);
|
response = new AsyncFileResponse(request->_tempFile, filename, emptyString, false, _callback);
|
||||||
if (_last_modified.length())
|
|
||||||
response->addHeader(T_Last_Modified, _last_modified.c_str());
|
|
||||||
if (_cache_control.length()) {
|
|
||||||
response->addHeader(T_Cache_Control, _cache_control.c_str());
|
|
||||||
response->addHeader(T_ETag, etag.c_str());
|
|
||||||
}
|
|
||||||
request->send(response);
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
request->send(404);
|
response->addHeader(T_ETag, etag.c_str());
|
||||||
}
|
|
||||||
|
if (_last_modified.length())
|
||||||
|
response->addHeader(T_Last_Modified, _last_modified.c_str());
|
||||||
|
if (_cache_control.length())
|
||||||
|
response->addHeader(T_Cache_Control, _cache_control.c_str());
|
||||||
|
|
||||||
|
request->send(response);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
AsyncStaticWebHandler& AsyncStaticWebHandler::setTemplateProcessor(AwsTemplateProcessor newCallback) {
|
AsyncStaticWebHandler& AsyncStaticWebHandler::setTemplateProcessor(AwsTemplateProcessor newCallback) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user