Compare commits
5 Commits
master
...
feature/es
Author | SHA1 | Date | |
---|---|---|---|
b25b62ceb9 | |||
35eafc624a | |||
f05e235243 | |||
fd04d2ff66 | |||
165db0f093 |
@ -1,129 +0,0 @@
|
|||||||
|
|
||||||
# Contributor Covenant Code of Conduct
|
|
||||||
|
|
||||||
## Our Pledge
|
|
||||||
|
|
||||||
We as members, contributors, and leaders pledge to make participation in our
|
|
||||||
community a harassment-free experience for everyone, regardless of age, body
|
|
||||||
size, visible or invisible disability, ethnicity, sex characteristics, gender
|
|
||||||
identity and expression, level of experience, education, socio-economic status,
|
|
||||||
nationality, personal appearance, race, religion, or sexual identity
|
|
||||||
and orientation.
|
|
||||||
|
|
||||||
We pledge to act and interact in ways that contribute to an open, welcoming,
|
|
||||||
diverse, inclusive, and healthy community.
|
|
||||||
|
|
||||||
## Our Standards
|
|
||||||
|
|
||||||
Examples of behavior that contributes to a positive environment for our
|
|
||||||
community include:
|
|
||||||
|
|
||||||
* Demonstrating empathy and kindness toward other people
|
|
||||||
* Being respectful of differing opinions, viewpoints, and experiences
|
|
||||||
* Giving and gracefully accepting constructive feedback
|
|
||||||
* Accepting responsibility and apologizing to those affected by our mistakes,
|
|
||||||
and learning from the experience
|
|
||||||
* Focusing on what is best not just for us as individuals, but for the
|
|
||||||
overall community
|
|
||||||
|
|
||||||
Examples of unacceptable behavior include:
|
|
||||||
|
|
||||||
* The use of sexualized language or imagery, and sexual attention or
|
|
||||||
advances of any kind
|
|
||||||
* Trolling, insulting or derogatory comments, and personal or political attacks
|
|
||||||
* Public or private harassment
|
|
||||||
* Publishing others' private information, such as a physical or email
|
|
||||||
address, without their explicit permission
|
|
||||||
* Other conduct which could reasonably be considered inappropriate in a
|
|
||||||
professional setting
|
|
||||||
|
|
||||||
## Enforcement Responsibilities
|
|
||||||
|
|
||||||
Community leaders are responsible for clarifying and enforcing our standards of
|
|
||||||
acceptable behavior and will take appropriate and fair corrective action in
|
|
||||||
response to any behavior that they deem inappropriate, threatening, offensive,
|
|
||||||
or harmful.
|
|
||||||
|
|
||||||
Community leaders have the right and responsibility to remove, edit, or reject
|
|
||||||
comments, commits, code, wiki edits, issues, and other contributions that are
|
|
||||||
not aligned to this Code of Conduct, and will communicate reasons for moderation
|
|
||||||
decisions when appropriate.
|
|
||||||
|
|
||||||
## Scope
|
|
||||||
|
|
||||||
This Code of Conduct applies within all community spaces, and also applies when
|
|
||||||
an individual is officially representing the community in public spaces.
|
|
||||||
Examples of representing our community include using an official e-mail address,
|
|
||||||
posting via an official social media account, or acting as an appointed
|
|
||||||
representative at an online or offline event.
|
|
||||||
|
|
||||||
## Enforcement
|
|
||||||
|
|
||||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
|
||||||
reported to the community leaders responsible for enforcement at
|
|
||||||
https://sidweb.nl/cms3/en/contact.
|
|
||||||
All complaints will be reviewed and investigated promptly and fairly.
|
|
||||||
|
|
||||||
All community leaders are obligated to respect the privacy and security of the
|
|
||||||
reporter of any incident.
|
|
||||||
|
|
||||||
## Enforcement Guidelines
|
|
||||||
|
|
||||||
Community leaders will follow these Community Impact Guidelines in determining
|
|
||||||
the consequences for any action they deem in violation of this Code of Conduct:
|
|
||||||
|
|
||||||
### 1. Correction
|
|
||||||
|
|
||||||
**Community Impact**: Use of inappropriate language or other behavior deemed
|
|
||||||
unprofessional or unwelcome in the community.
|
|
||||||
|
|
||||||
**Consequence**: A private, written warning from community leaders, providing
|
|
||||||
clarity around the nature of the violation and an explanation of why the
|
|
||||||
behavior was inappropriate. A public apology may be requested.
|
|
||||||
|
|
||||||
### 2. Warning
|
|
||||||
|
|
||||||
**Community Impact**: A violation through a single incident or series
|
|
||||||
of actions.
|
|
||||||
|
|
||||||
**Consequence**: A warning with consequences for continued behavior. No
|
|
||||||
interaction with the people involved, including unsolicited interaction with
|
|
||||||
those enforcing the Code of Conduct, for a specified period of time. This
|
|
||||||
includes avoiding interactions in community spaces as well as external channels
|
|
||||||
like social media. Violating these terms may lead to a temporary or
|
|
||||||
permanent ban.
|
|
||||||
|
|
||||||
### 3. Temporary Ban
|
|
||||||
|
|
||||||
**Community Impact**: A serious violation of community standards, including
|
|
||||||
sustained inappropriate behavior.
|
|
||||||
|
|
||||||
**Consequence**: A temporary ban from any sort of interaction or public
|
|
||||||
communication with the community for a specified period of time. No public or
|
|
||||||
private interaction with the people involved, including unsolicited interaction
|
|
||||||
with those enforcing the Code of Conduct, is allowed during this period.
|
|
||||||
Violating these terms may lead to a permanent ban.
|
|
||||||
|
|
||||||
### 4. Permanent Ban
|
|
||||||
|
|
||||||
**Community Impact**: Demonstrating a pattern of violation of community
|
|
||||||
standards, including sustained inappropriate behavior, harassment of an
|
|
||||||
individual, or aggression toward or disparagement of classes of individuals.
|
|
||||||
|
|
||||||
**Consequence**: A permanent ban from any sort of public interaction within
|
|
||||||
the community.
|
|
||||||
|
|
||||||
## Attribution
|
|
||||||
|
|
||||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
|
||||||
version 2.0, available at
|
|
||||||
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
|
|
||||||
|
|
||||||
Community Impact Guidelines were inspired by [Mozilla's code of conduct
|
|
||||||
enforcement ladder](https://github.com/mozilla/diversity).
|
|
||||||
|
|
||||||
[homepage]: https://www.contributor-covenant.org
|
|
||||||
|
|
||||||
For answers to common questions about this code of conduct, see the FAQ at
|
|
||||||
https://www.contributor-covenant.org/faq. Translations are available at
|
|
||||||
https://www.contributor-covenant.org/translations.
|
|
165
LICENSE
165
LICENSE
@ -1,165 +0,0 @@
|
|||||||
GNU LESSER GENERAL PUBLIC LICENSE
|
|
||||||
Version 3, 29 June 2007
|
|
||||||
|
|
||||||
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
|
||||||
Everyone is permitted to copy and distribute verbatim copies
|
|
||||||
of this license document, but changing it is not allowed.
|
|
||||||
|
|
||||||
|
|
||||||
This version of the GNU Lesser General Public License incorporates
|
|
||||||
the terms and conditions of version 3 of the GNU General Public
|
|
||||||
License, supplemented by the additional permissions listed below.
|
|
||||||
|
|
||||||
0. Additional Definitions.
|
|
||||||
|
|
||||||
As used herein, "this License" refers to version 3 of the GNU Lesser
|
|
||||||
General Public License, and the "GNU GPL" refers to version 3 of the GNU
|
|
||||||
General Public License.
|
|
||||||
|
|
||||||
"The Library" refers to a covered work governed by this License,
|
|
||||||
other than an Application or a Combined Work as defined below.
|
|
||||||
|
|
||||||
An "Application" is any work that makes use of an interface provided
|
|
||||||
by the Library, but which is not otherwise based on the Library.
|
|
||||||
Defining a subclass of a class defined by the Library is deemed a mode
|
|
||||||
of using an interface provided by the Library.
|
|
||||||
|
|
||||||
A "Combined Work" is a work produced by combining or linking an
|
|
||||||
Application with the Library. The particular version of the Library
|
|
||||||
with which the Combined Work was made is also called the "Linked
|
|
||||||
Version".
|
|
||||||
|
|
||||||
The "Minimal Corresponding Source" for a Combined Work means the
|
|
||||||
Corresponding Source for the Combined Work, excluding any source code
|
|
||||||
for portions of the Combined Work that, considered in isolation, are
|
|
||||||
based on the Application, and not on the Linked Version.
|
|
||||||
|
|
||||||
The "Corresponding Application Code" for a Combined Work means the
|
|
||||||
object code and/or source code for the Application, including any data
|
|
||||||
and utility programs needed for reproducing the Combined Work from the
|
|
||||||
Application, but excluding the System Libraries of the Combined Work.
|
|
||||||
|
|
||||||
1. Exception to Section 3 of the GNU GPL.
|
|
||||||
|
|
||||||
You may convey a covered work under sections 3 and 4 of this License
|
|
||||||
without being bound by section 3 of the GNU GPL.
|
|
||||||
|
|
||||||
2. Conveying Modified Versions.
|
|
||||||
|
|
||||||
If you modify a copy of the Library, and, in your modifications, a
|
|
||||||
facility refers to a function or data to be supplied by an Application
|
|
||||||
that uses the facility (other than as an argument passed when the
|
|
||||||
facility is invoked), then you may convey a copy of the modified
|
|
||||||
version:
|
|
||||||
|
|
||||||
a) under this License, provided that you make a good faith effort to
|
|
||||||
ensure that, in the event an Application does not supply the
|
|
||||||
function or data, the facility still operates, and performs
|
|
||||||
whatever part of its purpose remains meaningful, or
|
|
||||||
|
|
||||||
b) under the GNU GPL, with none of the additional permissions of
|
|
||||||
this License applicable to that copy.
|
|
||||||
|
|
||||||
3. Object Code Incorporating Material from Library Header Files.
|
|
||||||
|
|
||||||
The object code form of an Application may incorporate material from
|
|
||||||
a header file that is part of the Library. You may convey such object
|
|
||||||
code under terms of your choice, provided that, if the incorporated
|
|
||||||
material is not limited to numerical parameters, data structure
|
|
||||||
layouts and accessors, or small macros, inline functions and templates
|
|
||||||
(ten or fewer lines in length), you do both of the following:
|
|
||||||
|
|
||||||
a) Give prominent notice with each copy of the object code that the
|
|
||||||
Library is used in it and that the Library and its use are
|
|
||||||
covered by this License.
|
|
||||||
|
|
||||||
b) Accompany the object code with a copy of the GNU GPL and this license
|
|
||||||
document.
|
|
||||||
|
|
||||||
4. Combined Works.
|
|
||||||
|
|
||||||
You may convey a Combined Work under terms of your choice that,
|
|
||||||
taken together, effectively do not restrict modification of the
|
|
||||||
portions of the Library contained in the Combined Work and reverse
|
|
||||||
engineering for debugging such modifications, if you also do each of
|
|
||||||
the following:
|
|
||||||
|
|
||||||
a) Give prominent notice with each copy of the Combined Work that
|
|
||||||
the Library is used in it and that the Library and its use are
|
|
||||||
covered by this License.
|
|
||||||
|
|
||||||
b) Accompany the Combined Work with a copy of the GNU GPL and this license
|
|
||||||
document.
|
|
||||||
|
|
||||||
c) For a Combined Work that displays copyright notices during
|
|
||||||
execution, include the copyright notice for the Library among
|
|
||||||
these notices, as well as a reference directing the user to the
|
|
||||||
copies of the GNU GPL and this license document.
|
|
||||||
|
|
||||||
d) Do one of the following:
|
|
||||||
|
|
||||||
0) Convey the Minimal Corresponding Source under the terms of this
|
|
||||||
License, and the Corresponding Application Code in a form
|
|
||||||
suitable for, and under terms that permit, the user to
|
|
||||||
recombine or relink the Application with a modified version of
|
|
||||||
the Linked Version to produce a modified Combined Work, in the
|
|
||||||
manner specified by section 6 of the GNU GPL for conveying
|
|
||||||
Corresponding Source.
|
|
||||||
|
|
||||||
1) Use a suitable shared library mechanism for linking with the
|
|
||||||
Library. A suitable mechanism is one that (a) uses at run time
|
|
||||||
a copy of the Library already present on the user's computer
|
|
||||||
system, and (b) will operate properly with a modified version
|
|
||||||
of the Library that is interface-compatible with the Linked
|
|
||||||
Version.
|
|
||||||
|
|
||||||
e) Provide Installation Information, but only if you would otherwise
|
|
||||||
be required to provide such information under section 6 of the
|
|
||||||
GNU GPL, and only to the extent that such information is
|
|
||||||
necessary to install and execute a modified version of the
|
|
||||||
Combined Work produced by recombining or relinking the
|
|
||||||
Application with a modified version of the Linked Version. (If
|
|
||||||
you use option 4d0, the Installation Information must accompany
|
|
||||||
the Minimal Corresponding Source and Corresponding Application
|
|
||||||
Code. If you use option 4d1, you must provide the Installation
|
|
||||||
Information in the manner specified by section 6 of the GNU GPL
|
|
||||||
for conveying Corresponding Source.)
|
|
||||||
|
|
||||||
5. Combined Libraries.
|
|
||||||
|
|
||||||
You may place library facilities that are a work based on the
|
|
||||||
Library side by side in a single library together with other library
|
|
||||||
facilities that are not Applications and are not covered by this
|
|
||||||
License, and convey such a combined library under terms of your
|
|
||||||
choice, if you do both of the following:
|
|
||||||
|
|
||||||
a) Accompany the combined library with a copy of the same work based
|
|
||||||
on the Library, uncombined with any other library facilities,
|
|
||||||
conveyed under the terms of this License.
|
|
||||||
|
|
||||||
b) Give prominent notice with the combined library that part of it
|
|
||||||
is a work based on the Library, and explaining where to find the
|
|
||||||
accompanying uncombined form of the same work.
|
|
||||||
|
|
||||||
6. Revised Versions of the GNU Lesser General Public License.
|
|
||||||
|
|
||||||
The Free Software Foundation may publish revised and/or new versions
|
|
||||||
of the GNU Lesser General Public License from time to time. Such new
|
|
||||||
versions will be similar in spirit to the present version, but may
|
|
||||||
differ in detail to address new problems or concerns.
|
|
||||||
|
|
||||||
Each version is given a distinguishing version number. If the
|
|
||||||
Library as you received it specifies that a certain numbered version
|
|
||||||
of the GNU Lesser General Public License "or any later version"
|
|
||||||
applies to it, you have the option of following the terms and
|
|
||||||
conditions either of that published version or of any later version
|
|
||||||
published by the Free Software Foundation. If the Library as you
|
|
||||||
received it does not specify a version number of the GNU Lesser
|
|
||||||
General Public License, you may choose any version of the GNU Lesser
|
|
||||||
General Public License ever published by the Free Software Foundation.
|
|
||||||
|
|
||||||
If the Library as you received it specifies that a proxy can decide
|
|
||||||
whether future versions of the GNU Lesser General Public License shall
|
|
||||||
apply, that proxy's public statement of acceptance of any version is
|
|
||||||
permanent authorization for you to choose that version for the
|
|
||||||
Library.
|
|
1
_config.yml
Normal file
1
_config.yml
Normal file
@ -0,0 +1 @@
|
|||||||
|
theme: jekyll-theme-cayman
|
3
component.mk
Normal file
3
component.mk
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
COMPONENT_ADD_INCLUDEDIRS := src
|
||||||
|
COMPONENT_SRCDIRS := src
|
||||||
|
CXXFLAGS += -fno-rtti
|
@ -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
|
|
||||||
|
|
1885
docs/index.md
1885
docs/index.md
File diff suppressed because it is too large
Load Diff
@ -1,13 +1,10 @@
|
|||||||
#include <DNSServer.h>
|
#include <DNSServer.h>
|
||||||
#ifdef ESP32
|
#ifdef ESP32
|
||||||
#include <AsyncTCP.h>
|
#include <WiFi.h>
|
||||||
#include <WiFi.h>
|
#include <AsyncTCP.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"
|
||||||
|
|
||||||
@ -15,47 +12,36 @@ 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(AsyncWebServerRequest *request){
|
||||||
|
//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());
|
||||||
#ifndef CONFIG_IDF_TARGET_ESP32H2
|
|
||||||
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());
|
||||||
#endif
|
|
||||||
response->print("</body></html>");
|
response->print("</body></html>");
|
||||||
request->send(response);
|
request->send(response);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
void setup() {
|
|
||||||
Serial.begin(115200);
|
|
||||||
Serial.println();
|
|
||||||
Serial.println("Configuring access point...");
|
|
||||||
|
|
||||||
#ifndef CONFIG_IDF_TARGET_ESP32H2
|
|
||||||
if (!WiFi.softAP("esp-captive")) {
|
|
||||||
Serial.println("Soft AP creation failed.");
|
|
||||||
while (1)
|
|
||||||
;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
void setup(){
|
||||||
|
//your other setup stuff...
|
||||||
|
WiFi.softAP("esp-captive");
|
||||||
dnsServer.start(53, "*", WiFi.softAPIP());
|
dnsServer.start(53, "*", WiFi.softAPIP());
|
||||||
#endif
|
server.addHandler(new CaptiveRequestHandler()).setFilter(ON_AP_FILTER);//only when requested from AP
|
||||||
|
//more handlers...
|
||||||
server.addHandler(new CaptiveRequestHandler()).setFilter(ON_AP_FILTER); // only when requested from AP
|
|
||||||
// more handlers...
|
|
||||||
server.begin();
|
server.begin();
|
||||||
}
|
}
|
||||||
|
|
||||||
void loop() {
|
void loop(){
|
||||||
dnsServer.processNextRequest();
|
dnsServer.processNextRequest();
|
||||||
}
|
}
|
||||||
|
@ -1,37 +0,0 @@
|
|||||||
#include "mbedtls/md5.h"
|
|
||||||
#include <Arduino.h>
|
|
||||||
#include <MD5Builder.h>
|
|
||||||
|
|
||||||
void setup() {
|
|
||||||
Serial.begin(115200);
|
|
||||||
delay(2000);
|
|
||||||
|
|
||||||
const char* data = "Hello World";
|
|
||||||
|
|
||||||
{
|
|
||||||
uint8_t md5[16];
|
|
||||||
mbedtls_md5_context _ctx;
|
|
||||||
mbedtls_md5_init(&_ctx);
|
|
||||||
mbedtls_md5_starts(&_ctx);
|
|
||||||
mbedtls_md5_update(&_ctx, (const unsigned char*)data, strlen(data));
|
|
||||||
mbedtls_md5_finish(&_ctx, md5);
|
|
||||||
char output[33];
|
|
||||||
for (int i = 0; i < 16; i++) {
|
|
||||||
sprintf_P(output + (i * 2), PSTR("%02x"), md5[i]);
|
|
||||||
}
|
|
||||||
Serial.println(String(output));
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
MD5Builder md5;
|
|
||||||
md5.begin();
|
|
||||||
md5.add(data, strlen(data);
|
|
||||||
md5.calculate();
|
|
||||||
char output[33];
|
|
||||||
md5.getChars(output);
|
|
||||||
Serial.println(String(output));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void loop() {
|
|
||||||
}
|
|
@ -1,127 +0,0 @@
|
|||||||
// Reproduced issue https://github.com/mathieucarbou/ESPAsyncWebServer/issues/26
|
|
||||||
|
|
||||||
#include <DNSServer.h>
|
|
||||||
#ifdef ESP32
|
|
||||||
#include <AsyncTCP.h>
|
|
||||||
#include <WiFi.h>
|
|
||||||
#elif defined(ESP8266)
|
|
||||||
#include <ESP8266WiFi.h>
|
|
||||||
#include <ESPAsyncTCP.h>
|
|
||||||
#elif defined(TARGET_RP2040)
|
|
||||||
#include <WebServer.h>
|
|
||||||
#include <WiFi.h>
|
|
||||||
#endif
|
|
||||||
#include "ESPAsyncWebServer.h"
|
|
||||||
|
|
||||||
DNSServer dnsServer;
|
|
||||||
AsyncWebServer server(80);
|
|
||||||
|
|
||||||
class CaptiveRequestHandler : public AsyncWebHandler {
|
|
||||||
public:
|
|
||||||
CaptiveRequestHandler() {}
|
|
||||||
virtual ~CaptiveRequestHandler() {}
|
|
||||||
|
|
||||||
bool canHandle(__unused AsyncWebServerRequest* request) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void handleRequest(AsyncWebServerRequest* request) {
|
|
||||||
AsyncResponseStream* response = request->beginResponseStream("text/html");
|
|
||||||
response->print("<!DOCTYPE html><html><head><title>Captive Portal</title></head><body>");
|
|
||||||
response->print("<p>This is out captive portal front page.</p>");
|
|
||||||
response->printf("<p>You were trying to reach: http://%s%s</p>", request->host().c_str(), request->url().c_str());
|
|
||||||
#ifndef CONFIG_IDF_TARGET_ESP32H2
|
|
||||||
response->printf("<p>Try opening <a href='http://%s'>this link</a> instead</p>", WiFi.softAPIP().toString().c_str());
|
|
||||||
#endif
|
|
||||||
response->print("</body></html>");
|
|
||||||
request->send(response);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
bool hit1 = false;
|
|
||||||
bool hit2 = false;
|
|
||||||
|
|
||||||
void setup() {
|
|
||||||
Serial.begin(115200);
|
|
||||||
|
|
||||||
server
|
|
||||||
.on("/", HTTP_GET, [](AsyncWebServerRequest* request) {
|
|
||||||
Serial.println("Captive portal request...");
|
|
||||||
#ifndef CONFIG_IDF_TARGET_ESP32H2
|
|
||||||
Serial.println("WiFi.localIP(): " + WiFi.localIP().toString());
|
|
||||||
#endif
|
|
||||||
Serial.println("request->client()->localIP(): " + request->client()->localIP().toString());
|
|
||||||
#if ESP_IDF_VERSION_MAJOR >= 5
|
|
||||||
#ifndef CONFIG_IDF_TARGET_ESP32H2
|
|
||||||
Serial.println("WiFi.type(): " + String((int)WiFi.localIP().type()));
|
|
||||||
#endif
|
|
||||||
Serial.println("request->client()->type(): " + String((int)request->client()->localIP().type()));
|
|
||||||
#endif
|
|
||||||
#ifndef CONFIG_IDF_TARGET_ESP32H2
|
|
||||||
Serial.println(WiFi.localIP() == request->client()->localIP() ? "should be: ON_STA_FILTER" : "should be: ON_AP_FILTER");
|
|
||||||
Serial.println(WiFi.localIP() == request->client()->localIP());
|
|
||||||
Serial.println(WiFi.localIP().toString() == request->client()->localIP().toString());
|
|
||||||
#endif
|
|
||||||
request->send(200, "text/plain", "This is the captive portal");
|
|
||||||
hit1 = true;
|
|
||||||
})
|
|
||||||
.setFilter(ON_AP_FILTER);
|
|
||||||
|
|
||||||
server
|
|
||||||
.on("/", HTTP_GET, [](AsyncWebServerRequest* request) {
|
|
||||||
Serial.println("Website request...");
|
|
||||||
#ifndef CONFIG_IDF_TARGET_ESP32H2
|
|
||||||
Serial.println("WiFi.localIP(): " + WiFi.localIP().toString());
|
|
||||||
#endif
|
|
||||||
Serial.println("request->client()->localIP(): " + request->client()->localIP().toString());
|
|
||||||
#if ESP_IDF_VERSION_MAJOR >= 5
|
|
||||||
#ifndef CONFIG_IDF_TARGET_ESP32H2
|
|
||||||
Serial.println("WiFi.type(): " + String((int)WiFi.localIP().type()));
|
|
||||||
#endif
|
|
||||||
Serial.println("request->client()->type(): " + String((int)request->client()->localIP().type()));
|
|
||||||
#endif
|
|
||||||
#ifndef CONFIG_IDF_TARGET_ESP32H2
|
|
||||||
Serial.println(WiFi.localIP() == request->client()->localIP() ? "should be: ON_STA_FILTER" : "should be: ON_AP_FILTER");
|
|
||||||
Serial.println(WiFi.localIP() == request->client()->localIP());
|
|
||||||
Serial.println(WiFi.localIP().toString() == request->client()->localIP().toString());
|
|
||||||
#endif
|
|
||||||
request->send(200, "text/plain", "This is the website");
|
|
||||||
hit2 = true;
|
|
||||||
})
|
|
||||||
.setFilter(ON_STA_FILTER);
|
|
||||||
|
|
||||||
// assert(WiFi.softAP("esp-captive-portal"));
|
|
||||||
// dnsServer.start(53, "*", WiFi.softAPIP());
|
|
||||||
// server.begin();
|
|
||||||
// Serial.println("Captive portal started!");
|
|
||||||
|
|
||||||
// while (!hit1) {
|
|
||||||
// dnsServer.processNextRequest();
|
|
||||||
// yield();
|
|
||||||
// }
|
|
||||||
// delay(1000); // Wait for the client to process the response
|
|
||||||
|
|
||||||
// Serial.println("Captive portal opened, stopping it and connecting to WiFi...");
|
|
||||||
// dnsServer.stop();
|
|
||||||
// WiFi.softAPdisconnect();
|
|
||||||
|
|
||||||
#ifndef CONFIG_IDF_TARGET_ESP32H2
|
|
||||||
WiFi.persistent(false);
|
|
||||||
WiFi.begin("IoT");
|
|
||||||
while (WiFi.status() != WL_CONNECTED) {
|
|
||||||
delay(500);
|
|
||||||
}
|
|
||||||
Serial.println("Connected to WiFi with IP address: " + WiFi.localIP().toString());
|
|
||||||
#endif
|
|
||||||
|
|
||||||
server.begin();
|
|
||||||
|
|
||||||
// while (!hit2) {
|
|
||||||
// delay(10);
|
|
||||||
// }
|
|
||||||
// delay(1000); // Wait for the client to process the response
|
|
||||||
// ESP.restart();
|
|
||||||
}
|
|
||||||
|
|
||||||
void loop() {
|
|
||||||
}
|
|
@ -1,472 +0,0 @@
|
|||||||
//
|
|
||||||
// A simple server implementation showing how to:
|
|
||||||
// * serve static messages
|
|
||||||
// * read GET and POST parameters
|
|
||||||
// * handle missing pages / 404s
|
|
||||||
//
|
|
||||||
|
|
||||||
#include <Arduino.h>
|
|
||||||
#ifdef ESP32
|
|
||||||
#include <AsyncTCP.h>
|
|
||||||
#include <WiFi.h>
|
|
||||||
#elif defined(ESP8266)
|
|
||||||
#include <ESP8266WiFi.h>
|
|
||||||
#include <ESPAsyncTCP.h>
|
|
||||||
#elif defined(TARGET_RP2040)
|
|
||||||
#include <WebServer.h>
|
|
||||||
#include <WiFi.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <ESPAsyncWebServer.h>
|
|
||||||
|
|
||||||
#if ASYNC_JSON_SUPPORT == 1
|
|
||||||
#include <ArduinoJson.h>
|
|
||||||
#include <AsyncJson.h>
|
|
||||||
#include <AsyncMessagePack.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <LittleFS.h>
|
|
||||||
|
|
||||||
AsyncWebServer server(80);
|
|
||||||
AsyncEventSource events("/events");
|
|
||||||
AsyncWebSocket ws("/ws");
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Middlewares
|
|
||||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
// log incoming requests
|
|
||||||
LoggingMiddleware requestLogger;
|
|
||||||
|
|
||||||
// CORS
|
|
||||||
CorsMiddleware cors;
|
|
||||||
|
|
||||||
// maximum 5 requests per 10 seconds
|
|
||||||
RateLimitMiddleware rateLimit;
|
|
||||||
|
|
||||||
// filter out specific headers from the incoming request
|
|
||||||
HeaderFilterMiddleware headerFilter;
|
|
||||||
|
|
||||||
// remove all headers from the incoming request except the ones provided in the constructor
|
|
||||||
HeaderFreeMiddleware headerFree;
|
|
||||||
|
|
||||||
// basicAuth
|
|
||||||
AuthenticationMiddleware basicAuth;
|
|
||||||
AuthenticationMiddleware basicAuthHash;
|
|
||||||
|
|
||||||
// simple digest authentication
|
|
||||||
AuthenticationMiddleware digestAuth;
|
|
||||||
AuthenticationMiddleware digestAuthHash;
|
|
||||||
|
|
||||||
// complex authentication which adds request attributes for the next middlewares and handler
|
|
||||||
AsyncMiddlewareFunction complexAuth([](AsyncWebServerRequest* request, ArMiddlewareNext next) {
|
|
||||||
if (!request->authenticate("user", "password")) {
|
|
||||||
return request->requestAuthentication();
|
|
||||||
}
|
|
||||||
request->setAttribute("user", "Mathieu");
|
|
||||||
request->setAttribute("role", "staff");
|
|
||||||
|
|
||||||
next();
|
|
||||||
|
|
||||||
request->getResponse()->addHeader("X-Rate-Limit", "200");
|
|
||||||
});
|
|
||||||
|
|
||||||
AuthorizationMiddleware authz([](AsyncWebServerRequest* request) { return request->getAttribute("role") == "staff"; });
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
const char* PARAM_MESSAGE PROGMEM = "message";
|
|
||||||
const char* SSE_HTLM PROGMEM = R"(
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<title>Server-Sent Events</title>
|
|
||||||
<script>
|
|
||||||
if (!!window.EventSource) {
|
|
||||||
var source = new EventSource('/events');
|
|
||||||
source.addEventListener('open', function(e) {
|
|
||||||
console.log("Events Connected");
|
|
||||||
}, false);
|
|
||||||
source.addEventListener('error', function(e) {
|
|
||||||
if (e.target.readyState != EventSource.OPEN) {
|
|
||||||
console.log("Events Disconnected");
|
|
||||||
}
|
|
||||||
}, false);
|
|
||||||
source.addEventListener('message', function(e) {
|
|
||||||
console.log("message", e.data);
|
|
||||||
}, false);
|
|
||||||
source.addEventListener('heartbeat', function(e) {
|
|
||||||
console.log("heartbeat", e.data);
|
|
||||||
}, false);
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<h1>Open your browser console!</h1>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
)";
|
|
||||||
|
|
||||||
void notFound(AsyncWebServerRequest* request) {
|
|
||||||
request->send(404, "text/plain", "Not found");
|
|
||||||
}
|
|
||||||
|
|
||||||
#if ASYNC_JSON_SUPPORT == 1
|
|
||||||
AsyncCallbackJsonWebHandler* jsonHandler = new AsyncCallbackJsonWebHandler("/json2");
|
|
||||||
AsyncCallbackMessagePackWebHandler* msgPackHandler = new AsyncCallbackMessagePackWebHandler("/msgpack2");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void setup() {
|
|
||||||
|
|
||||||
Serial.begin(115200);
|
|
||||||
|
|
||||||
#ifndef CONFIG_IDF_TARGET_ESP32H2
|
|
||||||
// WiFi.mode(WIFI_STA);
|
|
||||||
// WiFi.begin("YOUR_SSID", "YOUR_PASSWORD");
|
|
||||||
// if (WiFi.waitForConnectResult() != WL_CONNECTED) {
|
|
||||||
// Serial.printf("WiFi Failed!\n");
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
// Serial.print("IP Address: ");
|
|
||||||
// Serial.println(WiFi.localIP());
|
|
||||||
|
|
||||||
WiFi.mode(WIFI_AP);
|
|
||||||
WiFi.softAP("esp-captive");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// curl -v -X GET http://192.168.4.1/handler-not-sending-response
|
|
||||||
server.on("/handler-not-sending-response", HTTP_GET, [](AsyncWebServerRequest* request) {
|
|
||||||
// handler forgot to send a response to the client => 501 Not Implemented
|
|
||||||
});
|
|
||||||
|
|
||||||
// This is possible to replace a response.
|
|
||||||
// the previous one will be deleted.
|
|
||||||
// response sending happens when the handler returns.
|
|
||||||
// curl -v -X GET http://192.168.4.1/replace
|
|
||||||
server.on("/replace", HTTP_GET, [](AsyncWebServerRequest* request) {
|
|
||||||
request->send(200, "text/plain", "Hello, world");
|
|
||||||
// oups! finally we want to send a different response
|
|
||||||
request->send(400, "text/plain", "validation error");
|
|
||||||
#ifndef TARGET_RP2040
|
|
||||||
Serial.printf("Free heap: %" PRIu32 "\n", ESP.getFreeHeap());
|
|
||||||
#endif
|
|
||||||
});
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////
|
|
||||||
// Request header manipulations
|
|
||||||
///////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
// curl -v -X GET -H "x-remove-me: value" http://192.168.4.1/headers
|
|
||||||
server.on("/headers", HTTP_GET, [](AsyncWebServerRequest* request) {
|
|
||||||
Serial.printf("Request Headers:\n");
|
|
||||||
for (auto& h : request->getHeaders())
|
|
||||||
Serial.printf("Request Header: %s = %s\n", h.name().c_str(), h.value().c_str());
|
|
||||||
|
|
||||||
// remove x-remove-me header
|
|
||||||
request->removeHeader("x-remove-me");
|
|
||||||
Serial.printf("Request Headers:\n");
|
|
||||||
for (auto& h : request->getHeaders())
|
|
||||||
Serial.printf("Request Header: %s = %s\n", h.name().c_str(), h.value().c_str());
|
|
||||||
|
|
||||||
std::vector<const char*> headers;
|
|
||||||
request->getHeaderNames(headers);
|
|
||||||
for (auto& h : headers)
|
|
||||||
Serial.printf("Request Header Name: %s\n", h);
|
|
||||||
|
|
||||||
request->send(200);
|
|
||||||
});
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////
|
|
||||||
// Middlewares at server level (will apply to all requests)
|
|
||||||
///////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
requestLogger.setOutput(Serial);
|
|
||||||
|
|
||||||
basicAuth.setUsername("admin");
|
|
||||||
basicAuth.setPassword("admin");
|
|
||||||
basicAuth.setRealm("MyApp");
|
|
||||||
basicAuth.setAuthFailureMessage("Authentication failed");
|
|
||||||
basicAuth.setAuthType(AsyncAuthType::AUTH_BASIC);
|
|
||||||
basicAuth.generateHash();
|
|
||||||
|
|
||||||
basicAuthHash.setUsername("admin");
|
|
||||||
basicAuthHash.setPasswordHash("YWRtaW46YWRtaW4="); // BASE64(admin:admin)
|
|
||||||
basicAuthHash.setRealm("MyApp");
|
|
||||||
basicAuthHash.setAuthFailureMessage("Authentication failed");
|
|
||||||
basicAuthHash.setAuthType(AsyncAuthType::AUTH_BASIC);
|
|
||||||
|
|
||||||
digestAuth.setUsername("admin");
|
|
||||||
digestAuth.setPassword("admin");
|
|
||||||
digestAuth.setRealm("MyApp");
|
|
||||||
digestAuth.setAuthFailureMessage("Authentication failed");
|
|
||||||
digestAuth.setAuthType(AsyncAuthType::AUTH_DIGEST);
|
|
||||||
digestAuth.generateHash();
|
|
||||||
|
|
||||||
digestAuthHash.setUsername("admin");
|
|
||||||
digestAuthHash.setPasswordHash("f499b71f9a36d838b79268e145e132f7"); // MD5(user:realm:pass)
|
|
||||||
digestAuthHash.setRealm("MyApp");
|
|
||||||
digestAuthHash.setAuthFailureMessage("Authentication failed");
|
|
||||||
digestAuthHash.setAuthType(AsyncAuthType::AUTH_DIGEST);
|
|
||||||
|
|
||||||
rateLimit.setMaxRequests(5);
|
|
||||||
rateLimit.setWindowSize(10);
|
|
||||||
|
|
||||||
headerFilter.filter("X-Remove-Me");
|
|
||||||
headerFree.keep("X-Keep-Me");
|
|
||||||
headerFree.keep("host");
|
|
||||||
|
|
||||||
// global middleware
|
|
||||||
server.addMiddleware(&requestLogger);
|
|
||||||
server.addMiddlewares({&rateLimit, &cors, &headerFilter});
|
|
||||||
|
|
||||||
cors.setOrigin("http://192.168.4.1");
|
|
||||||
cors.setMethods("POST, GET, OPTIONS, DELETE");
|
|
||||||
cors.setHeaders("X-Custom-Header");
|
|
||||||
cors.setAllowCredentials(false);
|
|
||||||
cors.setMaxAge(600);
|
|
||||||
|
|
||||||
// Test CORS preflight request
|
|
||||||
// curl -v -X OPTIONS -H "origin: http://192.168.4.1" http://192.168.4.1/middleware/cors
|
|
||||||
server.on("/middleware/cors", HTTP_GET, [](AsyncWebServerRequest* request) {
|
|
||||||
request->send(200, "text/plain", "Hello, world!");
|
|
||||||
});
|
|
||||||
|
|
||||||
// curl -v -X GET -H "x-remove-me: value" http://192.168.4.1/middleware/test-header-filter
|
|
||||||
// - requestLogger will log the incoming headers (including x-remove-me)
|
|
||||||
// - headerFilter will remove x-remove-me header
|
|
||||||
// - handler will log the remaining headers
|
|
||||||
server.on("/middleware/test-header-filter", HTTP_GET, [](AsyncWebServerRequest* request) {
|
|
||||||
for (auto& h : request->getHeaders())
|
|
||||||
Serial.printf("Request Header: %s = %s\n", h.name().c_str(), h.value().c_str());
|
|
||||||
request->send(200);
|
|
||||||
});
|
|
||||||
|
|
||||||
// curl -v -X GET -H "x-keep-me: value" http://192.168.4.1/middleware/test-header-free
|
|
||||||
// - requestLogger will log the incoming headers (including x-keep-me)
|
|
||||||
// - headerFree will remove all headers except x-keep-me and host
|
|
||||||
// - handler will log the remaining headers (x-keep-me and host)
|
|
||||||
server.on("/middleware/test-header-free", HTTP_GET, [](AsyncWebServerRequest* request) {
|
|
||||||
for (auto& h : request->getHeaders())
|
|
||||||
Serial.printf("Request Header: %s = %s\n", h.name().c_str(), h.value().c_str());
|
|
||||||
request->send(200);
|
|
||||||
})
|
|
||||||
.addMiddleware(&headerFree);
|
|
||||||
|
|
||||||
// basic authentication method
|
|
||||||
// curl -v -X GET -H "origin: http://192.168.4.1" -u admin:admin http://192.168.4.1/middleware/auth-basic
|
|
||||||
server.on("/middleware/auth-basic", HTTP_GET, [](AsyncWebServerRequest* request) {
|
|
||||||
request->send(200, "text/plain", "Hello, world!");
|
|
||||||
})
|
|
||||||
.addMiddleware(&basicAuth);
|
|
||||||
|
|
||||||
// basic authentication method with hash
|
|
||||||
// curl -v -X GET -H "origin: http://192.168.4.1" -u admin:admin http://192.168.4.1/middleware/auth-basic-hash
|
|
||||||
server.on("/middleware/auth-basic-hash", HTTP_GET, [](AsyncWebServerRequest* request) {
|
|
||||||
request->send(200, "text/plain", "Hello, world!");
|
|
||||||
})
|
|
||||||
.addMiddleware(&basicAuthHash);
|
|
||||||
|
|
||||||
// digest authentication
|
|
||||||
// curl -v -X GET -H "origin: http://192.168.4.1" -u admin:admin --digest http://192.168.4.1/middleware/auth-digest
|
|
||||||
server.on("/middleware/auth-digest", HTTP_GET, [](AsyncWebServerRequest* request) {
|
|
||||||
request->send(200, "text/plain", "Hello, world!");
|
|
||||||
})
|
|
||||||
.addMiddleware(&digestAuth);
|
|
||||||
|
|
||||||
// digest authentication with hash
|
|
||||||
// curl -v -X GET -H "origin: http://192.168.4.1" -u admin:admin --digest http://192.168.4.1/middleware/auth-digest-hash
|
|
||||||
server.on("/middleware/auth-digest-hash", HTTP_GET, [](AsyncWebServerRequest* request) {
|
|
||||||
request->send(200, "text/plain", "Hello, world!");
|
|
||||||
})
|
|
||||||
.addMiddleware(&digestAuthHash);
|
|
||||||
|
|
||||||
// test digest auth with cors
|
|
||||||
// curl -v -X GET -H "origin: http://192.168.4.1" --digest -u user:password http://192.168.4.1/middleware/auth-custom
|
|
||||||
server.on("/middleware/auth-custom", HTTP_GET, [](AsyncWebServerRequest* request) {
|
|
||||||
String buffer = "Hello ";
|
|
||||||
buffer.concat(request->getAttribute("user"));
|
|
||||||
buffer.concat(" with role: ");
|
|
||||||
buffer.concat(request->getAttribute("role"));
|
|
||||||
request->send(200, "text/plain", buffer);
|
|
||||||
})
|
|
||||||
.addMiddlewares({&complexAuth, &authz});
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
// curl -v -X GET -H "origin: http://192.168.4.1" http://192.168.4.1/redirect
|
|
||||||
// curl -v -X POST -H "origin: http://192.168.4.1" http://192.168.4.1/redirect
|
|
||||||
server.on("/redirect", HTTP_GET | HTTP_POST, [](AsyncWebServerRequest* request) {
|
|
||||||
request->redirect("/");
|
|
||||||
});
|
|
||||||
|
|
||||||
server.on("/", HTTP_GET, [](AsyncWebServerRequest* request) {
|
|
||||||
request->send(200, "text/plain", "Hello, world");
|
|
||||||
});
|
|
||||||
|
|
||||||
server.on("/file", HTTP_GET, [](AsyncWebServerRequest* request) {
|
|
||||||
request->send(LittleFS, "/index.html");
|
|
||||||
});
|
|
||||||
|
|
||||||
/*
|
|
||||||
❯ curl -I -X HEAD http://192.168.4.1/download
|
|
||||||
HTTP/1.1 200 OK
|
|
||||||
Content-Length: 1024
|
|
||||||
Content-Type: application/octet-stream
|
|
||||||
Connection: close
|
|
||||||
Accept-Ranges: bytes
|
|
||||||
*/
|
|
||||||
// Ref: https://github.com/mathieucarbou/ESPAsyncWebServer/pull/80
|
|
||||||
server.on("/download", HTTP_HEAD | HTTP_GET, [](AsyncWebServerRequest* request) {
|
|
||||||
if (request->method() == HTTP_HEAD) {
|
|
||||||
AsyncWebServerResponse* response = request->beginResponse(200, "application/octet-stream");
|
|
||||||
response->addHeader(asyncsrv::T_Accept_Ranges, "bytes");
|
|
||||||
response->addHeader(asyncsrv::T_Content_Length, 10);
|
|
||||||
response->setContentLength(1024); // overrides previous one
|
|
||||||
response->addHeader(asyncsrv::T_Content_Type, "foo");
|
|
||||||
response->setContentType("application/octet-stream"); // overrides previous one
|
|
||||||
// ...
|
|
||||||
request->send(response);
|
|
||||||
} else {
|
|
||||||
// ...
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// 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);
|
|
||||||
});
|
|
||||||
|
|
||||||
// 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);
|
|
||||||
});
|
|
||||||
|
|
||||||
#if ASYNC_JSON_SUPPORT == 1
|
|
||||||
// JSON
|
|
||||||
|
|
||||||
// receives JSON and sends JSON
|
|
||||||
jsonHandler->onRequest([](AsyncWebServerRequest* request, JsonVariant& json) {
|
|
||||||
// JsonObject jsonObj = json.as<JsonObject>();
|
|
||||||
// ...
|
|
||||||
|
|
||||||
AsyncJsonResponse* response = new AsyncJsonResponse();
|
|
||||||
JsonObject root = response->getRoot().to<JsonObject>();
|
|
||||||
root["hello"] = "world";
|
|
||||||
response->setLength();
|
|
||||||
request->send(response);
|
|
||||||
});
|
|
||||||
|
|
||||||
// sends JSON
|
|
||||||
server.on("/json1", HTTP_GET, [](AsyncWebServerRequest* request) {
|
|
||||||
AsyncJsonResponse* response = new AsyncJsonResponse();
|
|
||||||
JsonObject root = response->getRoot().to<JsonObject>();
|
|
||||||
root["hello"] = "world";
|
|
||||||
response->setLength();
|
|
||||||
request->send(response);
|
|
||||||
});
|
|
||||||
|
|
||||||
// MessagePack
|
|
||||||
|
|
||||||
// receives MessagePack and sends MessagePack
|
|
||||||
msgPackHandler->onRequest([](AsyncWebServerRequest* request, JsonVariant& json) {
|
|
||||||
// JsonObject jsonObj = json.as<JsonObject>();
|
|
||||||
// ...
|
|
||||||
|
|
||||||
AsyncMessagePackResponse* response = new AsyncMessagePackResponse();
|
|
||||||
JsonObject root = response->getRoot().to<JsonObject>();
|
|
||||||
root["hello"] = "world";
|
|
||||||
response->setLength();
|
|
||||||
request->send(response);
|
|
||||||
});
|
|
||||||
|
|
||||||
// sends MessagePack
|
|
||||||
server.on("/msgpack1", HTTP_GET, [](AsyncWebServerRequest* request) {
|
|
||||||
AsyncMessagePackResponse* response = new AsyncMessagePackResponse();
|
|
||||||
JsonObject root = response->getRoot().to<JsonObject>();
|
|
||||||
root["hello"] = "world";
|
|
||||||
response->setLength();
|
|
||||||
request->send(response);
|
|
||||||
});
|
|
||||||
#endif
|
|
||||||
|
|
||||||
events.onConnect([](AsyncEventSourceClient* client) {
|
|
||||||
if (client->lastId()) {
|
|
||||||
Serial.printf("SSE Client reconnected! Last message ID that it gat is: %" PRIu32 "\n", client->lastId());
|
|
||||||
}
|
|
||||||
client->send("hello!", NULL, millis(), 1000);
|
|
||||||
});
|
|
||||||
|
|
||||||
server.on("/sse", HTTP_GET, [](AsyncWebServerRequest* request) {
|
|
||||||
request->send(200, "text/html", SSE_HTLM);
|
|
||||||
});
|
|
||||||
|
|
||||||
ws.onEvent([](AsyncWebSocket* server, AsyncWebSocketClient* client, AwsEventType type, void* arg, uint8_t* data, size_t len) {
|
|
||||||
(void)len;
|
|
||||||
if (type == WS_EVT_CONNECT) {
|
|
||||||
Serial.println("ws connect");
|
|
||||||
client->setCloseClientOnQueueFull(false);
|
|
||||||
client->ping();
|
|
||||||
} else if (type == WS_EVT_DISCONNECT) {
|
|
||||||
Serial.println("ws disconnect");
|
|
||||||
} else if (type == WS_EVT_ERROR) {
|
|
||||||
Serial.println("ws error");
|
|
||||||
} else if (type == WS_EVT_PONG) {
|
|
||||||
Serial.println("ws pong");
|
|
||||||
} else if (type == WS_EVT_DATA) {
|
|
||||||
AwsFrameInfo* info = (AwsFrameInfo*)arg;
|
|
||||||
String msg = "";
|
|
||||||
if (info->final && info->index == 0 && info->len == len) {
|
|
||||||
if (info->opcode == WS_TEXT) {
|
|
||||||
data[len] = 0;
|
|
||||||
Serial.printf("ws text: %s\n", (char*)data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
server.addHandler(&events);
|
|
||||||
server.addHandler(&ws);
|
|
||||||
|
|
||||||
#if ASYNC_JSON_SUPPORT == 1
|
|
||||||
server.addHandler(jsonHandler);
|
|
||||||
server.addHandler(msgPackHandler);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
server.onNotFound(notFound);
|
|
||||||
|
|
||||||
server.begin();
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t lastSSE = 0;
|
|
||||||
uint32_t deltaSSE = 5;
|
|
||||||
|
|
||||||
uint32_t lastWS = 0;
|
|
||||||
uint32_t deltaWS = 100;
|
|
||||||
|
|
||||||
void loop() {
|
|
||||||
uint32_t now = millis();
|
|
||||||
if (now - lastSSE >= deltaSSE) {
|
|
||||||
events.send(String("ping-") + now, "heartbeat", now);
|
|
||||||
lastSSE = millis();
|
|
||||||
}
|
|
||||||
if (now - lastWS >= deltaWS) {
|
|
||||||
ws.printfAll("kp%.4f", (10.0 / 3.0));
|
|
||||||
// ws.getClients
|
|
||||||
for (auto& client : ws.getClients()) {
|
|
||||||
client.text("kp%.4f", (10.0 / 3.0));
|
|
||||||
}
|
|
||||||
lastWS = millis();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,37 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <Stream.h>
|
|
||||||
|
|
||||||
class StreamConcat : public Stream {
|
|
||||||
public:
|
|
||||||
StreamConcat(Stream* s1, Stream* s2) : _s1(s1), _s2(s2) {}
|
|
||||||
|
|
||||||
size_t write(__unused const uint8_t* p, __unused size_t n) override { return 0; }
|
|
||||||
size_t write(__unused uint8_t c) override { return 0; }
|
|
||||||
void flush() override {}
|
|
||||||
|
|
||||||
int available() override { return _s1->available() + _s2->available(); }
|
|
||||||
|
|
||||||
int read() override {
|
|
||||||
int c = _s1->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 {
|
|
||||||
#endif
|
|
||||||
size_t count = _s1->readBytes(buffer, length);
|
|
||||||
return count > 0 ? count : _s2->readBytes(buffer, length);
|
|
||||||
}
|
|
||||||
|
|
||||||
int peek() override {
|
|
||||||
int c = _s1->peek();
|
|
||||||
return c != -1 ? c : _s2->peek();
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
Stream* _s1;
|
|
||||||
Stream* _s2;
|
|
||||||
};
|
|
@ -1,87 +0,0 @@
|
|||||||
#include <Arduino.h>
|
|
||||||
#include <DNSServer.h>
|
|
||||||
#ifdef ESP32
|
|
||||||
#include <AsyncTCP.h>
|
|
||||||
#include <WiFi.h>
|
|
||||||
#elif defined(ESP8266)
|
|
||||||
#include <ESP8266WiFi.h>
|
|
||||||
#include <ESPAsyncTCP.h>
|
|
||||||
#elif defined(TARGET_RP2040)
|
|
||||||
#include <WebServer.h>
|
|
||||||
#include <WiFi.h>
|
|
||||||
#endif
|
|
||||||
#include "StreamConcat.h"
|
|
||||||
#include "StreamString.h"
|
|
||||||
#include <ESPAsyncWebServer.h>
|
|
||||||
#include <LittleFS.h>
|
|
||||||
|
|
||||||
DNSServer dnsServer;
|
|
||||||
AsyncWebServer server(80);
|
|
||||||
|
|
||||||
void setup() {
|
|
||||||
Serial.begin(115200);
|
|
||||||
|
|
||||||
LittleFS.begin();
|
|
||||||
|
|
||||||
#ifndef CONFIG_IDF_TARGET_ESP32H2
|
|
||||||
WiFi.mode(WIFI_AP);
|
|
||||||
WiFi.softAP("esp-captive");
|
|
||||||
|
|
||||||
dnsServer.start(53, "*", WiFi.softAPIP());
|
|
||||||
#endif
|
|
||||||
|
|
||||||
File file1 = LittleFS.open("/header.html", "w");
|
|
||||||
file1.print("<html><head><title>ESP Captive Portal</title><meta http-equiv=\"refresh\" content=\"1\"></head><body>");
|
|
||||||
file1.close();
|
|
||||||
|
|
||||||
File file2 = LittleFS.open("/body.html", "w");
|
|
||||||
file2.print("<h1>Welcome to ESP Captive Portal</h1>");
|
|
||||||
file2.close();
|
|
||||||
|
|
||||||
File file3 = LittleFS.open("/footer.html", "w");
|
|
||||||
file3.print("</body></html>");
|
|
||||||
file3.close();
|
|
||||||
|
|
||||||
server.on("/", HTTP_GET, [](AsyncWebServerRequest* request) {
|
|
||||||
File header = LittleFS.open("/header.html", "r");
|
|
||||||
File body = LittleFS.open("/body.html", "r");
|
|
||||||
StreamConcat stream1(&header, &body);
|
|
||||||
|
|
||||||
StreamString content;
|
|
||||||
#if defined(TARGET_RP2040)
|
|
||||||
content.printf("FreeHeap: %d", rp2040.getFreeHeap());
|
|
||||||
#else
|
|
||||||
content.printf("FreeHeap: %" PRIu32, ESP.getFreeHeap());
|
|
||||||
#endif
|
|
||||||
StreamConcat stream2 = StreamConcat(&stream1, &content);
|
|
||||||
|
|
||||||
File footer = LittleFS.open("/footer.html", "r");
|
|
||||||
StreamConcat stream3 = StreamConcat(&stream2, &footer);
|
|
||||||
|
|
||||||
request->send(stream3, "text/html", stream3.available());
|
|
||||||
header.close();
|
|
||||||
body.close();
|
|
||||||
footer.close();
|
|
||||||
});
|
|
||||||
|
|
||||||
server.onNotFound([](AsyncWebServerRequest* request) {
|
|
||||||
request->send(404, "text/plain", "Not found");
|
|
||||||
});
|
|
||||||
|
|
||||||
server.begin();
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t last = 0;
|
|
||||||
|
|
||||||
void loop() {
|
|
||||||
// dnsServer.processNextRequest();
|
|
||||||
|
|
||||||
if (millis() - last > 2000) {
|
|
||||||
#if defined(TARGET_RP2040)
|
|
||||||
Serial.printf("FreeHeap: %d", rp2040.getFreeHeap());
|
|
||||||
#else
|
|
||||||
Serial.printf("FreeHeap: %" PRIu32, ESP.getFreeHeap());
|
|
||||||
#endif
|
|
||||||
last = millis();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,40 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <Stream.h>
|
|
||||||
|
|
||||||
class StreamString : public Stream {
|
|
||||||
public:
|
|
||||||
size_t write(const uint8_t* p, size_t n) override { return _buffer.concat(reinterpret_cast<const char*>(p), n) ? n : 0; }
|
|
||||||
size_t write(uint8_t c) override { return _buffer.concat(static_cast<char>(c)) ? 1 : 0; }
|
|
||||||
void flush() override {}
|
|
||||||
|
|
||||||
int available() override { return static_cast<int>(_buffer.length()); }
|
|
||||||
|
|
||||||
int read() override {
|
|
||||||
if (_buffer.length() == 0)
|
|
||||||
return -1;
|
|
||||||
char c = _buffer[0];
|
|
||||||
_buffer.remove(0, 1);
|
|
||||||
return c;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if defined(TARGET_RP2040)
|
|
||||||
size_t readBytes(char* buffer, size_t length) {
|
|
||||||
#else
|
|
||||||
size_t readBytes(char* buffer, size_t length) override {
|
|
||||||
#endif
|
|
||||||
if (length > _buffer.length())
|
|
||||||
length = _buffer.length();
|
|
||||||
// Don't use _str.ToCharArray() because it inserts a terminator
|
|
||||||
memcpy(buffer, _buffer.c_str(), length);
|
|
||||||
_buffer.remove(0, static_cast<unsigned int>(length));
|
|
||||||
return length;
|
|
||||||
}
|
|
||||||
|
|
||||||
int peek() override { return _buffer.length() > 0 ? _buffer[0] : -1; }
|
|
||||||
|
|
||||||
const String& buffer() const { return _buffer; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
String _buffer;
|
|
||||||
};
|
|
@ -1,107 +0,0 @@
|
|||||||
#include <DNSServer.h>
|
|
||||||
#ifdef ESP32
|
|
||||||
#include <AsyncTCP.h>
|
|
||||||
#include <WiFi.h>
|
|
||||||
#elif defined(ESP8266)
|
|
||||||
#include <ESP8266WiFi.h>
|
|
||||||
#include <ESPAsyncTCP.h>
|
|
||||||
#elif defined(TARGET_RP2040)
|
|
||||||
#include <WebServer.h>
|
|
||||||
#include <WiFi.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "ESPAsyncWebServer.h"
|
|
||||||
|
|
||||||
const char appWebPage[] PROGMEM = R"rawliteral(
|
|
||||||
<body>
|
|
||||||
<button id="button1" onclick="fetch('/button1');">Relay1</button>
|
|
||||||
<script>
|
|
||||||
const evtSource = new EventSource("/events");
|
|
||||||
button1 = document.getElementById("button1");
|
|
||||||
evtSource.addEventListener('state', (e) => {
|
|
||||||
const data = JSON.parse(e.data);
|
|
||||||
console.log('Event Source data: ', data);
|
|
||||||
if (data.button1) {
|
|
||||||
button1.style.backgroundColor = "green";
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
button1.style.backgroundColor = "red";
|
|
||||||
}
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
)rawliteral";
|
|
||||||
|
|
||||||
AsyncWebServer server(80);
|
|
||||||
AsyncEventSource events("/events");
|
|
||||||
|
|
||||||
const uint32_t interval = 1000;
|
|
||||||
const int button1Pin = 4;
|
|
||||||
|
|
||||||
uint32_t lastSend = 0;
|
|
||||||
|
|
||||||
void prepareJson(String& buffer) {
|
|
||||||
buffer.reserve(512);
|
|
||||||
buffer.concat("{\"button1\":");
|
|
||||||
buffer.concat(digitalRead(button1Pin) == LOW);
|
|
||||||
buffer.concat(",\"1234567890abcdefghij1234567890abcdefghij1234567890abcdefghij1234567890abcdefghij1234567890abcdefghij1234567890abcdefghij\":");
|
|
||||||
buffer.concat(random(0, 999999999));
|
|
||||||
buffer.concat("}");
|
|
||||||
}
|
|
||||||
|
|
||||||
void setup() {
|
|
||||||
Serial.begin(115200);
|
|
||||||
#if ARDUINO_USB_CDC_ON_BOOT
|
|
||||||
Serial.setTxTimeoutMs(0);
|
|
||||||
delay(100);
|
|
||||||
#else
|
|
||||||
while (!Serial)
|
|
||||||
yield();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
randomSeed(micros());
|
|
||||||
|
|
||||||
pinMode(button1Pin, OUTPUT);
|
|
||||||
digitalWrite(button1Pin, HIGH);
|
|
||||||
|
|
||||||
WiFi.softAP("esp-captive");
|
|
||||||
|
|
||||||
server.on("/", HTTP_GET, [](AsyncWebServerRequest* request) {
|
|
||||||
request->send(200, "text/html", appWebPage);
|
|
||||||
});
|
|
||||||
|
|
||||||
server.on("/button1", HTTP_GET, [](AsyncWebServerRequest* request) {
|
|
||||||
request->send(200, "text/plain", "OK");
|
|
||||||
digitalWrite(button1Pin, digitalRead(button1Pin) == LOW ? HIGH : LOW);
|
|
||||||
|
|
||||||
String buffer;
|
|
||||||
prepareJson(buffer);
|
|
||||||
ESP_LOGI("async_tcp", "Sending from handler...");
|
|
||||||
events.send(buffer.c_str(), "state", millis());
|
|
||||||
ESP_LOGI("async_tcp", "Sent from handler!");
|
|
||||||
});
|
|
||||||
|
|
||||||
events.onConnect([](AsyncEventSourceClient* client) {
|
|
||||||
String buffer;
|
|
||||||
prepareJson(buffer);
|
|
||||||
ESP_LOGI("async_tcp", "Sending from onConnect...");
|
|
||||||
client->send(buffer.c_str(), "state", millis(), 5000);
|
|
||||||
ESP_LOGI("async_tcp", "Sent from onConnect!");
|
|
||||||
});
|
|
||||||
|
|
||||||
server.addHandler(&events);
|
|
||||||
DefaultHeaders::Instance().addHeader("Access-Control-Allow-Origin", "*");
|
|
||||||
|
|
||||||
server.begin();
|
|
||||||
}
|
|
||||||
|
|
||||||
void loop() {
|
|
||||||
if (millis() - lastSend >= interval) {
|
|
||||||
String buffer;
|
|
||||||
prepareJson(buffer);
|
|
||||||
ESP_LOGI("loop", "Sending...");
|
|
||||||
events.send(buffer.c_str(), "state", millis());
|
|
||||||
ESP_LOGI("loop", "Sent!");
|
|
||||||
lastSend = millis();
|
|
||||||
}
|
|
||||||
}
|
|
1
examples/regex_patterns/.test.build_flags
Normal file
1
examples/regex_patterns/.test.build_flags
Normal file
@ -0,0 +1 @@
|
|||||||
|
-DASYNCWEBSERVER_REGEX=1
|
77
examples/regex_patterns/regex_patterns.ino
Normal file
77
examples/regex_patterns/regex_patterns.ino
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
//
|
||||||
|
// A simple server implementation with regex routes:
|
||||||
|
// * serve static messages
|
||||||
|
// * read GET and POST parameters
|
||||||
|
// * handle missing pages / 404s
|
||||||
|
//
|
||||||
|
|
||||||
|
// Add buildflag ASYNCWEBSERVER_REGEX to enable the regex support
|
||||||
|
|
||||||
|
// For platformio: platformio.ini:
|
||||||
|
// build_flags =
|
||||||
|
// -DASYNCWEBSERVER_REGEX
|
||||||
|
|
||||||
|
// For arduino IDE: create/update platform.local.txt
|
||||||
|
// Windows: C:\Users\(username)\AppData\Local\Arduino15\packages\espxxxx\hardware\espxxxx\{version}\platform.local.txt
|
||||||
|
// Linux: ~/.arduino15/packages/espxxxx/hardware/espxxxx/{version}/platform.local.txt
|
||||||
|
//
|
||||||
|
// compiler.cpp.extra_flags=-DASYNCWEBSERVER_REGEX=1
|
||||||
|
|
||||||
|
#include <Arduino.h>
|
||||||
|
#ifdef ESP32
|
||||||
|
#include <WiFi.h>
|
||||||
|
#include <AsyncTCP.h>
|
||||||
|
#elif defined(ESP8266)
|
||||||
|
#include <ESP8266WiFi.h>
|
||||||
|
#include <ESPAsyncTCP.h>
|
||||||
|
#endif
|
||||||
|
#include <ESPAsyncWebServer.h>
|
||||||
|
|
||||||
|
AsyncWebServer server(80);
|
||||||
|
|
||||||
|
const char* ssid = "YOUR_SSID";
|
||||||
|
const char* password = "YOUR_PASSWORD";
|
||||||
|
|
||||||
|
const char* PARAM_MESSAGE = "message";
|
||||||
|
|
||||||
|
void notFound(AsyncWebServerRequest *request) {
|
||||||
|
request->send(404, "text/plain", "Not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
|
||||||
|
Serial.begin(115200);
|
||||||
|
WiFi.mode(WIFI_STA);
|
||||||
|
WiFi.begin(ssid, password);
|
||||||
|
if (WiFi.waitForConnectResult() != WL_CONNECTED) {
|
||||||
|
Serial.printf("WiFi Failed!\n");
|
||||||
|
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>/sensor/<number>
|
||||||
|
server.on("^\\/sensor\\/([0-9]+)$", HTTP_GET, [] (AsyncWebServerRequest *request) {
|
||||||
|
String sensorNumber = request->pathArg(0);
|
||||||
|
request->send(200, "text/plain", "Hello, sensor: " + sensorNumber);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Send a GET request to <IP>/sensor/<number>/action/<action>
|
||||||
|
server.on("^\\/sensor\\/([0-9]+)\\/action\\/([a-zA-Z0-9]+)$", HTTP_GET, [] (AsyncWebServerRequest *request) {
|
||||||
|
String sensorNumber = request->pathArg(0);
|
||||||
|
String action = request->pathArg(1);
|
||||||
|
request->send(200, "text/plain", "Hello, sensor: " + sensorNumber + ", with action: " + action);
|
||||||
|
});
|
||||||
|
|
||||||
|
server.onNotFound(notFound);
|
||||||
|
|
||||||
|
server.begin();
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
}
|
74
examples/simple_server/simple_server.ino
Normal file
74
examples/simple_server/simple_server.ino
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
//
|
||||||
|
// A simple server implementation showing how to:
|
||||||
|
// * serve static messages
|
||||||
|
// * read GET and POST parameters
|
||||||
|
// * handle missing pages / 404s
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <Arduino.h>
|
||||||
|
#ifdef ESP32
|
||||||
|
#include <WiFi.h>
|
||||||
|
#include <AsyncTCP.h>
|
||||||
|
#elif defined(ESP8266)
|
||||||
|
#include <ESP8266WiFi.h>
|
||||||
|
#include <ESPAsyncTCP.h>
|
||||||
|
#endif
|
||||||
|
#include <ESPAsyncWebServer.h>
|
||||||
|
|
||||||
|
AsyncWebServer server(80);
|
||||||
|
|
||||||
|
const char* ssid = "YOUR_SSID";
|
||||||
|
const char* password = "YOUR_PASSWORD";
|
||||||
|
|
||||||
|
const char* PARAM_MESSAGE = "message";
|
||||||
|
|
||||||
|
void notFound(AsyncWebServerRequest *request) {
|
||||||
|
request->send(404, "text/plain", "Not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
|
||||||
|
Serial.begin(115200);
|
||||||
|
WiFi.mode(WIFI_STA);
|
||||||
|
WiFi.begin(ssid, password);
|
||||||
|
if (WiFi.waitForConnectResult() != WL_CONNECTED) {
|
||||||
|
Serial.printf("WiFi Failed!\n");
|
||||||
|
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);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 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() {
|
||||||
|
}
|
3
keywords.txt
Executable file
3
keywords.txt
Executable file
@ -0,0 +1,3 @@
|
|||||||
|
JsonArray KEYWORD1
|
||||||
|
add KEYWORD2
|
||||||
|
createArray KEYWORD3
|
19
library.json
Normal file
19
library.json
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"name":"ESPAsyncWebServer-esphome",
|
||||||
|
"description":"Asynchronous HTTP and WebSocket Server Library for ESP8266 and ESP32",
|
||||||
|
"keywords":"http,async,websocket,webserver",
|
||||||
|
"authors":
|
||||||
|
{
|
||||||
|
"name": "ESPHome Team",
|
||||||
|
"maintainer": true
|
||||||
|
},
|
||||||
|
"repository":
|
||||||
|
{
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/esphome/ESPAsyncWebServer.git"
|
||||||
|
},
|
||||||
|
"version": "3.1.0",
|
||||||
|
"license": "LGPL-3.0",
|
||||||
|
"frameworks": "arduino",
|
||||||
|
"platforms": ["espressif8266", "espressif32", "libretiny"]
|
||||||
|
}
|
@ -1,64 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "ESPAsyncWebServer",
|
|
||||||
"version": "3.3.7",
|
|
||||||
"description": "Asynchronous HTTP and WebSocket Server Library for ESP32, ESP8266 and RP2040. Supports: WebSocket, SSE, Authentication, Arduino Json 7, File Upload, Static File serving, URL Rewrite, URL Redirect, etc.",
|
|
||||||
"keywords": "http,async,websocket,webserver",
|
|
||||||
"homepage": "https://github.com/mathieucarbou/ESPAsyncWebServer",
|
|
||||||
"repository": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "https://github.com/mathieucarbou/ESPAsyncWebServer.git"
|
|
||||||
},
|
|
||||||
"authors": [
|
|
||||||
{
|
|
||||||
"name": "Hristo Gochkov"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Mathieu Carbou",
|
|
||||||
"maintainer": true
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"license": "LGPL-3.0",
|
|
||||||
"frameworks": "arduino",
|
|
||||||
"platforms": [
|
|
||||||
"espressif32",
|
|
||||||
"espressif8266",
|
|
||||||
"raspberrypi"
|
|
||||||
],
|
|
||||||
"dependencies": [
|
|
||||||
{
|
|
||||||
"owner": "mathieucarbou",
|
|
||||||
"name": "AsyncTCP",
|
|
||||||
"version": "^3.2.5",
|
|
||||||
"platforms": "espressif32"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"owner": "esphome",
|
|
||||||
"name": "ESPAsyncTCP-esphome",
|
|
||||||
"version": "^2.0.0",
|
|
||||||
"platforms": "espressif8266"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Hash",
|
|
||||||
"platforms": "espressif8266"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"owner": "khoih-prog",
|
|
||||||
"name": "AsyncTCP_RP2040W",
|
|
||||||
"version": "^1.2.0",
|
|
||||||
"platforms": "raspberrypi"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"export": {
|
|
||||||
"include": [
|
|
||||||
"examples",
|
|
||||||
"src",
|
|
||||||
"library.json",
|
|
||||||
"library.properties",
|
|
||||||
"LICENSE",
|
|
||||||
"README.md"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"build": {
|
|
||||||
"libCompatMode": "strict"
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,10 +0,0 @@
|
|||||||
name=ESPAsyncWebServer
|
|
||||||
version=3.3.7
|
|
||||||
author=Me-No-Dev
|
|
||||||
maintainer=Mathieu Carbou <mathieu.carbou@gmail.com>
|
|
||||||
sentence=Asynchronous HTTP and WebSocket Server Library for ESP32, ESP8266 and RP2040
|
|
||||||
paragraph=Supports: WebSocket, SSE, Authentication, Arduino Json 7, File Upload, Static File serving, URL Rewrite, URL Redirect, etc
|
|
||||||
category=Other
|
|
||||||
url=https://github.com/mathieucarbou/ESPAsyncWebServer
|
|
||||||
architectures=*
|
|
||||||
license=LGPL-3.0
|
|
111
platformio.ini
111
platformio.ini
@ -1,111 +0,0 @@
|
|||||||
[platformio]
|
|
||||||
default_envs = arduino-2, arduino-3, arduino-310rc1, esp8266, raspberrypi
|
|
||||||
lib_dir = .
|
|
||||||
; src_dir = examples/CaptivePortal
|
|
||||||
src_dir = examples/SimpleServer
|
|
||||||
; src_dir = examples/StreamFiles
|
|
||||||
; src_dir = examples/Filters
|
|
||||||
; src_dir = examples/Draft
|
|
||||||
; src_dir = examples/issues/Issue14
|
|
||||||
|
|
||||||
[env]
|
|
||||||
framework = arduino
|
|
||||||
build_flags =
|
|
||||||
-Wall -Wextra
|
|
||||||
-Wno-unused-parameter
|
|
||||||
-D CONFIG_ARDUHAL_LOG_COLORS
|
|
||||||
-D CORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_VERBOSE
|
|
||||||
-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_RUNNING_CORE=1
|
|
||||||
-D CONFIG_ASYNC_TCP_STACK_SIZE=4096
|
|
||||||
upload_protocol = esptool
|
|
||||||
monitor_speed = 115200
|
|
||||||
monitor_filters = esp32_exception_decoder, log2file
|
|
||||||
lib_deps =
|
|
||||||
; bblanchon/ArduinoJson @ 5.13.4
|
|
||||||
; bblanchon/ArduinoJson @ 6.21.5
|
|
||||||
bblanchon/ArduinoJson @ 7.2.0
|
|
||||||
mathieucarbou/AsyncTCP @ 3.2.5
|
|
||||||
board = esp32dev
|
|
||||||
|
|
||||||
[env:arduino-2]
|
|
||||||
platform = espressif32@6.9.0
|
|
||||||
|
|
||||||
[env:arduino-3]
|
|
||||||
platform = https://github.com/pioarduino/platform-espressif32/releases/download/51.03.05/platform-espressif32.zip
|
|
||||||
; board = esp32-s3-devkitc-1
|
|
||||||
; board = esp32-c6-devkitc-1
|
|
||||||
|
|
||||||
[env:arduino-3-no-json]
|
|
||||||
platform = https://github.com/pioarduino/platform-espressif32/releases/download/51.03.05/platform-espressif32.zip
|
|
||||||
; board = esp32-s3-devkitc-1
|
|
||||||
; board = esp32-c6-devkitc-1
|
|
||||||
lib_deps =
|
|
||||||
mathieucarbou/AsyncTCP @ 3.2.5
|
|
||||||
|
|
||||||
[env:arduino-310rc1]
|
|
||||||
platform = https://github.com/pioarduino/platform-espressif32/releases/download/53.03.10-rc1/platform-espressif32.zip
|
|
||||||
; board = esp32-s3-devkitc-1
|
|
||||||
; board = esp32-c6-devkitc-1
|
|
||||||
|
|
||||||
[env:esp8266]
|
|
||||||
platform = espressif8266
|
|
||||||
board = huzzah
|
|
||||||
; board = d1_mini
|
|
||||||
lib_deps =
|
|
||||||
bblanchon/ArduinoJson @ 7.2.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:raspberrypi]
|
|
||||||
upload_protocol = picotool
|
|
||||||
; platform = raspberrypi
|
|
||||||
platform = https://github.com/maxgerhardt/platform-raspberrypi.git#f2687073f73d554c9db41f29b4769fd9703f4e55
|
|
||||||
board = rpipicow
|
|
||||||
lib_deps =
|
|
||||||
bblanchon/ArduinoJson @ 7.2.0
|
|
||||||
khoih-prog/AsyncTCP_RP2040W @ 1.2.0
|
|
||||||
build_flags = ${env.build_flags}
|
|
||||||
-Wno-missing-field-initializers
|
|
||||||
|
|
||||||
; CI
|
|
||||||
|
|
||||||
[env:ci-arduino-2]
|
|
||||||
platform = espressif32@6.9.0
|
|
||||||
board = ${sysenv.PIO_BOARD}
|
|
||||||
|
|
||||||
[env:ci-arduino-3]
|
|
||||||
platform = https://github.com/pioarduino/platform-espressif32/releases/download/51.03.05/platform-espressif32.zip
|
|
||||||
board = ${sysenv.PIO_BOARD}
|
|
||||||
|
|
||||||
[env:ci-arduino-3-no-json]
|
|
||||||
platform = https://github.com/pioarduino/platform-espressif32/releases/download/51.03.05/platform-espressif32.zip
|
|
||||||
board = ${sysenv.PIO_BOARD}
|
|
||||||
lib_deps =
|
|
||||||
mathieucarbou/AsyncTCP @ 3.2.5
|
|
||||||
|
|
||||||
[env:ci-arduino-310rc1]
|
|
||||||
platform = https://github.com/pioarduino/platform-espressif32/releases/download/53.03.10-rc1/platform-espressif32.zip
|
|
||||||
board = ${sysenv.PIO_BOARD}
|
|
||||||
|
|
||||||
[env:ci-esp8266]
|
|
||||||
platform = espressif8266
|
|
||||||
board = ${sysenv.PIO_BOARD}
|
|
||||||
lib_deps =
|
|
||||||
bblanchon/ArduinoJson @ 7.2.0
|
|
||||||
esphome/ESPAsyncTCP-esphome @ 2.0.0
|
|
||||||
|
|
||||||
[env:ci-raspberrypi]
|
|
||||||
; platform = raspberrypi
|
|
||||||
platform = https://github.com/maxgerhardt/platform-raspberrypi.git#f2687073f73d554c9db41f29b4769fd9703f4e55
|
|
||||||
board = ${sysenv.PIO_BOARD}
|
|
||||||
lib_deps =
|
|
||||||
bblanchon/ArduinoJson @ 7.2.0
|
|
||||||
khoih-prog/AsyncTCP_RP2040W @ 1.2.0
|
|
||||||
build_flags = ${env.build_flags}
|
|
||||||
-Wno-missing-field-initializers
|
|
@ -18,70 +18,65 @@
|
|||||||
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"
|
||||||
#if defined(ESP32)
|
|
||||||
#include <rom/ets_sys.h>
|
|
||||||
#endif
|
|
||||||
#include "AsyncEventSource.h"
|
#include "AsyncEventSource.h"
|
||||||
|
|
||||||
using namespace asyncsrv;
|
static String generateEventMessage(const char *message, const char *event, uint32_t id, uint32_t reconnect){
|
||||||
|
String ev = "";
|
||||||
|
|
||||||
static String generateEventMessage(const char* message, const char* event, uint32_t id, uint32_t reconnect) {
|
if(reconnect){
|
||||||
String ev;
|
ev += "retry: ";
|
||||||
|
ev += String(reconnect);
|
||||||
if (reconnect) {
|
ev += "\r\n";
|
||||||
ev += T_retry_;
|
|
||||||
ev += reconnect;
|
|
||||||
ev += T_rn;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (id) {
|
if(id){
|
||||||
ev += T_id__;
|
ev += "id: ";
|
||||||
ev += id;
|
ev += String(id);
|
||||||
ev += T_rn;
|
ev += "\r\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (event != NULL) {
|
if(event != NULL){
|
||||||
ev += T_event_;
|
ev += "event: ";
|
||||||
ev += event;
|
ev += String(event);
|
||||||
ev += T_rn;
|
ev += "\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 += T_data_;
|
ev += "data: ";
|
||||||
ev += ldata;
|
ev += ldata;
|
||||||
ev += T_rnrn;
|
ev += "\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 {
|
||||||
@ -90,20 +85,20 @@ 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 += T_data_;
|
ev += "data: ";
|
||||||
ev += ldata;
|
ev += ldata;
|
||||||
ev += T_rn;
|
ev += "\r\n";
|
||||||
free(ldata);
|
free(ldata);
|
||||||
}
|
}
|
||||||
lineStart = nextLine;
|
lineStart = nextLine;
|
||||||
if (lineStart == ((char*)message + messageLen))
|
if(lineStart == ((char *)message + messageLen))
|
||||||
ev += T_rn;
|
ev += "\r\n";
|
||||||
}
|
}
|
||||||
} while (lineStart < ((char*)message + messageLen));
|
} while(lineStart < ((char *)message + messageLen));
|
||||||
}
|
}
|
||||||
|
|
||||||
return ev;
|
return ev;
|
||||||
@ -111,10 +106,11 @@ 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);
|
{
|
||||||
if (_data == nullptr) {
|
_data = (uint8_t*)malloc(_len+1);
|
||||||
|
if(_data == nullptr){
|
||||||
_len = 0;
|
_len = 0;
|
||||||
} else {
|
} else {
|
||||||
memcpy(_data, data, len);
|
memcpy(_data, data, len);
|
||||||
@ -123,13 +119,14 @@ 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, __attribute__((unused)) uint32_t time) {
|
size_t AsyncEventSourceMessage::ack(size_t len, uint32_t 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;
|
||||||
@ -140,269 +137,233 @@ size_t AsyncEventSourceMessage::ack(size_t len, __attribute__((unused)) uint32_t
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t AsyncEventSourceMessage::write(AsyncClient* client) {
|
size_t AsyncEventSourceMessage::send(AsyncClient *client) {
|
||||||
if (_sent >= _len || !client->canSend()) {
|
if (!client->canSend())
|
||||||
|
return 0;
|
||||||
|
const size_t len = _len - _sent;
|
||||||
|
if(client->space() < len){
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
size_t len = min(_len - _sent, client->space());
|
size_t sent = client->add((const char *)_data, len);
|
||||||
size_t sent = client->add((const char*)_data + _sent, len);
|
client->send();
|
||||||
_sent += sent;
|
_sent += sent;
|
||||||
return sent;
|
return sent;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t AsyncEventSourceMessage::send(AsyncClient* client) {
|
|
||||||
size_t sent = write(client);
|
|
||||||
return sent && client->send() ? sent : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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(T_Last_Event_ID))
|
if(request->hasHeader("Last-Event-ID"))
|
||||||
_lastId = atoi(request->getHeader(T_Last_Event_ID)->value().c_str());
|
_lastId = atoi(request->getHeader("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;
|
||||||
|
|
||||||
_client->setNoDelay(true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
AsyncEventSourceClient::~AsyncEventSourceClient() {
|
AsyncEventSourceClient::~AsyncEventSourceClient(){
|
||||||
#ifdef ESP32
|
_messageQueue.free();
|
||||||
std::lock_guard<std::mutex> lock(_lockmq);
|
|
||||||
#endif
|
|
||||||
_messageQueue.clear();
|
|
||||||
close();
|
close();
|
||||||
}
|
}
|
||||||
|
|
||||||
void AsyncEventSourceClient::_queueMessage(const char* message, size_t len) {
|
void AsyncEventSourceClient::_queueMessage(AsyncEventSourceMessage *dataMessage){
|
||||||
#ifdef ESP32
|
if(dataMessage == NULL)
|
||||||
// length() is not thread-safe, thus acquiring the lock before this call..
|
return;
|
||||||
std::lock_guard<std::mutex> lock(_lockmq);
|
if(!connected()){
|
||||||
#endif
|
delete dataMessage;
|
||||||
|
|
||||||
if (_messageQueue.size() >= SSE_MAX_QUEUED_MESSAGES) {
|
|
||||||
#ifdef ESP8266
|
|
||||||
ets_printf(String(F("ERROR: Too many messages queued\n")).c_str());
|
|
||||||
#elif defined(ESP32)
|
|
||||||
log_e("Too many messages queued: deleting message");
|
|
||||||
#endif
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if(_messageQueue.length() >= SSE_MAX_QUEUED_MESSAGES){
|
||||||
|
ets_printf("ERROR: Too many messages queued\n");
|
||||||
|
delete dataMessage;
|
||||||
|
} else {
|
||||||
|
_messageQueue.add(dataMessage);
|
||||||
|
}
|
||||||
|
if(_client->canSend())
|
||||||
|
_runQueue();
|
||||||
|
}
|
||||||
|
|
||||||
_messageQueue.emplace_back(message, len);
|
void AsyncEventSourceClient::_onAck(size_t len, uint32_t time){
|
||||||
// runqueue trigger when new messages added
|
while(len && !_messageQueue.isEmpty()){
|
||||||
if (_client->canSend()) {
|
len = _messageQueue.front()->ack(len, time);
|
||||||
|
if(_messageQueue.front()->finished())
|
||||||
|
_messageQueue.remove(_messageQueue.front());
|
||||||
|
}
|
||||||
|
|
||||||
|
_runQueue();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AsyncEventSourceClient::_onPoll(){
|
||||||
|
if(!_messageQueue.isEmpty()){
|
||||||
_runQueue();
|
_runQueue();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AsyncEventSourceClient::_onAck(size_t len __attribute__((unused)), uint32_t time __attribute__((unused))) {
|
|
||||||
#ifdef ESP32
|
|
||||||
// Same here, acquiring the lock early
|
|
||||||
std::lock_guard<std::mutex> lock(_lockmq);
|
|
||||||
#endif
|
|
||||||
_runQueue();
|
|
||||||
}
|
|
||||||
|
|
||||||
void AsyncEventSourceClient::_onPoll() {
|
void AsyncEventSourceClient::_onTimeout(uint32_t time __attribute__((unused))){
|
||||||
#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){
|
||||||
if (!connected())
|
_queueMessage(new AsyncEventSourceMessage(message, len));
|
||||||
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(ev.c_str(), ev.length());
|
_queueMessage(new AsyncEventSourceMessage(ev.c_str(), ev.length()));
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t AsyncEventSourceClient::packetsWaiting() const {
|
void AsyncEventSourceClient::_runQueue(){
|
||||||
#ifdef ESP32
|
while(!_messageQueue.isEmpty() && _messageQueue.front()->finished()){
|
||||||
std::lock_guard<std::mutex> lock(_lockmq);
|
_messageQueue.remove(_messageQueue.front());
|
||||||
#endif
|
}
|
||||||
return _messageQueue.size();
|
|
||||||
|
for(auto i = _messageQueue.begin(); i != _messageQueue.end(); ++i)
|
||||||
|
{
|
||||||
|
if(!(*i)->sent())
|
||||||
|
(*i)->send(_client);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AsyncEventSourceClient::_runQueue() {
|
|
||||||
size_t total_bytes_written = 0;
|
|
||||||
for (auto i = _messageQueue.begin(); i != _messageQueue.end(); ++i) {
|
|
||||||
if (!i->sent()) {
|
|
||||||
const size_t bytes_written = i->write(_client);
|
|
||||||
total_bytes_written += bytes_written;
|
|
||||||
if (bytes_written == 0)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (total_bytes_written > 0)
|
|
||||||
_client->send();
|
|
||||||
|
|
||||||
size_t len = total_bytes_written;
|
|
||||||
while (len && _messageQueue.size()) {
|
|
||||||
len = _messageQueue.front().ack(len);
|
|
||||||
if (_messageQueue.front().finished()) {
|
|
||||||
_messageQueue.pop_front();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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::_addClient(AsyncEventSourceClient * client){
|
||||||
AuthorizationMiddleware* m = new AuthorizationMiddleware(401, cb);
|
/*char * temp = (char *)malloc(2054);
|
||||||
m->_freeOnRemoval = true;
|
if(temp != NULL){
|
||||||
addMiddleware(m);
|
memset(temp+1,' ',2048);
|
||||||
}
|
temp[0] = ':';
|
||||||
|
temp[2049] = '\r';
|
||||||
|
temp[2050] = '\n';
|
||||||
|
temp[2051] = '\r';
|
||||||
|
temp[2052] = '\n';
|
||||||
|
temp[2053] = 0;
|
||||||
|
client->write((const char *)temp, 2053);
|
||||||
|
free(temp);
|
||||||
|
}*/
|
||||||
|
|
||||||
void AsyncEventSource::_addClient(AsyncEventSourceClient* client) {
|
_clients.add(client);
|
||||||
if (!client)
|
if(_connectcb)
|
||||||
return;
|
|
||||||
#ifdef ESP32
|
|
||||||
std::lock_guard<std::mutex> lock(_client_queue_lock);
|
|
||||||
#endif
|
|
||||||
_clients.emplace_back(client);
|
|
||||||
if (_connectcb)
|
|
||||||
_connectcb(client);
|
_connectcb(client);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AsyncEventSource::_handleDisconnect(AsyncEventSourceClient* client) {
|
void AsyncEventSource::_handleDisconnect(AsyncEventSourceClient * client){
|
||||||
#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
|
for(const auto &c: _clients){
|
||||||
// iterator should remain valid even when AsyncEventSource::_handleDisconnect()
|
if(c->connected())
|
||||||
// is called very early
|
|
||||||
#ifdef ESP32
|
|
||||||
std::lock_guard<std::mutex> lock(_client_queue_lock);
|
|
||||||
#endif
|
|
||||||
for (const auto& c : _clients) {
|
|
||||||
if (c->connected())
|
|
||||||
c->close();
|
c->close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// pmb fix
|
// pmb fix
|
||||||
size_t AsyncEventSource::avgPacketsWaiting() const {
|
size_t AsyncEventSource::avgPacketsWaiting() const {
|
||||||
size_t aql = 0;
|
if(_clients.isEmpty())
|
||||||
uint32_t nConnectedClients = 0;
|
|
||||||
#ifdef ESP32
|
|
||||||
std::lock_guard<std::mutex> lock(_client_queue_lock);
|
|
||||||
#endif
|
|
||||||
if (!_clients.size())
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
for (const auto& c : _clients) {
|
size_t aql=0;
|
||||||
if (c->connected()) {
|
uint32_t nConnectedClients=0;
|
||||||
aql += c->packetsWaiting();
|
|
||||||
|
for(const auto &c: _clients){
|
||||||
|
if(c->connected()) {
|
||||||
|
aql+=c->packetsWaiting();
|
||||||
++nConnectedClients;
|
++nConnectedClients;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ((aql) + (nConnectedClients / 2)) / (nConnectedClients); // round up
|
// return aql / nConnectedClients;
|
||||||
|
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);
|
||||||
#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 {
|
||||||
#ifdef ESP32
|
return _clients.count_if([](AsyncEventSourceClient *c){
|
||||||
std::lock_guard<std::mutex> lock(_client_queue_lock);
|
return c->connected();
|
||||||
#endif
|
});
|
||||||
size_t n_clients{0};
|
|
||||||
for (const auto& i : _clients)
|
|
||||||
if (i->connected())
|
|
||||||
++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("Last-Event-ID");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AsyncEventSource::handleRequest(AsyncWebServerRequest* request) {
|
void AsyncEventSource::handleRequest(AsyncWebServerRequest *request){
|
||||||
|
if((_username != "" && _password != "") && !request->authenticate(_username.c_str(), _password.c_str()))
|
||||||
|
return request->requestAuthentication();
|
||||||
request->send(new AsyncEventSourceResponse(this));
|
request->send(new AsyncEventSourceResponse(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Response
|
// Response
|
||||||
|
|
||||||
AsyncEventSourceResponse::AsyncEventSourceResponse(AsyncEventSource* server) {
|
AsyncEventSourceResponse::AsyncEventSourceResponse(AsyncEventSource *server){
|
||||||
_server = server;
|
_server = server;
|
||||||
_code = 200;
|
_code = 200;
|
||||||
_contentType = T_text_event_stream;
|
_contentType = "text/event-stream";
|
||||||
_sendContentLength = false;
|
_sendContentLength = false;
|
||||||
addHeader(T_Cache_Control, T_no_cache);
|
addHeader("Cache-Control", "no-cache");
|
||||||
addHeader(T_Connection, T_keep_alive);
|
addHeader("Connection","keep-alive");
|
||||||
}
|
}
|
||||||
|
|
||||||
void AsyncEventSourceResponse::_respond(AsyncWebServerRequest* request) {
|
void AsyncEventSourceResponse::_respond(AsyncWebServerRequest *request){
|
||||||
String out;
|
String out = _assembleHead(request->version());
|
||||||
_assembleHead(out, 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,133 +21,117 @@
|
|||||||
#define ASYNCEVENTSOURCE_H_
|
#define ASYNCEVENTSOURCE_H_
|
||||||
|
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#ifdef ESP32
|
#include <Arduino.h>
|
||||||
#include <AsyncTCP.h>
|
#if defined(ESP32) || defined(LIBRETINY)
|
||||||
#include <mutex>
|
#include <AsyncTCP.h>
|
||||||
#ifndef SSE_MAX_QUEUED_MESSAGES
|
#else
|
||||||
#define SSE_MAX_QUEUED_MESSAGES 32
|
#include <ESPAsyncTCP.h>
|
||||||
#endif
|
#endif
|
||||||
#elif defined(ESP8266)
|
|
||||||
#include <ESPAsyncTCP.h>
|
#ifndef SSE_MAX_QUEUED_MESSAGES
|
||||||
#ifndef SSE_MAX_QUEUED_MESSAGES
|
#define SSE_MAX_QUEUED_MESSAGES 32
|
||||||
#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
|
||||||
|
|
||||||
|
#if defined(ESP32) || defined(LIBRETINY)
|
||||||
|
#define DEFAULT_MAX_SSE_CLIENTS 8
|
||||||
|
#else
|
||||||
|
#define DEFAULT_MAX_SSE_CLIENTS 4
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
class AsyncEventSource;
|
class AsyncEventSource;
|
||||||
class AsyncEventSourceResponse;
|
class AsyncEventSourceResponse;
|
||||||
class AsyncEventSourceClient;
|
class AsyncEventSourceClient;
|
||||||
using ArEventHandlerFunction = std::function<void(AsyncEventSourceClient* client)>;
|
typedef std::function<void(AsyncEventSourceClient *client)> ArEventHandlerFunction;
|
||||||
using ArAuthorizeConnectHandler = ArAuthorizeFunction;
|
|
||||||
|
|
||||||
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 = 0);
|
size_t ack(size_t len, uint32_t time __attribute__((unused)));
|
||||||
size_t write(AsyncClient* client);
|
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;
|
||||||
std::list<AsyncEventSourceMessage> _messageQueue;
|
LinkedList<AsyncEventSourceMessage *> _messageQueue;
|
||||||
#ifdef ESP32
|
void _queueMessage(AsyncEventSourceMessage *dataMessage);
|
||||||
mutable std::mutex _lockmq;
|
|
||||||
#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 String& message, const String& event, uint32_t id = 0, uint32_t reconnect = 0) { send(message.c_str(), event.c_str(), id, reconnect); }
|
void send(const char *message, const char *event=NULL, uint32_t id=0, uint32_t reconnect=0);
|
||||||
void send(const String& message, const char* event, uint32_t id = 0, uint32_t reconnect = 0) { send(message.c_str(), event, id, reconnect); }
|
|
||||||
void send(const char* message, const char* event = NULL, uint32_t id = 0, uint32_t reconnect = 0);
|
|
||||||
bool connected() const { return (_client != NULL) && _client->connected(); }
|
bool 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 { return _messageQueue.length(); }
|
||||||
|
|
||||||
// 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;
|
||||||
std::list<std::unique_ptr<AsyncEventSourceClient>> _clients;
|
LinkedList<AsyncEventSourceClient *> _clients;
|
||||||
#ifdef ESP32
|
ArEventHandlerFunction _connectcb;
|
||||||
// Same as for individual messages, protect mutations of _clients list
|
|
||||||
// since simultaneous access from different tasks is possible
|
|
||||||
mutable std::mutex _client_queue_lock;
|
|
||||||
#endif
|
|
||||||
ArEventHandlerFunction _connectcb{nullptr};
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
AsyncEventSource(const String& url) : _url(url) {};
|
AsyncEventSource(const String& url);
|
||||||
~AsyncEventSource() { close(); };
|
~AsyncEventSource();
|
||||||
|
|
||||||
const char* url() const { return _url.c_str(); }
|
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 send(const char *message, const char *event=NULL, uint32_t id=0, uint32_t reconnect=0);
|
||||||
void send(const String& message, const String& event, uint32_t id = 0, uint32_t reconnect = 0) { send(message.c_str(), event.c_str(), id, reconnect); }
|
size_t count() const; //number clinets connected
|
||||||
void send(const String& message, const char* event, uint32_t id = 0, uint32_t reconnect = 0) { send(message.c_str(), event, id, reconnect); }
|
|
||||||
void send(const char* message, const char* event = NULL, uint32_t id = 0, uint32_t reconnect = 0);
|
|
||||||
// number of clients connected
|
|
||||||
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_ */
|
||||||
|
@ -1,155 +0,0 @@
|
|||||||
#include "AsyncJson.h"
|
|
||||||
|
|
||||||
#if ASYNC_JSON_SUPPORT == 1
|
|
||||||
|
|
||||||
#if ARDUINOJSON_VERSION_MAJOR == 5
|
|
||||||
AsyncJsonResponse::AsyncJsonResponse(bool isArray) : _isValid{false} {
|
|
||||||
_code = 200;
|
|
||||||
_contentType = asyncsrv::T_application_json;
|
|
||||||
if (isArray)
|
|
||||||
_root = _jsonBuffer.createArray();
|
|
||||||
else
|
|
||||||
_root = _jsonBuffer.createObject();
|
|
||||||
}
|
|
||||||
#elif ARDUINOJSON_VERSION_MAJOR == 6
|
|
||||||
AsyncJsonResponse::AsyncJsonResponse(bool isArray, size_t maxJsonBufferSize) : _jsonBuffer(maxJsonBufferSize), _isValid{false} {
|
|
||||||
_code = 200;
|
|
||||||
_contentType = asyncsrv::T_application_json;
|
|
||||||
if (isArray)
|
|
||||||
_root = _jsonBuffer.createNestedArray();
|
|
||||||
else
|
|
||||||
_root = _jsonBuffer.createNestedObject();
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
AsyncJsonResponse::AsyncJsonResponse(bool isArray) : _isValid{false} {
|
|
||||||
_code = 200;
|
|
||||||
_contentType = asyncsrv::T_application_json;
|
|
||||||
if (isArray)
|
|
||||||
_root = _jsonBuffer.add<JsonArray>();
|
|
||||||
else
|
|
||||||
_root = _jsonBuffer.add<JsonObject>();
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
size_t AsyncJsonResponse::setLength() {
|
|
||||||
#if ARDUINOJSON_VERSION_MAJOR == 5
|
|
||||||
_contentLength = _root.measureLength();
|
|
||||||
#else
|
|
||||||
_contentLength = measureJson(_root);
|
|
||||||
#endif
|
|
||||||
if (_contentLength) {
|
|
||||||
_isValid = true;
|
|
||||||
}
|
|
||||||
return _contentLength;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t AsyncJsonResponse::_fillBuffer(uint8_t* data, size_t len) {
|
|
||||||
ChunkPrint dest(data, _sentLength, len);
|
|
||||||
#if ARDUINOJSON_VERSION_MAJOR == 5
|
|
||||||
_root.printTo(dest);
|
|
||||||
#else
|
|
||||||
serializeJson(_root, dest);
|
|
||||||
#endif
|
|
||||||
return len;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if ARDUINOJSON_VERSION_MAJOR == 6
|
|
||||||
PrettyAsyncJsonResponse::PrettyAsyncJsonResponse(bool isArray, size_t maxJsonBufferSize) : AsyncJsonResponse{isArray, maxJsonBufferSize} {}
|
|
||||||
#else
|
|
||||||
PrettyAsyncJsonResponse::PrettyAsyncJsonResponse(bool isArray) : AsyncJsonResponse{isArray} {}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
size_t PrettyAsyncJsonResponse::setLength() {
|
|
||||||
#if ARDUINOJSON_VERSION_MAJOR == 5
|
|
||||||
_contentLength = _root.measurePrettyLength();
|
|
||||||
#else
|
|
||||||
_contentLength = measureJsonPretty(_root);
|
|
||||||
#endif
|
|
||||||
if (_contentLength) {
|
|
||||||
_isValid = true;
|
|
||||||
}
|
|
||||||
return _contentLength;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t PrettyAsyncJsonResponse::_fillBuffer(uint8_t* data, size_t len) {
|
|
||||||
ChunkPrint dest(data, _sentLength, len);
|
|
||||||
#if ARDUINOJSON_VERSION_MAJOR == 5
|
|
||||||
_root.prettyPrintTo(dest);
|
|
||||||
#else
|
|
||||||
serializeJsonPretty(_root, dest);
|
|
||||||
#endif
|
|
||||||
return len;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if ARDUINOJSON_VERSION_MAJOR == 6
|
|
||||||
AsyncCallbackJsonWebHandler::AsyncCallbackJsonWebHandler(const String& uri, ArJsonRequestHandlerFunction onRequest, size_t maxJsonBufferSize)
|
|
||||||
: _uri(uri), _method(HTTP_GET | HTTP_POST | HTTP_PUT | HTTP_PATCH), _onRequest(onRequest), maxJsonBufferSize(maxJsonBufferSize), _maxContentLength(16384) {}
|
|
||||||
#else
|
|
||||||
AsyncCallbackJsonWebHandler::AsyncCallbackJsonWebHandler(const String& uri, ArJsonRequestHandlerFunction onRequest)
|
|
||||||
: _uri(uri), _method(HTTP_GET | HTTP_POST | HTTP_PUT | HTTP_PATCH), _onRequest(onRequest), _maxContentLength(16384) {}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
bool AsyncCallbackJsonWebHandler::canHandle(AsyncWebServerRequest* request) {
|
|
||||||
if (!_onRequest)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
WebRequestMethodComposite request_method = request->method();
|
|
||||||
if (!(_method & request_method))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (_uri.length() && (_uri != request->url() && !request->url().startsWith(_uri + "/")))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (request_method != HTTP_GET && !request->contentType().equalsIgnoreCase(asyncsrv::T_application_json))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void AsyncCallbackJsonWebHandler::handleRequest(AsyncWebServerRequest* request) {
|
|
||||||
if (_onRequest) {
|
|
||||||
if (request->method() == HTTP_GET) {
|
|
||||||
JsonVariant json;
|
|
||||||
_onRequest(request, json);
|
|
||||||
return;
|
|
||||||
} else if (request->_tempObject != NULL) {
|
|
||||||
|
|
||||||
#if ARDUINOJSON_VERSION_MAJOR == 5
|
|
||||||
DynamicJsonBuffer jsonBuffer;
|
|
||||||
JsonVariant json = jsonBuffer.parse((uint8_t*)(request->_tempObject));
|
|
||||||
if (json.success()) {
|
|
||||||
#elif ARDUINOJSON_VERSION_MAJOR == 6
|
|
||||||
DynamicJsonDocument jsonBuffer(this->maxJsonBufferSize);
|
|
||||||
DeserializationError error = deserializeJson(jsonBuffer, (uint8_t*)(request->_tempObject));
|
|
||||||
if (!error) {
|
|
||||||
JsonVariant json = jsonBuffer.as<JsonVariant>();
|
|
||||||
#else
|
|
||||||
JsonDocument jsonBuffer;
|
|
||||||
DeserializationError error = deserializeJson(jsonBuffer, (uint8_t*)(request->_tempObject));
|
|
||||||
if (!error) {
|
|
||||||
JsonVariant json = jsonBuffer.as<JsonVariant>();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
_onRequest(request, json);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
request->send(_contentLength > _maxContentLength ? 413 : 400);
|
|
||||||
} else {
|
|
||||||
request->send(500);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void AsyncCallbackJsonWebHandler::handleBody(AsyncWebServerRequest* request, uint8_t* data, size_t len, size_t index, size_t total) {
|
|
||||||
if (_onRequest) {
|
|
||||||
_contentLength = total;
|
|
||||||
if (total > 0 && request->_tempObject == NULL && total < _maxContentLength) {
|
|
||||||
request->_tempObject = malloc(total);
|
|
||||||
}
|
|
||||||
if (request->_tempObject != NULL) {
|
|
||||||
memcpy((uint8_t*)(request->_tempObject) + index, data, len);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // ASYNC_JSON_SUPPORT
|
|
261
src/AsyncJson.h
261
src/AsyncJson.h
@ -26,7 +26,7 @@
|
|||||||
|
|
||||||
AsyncCallbackJsonWebHandler* handler = new AsyncCallbackJsonWebHandler("/rest/endpoint");
|
AsyncCallbackJsonWebHandler* handler = new AsyncCallbackJsonWebHandler("/rest/endpoint");
|
||||||
handler->onRequest([](AsyncWebServerRequest *request, JsonVariant &json) {
|
handler->onRequest([](AsyncWebServerRequest *request, JsonVariant &json) {
|
||||||
JsonObject jsonObj = json.as<JsonObject>();
|
JsonObject& jsonObj = json.as<JsonObject>();
|
||||||
// ...
|
// ...
|
||||||
});
|
});
|
||||||
server.addHandler(handler);
|
server.addHandler(handler);
|
||||||
@ -34,98 +34,221 @@
|
|||||||
*/
|
*/
|
||||||
#ifndef ASYNC_JSON_H_
|
#ifndef ASYNC_JSON_H_
|
||||||
#define ASYNC_JSON_H_
|
#define ASYNC_JSON_H_
|
||||||
|
#include <ArduinoJson.h>
|
||||||
|
#include <ESPAsyncWebServer.h>
|
||||||
|
#include <Print.h>
|
||||||
|
|
||||||
#if __has_include("ArduinoJson.h")
|
#if ARDUINOJSON_VERSION_MAJOR == 5
|
||||||
#include <ArduinoJson.h>
|
#define ARDUINOJSON_5_COMPATIBILITY
|
||||||
#if ARDUINOJSON_VERSION_MAJOR >= 5
|
#else
|
||||||
#define ASYNC_JSON_SUPPORT 1
|
|
||||||
#else
|
|
||||||
#define ASYNC_JSON_SUPPORT 0
|
|
||||||
#endif // ARDUINOJSON_VERSION_MAJOR >= 5
|
|
||||||
#endif // __has_include("ArduinoJson.h")
|
|
||||||
|
|
||||||
#if ASYNC_JSON_SUPPORT == 1
|
|
||||||
#include <ESPAsyncWebServer.h>
|
|
||||||
|
|
||||||
#include "ChunkPrint.h"
|
|
||||||
|
|
||||||
#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
|
||||||
|
|
||||||
class AsyncJsonResponse : public AsyncAbstractResponse {
|
constexpr const char* JSON_MIMETYPE = "application/json";
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Json Response
|
||||||
|
* */
|
||||||
|
|
||||||
|
class ChunkPrint : public Print {
|
||||||
|
private:
|
||||||
|
uint8_t* _destination;
|
||||||
|
size_t _to_skip;
|
||||||
|
size_t _to_write;
|
||||||
|
size_t _pos;
|
||||||
|
public:
|
||||||
|
ChunkPrint(uint8_t* destination, size_t from, size_t len)
|
||||||
|
: _destination(destination), _to_skip(from), _to_write(len), _pos{0} {}
|
||||||
|
virtual ~ChunkPrint(){}
|
||||||
|
size_t write(uint8_t c){
|
||||||
|
if (_to_skip > 0) {
|
||||||
|
_to_skip--;
|
||||||
|
return 1;
|
||||||
|
} else if (_to_write > 0) {
|
||||||
|
_to_write--;
|
||||||
|
_destination[_pos++] = c;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
size_t write(const uint8_t *buffer, size_t size)
|
||||||
|
{
|
||||||
|
return this->Print::write(buffer, size);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class AsyncJsonResponse: public AsyncAbstractResponse {
|
||||||
protected:
|
protected:
|
||||||
#if ARDUINOJSON_VERSION_MAJOR == 5
|
|
||||||
|
#ifdef ARDUINOJSON_5_COMPATIBILITY
|
||||||
DynamicJsonBuffer _jsonBuffer;
|
DynamicJsonBuffer _jsonBuffer;
|
||||||
#elif ARDUINOJSON_VERSION_MAJOR == 6
|
#else
|
||||||
DynamicJsonDocument _jsonBuffer;
|
DynamicJsonDocument _jsonBuffer;
|
||||||
#else
|
#endif
|
||||||
JsonDocument _jsonBuffer;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
JsonVariant _root;
|
JsonVariant _root;
|
||||||
bool _isValid;
|
bool _isValid;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
#if ARDUINOJSON_VERSION_MAJOR == 6
|
|
||||||
AsyncJsonResponse(bool isArray = false, size_t maxJsonBufferSize = DYNAMIC_JSON_DOCUMENT_SIZE);
|
#ifdef ARDUINOJSON_5_COMPATIBILITY
|
||||||
#else
|
AsyncJsonResponse(bool isArray=false): _isValid{false} {
|
||||||
AsyncJsonResponse(bool isArray = false);
|
_code = 200;
|
||||||
#endif
|
_contentType = JSON_MIMETYPE;
|
||||||
JsonVariant& getRoot() { return _root; }
|
if(isArray)
|
||||||
|
_root = _jsonBuffer.createArray();
|
||||||
|
else
|
||||||
|
_root = _jsonBuffer.createObject();
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
AsyncJsonResponse(bool isArray=false, size_t maxJsonBufferSize = DYNAMIC_JSON_DOCUMENT_SIZE) : _jsonBuffer(maxJsonBufferSize), _isValid{false} {
|
||||||
|
_code = 200;
|
||||||
|
_contentType = JSON_MIMETYPE;
|
||||||
|
if(isArray)
|
||||||
|
_root = _jsonBuffer.createNestedArray();
|
||||||
|
else
|
||||||
|
_root = _jsonBuffer.createNestedObject();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
~AsyncJsonResponse() {}
|
||||||
|
JsonVariant & getRoot() { return _root; }
|
||||||
bool _sourceValid() const { return _isValid; }
|
bool _sourceValid() const { return _isValid; }
|
||||||
size_t setLength();
|
size_t setLength() {
|
||||||
size_t getSize() const { return _jsonBuffer.size(); }
|
|
||||||
size_t _fillBuffer(uint8_t* data, size_t len);
|
#ifdef ARDUINOJSON_5_COMPATIBILITY
|
||||||
#if ARDUINOJSON_VERSION_MAJOR >= 6
|
_contentLength = _root.measureLength();
|
||||||
bool overflowed() const { return _jsonBuffer.overflowed(); }
|
#else
|
||||||
#endif
|
_contentLength = measureJson(_root);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (_contentLength) { _isValid = true; }
|
||||||
|
return _contentLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t getSize() { return _jsonBuffer.size(); }
|
||||||
|
|
||||||
|
size_t _fillBuffer(uint8_t *data, size_t len){
|
||||||
|
ChunkPrint dest(data, _sentLength, len);
|
||||||
|
|
||||||
|
#ifdef ARDUINOJSON_5_COMPATIBILITY
|
||||||
|
_root.printTo( dest ) ;
|
||||||
|
#else
|
||||||
|
serializeJson(_root, dest);
|
||||||
|
#endif
|
||||||
|
return len;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class PrettyAsyncJsonResponse : public AsyncJsonResponse {
|
class PrettyAsyncJsonResponse: public AsyncJsonResponse {
|
||||||
public:
|
public:
|
||||||
#if ARDUINOJSON_VERSION_MAJOR == 6
|
#ifdef ARDUINOJSON_5_COMPATIBILITY
|
||||||
PrettyAsyncJsonResponse(bool isArray = false, size_t maxJsonBufferSize = DYNAMIC_JSON_DOCUMENT_SIZE);
|
PrettyAsyncJsonResponse (bool isArray=false) : AsyncJsonResponse{isArray} {}
|
||||||
#else
|
#else
|
||||||
PrettyAsyncJsonResponse(bool isArray = false);
|
PrettyAsyncJsonResponse (bool isArray=false, size_t maxJsonBufferSize = DYNAMIC_JSON_DOCUMENT_SIZE) : AsyncJsonResponse{isArray, maxJsonBufferSize} {}
|
||||||
#endif
|
#endif
|
||||||
size_t setLength();
|
size_t setLength () {
|
||||||
size_t _fillBuffer(uint8_t* data, size_t len);
|
#ifdef ARDUINOJSON_5_COMPATIBILITY
|
||||||
|
_contentLength = _root.measurePrettyLength ();
|
||||||
|
#else
|
||||||
|
_contentLength = measureJsonPretty(_root);
|
||||||
|
#endif
|
||||||
|
if (_contentLength) {_isValid = true;}
|
||||||
|
return _contentLength;
|
||||||
|
}
|
||||||
|
size_t _fillBuffer (uint8_t *data, size_t len) {
|
||||||
|
ChunkPrint dest (data, _sentLength, len);
|
||||||
|
#ifdef ARDUINOJSON_5_COMPATIBILITY
|
||||||
|
_root.prettyPrintTo (dest);
|
||||||
|
#else
|
||||||
|
serializeJsonPretty(_root, dest);
|
||||||
|
#endif
|
||||||
|
return len;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef std::function<void(AsyncWebServerRequest* request, JsonVariant& json)> ArJsonRequestHandlerFunction;
|
typedef std::function<void(AsyncWebServerRequest *request, JsonVariant &json)> ArJsonRequestHandlerFunction;
|
||||||
|
|
||||||
class AsyncCallbackJsonWebHandler : public AsyncWebHandler {
|
class AsyncCallbackJsonWebHandler: public AsyncWebHandler {
|
||||||
protected:
|
private:
|
||||||
String _uri;
|
protected:
|
||||||
|
const String _uri;
|
||||||
WebRequestMethodComposite _method;
|
WebRequestMethodComposite _method;
|
||||||
ArJsonRequestHandlerFunction _onRequest;
|
ArJsonRequestHandlerFunction _onRequest;
|
||||||
size_t _contentLength;
|
size_t _contentLength;
|
||||||
#if ARDUINOJSON_VERSION_MAJOR == 6
|
#ifndef ARDUINOJSON_5_COMPATIBILITY
|
||||||
size_t maxJsonBufferSize;
|
const size_t maxJsonBufferSize;
|
||||||
#endif
|
#endif
|
||||||
size_t _maxContentLength;
|
size_t _maxContentLength;
|
||||||
|
public:
|
||||||
|
#ifdef ARDUINOJSON_5_COMPATIBILITY
|
||||||
|
AsyncCallbackJsonWebHandler(const String& uri, ArJsonRequestHandlerFunction onRequest)
|
||||||
|
: _uri(uri), _method(HTTP_POST|HTTP_PUT|HTTP_PATCH), _onRequest(onRequest), _maxContentLength(16384) {}
|
||||||
|
#else
|
||||||
|
AsyncCallbackJsonWebHandler(const String& uri, ArJsonRequestHandlerFunction onRequest, size_t maxJsonBufferSize=DYNAMIC_JSON_DOCUMENT_SIZE)
|
||||||
|
: _uri(uri), _method(HTTP_POST|HTTP_PUT|HTTP_PATCH), _onRequest(onRequest), maxJsonBufferSize(maxJsonBufferSize), _maxContentLength(16384) {}
|
||||||
|
#endif
|
||||||
|
|
||||||
public:
|
void setMethod(WebRequestMethodComposite method){ _method = method; }
|
||||||
#if ARDUINOJSON_VERSION_MAJOR == 6
|
void setMaxContentLength(int maxContentLength){ _maxContentLength = maxContentLength; }
|
||||||
AsyncCallbackJsonWebHandler(const String& uri, ArJsonRequestHandlerFunction onRequest = nullptr, size_t maxJsonBufferSize = DYNAMIC_JSON_DOCUMENT_SIZE);
|
void onRequest(ArJsonRequestHandlerFunction fn){ _onRequest = fn; }
|
||||||
#else
|
|
||||||
AsyncCallbackJsonWebHandler(const String& uri, ArJsonRequestHandlerFunction onRequest = nullptr);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void setMethod(WebRequestMethodComposite method) { _method = method; }
|
virtual bool canHandle(AsyncWebServerRequest *request) override final{
|
||||||
void setMaxContentLength(int maxContentLength) { _maxContentLength = maxContentLength; }
|
if(!_onRequest)
|
||||||
void onRequest(ArJsonRequestHandlerFunction fn) { _onRequest = fn; }
|
return false;
|
||||||
|
|
||||||
virtual bool canHandle(AsyncWebServerRequest* request) override final;
|
if(!(_method & request->method()))
|
||||||
virtual void handleRequest(AsyncWebServerRequest* request) override final;
|
return false;
|
||||||
virtual void handleUpload(__unused AsyncWebServerRequest* request, __unused const String& filename, __unused size_t index, __unused uint8_t* data, __unused size_t len, __unused bool final) override final {}
|
|
||||||
virtual void handleBody(AsyncWebServerRequest* request, uint8_t* data, size_t len, size_t index, size_t total) override final;
|
if(_uri.length() && (_uri != request->url() && !request->url().startsWith(_uri+"/")))
|
||||||
virtual bool isRequestHandlerTrivial() override final { return !_onRequest; }
|
return false;
|
||||||
|
|
||||||
|
if ( !request->contentType().equalsIgnoreCase(JSON_MIMETYPE) )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
request->addInterestingHeader("ANY");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void handleRequest(AsyncWebServerRequest *request) override final {
|
||||||
|
if(_onRequest) {
|
||||||
|
if (request->_tempObject != NULL) {
|
||||||
|
|
||||||
|
#ifdef ARDUINOJSON_5_COMPATIBILITY
|
||||||
|
DynamicJsonBuffer jsonBuffer;
|
||||||
|
JsonVariant json = jsonBuffer.parse((uint8_t*)(request->_tempObject));
|
||||||
|
if (json.success()) {
|
||||||
|
#else
|
||||||
|
DynamicJsonDocument jsonBuffer(this->maxJsonBufferSize);
|
||||||
|
DeserializationError error = deserializeJson(jsonBuffer, (uint8_t*)(request->_tempObject));
|
||||||
|
if(!error) {
|
||||||
|
JsonVariant json = jsonBuffer.as<JsonVariant>();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
_onRequest(request, json);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
request->send(_contentLength > _maxContentLength ? 413 : 400);
|
||||||
|
} else {
|
||||||
|
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 handleBody(AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total) override final {
|
||||||
|
if (_onRequest) {
|
||||||
|
_contentLength = total;
|
||||||
|
if (total > 0 && request->_tempObject == NULL && total < _maxContentLength) {
|
||||||
|
request->_tempObject = malloc(total);
|
||||||
|
}
|
||||||
|
if (request->_tempObject != NULL) {
|
||||||
|
memcpy((uint8_t*)(request->_tempObject) + index, data, len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
virtual bool isRequestHandlerTrivial() override final {return _onRequest ? false : true;}
|
||||||
};
|
};
|
||||||
|
#endif
|
||||||
#endif // ASYNC_JSON_SUPPORT == 1
|
|
||||||
|
|
||||||
#endif // ASYNC_JSON_H_
|
|
||||||
|
@ -1,106 +0,0 @@
|
|||||||
#include "AsyncMessagePack.h"
|
|
||||||
|
|
||||||
#if ASYNC_MSG_PACK_SUPPORT == 1
|
|
||||||
|
|
||||||
#if ARDUINOJSON_VERSION_MAJOR == 6
|
|
||||||
AsyncMessagePackResponse::AsyncMessagePackResponse(bool isArray, size_t maxJsonBufferSize) : _jsonBuffer(maxJsonBufferSize), _isValid{false} {
|
|
||||||
_code = 200;
|
|
||||||
_contentType = asyncsrv::T_application_msgpack;
|
|
||||||
if (isArray)
|
|
||||||
_root = _jsonBuffer.createNestedArray();
|
|
||||||
else
|
|
||||||
_root = _jsonBuffer.createNestedObject();
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
AsyncMessagePackResponse::AsyncMessagePackResponse(bool isArray) : _isValid{false} {
|
|
||||||
_code = 200;
|
|
||||||
_contentType = asyncsrv::T_application_msgpack;
|
|
||||||
if (isArray)
|
|
||||||
_root = _jsonBuffer.add<JsonArray>();
|
|
||||||
else
|
|
||||||
_root = _jsonBuffer.add<JsonObject>();
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
size_t AsyncMessagePackResponse::setLength() {
|
|
||||||
_contentLength = measureMsgPack(_root);
|
|
||||||
if (_contentLength) {
|
|
||||||
_isValid = true;
|
|
||||||
}
|
|
||||||
return _contentLength;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t AsyncMessagePackResponse::_fillBuffer(uint8_t* data, size_t len) {
|
|
||||||
ChunkPrint dest(data, _sentLength, len);
|
|
||||||
serializeMsgPack(_root, dest);
|
|
||||||
return len;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if ARDUINOJSON_VERSION_MAJOR == 6
|
|
||||||
AsyncCallbackMessagePackWebHandler::AsyncCallbackMessagePackWebHandler(const String& uri, ArMessagePackRequestHandlerFunction onRequest, size_t maxJsonBufferSize)
|
|
||||||
: _uri(uri), _method(HTTP_GET | HTTP_POST | HTTP_PUT | HTTP_PATCH), _onRequest(onRequest), maxJsonBufferSize(maxJsonBufferSize), _maxContentLength(16384) {}
|
|
||||||
#else
|
|
||||||
AsyncCallbackMessagePackWebHandler::AsyncCallbackMessagePackWebHandler(const String& uri, ArMessagePackRequestHandlerFunction onRequest)
|
|
||||||
: _uri(uri), _method(HTTP_GET | HTTP_POST | HTTP_PUT | HTTP_PATCH), _onRequest(onRequest), _maxContentLength(16384) {}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
bool AsyncCallbackMessagePackWebHandler::canHandle(AsyncWebServerRequest* request) {
|
|
||||||
if (!_onRequest)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
WebRequestMethodComposite request_method = request->method();
|
|
||||||
if (!(_method & request_method))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (_uri.length() && (_uri != request->url() && !request->url().startsWith(_uri + "/")))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (request_method != HTTP_GET && !request->contentType().equalsIgnoreCase(asyncsrv::T_application_msgpack))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void AsyncCallbackMessagePackWebHandler::handleRequest(AsyncWebServerRequest* request) {
|
|
||||||
if (_onRequest) {
|
|
||||||
if (request->method() == HTTP_GET) {
|
|
||||||
JsonVariant json;
|
|
||||||
_onRequest(request, json);
|
|
||||||
return;
|
|
||||||
} else if (request->_tempObject != NULL) {
|
|
||||||
|
|
||||||
#if ARDUINOJSON_VERSION_MAJOR == 6
|
|
||||||
DynamicJsonDocument jsonBuffer(this->maxJsonBufferSize);
|
|
||||||
DeserializationError error = deserializeMsgPack(jsonBuffer, (uint8_t*)(request->_tempObject));
|
|
||||||
if (!error) {
|
|
||||||
JsonVariant json = jsonBuffer.as<JsonVariant>();
|
|
||||||
#else
|
|
||||||
JsonDocument jsonBuffer;
|
|
||||||
DeserializationError error = deserializeMsgPack(jsonBuffer, (uint8_t*)(request->_tempObject));
|
|
||||||
if (!error) {
|
|
||||||
JsonVariant json = jsonBuffer.as<JsonVariant>();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
_onRequest(request, json);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
request->send(_contentLength > _maxContentLength ? 413 : 400);
|
|
||||||
} else {
|
|
||||||
request->send(500);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void AsyncCallbackMessagePackWebHandler::handleBody(AsyncWebServerRequest* request, uint8_t* data, size_t len, size_t index, size_t total) {
|
|
||||||
if (_onRequest) {
|
|
||||||
_contentLength = total;
|
|
||||||
if (total > 0 && request->_tempObject == NULL && total < _maxContentLength) {
|
|
||||||
request->_tempObject = malloc(total);
|
|
||||||
}
|
|
||||||
if (request->_tempObject != NULL) {
|
|
||||||
memcpy((uint8_t*)(request->_tempObject) + index, data, len);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // ASYNC_MSG_PACK_SUPPORT
|
|
@ -1,102 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
/*
|
|
||||||
server.on("/msg_pack", HTTP_ANY, [](AsyncWebServerRequest * request) {
|
|
||||||
AsyncMessagePackResponse * response = new AsyncMessagePackResponse();
|
|
||||||
JsonObject& root = response->getRoot();
|
|
||||||
root["key1"] = "key number one";
|
|
||||||
JsonObject& nested = root.createNestedObject("nested");
|
|
||||||
nested["key1"] = "key number one";
|
|
||||||
response->setLength();
|
|
||||||
request->send(response);
|
|
||||||
});
|
|
||||||
|
|
||||||
--------------------
|
|
||||||
|
|
||||||
AsyncCallbackMessagePackWebHandler* handler = new AsyncCallbackMessagePackWebHandler("/msg_pack/endpoint");
|
|
||||||
handler->onRequest([](AsyncWebServerRequest *request, JsonVariant &json) {
|
|
||||||
JsonObject jsonObj = json.as<JsonObject>();
|
|
||||||
// ...
|
|
||||||
});
|
|
||||||
server.addHandler(handler);
|
|
||||||
*/
|
|
||||||
|
|
||||||
#if __has_include("ArduinoJson.h")
|
|
||||||
#include <ArduinoJson.h>
|
|
||||||
#if ARDUINOJSON_VERSION_MAJOR >= 6
|
|
||||||
#define ASYNC_MSG_PACK_SUPPORT 1
|
|
||||||
#else
|
|
||||||
#define ASYNC_MSG_PACK_SUPPORT 0
|
|
||||||
#endif // ARDUINOJSON_VERSION_MAJOR >= 6
|
|
||||||
#endif // __has_include("ArduinoJson.h")
|
|
||||||
|
|
||||||
#if ASYNC_MSG_PACK_SUPPORT == 1
|
|
||||||
#include <ESPAsyncWebServer.h>
|
|
||||||
|
|
||||||
#include "ChunkPrint.h"
|
|
||||||
|
|
||||||
#if ARDUINOJSON_VERSION_MAJOR == 6
|
|
||||||
#ifndef DYNAMIC_JSON_DOCUMENT_SIZE
|
|
||||||
#define DYNAMIC_JSON_DOCUMENT_SIZE 1024
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
class AsyncMessagePackResponse : public AsyncAbstractResponse {
|
|
||||||
protected:
|
|
||||||
#if ARDUINOJSON_VERSION_MAJOR == 6
|
|
||||||
DynamicJsonDocument _jsonBuffer;
|
|
||||||
#else
|
|
||||||
JsonDocument _jsonBuffer;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
JsonVariant _root;
|
|
||||||
bool _isValid;
|
|
||||||
|
|
||||||
public:
|
|
||||||
#if ARDUINOJSON_VERSION_MAJOR == 6
|
|
||||||
AsyncMessagePackResponse(bool isArray = false, size_t maxJsonBufferSize = DYNAMIC_JSON_DOCUMENT_SIZE);
|
|
||||||
#else
|
|
||||||
AsyncMessagePackResponse(bool isArray = false);
|
|
||||||
#endif
|
|
||||||
JsonVariant& getRoot() { return _root; }
|
|
||||||
bool _sourceValid() const { return _isValid; }
|
|
||||||
size_t setLength();
|
|
||||||
size_t getSize() const { return _jsonBuffer.size(); }
|
|
||||||
size_t _fillBuffer(uint8_t* data, size_t len);
|
|
||||||
#if ARDUINOJSON_VERSION_MAJOR >= 6
|
|
||||||
bool overflowed() const { return _jsonBuffer.overflowed(); }
|
|
||||||
#endif
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef std::function<void(AsyncWebServerRequest* request, JsonVariant& json)> ArMessagePackRequestHandlerFunction;
|
|
||||||
|
|
||||||
class AsyncCallbackMessagePackWebHandler : public AsyncWebHandler {
|
|
||||||
protected:
|
|
||||||
String _uri;
|
|
||||||
WebRequestMethodComposite _method;
|
|
||||||
ArMessagePackRequestHandlerFunction _onRequest;
|
|
||||||
size_t _contentLength;
|
|
||||||
#if ARDUINOJSON_VERSION_MAJOR == 6
|
|
||||||
size_t maxJsonBufferSize;
|
|
||||||
#endif
|
|
||||||
size_t _maxContentLength;
|
|
||||||
|
|
||||||
public:
|
|
||||||
#if ARDUINOJSON_VERSION_MAJOR == 6
|
|
||||||
AsyncCallbackMessagePackWebHandler(const String& uri, ArMessagePackRequestHandlerFunction onRequest = nullptr, size_t maxJsonBufferSize = DYNAMIC_JSON_DOCUMENT_SIZE);
|
|
||||||
#else
|
|
||||||
AsyncCallbackMessagePackWebHandler(const String& uri, ArMessagePackRequestHandlerFunction onRequest = nullptr);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void setMethod(WebRequestMethodComposite method) { _method = method; }
|
|
||||||
void setMaxContentLength(int maxContentLength) { _maxContentLength = maxContentLength; }
|
|
||||||
void onRequest(ArMessagePackRequestHandlerFunction fn) { _onRequest = fn; }
|
|
||||||
|
|
||||||
virtual bool canHandle(AsyncWebServerRequest* request) override final;
|
|
||||||
virtual void handleRequest(AsyncWebServerRequest* request) override final;
|
|
||||||
virtual void handleUpload(__unused AsyncWebServerRequest* request, __unused const String& filename, __unused size_t index, __unused uint8_t* data, __unused size_t len, __unused bool final) override final {}
|
|
||||||
virtual void handleBody(AsyncWebServerRequest* request, uint8_t* data, size_t len, size_t index, size_t total) override final;
|
|
||||||
virtual bool isRequestHandlerTrivial() override final { return !_onRequest; }
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // ASYNC_MSG_PACK_SUPPORT == 1
|
|
@ -1,22 +0,0 @@
|
|||||||
#include <ESPAsyncWebServer.h>
|
|
||||||
|
|
||||||
AsyncWebHeader::AsyncWebHeader(const String& data) {
|
|
||||||
if (!data)
|
|
||||||
return;
|
|
||||||
int index = data.indexOf(':');
|
|
||||||
if (index < 0)
|
|
||||||
return;
|
|
||||||
_name = data.substring(0, index);
|
|
||||||
_value = data.substring(index + 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
String AsyncWebHeader::toString() const {
|
|
||||||
String str;
|
|
||||||
str.reserve(_name.length() + _value.length() + 2);
|
|
||||||
str.concat(_name);
|
|
||||||
str.concat((char)0x3a);
|
|
||||||
str.concat((char)0x20);
|
|
||||||
str.concat(_value);
|
|
||||||
str.concat(asyncsrv::T_rn);
|
|
||||||
return str;
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
@ -22,45 +22,34 @@
|
|||||||
#define ASYNCWEBSOCKET_H_
|
#define ASYNCWEBSOCKET_H_
|
||||||
|
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#ifdef ESP32
|
#if defined(ESP32) || defined(LIBRETINY)
|
||||||
#include <AsyncTCP.h>
|
#include <AsyncTCP.h>
|
||||||
#include <mutex>
|
#ifndef WS_MAX_QUEUED_MESSAGES
|
||||||
#ifndef WS_MAX_QUEUED_MESSAGES
|
#define WS_MAX_QUEUED_MESSAGES 32
|
||||||
#define WS_MAX_QUEUED_MESSAGES 32
|
#endif
|
||||||
#endif
|
#else
|
||||||
#elif defined(ESP8266)
|
#include <ESPAsyncTCP.h>
|
||||||
#include <ESPAsyncTCP.h>
|
#ifndef WS_MAX_QUEUED_MESSAGES
|
||||||
#ifndef WS_MAX_QUEUED_MESSAGES
|
#define WS_MAX_QUEUED_MESSAGES 8
|
||||||
#define WS_MAX_QUEUED_MESSAGES 8
|
#endif
|
||||||
#endif
|
|
||||||
#elif defined(TARGET_RP2040)
|
|
||||||
#include <AsyncTCP_RP2040W.h>
|
|
||||||
#ifndef WS_MAX_QUEUED_MESSAGES
|
|
||||||
#define WS_MAX_QUEUED_MESSAGES 32
|
|
||||||
#endif
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <ESPAsyncWebServer.h>
|
#include <ESPAsyncWebServer.h>
|
||||||
|
|
||||||
#include <memory>
|
#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_WS_CLIENTS
|
#if defined(ESP32) || defined(LIBRETINY)
|
||||||
#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
|
||||||
|
|
||||||
using AsyncWebSocketSharedBuffer = std::shared_ptr<std::vector<uint8_t>>;
|
|
||||||
|
|
||||||
class AsyncWebSocket;
|
class AsyncWebSocket;
|
||||||
class AsyncWebSocketResponse;
|
class AsyncWebSocketResponse;
|
||||||
class AsyncWebSocketClient;
|
class AsyncWebSocketClient;
|
||||||
@ -90,73 +79,94 @@ typedef struct {
|
|||||||
uint64_t index;
|
uint64_t index;
|
||||||
} AwsFrameInfo;
|
} AwsFrameInfo;
|
||||||
|
|
||||||
typedef enum { WS_DISCONNECTED,
|
typedef enum { WS_DISCONNECTED, WS_CONNECTED, WS_DISCONNECTING } AwsClientStatus;
|
||||||
WS_CONNECTED,
|
typedef enum { WS_CONTINUATION, WS_TEXT, WS_BINARY, WS_DISCONNECT = 0x08, WS_PING, WS_PONG } AwsFrameType;
|
||||||
WS_DISCONNECTING } AwsClientStatus;
|
typedef enum { WS_MSG_SENDING, WS_MSG_SENT, WS_MSG_ERROR } AwsMessageStatus;
|
||||||
typedef enum { WS_CONTINUATION,
|
typedef enum { WS_EVT_CONNECT, WS_EVT_DISCONNECT, WS_EVT_PONG, WS_EVT_ERROR, WS_EVT_DATA } AwsEventType;
|
||||||
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 AsyncWebSocketClient;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
AsyncWebSocketSharedBuffer _buffer;
|
uint8_t * _data;
|
||||||
|
size_t _len;
|
||||||
|
bool _lock;
|
||||||
|
uint32_t _count;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
AsyncWebSocketMessageBuffer() {}
|
AsyncWebSocketMessageBuffer();
|
||||||
explicit AsyncWebSocketMessageBuffer(size_t size);
|
AsyncWebSocketMessageBuffer(size_t size);
|
||||||
AsyncWebSocketMessageBuffer(const uint8_t* data, size_t size);
|
AsyncWebSocketMessageBuffer(uint8_t * data, size_t size);
|
||||||
//~AsyncWebSocketMessageBuffer();
|
AsyncWebSocketMessageBuffer(const AsyncWebSocketMessageBuffer &);
|
||||||
|
AsyncWebSocketMessageBuffer(AsyncWebSocketMessageBuffer &&);
|
||||||
|
~AsyncWebSocketMessageBuffer();
|
||||||
|
void operator ++(int i) { (void)i; _count++; }
|
||||||
|
void operator --(int i) { (void)i; if (_count > 0) { _count--; } ; }
|
||||||
bool reserve(size_t size);
|
bool reserve(size_t size);
|
||||||
uint8_t* get() { return _buffer->data(); }
|
void lock() { _lock = true; }
|
||||||
size_t length() const { return _buffer->size(); }
|
void unlock() { _lock = false; }
|
||||||
|
uint8_t * get() { return _data; }
|
||||||
|
size_t length() { return _len; }
|
||||||
|
uint32_t count() { return _count; }
|
||||||
|
bool canDelete() { return (!_count && !_lock); }
|
||||||
|
|
||||||
|
friend AsyncWebSocket;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class AsyncWebSocketMessage {
|
class AsyncWebSocketMessage {
|
||||||
private:
|
protected:
|
||||||
AsyncWebSocketSharedBuffer _WSbuffer;
|
uint8_t _opcode;
|
||||||
uint8_t _opcode{WS_TEXT};
|
bool _mask;
|
||||||
bool _mask{false};
|
AwsMessageStatus _status;
|
||||||
AwsMessageStatus _status{WS_MSG_ERROR};
|
|
||||||
size_t _sent{};
|
|
||||||
size_t _ack{};
|
|
||||||
size_t _acked{};
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
AsyncWebSocketMessage(AsyncWebSocketSharedBuffer buffer, uint8_t opcode = WS_TEXT, bool mask = false);
|
AsyncWebSocketMessage():_opcode(WS_TEXT),_mask(false),_status(WS_MSG_ERROR){}
|
||||||
|
virtual ~AsyncWebSocketMessage(){}
|
||||||
|
virtual void ack(size_t len __attribute__((unused)), uint32_t time __attribute__((unused))){}
|
||||||
|
virtual size_t send(AsyncClient *client __attribute__((unused))){ return 0; }
|
||||||
|
virtual bool finished(){ return _status != WS_MSG_SENDING; }
|
||||||
|
virtual bool betweenFrames() const { return false; }
|
||||||
|
};
|
||||||
|
|
||||||
bool finished() const { return _status != WS_MSG_SENDING; }
|
class AsyncWebSocketBasicMessage: public AsyncWebSocketMessage {
|
||||||
bool betweenFrames() const { return _acked == _ack; }
|
private:
|
||||||
|
size_t _len;
|
||||||
|
size_t _sent;
|
||||||
|
size_t _ack;
|
||||||
|
size_t _acked;
|
||||||
|
uint8_t * _data;
|
||||||
|
public:
|
||||||
|
AsyncWebSocketBasicMessage(const char * data, size_t len, uint8_t opcode=WS_TEXT, bool mask=false);
|
||||||
|
AsyncWebSocketBasicMessage(uint8_t opcode=WS_TEXT, bool mask=false);
|
||||||
|
virtual ~AsyncWebSocketBasicMessage() override;
|
||||||
|
virtual bool betweenFrames() const override { return _acked == _ack; }
|
||||||
|
virtual void ack(size_t len, uint32_t time) override ;
|
||||||
|
virtual size_t send(AsyncClient *client) override ;
|
||||||
|
};
|
||||||
|
|
||||||
void ack(size_t len, uint32_t time);
|
class AsyncWebSocketMultiMessage: public AsyncWebSocketMessage {
|
||||||
size_t send(AsyncClient* client);
|
private:
|
||||||
|
uint8_t * _data;
|
||||||
|
size_t _len;
|
||||||
|
size_t _sent;
|
||||||
|
size_t _ack;
|
||||||
|
size_t _acked;
|
||||||
|
AsyncWebSocketMessageBuffer * _WSbuffer;
|
||||||
|
public:
|
||||||
|
AsyncWebSocketMultiMessage(AsyncWebSocketMessageBuffer * buffer, uint8_t opcode=WS_TEXT, bool mask=false);
|
||||||
|
virtual ~AsyncWebSocketMultiMessage() override;
|
||||||
|
virtual bool betweenFrames() const override { return _acked == _ack; }
|
||||||
|
virtual void ack(size_t len, uint32_t time) override ;
|
||||||
|
virtual size_t send(AsyncClient *client) override ;
|
||||||
};
|
};
|
||||||
|
|
||||||
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
|
|
||||||
mutable std::mutex _lock;
|
LinkedList<AsyncWebSocketControl *> _controlQueue;
|
||||||
#endif
|
LinkedList<AsyncWebSocketMessage *> _messageQueue;
|
||||||
std::deque<AsyncWebSocketControl> _controlQueue;
|
|
||||||
std::deque<AsyncWebSocketMessage> _messageQueue;
|
|
||||||
bool closeWhenFull = true;
|
|
||||||
|
|
||||||
uint8_t _pstate;
|
uint8_t _pstate;
|
||||||
AwsFrameInfo _pinfo;
|
AwsFrameInfo _pinfo;
|
||||||
@ -164,207 +174,181 @@ 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 _queueMessage(AsyncWebSocketMessage *dataMessage);
|
||||||
void _queueMessage(AsyncWebSocketSharedBuffer buffer, uint8_t opcode = WS_TEXT, bool mask = false);
|
void _queueControl(AsyncWebSocketControl *controlMessage);
|
||||||
void _runQueue();
|
void _runQueue();
|
||||||
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(){ return _clientId; }
|
||||||
AwsClientStatus status() const { return _status; }
|
AwsClientStatus status(){ return _status; }
|
||||||
AsyncClient* client() { return _client; }
|
AsyncClient* client(){ return _client; }
|
||||||
const AsyncClient* client() const { return _client; }
|
AsyncWebSocket *server(){ return _server; }
|
||||||
AsyncWebSocket* server() { return _server; }
|
AwsFrameInfo const &pinfo() const { return _pinfo; }
|
||||||
const AsyncWebSocket* server() const { return _server; }
|
|
||||||
AwsFrameInfo const& pinfo() const { return _pinfo; }
|
|
||||||
|
|
||||||
// - If "true" (default), the connection will be closed if the message queue is full.
|
IPAddress remoteIP();
|
||||||
// This is the default behavior in yubox-node-org, which is not silently discarding messages but instead closes the connection.
|
uint16_t remotePort();
|
||||||
// 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.
|
|
||||||
//
|
|
||||||
// - 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 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.
|
|
||||||
// - IT is recommended to use the methods queueIsFull(), availableForWriteAll(), availableForWrite(clientId) to check if the queue is full before sending a message.
|
|
||||||
//
|
|
||||||
// Usage:
|
|
||||||
// - can be set in the onEvent listener when connecting (event type is: WS_EVT_CONNECT)
|
|
||||||
//
|
|
||||||
// Use cases:,
|
|
||||||
// - 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.
|
|
||||||
void setCloseClientOnQueueFull(bool close) { closeWhenFull = close; }
|
|
||||||
bool willCloseClientOnQueueFull() const { return closeWhenFull; }
|
|
||||||
|
|
||||||
IPAddress remoteIP() const;
|
//control frames
|
||||||
uint16_t remotePort() const;
|
void close(uint16_t code=0, const char * message=NULL);
|
||||||
|
void ping(uint8_t *data=NULL, size_t len=0);
|
||||||
|
|
||||||
bool shouldBeDeleted() const { return !_client; }
|
//set auto-ping period in seconds. disabled if zero (default)
|
||||||
|
void keepAlivePeriod(uint16_t seconds){
|
||||||
// control frames
|
|
||||||
void close(uint16_t code = 0, const char* message = NULL);
|
|
||||||
void ping(const uint8_t* data = NULL, size_t len = 0);
|
|
||||||
|
|
||||||
// set auto-ping period in seconds. disabled if zero (default)
|
|
||||||
void keepAlivePeriod(uint16_t seconds) {
|
|
||||||
_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(AsyncWebSocketMessage *message){ _queueMessage(message); }
|
||||||
bool queueIsFull() const;
|
bool queueIsFull();
|
||||||
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
|
||||||
|
size_t printf_P(PGM_P formatP, ...) __attribute__ ((format (printf, 2, 3)));
|
||||||
|
#endif
|
||||||
|
void text(const char * message, size_t len);
|
||||||
|
void text(const char * message);
|
||||||
|
void text(uint8_t * message, size_t len);
|
||||||
|
void text(char * message);
|
||||||
|
void text(const String &message);
|
||||||
|
void text(const __FlashStringHelper *data);
|
||||||
|
void text(AsyncWebSocketMessageBuffer *buffer);
|
||||||
|
|
||||||
void text(AsyncWebSocketSharedBuffer buffer);
|
void binary(const char * message, size_t len);
|
||||||
void text(const uint8_t* message, size_t len);
|
void binary(const char * message);
|
||||||
void text(const char* message, size_t len);
|
void binary(uint8_t * message, size_t len);
|
||||||
void text(const char* message);
|
void binary(char * message);
|
||||||
void text(const String& message);
|
void binary(const String &message);
|
||||||
void text(AsyncWebSocketMessageBuffer* buffer);
|
void binary(const __FlashStringHelper *data, size_t len);
|
||||||
|
void binary(AsyncWebSocketMessageBuffer *buffer);
|
||||||
|
|
||||||
void binary(AsyncWebSocketSharedBuffer buffer);
|
bool canSend() { return _messageQueue.length() < WS_MAX_QUEUED_MESSAGES; }
|
||||||
void binary(const uint8_t* message, size_t len);
|
|
||||||
void binary(const char* message, size_t len);
|
|
||||||
void binary(const char* message);
|
|
||||||
void binary(const String& message);
|
|
||||||
void binary(AsyncWebSocketMessageBuffer* buffer);
|
|
||||||
|
|
||||||
bool 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);
|
||||||
|
|
||||||
#ifdef ESP8266
|
|
||||||
size_t printf_P(PGM_P formatP, ...) __attribute__((format(printf, 2, 3)));
|
|
||||||
void text(const __FlashStringHelper* message);
|
|
||||||
void binary(const __FlashStringHelper* message, size_t len);
|
|
||||||
#endif
|
|
||||||
};
|
};
|
||||||
|
|
||||||
using AwsHandshakeHandler = std::function<bool(AsyncWebServerRequest* request)>;
|
typedef std::function<void(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventType type, void * arg, uint8_t *data, size_t len)> AwsEventHandler;
|
||||||
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 {
|
||||||
|
public:
|
||||||
|
typedef LinkedList<AsyncWebSocketClient *> AsyncWebSocketClientLinkedList;
|
||||||
private:
|
private:
|
||||||
String _url;
|
String _url;
|
||||||
std::list<AsyncWebSocketClient> _clients;
|
AsyncWebSocketClientLinkedList _clients;
|
||||||
uint32_t _cNextId;
|
uint32_t _cNextId;
|
||||||
AwsEventHandler _eventHandler{nullptr};
|
AwsEventHandler _eventHandler;
|
||||||
AwsHandshakeHandler _handshakeHandler;
|
|
||||||
bool _enabled;
|
bool _enabled;
|
||||||
#ifdef ESP32
|
AsyncWebLock _lock;
|
||||||
mutable std::mutex _lock;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit AsyncWebSocket(const char* url) : _url(url), _cNextId(1), _enabled(true) {}
|
AsyncWebSocket(const String& url);
|
||||||
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) != NULL; }
|
||||||
|
|
||||||
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, uint8_t *data=NULL, size_t len=0);
|
||||||
void pingAll(const uint8_t* data = NULL, size_t len = 0); // done
|
void pingAll(uint8_t *data=NULL, size_t len=0); // done
|
||||||
|
|
||||||
void text(uint32_t id, const uint8_t* message, size_t len);
|
void text(uint32_t id, const char * message, size_t len);
|
||||||
void text(uint32_t id, const char* message, size_t len);
|
void text(uint32_t id, const char * message);
|
||||||
void text(uint32_t id, const char* message);
|
void text(uint32_t id, uint8_t * message, size_t len);
|
||||||
void text(uint32_t id, const String& message);
|
void text(uint32_t id, char * message);
|
||||||
void text(uint32_t id, AsyncWebSocketMessageBuffer* buffer);
|
void text(uint32_t id, const String &message);
|
||||||
void text(uint32_t id, AsyncWebSocketSharedBuffer buffer);
|
void text(uint32_t id, const __FlashStringHelper *message);
|
||||||
|
|
||||||
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(uint8_t * message, size_t len);
|
||||||
void textAll(const String& message);
|
void textAll(char * message);
|
||||||
void textAll(AsyncWebSocketMessageBuffer* buffer);
|
void textAll(const String &message);
|
||||||
void textAll(AsyncWebSocketSharedBuffer buffer);
|
void textAll(const __FlashStringHelper *message); // need to convert
|
||||||
|
void textAll(AsyncWebSocketMessageBuffer * buffer);
|
||||||
|
|
||||||
void binary(uint32_t id, const uint8_t* message, size_t len);
|
void binary(uint32_t id, const char * message, size_t len);
|
||||||
void binary(uint32_t id, const char* message, size_t len);
|
void binary(uint32_t id, const char * message);
|
||||||
void binary(uint32_t id, const char* message);
|
void binary(uint32_t id, uint8_t * message, size_t len);
|
||||||
void binary(uint32_t id, const String& message);
|
void binary(uint32_t id, char * message);
|
||||||
void binary(uint32_t id, AsyncWebSocketMessageBuffer* buffer);
|
void binary(uint32_t id, const String &message);
|
||||||
void binary(uint32_t id, AsyncWebSocketSharedBuffer buffer);
|
void binary(uint32_t id, const __FlashStringHelper *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(uint8_t * message, size_t len);
|
||||||
void binaryAll(const String& message);
|
void binaryAll(char * message);
|
||||||
void binaryAll(AsyncWebSocketMessageBuffer* buffer);
|
void binaryAll(const String &message);
|
||||||
void binaryAll(AsyncWebSocketSharedBuffer buffer);
|
void binaryAll(const __FlashStringHelper *message, size_t len);
|
||||||
|
void binaryAll(AsyncWebSocketMessageBuffer * buffer);
|
||||||
|
|
||||||
size_t printf(uint32_t id, const char* format, ...) __attribute__((format(printf, 3, 4)));
|
void message(uint32_t id, AsyncWebSocketMessage *message);
|
||||||
size_t printfAll(const char* format, ...) __attribute__((format(printf, 2, 3)));
|
void messageAll(AsyncWebSocketMultiMessage *message);
|
||||||
|
|
||||||
#ifdef ESP8266
|
size_t printf(uint32_t id, const char *format, ...) __attribute__ ((format (printf, 3, 4)));
|
||||||
void text(uint32_t id, const __FlashStringHelper* message);
|
size_t printfAll(const char *format, ...) __attribute__ ((format (printf, 2, 3)));
|
||||||
void textAll(const __FlashStringHelper* message);
|
#ifndef ESP32
|
||||||
void binary(uint32_t id, const __FlashStringHelper* message, size_t len);
|
size_t printf_P(uint32_t id, PGM_P formatP, ...) __attribute__ ((format (printf, 3, 4)));
|
||||||
void binaryAll(const __FlashStringHelper* message, size_t len);
|
|
||||||
size_t printf_P(uint32_t id, PGM_P formatP, ...) __attribute__((format(printf, 3, 4)));
|
|
||||||
size_t printfAll_P(PGM_P formatP, ...) __attribute__((format(printf, 2, 3)));
|
|
||||||
#endif
|
#endif
|
||||||
|
size_t printfAll_P(PGM_P formatP, ...) __attribute__ ((format (printf, 2, 3)));
|
||||||
|
|
||||||
void onEvent(AwsEventHandler handler) { _eventHandler = handler; }
|
//event listener
|
||||||
void handleHandshake(AwsHandshakeHandler handler) { _handshakeHandler = handler; }
|
void onEvent(AwsEventHandler handler){
|
||||||
|
_eventHandler = handler;
|
||||||
|
}
|
||||||
|
|
||||||
|
//system callbacks (do not call)
|
||||||
|
uint32_t _getNextId(){ return _cNextId++; }
|
||||||
|
void _addClient(AsyncWebSocketClient * client);
|
||||||
|
void _handleDisconnect(AsyncWebSocketClient * client);
|
||||||
|
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(uint8_t * data, size_t size);
|
||||||
|
LinkedList<AsyncWebSocketMessageBuffer *> _buffers;
|
||||||
|
void _cleanBuffers();
|
||||||
|
|
||||||
std::list<AsyncWebSocketClient>& getClients() { return _clients; }
|
AsyncWebSocketClientLinkedList getClients() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
// 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_ */
|
||||||
|
87
src/AsyncWebSynchronization.h
Normal file
87
src/AsyncWebSynchronization.h
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
#ifndef ASYNCWEBSYNCHRONIZATION_H_
|
||||||
|
#define ASYNCWEBSYNCHRONIZATION_H_
|
||||||
|
|
||||||
|
// Synchronisation is only available on ESP32, as the ESP8266 isn't using FreeRTOS by default
|
||||||
|
|
||||||
|
#include <ESPAsyncWebServer.h>
|
||||||
|
|
||||||
|
#if defined(ESP32) || (defined(LIBRETINY) && LT_HAS_FREERTOS)
|
||||||
|
|
||||||
|
// This is the ESP32 version of the Sync Lock, using the FreeRTOS Semaphore
|
||||||
|
class AsyncWebLock
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
SemaphoreHandle_t _lock;
|
||||||
|
mutable void *_lockedBy;
|
||||||
|
|
||||||
|
public:
|
||||||
|
AsyncWebLock() {
|
||||||
|
_lock = xSemaphoreCreateBinary();
|
||||||
|
_lockedBy = NULL;
|
||||||
|
xSemaphoreGive(_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
~AsyncWebLock() {
|
||||||
|
vSemaphoreDelete(_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool lock() const {
|
||||||
|
extern void *pxCurrentTCB;
|
||||||
|
if (_lockedBy != pxCurrentTCB) {
|
||||||
|
xSemaphoreTake(_lock, portMAX_DELAY);
|
||||||
|
_lockedBy = pxCurrentTCB;
|
||||||
|
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 {
|
||||||
|
}
|
||||||
|
};
|
||||||
|
#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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // ASYNCWEBSYNCHRONIZATION_H_
|
@ -1,284 +0,0 @@
|
|||||||
/*
|
|
||||||
* FIPS-180-1 compliant SHA-1 implementation
|
|
||||||
*
|
|
||||||
* Copyright (C) 2006-2015, ARM Limited, All Rights Reserved
|
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
||||||
* not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*
|
|
||||||
* This file is part of mbed TLS (https://tls.mbed.org)
|
|
||||||
* Modified for esp32 by Lucas Saavedra Vaz on 11 Jan 2024
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <Arduino.h>
|
|
||||||
#if ESP_IDF_VERSION_MAJOR < 5
|
|
||||||
|
|
||||||
#include "BackPort_SHA1Builder.h"
|
|
||||||
|
|
||||||
// 32-bit integer manipulation macros (big endian)
|
|
||||||
|
|
||||||
#ifndef GET_UINT32_BE
|
|
||||||
#define GET_UINT32_BE(n, b, i) \
|
|
||||||
{ (n) = ((uint32_t)(b)[(i)] << 24) | ((uint32_t)(b)[(i) + 1] << 16) | ((uint32_t)(b)[(i) + 2] << 8) | ((uint32_t)(b)[(i) + 3]); }
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef PUT_UINT32_BE
|
|
||||||
#define PUT_UINT32_BE(n, b, i) \
|
|
||||||
{ \
|
|
||||||
(b)[(i)] = (uint8_t)((n) >> 24); \
|
|
||||||
(b)[(i) + 1] = (uint8_t)((n) >> 16); \
|
|
||||||
(b)[(i) + 2] = (uint8_t)((n) >> 8); \
|
|
||||||
(b)[(i) + 3] = (uint8_t)((n)); \
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Constants
|
|
||||||
|
|
||||||
static const uint8_t sha1_padding[64] = {0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
|
|
||||||
|
|
||||||
// Private methods
|
|
||||||
|
|
||||||
void SHA1Builder::process(const uint8_t *data) {
|
|
||||||
uint32_t temp, W[16], A, B, C, D, E;
|
|
||||||
|
|
||||||
GET_UINT32_BE(W[0], data, 0);
|
|
||||||
GET_UINT32_BE(W[1], data, 4);
|
|
||||||
GET_UINT32_BE(W[2], data, 8);
|
|
||||||
GET_UINT32_BE(W[3], data, 12);
|
|
||||||
GET_UINT32_BE(W[4], data, 16);
|
|
||||||
GET_UINT32_BE(W[5], data, 20);
|
|
||||||
GET_UINT32_BE(W[6], data, 24);
|
|
||||||
GET_UINT32_BE(W[7], data, 28);
|
|
||||||
GET_UINT32_BE(W[8], data, 32);
|
|
||||||
GET_UINT32_BE(W[9], data, 36);
|
|
||||||
GET_UINT32_BE(W[10], data, 40);
|
|
||||||
GET_UINT32_BE(W[11], data, 44);
|
|
||||||
GET_UINT32_BE(W[12], data, 48);
|
|
||||||
GET_UINT32_BE(W[13], data, 52);
|
|
||||||
GET_UINT32_BE(W[14], data, 56);
|
|
||||||
GET_UINT32_BE(W[15], data, 60);
|
|
||||||
|
|
||||||
#define sha1_S(x, n) ((x << n) | ((x & 0xFFFFFFFF) >> (32 - n)))
|
|
||||||
|
|
||||||
#define sha1_R(t) (temp = W[(t - 3) & 0x0F] ^ W[(t - 8) & 0x0F] ^ W[(t - 14) & 0x0F] ^ W[t & 0x0F], (W[t & 0x0F] = sha1_S(temp, 1)))
|
|
||||||
|
|
||||||
#define sha1_P(a, b, c, d, e, x) \
|
|
||||||
{ \
|
|
||||||
e += sha1_S(a, 5) + sha1_F(b, c, d) + sha1_K + x; \
|
|
||||||
b = sha1_S(b, 30); \
|
|
||||||
}
|
|
||||||
|
|
||||||
A = state[0];
|
|
||||||
B = state[1];
|
|
||||||
C = state[2];
|
|
||||||
D = state[3];
|
|
||||||
E = state[4];
|
|
||||||
|
|
||||||
#define sha1_F(x, y, z) (z ^ (x & (y ^ z)))
|
|
||||||
#define sha1_K 0x5A827999
|
|
||||||
|
|
||||||
sha1_P(A, B, C, D, E, W[0]);
|
|
||||||
sha1_P(E, A, B, C, D, W[1]);
|
|
||||||
sha1_P(D, E, A, B, C, W[2]);
|
|
||||||
sha1_P(C, D, E, A, B, W[3]);
|
|
||||||
sha1_P(B, C, D, E, A, W[4]);
|
|
||||||
sha1_P(A, B, C, D, E, W[5]);
|
|
||||||
sha1_P(E, A, B, C, D, W[6]);
|
|
||||||
sha1_P(D, E, A, B, C, W[7]);
|
|
||||||
sha1_P(C, D, E, A, B, W[8]);
|
|
||||||
sha1_P(B, C, D, E, A, W[9]);
|
|
||||||
sha1_P(A, B, C, D, E, W[10]);
|
|
||||||
sha1_P(E, A, B, C, D, W[11]);
|
|
||||||
sha1_P(D, E, A, B, C, W[12]);
|
|
||||||
sha1_P(C, D, E, A, B, W[13]);
|
|
||||||
sha1_P(B, C, D, E, A, W[14]);
|
|
||||||
sha1_P(A, B, C, D, E, W[15]);
|
|
||||||
sha1_P(E, A, B, C, D, sha1_R(16));
|
|
||||||
sha1_P(D, E, A, B, C, sha1_R(17));
|
|
||||||
sha1_P(C, D, E, A, B, sha1_R(18));
|
|
||||||
sha1_P(B, C, D, E, A, sha1_R(19));
|
|
||||||
|
|
||||||
#undef sha1_K
|
|
||||||
#undef sha1_F
|
|
||||||
|
|
||||||
#define sha1_F(x, y, z) (x ^ y ^ z)
|
|
||||||
#define sha1_K 0x6ED9EBA1
|
|
||||||
|
|
||||||
sha1_P(A, B, C, D, E, sha1_R(20));
|
|
||||||
sha1_P(E, A, B, C, D, sha1_R(21));
|
|
||||||
sha1_P(D, E, A, B, C, sha1_R(22));
|
|
||||||
sha1_P(C, D, E, A, B, sha1_R(23));
|
|
||||||
sha1_P(B, C, D, E, A, sha1_R(24));
|
|
||||||
sha1_P(A, B, C, D, E, sha1_R(25));
|
|
||||||
sha1_P(E, A, B, C, D, sha1_R(26));
|
|
||||||
sha1_P(D, E, A, B, C, sha1_R(27));
|
|
||||||
sha1_P(C, D, E, A, B, sha1_R(28));
|
|
||||||
sha1_P(B, C, D, E, A, sha1_R(29));
|
|
||||||
sha1_P(A, B, C, D, E, sha1_R(30));
|
|
||||||
sha1_P(E, A, B, C, D, sha1_R(31));
|
|
||||||
sha1_P(D, E, A, B, C, sha1_R(32));
|
|
||||||
sha1_P(C, D, E, A, B, sha1_R(33));
|
|
||||||
sha1_P(B, C, D, E, A, sha1_R(34));
|
|
||||||
sha1_P(A, B, C, D, E, sha1_R(35));
|
|
||||||
sha1_P(E, A, B, C, D, sha1_R(36));
|
|
||||||
sha1_P(D, E, A, B, C, sha1_R(37));
|
|
||||||
sha1_P(C, D, E, A, B, sha1_R(38));
|
|
||||||
sha1_P(B, C, D, E, A, sha1_R(39));
|
|
||||||
|
|
||||||
#undef sha1_K
|
|
||||||
#undef sha1_F
|
|
||||||
|
|
||||||
#define sha1_F(x, y, z) ((x & y) | (z & (x | y)))
|
|
||||||
#define sha1_K 0x8F1BBCDC
|
|
||||||
|
|
||||||
sha1_P(A, B, C, D, E, sha1_R(40));
|
|
||||||
sha1_P(E, A, B, C, D, sha1_R(41));
|
|
||||||
sha1_P(D, E, A, B, C, sha1_R(42));
|
|
||||||
sha1_P(C, D, E, A, B, sha1_R(43));
|
|
||||||
sha1_P(B, C, D, E, A, sha1_R(44));
|
|
||||||
sha1_P(A, B, C, D, E, sha1_R(45));
|
|
||||||
sha1_P(E, A, B, C, D, sha1_R(46));
|
|
||||||
sha1_P(D, E, A, B, C, sha1_R(47));
|
|
||||||
sha1_P(C, D, E, A, B, sha1_R(48));
|
|
||||||
sha1_P(B, C, D, E, A, sha1_R(49));
|
|
||||||
sha1_P(A, B, C, D, E, sha1_R(50));
|
|
||||||
sha1_P(E, A, B, C, D, sha1_R(51));
|
|
||||||
sha1_P(D, E, A, B, C, sha1_R(52));
|
|
||||||
sha1_P(C, D, E, A, B, sha1_R(53));
|
|
||||||
sha1_P(B, C, D, E, A, sha1_R(54));
|
|
||||||
sha1_P(A, B, C, D, E, sha1_R(55));
|
|
||||||
sha1_P(E, A, B, C, D, sha1_R(56));
|
|
||||||
sha1_P(D, E, A, B, C, sha1_R(57));
|
|
||||||
sha1_P(C, D, E, A, B, sha1_R(58));
|
|
||||||
sha1_P(B, C, D, E, A, sha1_R(59));
|
|
||||||
|
|
||||||
#undef sha1_K
|
|
||||||
#undef sha1_F
|
|
||||||
|
|
||||||
#define sha1_F(x, y, z) (x ^ y ^ z)
|
|
||||||
#define sha1_K 0xCA62C1D6
|
|
||||||
|
|
||||||
sha1_P(A, B, C, D, E, sha1_R(60));
|
|
||||||
sha1_P(E, A, B, C, D, sha1_R(61));
|
|
||||||
sha1_P(D, E, A, B, C, sha1_R(62));
|
|
||||||
sha1_P(C, D, E, A, B, sha1_R(63));
|
|
||||||
sha1_P(B, C, D, E, A, sha1_R(64));
|
|
||||||
sha1_P(A, B, C, D, E, sha1_R(65));
|
|
||||||
sha1_P(E, A, B, C, D, sha1_R(66));
|
|
||||||
sha1_P(D, E, A, B, C, sha1_R(67));
|
|
||||||
sha1_P(C, D, E, A, B, sha1_R(68));
|
|
||||||
sha1_P(B, C, D, E, A, sha1_R(69));
|
|
||||||
sha1_P(A, B, C, D, E, sha1_R(70));
|
|
||||||
sha1_P(E, A, B, C, D, sha1_R(71));
|
|
||||||
sha1_P(D, E, A, B, C, sha1_R(72));
|
|
||||||
sha1_P(C, D, E, A, B, sha1_R(73));
|
|
||||||
sha1_P(B, C, D, E, A, sha1_R(74));
|
|
||||||
sha1_P(A, B, C, D, E, sha1_R(75));
|
|
||||||
sha1_P(E, A, B, C, D, sha1_R(76));
|
|
||||||
sha1_P(D, E, A, B, C, sha1_R(77));
|
|
||||||
sha1_P(C, D, E, A, B, sha1_R(78));
|
|
||||||
sha1_P(B, C, D, E, A, sha1_R(79));
|
|
||||||
|
|
||||||
#undef sha1_K
|
|
||||||
#undef sha1_F
|
|
||||||
|
|
||||||
state[0] += A;
|
|
||||||
state[1] += B;
|
|
||||||
state[2] += C;
|
|
||||||
state[3] += D;
|
|
||||||
state[4] += E;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Public methods
|
|
||||||
|
|
||||||
void SHA1Builder::begin() {
|
|
||||||
total[0] = 0;
|
|
||||||
total[1] = 0;
|
|
||||||
|
|
||||||
state[0] = 0x67452301;
|
|
||||||
state[1] = 0xEFCDAB89;
|
|
||||||
state[2] = 0x98BADCFE;
|
|
||||||
state[3] = 0x10325476;
|
|
||||||
state[4] = 0xC3D2E1F0;
|
|
||||||
|
|
||||||
memset(buffer, 0x00, sizeof(buffer));
|
|
||||||
memset(hash, 0x00, sizeof(hash));
|
|
||||||
}
|
|
||||||
|
|
||||||
void SHA1Builder::add(const uint8_t *data, size_t len) {
|
|
||||||
size_t fill;
|
|
||||||
uint32_t left;
|
|
||||||
|
|
||||||
if (len == 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
left = total[0] & 0x3F;
|
|
||||||
fill = 64 - left;
|
|
||||||
|
|
||||||
total[0] += (uint32_t)len;
|
|
||||||
total[0] &= 0xFFFFFFFF;
|
|
||||||
|
|
||||||
if (total[0] < (uint32_t)len) {
|
|
||||||
total[1]++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (left && len >= fill) {
|
|
||||||
memcpy((void *)(buffer + left), data, fill);
|
|
||||||
process(buffer);
|
|
||||||
data += fill;
|
|
||||||
len -= fill;
|
|
||||||
left = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (len >= 64) {
|
|
||||||
process(data);
|
|
||||||
data += 64;
|
|
||||||
len -= 64;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (len > 0) {
|
|
||||||
memcpy((void *)(buffer + left), data, len);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SHA1Builder::calculate(void) {
|
|
||||||
uint32_t last, padn;
|
|
||||||
uint32_t high, low;
|
|
||||||
uint8_t msglen[8];
|
|
||||||
|
|
||||||
high = (total[0] >> 29) | (total[1] << 3);
|
|
||||||
low = (total[0] << 3);
|
|
||||||
|
|
||||||
PUT_UINT32_BE(high, msglen, 0);
|
|
||||||
PUT_UINT32_BE(low, msglen, 4);
|
|
||||||
|
|
||||||
last = total[0] & 0x3F;
|
|
||||||
padn = (last < 56) ? (56 - last) : (120 - last);
|
|
||||||
|
|
||||||
add((uint8_t *)sha1_padding, padn);
|
|
||||||
add(msglen, 8);
|
|
||||||
|
|
||||||
PUT_UINT32_BE(state[0], hash, 0);
|
|
||||||
PUT_UINT32_BE(state[1], hash, 4);
|
|
||||||
PUT_UINT32_BE(state[2], hash, 8);
|
|
||||||
PUT_UINT32_BE(state[3], hash, 12);
|
|
||||||
PUT_UINT32_BE(state[4], hash, 16);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SHA1Builder::getBytes(uint8_t *output) {
|
|
||||||
memcpy(output, hash, SHA1_HASH_SIZE);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // ESP_IDF_VERSION_MAJOR < 5
|
|
@ -1,44 +0,0 @@
|
|||||||
// Copyright 2024 Espressif Systems (Shanghai) PTE LTD
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
#include <Arduino.h>
|
|
||||||
#if ESP_IDF_VERSION_MAJOR < 5
|
|
||||||
|
|
||||||
#ifndef SHA1Builder_h
|
|
||||||
#define SHA1Builder_h
|
|
||||||
|
|
||||||
#include <Stream.h>
|
|
||||||
#include <WString.h>
|
|
||||||
|
|
||||||
#define SHA1_HASH_SIZE 20
|
|
||||||
|
|
||||||
class SHA1Builder {
|
|
||||||
private:
|
|
||||||
uint32_t total[2]; /* number of bytes processed */
|
|
||||||
uint32_t state[5]; /* intermediate digest state */
|
|
||||||
unsigned char buffer[64]; /* data block being processed */
|
|
||||||
uint8_t hash[SHA1_HASH_SIZE]; /* SHA-1 result */
|
|
||||||
|
|
||||||
void process(const uint8_t* data);
|
|
||||||
|
|
||||||
public:
|
|
||||||
void begin();
|
|
||||||
void add(const uint8_t* data, size_t len);
|
|
||||||
void calculate();
|
|
||||||
void getBytes(uint8_t* output);
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // SHA1Builder_h
|
|
||||||
|
|
||||||
#endif // ESP_IDF_VERSION_MAJOR < 5
|
|
@ -1,16 +0,0 @@
|
|||||||
#include <ChunkPrint.h>
|
|
||||||
|
|
||||||
ChunkPrint::ChunkPrint(uint8_t* destination, size_t from, size_t len)
|
|
||||||
: _destination(destination), _to_skip(from), _to_write(len), _pos{0} {}
|
|
||||||
|
|
||||||
size_t ChunkPrint::write(uint8_t c) {
|
|
||||||
if (_to_skip > 0) {
|
|
||||||
_to_skip--;
|
|
||||||
return 1;
|
|
||||||
} else if (_to_write > 0) {
|
|
||||||
_to_write--;
|
|
||||||
_destination[_pos++] = c;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
@ -1,19 +0,0 @@
|
|||||||
#ifndef CHUNKPRINT_H
|
|
||||||
#define CHUNKPRINT_H
|
|
||||||
|
|
||||||
#include <Print.h>
|
|
||||||
|
|
||||||
class ChunkPrint : public Print {
|
|
||||||
private:
|
|
||||||
uint8_t* _destination;
|
|
||||||
size_t _to_skip;
|
|
||||||
size_t _to_write;
|
|
||||||
size_t _pos;
|
|
||||||
|
|
||||||
public:
|
|
||||||
ChunkPrint(uint8_t* destination, size_t from, size_t len);
|
|
||||||
virtual ~ChunkPrint() {}
|
|
||||||
size_t write(uint8_t c);
|
|
||||||
size_t write(const uint8_t* buffer, size_t size) { return this->Print::write(buffer, size); }
|
|
||||||
};
|
|
||||||
#endif
|
|
@ -23,43 +23,29 @@
|
|||||||
|
|
||||||
#include "Arduino.h"
|
#include "Arduino.h"
|
||||||
|
|
||||||
#include "FS.h"
|
|
||||||
#include <algorithm>
|
|
||||||
#include <deque>
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <list>
|
#include "FS.h"
|
||||||
#include <unordered_map>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#ifdef ESP32
|
#include "StringArray.h"
|
||||||
#include <AsyncTCP.h>
|
|
||||||
#include <WiFi.h>
|
#if defined(ESP32) || defined(LIBRETINY)
|
||||||
|
#include <WiFi.h>
|
||||||
|
#include <AsyncTCP.h>
|
||||||
#elif defined(ESP8266)
|
#elif defined(ESP8266)
|
||||||
#include <ESP8266WiFi.h>
|
#include <ESP8266WiFi.h>
|
||||||
#include <ESPAsyncTCP.h>
|
#include <ESPAsyncTCP.h>
|
||||||
#elif defined(TARGET_RP2040)
|
|
||||||
#include <AsyncTCP_RP2040W.h>
|
|
||||||
#include <HTTP_Method.h>
|
|
||||||
#include <WiFi.h>
|
|
||||||
#include <http_parser.h>
|
|
||||||
#else
|
#else
|
||||||
#error Platform not supported
|
#error Platform not supported
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "literals.h"
|
|
||||||
|
|
||||||
#define ASYNCWEBSERVER_VERSION "3.3.7"
|
|
||||||
#define ASYNCWEBSERVER_VERSION_MAJOR 3
|
|
||||||
#define ASYNCWEBSERVER_VERSION_MINOR 3
|
|
||||||
#define ASYNCWEBSERVER_VERSION_REVISION 7
|
|
||||||
#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
|
||||||
|
|
||||||
|
#define DEBUGF(...) //Serial.printf(__VA_ARGS__)
|
||||||
|
|
||||||
class AsyncWebServer;
|
class AsyncWebServer;
|
||||||
class AsyncWebServerRequest;
|
class AsyncWebServerRequest;
|
||||||
class AsyncWebServerResponse;
|
class AsyncWebServerResponse;
|
||||||
@ -70,12 +56,8 @@ class AsyncWebHandler;
|
|||||||
class AsyncStaticWebHandler;
|
class AsyncStaticWebHandler;
|
||||||
class AsyncCallbackWebHandler;
|
class AsyncCallbackWebHandler;
|
||||||
class AsyncResponseStream;
|
class AsyncResponseStream;
|
||||||
class AsyncMiddlewareChain;
|
|
||||||
|
|
||||||
#if defined(TARGET_RP2040)
|
#ifndef WEBSERVER_H
|
||||||
typedef enum http_method WebRequestMethod;
|
|
||||||
#else
|
|
||||||
#ifndef WEBSERVER_H
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
HTTP_GET = 0b00000001,
|
HTTP_GET = 0b00000001,
|
||||||
HTTP_POST = 0b00000010,
|
HTTP_POST = 0b00000010,
|
||||||
@ -86,25 +68,10 @@ typedef enum {
|
|||||||
HTTP_OPTIONS = 0b01000000,
|
HTTP_OPTIONS = 0b01000000,
|
||||||
HTTP_ANY = 0b01111111,
|
HTTP_ANY = 0b01111111,
|
||||||
} WebRequestMethod;
|
} WebRequestMethod;
|
||||||
#endif
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef HAVE_FS_FILE_OPEN_MODE
|
//if this value is returned when asked for data, packet will not be sent and you will be asked for data again
|
||||||
namespace fs {
|
|
||||||
class FileOpenMode {
|
|
||||||
public:
|
|
||||||
static const char* read;
|
|
||||||
static const char* write;
|
|
||||||
static const char* append;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
#else
|
|
||||||
#include "FileOpenMode.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// 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
|
||||||
#define RESPONSE_STREAM_BUFFER_SIZE 1460
|
|
||||||
|
|
||||||
typedef uint8_t WebRequestMethodComposite;
|
typedef uint8_t WebRequestMethodComposite;
|
||||||
typedef std::function<void(void)> ArDisconnectHandler;
|
typedef std::function<void(void)> ArDisconnectHandler;
|
||||||
@ -122,7 +89,8 @@ 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; }
|
||||||
@ -140,38 +108,25 @@ class AsyncWebHeader {
|
|||||||
String _value;
|
String _value;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
AsyncWebHeader() = default;
|
AsyncWebHeader(const String& name, const String& value): _name(name), _value(value){}
|
||||||
AsyncWebHeader(const AsyncWebHeader&) = default;
|
AsyncWebHeader(const String& data): _name(), _value(){
|
||||||
AsyncWebHeader(const char* name, const char* value) : _name(name), _value(value) {}
|
if(!data) return;
|
||||||
AsyncWebHeader(const String& name, const String& value) : _name(name), _value(value) {}
|
int index = data.indexOf(':');
|
||||||
AsyncWebHeader(const String& data);
|
if (index < 0) return;
|
||||||
|
_name = data.substring(0, index);
|
||||||
AsyncWebHeader& operator=(const AsyncWebHeader&) = default;
|
_value = data.substring(index + 2);
|
||||||
|
}
|
||||||
|
~AsyncWebHeader(){}
|
||||||
const String& name() const { return _name; }
|
const String& name() const { return _name; }
|
||||||
const String& value() const { return _value; }
|
const String& value() const { return _value; }
|
||||||
String toString() const;
|
String toString() const { return String(_name+": "+_value+"\r\n"); }
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 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,
|
typedef enum { RCT_NOT_USED = -1, RCT_DEFAULT = 0, RCT_HTTP, RCT_WS, RCT_EVENT, RCT_MAX } RequestedConnectionType;
|
||||||
RCT_DEFAULT = 0,
|
|
||||||
RCT_HTTP,
|
|
||||||
RCT_WS,
|
|
||||||
RCT_EVENT,
|
|
||||||
RCT_MAX } RequestedConnectionType;
|
|
||||||
|
|
||||||
// this enum is similar to Arduino WebServer's AsyncAuthType and PsychicHttp
|
|
||||||
typedef enum {
|
|
||||||
AUTH_NONE = 0,
|
|
||||||
AUTH_BASIC,
|
|
||||||
AUTH_DIGEST,
|
|
||||||
AUTH_BEARER,
|
|
||||||
AUTH_OTHER,
|
|
||||||
} AsyncAuthType;
|
|
||||||
|
|
||||||
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;
|
||||||
@ -181,17 +136,14 @@ class AsyncWebServerRequest {
|
|||||||
using FS = fs::FS;
|
using FS = fs::FS;
|
||||||
friend class AsyncWebServer;
|
friend class AsyncWebServer;
|
||||||
friend class AsyncCallbackWebHandler;
|
friend class AsyncCallbackWebHandler;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
AsyncClient* _client;
|
AsyncClient* _client;
|
||||||
AsyncWebServer* _server;
|
AsyncWebServer* _server;
|
||||||
AsyncWebHandler* _handler;
|
AsyncWebHandler* _handler;
|
||||||
AsyncWebServerResponse* _response;
|
AsyncWebServerResponse* _response;
|
||||||
|
StringArray _interestingHeaders;
|
||||||
ArDisconnectHandler _onDisconnectfn;
|
ArDisconnectHandler _onDisconnectfn;
|
||||||
|
|
||||||
// response is sent
|
|
||||||
bool _sent = false;
|
|
||||||
|
|
||||||
String _temp;
|
String _temp;
|
||||||
uint8_t _parseState;
|
uint8_t _parseState;
|
||||||
|
|
||||||
@ -203,18 +155,17 @@ class AsyncWebServerRequest {
|
|||||||
String _boundary;
|
String _boundary;
|
||||||
String _authorization;
|
String _authorization;
|
||||||
RequestedConnectionType _reqconntype;
|
RequestedConnectionType _reqconntype;
|
||||||
AsyncAuthType _authMethod = AsyncAuthType::AUTH_NONE;
|
void _removeNotInterestingHeaders();
|
||||||
|
bool _isDigest;
|
||||||
bool _isMultipart;
|
bool _isMultipart;
|
||||||
bool _isPlainPost;
|
bool _isPlainPost;
|
||||||
bool _expectingContinue;
|
bool _expectingContinue;
|
||||||
size_t _contentLength;
|
size_t _contentLength;
|
||||||
size_t _parsedLength;
|
size_t _parsedLength;
|
||||||
|
|
||||||
std::list<AsyncWebHeader> _headers;
|
LinkedList<AsyncWebHeader *> _headers;
|
||||||
std::list<AsyncWebParameter> _params;
|
LinkedList<AsyncWebParameter *> _params;
|
||||||
std::vector<String> _pathParams;
|
LinkedList<String *> _pathParams;
|
||||||
|
|
||||||
std::unordered_map<const char*, String, std::hash<const char*>, std::equal_to<const char*>> _attributes;
|
|
||||||
|
|
||||||
uint8_t _multiParseState;
|
uint8_t _multiParseState;
|
||||||
uint8_t _boundaryPosition;
|
uint8_t _boundaryPosition;
|
||||||
@ -224,7 +175,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;
|
||||||
|
|
||||||
@ -233,9 +184,10 @@ 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 _addPathParam(const char* param);
|
void _addParam(AsyncWebParameter*);
|
||||||
|
void _addPathParam(const char *param);
|
||||||
|
|
||||||
bool _parseReqHead();
|
bool _parseReqHead();
|
||||||
bool _parseReqHeader();
|
bool _parseReqHeader();
|
||||||
@ -250,12 +202,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; }
|
||||||
@ -263,240 +215,74 @@ 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 char * methodToString() const;
|
||||||
#ifndef ESP8266
|
const char * requestedConnTypeToString() const;
|
||||||
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* credentials, const char* realm = NULL, bool isHash = false);
|
bool authenticate(const char * username, const char * password, const char * realm = NULL, bool passwordIsHash = false);
|
||||||
void requestAuthentication(const char* realm = nullptr, bool isDigest = true) { requestAuthentication(isDigest ? AsyncAuthType::AUTH_DIGEST : AsyncAuthType::AUTH_BASIC, realm); }
|
void requestAuthentication(const char * realm = NULL, bool isDigest = true);
|
||||||
void requestAuthentication(AsyncAuthType method, const char* realm = nullptr, const char* _authFailMsg = nullptr);
|
|
||||||
|
|
||||||
void setHandler(AsyncWebHandler* handler) { _handler = handler; }
|
void setHandler(AsyncWebHandler *handler){ _handler = handler; }
|
||||||
|
void addInterestingHeader(const String& name);
|
||||||
|
|
||||||
#ifndef ESP8266
|
void redirect(const String& url);
|
||||||
[[deprecated("All headers are now collected. Use removeHeader(name) or HeaderFreeMiddleware if you really need to free some headers.")]]
|
|
||||||
#endif
|
|
||||||
void addInterestingHeader(__unused const char* name) {
|
|
||||||
}
|
|
||||||
#ifndef ESP8266
|
|
||||||
[[deprecated("All headers are now collected. Use removeHeader(name) or HeaderFreeMiddleware if you really need to free some headers.")]]
|
|
||||||
#endif
|
|
||||||
void addInterestingHeader(__unused const String& name) {
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
void send(AsyncWebServerResponse *response);
|
||||||
* @brief issue HTTP redirect responce with Location header
|
void send(int code, const String& contentType=String(), const String& content=String());
|
||||||
*
|
void send(FS &fs, const String& path, const String& contentType=String(), bool download=false, AwsTemplateProcessor callback=nullptr);
|
||||||
* @param url - url to redirect to
|
void send(File content, const String& path, const String& contentType=String(), bool download=false, AwsTemplateProcessor callback=nullptr);
|
||||||
* @param code - responce code, default is 302 : temporary redirect
|
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, int code = 302);
|
void sendChunked(const String& contentType, AwsResponseFiller callback, AwsTemplateProcessor templateCallback=nullptr);
|
||||||
void redirect(const String& url, int code = 302) { return redirect(url.c_str(), code); };
|
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);
|
||||||
|
|
||||||
void send(AsyncWebServerResponse* response);
|
AsyncWebServerResponse *beginResponse(int code, const String& contentType=String(), const String& content=String());
|
||||||
AsyncWebServerResponse* getResponse() const { return _response; }
|
AsyncWebServerResponse *beginResponse(FS &fs, const String& path, const String& contentType=String(), bool download=false, AwsTemplateProcessor callback=nullptr);
|
||||||
|
AsyncWebServerResponse *beginResponse(File content, const String& path, const String& contentType=String(), bool download=false, AwsTemplateProcessor callback=nullptr);
|
||||||
|
AsyncWebServerResponse *beginResponse(Stream &stream, const String& contentType, size_t len, AwsTemplateProcessor callback=nullptr);
|
||||||
|
AsyncWebServerResponse *beginResponse(const String& contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback=nullptr);
|
||||||
|
AsyncWebServerResponse *beginChunkedResponse(const String& contentType, AwsResponseFiller callback, AwsTemplateProcessor templateCallback=nullptr);
|
||||||
|
AsyncResponseStream *beginResponseStream(const String& contentType, size_t bufferSize=1460);
|
||||||
|
AsyncWebServerResponse *beginResponse_P(int code, const String& contentType, const uint8_t * content, size_t len, AwsTemplateProcessor callback=nullptr);
|
||||||
|
AsyncWebServerResponse *beginResponse_P(int code, const String& contentType, PGM_P content, AwsTemplateProcessor callback=nullptr);
|
||||||
|
|
||||||
void send(int code, const char* contentType = asyncsrv::empty, const char* content = asyncsrv::empty, AwsTemplateProcessor callback = nullptr) { send(beginResponse(code, contentType, content, callback)); }
|
size_t headers() const; // get header count
|
||||||
void send(int code, const String& contentType, const char* content = asyncsrv::empty, AwsTemplateProcessor callback = nullptr) { send(beginResponse(code, contentType.c_str(), content, callback)); }
|
bool hasHeader(const String& name) const; // check if header exists
|
||||||
void send(int code, const String& contentType, const String& content, AwsTemplateProcessor callback = nullptr) { send(beginResponse(code, contentType.c_str(), content.c_str(), callback)); }
|
bool hasHeader(const __FlashStringHelper * data) const; // check if header exists
|
||||||
|
|
||||||
void send(int code, const char* contentType, const uint8_t* content, size_t len, AwsTemplateProcessor callback = nullptr) { send(beginResponse(code, contentType, content, len, callback)); }
|
AsyncWebHeader* getHeader(const String& name) const;
|
||||||
void send(int code, const String& contentType, const uint8_t* content, size_t len, AwsTemplateProcessor callback = nullptr) { send(beginResponse(code, contentType, content, len, callback)); }
|
AsyncWebHeader* getHeader(const __FlashStringHelper * data) const;
|
||||||
|
AsyncWebHeader* getHeader(size_t num) const;
|
||||||
|
|
||||||
void send(FS& fs, const String& path, const char* contentType = asyncsrv::empty, bool download = false, AwsTemplateProcessor callback = nullptr) {
|
size_t params() const; // get arguments count
|
||||||
if (fs.exists(path) || (!download && fs.exists(path + asyncsrv::T__gz))) {
|
bool hasParam(const String& name, bool post=false, bool file=false) const;
|
||||||
send(beginResponse(fs, path, contentType, download, callback));
|
bool hasParam(const __FlashStringHelper * data, bool post=false, bool file=false) const;
|
||||||
} else
|
|
||||||
send(404);
|
|
||||||
}
|
|
||||||
void send(FS& fs, const String& path, const String& contentType, bool download = false, AwsTemplateProcessor callback = nullptr) { send(fs, path, contentType.c_str(), download, callback); }
|
|
||||||
|
|
||||||
void send(File content, const String& path, const char* contentType = asyncsrv::empty, bool download = false, AwsTemplateProcessor callback = nullptr) {
|
AsyncWebParameter* getParam(const String& name, bool post=false, bool file=false) const;
|
||||||
if (content) {
|
AsyncWebParameter* getParam(const __FlashStringHelper * data, bool post, bool file) const;
|
||||||
send(beginResponse(content, path, contentType, download, callback));
|
AsyncWebParameter* getParam(size_t num) const;
|
||||||
} else
|
|
||||||
send(404);
|
|
||||||
}
|
|
||||||
void send(File content, const String& path, const String& contentType, bool download = false, AwsTemplateProcessor callback = nullptr) { send(content, path, contentType.c_str(), download, callback); }
|
|
||||||
|
|
||||||
void send(Stream& stream, const char* contentType, size_t len, AwsTemplateProcessor callback = nullptr) { send(beginResponse(stream, contentType, len, callback)); }
|
|
||||||
void send(Stream& stream, const String& contentType, size_t len, AwsTemplateProcessor callback = nullptr) { send(beginResponse(stream, contentType, len, callback)); }
|
|
||||||
|
|
||||||
void send(const char* contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback = nullptr) { send(beginResponse(contentType, len, callback, templateCallback)); }
|
|
||||||
void send(const String& contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback = nullptr) { send(beginResponse(contentType, len, callback, templateCallback)); }
|
|
||||||
|
|
||||||
void sendChunked(const char* contentType, AwsResponseFiller callback, AwsTemplateProcessor templateCallback = nullptr) { send(beginChunkedResponse(contentType, callback, templateCallback)); }
|
|
||||||
void sendChunked(const String& contentType, AwsResponseFiller callback, AwsTemplateProcessor templateCallback = nullptr) { send(beginChunkedResponse(contentType, callback, templateCallback)); }
|
|
||||||
|
|
||||||
#ifndef ESP8266
|
|
||||||
[[deprecated("Replaced by send(int code, const String& contentType, const uint8_t* content, size_t len, AwsTemplateProcessor callback = nullptr)")]]
|
|
||||||
#endif
|
|
||||||
void send_P(int code, const String& contentType, const uint8_t* content, size_t len, AwsTemplateProcessor callback = nullptr) {
|
|
||||||
send(code, contentType, content, len, callback);
|
|
||||||
}
|
|
||||||
#ifndef ESP8266
|
|
||||||
[[deprecated("Replaced by send(int code, const String& contentType, const char* content = asyncsrv::empty, AwsTemplateProcessor callback = nullptr)")]]
|
|
||||||
void send_P(int code, const String& contentType, PGM_P content, AwsTemplateProcessor callback = nullptr) {
|
|
||||||
send(code, contentType, content, callback);
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
void send_P(int code, const String& contentType, PGM_P content, AwsTemplateProcessor callback = nullptr) {
|
|
||||||
send(beginResponse_P(code, contentType, content, callback));
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
AsyncWebServerResponse* beginResponse(int code, const char* contentType = asyncsrv::empty, const char* content = asyncsrv::empty, AwsTemplateProcessor callback = nullptr);
|
|
||||||
AsyncWebServerResponse* beginResponse(int code, const String& contentType, const char* content = asyncsrv::empty, AwsTemplateProcessor callback = nullptr) { return beginResponse(code, contentType.c_str(), content, callback); }
|
|
||||||
AsyncWebServerResponse* beginResponse(int code, const String& contentType, const String& content, AwsTemplateProcessor callback = nullptr) { return beginResponse(code, contentType.c_str(), content.c_str(), callback); }
|
|
||||||
|
|
||||||
AsyncWebServerResponse* beginResponse(int code, const char* contentType, const uint8_t* content, size_t len, AwsTemplateProcessor callback = nullptr);
|
|
||||||
AsyncWebServerResponse* beginResponse(int code, const String& contentType, const uint8_t* content, size_t len, AwsTemplateProcessor callback = nullptr) { return beginResponse(code, contentType.c_str(), content, len, callback); }
|
|
||||||
|
|
||||||
AsyncWebServerResponse* beginResponse(FS& fs, const String& path, const char* contentType = asyncsrv::empty, bool download = false, AwsTemplateProcessor callback = nullptr);
|
|
||||||
AsyncWebServerResponse* beginResponse(FS& fs, const String& path, const String& contentType = emptyString, bool download = false, AwsTemplateProcessor callback = nullptr) { return beginResponse(fs, path, contentType.c_str(), download, callback); }
|
|
||||||
|
|
||||||
AsyncWebServerResponse* beginResponse(File content, const String& path, const char* contentType = asyncsrv::empty, bool download = false, AwsTemplateProcessor callback = nullptr);
|
|
||||||
AsyncWebServerResponse* beginResponse(File content, const String& path, const String& contentType = emptyString, bool download = false, AwsTemplateProcessor callback = nullptr) { return beginResponse(content, path, contentType.c_str(), download, callback); }
|
|
||||||
|
|
||||||
AsyncWebServerResponse* beginResponse(Stream& stream, const char* contentType, size_t len, AwsTemplateProcessor callback = nullptr);
|
|
||||||
AsyncWebServerResponse* beginResponse(Stream& stream, const String& contentType, size_t len, AwsTemplateProcessor callback = nullptr) { return beginResponse(stream, contentType.c_str(), len, callback); }
|
|
||||||
|
|
||||||
AsyncWebServerResponse* beginResponse(const char* contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback = nullptr);
|
|
||||||
AsyncWebServerResponse* beginResponse(const String& contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback = nullptr) { return beginResponse(contentType.c_str(), len, callback, templateCallback); }
|
|
||||||
|
|
||||||
AsyncWebServerResponse* beginChunkedResponse(const char* contentType, AwsResponseFiller callback, AwsTemplateProcessor templateCallback = nullptr);
|
|
||||||
AsyncWebServerResponse* beginChunkedResponse(const String& contentType, AwsResponseFiller callback, AwsTemplateProcessor templateCallback = nullptr);
|
|
||||||
|
|
||||||
AsyncResponseStream* beginResponseStream(const char* contentType, size_t bufferSize = RESPONSE_STREAM_BUFFER_SIZE);
|
|
||||||
AsyncResponseStream* beginResponseStream(const String& contentType, size_t bufferSize = RESPONSE_STREAM_BUFFER_SIZE) { return beginResponseStream(contentType.c_str(), bufferSize); }
|
|
||||||
|
|
||||||
#ifndef ESP8266
|
|
||||||
[[deprecated("Replaced by beginResponse(int code, const String& contentType, const uint8_t* content, size_t len, AwsTemplateProcessor callback = nullptr)")]]
|
|
||||||
#endif
|
|
||||||
AsyncWebServerResponse* beginResponse_P(int code, const String& contentType, const uint8_t* content, size_t len, AwsTemplateProcessor callback = nullptr) {
|
|
||||||
return beginResponse(code, contentType.c_str(), content, len, callback);
|
|
||||||
}
|
|
||||||
#ifndef ESP8266
|
|
||||||
[[deprecated("Replaced by beginResponse(int code, const String& contentType, const char* content = asyncsrv::empty, AwsTemplateProcessor callback = nullptr)")]]
|
|
||||||
#endif
|
|
||||||
AsyncWebServerResponse* beginResponse_P(int code, const String& contentType, PGM_P content, AwsTemplateProcessor callback = nullptr);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Get the Request parameter by name
|
|
||||||
*
|
|
||||||
* @param name
|
|
||||||
* @param post
|
|
||||||
* @param file
|
|
||||||
* @return const AsyncWebParameter*
|
|
||||||
*/
|
|
||||||
const AsyncWebParameter* getParam(const char* name, bool post = false, bool file = false) const;
|
|
||||||
|
|
||||||
const AsyncWebParameter* getParam(const String& name, bool post = false, bool file = false) const { return getParam(name.c_str(), post, file); };
|
|
||||||
#ifdef ESP8266
|
|
||||||
const AsyncWebParameter* getParam(const __FlashStringHelper* data, bool post, bool file) const;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Get request parameter by number
|
|
||||||
* i.e., n-th parameter
|
|
||||||
* @param num
|
|
||||||
* @return const AsyncWebParameter*
|
|
||||||
*/
|
|
||||||
const AsyncWebParameter* getParam(size_t num) const;
|
|
||||||
|
|
||||||
size_t args() const { return params(); } // get arguments count
|
size_t args() const { return params(); } // get arguments count
|
||||||
|
const String& arg(const String& name) const; // get request argument value by name
|
||||||
// get request argument value by name
|
const String& arg(const __FlashStringHelper * data) const; // get request argument value by F(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& arg(size_t i) const; // get request argument value by number
|
||||||
const String& argName(size_t i) const; // get request argument name by number
|
const String& argName(size_t i) const; // get request argument name by number
|
||||||
bool hasArg(const char* name) const; // check if argument exists
|
bool hasArg(const char* name) const; // check if argument exists
|
||||||
bool hasArg(const String& name) const { return hasArg(name.c_str()); };
|
bool hasArg(const __FlashStringHelper * data) const; // check if F(argument) exists
|
||||||
#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;
|
||||||
|
|
||||||
// get request header value by name
|
const String& header(const char* name) const;// get request header value by name
|
||||||
const String& header(const char* name) const;
|
const String& header(const __FlashStringHelper * data) const;// get request header value by F(name)
|
||||||
const String& header(const String& name) const { return header(name.c_str()); };
|
|
||||||
|
|
||||||
#ifdef ESP8266
|
|
||||||
const String& header(const __FlashStringHelper* data) const; // get request header value by F(name)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
const String& header(size_t i) const; // get request header value by number
|
const String& header(size_t i) const; // get request header value by number
|
||||||
const String& headerName(size_t i) const; // get request header name by number
|
const String& headerName(size_t i) const; // get request header name by number
|
||||||
|
|
||||||
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 std::list<AsyncWebHeader>& getHeaders() const { return _headers; }
|
|
||||||
|
|
||||||
size_t getHeaderNames(std::vector<const char*>& names) const;
|
|
||||||
|
|
||||||
// Remove a header from the request.
|
|
||||||
// It will free the memory and prevent the header to be seen during request processing.
|
|
||||||
bool removeHeader(const char* name);
|
|
||||||
// Remove all request headers.
|
|
||||||
void removeHeaders() { _headers.clear(); }
|
|
||||||
|
|
||||||
size_t params() const; // get arguments count
|
|
||||||
bool hasParam(const char* name, bool post = false, bool file = false) const;
|
|
||||||
bool hasParam(const String& name, bool post = false, bool file = false) const { return hasParam(name.c_str(), post, file); };
|
|
||||||
#ifdef ESP8266
|
|
||||||
bool hasParam(const __FlashStringHelper* data, bool post = false, bool file = false) const { return hasParam(String(data).c_str(), post, file); };
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// REQUEST ATTRIBUTES
|
|
||||||
|
|
||||||
void setAttribute(const char* name, const char* value) { _attributes[name] = value; }
|
|
||||||
void setAttribute(const char* name, bool value) { _attributes[name] = value ? "1" : emptyString; }
|
|
||||||
void setAttribute(const char* name, long value) { _attributes[name] = String(value); }
|
|
||||||
void setAttribute(const char* name, float value, unsigned int decimalPlaces = 2) { _attributes[name] = String(value, decimalPlaces); }
|
|
||||||
void setAttribute(const char* name, double value, unsigned int decimalPlaces = 2) { _attributes[name] = String(value, decimalPlaces); }
|
|
||||||
|
|
||||||
bool hasAttribute(const char* name) const { return _attributes.find(name) != _attributes.end(); }
|
|
||||||
|
|
||||||
const String& getAttribute(const char* name, const String& defaultValue = emptyString) const;
|
|
||||||
bool getAttribute(const char* name, bool defaultValue) const;
|
|
||||||
long getAttribute(const char* name, long defaultValue) const;
|
|
||||||
float getAttribute(const char* name, float defaultValue) const;
|
|
||||||
double getAttribute(const char* name, double defaultValue) const;
|
|
||||||
|
|
||||||
String urlDecode(const String& text) const;
|
String urlDecode(const String& text) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -504,181 +290,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)
|
||||||
* */
|
* */
|
||||||
|
|
||||||
using ArRequestFilterFunction = std::function<bool(AsyncWebServerRequest* request)>;
|
typedef std::function<bool(AsyncWebServerRequest *request)> ArRequestFilterFunction;
|
||||||
|
|
||||||
bool ON_STA_FILTER(AsyncWebServerRequest* request);
|
bool ON_STA_FILTER(AsyncWebServerRequest *request);
|
||||||
|
|
||||||
bool ON_AP_FILTER(AsyncWebServerRequest* request);
|
bool ON_AP_FILTER(AsyncWebServerRequest *request);
|
||||||
|
|
||||||
/*
|
|
||||||
* MIDDLEWARE :: Request interceptor, assigned to a AsyncWebHandler (or the server), which can be used:
|
|
||||||
* 1. to run some code before the final handler is executed (e.g. check authentication)
|
|
||||||
* 2. decide whether to proceed or not with the next handler
|
|
||||||
* */
|
|
||||||
|
|
||||||
using ArMiddlewareNext = std::function<void(void)>;
|
|
||||||
using ArMiddlewareCallback = std::function<void(AsyncWebServerRequest* request, ArMiddlewareNext next)>;
|
|
||||||
|
|
||||||
// Middleware is a base class for all middleware
|
|
||||||
class AsyncMiddleware {
|
|
||||||
public:
|
|
||||||
virtual ~AsyncMiddleware() {}
|
|
||||||
virtual void run(__unused AsyncWebServerRequest* request, __unused ArMiddlewareNext next) {
|
|
||||||
return next();
|
|
||||||
};
|
|
||||||
|
|
||||||
private:
|
|
||||||
friend class AsyncWebHandler;
|
|
||||||
friend class AsyncEventSource;
|
|
||||||
friend class AsyncMiddlewareChain;
|
|
||||||
bool _freeOnRemoval = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Create a custom middleware by providing an anonymous callback function
|
|
||||||
class AsyncMiddlewareFunction : public AsyncMiddleware {
|
|
||||||
public:
|
|
||||||
AsyncMiddlewareFunction(ArMiddlewareCallback fn) : _fn(fn) {}
|
|
||||||
void run(AsyncWebServerRequest* request, ArMiddlewareNext next) override { return _fn(request, next); };
|
|
||||||
|
|
||||||
private:
|
|
||||||
ArMiddlewareCallback _fn;
|
|
||||||
};
|
|
||||||
|
|
||||||
// For internal use only: super class to add/remove middleware to server or handlers
|
|
||||||
class AsyncMiddlewareChain {
|
|
||||||
public:
|
|
||||||
virtual ~AsyncMiddlewareChain();
|
|
||||||
|
|
||||||
void addMiddleware(ArMiddlewareCallback fn);
|
|
||||||
void addMiddleware(AsyncMiddleware* middleware);
|
|
||||||
void addMiddlewares(std::vector<AsyncMiddleware*> middlewares);
|
|
||||||
bool removeMiddleware(AsyncMiddleware* middleware);
|
|
||||||
|
|
||||||
// For internal use only
|
|
||||||
void _runChain(AsyncWebServerRequest* request, ArMiddlewareNext finalizer);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
std::list<AsyncMiddleware*> _middlewares;
|
|
||||||
};
|
|
||||||
|
|
||||||
// AuthenticationMiddleware is a middleware that checks if the request is authenticated
|
|
||||||
class AuthenticationMiddleware : public AsyncMiddleware {
|
|
||||||
public:
|
|
||||||
void setUsername(const char* username);
|
|
||||||
void setPassword(const char* password);
|
|
||||||
void setPasswordHash(const char* hash);
|
|
||||||
|
|
||||||
void setRealm(const char* realm) { _realm = realm; }
|
|
||||||
void setAuthFailureMessage(const char* message) { _authFailMsg = message; }
|
|
||||||
void setAuthType(AsyncAuthType authMethod) { _authMethod = authMethod; }
|
|
||||||
|
|
||||||
// precompute and store the hash value based on the username, realm, and authMethod
|
|
||||||
// returns true if the hash was successfully generated and replaced
|
|
||||||
bool generateHash();
|
|
||||||
|
|
||||||
bool allowed(AsyncWebServerRequest* request);
|
|
||||||
|
|
||||||
void run(AsyncWebServerRequest* request, ArMiddlewareNext next);
|
|
||||||
|
|
||||||
private:
|
|
||||||
String _username;
|
|
||||||
String _credentials;
|
|
||||||
bool _hash = false;
|
|
||||||
|
|
||||||
String _realm = asyncsrv::T_LOGIN_REQ;
|
|
||||||
AsyncAuthType _authMethod = AsyncAuthType::AUTH_NONE;
|
|
||||||
String _authFailMsg;
|
|
||||||
bool _hasCreds = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
using ArAuthorizeFunction = std::function<bool(AsyncWebServerRequest* request)>;
|
|
||||||
// AuthorizationMiddleware is a middleware that checks if the request is authorized
|
|
||||||
class AuthorizationMiddleware : public AsyncMiddleware {
|
|
||||||
public:
|
|
||||||
AuthorizationMiddleware(ArAuthorizeFunction authorizeConnectHandler) : _code(403), _authz(authorizeConnectHandler) {}
|
|
||||||
AuthorizationMiddleware(int code, ArAuthorizeFunction authorizeConnectHandler) : _code(code), _authz(authorizeConnectHandler) {}
|
|
||||||
|
|
||||||
void run(AsyncWebServerRequest* request, ArMiddlewareNext next) { return _authz && !_authz(request) ? request->send(_code) : next(); }
|
|
||||||
|
|
||||||
private:
|
|
||||||
int _code;
|
|
||||||
ArAuthorizeFunction _authz;
|
|
||||||
};
|
|
||||||
|
|
||||||
// remove all headers from the incoming request except the ones provided in the constructor
|
|
||||||
class HeaderFreeMiddleware : public AsyncMiddleware {
|
|
||||||
public:
|
|
||||||
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 run(AsyncWebServerRequest* request, ArMiddlewareNext next);
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::vector<const char*> _toKeep;
|
|
||||||
};
|
|
||||||
|
|
||||||
// filter out specific headers from the incoming request
|
|
||||||
class HeaderFilterMiddleware : public AsyncMiddleware {
|
|
||||||
public:
|
|
||||||
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 run(AsyncWebServerRequest* request, ArMiddlewareNext next);
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::vector<const char*> _toRemove;
|
|
||||||
};
|
|
||||||
|
|
||||||
// curl-like logging of incoming requests
|
|
||||||
class LoggingMiddleware : public AsyncMiddleware {
|
|
||||||
public:
|
|
||||||
void setOutput(Print& output) { _out = &output; }
|
|
||||||
void setEnabled(bool enabled) { _enabled = enabled; }
|
|
||||||
bool isEnabled() { return _enabled && _out; }
|
|
||||||
|
|
||||||
void run(AsyncWebServerRequest* request, ArMiddlewareNext next);
|
|
||||||
|
|
||||||
private:
|
|
||||||
Print* _out = nullptr;
|
|
||||||
bool _enabled = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
// CORS Middleware
|
|
||||||
class CorsMiddleware : public AsyncMiddleware {
|
|
||||||
public:
|
|
||||||
void setOrigin(const char* origin) { _origin = origin; }
|
|
||||||
void setMethods(const char* methods) { _methods = methods; }
|
|
||||||
void setHeaders(const char* headers) { _headers = headers; }
|
|
||||||
void setAllowCredentials(bool credentials) { _credentials = credentials; }
|
|
||||||
void setMaxAge(uint32_t seconds) { _maxAge = seconds; }
|
|
||||||
|
|
||||||
void addCORSHeaders(AsyncWebServerResponse* response);
|
|
||||||
|
|
||||||
void run(AsyncWebServerRequest* request, ArMiddlewareNext next);
|
|
||||||
|
|
||||||
private:
|
|
||||||
String _origin = "*";
|
|
||||||
String _methods = "*";
|
|
||||||
String _headers = "*";
|
|
||||||
bool _credentials = true;
|
|
||||||
uint32_t _maxAge = 86400;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Rate limit Middleware
|
|
||||||
class RateLimitMiddleware : public AsyncMiddleware {
|
|
||||||
public:
|
|
||||||
void setMaxRequests(size_t maxRequests) { _maxRequests = maxRequests; }
|
|
||||||
void setWindowSize(uint32_t seconds) { _windowSizeMillis = seconds * 1000; }
|
|
||||||
|
|
||||||
bool isRequestAllowed(uint32_t& retryAfterSeconds);
|
|
||||||
|
|
||||||
void run(AsyncWebServerRequest* request, ArMiddlewareNext next);
|
|
||||||
|
|
||||||
private:
|
|
||||||
size_t _maxRequests = 0;
|
|
||||||
uint32_t _windowSizeMillis = 0;
|
|
||||||
std::list<uint32_t> _requestTimes;
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* REWRITE :: One instance can be handle any Request (done by the Server)
|
* REWRITE :: One instance can be handle any Request (done by the Server)
|
||||||
@ -689,51 +305,46 @@ class AsyncWebRewrite {
|
|||||||
String _from;
|
String _from;
|
||||||
String _toUrl;
|
String _toUrl;
|
||||||
String _params;
|
String _params;
|
||||||
ArRequestFilterFunction _filter{nullptr};
|
ArRequestFilterFunction _filter;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
AsyncWebRewrite(const char* from, const char* to) : _from(from), _toUrl(to) {
|
AsyncWebRewrite(const char* from, const char* to): _from(from), _toUrl(to), _params(String()), _filter(NULL){
|
||||||
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) {
|
AsyncWebRewrite& setFilter(ArRequestFilterFunction fn) { _filter = fn; return *this; }
|
||||||
_filter = fn;
|
bool filter(AsyncWebServerRequest *request) const { return _filter == NULL || _filter(request); }
|
||||||
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); }
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* HANDLER :: One instance can be attached to any Request (done by the Server)
|
* HANDLER :: One instance can be attached to any Request (done by the Server)
|
||||||
* */
|
* */
|
||||||
|
|
||||||
class AsyncWebHandler : public AsyncMiddlewareChain {
|
class AsyncWebHandler {
|
||||||
protected:
|
protected:
|
||||||
ArRequestFilterFunction _filter = nullptr;
|
ArRequestFilterFunction _filter;
|
||||||
AuthenticationMiddleware* _authMiddleware = nullptr;
|
String _username;
|
||||||
|
String _password;
|
||||||
public:
|
public:
|
||||||
AsyncWebHandler() {}
|
AsyncWebHandler():_username(""), _password(""){}
|
||||||
AsyncWebHandler& setFilter(ArRequestFilterFunction fn);
|
AsyncWebHandler& setFilter(ArRequestFilterFunction fn) { _filter = fn; return *this; }
|
||||||
AsyncWebHandler& setAuthentication(const char* username, const char* password);
|
AsyncWebHandler& setAuthentication(const char *username, const char *password){ _username = String(username);_password = String(password); return *this; };
|
||||||
AsyncWebHandler& setAuthentication(const String& username, const String& password) { return setAuthentication(username.c_str(), password.c_str()); };
|
bool filter(AsyncWebServerRequest *request){ return _filter == NULL || _filter(request); }
|
||||||
bool filter(AsyncWebServerRequest* request) { return _filter == NULL || _filter(request); }
|
virtual ~AsyncWebHandler(){}
|
||||||
virtual ~AsyncWebHandler() {}
|
virtual bool canHandle(AsyncWebServerRequest *request __attribute__((unused))){
|
||||||
virtual bool canHandle(AsyncWebServerRequest* request __attribute__((unused))) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
virtual void handleRequest(__unused AsyncWebServerRequest* request) {}
|
virtual void handleRequest(AsyncWebServerRequest *request __attribute__((unused))){}
|
||||||
virtual void handleUpload(__unused AsyncWebServerRequest* request, __unused const String& filename, __unused size_t index, __unused uint8_t* data, __unused size_t len, __unused bool final) {}
|
virtual void 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(__unused AsyncWebServerRequest* request, __unused uint8_t* data, __unused size_t len, __unused size_t index, __unused size_t total) {}
|
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;}
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -741,18 +352,13 @@ class AsyncWebHandler : public AsyncMiddlewareChain {
|
|||||||
* */
|
* */
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
RESPONSE_SETUP,
|
RESPONSE_SETUP, RESPONSE_HEADERS, RESPONSE_CONTENT, RESPONSE_WAIT_ACK, RESPONSE_END, RESPONSE_FAILED
|
||||||
RESPONSE_HEADERS,
|
|
||||||
RESPONSE_CONTENT,
|
|
||||||
RESPONSE_WAIT_ACK,
|
|
||||||
RESPONSE_END,
|
|
||||||
RESPONSE_FAILED
|
|
||||||
} WebResponseState;
|
} WebResponseState;
|
||||||
|
|
||||||
class AsyncWebServerResponse {
|
class AsyncWebServerResponse {
|
||||||
protected:
|
protected:
|
||||||
int _code;
|
int _code;
|
||||||
std::list<AsyncWebHeader> _headers;
|
LinkedList<AsyncWebHeader *> _headers;
|
||||||
String _contentType;
|
String _contentType;
|
||||||
size_t _contentLength;
|
size_t _contentLength;
|
||||||
bool _sendContentLength;
|
bool _sendContentLength;
|
||||||
@ -762,61 +368,37 @@ class AsyncWebServerResponse {
|
|||||||
size_t _ackedLength;
|
size_t _ackedLength;
|
||||||
size_t _writtenLength;
|
size_t _writtenLength;
|
||||||
WebResponseState _state;
|
WebResponseState _state;
|
||||||
|
const char* _responseCodeToString(int code);
|
||||||
public:
|
|
||||||
#ifndef ESP8266
|
|
||||||
static const char* responseCodeToString(int code);
|
|
||||||
#else
|
|
||||||
static const __FlashStringHelper* responseCodeToString(int code);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
AsyncWebServerResponse();
|
AsyncWebServerResponse();
|
||||||
virtual ~AsyncWebServerResponse();
|
virtual ~AsyncWebServerResponse();
|
||||||
virtual void setCode(int code);
|
virtual void setCode(int code);
|
||||||
int code() const { return _code; }
|
|
||||||
virtual void setContentLength(size_t len);
|
virtual void setContentLength(size_t len);
|
||||||
void setContentType(const String& type) { setContentType(type.c_str()); }
|
virtual void setContentType(const String& type);
|
||||||
virtual void setContentType(const char* type);
|
virtual void addHeader(const String& name, const String& value);
|
||||||
virtual bool addHeader(const char* name, const char* value, bool replaceExisting = true);
|
virtual String _assembleHead(uint8_t version);
|
||||||
bool addHeader(const String& name, const String& value, bool replaceExisting = true) { return addHeader(name.c_str(), value.c_str(), replaceExisting); }
|
|
||||||
bool addHeader(const char* name, long value, bool replaceExisting = true) { return addHeader(name, String(value), replaceExisting); }
|
|
||||||
bool addHeader(const String& name, long value, bool replaceExisting = true) { return addHeader(name.c_str(), value, replaceExisting); }
|
|
||||||
virtual bool removeHeader(const char* name);
|
|
||||||
virtual const AsyncWebHeader* getHeader(const char* name) const;
|
|
||||||
const std::list<AsyncWebHeader>& getHeaders() const { return _headers; }
|
|
||||||
|
|
||||||
#ifndef ESP8266
|
|
||||||
[[deprecated("Use instead: _assembleHead(String& buffer, uint8_t version)")]]
|
|
||||||
#endif
|
|
||||||
String _assembleHead(uint8_t version) {
|
|
||||||
String buffer;
|
|
||||||
_assembleHead(buffer, version);
|
|
||||||
return buffer;
|
|
||||||
}
|
|
||||||
virtual void _assembleHead(String& buffer, uint8_t version);
|
|
||||||
|
|
||||||
virtual bool _started() const;
|
virtual bool _started() const;
|
||||||
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 : public AsyncMiddlewareChain {
|
class AsyncWebServer {
|
||||||
protected:
|
protected:
|
||||||
AsyncServer _server;
|
AsyncServer _server;
|
||||||
std::list<std::shared_ptr<AsyncWebRewrite>> _rewrites;
|
LinkedList<AsyncWebRewrite*> _rewrites;
|
||||||
std::list<std::unique_ptr<AsyncWebHandler>> _handlers;
|
LinkedList<AsyncWebHandler*> _handlers;
|
||||||
AsyncCallbackWebHandler* _catchAllHandler;
|
AsyncCallbackWebHandler* _catchAllHandler;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@ -828,97 +410,62 @@ class AsyncWebServer : public AsyncMiddlewareChain {
|
|||||||
|
|
||||||
#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);
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief (compat) Add url rewrite rule by pointer
|
|
||||||
* a deep copy of the pointer object will be created,
|
|
||||||
* it is up to user to manage further lifetime of the object in argument
|
|
||||||
*
|
|
||||||
* @param rewrite pointer to rewrite object to copy setting from
|
|
||||||
* @return AsyncWebRewrite& reference to a newly created rewrite rule
|
|
||||||
*/
|
|
||||||
AsyncWebRewrite& addRewrite(std::shared_ptr<AsyncWebRewrite> rewrite);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief add url rewrite rule
|
|
||||||
*
|
|
||||||
* @param from
|
|
||||||
* @param to
|
|
||||||
* @return AsyncWebRewrite&
|
|
||||||
*/
|
|
||||||
AsyncWebRewrite& rewrite(const char* from, const char* to);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief (compat) remove rewrite rule via referenced object
|
|
||||||
* this will NOT deallocate pointed object itself, internal rule with same from/to urls will be removed if any
|
|
||||||
* it's a compat method, better use `removeRewrite(const char* from, const char* to)`
|
|
||||||
* @param rewrite
|
|
||||||
* @return true
|
|
||||||
* @return false
|
|
||||||
*/
|
|
||||||
bool removeRewrite(AsyncWebRewrite* rewrite);
|
bool removeRewrite(AsyncWebRewrite* rewrite);
|
||||||
|
AsyncWebRewrite& rewrite(const char* from, const char* to);
|
||||||
/**
|
|
||||||
* @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);
|
||||||
|
|
||||||
AsyncCallbackWebHandler& on(const char* uri, ArRequestHandlerFunction onRequest) { return on(uri, HTTP_ANY, onRequest); }
|
AsyncCallbackWebHandler& on(const char* uri, ArRequestHandlerFunction onRequest);
|
||||||
AsyncCallbackWebHandler& on(const char* uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest, ArUploadHandlerFunction onUpload = nullptr, ArBodyHandlerFunction onBody = nullptr);
|
AsyncCallbackWebHandler& on(const char* uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest);
|
||||||
|
AsyncCallbackWebHandler& on(const char* uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest, ArUploadHandlerFunction onUpload);
|
||||||
|
AsyncCallbackWebHandler& on(const char* uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest, ArUploadHandlerFunction onUpload, ArBodyHandlerFunction onBody);
|
||||||
|
|
||||||
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 = LinkedList<AsyncWebHeader *>;
|
||||||
headers_t _headers;
|
headers_t _headers;
|
||||||
|
|
||||||
public:
|
DefaultHeaders()
|
||||||
DefaultHeaders() = default;
|
:_headers(headers_t([](AsyncWebHeader *h){ delete h; }))
|
||||||
|
{}
|
||||||
|
public:
|
||||||
|
using ConstIterator = headers_t::ConstIterator;
|
||||||
|
|
||||||
using ConstIterator = headers_t::const_iterator;
|
void addHeader(const String& name, const String& value){
|
||||||
|
_headers.add(new AsyncWebHeader(name, value));
|
||||||
void addHeader(const String& name, const String& 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 "AsyncEventSource.h"
|
|
||||||
#include "AsyncWebSocket.h"
|
|
||||||
#include "WebHandlerImpl.h"
|
|
||||||
#include "WebResponseImpl.h"
|
#include "WebResponseImpl.h"
|
||||||
|
#include "WebHandlerImpl.h"
|
||||||
|
#include "AsyncWebSocket.h"
|
||||||
|
#include "AsyncEventSource.h"
|
||||||
|
|
||||||
#endif /* _AsyncWebServer_H_ */
|
#endif /* _AsyncWebServer_H_ */
|
||||||
|
@ -1,253 +0,0 @@
|
|||||||
#include "WebAuthentication.h"
|
|
||||||
#include <ESPAsyncWebServer.h>
|
|
||||||
|
|
||||||
AsyncMiddlewareChain::~AsyncMiddlewareChain() {
|
|
||||||
for (AsyncMiddleware* m : _middlewares)
|
|
||||||
if (m->_freeOnRemoval)
|
|
||||||
delete m;
|
|
||||||
}
|
|
||||||
|
|
||||||
void AsyncMiddlewareChain::addMiddleware(ArMiddlewareCallback fn) {
|
|
||||||
AsyncMiddlewareFunction* m = new AsyncMiddlewareFunction(fn);
|
|
||||||
m->_freeOnRemoval = true;
|
|
||||||
_middlewares.emplace_back(m);
|
|
||||||
}
|
|
||||||
|
|
||||||
void AsyncMiddlewareChain::addMiddleware(AsyncMiddleware* middleware) {
|
|
||||||
if (middleware)
|
|
||||||
_middlewares.emplace_back(middleware);
|
|
||||||
}
|
|
||||||
|
|
||||||
void AsyncMiddlewareChain::addMiddlewares(std::vector<AsyncMiddleware*> middlewares) {
|
|
||||||
for (AsyncMiddleware* m : middlewares)
|
|
||||||
addMiddleware(m);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool AsyncMiddlewareChain::removeMiddleware(AsyncMiddleware* middleware) {
|
|
||||||
// remove all middlewares from _middlewares vector being equal to middleware, delete them having _freeOnRemoval flag to true and resize the vector.
|
|
||||||
const size_t size = _middlewares.size();
|
|
||||||
_middlewares.erase(std::remove_if(_middlewares.begin(), _middlewares.end(), [middleware](AsyncMiddleware* m) {
|
|
||||||
if (m == middleware) {
|
|
||||||
if (m->_freeOnRemoval)
|
|
||||||
delete m;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}),
|
|
||||||
_middlewares.end());
|
|
||||||
return size != _middlewares.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
void AsyncMiddlewareChain::_runChain(AsyncWebServerRequest* request, ArMiddlewareNext finalizer) {
|
|
||||||
if (!_middlewares.size())
|
|
||||||
return finalizer();
|
|
||||||
ArMiddlewareNext next;
|
|
||||||
std::list<AsyncMiddleware*>::iterator it = _middlewares.begin();
|
|
||||||
next = [this, &next, &it, request, finalizer]() {
|
|
||||||
if (it == _middlewares.end())
|
|
||||||
return finalizer();
|
|
||||||
AsyncMiddleware* m = *it;
|
|
||||||
it++;
|
|
||||||
return m->run(request, next);
|
|
||||||
};
|
|
||||||
return next();
|
|
||||||
}
|
|
||||||
|
|
||||||
void AuthenticationMiddleware::setUsername(const char* username) {
|
|
||||||
_username = username;
|
|
||||||
_hasCreds = _username.length() && _credentials.length();
|
|
||||||
}
|
|
||||||
|
|
||||||
void AuthenticationMiddleware::setPassword(const char* password) {
|
|
||||||
_credentials = password;
|
|
||||||
_hash = false;
|
|
||||||
_hasCreds = _username.length() && _credentials.length();
|
|
||||||
}
|
|
||||||
|
|
||||||
void AuthenticationMiddleware::setPasswordHash(const char* hash) {
|
|
||||||
_credentials = hash;
|
|
||||||
_hash = true;
|
|
||||||
_hasCreds = _username.length() && _credentials.length();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool AuthenticationMiddleware::generateHash() {
|
|
||||||
// ensure we have all the necessary data
|
|
||||||
if (!_hasCreds)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// if we already have a hash, do nothing
|
|
||||||
if (_hash)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
switch (_authMethod) {
|
|
||||||
case AsyncAuthType::AUTH_DIGEST:
|
|
||||||
_credentials = generateDigestHash(_username.c_str(), _credentials.c_str(), _realm.c_str());
|
|
||||||
_hash = true;
|
|
||||||
return true;
|
|
||||||
|
|
||||||
case AsyncAuthType::AUTH_BASIC:
|
|
||||||
_credentials = generateBasicHash(_username.c_str(), _credentials.c_str());
|
|
||||||
_hash = true;
|
|
||||||
return true;
|
|
||||||
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool AuthenticationMiddleware::allowed(AsyncWebServerRequest* request) {
|
|
||||||
if (_authMethod == AsyncAuthType::AUTH_NONE)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
if (!_hasCreds)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return request->authenticate(_username.c_str(), _credentials.c_str(), _realm.c_str(), _hash);
|
|
||||||
}
|
|
||||||
|
|
||||||
void AuthenticationMiddleware::run(AsyncWebServerRequest* request, ArMiddlewareNext next) {
|
|
||||||
return allowed(request) ? next() : request->requestAuthentication(_authMethod, _realm.c_str(), _authFailMsg.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
void HeaderFreeMiddleware::run(AsyncWebServerRequest* request, ArMiddlewareNext next) {
|
|
||||||
std::vector<const char*> reqHeaders;
|
|
||||||
request->getHeaderNames(reqHeaders);
|
|
||||||
for (const char* h : reqHeaders) {
|
|
||||||
bool keep = false;
|
|
||||||
for (const char* k : _toKeep) {
|
|
||||||
if (strcasecmp(h, k) == 0) {
|
|
||||||
keep = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!keep) {
|
|
||||||
request->removeHeader(h);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
next();
|
|
||||||
}
|
|
||||||
|
|
||||||
void HeaderFilterMiddleware::run(AsyncWebServerRequest* request, ArMiddlewareNext next) {
|
|
||||||
for (auto it = _toRemove.begin(); it != _toRemove.end(); ++it)
|
|
||||||
request->removeHeader(*it);
|
|
||||||
next();
|
|
||||||
}
|
|
||||||
|
|
||||||
void LoggingMiddleware::run(AsyncWebServerRequest* request, ArMiddlewareNext next) {
|
|
||||||
if (!isEnabled()) {
|
|
||||||
next();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_out->print(F("* Connection from "));
|
|
||||||
_out->print(request->client()->remoteIP().toString());
|
|
||||||
_out->print(':');
|
|
||||||
_out->println(request->client()->remotePort());
|
|
||||||
_out->print('>');
|
|
||||||
_out->print(' ');
|
|
||||||
_out->print(request->methodToString());
|
|
||||||
_out->print(' ');
|
|
||||||
_out->print(request->url().c_str());
|
|
||||||
_out->print(F(" HTTP/1."));
|
|
||||||
_out->println(request->version());
|
|
||||||
for (auto& h : request->getHeaders()) {
|
|
||||||
if (h.value().length()) {
|
|
||||||
_out->print('>');
|
|
||||||
_out->print(' ');
|
|
||||||
_out->print(h.name());
|
|
||||||
_out->print(':');
|
|
||||||
_out->print(' ');
|
|
||||||
_out->println(h.value());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_out->println(F(">"));
|
|
||||||
uint32_t elapsed = millis();
|
|
||||||
next();
|
|
||||||
elapsed = millis() - elapsed;
|
|
||||||
AsyncWebServerResponse* response = request->getResponse();
|
|
||||||
if (response) {
|
|
||||||
_out->print(F("* Processed in "));
|
|
||||||
_out->print(elapsed);
|
|
||||||
_out->println(F(" ms"));
|
|
||||||
_out->print('<');
|
|
||||||
_out->print(F(" HTTP/1."));
|
|
||||||
_out->print(request->version());
|
|
||||||
_out->print(' ');
|
|
||||||
_out->print(response->code());
|
|
||||||
_out->print(' ');
|
|
||||||
_out->println(AsyncWebServerResponse::responseCodeToString(response->code()));
|
|
||||||
for (auto& h : response->getHeaders()) {
|
|
||||||
if (h.value().length()) {
|
|
||||||
_out->print('<');
|
|
||||||
_out->print(' ');
|
|
||||||
_out->print(h.name());
|
|
||||||
_out->print(':');
|
|
||||||
_out->print(' ');
|
|
||||||
_out->println(h.value());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_out->println('<');
|
|
||||||
} else {
|
|
||||||
_out->println(F("* Connection closed!"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void CorsMiddleware::addCORSHeaders(AsyncWebServerResponse* response) {
|
|
||||||
response->addHeader(F("Access-Control-Allow-Origin"), _origin.c_str());
|
|
||||||
response->addHeader(F("Access-Control-Allow-Methods"), _methods.c_str());
|
|
||||||
response->addHeader(F("Access-Control-Allow-Headers"), _headers.c_str());
|
|
||||||
response->addHeader(F("Access-Control-Allow-Credentials"), _credentials ? F("true") : F("false"));
|
|
||||||
response->addHeader(F("Access-Control-Max-Age"), String(_maxAge).c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
void CorsMiddleware::run(AsyncWebServerRequest* request, ArMiddlewareNext next) {
|
|
||||||
// Origin header ? => CORS handling
|
|
||||||
if (request->hasHeader(F("Origin"))) {
|
|
||||||
// check if this is a preflight request => handle it and return
|
|
||||||
if (request->method() == HTTP_OPTIONS) {
|
|
||||||
AsyncWebServerResponse* response = request->beginResponse(200);
|
|
||||||
addCORSHeaders(response);
|
|
||||||
request->send(response);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// CORS request, no options => let the request pass and add CORS headers after
|
|
||||||
next();
|
|
||||||
AsyncWebServerResponse* response = request->getResponse();
|
|
||||||
if (response) {
|
|
||||||
addCORSHeaders(response);
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
// NO Origin header => no CORS handling
|
|
||||||
next();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool RateLimitMiddleware::isRequestAllowed(uint32_t& retryAfterSeconds) {
|
|
||||||
uint32_t now = millis();
|
|
||||||
|
|
||||||
while (!_requestTimes.empty() && _requestTimes.front() <= now - _windowSizeMillis)
|
|
||||||
_requestTimes.pop_front();
|
|
||||||
|
|
||||||
_requestTimes.push_back(now);
|
|
||||||
|
|
||||||
if (_requestTimes.size() > _maxRequests) {
|
|
||||||
_requestTimes.pop_front();
|
|
||||||
retryAfterSeconds = (_windowSizeMillis - (now - _requestTimes.front())) / 1000 + 1;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
retryAfterSeconds = 0;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void RateLimitMiddleware::run(AsyncWebServerRequest* request, ArMiddlewareNext next) {
|
|
||||||
uint32_t retryAfterSeconds;
|
|
||||||
if (isRequestAllowed(retryAfterSeconds)) {
|
|
||||||
next();
|
|
||||||
} else {
|
|
||||||
AsyncWebServerResponse* response = request->beginResponse(429);
|
|
||||||
response->addHeader(F("Retry-After"), retryAfterSeconds);
|
|
||||||
request->send(response);
|
|
||||||
}
|
|
||||||
}
|
|
202
src/StringArray.h
Normal file
202
src/StringArray.h
Normal file
@ -0,0 +1,202 @@
|
|||||||
|
/*
|
||||||
|
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;
|
||||||
|
ItemType* _nextNode = nullptr;
|
||||||
|
public:
|
||||||
|
Iterator(ItemType* current = nullptr) : _node(current) {
|
||||||
|
if (_node != nullptr) {
|
||||||
|
_nextNode = current->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Iterator(const Iterator& i) : _node(i._node) {}
|
||||||
|
Iterator& operator ++() {
|
||||||
|
_node = _nextNode;
|
||||||
|
_nextNode = _node != nullptr ? _node->next : nullptr;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class StringArray : public LinkedList<String> {
|
||||||
|
public:
|
||||||
|
|
||||||
|
StringArray() : LinkedList(nullptr) {}
|
||||||
|
|
||||||
|
bool containsIgnoreCase(const String& str){
|
||||||
|
for (const auto& s : *this) {
|
||||||
|
if (str.equalsIgnoreCase(s)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* STRINGARRAY_H_ */
|
@ -20,218 +20,216 @@
|
|||||||
*/
|
*/
|
||||||
#include "WebAuthentication.h"
|
#include "WebAuthentication.h"
|
||||||
#include <libb64/cencode.h>
|
#include <libb64/cencode.h>
|
||||||
#if defined(ESP32) || defined(TARGET_RP2040)
|
#ifdef ESP32
|
||||||
#include <MD5Builder.h>
|
#include "mbedtls/md5.h"
|
||||||
#else
|
#else
|
||||||
#include "md5.h"
|
#include "md5.h"
|
||||||
#endif
|
#endif
|
||||||
#include "literals.h"
|
|
||||||
|
|
||||||
using namespace asyncsrv;
|
|
||||||
|
|
||||||
// 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;
|
||||||
return generateBasicHash(username, password).equalsIgnoreCase(hash);
|
|
||||||
}
|
|
||||||
|
|
||||||
String generateBasicHash(const char* username, const char* password) {
|
size_t toencodeLen = strlen(username)+strlen(password)+1;
|
||||||
if (username == NULL || password == NULL)
|
size_t encodedLen = base64_encode_expected_len(toencodeLen);
|
||||||
return emptyString;
|
if(strlen(hash) != encodedLen)
|
||||||
|
return false;
|
||||||
|
|
||||||
size_t toencodeLen = strlen(username) + strlen(password) + 1;
|
char *toencode = new char[toencodeLen+1];
|
||||||
|
if(toencode == NULL){
|
||||||
char* toencode = new char[toencodeLen + 1];
|
return false;
|
||||||
if (toencode == NULL) {
|
|
||||||
return emptyString;
|
|
||||||
}
|
}
|
||||||
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 emptyString;
|
return false;
|
||||||
}
|
}
|
||||||
sprintf_P(toencode, PSTR("%s:%s"), username, password);
|
sprintf(toencode, "%s:%s", username, password);
|
||||||
if (base64_encode_chars(toencode, toencodeLen, encoded) > 0) {
|
if(base64_encode_chars(toencode, toencodeLen, encoded) > 0 && memcmp(hash, encoded, encodedLen) == 0){
|
||||||
String res = String(encoded);
|
|
||||||
delete[] toencode;
|
delete[] toencode;
|
||||||
delete[] encoded;
|
delete[] encoded;
|
||||||
return res;
|
return true;
|
||||||
}
|
}
|
||||||
delete[] toencode;
|
delete[] toencode;
|
||||||
delete[] encoded;
|
delete[] encoded;
|
||||||
return emptyString;
|
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
|
||||||
#if defined(ESP32) || defined(TARGET_RP2040)
|
#ifdef ESP32
|
||||||
MD5Builder md5;
|
mbedtls_md5_context _ctx;
|
||||||
md5.begin();
|
|
||||||
md5.add(data, len);
|
|
||||||
md5.calculate();
|
|
||||||
md5.getChars(output);
|
|
||||||
#else
|
#else
|
||||||
md5_context_t _ctx;
|
md5_context_t _ctx;
|
||||||
|
#endif
|
||||||
uint8_t* _buf = (uint8_t*)malloc(16);
|
uint8_t i;
|
||||||
if (_buf == NULL)
|
uint8_t * _buf = (uint8_t*)malloc(16);
|
||||||
|
if(_buf == NULL)
|
||||||
return false;
|
return false;
|
||||||
memset(_buf, 0x00, 16);
|
memset(_buf, 0x00, 16);
|
||||||
|
#ifdef ESP32
|
||||||
|
mbedtls_md5_init(&_ctx);
|
||||||
|
mbedtls_md5_starts_ret(&_ctx);
|
||||||
|
mbedtls_md5_update_ret(&_ctx, data, len);
|
||||||
|
mbedtls_md5_finish_ret(&_ctx, _buf);
|
||||||
|
#else
|
||||||
MD5Init(&_ctx);
|
MD5Init(&_ctx);
|
||||||
MD5Update(&_ctx, data, len);
|
MD5Update(&_ctx, data, len);
|
||||||
MD5Final(_buf, &_ctx);
|
MD5Final(_buf, &_ctx);
|
||||||
|
|
||||||
for (uint8_t i = 0; i < 16; i++) {
|
|
||||||
sprintf_P(output + (i * 2), PSTR("%02x"), _buf[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
free(_buf);
|
|
||||||
#endif
|
#endif
|
||||||
|
for(i = 0; i < 16; i++) {
|
||||||
|
sprintf(output + (i * 2), "%02x", _buf[i]);
|
||||||
|
}
|
||||||
|
free(_buf);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
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 "";
|
||||||
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 "";
|
||||||
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 "";
|
||||||
}
|
}
|
||||||
char* out = (char*)malloc(33);
|
char * out = (char*)malloc(33);
|
||||||
|
String res = String(username);
|
||||||
String in;
|
res.concat(":");
|
||||||
in.reserve(strlen(username) + strlen(realm) + strlen(password) + 2);
|
res.concat(realm);
|
||||||
in.concat(username);
|
res.concat(":");
|
||||||
in.concat(':');
|
String in = res;
|
||||||
in.concat(realm);
|
|
||||||
in.concat(':');
|
|
||||||
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 "";
|
||||||
return emptyString;
|
res.concat(out);
|
||||||
|
|
||||||
in = String(out);
|
|
||||||
free(out);
|
free(out);
|
||||||
return in;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef ESP8266
|
String requestDigestAuthentication(const char * realm){
|
||||||
bool checkDigestAuthentication(const char* header, const char* method, const char* username, const char* password, const char* realm, bool passwordIsHash, const char* nonce, const char* opaque, const char* uri)
|
String header = "realm=\"";
|
||||||
#else
|
if(realm == NULL)
|
||||||
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)
|
header.concat("asyncesp");
|
||||||
#endif
|
else
|
||||||
{
|
header.concat(realm);
|
||||||
if (username == NULL || password == NULL || header == NULL || method == NULL) {
|
header.concat( "\", qop=\"auth\", nonce=\"");
|
||||||
// os_printf("AUTH FAIL: missing requred fields\n");
|
header.concat(genRandomMD5());
|
||||||
|
header.concat("\", opaque=\"");
|
||||||
|
header.concat(genRandomMD5());
|
||||||
|
header.concat("\"");
|
||||||
|
return header;
|
||||||
|
}
|
||||||
|
|
||||||
|
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){
|
||||||
|
if(username == NULL || password == NULL || header == NULL || method == NULL){
|
||||||
|
//os_printf("AUTH FAIL: missing requred fields\n");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
String myHeader(header);
|
String myHeader = String(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 myUsername = String();
|
||||||
String myRealm;
|
String myRealm = String();
|
||||||
String myNonce;
|
String myNonce = String();
|
||||||
String myUri;
|
String myUri = String();
|
||||||
String myResponse;
|
String myResponse = String();
|
||||||
String myQop;
|
String myQop = String();
|
||||||
String myNc;
|
String myNc = String();
|
||||||
String myCnonce;
|
String myCnonce = String();
|
||||||
|
|
||||||
myHeader += (char)0x2c; // ','
|
myHeader += ", ";
|
||||||
myHeader += (char)0x20; // ' '
|
|
||||||
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("\"")){
|
||||||
avLine = avLine.substring(1, avLine.length() - 1);
|
avLine = avLine.substring(1, avLine.length() - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (varName.equals(T_username)) {
|
if(varName.equals("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(T_realm)) {
|
} else if(varName.equals("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(T_nonce)) {
|
} else if(varName.equals("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(T_opaque)) {
|
} else if(varName.equals("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(T_uri)) {
|
} else if(varName.equals("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(T_response)) {
|
} else if(varName.equals("response")){
|
||||||
myResponse = avLine;
|
myResponse = avLine;
|
||||||
} else if (varName.equals(T_qop)) {
|
} else if(varName.equals("qop")){
|
||||||
myQop = avLine;
|
myQop = avLine;
|
||||||
} else if (varName.equals(T_nc)) {
|
} else if(varName.equals("nc")){
|
||||||
myNc = avLine;
|
myNc = avLine;
|
||||||
} else if (varName.equals(T_cnonce)) {
|
} else if(varName.equals("cnonce")){
|
||||||
myCnonce = avLine;
|
myCnonce = avLine;
|
||||||
}
|
}
|
||||||
} while (nextBreak > 0);
|
} while(nextBreak > 0);
|
||||||
|
|
||||||
String ha1 = passwordIsHash ? password : stringMD5(myUsername + ':' + myRealm + ':' + password).c_str();
|
String ha1 = (passwordIsHash) ? String(password) : stringMD5(myUsername + ":" + myRealm + ":" + String(password));
|
||||||
String ha2 = stringMD5(String(method) + ':' + myUri);
|
String ha2 = String(method) + ":" + myUri;
|
||||||
String response = ha1 + ':' + myNonce + ':' + myNc + ':' + myCnonce + ':' + myQop + ':' + 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,19 +24,11 @@
|
|||||||
|
|
||||||
#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);
|
||||||
|
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);
|
||||||
|
|
||||||
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);
|
//for storing hashed versions on the device that can be authenticated against
|
||||||
|
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);
|
|
||||||
|
|
||||||
String generateBasicHash(const char* username, const char* password);
|
|
||||||
|
|
||||||
String genRandomMD5();
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -23,21 +23,19 @@
|
|||||||
|
|
||||||
#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;
|
||||||
@ -49,24 +47,23 @@ 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);
|
AsyncStaticWebHandler& setTemplateProcessor(AwsTemplateProcessor newCallback) {_callback = newCallback; return *this;}
|
||||||
};
|
};
|
||||||
|
|
||||||
class AsyncCallbackWebHandler : public AsyncWebHandler {
|
class AsyncCallbackWebHandler: public AsyncWebHandler {
|
||||||
private:
|
private:
|
||||||
protected:
|
protected:
|
||||||
String _uri;
|
String _uri;
|
||||||
@ -75,20 +72,80 @@ 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){
|
||||||
void setMethod(WebRequestMethodComposite method) { _method = method; }
|
_uri = uri;
|
||||||
void onRequest(ArRequestHandlerFunction fn) { _onRequest = fn; }
|
_isRegex = uri.startsWith("^") && uri.endsWith("$");
|
||||||
void onUpload(ArUploadHandlerFunction fn) { _onUpload = fn; }
|
}
|
||||||
void onBody(ArBodyHandlerFunction fn) { _onBody = fn; }
|
void setMethod(WebRequestMethodComposite method){ _method = method; }
|
||||||
|
void onRequest(ArRequestHandlerFunction fn){ _onRequest = fn; }
|
||||||
|
void onUpload(ArUploadHandlerFunction fn){ _onUpload = fn; }
|
||||||
|
void onBody(ArBodyHandlerFunction fn){ _onBody = fn; }
|
||||||
|
|
||||||
virtual bool canHandle(AsyncWebServerRequest* request) override final;
|
virtual bool canHandle(AsyncWebServerRequest *request) override final{
|
||||||
virtual void handleRequest(AsyncWebServerRequest* request) override final;
|
|
||||||
virtual void handleUpload(AsyncWebServerRequest* request, const String& filename, size_t index, uint8_t* data, size_t len, bool final) override final;
|
if(!_onRequest)
|
||||||
virtual void handleBody(AsyncWebServerRequest* request, uint8_t* data, size_t len, size_t index, size_t total) override final;
|
return false;
|
||||||
virtual bool isRequestHandlerTrivial() override final { return !_onRequest; }
|
|
||||||
|
if(!(_method & request->method()))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
#ifdef ASYNCWEBSERVER_REGEX
|
||||||
|
if (_isRegex) {
|
||||||
|
std::regex pattern(_uri.c_str());
|
||||||
|
std::smatch matches;
|
||||||
|
std::string s(request->url().c_str());
|
||||||
|
if(std::regex_search(s, matches, pattern)) {
|
||||||
|
for (size_t i = 1; i < matches.size(); ++i) { // start from 1
|
||||||
|
request->_addPathParam(matches[i].str().c_str());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
#endif
|
||||||
|
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);
|
||||||
|
uriTemplate = uriTemplate.substring(0, uriTemplate.length() - 1);
|
||||||
|
if (!request->url().startsWith(uriTemplate))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if(_uri.length() && (_uri != request->url() && !request->url().startsWith(_uri+"/")))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
request->addInterestingHeader("ANY");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void handleRequest(AsyncWebServerRequest *request) override final {
|
||||||
|
if((_username != "" && _password != "") && !request->authenticate(_username.c_str(), _password.c_str()))
|
||||||
|
return request->requestAuthentication();
|
||||||
|
if(_onRequest)
|
||||||
|
_onRequest(request);
|
||||||
|
else
|
||||||
|
request->send(500);
|
||||||
|
}
|
||||||
|
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()))
|
||||||
|
return request->requestAuthentication();
|
||||||
|
if(_onUpload)
|
||||||
|
_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 {
|
||||||
|
if((_username != "" && _password != "") && !request->authenticate(_username.c_str(), _password.c_str()))
|
||||||
|
return request->requestAuthentication();
|
||||||
|
if(_onBody)
|
||||||
|
_onBody(request, data, len, index, total);
|
||||||
|
}
|
||||||
|
virtual bool isRequestHandlerTrivial() override final {return _onRequest ? false : true;}
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* ASYNCWEBSERVERHANDLERIMPL_H_ */
|
#endif /* ASYNCWEBSERVERHANDLERIMPL_H_ */
|
||||||
|
@ -21,102 +21,94 @@
|
|||||||
#include "ESPAsyncWebServer.h"
|
#include "ESPAsyncWebServer.h"
|
||||||
#include "WebHandlerImpl.h"
|
#include "WebHandlerImpl.h"
|
||||||
|
|
||||||
using namespace asyncsrv;
|
|
||||||
|
|
||||||
AsyncWebHandler& AsyncWebHandler::setFilter(ArRequestFilterFunction fn) {
|
|
||||||
_filter = fn;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
AsyncWebHandler& AsyncWebHandler::setAuthentication(const char* username, const char* password) {
|
|
||||||
if (!_authMiddleware) {
|
|
||||||
_authMiddleware = new AuthenticationMiddleware();
|
|
||||||
_authMiddleware->_freeOnRemoval = true;
|
|
||||||
addMiddleware(_authMiddleware);
|
|
||||||
}
|
|
||||||
_authMiddleware->setUsername(username);
|
|
||||||
_authMiddleware->setPassword(password);
|
|
||||||
return *this;
|
|
||||||
};
|
|
||||||
|
|
||||||
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("index.htm"), _cache_control(cache_control), _last_modified(""), _callback(nullptr)
|
||||||
|
{
|
||||||
// Ensure leading '/'
|
// Ensure leading '/'
|
||||||
if (_uri.length() == 0 || _uri[0] != '/')
|
if (_uri.length() == 0 || _uri[0] != '/') _uri = "/" + _uri;
|
||||||
_uri = String('/') + _uri;
|
if (_path.length() == 0 || _path[0] != '/') _path = "/" + _path;
|
||||||
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] == '/')
|
if (_uri[_uri.length()-1] == '/') _uri = _uri.substring(0, _uri.length()-1);
|
||||||
_uri = _uri.substring(0, _uri.length() - 1);
|
if (_path[_path.length()-1] == '/') _path = _path.substring(0, _path.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 = String(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");
|
|
||||||
char format[strlen_P(formatP) + 1];
|
|
||||||
strcpy_P(format, formatP);
|
|
||||||
|
|
||||||
char result[30];
|
char result[30];
|
||||||
strftime(result, sizeof(result), format, last_modified);
|
strftime (result,30,"%a, %d %b %Y %H:%M:%S %Z", 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 || !request->url().startsWith(_uri) || !request->isExpectedRequestedConnType(RCT_DEFAULT, RCT_HTTP)) {
|
if(request->method() != HTTP_GET
|
||||||
|
|| !request->url().startsWith(_uri)
|
||||||
|
|| !request->isExpectedRequestedConnType(RCT_DEFAULT, RCT_HTTP)
|
||||||
|
){
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return _getFile(request);
|
if (_getFile(request)) {
|
||||||
|
// We interested in "If-Modified-Since" header to check if file was modified
|
||||||
|
if (_last_modified.length())
|
||||||
|
request->addInterestingHeader("If-Modified-Since");
|
||||||
|
|
||||||
|
if(_cache_control.length())
|
||||||
|
request->addInterestingHeader("If-None-Match");
|
||||||
|
|
||||||
|
DEBUGF("[AsyncStaticWebHandler::canHandle] TRUE\n");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
@ -129,119 +121,96 @@ 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 += "/";
|
||||||
path += _default_file;
|
path += _default_file;
|
||||||
|
|
||||||
return _fileExists(request, path);
|
return _fileExists(request, path);
|
||||||
}
|
}
|
||||||
|
|
||||||
#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 + ".gz";
|
||||||
|
|
||||||
if (_gzipFirst) {
|
if (_gzipFirst) {
|
||||||
if (_fs.exists(gzip)) {
|
request->_tempFile = _fs.open(gzip, "r");
|
||||||
request->_tempFile = _fs.open(gzip, fs::FileOpenMode::read);
|
|
||||||
gzipFound = FILE_IS_REAL(request->_tempFile);
|
gzipFound = FILE_IS_REAL(request->_tempFile);
|
||||||
}
|
if (!gzipFound){
|
||||||
if (!gzipFound) {
|
request->_tempFile = _fs.open(path, "r");
|
||||||
if (_fs.exists(path)) {
|
|
||||||
request->_tempFile = _fs.open(path, fs::FileOpenMode::read);
|
|
||||||
fileFound = FILE_IS_REAL(request->_tempFile);
|
fileFound = FILE_IS_REAL(request->_tempFile);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
if (_fs.exists(path)) {
|
request->_tempFile = _fs.open(path, "r");
|
||||||
request->_tempFile = _fs.open(path, fs::FileOpenMode::read);
|
|
||||||
fileFound = FILE_IS_REAL(request->_tempFile);
|
fileFound = FILE_IS_REAL(request->_tempFile);
|
||||||
}
|
if (!fileFound){
|
||||||
if (!fileFound) {
|
request->_tempFile = _fs.open(gzip, "r");
|
||||||
if (_fs.exists(gzip)) {
|
|
||||||
request->_tempFile = _fs.open(gzip, fs::FileOpenMode::read);
|
|
||||||
gzipFound = FILE_IS_REAL(request->_tempFile);
|
gzipFound = FILE_IS_REAL(request->_tempFile);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
bool found = fileFound || gzipFound;
|
bool found = fileFound || gzipFound;
|
||||||
|
|
||||||
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(_tempPath, pathLen+1, "%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)
|
if (_gzipStats == 0x00) _gzipFirst = false; // All files are not gzip
|
||||||
_gzipFirst = false; // All files are not gzip
|
else if (_gzipStats == 0xFF) _gzipFirst = true; // All files are gzip
|
||||||
else if (_gzipStats == 0xFF)
|
else _gzipFirst = _countBits(_gzipStats) > 4; // IF we have more gzip files - try gzip first
|
||||||
_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++)
|
for (n=0; w!=0; n++) w&=w-1;
|
||||||
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 != "" && _password != "") && !request->authenticate(_username.c_str(), _password.c_str()))
|
||||||
|
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)
|
String etag = String(request->_tempFile.size());
|
||||||
// set etag to lastmod timestamp if available, otherwise to size
|
if (_last_modified.length() && _last_modified == request->header("If-Modified-Since")) {
|
||||||
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(T_IMS)) {
|
|
||||||
request->_tempFile.close();
|
request->_tempFile.close();
|
||||||
request->send(304); // Not modified
|
request->send(304); // Not modified
|
||||||
} else if (_cache_control.length() && request->hasHeader(T_INM) && request->header(T_INM).equals(etag)) {
|
} else if (_cache_control.length() && request->hasHeader("If-None-Match") && request->header("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(T_Cache_Control, _cache_control.c_str());
|
response->addHeader("Cache-Control", _cache_control);
|
||||||
response->addHeader(T_ETag, etag.c_str());
|
response->addHeader("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(T_Last_Modified, _last_modified.c_str());
|
response->addHeader("Last-Modified", _last_modified);
|
||||||
if (_cache_control.length()) {
|
if (_cache_control.length()){
|
||||||
response->addHeader(T_Cache_Control, _cache_control.c_str());
|
response->addHeader("Cache-Control", _cache_control);
|
||||||
response->addHeader(T_ETag, etag.c_str());
|
response->addHeader("ETag", etag);
|
||||||
}
|
}
|
||||||
request->send(response);
|
request->send(response);
|
||||||
}
|
}
|
||||||
@ -249,65 +218,3 @@ void AsyncStaticWebHandler::handleRequest(AsyncWebServerRequest* request) {
|
|||||||
request->send(404);
|
request->send(404);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
AsyncStaticWebHandler& AsyncStaticWebHandler::setTemplateProcessor(AwsTemplateProcessor newCallback) {
|
|
||||||
_callback = newCallback;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
void AsyncCallbackWebHandler::setUri(const String& uri) {
|
|
||||||
_uri = uri;
|
|
||||||
_isRegex = uri.startsWith("^") && uri.endsWith("$");
|
|
||||||
}
|
|
||||||
|
|
||||||
bool AsyncCallbackWebHandler::canHandle(AsyncWebServerRequest* request) {
|
|
||||||
if (!_onRequest)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (!(_method & request->method()))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
#ifdef ASYNCWEBSERVER_REGEX
|
|
||||||
if (_isRegex) {
|
|
||||||
std::regex pattern(_uri.c_str());
|
|
||||||
std::smatch matches;
|
|
||||||
std::string s(request->url().c_str());
|
|
||||||
if (std::regex_search(s, matches, pattern)) {
|
|
||||||
for (size_t i = 1; i < matches.size(); ++i) { // start from 1
|
|
||||||
request->_addPathParam(matches[i].str().c_str());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else
|
|
||||||
#endif
|
|
||||||
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);
|
|
||||||
uriTemplate = uriTemplate.substring(0, uriTemplate.length() - 1);
|
|
||||||
if (!request->url().startsWith(uriTemplate))
|
|
||||||
return false;
|
|
||||||
} else if (_uri.length() && (_uri != request->url() && !request->url().startsWith(_uri + "/")))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void AsyncCallbackWebHandler::handleRequest(AsyncWebServerRequest* request) {
|
|
||||||
if (_onRequest)
|
|
||||||
_onRequest(request);
|
|
||||||
else
|
|
||||||
request->send(500);
|
|
||||||
}
|
|
||||||
void AsyncCallbackWebHandler::handleUpload(AsyncWebServerRequest* request, const String& filename, size_t index, uint8_t* data, size_t len, bool final) {
|
|
||||||
if (_onUpload)
|
|
||||||
_onUpload(request, filename, index, data, len, final);
|
|
||||||
}
|
|
||||||
void AsyncCallbackWebHandler::handleBody(AsyncWebServerRequest* request, uint8_t* data, size_t len, size_t index, size_t total) {
|
|
||||||
if (_onBody)
|
|
||||||
_onBody(request, data, len, index, total);
|
|
||||||
}
|
|
1085
src/WebRequest.cpp
1085
src/WebRequest.cpp
File diff suppressed because it is too large
Load Diff
@ -22,29 +22,24 @@
|
|||||||
#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 <memory>
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include "literals.h"
|
|
||||||
|
|
||||||
// It is possible to restore these defines, but one can use _min and _max instead. Or std::min, std::max.
|
// 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:
|
||||||
explicit AsyncBasicResponse(int code, const char* contentType = asyncsrv::empty, const char* content = asyncsrv::empty);
|
AsyncBasicResponse(int code, const String& contentType=String(), const String& content=String());
|
||||||
AsyncBasicResponse(int code, const String& contentType, const String& content = emptyString) : AsyncBasicResponse(code, contentType.c_str(), content.c_str()) {}
|
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().
|
||||||
@ -54,102 +49,86 @@ class AsyncAbstractResponse : public AsyncWebServerResponse {
|
|||||||
std::vector<uint8_t> _cache;
|
std::vector<uint8_t> _cache;
|
||||||
size_t _readDataFromCacheOrContent(uint8_t* data, const size_t len);
|
size_t _readDataFromCacheOrContent(uint8_t* data, const size_t len);
|
||||||
size_t _fillBufferAndProcessTemplates(uint8_t* buf, size_t maxLen);
|
size_t _fillBufferAndProcessTemplates(uint8_t* buf, size_t maxLen);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
AwsTemplateProcessor _callback;
|
AwsTemplateProcessor _callback;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
AsyncAbstractResponse(AwsTemplateProcessor callback = nullptr);
|
AsyncAbstractResponse(AwsTemplateProcessor callback=nullptr);
|
||||||
void _respond(AsyncWebServerRequest* request);
|
void _respond(AsyncWebServerRequest *request);
|
||||||
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 _setContentTypeFromPath(const String& path);
|
void _setContentType(const String& path);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
AsyncFileResponse(FS& fs, const String& path, const char* contentType = asyncsrv::empty, bool download = false, AwsTemplateProcessor callback = nullptr);
|
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, bool download = false, AwsTemplateProcessor callback = nullptr) : AsyncFileResponse(fs, path, contentType.c_str(), download, callback) {}
|
AsyncFileResponse(File content, const String& path, const String& contentType=String(), bool download=false, AwsTemplateProcessor callback=nullptr);
|
||||||
AsyncFileResponse(File content, const String& path, const char* contentType = asyncsrv::empty, bool download = false, AwsTemplateProcessor callback = nullptr);
|
|
||||||
AsyncFileResponse(File content, const String& path, const String& contentType, bool download = false, AwsTemplateProcessor callack = nullptr) : AsyncFileResponse(content, path, contentType.c_str(), download, callack) {}
|
|
||||||
~AsyncFileResponse();
|
~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 char* contentType, size_t len, AwsTemplateProcessor callback = nullptr);
|
AsyncStreamResponse(Stream &stream, const String& contentType, size_t len, AwsTemplateProcessor callback=nullptr);
|
||||||
AsyncStreamResponse(Stream& stream, const String& contentType, size_t len, AwsTemplateProcessor callback = nullptr) : AsyncStreamResponse(stream, contentType.c_str(), len, callback) {}
|
|
||||||
bool _sourceValid() const { return !!(_content); }
|
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 char* contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback = nullptr);
|
AsyncCallbackResponse(const String& contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback=nullptr);
|
||||||
AsyncCallbackResponse(const String& contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback = nullptr) : AsyncCallbackResponse(contentType.c_str(), len, callback, templateCallback) {}
|
|
||||||
bool _sourceValid() const { return !!(_content); }
|
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 char* contentType, AwsResponseFiller callback, AwsTemplateProcessor templateCallback = nullptr);
|
AsyncChunkedResponse(const String& contentType, AwsResponseFiller callback, AwsTemplateProcessor templateCallback=nullptr);
|
||||||
AsyncChunkedResponse(const String& contentType, AwsResponseFiller callback, AwsTemplateProcessor templateCallback = nullptr) : AsyncChunkedResponse(contentType.c_str(), callback, templateCallback) {}
|
|
||||||
bool _sourceValid() const { return !!(_content); }
|
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 char* contentType, const uint8_t* content, size_t len, AwsTemplateProcessor callback = nullptr);
|
AsyncProgmemResponse(int code, const String& contentType, const uint8_t * content, size_t len, AwsTemplateProcessor callback=nullptr);
|
||||||
AsyncProgmemResponse(int code, const String& contentType, const uint8_t* content, size_t len, AwsTemplateProcessor callback = nullptr) : AsyncProgmemResponse(code, contentType.c_str(), content, len, callback) {}
|
|
||||||
bool _sourceValid() const { return true; }
|
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;
|
cbuf *_content;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
AsyncResponseStream(const char* contentType, size_t bufferSize);
|
AsyncResponseStream(const String& contentType, size_t bufferSize);
|
||||||
AsyncResponseStream(const String& contentType, size_t bufferSize) : AsyncResponseStream(contentType.c_str(), 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;
|
||||||
};
|
};
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -21,144 +21,110 @@
|
|||||||
#include "ESPAsyncWebServer.h"
|
#include "ESPAsyncWebServer.h"
|
||||||
#include "WebHandlerImpl.h"
|
#include "WebHandlerImpl.h"
|
||||||
|
|
||||||
using namespace asyncsrv;
|
bool ON_STA_FILTER(AsyncWebServerRequest *request) {
|
||||||
|
|
||||||
bool ON_STA_FILTER(AsyncWebServerRequest* request) {
|
|
||||||
#ifndef CONFIG_IDF_TARGET_ESP32H2
|
|
||||||
return WiFi.localIP() == request->client()->localIP();
|
return WiFi.localIP() == request->client()->localIP();
|
||||||
#else
|
|
||||||
return false;
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ON_AP_FILTER(AsyncWebServerRequest* request) {
|
bool ON_AP_FILTER(AsyncWebServerRequest *request) {
|
||||||
#ifndef CONFIG_IDF_TARGET_ESP32H2
|
|
||||||
return WiFi.localIP() != request->client()->localIP();
|
return WiFi.localIP() != request->client()->localIP();
|
||||||
#else
|
|
||||||
return false;
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef HAVE_FS_FILE_OPEN_MODE
|
|
||||||
const char* fs::FileOpenMode::read = "r";
|
|
||||||
const char* fs::FileOpenMode::write = "w";
|
|
||||||
const char* fs::FileOpenMode::append = "a";
|
|
||||||
#endif
|
|
||||||
|
|
||||||
AsyncWebServer::AsyncWebServer(uint16_t port)
|
AsyncWebServer::AsyncWebServer(uint16_t port)
|
||||||
: _server(port) {
|
: _server(port)
|
||||||
|
, _rewrites(LinkedList<AsyncWebRewrite*>(nullptr))
|
||||||
|
, _handlers(LinkedList<AsyncWebHandler*>(nullptr))
|
||||||
|
{
|
||||||
_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)
|
if(_catchAllHandler) delete _catchAllHandler;
|
||||||
delete _catchAllHandler;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
AsyncWebRewrite& AsyncWebServer::addRewrite(std::shared_ptr<AsyncWebRewrite> rewrite) {
|
AsyncWebRewrite& AsyncWebServer::addRewrite(AsyncWebRewrite* rewrite){
|
||||||
_rewrites.emplace_back(rewrite);
|
_rewrites.add(rewrite);
|
||||||
return *_rewrites.back().get();
|
return *rewrite;
|
||||||
}
|
}
|
||||||
|
|
||||||
AsyncWebRewrite& AsyncWebServer::addRewrite(AsyncWebRewrite* rewrite) {
|
bool AsyncWebServer::removeRewrite(AsyncWebRewrite *rewrite){
|
||||||
_rewrites.emplace_back(rewrite);
|
return _rewrites.remove(rewrite);
|
||||||
return *_rewrites.back().get();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AsyncWebServer::removeRewrite(AsyncWebRewrite* rewrite) {
|
AsyncWebRewrite& AsyncWebServer::rewrite(const char* from, const char* to){
|
||||||
return removeRewrite(rewrite->from().c_str(), rewrite->toUrl().c_str());
|
return addRewrite(new AsyncWebRewrite(from, to));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AsyncWebServer::removeRewrite(const char* from, const char* to) {
|
AsyncWebHandler& AsyncWebServer::addHandler(AsyncWebHandler* handler){
|
||||||
for (auto r = _rewrites.begin(); r != _rewrites.end(); ++r) {
|
_handlers.add(handler);
|
||||||
if (r->get()->from() == from && r->get()->toUrl() == to) {
|
return *handler;
|
||||||
_rewrites.erase(r);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
AsyncWebRewrite& AsyncWebServer::rewrite(const char* from, const char* to) {
|
bool AsyncWebServer::removeHandler(AsyncWebHandler *handler){
|
||||||
_rewrites.emplace_back(std::make_shared<AsyncWebRewrite>(from, to));
|
return _handlers.remove(handler);
|
||||||
return *_rewrites.back().get();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
AsyncWebHandler& AsyncWebServer::addHandler(AsyncWebHandler* handler) {
|
void AsyncWebServer::begin(){
|
||||||
_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 (auto& h : _handlers) {
|
for(const auto& h: _handlers){
|
||||||
if (h->filter(request) && h->canHandle(request)) {
|
if (h->filter(request) && h->canHandle(request)){
|
||||||
request->setHandler(h.get());
|
request->setHandler(h);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
request->addInterestingHeader("ANY");
|
||||||
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);
|
||||||
@ -169,31 +135,59 @@ AsyncCallbackWebHandler& AsyncWebServer::on(const char* uri, WebRequestMethodCom
|
|||||||
return *handler;
|
return *handler;
|
||||||
}
|
}
|
||||||
|
|
||||||
AsyncStaticWebHandler& AsyncWebServer::serveStatic(const char* uri, fs::FS& fs, const char* path, const char* cache_control) {
|
AsyncCallbackWebHandler& AsyncWebServer::on(const char* uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest, ArUploadHandlerFunction onUpload){
|
||||||
|
AsyncCallbackWebHandler* handler = new AsyncCallbackWebHandler();
|
||||||
|
handler->setUri(uri);
|
||||||
|
handler->setMethod(method);
|
||||||
|
handler->onRequest(onRequest);
|
||||||
|
handler->onUpload(onUpload);
|
||||||
|
addHandler(handler);
|
||||||
|
return *handler;
|
||||||
|
}
|
||||||
|
|
||||||
|
AsyncCallbackWebHandler& AsyncWebServer::on(const char* uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest){
|
||||||
|
AsyncCallbackWebHandler* handler = new AsyncCallbackWebHandler();
|
||||||
|
handler->setUri(uri);
|
||||||
|
handler->setMethod(method);
|
||||||
|
handler->onRequest(onRequest);
|
||||||
|
addHandler(handler);
|
||||||
|
return *handler;
|
||||||
|
}
|
||||||
|
|
||||||
|
AsyncCallbackWebHandler& AsyncWebServer::on(const char* uri, ArRequestHandlerFunction onRequest){
|
||||||
|
AsyncCallbackWebHandler* handler = new AsyncCallbackWebHandler();
|
||||||
|
handler->setUri(uri);
|
||||||
|
handler->onRequest(onRequest);
|
||||||
|
addHandler(handler);
|
||||||
|
return *handler;
|
||||||
|
}
|
||||||
|
|
||||||
|
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.clear();
|
_rewrites.free();
|
||||||
_handlers.clear();
|
_handlers.free();
|
||||||
|
|
||||||
if (_catchAllHandler != NULL) {
|
if (_catchAllHandler != NULL){
|
||||||
_catchAllHandler->onRequest(NULL);
|
_catchAllHandler->onRequest(NULL);
|
||||||
_catchAllHandler->onUpload(NULL);
|
_catchAllHandler->onUpload(NULL);
|
||||||
_catchAllHandler->onBody(NULL);
|
_catchAllHandler->onBody(NULL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
347
src/literals.h
347
src/literals.h
@ -1,347 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
namespace asyncsrv {
|
|
||||||
|
|
||||||
static constexpr const char* empty = "";
|
|
||||||
|
|
||||||
#ifndef ESP8622
|
|
||||||
static constexpr const char* T_100_CONTINUE = "100-continue";
|
|
||||||
static constexpr const char* T_ACCEPT = "Accept";
|
|
||||||
static constexpr const char* T_Accept_Ranges = "Accept-Ranges";
|
|
||||||
static constexpr const char* T_app_xform_urlencoded = "application/x-www-form-urlencoded";
|
|
||||||
static constexpr const char* T_AUTH = "Authorization";
|
|
||||||
static constexpr const char* T_BASIC = "Basic";
|
|
||||||
static constexpr const char* T_BASIC_REALM = "Basic realm=\"";
|
|
||||||
static constexpr const char* T_LOGIN_REQ = "Login Required";
|
|
||||||
static constexpr const char* T_BODY = "body";
|
|
||||||
static constexpr const char* T_Cache_Control = "Cache-Control";
|
|
||||||
static constexpr const char* T_chunked = "chunked";
|
|
||||||
static constexpr const char* T_close = "close";
|
|
||||||
static constexpr const char* T_Connection = "Connection";
|
|
||||||
static constexpr const char* T_Content_Disposition = "Content-Disposition";
|
|
||||||
static constexpr const char* T_Content_Encoding = "Content-Encoding";
|
|
||||||
static constexpr const char* T_Content_Length = "Content-Length";
|
|
||||||
static constexpr const char* T_Content_Type = "Content-Type";
|
|
||||||
static constexpr const char* T_Cookie = "Cookie";
|
|
||||||
static constexpr const char* T_DIGEST = "Digest";
|
|
||||||
static constexpr const char* T_DIGEST_ = "Digest ";
|
|
||||||
static constexpr const char* T_BEARER = "Bearer";
|
|
||||||
static constexpr const char* T_ETag = "ETag";
|
|
||||||
static constexpr const char* T_EXPECT = "Expect";
|
|
||||||
static constexpr const char* T_HTTP_1_0 = "HTTP/1.0";
|
|
||||||
static constexpr const char* T_HTTP_100_CONT = "HTTP/1.1 100 Continue\r\n\r\n";
|
|
||||||
static constexpr const char* T_IMS = "If-Modified-Since";
|
|
||||||
static constexpr const char* T_INM = "If-None-Match";
|
|
||||||
static constexpr const char* T_keep_alive = "keep-alive";
|
|
||||||
static constexpr const char* T_Last_Event_ID = "Last-Event-ID";
|
|
||||||
static constexpr const char* T_Last_Modified = "Last-Modified";
|
|
||||||
static constexpr const char* T_LOCATION = "Location";
|
|
||||||
static constexpr const char* T_MULTIPART_ = "multipart/";
|
|
||||||
static constexpr const char* T_no_cache = "no-cache";
|
|
||||||
static constexpr const char* T_none = "none";
|
|
||||||
static constexpr const char* T_UPGRADE = "Upgrade";
|
|
||||||
static constexpr const char* T_WS = "websocket";
|
|
||||||
static constexpr const char* T_WWW_AUTH = "WWW-Authenticate";
|
|
||||||
static constexpr const char* T_Transfer_Encoding = "Transfer-Encoding";
|
|
||||||
|
|
||||||
// HTTP Methods
|
|
||||||
static constexpr const char* T_ANY = "ANY";
|
|
||||||
static constexpr const char* T_GET = "GET";
|
|
||||||
static constexpr const char* T_POST = "POST";
|
|
||||||
static constexpr const char* T_PUT = "PUT";
|
|
||||||
static constexpr const char* T_DELETE = "DELETE";
|
|
||||||
static constexpr const char* T_PATCH = "PATCH";
|
|
||||||
static constexpr const char* T_HEAD = "HEAD";
|
|
||||||
static constexpr const char* T_OPTIONS = "OPTIONS";
|
|
||||||
static constexpr const char* T_UNKNOWN = "UNKNOWN";
|
|
||||||
|
|
||||||
// Req content types
|
|
||||||
static constexpr const char* T_RCT_NOT_USED = "RCT_NOT_USED";
|
|
||||||
static constexpr const char* T_RCT_DEFAULT = "RCT_DEFAULT";
|
|
||||||
static constexpr const char* T_RCT_HTTP = "RCT_HTTP";
|
|
||||||
static constexpr const char* T_RCT_WS = "RCT_WS";
|
|
||||||
static constexpr const char* T_RCT_EVENT = "RCT_EVENT";
|
|
||||||
static constexpr const char* T_ERROR = "ERROR";
|
|
||||||
|
|
||||||
// extentions & MIME-Types
|
|
||||||
static constexpr const char* T__css = ".css";
|
|
||||||
static constexpr const char* T__eot = ".eot";
|
|
||||||
static constexpr const char* T__gif = ".gif";
|
|
||||||
static constexpr const char* T__gz = ".gz";
|
|
||||||
static constexpr const char* T__htm = ".htm";
|
|
||||||
static constexpr const char* T__html = ".html";
|
|
||||||
static constexpr const char* T__ico = ".ico";
|
|
||||||
static constexpr const char* T__jpg = ".jpg";
|
|
||||||
static constexpr const char* T__js = ".js";
|
|
||||||
static constexpr const char* T__json = ".json";
|
|
||||||
static constexpr const char* T__pdf = ".pdf";
|
|
||||||
static constexpr const char* T__png = ".png";
|
|
||||||
static constexpr const char* T__svg = ".svg";
|
|
||||||
static constexpr const char* T__ttf = ".ttf";
|
|
||||||
static constexpr const char* T__woff = ".woff";
|
|
||||||
static constexpr const char* T__woff2 = ".woff2";
|
|
||||||
static constexpr const char* T__xml = ".xml";
|
|
||||||
static constexpr const char* T__zip = ".zip";
|
|
||||||
static constexpr const char* T_application_javascript = "application/javascript";
|
|
||||||
static constexpr const char* T_application_json = "application/json";
|
|
||||||
static constexpr const char* T_application_msgpack = "application/msgpack";
|
|
||||||
static constexpr const char* T_application_pdf = "application/pdf";
|
|
||||||
static constexpr const char* T_application_x_gzip = "application/x-gzip";
|
|
||||||
static constexpr const char* T_application_zip = "application/zip";
|
|
||||||
static constexpr const char* T_font_eot = "font/eot";
|
|
||||||
static constexpr const char* T_font_ttf = "font/ttf";
|
|
||||||
static constexpr const char* T_font_woff = "font/woff";
|
|
||||||
static constexpr const char* T_font_woff2 = "font/woff2";
|
|
||||||
static constexpr const char* T_image_gif = "image/gif";
|
|
||||||
static constexpr const char* T_image_jpeg = "image/jpeg";
|
|
||||||
static constexpr const char* T_image_png = "image/png";
|
|
||||||
static constexpr const char* T_image_svg_xml = "image/svg+xml";
|
|
||||||
static constexpr const char* T_image_x_icon = "image/x-icon";
|
|
||||||
static constexpr const char* T_text_css = "text/css";
|
|
||||||
static constexpr const char* T_text_event_stream = "text/event-stream";
|
|
||||||
static constexpr const char* T_text_html = "text/html";
|
|
||||||
static constexpr const char* T_text_plain = "text/plain";
|
|
||||||
static constexpr const char* T_text_xml = "text/xml";
|
|
||||||
|
|
||||||
// Responce codes
|
|
||||||
static constexpr const char* T_HTTP_CODE_100 = "Continue";
|
|
||||||
static constexpr const char* T_HTTP_CODE_101 = "Switching Protocols";
|
|
||||||
static constexpr const char* T_HTTP_CODE_200 = "OK";
|
|
||||||
static constexpr const char* T_HTTP_CODE_201 = "Created";
|
|
||||||
static constexpr const char* T_HTTP_CODE_202 = "Accepted";
|
|
||||||
static constexpr const char* T_HTTP_CODE_203 = "Non-Authoritative Information";
|
|
||||||
static constexpr const char* T_HTTP_CODE_204 = "No Content";
|
|
||||||
static constexpr const char* T_HTTP_CODE_205 = "Reset Content";
|
|
||||||
static constexpr const char* T_HTTP_CODE_206 = "Partial Content";
|
|
||||||
static constexpr const char* T_HTTP_CODE_300 = "Multiple Choices";
|
|
||||||
static constexpr const char* T_HTTP_CODE_301 = "Moved Permanently";
|
|
||||||
static constexpr const char* T_HTTP_CODE_302 = "Found";
|
|
||||||
static constexpr const char* T_HTTP_CODE_303 = "See Other";
|
|
||||||
static constexpr const char* T_HTTP_CODE_304 = "Not Modified";
|
|
||||||
static constexpr const char* T_HTTP_CODE_305 = "Use Proxy";
|
|
||||||
static constexpr const char* T_HTTP_CODE_307 = "Temporary Redirect";
|
|
||||||
static constexpr const char* T_HTTP_CODE_400 = "Bad Request";
|
|
||||||
static constexpr const char* T_HTTP_CODE_401 = "Unauthorized";
|
|
||||||
static constexpr const char* T_HTTP_CODE_402 = "Payment Required";
|
|
||||||
static constexpr const char* T_HTTP_CODE_403 = "Forbidden";
|
|
||||||
static constexpr const char* T_HTTP_CODE_404 = "Not Found";
|
|
||||||
static constexpr const char* T_HTTP_CODE_405 = "Method Not Allowed";
|
|
||||||
static constexpr const char* T_HTTP_CODE_406 = "Not Acceptable";
|
|
||||||
static constexpr const char* T_HTTP_CODE_407 = "Proxy Authentication Required";
|
|
||||||
static constexpr const char* T_HTTP_CODE_408 = "Request Time-out";
|
|
||||||
static constexpr const char* T_HTTP_CODE_409 = "Conflict";
|
|
||||||
static constexpr const char* T_HTTP_CODE_410 = "Gone";
|
|
||||||
static constexpr const char* T_HTTP_CODE_411 = "Length Required";
|
|
||||||
static constexpr const char* T_HTTP_CODE_412 = "Precondition Failed";
|
|
||||||
static constexpr const char* T_HTTP_CODE_413 = "Request Entity Too Large";
|
|
||||||
static constexpr const char* T_HTTP_CODE_414 = "Request-URI Too Large";
|
|
||||||
static constexpr const char* T_HTTP_CODE_415 = "Unsupported Media Type";
|
|
||||||
static constexpr const char* T_HTTP_CODE_416 = "Requested range not satisfiable";
|
|
||||||
static constexpr const char* T_HTTP_CODE_417 = "Expectation Failed";
|
|
||||||
static constexpr const char* T_HTTP_CODE_429 = "Too Many Requests";
|
|
||||||
static constexpr const char* T_HTTP_CODE_500 = "Internal Server Error";
|
|
||||||
static constexpr const char* T_HTTP_CODE_501 = "Not Implemented";
|
|
||||||
static constexpr const char* T_HTTP_CODE_502 = "Bad Gateway";
|
|
||||||
static constexpr const char* T_HTTP_CODE_503 = "Service Unavailable";
|
|
||||||
static constexpr const char* T_HTTP_CODE_504 = "Gateway Time-out";
|
|
||||||
static constexpr const char* T_HTTP_CODE_505 = "HTTP Version not supported";
|
|
||||||
static constexpr const char* T_HTTP_CODE_ANY = "Unknown code";
|
|
||||||
|
|
||||||
// other
|
|
||||||
static constexpr const char* T__opaque = "\", opaque=\"";
|
|
||||||
static constexpr const char* T_13 = "13";
|
|
||||||
static constexpr const char* T_auth_nonce = "\", qop=\"auth\", nonce=\"";
|
|
||||||
static constexpr const char* T_cnonce = "cnonce";
|
|
||||||
static constexpr const char* T_data_ = "data: ";
|
|
||||||
static constexpr const char* T_event_ = "event: ";
|
|
||||||
static constexpr const char* T_filename = "filename";
|
|
||||||
static constexpr const char* T_gzip = "gzip";
|
|
||||||
static constexpr const char* T_Host = "Host";
|
|
||||||
static constexpr const char* T_id__ = "id: ";
|
|
||||||
static constexpr const char* T_name = "name";
|
|
||||||
static constexpr const char* T_nc = "nc";
|
|
||||||
static constexpr const char* T_nonce = "nonce";
|
|
||||||
static constexpr const char* T_opaque = "opaque";
|
|
||||||
static constexpr const char* T_qop = "qop";
|
|
||||||
static constexpr const char* T_realm = "realm";
|
|
||||||
static constexpr const char* T_realm__ = "realm=\"";
|
|
||||||
static constexpr const char* T_response = "response";
|
|
||||||
static constexpr const char* T_retry_ = "retry: ";
|
|
||||||
static constexpr const char* T_rn = "\r\n";
|
|
||||||
static constexpr const char* T_rnrn = "\r\n\r\n";
|
|
||||||
static constexpr const char* T_uri = "uri";
|
|
||||||
static constexpr const char* T_username = "username";
|
|
||||||
|
|
||||||
|
|
||||||
#else // ESP8622
|
|
||||||
|
|
||||||
static const char T_100_CONTINUE[] PROGMEM = "100-continue";
|
|
||||||
static const char T_ACCEPT[] PROGMEM = "Accept";
|
|
||||||
static const char T_Accept_Ranges[] PROGMEM = "Accept-Ranges";
|
|
||||||
static const char T_app_xform_urlencoded[] PROGMEM = "application/x-www-form-urlencoded";
|
|
||||||
static const char T_AUTH[] PROGMEM = "Authorization";
|
|
||||||
static const char T_BASIC[] PROGMEM = "Basic";
|
|
||||||
static const char T_BASIC_REALM[] PROGMEM = "Basic realm=\"";
|
|
||||||
static const char T_LOGIN_REQ[] PROGMEM = "Login Required";
|
|
||||||
static const char T_BODY[] PROGMEM = "body";
|
|
||||||
static const char T_Cache_Control[] PROGMEM = "Cache-Control";
|
|
||||||
static const char T_chunked[] PROGMEM = "chunked";
|
|
||||||
static const char T_close[] PROGMEM = "close";
|
|
||||||
static const char T_Connection[] PROGMEM = "Connection";
|
|
||||||
static const char T_Content_Disposition[] PROGMEM = "Content-Disposition";
|
|
||||||
static const char T_Content_Encoding[] PROGMEM = "Content-Encoding";
|
|
||||||
static const char T_Content_Length[] PROGMEM = "Content-Length";
|
|
||||||
static const char T_Content_Type[] PROGMEM = "Content-Type";
|
|
||||||
static const char T_Cookie[] PROGMEM = "Cookie";
|
|
||||||
static const char T_DIGEST[] PROGMEM = "Digest";
|
|
||||||
static const char T_DIGEST_[] PROGMEM = "Digest ";
|
|
||||||
static const char T_BEARER[] PROGMEM = "Bearer";
|
|
||||||
static const char T_ETag[] PROGMEM = "ETag";
|
|
||||||
static const char T_EXPECT[] PROGMEM = "Expect";
|
|
||||||
static const char T_HTTP_1_0[] PROGMEM = "HTTP/1.0";
|
|
||||||
static const char T_HTTP_100_CONT[] PROGMEM = "HTTP/1.1 100 Continue\r\n\r\n";
|
|
||||||
static const char T_IMS[] PROGMEM = "If-Modified-Since";
|
|
||||||
static const char T_INM[] PROGMEM = "If-None-Match";
|
|
||||||
static const char T_keep_alive[] PROGMEM = "keep-alive";
|
|
||||||
static const char T_Last_Event_ID[] PROGMEM = "Last-Event-ID";
|
|
||||||
static const char T_Last_Modified[] PROGMEM = "Last-Modified";
|
|
||||||
static const char T_LOCATION[] PROGMEM = "Location";
|
|
||||||
static const char T_MULTIPART_[] PROGMEM = "multipart/";
|
|
||||||
static const char T_no_cache[] PROGMEM = "no-cache";
|
|
||||||
static const char T_none[] PROGMEM = "none";
|
|
||||||
static const char T_UPGRADE[] PROGMEM = "Upgrade";
|
|
||||||
static const char T_WS[] PROGMEM = "websocket";
|
|
||||||
static const char T_WWW_AUTH[] PROGMEM = "WWW-Authenticate";
|
|
||||||
static const char T_Transfer_Encoding[] PROGMEM = "Transfer-Encoding";
|
|
||||||
|
|
||||||
// HTTP Methods
|
|
||||||
static const char T_ANY[] PROGMEM = "ANY";
|
|
||||||
static const char T_GET[] PROGMEM = "GET";
|
|
||||||
static const char T_POST[] PROGMEM = "POST";
|
|
||||||
static const char T_PUT[] PROGMEM = "PUT";
|
|
||||||
static const char T_DELETE[] PROGMEM = "DELETE";
|
|
||||||
static const char T_PATCH[] PROGMEM = "PATCH";
|
|
||||||
static const char T_HEAD[] PROGMEM = "HEAD";
|
|
||||||
static const char T_OPTIONS[] PROGMEM = "OPTIONS";
|
|
||||||
static const char T_UNKNOWN[] PROGMEM = "UNKNOWN";
|
|
||||||
|
|
||||||
// Req content types
|
|
||||||
static const char T_RCT_NOT_USED[] PROGMEM = "RCT_NOT_USED";
|
|
||||||
static const char T_RCT_DEFAULT[] PROGMEM = "RCT_DEFAULT";
|
|
||||||
static const char T_RCT_HTTP[] PROGMEM = "RCT_HTTP";
|
|
||||||
static const char T_RCT_WS[] PROGMEM = "RCT_WS";
|
|
||||||
static const char T_RCT_EVENT[] PROGMEM = "RCT_EVENT";
|
|
||||||
static const char T_ERROR[] PROGMEM = "ERROR";
|
|
||||||
|
|
||||||
// extentions & MIME-Types
|
|
||||||
static const char T__css[] PROGMEM = ".css";
|
|
||||||
static const char T__eot[] PROGMEM = ".eot";
|
|
||||||
static const char T__gif[] PROGMEM = ".gif";
|
|
||||||
static const char T__gz[] PROGMEM = ".gz";
|
|
||||||
static const char T__htm[] PROGMEM = ".htm";
|
|
||||||
static const char T__html[] PROGMEM = ".html";
|
|
||||||
static const char T__ico[] PROGMEM = ".ico";
|
|
||||||
static const char T__jpg[] PROGMEM = ".jpg";
|
|
||||||
static const char T__js[] PROGMEM = ".js";
|
|
||||||
static const char T__json[] PROGMEM = ".json";
|
|
||||||
static const char T__pdf[] PROGMEM = ".pdf";
|
|
||||||
static const char T__png[] PROGMEM = ".png";
|
|
||||||
static const char T__svg[] PROGMEM = ".svg";
|
|
||||||
static const char T__ttf[] PROGMEM = ".ttf";
|
|
||||||
static const char T__woff[] PROGMEM = ".woff";
|
|
||||||
static const char T__woff2[] PROGMEM = ".woff2";
|
|
||||||
static const char T__xml[] PROGMEM = ".xml";
|
|
||||||
static const char T__zip[] PROGMEM = ".zip";
|
|
||||||
static const char T_application_javascript[] PROGMEM = "application/javascript";
|
|
||||||
static const char T_application_json[] PROGMEM = "application/json";
|
|
||||||
static const char T_application_msgpack[] PROGMEM = "application/msgpack";
|
|
||||||
static const char T_application_pdf[] PROGMEM = "application/pdf";
|
|
||||||
static const char T_application_x_gzip[] PROGMEM = "application/x-gzip";
|
|
||||||
static const char T_application_zip[] PROGMEM = "application/zip";
|
|
||||||
static const char T_font_eot[] PROGMEM = "font/eot";
|
|
||||||
static const char T_font_ttf[] PROGMEM = "font/ttf";
|
|
||||||
static const char T_font_woff[] PROGMEM = "font/woff";
|
|
||||||
static const char T_font_woff2[] PROGMEM = "font/woff2";
|
|
||||||
static const char T_image_gif[] PROGMEM = "image/gif";
|
|
||||||
static const char T_image_jpeg[] PROGMEM = "image/jpeg";
|
|
||||||
static const char T_image_png[] PROGMEM = "image/png";
|
|
||||||
static const char T_image_svg_xml[] PROGMEM = "image/svg+xml";
|
|
||||||
static const char T_image_x_icon[] PROGMEM = "image/x-icon";
|
|
||||||
static const char T_text_css[] PROGMEM = "text/css";
|
|
||||||
static const char T_text_event_stream[] PROGMEM = "text/event-stream";
|
|
||||||
static const char T_text_html[] PROGMEM = "text/html";
|
|
||||||
static const char T_text_plain[] PROGMEM = "text/plain";
|
|
||||||
static const char T_text_xml[] PROGMEM = "text/xml";
|
|
||||||
|
|
||||||
// Responce codes
|
|
||||||
static const char T_HTTP_CODE_100[] PROGMEM = "Continue";
|
|
||||||
static const char T_HTTP_CODE_101[] PROGMEM = "Switching Protocols";
|
|
||||||
static const char T_HTTP_CODE_200[] PROGMEM = "OK";
|
|
||||||
static const char T_HTTP_CODE_201[] PROGMEM = "Created";
|
|
||||||
static const char T_HTTP_CODE_202[] PROGMEM = "Accepted";
|
|
||||||
static const char T_HTTP_CODE_203[] PROGMEM = "Non-Authoritative Information";
|
|
||||||
static const char T_HTTP_CODE_204[] PROGMEM = "No Content";
|
|
||||||
static const char T_HTTP_CODE_205[] PROGMEM = "Reset Content";
|
|
||||||
static const char T_HTTP_CODE_206[] PROGMEM = "Partial Content";
|
|
||||||
static const char T_HTTP_CODE_300[] PROGMEM = "Multiple Choices";
|
|
||||||
static const char T_HTTP_CODE_301[] PROGMEM = "Moved Permanently";
|
|
||||||
static const char T_HTTP_CODE_302[] PROGMEM = "Found";
|
|
||||||
static const char T_HTTP_CODE_303[] PROGMEM = "See Other";
|
|
||||||
static const char T_HTTP_CODE_304[] PROGMEM = "Not Modified";
|
|
||||||
static const char T_HTTP_CODE_305[] PROGMEM = "Use Proxy";
|
|
||||||
static const char T_HTTP_CODE_307[] PROGMEM = "Temporary Redirect";
|
|
||||||
static const char T_HTTP_CODE_400[] PROGMEM = "Bad Request";
|
|
||||||
static const char T_HTTP_CODE_401[] PROGMEM = "Unauthorized";
|
|
||||||
static const char T_HTTP_CODE_402[] PROGMEM = "Payment Required";
|
|
||||||
static const char T_HTTP_CODE_403[] PROGMEM = "Forbidden";
|
|
||||||
static const char T_HTTP_CODE_404[] PROGMEM = "Not Found";
|
|
||||||
static const char T_HTTP_CODE_405[] PROGMEM = "Method Not Allowed";
|
|
||||||
static const char T_HTTP_CODE_406[] PROGMEM = "Not Acceptable";
|
|
||||||
static const char T_HTTP_CODE_407[] PROGMEM = "Proxy Authentication Required";
|
|
||||||
static const char T_HTTP_CODE_408[] PROGMEM = "Request Time-out";
|
|
||||||
static const char T_HTTP_CODE_409[] PROGMEM = "Conflict";
|
|
||||||
static const char T_HTTP_CODE_410[] PROGMEM = "Gone";
|
|
||||||
static const char T_HTTP_CODE_411[] PROGMEM = "Length Required";
|
|
||||||
static const char T_HTTP_CODE_412[] PROGMEM = "Precondition Failed";
|
|
||||||
static const char T_HTTP_CODE_413[] PROGMEM = "Request Entity Too Large";
|
|
||||||
static const char T_HTTP_CODE_414[] PROGMEM = "Request-URI Too Large";
|
|
||||||
static const char T_HTTP_CODE_415[] PROGMEM = "Unsupported Media Type";
|
|
||||||
static const char T_HTTP_CODE_416[] PROGMEM = "Requested range not satisfiable";
|
|
||||||
static const char T_HTTP_CODE_417[] PROGMEM = "Expectation Failed";
|
|
||||||
static const char T_HTTP_CODE_429[] PROGMEM = "Too Many Requests";
|
|
||||||
static const char T_HTTP_CODE_500[] PROGMEM = "Internal Server Error";
|
|
||||||
static const char T_HTTP_CODE_501[] PROGMEM = "Not Implemented";
|
|
||||||
static const char T_HTTP_CODE_502[] PROGMEM = "Bad Gateway";
|
|
||||||
static const char T_HTTP_CODE_503[] PROGMEM = "Service Unavailable";
|
|
||||||
static const char T_HTTP_CODE_504[] PROGMEM = "Gateway Time-out";
|
|
||||||
static const char T_HTTP_CODE_505[] PROGMEM = "HTTP Version not supported";
|
|
||||||
static const char T_HTTP_CODE_ANY[] PROGMEM = "Unknown code";
|
|
||||||
|
|
||||||
// other
|
|
||||||
static const char T__opaque[] PROGMEM = "\", opaque=\"";
|
|
||||||
static const char T_13[] PROGMEM = "13";
|
|
||||||
static const char T_auth_nonce[] PROGMEM = "\", qop=\"auth\", nonce=\"";
|
|
||||||
static const char T_cnonce[] PROGMEM = "cnonce";
|
|
||||||
static const char T_data_[] PROGMEM = "data: ";
|
|
||||||
static const char T_event_[] PROGMEM = "event: ";
|
|
||||||
static const char T_filename[] PROGMEM = "filename";
|
|
||||||
static const char T_gzip[] PROGMEM = "gzip";
|
|
||||||
static const char T_Host[] PROGMEM = "Host";
|
|
||||||
static const char T_id__[] PROGMEM = "id: ";
|
|
||||||
static const char T_name[] PROGMEM = "name";
|
|
||||||
static const char T_nc[] PROGMEM = "nc";
|
|
||||||
static const char T_nonce[] PROGMEM = "nonce";
|
|
||||||
static const char T_opaque[] PROGMEM = "opaque";
|
|
||||||
static const char T_qop[] PROGMEM = "qop";
|
|
||||||
static const char T_realm[] PROGMEM = "realm";
|
|
||||||
static const char T_realm__[] PROGMEM = "realm=\"";
|
|
||||||
static const char T_response[] PROGMEM = "response";
|
|
||||||
static const char T_retry_[] PROGMEM = "retry: ";
|
|
||||||
static const char T_rn[] PROGMEM = "\r\n";
|
|
||||||
static const char T_rnrn[] PROGMEM = "\r\n\r\n";
|
|
||||||
static const char T_uri[] PROGMEM = "uri";
|
|
||||||
static const char T_username[] PROGMEM = "username";
|
|
||||||
|
|
||||||
#endif // ESP8622
|
|
||||||
|
|
||||||
} // namespace asyncsrv {}
|
|
Loading…
Reference in New Issue
Block a user