mirror of
https://github.com/dalathegreat/Battery-Emulator.git
synced 2025-10-05 10:49:42 +02:00
Update ESPAsyncWebserver to v3.7.2
This commit is contained in:
parent
545fceb4cc
commit
27c29867fe
30 changed files with 3596 additions and 4660 deletions
|
@ -6,12 +6,4 @@ set(COMPONENT_ADD_INCLUDEDIRS
|
|||
"src"
|
||||
)
|
||||
|
||||
set(COMPONENT_REQUIRES
|
||||
"arduino-esp32"
|
||||
"AsyncTCP"
|
||||
)
|
||||
|
||||
register_component()
|
||||
|
||||
target_compile_definitions(${COMPONENT_TARGET} PUBLIC -DESP32)
|
||||
target_compile_options(${COMPONENT_TARGET} PRIVATE -fno-rtti)
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
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,
|
||||
identity and expression, level of experience, education, socioeconomic status,
|
||||
nationality, personal appearance, race, religion, or sexual identity
|
||||
and orientation.
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "ESPAsyncWebServer",
|
||||
"version": "3.6.2",
|
||||
"version": "3.7.2",
|
||||
"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/ESP32Async/ESPAsyncWebServer",
|
||||
|
@ -24,7 +24,7 @@
|
|||
{
|
||||
"owner": "ESP32Async",
|
||||
"name": "AsyncTCP",
|
||||
"version": "^3.3.2",
|
||||
"version": "^3.3.6",
|
||||
"platforms": "espressif32"
|
||||
},
|
||||
{
|
||||
|
@ -38,9 +38,9 @@
|
|||
"platforms": "espressif8266"
|
||||
},
|
||||
{
|
||||
"owner": "khoih-prog",
|
||||
"name": "AsyncTCP_RP2040W",
|
||||
"version": "^1.2.0",
|
||||
"owner": "ayushsharma82",
|
||||
"name": "RPAsyncTCP",
|
||||
"version": "^1.3.1",
|
||||
"platforms": "raspberrypi"
|
||||
}
|
||||
],
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
name=ESP Async WebServer
|
||||
includes=ESPAsyncWebServer.h
|
||||
version=3.6.2
|
||||
version=3.7.2
|
||||
author=ESP32Async
|
||||
maintainer=ESP32Async
|
||||
sentence=Asynchronous HTTP and WebSocket Server Library for ESP32, ESP8266 and RP2040
|
||||
|
|
|
@ -1,22 +1,6 @@
|
|||
/*
|
||||
Asynchronous WebServer library for Espressif MCUs
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// Copyright 2016-2025 Hristo Gochkov, Mathieu Carbou, Emil Muratov
|
||||
|
||||
Copyright (c) 2016 Hristo Gochkov. All rights reserved.
|
||||
|
||||
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
|
||||
*/
|
||||
#include "Arduino.h"
|
||||
#if defined(ESP32)
|
||||
#include <rom/ets_sys.h>
|
||||
|
@ -30,15 +14,22 @@ using namespace asyncsrv;
|
|||
static String generateEventMessage(const char *message, const char *event, uint32_t id, uint32_t reconnect) {
|
||||
String str;
|
||||
size_t len{0};
|
||||
if (message)
|
||||
if (message) {
|
||||
len += strlen(message);
|
||||
}
|
||||
|
||||
if (event)
|
||||
if (event) {
|
||||
len += strlen(event);
|
||||
}
|
||||
|
||||
len += 42; // give it some overhead
|
||||
|
||||
str.reserve(len);
|
||||
if (!str.reserve(len)) {
|
||||
#ifdef ESP32
|
||||
log_e("Failed to allocate");
|
||||
#endif
|
||||
return emptyString;
|
||||
}
|
||||
|
||||
if (reconnect) {
|
||||
str += T_retry_;
|
||||
|
@ -58,8 +49,9 @@ static String generateEventMessage(const char* message, const char* event, uint3
|
|||
str += ASYNC_SSE_NEW_LINE_CHAR; // '\n'
|
||||
}
|
||||
|
||||
if (!message)
|
||||
if (!message) {
|
||||
return str;
|
||||
}
|
||||
|
||||
size_t messageLen = strlen(message);
|
||||
char *lineStart = (char *)message;
|
||||
|
@ -79,7 +71,7 @@ static String generateEventMessage(const char* message, const char* event, uint3
|
|||
char *nextLine = NULL;
|
||||
if (nextN != NULL && nextR != NULL) { // windows line-ending \r\n
|
||||
if (nextR + 1 == nextN) {
|
||||
// normal \r\n sequense
|
||||
// normal \r\n sequence
|
||||
lineEnd = nextR;
|
||||
nextLine = nextN + 1;
|
||||
} else {
|
||||
|
@ -124,8 +116,9 @@ size_t AsyncEventSourceMessage::ack(size_t len, __attribute__((unused)) uint32_t
|
|||
}
|
||||
|
||||
size_t AsyncEventSourceMessage::write(AsyncClient *client) {
|
||||
if (!client)
|
||||
if (!client) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (_sent >= _data->length() || !client->canSend()) {
|
||||
return 0;
|
||||
|
@ -155,19 +148,42 @@ size_t AsyncEventSourceMessage::send(AsyncClient* client) {
|
|||
|
||||
// Client
|
||||
|
||||
AsyncEventSourceClient::AsyncEventSourceClient(AsyncWebServerRequest* request, AsyncEventSource* server)
|
||||
: _client(request->client()), _server(server) {
|
||||
AsyncEventSourceClient::AsyncEventSourceClient(AsyncWebServerRequest *request, AsyncEventSource *server) : _client(request->client()), _server(server) {
|
||||
|
||||
if (request->hasHeader(T_Last_Event_ID))
|
||||
if (request->hasHeader(T_Last_Event_ID)) {
|
||||
_lastId = atoi(request->getHeader(T_Last_Event_ID)->value().c_str());
|
||||
}
|
||||
|
||||
_client->setRxTimeout(0);
|
||||
_client->onError(NULL, NULL);
|
||||
_client->onAck([](void* r, AsyncClient* c, size_t len, uint32_t time) { (void)c; static_cast<AsyncEventSourceClient*>(r)->_onAck(len, time); }, this);
|
||||
_client->onPoll([](void* r, AsyncClient* c) { (void)c; static_cast<AsyncEventSourceClient*>(r)->_onPoll(); }, this);
|
||||
_client->onAck(
|
||||
[](void *r, AsyncClient *c, size_t len, uint32_t time) {
|
||||
(void)c;
|
||||
static_cast<AsyncEventSourceClient *>(r)->_onAck(len, time);
|
||||
},
|
||||
this
|
||||
);
|
||||
_client->onPoll(
|
||||
[](void *r, AsyncClient *c) {
|
||||
(void)c;
|
||||
static_cast<AsyncEventSourceClient *>(r)->_onPoll();
|
||||
},
|
||||
this
|
||||
);
|
||||
_client->onData(NULL, NULL);
|
||||
_client->onTimeout([this](void* r, AsyncClient* c __attribute__((unused)), uint32_t time) { static_cast<AsyncEventSourceClient*>(r)->_onTimeout(time); }, this);
|
||||
_client->onDisconnect([this](void* r, AsyncClient* c) { static_cast<AsyncEventSourceClient*>(r)->_onDisconnect(); delete c; }, this);
|
||||
_client->onTimeout(
|
||||
[this](void *r, AsyncClient *c __attribute__((unused)), uint32_t time) {
|
||||
static_cast<AsyncEventSourceClient *>(r)->_onTimeout(time);
|
||||
},
|
||||
this
|
||||
);
|
||||
_client->onDisconnect(
|
||||
[this](void *r, AsyncClient *c) {
|
||||
static_cast<AsyncEventSourceClient *>(r)->_onDisconnect();
|
||||
delete c;
|
||||
},
|
||||
this
|
||||
);
|
||||
|
||||
_server->_addClient(this);
|
||||
delete request;
|
||||
|
@ -249,10 +265,11 @@ void AsyncEventSourceClient::_onAck(size_t len __attribute__((unused)), uint32_t
|
|||
#endif
|
||||
|
||||
// adjust in-flight len
|
||||
if (len < _inflight)
|
||||
if (len < _inflight) {
|
||||
_inflight -= len;
|
||||
else
|
||||
} else {
|
||||
_inflight = 0;
|
||||
}
|
||||
|
||||
// acknowledge as much messages's data as we got confirmed len from a AsyncTCP
|
||||
while (len && _messageQueue.size()) {
|
||||
|
@ -264,9 +281,10 @@ void AsyncEventSourceClient::_onAck(size_t len __attribute__((unused)), uint32_t
|
|||
}
|
||||
|
||||
// try to send another batch of data
|
||||
if (_messageQueue.size())
|
||||
if (_messageQueue.size()) {
|
||||
_runQueue();
|
||||
}
|
||||
}
|
||||
|
||||
void AsyncEventSourceClient::_onPoll() {
|
||||
if (_messageQueue.size()) {
|
||||
|
@ -279,31 +297,36 @@ void AsyncEventSourceClient::_onPoll() {
|
|||
}
|
||||
|
||||
void AsyncEventSourceClient::_onTimeout(uint32_t time __attribute__((unused))) {
|
||||
if (_client)
|
||||
if (_client) {
|
||||
_client->close(true);
|
||||
}
|
||||
}
|
||||
|
||||
void AsyncEventSourceClient::_onDisconnect() {
|
||||
if (!_client)
|
||||
if (!_client) {
|
||||
return;
|
||||
}
|
||||
_client = nullptr;
|
||||
_server->_handleDisconnect(this);
|
||||
}
|
||||
|
||||
void AsyncEventSourceClient::close() {
|
||||
if (_client)
|
||||
if (_client) {
|
||||
_client->close();
|
||||
}
|
||||
}
|
||||
|
||||
bool AsyncEventSourceClient::send(const char *message, const char *event, uint32_t id, uint32_t reconnect) {
|
||||
if (!connected())
|
||||
if (!connected()) {
|
||||
return false;
|
||||
}
|
||||
return _queueMessage(std::make_shared<String>(generateEventMessage(message, event, id, reconnect)));
|
||||
}
|
||||
|
||||
void AsyncEventSourceClient::_runQueue() {
|
||||
if (!_client)
|
||||
if (!_client) {
|
||||
return;
|
||||
}
|
||||
|
||||
// there is no need to lock the mutex here, 'cause all the calls to this method must be already lock'ed
|
||||
size_t total_bytes_written = 0;
|
||||
|
@ -320,14 +343,16 @@ void AsyncEventSourceClient::_runQueue() {
|
|||
}
|
||||
|
||||
// flush socket
|
||||
if (total_bytes_written)
|
||||
if (total_bytes_written) {
|
||||
_client->send();
|
||||
}
|
||||
}
|
||||
|
||||
void AsyncEventSourceClient::set_max_inflight_bytes(size_t value) {
|
||||
if (value >= SSE_MIN_INFLIGH && value <= SSE_MAX_INFLIGH)
|
||||
if (value >= SSE_MIN_INFLIGH && value <= SSE_MAX_INFLIGH) {
|
||||
_max_inflight = value;
|
||||
}
|
||||
}
|
||||
|
||||
/* AsyncEventSource */
|
||||
|
||||
|
@ -338,27 +363,32 @@ void AsyncEventSource::authorizeConnect(ArAuthorizeConnectHandler cb) {
|
|||
}
|
||||
|
||||
void AsyncEventSource::_addClient(AsyncEventSourceClient *client) {
|
||||
if (!client)
|
||||
if (!client) {
|
||||
return;
|
||||
}
|
||||
#ifdef ESP32
|
||||
std::lock_guard<std::mutex> lock(_client_queue_lock);
|
||||
#endif
|
||||
_clients.emplace_back(client);
|
||||
if (_connectcb)
|
||||
if (_connectcb) {
|
||||
_connectcb(client);
|
||||
}
|
||||
|
||||
_adjust_inflight_window();
|
||||
}
|
||||
|
||||
void AsyncEventSource::_handleDisconnect(AsyncEventSourceClient *client) {
|
||||
if (_disconnectcb)
|
||||
if (_disconnectcb) {
|
||||
_disconnectcb(client);
|
||||
}
|
||||
#ifdef ESP32
|
||||
std::lock_guard<std::mutex> lock(_client_queue_lock);
|
||||
#endif
|
||||
for (auto i = _clients.begin(); i != _clients.end(); ++i) {
|
||||
if (i->get() == client)
|
||||
if (i->get() == client) {
|
||||
_clients.erase(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
_adjust_inflight_window();
|
||||
}
|
||||
|
@ -371,10 +401,11 @@ void AsyncEventSource::close() {
|
|||
std::lock_guard<std::mutex> lock(_client_queue_lock);
|
||||
#endif
|
||||
for (const auto &c : _clients) {
|
||||
if (c->connected())
|
||||
if (c->connected()) {
|
||||
c->close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// pmb fix
|
||||
size_t AsyncEventSource::avgPacketsWaiting() const {
|
||||
|
@ -383,8 +414,9 @@ size_t AsyncEventSource::avgPacketsWaiting() const {
|
|||
#ifdef ESP32
|
||||
std::lock_guard<std::mutex> lock(_client_queue_lock);
|
||||
#endif
|
||||
if (!_clients.size())
|
||||
if (!_clients.size()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (const auto &c : _clients) {
|
||||
if (c->connected()) {
|
||||
|
@ -395,8 +427,7 @@ size_t AsyncEventSource::avgPacketsWaiting() const {
|
|||
return ((aql) + (nConnectedClients / 2)) / (nConnectedClients); // round up
|
||||
}
|
||||
|
||||
AsyncEventSource::SendStatus AsyncEventSource::send(
|
||||
const char* message, const char* event, uint32_t id, uint32_t reconnect) {
|
||||
AsyncEventSource::SendStatus AsyncEventSource::send(const char *message, const char *event, uint32_t id, uint32_t reconnect) {
|
||||
AsyncEvent_SharedData_t shared_msg = std::make_shared<String>(generateEventMessage(message, event, id, reconnect));
|
||||
#ifdef ESP32
|
||||
std::lock_guard<std::mutex> lock(_client_queue_lock);
|
||||
|
@ -404,11 +435,12 @@ AsyncEventSource::SendStatus AsyncEventSource::send(
|
|||
size_t hits = 0;
|
||||
size_t miss = 0;
|
||||
for (const auto &c : _clients) {
|
||||
if (c->write(shared_msg))
|
||||
if (c->write(shared_msg)) {
|
||||
++hits;
|
||||
else
|
||||
} else {
|
||||
++miss;
|
||||
}
|
||||
}
|
||||
return hits == 0 ? DISCARDED : (miss == 0 ? ENQUEUED : PARTIALLY_ENQUEUED);
|
||||
}
|
||||
|
||||
|
@ -417,9 +449,11 @@ size_t AsyncEventSource::count() const {
|
|||
std::lock_guard<std::mutex> lock(_client_queue_lock);
|
||||
#endif
|
||||
size_t n_clients{0};
|
||||
for (const auto& i : _clients)
|
||||
if (i->connected())
|
||||
for (const auto &i : _clients) {
|
||||
if (i->connected()) {
|
||||
++n_clients;
|
||||
}
|
||||
}
|
||||
|
||||
return n_clients;
|
||||
}
|
||||
|
@ -435,8 +469,9 @@ void AsyncEventSource::handleRequest(AsyncWebServerRequest* request) {
|
|||
void AsyncEventSource::_adjust_inflight_window() {
|
||||
if (_clients.size()) {
|
||||
size_t inflight = SSE_MAX_INFLIGH / _clients.size();
|
||||
for (const auto& c : _clients)
|
||||
for (const auto &c : _clients) {
|
||||
c->set_max_inflight_bytes(inflight);
|
||||
}
|
||||
// Serial.printf("adjusted inflight to: %u\n", inflight);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,29 +1,13 @@
|
|||
/*
|
||||
Asynchronous WebServer library for Espressif MCUs
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// Copyright 2016-2025 Hristo Gochkov, Mathieu Carbou, Emil Muratov
|
||||
|
||||
Copyright (c) 2016 Hristo Gochkov. All rights reserved.
|
||||
|
||||
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 ASYNCEVENTSOURCE_H_
|
||||
#define ASYNCEVENTSOURCE_H_
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
#ifdef ESP32
|
||||
#include "../../ESP32Async-AsyncTCP/src/AsyncTCP.h"
|
||||
#include <AsyncTCP.h>
|
||||
#include <mutex>
|
||||
#ifndef SSE_MAX_QUEUED_MESSAGES
|
||||
#define SSE_MAX_QUEUED_MESSAGES 32
|
||||
|
@ -37,8 +21,8 @@
|
|||
#endif
|
||||
#define SSE_MIN_INFLIGH 2 * 1460 // allow 2 MSS packets
|
||||
#define SSE_MAX_INFLIGH 8 * 1024 // but no more than 8k, no need to blow it, since same data is kept in local Q
|
||||
#elif defined(TARGET_RP2040)
|
||||
#include <AsyncTCP_RP2040W.h>
|
||||
#elif defined(TARGET_RP2040) || defined(TARGET_RP2350) || defined(PICO_RP2040) || defined(PICO_RP2350)
|
||||
#include <RPAsyncTCP.h>
|
||||
#ifndef SSE_MAX_QUEUED_MESSAGES
|
||||
#define SSE_MAX_QUEUED_MESSAGES 32
|
||||
#endif
|
||||
|
@ -46,7 +30,7 @@
|
|||
#define SSE_MAX_INFLIGH 16 * 1024 // but no more than 16k, no need to blow it, since same data is kept in local Q
|
||||
#endif
|
||||
|
||||
#include "ESPAsyncWebServer.h"
|
||||
#include <ESPAsyncWebServer.h>
|
||||
|
||||
#ifdef ESP8266
|
||||
#include <Hash.h>
|
||||
|
@ -76,18 +60,26 @@ class AsyncEventSourceMessage {
|
|||
|
||||
public:
|
||||
AsyncEventSourceMessage(AsyncEvent_SharedData_t data) : _data(data){};
|
||||
#ifdef ESP32
|
||||
#if defined(ESP32)
|
||||
AsyncEventSourceMessage(const char *data, size_t len) : _data(std::make_shared<String>(data, len)){};
|
||||
#elif defined(TARGET_RP2040) || defined(TARGET_RP2350) || defined(PICO_RP2040) || defined(PICO_RP2350)
|
||||
AsyncEventSourceMessage(const char *data, size_t len) : _data(std::make_shared<String>()) {
|
||||
if (data && len > 0) {
|
||||
_data->concat(data, len);
|
||||
}
|
||||
};
|
||||
#else
|
||||
// esp8266's String does not have constructor with data/length arguments. Use a concat method here
|
||||
AsyncEventSourceMessage(const char* data, size_t len) { _data->concat(data, len); };
|
||||
AsyncEventSourceMessage(const char *data, size_t len) {
|
||||
_data->concat(data, len);
|
||||
};
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief acknowledge sending len bytes of data
|
||||
* @note if num of bytes to ack is larger then the unacknowledged message length the number of carried over bytes are returned
|
||||
*
|
||||
* @param len bytes to acknowlegde
|
||||
* @param len bytes to acknowledge
|
||||
* @param time
|
||||
* @return size_t number of extra bytes carried over
|
||||
*/
|
||||
|
@ -111,13 +103,17 @@ class AsyncEventSourceMessage {
|
|||
size_t send(AsyncClient *client);
|
||||
|
||||
// returns true if full message's length were acked
|
||||
bool finished() { return _acked == _data->length(); }
|
||||
bool finished() {
|
||||
return _acked == _data->length();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief returns true if all data has been sent already
|
||||
*
|
||||
*/
|
||||
bool sent() { return _sent == _data->length(); }
|
||||
bool sent() {
|
||||
return _sent == _data->length();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -155,8 +151,12 @@ class AsyncEventSourceClient {
|
|||
* @return false if queue is full
|
||||
*/
|
||||
bool send(const char *message, const char *event = NULL, uint32_t id = 0, uint32_t reconnect = 0);
|
||||
bool send(const String& message, const String& event, uint32_t id = 0, uint32_t reconnect = 0) { return send(message.c_str(), event.c_str(), id, reconnect); }
|
||||
bool send(const String& message, const char* event, uint32_t id = 0, uint32_t reconnect = 0) { return send(message.c_str(), event, id, reconnect); }
|
||||
bool send(const String &message, const String &event, uint32_t id = 0, uint32_t reconnect = 0) {
|
||||
return send(message.c_str(), event.c_str(), id, reconnect);
|
||||
}
|
||||
bool send(const String &message, const char *event, uint32_t id = 0, uint32_t reconnect = 0) {
|
||||
return send(message.c_str(), event, id, reconnect);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief place supplied preformatted SSE message to the message queue
|
||||
|
@ -166,20 +166,32 @@ class AsyncEventSourceClient {
|
|||
* @return true on success
|
||||
* @return false on queue overflow or no client connected
|
||||
*/
|
||||
bool write(AsyncEvent_SharedData_t message) { return connected() && _queueMessage(std::move(message)); };
|
||||
bool write(AsyncEvent_SharedData_t message) {
|
||||
return connected() && _queueMessage(std::move(message));
|
||||
};
|
||||
|
||||
[[deprecated("Use _write(AsyncEvent_SharedData_t message) instead to share same data with multiple SSE clients")]]
|
||||
bool write(const char* message, size_t len) { return connected() && _queueMessage(message, len); };
|
||||
bool write(const char *message, size_t len) {
|
||||
return connected() && _queueMessage(message, len);
|
||||
};
|
||||
|
||||
// close client's connection
|
||||
void close();
|
||||
|
||||
// getters
|
||||
|
||||
AsyncClient* client() { return _client; }
|
||||
bool connected() const { return _client && _client->connected(); }
|
||||
uint32_t lastId() const { return _lastId; }
|
||||
size_t packetsWaiting() const { return _messageQueue.size(); };
|
||||
AsyncClient *client() {
|
||||
return _client;
|
||||
}
|
||||
bool connected() const {
|
||||
return _client && _client->connected();
|
||||
}
|
||||
uint32_t lastId() const {
|
||||
return _lastId;
|
||||
}
|
||||
size_t packetsWaiting() const {
|
||||
return _messageQueue.size();
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Sets max amount of bytes that could be written to client's socket while awaiting delivery acknowledge
|
||||
|
@ -195,7 +207,9 @@ class AsyncEventSourceClient {
|
|||
*
|
||||
* @return size_t
|
||||
*/
|
||||
size_t get_max_inflight_bytes() const { return _max_inflight; }
|
||||
size_t get_max_inflight_bytes() const {
|
||||
return _max_inflight;
|
||||
}
|
||||
|
||||
// system callbacks (do not call if from user code!)
|
||||
void _onAck(size_t len, uint32_t time);
|
||||
|
@ -233,9 +247,13 @@ class AsyncEventSource : public AsyncWebHandler {
|
|||
|
||||
AsyncEventSource(const char *url) : _url(url){};
|
||||
AsyncEventSource(const String &url) : _url(url){};
|
||||
~AsyncEventSource() { close(); };
|
||||
~AsyncEventSource() {
|
||||
close();
|
||||
};
|
||||
|
||||
const char* url() const { return _url.c_str(); }
|
||||
const char *url() const {
|
||||
return _url.c_str();
|
||||
}
|
||||
// close all connected clients
|
||||
void close();
|
||||
|
||||
|
@ -245,7 +263,9 @@ class AsyncEventSource : public AsyncWebHandler {
|
|||
*
|
||||
* @param cb
|
||||
*/
|
||||
void onConnect(ArEventHandlerFunction cb) { _connectcb = cb; }
|
||||
void onConnect(ArEventHandlerFunction cb) {
|
||||
_connectcb = cb;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Send an SSE message to client
|
||||
|
@ -258,11 +278,17 @@ class AsyncEventSource : public AsyncWebHandler {
|
|||
* @return SendStatus if message was placed in any/all/part of the client's queues
|
||||
*/
|
||||
SendStatus send(const char *message, const char *event = NULL, uint32_t id = 0, uint32_t reconnect = 0);
|
||||
SendStatus send(const String& message, const String& event, uint32_t id = 0, uint32_t reconnect = 0) { return send(message.c_str(), event.c_str(), id, reconnect); }
|
||||
SendStatus send(const String& message, const char* event, uint32_t id = 0, uint32_t reconnect = 0) { return send(message.c_str(), event, id, reconnect); }
|
||||
SendStatus send(const String &message, const String &event, uint32_t id = 0, uint32_t reconnect = 0) {
|
||||
return send(message.c_str(), event.c_str(), id, reconnect);
|
||||
}
|
||||
SendStatus send(const String &message, const char *event, uint32_t id = 0, uint32_t reconnect = 0) {
|
||||
return send(message.c_str(), event, id, reconnect);
|
||||
}
|
||||
|
||||
// The client pointer sent to the callback is only for reference purposes. DO NOT CALL ANY METHOD ON IT !
|
||||
void onDisconnect(ArEventHandlerFunction cb) { _disconnectcb = cb; }
|
||||
void onDisconnect(ArEventHandlerFunction cb) {
|
||||
_disconnectcb = cb;
|
||||
}
|
||||
void authorizeConnect(ArAuthorizeConnectHandler cb);
|
||||
|
||||
// returns number of connected clients
|
||||
|
@ -286,7 +312,9 @@ class AsyncEventSourceResponse : public AsyncWebServerResponse {
|
|||
AsyncEventSourceResponse(AsyncEventSource *server);
|
||||
void _respond(AsyncWebServerRequest *request);
|
||||
size_t _ack(AsyncWebServerRequest *request, size_t len, uint32_t time);
|
||||
bool _sourceValid() const { return true; }
|
||||
bool _sourceValid() const {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* ASYNCEVENTSOURCE_H_ */
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// Copyright 2016-2025 Hristo Gochkov, Mathieu Carbou, Emil Muratov
|
||||
|
||||
#include "AsyncJson.h"
|
||||
|
||||
#if ASYNC_JSON_SUPPORT == 1
|
||||
|
@ -6,29 +9,32 @@
|
|||
AsyncJsonResponse::AsyncJsonResponse(bool isArray) : _isValid{false} {
|
||||
_code = 200;
|
||||
_contentType = asyncsrv::T_application_json;
|
||||
if (isArray)
|
||||
if (isArray) {
|
||||
_root = _jsonBuffer.createArray();
|
||||
else
|
||||
} 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)
|
||||
if (isArray) {
|
||||
_root = _jsonBuffer.createNestedArray();
|
||||
else
|
||||
} else {
|
||||
_root = _jsonBuffer.createNestedObject();
|
||||
}
|
||||
}
|
||||
#else
|
||||
AsyncJsonResponse::AsyncJsonResponse(bool isArray) : _isValid{false} {
|
||||
_code = 200;
|
||||
_contentType = asyncsrv::T_application_json;
|
||||
if (isArray)
|
||||
if (isArray) {
|
||||
_root = _jsonBuffer.add<JsonArray>();
|
||||
else
|
||||
} else {
|
||||
_root = _jsonBuffer.add<JsonObject>();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
size_t AsyncJsonResponse::setLength() {
|
||||
|
@ -90,14 +96,17 @@ AsyncCallbackJsonWebHandler::AsyncCallbackJsonWebHandler(const String& uri, ArJs
|
|||
#endif
|
||||
|
||||
bool AsyncCallbackJsonWebHandler::canHandle(AsyncWebServerRequest *request) const {
|
||||
if (!_onRequest || !request->isHTTP() || !(_method & request->method()))
|
||||
if (!_onRequest || !request->isHTTP() || !(_method & request->method())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_uri.length() && (_uri != request->url() && !request->url().startsWith(_uri + "/")))
|
||||
if (_uri.length() && (_uri != request->url() && !request->url().startsWith(_uri + "/"))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (request->method() != HTTP_GET && !request->contentType().equalsIgnoreCase(asyncsrv::T_application_json))
|
||||
if (request->method() != HTTP_GET && !request->contentType().equalsIgnoreCase(asyncsrv::T_application_json)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -141,6 +150,13 @@ void AsyncCallbackJsonWebHandler::handleBody(AsyncWebServerRequest* request, uin
|
|||
_contentLength = total;
|
||||
if (total > 0 && request->_tempObject == NULL && total < _maxContentLength) {
|
||||
request->_tempObject = malloc(total);
|
||||
if (request->_tempObject == NULL) {
|
||||
#ifdef ESP32
|
||||
log_e("Failed to allocate");
|
||||
#endif
|
||||
request->abort();
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (request->_tempObject != NULL) {
|
||||
memcpy((uint8_t *)(request->_tempObject) + index, data, len);
|
||||
|
|
|
@ -1,37 +1,6 @@
|
|||
// AsyncJson.h
|
||||
/*
|
||||
Async Response to use with ArduinoJson and AsyncWebServer
|
||||
Written by Andrew Melvin (SticilFace) with help from me-no-dev and BBlanchon.
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// Copyright 2016-2025 Hristo Gochkov, Mathieu Carbou, Emil Muratov
|
||||
|
||||
Example of callback in use
|
||||
|
||||
server.on("/json", HTTP_ANY, [](AsyncWebServerRequest * request) {
|
||||
|
||||
AsyncJsonResponse * response = new AsyncJsonResponse();
|
||||
JsonObject& root = response->getRoot();
|
||||
root["key1"] = "key number one";
|
||||
JsonObject& nested = root.createNestedObject("nested");
|
||||
nested["key1"] = "key number one";
|
||||
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
});
|
||||
|
||||
--------------------
|
||||
|
||||
Async Request to use with ArduinoJson and AsyncWebServer
|
||||
Written by Arsène von Wyss (avonwyss)
|
||||
|
||||
Example
|
||||
|
||||
AsyncCallbackJsonWebHandler* handler = new AsyncCallbackJsonWebHandler("/rest/endpoint");
|
||||
handler->onRequest([](AsyncWebServerRequest *request, JsonVariant &json) {
|
||||
JsonObject jsonObj = json.as<JsonObject>();
|
||||
// ...
|
||||
});
|
||||
server.addHandler(handler);
|
||||
|
||||
*/
|
||||
#ifndef ASYNC_JSON_H_
|
||||
#define ASYNC_JSON_H_
|
||||
|
||||
|
@ -74,13 +43,21 @@ class AsyncJsonResponse : public AsyncAbstractResponse {
|
|||
#else
|
||||
AsyncJsonResponse(bool isArray = false);
|
||||
#endif
|
||||
JsonVariant& getRoot() { return _root; }
|
||||
bool _sourceValid() const { return _isValid; }
|
||||
JsonVariant &getRoot() {
|
||||
return _root;
|
||||
}
|
||||
bool _sourceValid() const {
|
||||
return _isValid;
|
||||
}
|
||||
size_t setLength();
|
||||
size_t getSize() const { return _jsonBuffer.size(); }
|
||||
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(); }
|
||||
bool overflowed() const {
|
||||
return _jsonBuffer.overflowed();
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
|
@ -115,15 +92,26 @@ class AsyncCallbackJsonWebHandler : public AsyncWebHandler {
|
|||
AsyncCallbackJsonWebHandler(const String &uri, ArJsonRequestHandlerFunction onRequest = nullptr);
|
||||
#endif
|
||||
|
||||
void setMethod(WebRequestMethodComposite method) { _method = method; }
|
||||
void setMaxContentLength(int maxContentLength) { _maxContentLength = maxContentLength; }
|
||||
void onRequest(ArJsonRequestHandlerFunction fn) { _onRequest = fn; }
|
||||
void setMethod(WebRequestMethodComposite method) {
|
||||
_method = method;
|
||||
}
|
||||
void setMaxContentLength(int maxContentLength) {
|
||||
_maxContentLength = maxContentLength;
|
||||
}
|
||||
void onRequest(ArJsonRequestHandlerFunction fn) {
|
||||
_onRequest = fn;
|
||||
}
|
||||
|
||||
bool canHandle(AsyncWebServerRequest *request) const override final;
|
||||
void handleRequest(AsyncWebServerRequest *request) override final;
|
||||
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 {}
|
||||
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 {}
|
||||
void handleBody(AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total) override final;
|
||||
bool isRequestHandlerTrivial() const override final { return !_onRequest; }
|
||||
bool isRequestHandlerTrivial() const override final {
|
||||
return !_onRequest;
|
||||
}
|
||||
};
|
||||
|
||||
#endif // ASYNC_JSON_SUPPORT == 1
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// Copyright 2016-2025 Hristo Gochkov, Mathieu Carbou, Emil Muratov
|
||||
|
||||
#include "AsyncMessagePack.h"
|
||||
|
||||
#if ASYNC_MSG_PACK_SUPPORT == 1
|
||||
|
@ -6,20 +9,22 @@
|
|||
AsyncMessagePackResponse::AsyncMessagePackResponse(bool isArray, size_t maxJsonBufferSize) : _jsonBuffer(maxJsonBufferSize), _isValid{false} {
|
||||
_code = 200;
|
||||
_contentType = asyncsrv::T_application_msgpack;
|
||||
if (isArray)
|
||||
if (isArray) {
|
||||
_root = _jsonBuffer.createNestedArray();
|
||||
else
|
||||
} else {
|
||||
_root = _jsonBuffer.createNestedObject();
|
||||
}
|
||||
}
|
||||
#else
|
||||
AsyncMessagePackResponse::AsyncMessagePackResponse(bool isArray) : _isValid{false} {
|
||||
_code = 200;
|
||||
_contentType = asyncsrv::T_application_msgpack;
|
||||
if (isArray)
|
||||
if (isArray) {
|
||||
_root = _jsonBuffer.add<JsonArray>();
|
||||
else
|
||||
} else {
|
||||
_root = _jsonBuffer.add<JsonObject>();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
size_t AsyncMessagePackResponse::setLength() {
|
||||
|
@ -37,7 +42,9 @@ size_t AsyncMessagePackResponse::_fillBuffer(uint8_t* data, size_t len) {
|
|||
}
|
||||
|
||||
#if ARDUINOJSON_VERSION_MAJOR == 6
|
||||
AsyncCallbackMessagePackWebHandler::AsyncCallbackMessagePackWebHandler(const String& uri, ArMessagePackRequestHandlerFunction onRequest, size_t maxJsonBufferSize)
|
||||
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)
|
||||
|
@ -45,14 +52,17 @@ AsyncCallbackMessagePackWebHandler::AsyncCallbackMessagePackWebHandler(const Str
|
|||
#endif
|
||||
|
||||
bool AsyncCallbackMessagePackWebHandler::canHandle(AsyncWebServerRequest *request) const {
|
||||
if (!_onRequest || !request->isHTTP() || !(_method & request->method()))
|
||||
if (!_onRequest || !request->isHTTP() || !(_method & request->method())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_uri.length() && (_uri != request->url() && !request->url().startsWith(_uri + "/")))
|
||||
if (_uri.length() && (_uri != request->url() && !request->url().startsWith(_uri + "/"))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (request->method() != HTTP_GET && !request->contentType().equalsIgnoreCase(asyncsrv::T_application_msgpack))
|
||||
if (request->method() != HTTP_GET && !request->contentType().equalsIgnoreCase(asyncsrv::T_application_msgpack)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -92,6 +102,13 @@ void AsyncCallbackMessagePackWebHandler::handleBody(AsyncWebServerRequest* reque
|
|||
_contentLength = total;
|
||||
if (total > 0 && request->_tempObject == NULL && total < _maxContentLength) {
|
||||
request->_tempObject = malloc(total);
|
||||
if (request->_tempObject == NULL) {
|
||||
#ifdef ESP32
|
||||
log_e("Failed to allocate");
|
||||
#endif
|
||||
request->abort();
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (request->_tempObject != NULL) {
|
||||
memcpy((uint8_t *)(request->_tempObject) + index, data, len);
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// Copyright 2016-2025 Hristo Gochkov, Mathieu Carbou, Emil Muratov
|
||||
|
||||
#pragma once
|
||||
|
||||
/*
|
||||
|
@ -58,13 +61,21 @@ class AsyncMessagePackResponse : public AsyncAbstractResponse {
|
|||
#else
|
||||
AsyncMessagePackResponse(bool isArray = false);
|
||||
#endif
|
||||
JsonVariant& getRoot() { return _root; }
|
||||
bool _sourceValid() const { return _isValid; }
|
||||
JsonVariant &getRoot() {
|
||||
return _root;
|
||||
}
|
||||
bool _sourceValid() const {
|
||||
return _isValid;
|
||||
}
|
||||
size_t setLength();
|
||||
size_t getSize() const { return _jsonBuffer.size(); }
|
||||
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(); }
|
||||
bool overflowed() const {
|
||||
return _jsonBuffer.overflowed();
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
|
@ -83,20 +94,33 @@ class AsyncCallbackMessagePackWebHandler : public AsyncWebHandler {
|
|||
|
||||
public:
|
||||
#if ARDUINOJSON_VERSION_MAJOR == 6
|
||||
AsyncCallbackMessagePackWebHandler(const String& uri, ArMessagePackRequestHandlerFunction onRequest = nullptr, size_t maxJsonBufferSize = DYNAMIC_JSON_DOCUMENT_SIZE);
|
||||
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; }
|
||||
void setMethod(WebRequestMethodComposite method) {
|
||||
_method = method;
|
||||
}
|
||||
void setMaxContentLength(int maxContentLength) {
|
||||
_maxContentLength = maxContentLength;
|
||||
}
|
||||
void onRequest(ArMessagePackRequestHandlerFunction fn) {
|
||||
_onRequest = fn;
|
||||
}
|
||||
|
||||
bool canHandle(AsyncWebServerRequest *request) const override final;
|
||||
void handleRequest(AsyncWebServerRequest *request) override final;
|
||||
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 {}
|
||||
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 {}
|
||||
void handleBody(AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total) override final;
|
||||
bool isRequestHandlerTrivial() const override final { return !_onRequest; }
|
||||
bool isRequestHandlerTrivial() const override final {
|
||||
return !_onRequest;
|
||||
}
|
||||
};
|
||||
|
||||
#endif // ASYNC_MSG_PACK_SUPPORT == 1
|
||||
|
|
|
@ -1,22 +1,32 @@
|
|||
#include "ESPAsyncWebServer.h"
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// Copyright 2016-2025 Hristo Gochkov, Mathieu Carbou, Emil Muratov
|
||||
|
||||
#include <ESPAsyncWebServer.h>
|
||||
|
||||
AsyncWebHeader::AsyncWebHeader(const String &data) {
|
||||
if (!data)
|
||||
if (!data) {
|
||||
return;
|
||||
}
|
||||
int index = data.indexOf(':');
|
||||
if (index < 0)
|
||||
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);
|
||||
if (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);
|
||||
} else {
|
||||
#ifdef ESP32
|
||||
log_e("Failed to allocate");
|
||||
#endif
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// Copyright 2016-2025 Hristo Gochkov, Mathieu Carbou, Emil Muratov
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** Major version number (X.x.x) */
|
||||
#define ASYNCWEBSERVER_VERSION_MAJOR 3
|
||||
/** Minor version number (x.X.x) */
|
||||
#define ASYNCWEBSERVER_VERSION_MINOR 7
|
||||
/** Patch version number (x.x.X) */
|
||||
#define ASYNCWEBSERVER_VERSION_PATCH 2
|
||||
|
||||
/**
|
||||
* Macro to convert version number into an integer
|
||||
*
|
||||
* To be used in comparisons, such as ASYNCWEBSERVER_VERSION >= ASYNCWEBSERVER_VERSION_VAL(2, 0, 0)
|
||||
*/
|
||||
#define ASYNCWEBSERVER_VERSION_VAL(major, minor, patch) ((major << 16) | (minor << 8) | (patch))
|
||||
|
||||
/**
|
||||
* Current version, as an integer
|
||||
*
|
||||
* To be used in comparisons, such as ASYNCWEBSERVER_VERSION_NUM >= ASYNCWEBSERVER_VERSION_VAL(2, 0, 0)
|
||||
*/
|
||||
#define ASYNCWEBSERVER_VERSION_NUM ASYNCWEBSERVER_VERSION_VAL(ASYNCWEBSERVER_VERSION_MAJOR, ASYNCWEBSERVER_VERSION_MINOR, ASYNCWEBSERVER_VERSION_PATCH)
|
||||
|
||||
/**
|
||||
* Current version, as string
|
||||
*/
|
||||
#define df2xstr(s) #s
|
||||
#define df2str(s) df2xstr(s)
|
||||
#define ASYNCWEBSERVER_VERSION df2str(ASYNCWEBSERVER_VERSION_MAJOR) "." df2str(ASYNCWEBSERVER_VERSION_MINOR) "." df2str(ASYNCWEBSERVER_VERSION_PATCH)
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
|
@ -1,23 +1,6 @@
|
|||
/*
|
||||
Asynchronous WebServer library for Espressif MCUs
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// Copyright 2016-2025 Hristo Gochkov, Mathieu Carbou, Emil Muratov
|
||||
|
||||
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
|
||||
*/
|
||||
#include "AsyncWebSocket.h"
|
||||
#include "Arduino.h"
|
||||
|
||||
|
@ -32,18 +15,20 @@
|
|||
#include <SHA1Builder.h>
|
||||
#endif
|
||||
#include <rom/ets_sys.h>
|
||||
#elif defined(TARGET_RP2040) || defined(ESP8266)
|
||||
#elif defined(TARGET_RP2040) || defined(TARGET_RP2350) || defined(PICO_RP2040) || defined(PICO_RP2350) || defined(ESP8266)
|
||||
#include <Hash.h>
|
||||
#endif
|
||||
|
||||
using namespace asyncsrv;
|
||||
|
||||
size_t webSocketSendFrameWindow(AsyncClient *client) {
|
||||
if (!client || !client->canSend())
|
||||
if (!client || !client->canSend()) {
|
||||
return 0;
|
||||
}
|
||||
size_t space = client->space();
|
||||
if (space < 9)
|
||||
if (space < 9) {
|
||||
return 0;
|
||||
}
|
||||
return space - 8;
|
||||
}
|
||||
|
||||
|
@ -66,30 +51,35 @@ size_t webSocketSendFrame(AsyncClient* client, bool final, uint8_t opcode, bool
|
|||
mbuf[2] = rand() % 0xFF;
|
||||
mbuf[3] = rand() % 0xFF;
|
||||
}
|
||||
if (len > 125)
|
||||
if (len > 125) {
|
||||
headLen += 2;
|
||||
}
|
||||
if (space < headLen) {
|
||||
// Serial.println("SF 2");
|
||||
return 0;
|
||||
}
|
||||
space -= headLen;
|
||||
|
||||
if (len > space)
|
||||
if (len > space) {
|
||||
len = space;
|
||||
}
|
||||
|
||||
uint8_t *buf = (uint8_t *)malloc(headLen);
|
||||
if (buf == NULL) {
|
||||
// os_printf("could not malloc %u bytes for frame header\n", headLen);
|
||||
// Serial.println("SF 3");
|
||||
#ifdef ESP32
|
||||
log_e("Failed to allocate");
|
||||
#endif
|
||||
client->abort();
|
||||
return 0;
|
||||
}
|
||||
|
||||
buf[0] = opcode & 0x0F;
|
||||
if (final)
|
||||
if (final) {
|
||||
buf[0] |= 0x80;
|
||||
if (len < 126)
|
||||
}
|
||||
if (len < 126) {
|
||||
buf[1] = len & 0x7F;
|
||||
else {
|
||||
} else {
|
||||
buf[1] = 126;
|
||||
buf[2] = (uint8_t)((len >> 8) & 0xFF);
|
||||
buf[3] = (uint8_t)(len & 0xFF);
|
||||
|
@ -109,9 +99,10 @@ size_t webSocketSendFrame(AsyncClient* client, bool final, uint8_t opcode, bool
|
|||
if (len) {
|
||||
if (len && mask) {
|
||||
size_t i;
|
||||
for (i = 0; i < len; i++)
|
||||
for (i = 0; i < len; i++) {
|
||||
data[i] = data[i] ^ mbuf[i % 4];
|
||||
}
|
||||
}
|
||||
if (client->add((const char *)data, len) != len) {
|
||||
// os_printf("error adding %lu data bytes\n", len);
|
||||
// Serial.println("SF 5");
|
||||
|
@ -131,8 +122,7 @@ size_t webSocketSendFrame(AsyncClient* client, bool final, uint8_t opcode, bool
|
|||
* AsyncWebSocketMessageBuffer
|
||||
*/
|
||||
|
||||
AsyncWebSocketMessageBuffer::AsyncWebSocketMessageBuffer(const uint8_t* data, size_t size)
|
||||
: _buffer(std::make_shared<std::vector<uint8_t>>(size)) {
|
||||
AsyncWebSocketMessageBuffer::AsyncWebSocketMessageBuffer(const uint8_t *data, size_t size) : _buffer(std::make_shared<std::vector<uint8_t>>(size)) {
|
||||
if (_buffer->capacity() < size) {
|
||||
_buffer->reserve(size);
|
||||
} else {
|
||||
|
@ -140,16 +130,16 @@ AsyncWebSocketMessageBuffer::AsyncWebSocketMessageBuffer(const uint8_t* data, si
|
|||
}
|
||||
}
|
||||
|
||||
AsyncWebSocketMessageBuffer::AsyncWebSocketMessageBuffer(size_t size)
|
||||
: _buffer(std::make_shared<std::vector<uint8_t>>(size)) {
|
||||
AsyncWebSocketMessageBuffer::AsyncWebSocketMessageBuffer(size_t size) : _buffer(std::make_shared<std::vector<uint8_t>>(size)) {
|
||||
if (_buffer->capacity() < size) {
|
||||
_buffer->reserve(size);
|
||||
}
|
||||
}
|
||||
|
||||
bool AsyncWebSocketMessageBuffer::reserve(size_t size) {
|
||||
if (_buffer->capacity() >= size)
|
||||
if (_buffer->capacity() >= size) {
|
||||
return true;
|
||||
}
|
||||
_buffer->reserve(size);
|
||||
return _buffer->capacity() >= size;
|
||||
}
|
||||
|
@ -169,30 +159,44 @@ class AsyncWebSocketControl {
|
|||
public:
|
||||
AsyncWebSocketControl(uint8_t opcode, const uint8_t *data = NULL, size_t len = 0, bool mask = false)
|
||||
: _opcode(opcode), _len(len), _mask(len && mask), _finished(false) {
|
||||
if (data == NULL)
|
||||
if (data == NULL) {
|
||||
_len = 0;
|
||||
}
|
||||
if (_len) {
|
||||
if (_len > 125)
|
||||
if (_len > 125) {
|
||||
_len = 125;
|
||||
}
|
||||
|
||||
_data = (uint8_t *)malloc(_len);
|
||||
|
||||
if (_data == NULL)
|
||||
if (_data == NULL) {
|
||||
#ifdef ESP32
|
||||
log_e("Failed to allocate");
|
||||
#endif
|
||||
_len = 0;
|
||||
else
|
||||
} else {
|
||||
memcpy(_data, data, len);
|
||||
} else
|
||||
}
|
||||
} else {
|
||||
_data = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
~AsyncWebSocketControl() {
|
||||
if (_data != NULL)
|
||||
if (_data != NULL) {
|
||||
free(_data);
|
||||
}
|
||||
}
|
||||
|
||||
bool finished() const { return _finished; }
|
||||
uint8_t opcode() { return _opcode; }
|
||||
uint8_t len() { return _len + 2; }
|
||||
bool finished() const {
|
||||
return _finished;
|
||||
}
|
||||
uint8_t opcode() {
|
||||
return _opcode;
|
||||
}
|
||||
uint8_t len() {
|
||||
return _len + 2;
|
||||
}
|
||||
size_t send(AsyncClient *client) {
|
||||
_finished = true;
|
||||
return webSocketSendFrame(client, true, _opcode & 0x0F, _mask, _data, _len);
|
||||
|
@ -203,11 +207,8 @@ class AsyncWebSocketControl {
|
|||
* AsyncWebSocketMessage Message
|
||||
*/
|
||||
|
||||
AsyncWebSocketMessage::AsyncWebSocketMessage(AsyncWebSocketSharedBuffer buffer, uint8_t opcode, bool mask) : _WSbuffer{buffer},
|
||||
_opcode(opcode & 0x07),
|
||||
_mask{mask},
|
||||
_status{_WSbuffer ? WS_MSG_SENDING : WS_MSG_ERROR} {
|
||||
}
|
||||
AsyncWebSocketMessage::AsyncWebSocketMessage(AsyncWebSocketSharedBuffer buffer, uint8_t opcode, bool mask)
|
||||
: _WSbuffer{buffer}, _opcode(opcode & 0x07), _mask{mask}, _status{_WSbuffer ? WS_MSG_SENDING : WS_MSG_ERROR} {}
|
||||
|
||||
void AsyncWebSocketMessage::ack(size_t len, uint32_t time) {
|
||||
(void)time;
|
||||
|
@ -219,17 +220,20 @@ void AsyncWebSocketMessage::ack(size_t len, uint32_t time) {
|
|||
}
|
||||
|
||||
size_t AsyncWebSocketMessage::send(AsyncClient *client) {
|
||||
if (!client)
|
||||
if (!client) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (_status != WS_MSG_SENDING)
|
||||
if (_status != WS_MSG_SENDING) {
|
||||
return 0;
|
||||
}
|
||||
if (_acked < _ack) {
|
||||
return 0;
|
||||
}
|
||||
if (_sent == _WSbuffer->size()) {
|
||||
if (_acked == _ack)
|
||||
if (_acked == _ack) {
|
||||
_status = WS_MSG_SENT;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
if (_sent > _WSbuffer->size()) {
|
||||
|
@ -271,8 +275,7 @@ size_t AsyncWebSocketMessage::send(AsyncClient* client) {
|
|||
const char *AWSC_PING_PAYLOAD = "ESPAsyncWebServer-PING";
|
||||
const size_t AWSC_PING_PAYLOAD_LEN = 22;
|
||||
|
||||
AsyncWebSocketClient::AsyncWebSocketClient(AsyncWebServerRequest* request, AsyncWebSocket* server)
|
||||
: _tempObject(NULL) {
|
||||
AsyncWebSocketClient::AsyncWebSocketClient(AsyncWebServerRequest *request, AsyncWebSocket *server) : _tempObject(NULL) {
|
||||
_client = request->client();
|
||||
_server = server;
|
||||
_clientId = _server->_getNextId();
|
||||
|
@ -281,12 +284,48 @@ AsyncWebSocketClient::AsyncWebSocketClient(AsyncWebServerRequest* request, Async
|
|||
_lastMessageTime = millis();
|
||||
_keepAlivePeriod = 0;
|
||||
_client->setRxTimeout(0);
|
||||
_client->onError([](void* r, AsyncClient* c, int8_t error) { (void)c; ((AsyncWebSocketClient*)(r))->_onError(error); }, this);
|
||||
_client->onAck([](void* r, AsyncClient* c, size_t len, uint32_t time) { (void)c; ((AsyncWebSocketClient*)(r))->_onAck(len, time); }, this);
|
||||
_client->onDisconnect([](void* r, AsyncClient* c) { ((AsyncWebSocketClient*)(r))->_onDisconnect(); delete c; }, this);
|
||||
_client->onTimeout([](void* r, AsyncClient* c, uint32_t time) { (void)c; ((AsyncWebSocketClient*)(r))->_onTimeout(time); }, this);
|
||||
_client->onData([](void* r, AsyncClient* c, void* buf, size_t len) { (void)c; ((AsyncWebSocketClient*)(r))->_onData(buf, len); }, this);
|
||||
_client->onPoll([](void* r, AsyncClient* c) { (void)c; ((AsyncWebSocketClient*)(r))->_onPoll(); }, this);
|
||||
_client->onError(
|
||||
[](void *r, AsyncClient *c, int8_t error) {
|
||||
(void)c;
|
||||
((AsyncWebSocketClient *)(r))->_onError(error);
|
||||
},
|
||||
this
|
||||
);
|
||||
_client->onAck(
|
||||
[](void *r, AsyncClient *c, size_t len, uint32_t time) {
|
||||
(void)c;
|
||||
((AsyncWebSocketClient *)(r))->_onAck(len, time);
|
||||
},
|
||||
this
|
||||
);
|
||||
_client->onDisconnect(
|
||||
[](void *r, AsyncClient *c) {
|
||||
((AsyncWebSocketClient *)(r))->_onDisconnect();
|
||||
delete c;
|
||||
},
|
||||
this
|
||||
);
|
||||
_client->onTimeout(
|
||||
[](void *r, AsyncClient *c, uint32_t time) {
|
||||
(void)c;
|
||||
((AsyncWebSocketClient *)(r))->_onTimeout(time);
|
||||
},
|
||||
this
|
||||
);
|
||||
_client->onData(
|
||||
[](void *r, AsyncClient *c, void *buf, size_t len) {
|
||||
(void)c;
|
||||
((AsyncWebSocketClient *)(r))->_onData(buf, len);
|
||||
},
|
||||
this
|
||||
);
|
||||
_client->onPoll(
|
||||
[](void *r, AsyncClient *c) {
|
||||
(void)c;
|
||||
((AsyncWebSocketClient *)(r))->_onPoll();
|
||||
},
|
||||
this
|
||||
);
|
||||
delete request;
|
||||
memset(&_pinfo, 0, sizeof(_pinfo));
|
||||
}
|
||||
|
@ -303,9 +342,10 @@ AsyncWebSocketClient::~AsyncWebSocketClient() {
|
|||
}
|
||||
|
||||
void AsyncWebSocketClient::_clearQueue() {
|
||||
while (!_messageQueue.empty() && _messageQueue.front().finished())
|
||||
while (!_messageQueue.empty() && _messageQueue.front().finished()) {
|
||||
_messageQueue.pop_front();
|
||||
}
|
||||
}
|
||||
|
||||
void AsyncWebSocketClient::_onAck(size_t len, uint32_t time) {
|
||||
_lastMessageTime = millis();
|
||||
|
@ -321,8 +361,9 @@ void AsyncWebSocketClient::_onAck(size_t len, uint32_t time) {
|
|||
if (_status == WS_DISCONNECTING && head.opcode() == WS_DISCONNECT) {
|
||||
_controlQueue.pop_front();
|
||||
_status = WS_DISCONNECTED;
|
||||
if (_client)
|
||||
if (_client) {
|
||||
_client->close(true);
|
||||
}
|
||||
return;
|
||||
}
|
||||
_controlQueue.pop_front();
|
||||
|
@ -339,8 +380,9 @@ void AsyncWebSocketClient::_onAck(size_t len, uint32_t time) {
|
|||
}
|
||||
|
||||
void AsyncWebSocketClient::_onPoll() {
|
||||
if (!_client)
|
||||
if (!_client) {
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef ESP32
|
||||
std::unique_lock<std::mutex> lock(_lock);
|
||||
|
@ -357,12 +399,14 @@ void AsyncWebSocketClient::_onPoll() {
|
|||
|
||||
void AsyncWebSocketClient::_runQueue() {
|
||||
// all calls to this method MUST be protected by a mutex lock!
|
||||
if (!_client)
|
||||
if (!_client) {
|
||||
return;
|
||||
}
|
||||
|
||||
_clearQueue();
|
||||
|
||||
if (!_controlQueue.empty() && (_messageQueue.empty() || _messageQueue.front().betweenFrames()) && webSocketSendFrameWindow(_client) > (size_t)(_controlQueue.front().len() - 1)) {
|
||||
if (!_controlQueue.empty() && (_messageQueue.empty() || _messageQueue.front().betweenFrames())
|
||||
&& webSocketSendFrameWindow(_client) > (size_t)(_controlQueue.front().len() - 1)) {
|
||||
_controlQueue.front().send(_client);
|
||||
} else if (!_messageQueue.empty() && _messageQueue.front().betweenFrames() && webSocketSendFrameWindow(_client)) {
|
||||
_messageQueue.front().send(_client);
|
||||
|
@ -391,8 +435,9 @@ bool AsyncWebSocketClient::canSend() const {
|
|||
}
|
||||
|
||||
bool AsyncWebSocketClient::_queueControl(uint8_t opcode, const uint8_t *data, size_t len, bool mask) {
|
||||
if (!_client)
|
||||
if (!_client) {
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef ESP32
|
||||
std::lock_guard<std::mutex> lock(_lock);
|
||||
|
@ -400,15 +445,17 @@ bool AsyncWebSocketClient::_queueControl(uint8_t opcode, const uint8_t* data, si
|
|||
|
||||
_controlQueue.emplace_back(opcode, data, len, mask);
|
||||
|
||||
if (_client && _client->canSend())
|
||||
if (_client && _client->canSend()) {
|
||||
_runQueue();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AsyncWebSocketClient::_queueMessage(AsyncWebSocketSharedBuffer buffer, uint8_t opcode, bool mask) {
|
||||
if (!_client || buffer->size() == 0 || _status != WS_CONNECTED)
|
||||
if (!_client || buffer->size() == 0 || _status != WS_CONNECTED) {
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef ESP32
|
||||
std::lock_guard<std::mutex> lock(_lock);
|
||||
|
@ -418,8 +465,9 @@ bool AsyncWebSocketClient::_queueMessage(AsyncWebSocketSharedBuffer buffer, uint
|
|||
if (closeWhenFull) {
|
||||
_status = WS_DISCONNECTED;
|
||||
|
||||
if (_client)
|
||||
if (_client) {
|
||||
_client->close(true);
|
||||
}
|
||||
|
||||
#ifdef ESP8266
|
||||
ets_printf("AsyncWebSocketClient::_queueMessage: Too many messages queued: closing connection\n");
|
||||
|
@ -440,15 +488,17 @@ bool AsyncWebSocketClient::_queueMessage(AsyncWebSocketSharedBuffer buffer, uint
|
|||
|
||||
_messageQueue.emplace_back(buffer, opcode, mask);
|
||||
|
||||
if (_client && _client->canSend())
|
||||
if (_client && _client->canSend()) {
|
||||
_runQueue();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void AsyncWebSocketClient::close(uint16_t code, const char *message) {
|
||||
if (_status != WS_CONNECTED)
|
||||
if (_status != WS_CONNECTED) {
|
||||
return;
|
||||
}
|
||||
|
||||
_status = WS_DISCONNECTING;
|
||||
|
||||
|
@ -456,8 +506,9 @@ void AsyncWebSocketClient::close(uint16_t code, const char* message) {
|
|||
uint8_t packetLen = 2;
|
||||
if (message != NULL) {
|
||||
size_t mlen = strlen(message);
|
||||
if (mlen > 123)
|
||||
if (mlen > 123) {
|
||||
mlen = 123;
|
||||
}
|
||||
packetLen += mlen;
|
||||
}
|
||||
char *buf = (char *)malloc(packetLen);
|
||||
|
@ -470,6 +521,11 @@ void AsyncWebSocketClient::close(uint16_t code, const char* message) {
|
|||
_queueControl(WS_DISCONNECT, (uint8_t *)buf, packetLen);
|
||||
free(buf);
|
||||
return;
|
||||
} else {
|
||||
#ifdef ESP32
|
||||
log_e("Failed to allocate");
|
||||
_client->abort();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
_queueControl(WS_DISCONNECT);
|
||||
|
@ -484,8 +540,9 @@ void AsyncWebSocketClient::_onError(int8_t) {
|
|||
}
|
||||
|
||||
void AsyncWebSocketClient::_onTimeout(uint32_t time) {
|
||||
if (!_client)
|
||||
if (!_client) {
|
||||
return;
|
||||
}
|
||||
// Serial.println("onTime");
|
||||
(void)time;
|
||||
_client->close(true);
|
||||
|
@ -522,12 +579,14 @@ void AsyncWebSocketClient::_onData(void* pbuf, size_t plen) {
|
|||
plen -= 2;
|
||||
|
||||
} else if (_pinfo.len == 127 && plen >= 8) {
|
||||
_pinfo.len = fdata[9] | (uint16_t)(fdata[8]) << 8 | (uint32_t)(fdata[7]) << 16 | (uint32_t)(fdata[6]) << 24 | (uint64_t)(fdata[5]) << 32 | (uint64_t)(fdata[4]) << 40 | (uint64_t)(fdata[3]) << 48 | (uint64_t)(fdata[2]) << 56;
|
||||
_pinfo.len = fdata[9] | (uint16_t)(fdata[8]) << 8 | (uint32_t)(fdata[7]) << 16 | (uint32_t)(fdata[6]) << 24 | (uint64_t)(fdata[5]) << 32
|
||||
| (uint64_t)(fdata[4]) << 40 | (uint64_t)(fdata[3]) << 48 | (uint64_t)(fdata[2]) << 56;
|
||||
data += 8;
|
||||
plen -= 8;
|
||||
}
|
||||
|
||||
if (_pinfo.masked && plen >= 4) { // if ws.close() is called, Safari sends a close frame with plen 2 and masked bit set. We must not decrement plen which is already 0.
|
||||
if (_pinfo.masked
|
||||
&& plen >= 4) { // if ws.close() is called, Safari sends a close frame with plen 2 and masked bit set. We must not decrement plen which is already 0.
|
||||
memcpy(_pinfo.mask, data, 4);
|
||||
data += 4;
|
||||
plen -= 4;
|
||||
|
@ -538,9 +597,10 @@ void AsyncWebSocketClient::_onData(void* pbuf, size_t plen) {
|
|||
const auto datalast = data[datalen];
|
||||
|
||||
if (_pinfo.masked) {
|
||||
for (size_t i = 0; i < datalen; i++)
|
||||
for (size_t i = 0; i < datalen; i++) {
|
||||
data[i] ^= _pinfo.mask[(_pinfo.index + i) % 4];
|
||||
}
|
||||
}
|
||||
|
||||
if ((datalen + _pinfo.index) < _pinfo.len) {
|
||||
_pstate = 1;
|
||||
|
@ -551,8 +611,9 @@ void AsyncWebSocketClient::_onData(void* pbuf, size_t plen) {
|
|||
_pinfo.num = 0;
|
||||
}
|
||||
}
|
||||
if (datalen > 0)
|
||||
if (datalen > 0) {
|
||||
_server->_handleEvent(this, WS_EVT_DATA, (void *)&_pinfo, data, datalen);
|
||||
}
|
||||
|
||||
_pinfo.index += datalen;
|
||||
} else if ((datalen + _pinfo.index) == _pinfo.len) {
|
||||
|
@ -567,27 +628,31 @@ void AsyncWebSocketClient::_onData(void* pbuf, size_t plen) {
|
|||
}
|
||||
if (_status == WS_DISCONNECTING) {
|
||||
_status = WS_DISCONNECTED;
|
||||
if (_client)
|
||||
if (_client) {
|
||||
_client->close(true);
|
||||
}
|
||||
} else {
|
||||
_status = WS_DISCONNECTING;
|
||||
if (_client)
|
||||
if (_client) {
|
||||
_client->ackLater();
|
||||
}
|
||||
_queueControl(WS_DISCONNECT, data, datalen);
|
||||
}
|
||||
} else if (_pinfo.opcode == WS_PING) {
|
||||
_server->_handleEvent(this, WS_EVT_PING, NULL, NULL, 0);
|
||||
_queueControl(WS_PONG, data, datalen);
|
||||
} else if (_pinfo.opcode == WS_PONG) {
|
||||
if (datalen != AWSC_PING_PAYLOAD_LEN || memcmp(AWSC_PING_PAYLOAD, data, AWSC_PING_PAYLOAD_LEN) != 0)
|
||||
if (datalen != AWSC_PING_PAYLOAD_LEN || memcmp(AWSC_PING_PAYLOAD, data, AWSC_PING_PAYLOAD_LEN) != 0) {
|
||||
_server->_handleEvent(this, WS_EVT_PONG, NULL, NULL, 0);
|
||||
}
|
||||
} else if (_pinfo.opcode < WS_DISCONNECT) { // continuation or text/binary frame
|
||||
_server->_handleEvent(this, WS_EVT_DATA, (void *)&_pinfo, data, datalen);
|
||||
if (_pinfo.final)
|
||||
if (_pinfo.final) {
|
||||
_pinfo.num = 0;
|
||||
else
|
||||
} else {
|
||||
_pinfo.num += 1;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// os_printf("frame error: len: %u, index: %llu, total: %llu\n", datalen, _pinfo.index, _pinfo.len);
|
||||
// what should we do?
|
||||
|
@ -595,8 +660,9 @@ void AsyncWebSocketClient::_onData(void* pbuf, size_t plen) {
|
|||
}
|
||||
|
||||
// restore byte as _handleEvent may have added a null terminator i.e., data[len] = 0;
|
||||
if (datalen)
|
||||
if (datalen) {
|
||||
data[datalen] = datalast;
|
||||
}
|
||||
|
||||
data += datalen;
|
||||
plen -= datalen;
|
||||
|
@ -609,13 +675,15 @@ size_t AsyncWebSocketClient::printf(const char* format, ...) {
|
|||
size_t len = vsnprintf(nullptr, 0, format, arg);
|
||||
va_end(arg);
|
||||
|
||||
if (len == 0)
|
||||
if (len == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
char *buffer = new char[len + 1];
|
||||
|
||||
if (!buffer)
|
||||
if (!buffer) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
va_start(arg, format);
|
||||
len = vsnprintf(buffer, len + 1, format, arg);
|
||||
|
@ -633,13 +701,15 @@ size_t AsyncWebSocketClient::printf_P(PGM_P formatP, ...) {
|
|||
size_t len = vsnprintf_P(nullptr, 0, formatP, arg);
|
||||
va_end(arg);
|
||||
|
||||
if (len == 0)
|
||||
if (len == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
char *buffer = new char[len + 1];
|
||||
|
||||
if (!buffer)
|
||||
if (!buffer) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
va_start(arg, formatP);
|
||||
len = vsnprintf_P(buffer, len + 1, formatP, arg);
|
||||
|
@ -657,7 +727,7 @@ namespace {
|
|||
std::memcpy(buffer->data(), message, len);
|
||||
return buffer;
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
bool AsyncWebSocketClient::text(AsyncWebSocketMessageBuffer *buffer) {
|
||||
bool enqueued = false;
|
||||
|
@ -694,8 +764,9 @@ bool AsyncWebSocketClient::text(const __FlashStringHelper* data) {
|
|||
|
||||
size_t n = 0;
|
||||
while (1) {
|
||||
if (pgm_read_byte(p + n) == 0)
|
||||
if (pgm_read_byte(p + n) == 0) {
|
||||
break;
|
||||
}
|
||||
n += 1;
|
||||
}
|
||||
|
||||
|
@ -755,15 +826,17 @@ bool AsyncWebSocketClient::binary(const __FlashStringHelper* data, size_t len) {
|
|||
#endif
|
||||
|
||||
IPAddress AsyncWebSocketClient::remoteIP() const {
|
||||
if (!_client)
|
||||
if (!_client) {
|
||||
return IPAddress((uint32_t)0U);
|
||||
}
|
||||
|
||||
return _client->remoteIP();
|
||||
}
|
||||
|
||||
uint16_t AsyncWebSocketClient::remotePort() const {
|
||||
if (!_client)
|
||||
if (!_client) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return _client->remotePort();
|
||||
}
|
||||
|
@ -785,48 +858,62 @@ AsyncWebSocketClient* AsyncWebSocket::_newClient(AsyncWebServerRequest* request)
|
|||
}
|
||||
|
||||
bool AsyncWebSocket::availableForWriteAll() {
|
||||
return std::none_of(std::begin(_clients), std::end(_clients), [](const AsyncWebSocketClient& c) { return c.queueIsFull(); });
|
||||
return std::none_of(std::begin(_clients), std::end(_clients), [](const AsyncWebSocketClient &c) {
|
||||
return c.queueIsFull();
|
||||
});
|
||||
}
|
||||
|
||||
bool AsyncWebSocket::availableForWrite(uint32_t id) {
|
||||
const auto iter = std::find_if(std::begin(_clients), std::end(_clients), [id](const AsyncWebSocketClient& c) { return c.id() == id; });
|
||||
if (iter == std::end(_clients))
|
||||
const auto iter = std::find_if(std::begin(_clients), std::end(_clients), [id](const AsyncWebSocketClient &c) {
|
||||
return c.id() == id;
|
||||
});
|
||||
if (iter == std::end(_clients)) {
|
||||
return true;
|
||||
}
|
||||
return !iter->queueIsFull();
|
||||
}
|
||||
|
||||
size_t AsyncWebSocket::count() const {
|
||||
return std::count_if(std::begin(_clients), std::end(_clients), [](const AsyncWebSocketClient& c) { return c.status() == WS_CONNECTED; });
|
||||
return std::count_if(std::begin(_clients), std::end(_clients), [](const AsyncWebSocketClient &c) {
|
||||
return c.status() == WS_CONNECTED;
|
||||
});
|
||||
}
|
||||
|
||||
AsyncWebSocketClient *AsyncWebSocket::client(uint32_t id) {
|
||||
const auto iter = std::find_if(_clients.begin(), _clients.end(), [id](const AsyncWebSocketClient& c) { return c.id() == id && c.status() == WS_CONNECTED; });
|
||||
if (iter == std::end(_clients))
|
||||
const auto iter = std::find_if(_clients.begin(), _clients.end(), [id](const AsyncWebSocketClient &c) {
|
||||
return c.id() == id && c.status() == WS_CONNECTED;
|
||||
});
|
||||
if (iter == std::end(_clients)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return &(*iter);
|
||||
}
|
||||
|
||||
void AsyncWebSocket::close(uint32_t id, uint16_t code, const char *message) {
|
||||
if (AsyncWebSocketClient* c = client(id))
|
||||
if (AsyncWebSocketClient *c = client(id)) {
|
||||
c->close(code, message);
|
||||
}
|
||||
}
|
||||
|
||||
void AsyncWebSocket::closeAll(uint16_t code, const char *message) {
|
||||
for (auto& c : _clients)
|
||||
if (c.status() == WS_CONNECTED)
|
||||
for (auto &c : _clients) {
|
||||
if (c.status() == WS_CONNECTED) {
|
||||
c.close(code, message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AsyncWebSocket::cleanupClients(uint16_t maxClients) {
|
||||
if (count() > maxClients)
|
||||
if (count() > maxClients) {
|
||||
_clients.front().close();
|
||||
}
|
||||
|
||||
for (auto iter = std::begin(_clients); iter != std::end(_clients);) {
|
||||
if (iter->shouldBeDeleted())
|
||||
iter = _clients.erase(iter);
|
||||
else
|
||||
iter++;
|
||||
for (auto i = _clients.begin(); i != _clients.end(); ++i) {
|
||||
if (i->shouldBeDeleted()) {
|
||||
_clients.erase(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -838,11 +925,13 @@ bool AsyncWebSocket::ping(uint32_t id, const uint8_t* data, size_t len) {
|
|||
AsyncWebSocket::SendStatus AsyncWebSocket::pingAll(const uint8_t *data, size_t len) {
|
||||
size_t hit = 0;
|
||||
size_t miss = 0;
|
||||
for (auto& c : _clients)
|
||||
if (c.status() == WS_CONNECTED && c.ping(data, len))
|
||||
for (auto &c : _clients) {
|
||||
if (c.status() == WS_CONNECTED && c.ping(data, len)) {
|
||||
hit++;
|
||||
else
|
||||
} else {
|
||||
miss++;
|
||||
}
|
||||
}
|
||||
return hit == 0 ? DISCARDED : (miss == 0 ? ENQUEUED : PARTIALLY_ENQUEUED);
|
||||
}
|
||||
|
||||
|
@ -866,8 +955,9 @@ bool AsyncWebSocket::text(uint32_t id, const __FlashStringHelper* data) {
|
|||
|
||||
size_t n = 0;
|
||||
while (true) {
|
||||
if (pgm_read_byte(p + n) == 0)
|
||||
if (pgm_read_byte(p + n) == 0) {
|
||||
break;
|
||||
}
|
||||
n += 1;
|
||||
}
|
||||
|
||||
|
@ -914,8 +1004,9 @@ AsyncWebSocket::SendStatus AsyncWebSocket::textAll(const __FlashStringHelper* da
|
|||
|
||||
size_t n = 0;
|
||||
while (1) {
|
||||
if (pgm_read_byte(p + n) == 0)
|
||||
if (pgm_read_byte(p + n) == 0) {
|
||||
break;
|
||||
}
|
||||
n += 1;
|
||||
}
|
||||
|
||||
|
@ -942,11 +1033,13 @@ AsyncWebSocket::SendStatus AsyncWebSocket::textAll(AsyncWebSocketMessageBuffer*
|
|||
AsyncWebSocket::SendStatus AsyncWebSocket::textAll(AsyncWebSocketSharedBuffer buffer) {
|
||||
size_t hit = 0;
|
||||
size_t miss = 0;
|
||||
for (auto& c : _clients)
|
||||
if (c.status() == WS_CONNECTED && c.text(buffer))
|
||||
for (auto &c : _clients) {
|
||||
if (c.status() == WS_CONNECTED && c.text(buffer)) {
|
||||
hit++;
|
||||
else
|
||||
} else {
|
||||
miss++;
|
||||
}
|
||||
}
|
||||
return hit == 0 ? DISCARDED : (miss == 0 ? ENQUEUED : PARTIALLY_ENQUEUED);
|
||||
}
|
||||
|
||||
|
@ -1029,11 +1122,13 @@ AsyncWebSocket::SendStatus AsyncWebSocket::binaryAll(AsyncWebSocketMessageBuffer
|
|||
AsyncWebSocket::SendStatus AsyncWebSocket::binaryAll(AsyncWebSocketSharedBuffer buffer) {
|
||||
size_t hit = 0;
|
||||
size_t miss = 0;
|
||||
for (auto& c : _clients)
|
||||
if (c.status() == WS_CONNECTED && c.binary(buffer))
|
||||
for (auto &c : _clients) {
|
||||
if (c.status() == WS_CONNECTED && c.binary(buffer)) {
|
||||
hit++;
|
||||
else
|
||||
} else {
|
||||
miss++;
|
||||
}
|
||||
}
|
||||
return hit == 0 ? DISCARDED : (miss == 0 ? ENQUEUED : PARTIALLY_ENQUEUED);
|
||||
}
|
||||
|
||||
|
@ -1055,13 +1150,15 @@ size_t AsyncWebSocket::printfAll(const char* format, ...) {
|
|||
size_t len = vsnprintf(nullptr, 0, format, arg);
|
||||
va_end(arg);
|
||||
|
||||
if (len == 0)
|
||||
if (len == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
char *buffer = new char[len + 1];
|
||||
|
||||
if (!buffer)
|
||||
if (!buffer) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
va_start(arg, format);
|
||||
len = vsnprintf(buffer, len + 1, format, arg);
|
||||
|
@ -1091,13 +1188,15 @@ size_t AsyncWebSocket::printfAll_P(PGM_P formatP, ...) {
|
|||
size_t len = vsnprintf_P(nullptr, 0, formatP, arg);
|
||||
va_end(arg);
|
||||
|
||||
if (len == 0)
|
||||
if (len == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
char *buffer = new char[len + 1];
|
||||
|
||||
if (!buffer)
|
||||
if (!buffer) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
va_start(arg, formatP);
|
||||
len = vsnprintf_P(buffer, len + 1, formatP, arg);
|
||||
|
@ -1155,6 +1254,13 @@ void AsyncWebSocket::handleRequest(AsyncWebServerRequest* request) {
|
|||
}
|
||||
const AsyncWebHeader *key = request->getHeader(WS_STR_KEY);
|
||||
AsyncWebServerResponse *response = new AsyncWebSocketResponse(key->value(), this);
|
||||
if (response == NULL) {
|
||||
#ifdef ESP32
|
||||
log_e("Failed to allocate");
|
||||
#endif
|
||||
request->abort();
|
||||
return;
|
||||
}
|
||||
if (request->hasHeader(WS_STR_PROTOCOL)) {
|
||||
const AsyncWebHeader *protocol = request->getHeader(WS_STR_PROTOCOL);
|
||||
// ToDo: check protocol
|
||||
|
@ -1164,23 +1270,11 @@ void AsyncWebSocket::handleRequest(AsyncWebServerRequest* request) {
|
|||
}
|
||||
|
||||
AsyncWebSocketMessageBuffer *AsyncWebSocket::makeBuffer(size_t size) {
|
||||
AsyncWebSocketMessageBuffer* buffer = new AsyncWebSocketMessageBuffer(size);
|
||||
if (buffer->length() != size) {
|
||||
delete buffer;
|
||||
return nullptr;
|
||||
} else {
|
||||
return buffer;
|
||||
}
|
||||
return new AsyncWebSocketMessageBuffer(size);
|
||||
}
|
||||
|
||||
AsyncWebSocketMessageBuffer *AsyncWebSocket::makeBuffer(const uint8_t *data, size_t size) {
|
||||
AsyncWebSocketMessageBuffer* buffer = new AsyncWebSocketMessageBuffer(data, size);
|
||||
if (buffer->length() != size) {
|
||||
delete buffer;
|
||||
return nullptr;
|
||||
} else {
|
||||
return buffer;
|
||||
}
|
||||
return new AsyncWebSocketMessageBuffer(data, size);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1196,11 +1290,14 @@ AsyncWebSocketResponse::AsyncWebSocketResponse(const String& key, AsyncWebSocket
|
|||
uint8_t hash[20];
|
||||
char buffer[33];
|
||||
|
||||
#if defined(ESP8266) || defined(TARGET_RP2040)
|
||||
#if defined(ESP8266) || defined(TARGET_RP2040) || defined(PICO_RP2040) || defined(PICO_RP2350) || defined(TARGET_RP2350)
|
||||
sha1(key + WS_STR_UUID, hash);
|
||||
#else
|
||||
String k;
|
||||
k.reserve(key.length() + WS_STR_UUID_LEN);
|
||||
if (!k.reserve(key.length() + WS_STR_UUID_LEN)) {
|
||||
log_e("Failed to allocate");
|
||||
return;
|
||||
}
|
||||
k.concat(key);
|
||||
k.concat(WS_STR_UUID);
|
||||
SHA1Builder sha1;
|
||||
|
@ -1232,8 +1329,9 @@ void AsyncWebSocketResponse::_respond(AsyncWebServerRequest* request) {
|
|||
size_t AsyncWebSocketResponse::_ack(AsyncWebServerRequest *request, size_t len, uint32_t time) {
|
||||
(void)time;
|
||||
|
||||
if (len)
|
||||
if (len) {
|
||||
_server->_newClient(request);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -1,29 +1,12 @@
|
|||
/*
|
||||
Asynchronous WebServer library for Espressif MCUs
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// Copyright 2016-2025 Hristo Gochkov, Mathieu Carbou, Emil Muratov
|
||||
|
||||
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 ASYNCWEBSOCKET_H_
|
||||
#define ASYNCWEBSOCKET_H_
|
||||
|
||||
#include <Arduino.h>
|
||||
#ifdef ESP32
|
||||
#include "../../ESP32Async-AsyncTCP/src/AsyncTCP.h"
|
||||
#include <AsyncTCP.h>
|
||||
#include <mutex>
|
||||
#ifndef WS_MAX_QUEUED_MESSAGES
|
||||
#define WS_MAX_QUEUED_MESSAGES 32
|
||||
|
@ -33,14 +16,14 @@
|
|||
#ifndef WS_MAX_QUEUED_MESSAGES
|
||||
#define WS_MAX_QUEUED_MESSAGES 8
|
||||
#endif
|
||||
#elif defined(TARGET_RP2040)
|
||||
#include <AsyncTCP_RP2040W.h>
|
||||
#elif defined(TARGET_RP2040) || defined(TARGET_RP2350) || defined(PICO_RP2040) || defined(PICO_RP2350)
|
||||
#include <RPAsyncTCP.h>
|
||||
#ifndef WS_MAX_QUEUED_MESSAGES
|
||||
#define WS_MAX_QUEUED_MESSAGES 32
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include "ESPAsyncWebServer.h"
|
||||
#include <ESPAsyncWebServer.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
|
@ -90,24 +73,32 @@ typedef struct {
|
|||
uint64_t index;
|
||||
} AwsFrameInfo;
|
||||
|
||||
typedef enum { WS_DISCONNECTED,
|
||||
typedef enum {
|
||||
WS_DISCONNECTED,
|
||||
WS_CONNECTED,
|
||||
WS_DISCONNECTING } AwsClientStatus;
|
||||
typedef enum { WS_CONTINUATION,
|
||||
WS_DISCONNECTING
|
||||
} AwsClientStatus;
|
||||
typedef enum {
|
||||
WS_CONTINUATION,
|
||||
WS_TEXT,
|
||||
WS_BINARY,
|
||||
WS_DISCONNECT = 0x08,
|
||||
WS_PING,
|
||||
WS_PONG } AwsFrameType;
|
||||
typedef enum { WS_MSG_SENDING,
|
||||
WS_PONG
|
||||
} AwsFrameType;
|
||||
typedef enum {
|
||||
WS_MSG_SENDING,
|
||||
WS_MSG_SENT,
|
||||
WS_MSG_ERROR } AwsMessageStatus;
|
||||
typedef enum { WS_EVT_CONNECT,
|
||||
WS_MSG_ERROR
|
||||
} AwsMessageStatus;
|
||||
typedef enum {
|
||||
WS_EVT_CONNECT,
|
||||
WS_EVT_DISCONNECT,
|
||||
WS_EVT_PING,
|
||||
WS_EVT_PONG,
|
||||
WS_EVT_ERROR,
|
||||
WS_EVT_DATA } AwsEventType;
|
||||
WS_EVT_DATA
|
||||
} AwsEventType;
|
||||
|
||||
class AsyncWebSocketMessageBuffer {
|
||||
friend AsyncWebSocket;
|
||||
|
@ -122,8 +113,12 @@ class AsyncWebSocketMessageBuffer {
|
|||
AsyncWebSocketMessageBuffer(const uint8_t *data, size_t size);
|
||||
//~AsyncWebSocketMessageBuffer();
|
||||
bool reserve(size_t size);
|
||||
uint8_t* get() { return _buffer->data(); }
|
||||
size_t length() const { return _buffer->size(); }
|
||||
uint8_t *get() {
|
||||
return _buffer->data();
|
||||
}
|
||||
size_t length() const {
|
||||
return _buffer->size();
|
||||
}
|
||||
};
|
||||
|
||||
class AsyncWebSocketMessage {
|
||||
|
@ -139,8 +134,12 @@ class AsyncWebSocketMessage {
|
|||
public:
|
||||
AsyncWebSocketMessage(AsyncWebSocketSharedBuffer buffer, uint8_t opcode = WS_TEXT, bool mask = false);
|
||||
|
||||
bool finished() const { return _status != WS_MSG_SENDING; }
|
||||
bool betweenFrames() const { return _acked == _ack; }
|
||||
bool finished() const {
|
||||
return _status != WS_MSG_SENDING;
|
||||
}
|
||||
bool betweenFrames() const {
|
||||
return _acked == _ack;
|
||||
}
|
||||
|
||||
void ack(size_t len, uint32_t time);
|
||||
size_t send(AsyncClient *client);
|
||||
|
@ -177,13 +176,27 @@ class AsyncWebSocketClient {
|
|||
~AsyncWebSocketClient();
|
||||
|
||||
// client id increments for the given server
|
||||
uint32_t id() const { return _clientId; }
|
||||
AwsClientStatus status() const { return _status; }
|
||||
AsyncClient* client() { return _client; }
|
||||
const AsyncClient* client() const { return _client; }
|
||||
AsyncWebSocket* server() { return _server; }
|
||||
const AsyncWebSocket* server() const { return _server; }
|
||||
AwsFrameInfo const& pinfo() const { return _pinfo; }
|
||||
uint32_t id() const {
|
||||
return _clientId;
|
||||
}
|
||||
AwsClientStatus status() const {
|
||||
return _status;
|
||||
}
|
||||
AsyncClient *client() {
|
||||
return _client;
|
||||
}
|
||||
const AsyncClient *client() const {
|
||||
return _client;
|
||||
}
|
||||
AsyncWebSocket *server() {
|
||||
return _server;
|
||||
}
|
||||
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.
|
||||
// This is the default behavior in yubox-node-org, which is not silently discarding messages but instead closes the connection.
|
||||
|
@ -203,13 +216,19 @@ class AsyncWebSocketClient {
|
|||
// 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; }
|
||||
void setCloseClientOnQueueFull(bool close) {
|
||||
closeWhenFull = close;
|
||||
}
|
||||
bool willCloseClientOnQueueFull() const {
|
||||
return closeWhenFull;
|
||||
}
|
||||
|
||||
IPAddress remoteIP() const;
|
||||
uint16_t remotePort() const;
|
||||
|
||||
bool shouldBeDeleted() const { return !_client; }
|
||||
bool shouldBeDeleted() const {
|
||||
return !_client;
|
||||
}
|
||||
|
||||
// control frames
|
||||
void close(uint16_t code = 0, const char *message = NULL);
|
||||
|
@ -224,7 +243,9 @@ class AsyncWebSocketClient {
|
|||
}
|
||||
|
||||
// data packets
|
||||
void message(AsyncWebSocketSharedBuffer buffer, uint8_t opcode = WS_TEXT, bool mask = false) { _queueMessage(buffer, opcode, mask); }
|
||||
void message(AsyncWebSocketSharedBuffer buffer, uint8_t opcode = WS_TEXT, bool mask = false) {
|
||||
_queueMessage(buffer, opcode, mask);
|
||||
}
|
||||
bool queueIsFull() const;
|
||||
size_t queueLen() const;
|
||||
|
||||
|
@ -287,15 +308,23 @@ class AsyncWebSocket : public AsyncWebHandler {
|
|||
explicit AsyncWebSocket(const char *url) : _url(url), _cNextId(1), _enabled(true) {}
|
||||
AsyncWebSocket(const String &url) : _url(url), _cNextId(1), _enabled(true) {}
|
||||
~AsyncWebSocket(){};
|
||||
const char* url() const { return _url.c_str(); }
|
||||
void enable(bool e) { _enabled = e; }
|
||||
bool enabled() const { return _enabled; }
|
||||
const char *url() const {
|
||||
return _url.c_str();
|
||||
}
|
||||
void enable(bool e) {
|
||||
_enabled = e;
|
||||
}
|
||||
bool enabled() const {
|
||||
return _enabled;
|
||||
}
|
||||
bool availableForWriteAll();
|
||||
bool availableForWrite(uint32_t id);
|
||||
|
||||
size_t count() const;
|
||||
AsyncWebSocketClient *client(uint32_t id);
|
||||
bool hasClient(uint32_t id) { return client(id) != nullptr; }
|
||||
bool hasClient(uint32_t id) {
|
||||
return client(id) != nullptr;
|
||||
}
|
||||
|
||||
void close(uint32_t id, uint16_t code = 0, const char *message = NULL);
|
||||
void closeAll(uint16_t code = 0, const char *message = NULL);
|
||||
|
@ -344,11 +373,17 @@ class AsyncWebSocket : public AsyncWebHandler {
|
|||
size_t printfAll_P(PGM_P formatP, ...) __attribute__((format(printf, 2, 3)));
|
||||
#endif
|
||||
|
||||
void onEvent(AwsEventHandler handler) { _eventHandler = handler; }
|
||||
void handleHandshake(AwsHandshakeHandler handler) { _handshakeHandler = handler; }
|
||||
void onEvent(AwsEventHandler handler) {
|
||||
_eventHandler = handler;
|
||||
}
|
||||
void handleHandshake(AwsHandshakeHandler handler) {
|
||||
_handshakeHandler = handler;
|
||||
}
|
||||
|
||||
// system callbacks (do not call)
|
||||
uint32_t _getNextId() { return _cNextId++; }
|
||||
uint32_t _getNextId() {
|
||||
return _cNextId++;
|
||||
}
|
||||
AsyncWebSocketClient *_newClient(AsyncWebServerRequest *request);
|
||||
void _handleEvent(AsyncWebSocketClient *client, AwsEventType type, void *arg, uint8_t *data, size_t len);
|
||||
bool canHandle(AsyncWebServerRequest *request) const override final;
|
||||
|
@ -358,7 +393,9 @@ class AsyncWebSocket : public AsyncWebHandler {
|
|||
AsyncWebSocketMessageBuffer *makeBuffer(size_t size = 0);
|
||||
AsyncWebSocketMessageBuffer *makeBuffer(const uint8_t *data, size_t size);
|
||||
|
||||
std::list<AsyncWebSocketClient>& getClients() { return _clients; }
|
||||
std::list<AsyncWebSocketClient> &getClients() {
|
||||
return _clients;
|
||||
}
|
||||
};
|
||||
|
||||
// WebServer response to authenticate the socket and detach the tcp client from the web server request
|
||||
|
@ -371,7 +408,9 @@ class AsyncWebSocketResponse : public AsyncWebServerResponse {
|
|||
AsyncWebSocketResponse(const String &key, AsyncWebSocket *server);
|
||||
void _respond(AsyncWebServerRequest *request);
|
||||
size_t _ack(AsyncWebServerRequest *request, size_t len, uint32_t time);
|
||||
bool _sourceValid() const { return true; }
|
||||
bool _sourceValid() const {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* ASYNCWEBSOCKET_H_ */
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
#include "ChunkPrint.h"
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// Copyright 2016-2025 Hristo Gochkov, Mathieu Carbou, Emil Muratov
|
||||
|
||||
ChunkPrint::ChunkPrint(uint8_t* destination, size_t from, size_t len)
|
||||
: _destination(destination), _to_skip(from), _to_write(len), _pos{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) {
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// Copyright 2016-2025 Hristo Gochkov, Mathieu Carbou, Emil Muratov
|
||||
|
||||
#ifndef CHUNKPRINT_H
|
||||
#define CHUNKPRINT_H
|
||||
|
||||
|
@ -13,6 +16,8 @@ class ChunkPrint : public Print {
|
|||
public:
|
||||
ChunkPrint(uint8_t *destination, size_t from, size_t len);
|
||||
size_t write(uint8_t c);
|
||||
size_t write(const uint8_t* buffer, size_t size) { return this->Print::write(buffer, size); }
|
||||
size_t write(const uint8_t *buffer, size_t size) {
|
||||
return this->Print::write(buffer, size);
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
|
|
@ -1,23 +1,6 @@
|
|||
/*
|
||||
Asynchronous WebServer library for Espressif MCUs
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// Copyright 2016-2025 Hristo Gochkov, Mathieu Carbou, Emil Muratov
|
||||
|
||||
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 _ESPAsyncWebServer_H_
|
||||
#define _ESPAsyncWebServer_H_
|
||||
|
||||
|
@ -32,13 +15,13 @@
|
|||
#include <vector>
|
||||
|
||||
#ifdef ESP32
|
||||
#include "../../ESP32Async-AsyncTCP/src/AsyncTCP.h"
|
||||
#include <AsyncTCP.h>
|
||||
#include <WiFi.h>
|
||||
#elif defined(ESP8266)
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <ESPAsyncTCP.h>
|
||||
#elif defined(TARGET_RP2040)
|
||||
#include <AsyncTCP_RP2040W.h>
|
||||
#elif defined(TARGET_RP2040) || defined(TARGET_RP2350) || defined(PICO_RP2040) || defined(PICO_RP2350)
|
||||
#include <RPAsyncTCP.h>
|
||||
#include <HTTP_Method.h>
|
||||
#include <WiFi.h>
|
||||
#include <http_parser.h>
|
||||
|
@ -48,10 +31,7 @@
|
|||
|
||||
#include "literals.h"
|
||||
|
||||
#define ASYNCWEBSERVER_VERSION "3.6.2"
|
||||
#define ASYNCWEBSERVER_VERSION_MAJOR 3
|
||||
#define ASYNCWEBSERVER_VERSION_MINOR 6
|
||||
#define ASYNCWEBSERVER_VERSION_REVISION 2
|
||||
#include "AsyncWebServerVersion.h"
|
||||
#define ASYNCWEBSERVER_FORK_ESP32Async
|
||||
|
||||
#ifdef ASYNCWEBSERVER_REGEX
|
||||
|
@ -78,6 +58,10 @@ class AsyncCallbackWebHandler;
|
|||
class AsyncResponseStream;
|
||||
class AsyncMiddlewareChain;
|
||||
|
||||
#if defined(TARGET_RP2040) || defined(TARGET_RP2350) || defined(PICO_RP2040) || defined(PICO_RP2350)
|
||||
typedef enum http_method WebRequestMethod;
|
||||
#else
|
||||
#ifndef WEBSERVER_H
|
||||
typedef enum {
|
||||
HTTP_GET = 0b00000001,
|
||||
HTTP_POST = 0b00000010,
|
||||
|
@ -88,6 +72,8 @@ typedef enum {
|
|||
HTTP_OPTIONS = 0b01000000,
|
||||
HTTP_ANY = 0b01111111,
|
||||
} WebRequestMethod;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef HAVE_FS_FILE_OPEN_MODE
|
||||
namespace fs {
|
||||
|
@ -97,7 +83,7 @@ namespace fs {
|
|||
static const char *write;
|
||||
static const char *append;
|
||||
};
|
||||
};
|
||||
}; // namespace fs
|
||||
#else
|
||||
#include "FileOpenMode.h"
|
||||
#endif
|
||||
|
@ -122,12 +108,23 @@ class AsyncWebParameter {
|
|||
bool _isFile;
|
||||
|
||||
public:
|
||||
AsyncWebParameter(const String& name, const String& value, bool form = false, bool file = false, size_t size = 0) : _name(name), _value(value), _size(size), _isForm(form), _isFile(file) {}
|
||||
const String& name() const { return _name; }
|
||||
const String& value() const { return _value; }
|
||||
size_t size() const { return _size; }
|
||||
bool isPost() const { return _isForm; }
|
||||
bool isFile() const { return _isFile; }
|
||||
AsyncWebParameter(const String &name, const String &value, bool form = false, bool file = false, size_t size = 0)
|
||||
: _name(name), _value(value), _size(size), _isForm(form), _isFile(file) {}
|
||||
const String &name() const {
|
||||
return _name;
|
||||
}
|
||||
const String &value() const {
|
||||
return _value;
|
||||
}
|
||||
size_t size() const {
|
||||
return _size;
|
||||
}
|
||||
bool isPost() const {
|
||||
return _isForm;
|
||||
}
|
||||
bool isFile() const {
|
||||
return _isFile;
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -147,8 +144,12 @@ class AsyncWebHeader {
|
|||
|
||||
AsyncWebHeader &operator=(const AsyncWebHeader &) = default;
|
||||
|
||||
const String& name() const { return _name; }
|
||||
const String& value() const { return _value; }
|
||||
const String &name() const {
|
||||
return _name;
|
||||
}
|
||||
const String &value() const {
|
||||
return _value;
|
||||
}
|
||||
String toString() const;
|
||||
};
|
||||
|
||||
|
@ -156,12 +157,14 @@ class AsyncWebHeader {
|
|||
* 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_MAX
|
||||
} RequestedConnectionType;
|
||||
|
||||
// this enum is similar to Arduino WebServer's AsyncAuthType and PsychicHttp
|
||||
typedef enum {
|
||||
|
@ -176,6 +179,8 @@ typedef enum {
|
|||
typedef std::function<size_t(uint8_t *, size_t, size_t)> AwsResponseFiller;
|
||||
typedef std::function<String(const String &)> AwsTemplateProcessor;
|
||||
|
||||
using AsyncWebServerRequestPtr = std::weak_ptr<AsyncWebServerRequest>;
|
||||
|
||||
class AsyncWebServerRequest {
|
||||
using File = fs::File;
|
||||
using FS = fs::FS;
|
||||
|
@ -189,8 +194,9 @@ class AsyncWebServerRequest {
|
|||
AsyncWebServerResponse *_response;
|
||||
ArDisconnectHandler _onDisconnectfn;
|
||||
|
||||
// response is sent
|
||||
bool _sent = false;
|
||||
bool _sent = false; // response is sent
|
||||
bool _paused = false; // request is paused (request continuation)
|
||||
std::shared_ptr<AsyncWebServerRequest> _this; // shared pointer to this request
|
||||
|
||||
String _temp;
|
||||
uint8_t _parseState;
|
||||
|
@ -212,7 +218,7 @@ class AsyncWebServerRequest {
|
|||
|
||||
std::list<AsyncWebHeader> _headers;
|
||||
std::list<AsyncWebParameter> _params;
|
||||
std::vector<String> _pathParams;
|
||||
std::list<String> _pathParams;
|
||||
|
||||
std::unordered_map<const char *, String, std::hash<const char *>, std::equal_to<const char *>> _attributes;
|
||||
|
||||
|
@ -248,6 +254,9 @@ class AsyncWebServerRequest {
|
|||
void _handleUploadByte(uint8_t data, bool last);
|
||||
void _handleUploadEnd();
|
||||
|
||||
void _send();
|
||||
void _runMiddlewareChain();
|
||||
|
||||
public:
|
||||
File _tempFile;
|
||||
void *_tempObject;
|
||||
|
@ -255,23 +264,48 @@ class AsyncWebServerRequest {
|
|||
AsyncWebServerRequest(AsyncWebServer *, AsyncClient *);
|
||||
~AsyncWebServerRequest();
|
||||
|
||||
AsyncClient* client() { return _client; }
|
||||
uint8_t version() const { return _version; }
|
||||
WebRequestMethodComposite method() const { return _method; }
|
||||
const String& url() const { return _url; }
|
||||
const String& host() const { return _host; }
|
||||
const String& contentType() const { return _contentType; }
|
||||
size_t contentLength() const { return _contentLength; }
|
||||
bool multipart() const { return _isMultipart; }
|
||||
AsyncClient *client() {
|
||||
return _client;
|
||||
}
|
||||
uint8_t version() const {
|
||||
return _version;
|
||||
}
|
||||
WebRequestMethodComposite method() const {
|
||||
return _method;
|
||||
}
|
||||
const String &url() const {
|
||||
return _url;
|
||||
}
|
||||
const String &host() const {
|
||||
return _host;
|
||||
}
|
||||
const String &contentType() const {
|
||||
return _contentType;
|
||||
}
|
||||
size_t contentLength() const {
|
||||
return _contentLength;
|
||||
}
|
||||
bool multipart() const {
|
||||
return _isMultipart;
|
||||
}
|
||||
|
||||
const char *methodToString() const;
|
||||
const char *requestedConnTypeToString() const;
|
||||
|
||||
RequestedConnectionType requestedConnType() const { return _reqconntype; }
|
||||
bool isExpectedRequestedConnType(RequestedConnectionType erct1, RequestedConnectionType erct2 = RCT_NOT_USED, RequestedConnectionType erct3 = RCT_NOT_USED) const;
|
||||
bool isWebSocketUpgrade() const { return _method == HTTP_GET && isExpectedRequestedConnType(RCT_WS); }
|
||||
bool isSSE() const { return _method == HTTP_GET && isExpectedRequestedConnType(RCT_EVENT); }
|
||||
bool isHTTP() const { return isExpectedRequestedConnType(RCT_DEFAULT, RCT_HTTP); }
|
||||
RequestedConnectionType requestedConnType() const {
|
||||
return _reqconntype;
|
||||
}
|
||||
bool isExpectedRequestedConnType(RequestedConnectionType erct1, RequestedConnectionType erct2 = RCT_NOT_USED, RequestedConnectionType erct3 = RCT_NOT_USED)
|
||||
const;
|
||||
bool isWebSocketUpgrade() const {
|
||||
return _method == HTTP_GET && isExpectedRequestedConnType(RCT_WS);
|
||||
}
|
||||
bool isSSE() const {
|
||||
return _method == HTTP_GET && isExpectedRequestedConnType(RCT_EVENT);
|
||||
}
|
||||
bool isHTTP() const {
|
||||
return isExpectedRequestedConnType(RCT_DEFAULT, RCT_HTTP);
|
||||
}
|
||||
void onDisconnect(ArDisconnectHandler fn);
|
||||
|
||||
// hash is the string representation of:
|
||||
|
@ -279,10 +313,17 @@ class AsyncWebServerRequest {
|
|||
// user:realm:md5(user:realm:pass) for digest
|
||||
bool authenticate(const char *hash) const;
|
||||
bool authenticate(const char *username, const char *credentials, const char *realm = NULL, bool isHash = false) const;
|
||||
void requestAuthentication(const char* realm = nullptr, bool isDigest = true) { requestAuthentication(isDigest ? AsyncAuthType::AUTH_DIGEST : AsyncAuthType::AUTH_BASIC, realm); }
|
||||
void requestAuthentication(const char *realm = nullptr, bool isDigest = true) {
|
||||
requestAuthentication(isDigest ? AsyncAuthType::AUTH_DIGEST : AsyncAuthType::AUTH_BASIC, realm);
|
||||
}
|
||||
void requestAuthentication(AsyncAuthType method, const char *realm = nullptr, const char *_authFailMsg = nullptr);
|
||||
|
||||
void setHandler(AsyncWebHandler* handler) { _handler = handler; }
|
||||
// IMPORTANT: this method is for internal use ONLY
|
||||
// Please do not use it!
|
||||
// It can be removed or modified at any time without notice
|
||||
void setHandler(AsyncWebHandler *handler) {
|
||||
_handler = handler;
|
||||
}
|
||||
|
||||
#ifndef ESP8266
|
||||
[[deprecated("All headers are now collected. Use removeHeader(name) or AsyncHeaderFreeMiddleware if you really need to free some headers.")]]
|
||||
|
@ -296,48 +337,80 @@ class AsyncWebServerRequest {
|
|||
}
|
||||
|
||||
/**
|
||||
* @brief issue HTTP redirect responce with Location header
|
||||
* @brief issue HTTP redirect response with Location header
|
||||
*
|
||||
* @param url - url to redirect to
|
||||
* @param code - responce code, default is 302 : temporary redirect
|
||||
* @param code - response code, default is 302 : temporary redirect
|
||||
*/
|
||||
void redirect(const char *url, int code = 302);
|
||||
void redirect(const String& url, int code = 302) { return redirect(url.c_str(), code); };
|
||||
void redirect(const String &url, int code = 302) {
|
||||
return redirect(url.c_str(), code);
|
||||
};
|
||||
|
||||
void send(AsyncWebServerResponse *response);
|
||||
AsyncWebServerResponse* getResponse() const { return _response; }
|
||||
AsyncWebServerResponse *getResponse() const {
|
||||
return _response;
|
||||
}
|
||||
|
||||
void send(int code, const char* contentType = asyncsrv::empty, const char* content = asyncsrv::empty, AwsTemplateProcessor callback = nullptr) { send(beginResponse(code, contentType, content, callback)); }
|
||||
void send(int code, const String& contentType, const char* content = asyncsrv::empty, AwsTemplateProcessor callback = nullptr) { send(beginResponse(code, contentType.c_str(), content, callback)); }
|
||||
void send(int code, const String& contentType, const String& content, AwsTemplateProcessor callback = nullptr) { send(beginResponse(code, contentType.c_str(), content.c_str(), callback)); }
|
||||
void send(int code, const char *contentType = asyncsrv::empty, const char *content = asyncsrv::empty, AwsTemplateProcessor callback = nullptr) {
|
||||
send(beginResponse(code, contentType, content, callback));
|
||||
}
|
||||
void send(int code, const String &contentType, const char *content = asyncsrv::empty, AwsTemplateProcessor callback = nullptr) {
|
||||
send(beginResponse(code, contentType.c_str(), content, callback));
|
||||
}
|
||||
void send(int code, const String &contentType, const String &content, AwsTemplateProcessor callback = nullptr) {
|
||||
send(beginResponse(code, contentType.c_str(), content.c_str(), callback));
|
||||
}
|
||||
|
||||
void send(int code, const char* contentType, const uint8_t* content, size_t len, AwsTemplateProcessor callback = nullptr) { send(beginResponse(code, contentType, content, len, callback)); }
|
||||
void send(int code, const String& contentType, const uint8_t* content, size_t len, AwsTemplateProcessor callback = nullptr) { send(beginResponse(code, contentType, content, len, callback)); }
|
||||
void send(int code, const char *contentType, const uint8_t *content, size_t len, AwsTemplateProcessor callback = nullptr) {
|
||||
send(beginResponse(code, contentType, content, len, callback));
|
||||
}
|
||||
void send(int code, const String &contentType, const uint8_t *content, size_t len, AwsTemplateProcessor callback = nullptr) {
|
||||
send(beginResponse(code, contentType, content, len, callback));
|
||||
}
|
||||
|
||||
void send(FS &fs, const String &path, const char *contentType = asyncsrv::empty, bool download = false, AwsTemplateProcessor callback = nullptr) {
|
||||
if (fs.exists(path) || (!download && fs.exists(path + asyncsrv::T__gz))) {
|
||||
send(beginResponse(fs, path, contentType, download, callback));
|
||||
} else
|
||||
} 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(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) {
|
||||
if (content) {
|
||||
send(beginResponse(content, path, contentType, download, callback));
|
||||
} else
|
||||
} 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(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(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 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)); }
|
||||
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)")]]
|
||||
|
@ -356,30 +429,51 @@ class AsyncWebServerRequest {
|
|||
}
|
||||
#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 = 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(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(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(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(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 *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); }
|
||||
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)")]]
|
||||
|
@ -388,10 +482,37 @@ class AsyncWebServerRequest {
|
|||
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)")]]
|
||||
[[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 Request Continuation: this function pauses the current request and returns a weak pointer (AsyncWebServerRequestPtr is a std::weak_ptr) to the request in order to reuse it later on.
|
||||
* The middelware chain will continue to be processed until the end, but no response will be sent.
|
||||
* To resume operations (send the request), the request must be retrieved from the weak pointer and a send() function must be called.
|
||||
* AsyncWebServerRequestPtr is the only object allowed to exist the scope of the request handler.
|
||||
* @warning This function should be called from within the context of a request (in a handler or middleware for example).
|
||||
* @warning While the request is paused, if the client aborts the request, the latter will be disconnected and deleted.
|
||||
* So it is the responsibility of the user to check the validity of the request pointer (AsyncWebServerRequestPtr) before using it by calling lock() and/or expired().
|
||||
*/
|
||||
AsyncWebServerRequestPtr pause();
|
||||
|
||||
bool isPaused() const {
|
||||
return _paused;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Aborts the request and close the client (RST).
|
||||
* Mark the request as sent.
|
||||
* If it was paused, it will be unpaused and it won't be possible to resume it.
|
||||
*/
|
||||
void abort();
|
||||
|
||||
bool isSent() const {
|
||||
return _sent;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the Request parameter by name
|
||||
*
|
||||
|
@ -402,7 +523,9 @@ class AsyncWebServerRequest {
|
|||
*/
|
||||
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); };
|
||||
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
|
||||
|
@ -415,19 +538,25 @@ class AsyncWebServerRequest {
|
|||
*/
|
||||
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
|
||||
|
||||
// get request argument value by name
|
||||
const String &arg(const char *name) const;
|
||||
// get request argument value by name
|
||||
const String& arg(const String& name) const { return arg(name.c_str()); };
|
||||
const String &arg(const String &name) const {
|
||||
return arg(name.c_str());
|
||||
};
|
||||
#ifdef ESP8266
|
||||
const String &arg(const __FlashStringHelper *data) const; // get request argument value by F(name)
|
||||
#endif
|
||||
const String &arg(size_t i) const; // get request argument value by number
|
||||
const String &argName(size_t i) const; // get request argument name by number
|
||||
bool hasArg(const char *name) const; // check if argument exists
|
||||
bool hasArg(const String& name) const { return hasArg(name.c_str()); };
|
||||
bool hasArg(const String &name) const {
|
||||
return hasArg(name.c_str());
|
||||
};
|
||||
#ifdef ESP8266
|
||||
bool hasArg(const __FlashStringHelper *data) const; // check if F(argument) exists
|
||||
#endif
|
||||
|
@ -436,7 +565,9 @@ class AsyncWebServerRequest {
|
|||
|
||||
// get request header value by name
|
||||
const String &header(const char *name) const;
|
||||
const String& header(const String& name) const { return header(name.c_str()); };
|
||||
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)
|
||||
|
@ -449,20 +580,26 @@ class AsyncWebServerRequest {
|
|||
|
||||
// check if header exists
|
||||
bool hasHeader(const char *name) const;
|
||||
bool hasHeader(const String& name) const { return hasHeader(name.c_str()); };
|
||||
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()); };
|
||||
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; }
|
||||
const std::list<AsyncWebHeader> &getHeaders() const {
|
||||
return _headers;
|
||||
}
|
||||
|
||||
size_t getHeaderNames(std::vector<const char *> &names) const;
|
||||
|
||||
|
@ -470,24 +607,42 @@ class AsyncWebServerRequest {
|
|||
// 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(); }
|
||||
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); };
|
||||
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); };
|
||||
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); }
|
||||
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(); }
|
||||
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;
|
||||
|
@ -521,7 +676,9 @@ using ArMiddlewareCallback = std::function<void(AsyncWebServerRequest* request,
|
|||
class AsyncMiddleware {
|
||||
public:
|
||||
virtual ~AsyncMiddleware() {}
|
||||
virtual void run(__unused AsyncWebServerRequest* request, __unused ArMiddlewareNext next) { return next(); };
|
||||
virtual void run(__unused AsyncWebServerRequest *request, __unused ArMiddlewareNext next) {
|
||||
return next();
|
||||
};
|
||||
|
||||
private:
|
||||
friend class AsyncWebHandler;
|
||||
|
@ -534,7 +691,9 @@ class AsyncMiddleware {
|
|||
class AsyncMiddlewareFunction : public AsyncMiddleware {
|
||||
public:
|
||||
AsyncMiddlewareFunction(ArMiddlewareCallback fn) : _fn(fn) {}
|
||||
void run(AsyncWebServerRequest* request, ArMiddlewareNext next) override { return _fn(request, next); };
|
||||
void run(AsyncWebServerRequest *request, ArMiddlewareNext next) override {
|
||||
return _fn(request, next);
|
||||
};
|
||||
|
||||
private:
|
||||
ArMiddlewareCallback _fn;
|
||||
|
@ -564,8 +723,12 @@ class AsyncAuthenticationMiddleware : public AsyncMiddleware {
|
|||
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 setRealm(const char *realm) {
|
||||
_realm = realm;
|
||||
}
|
||||
void setAuthFailureMessage(const char *message) {
|
||||
_authFailMsg = message;
|
||||
}
|
||||
|
||||
// set the authentication method to use
|
||||
// default is AUTH_NONE: no authentication required
|
||||
|
@ -575,7 +738,9 @@ class AsyncAuthenticationMiddleware : public AsyncMiddleware {
|
|||
// AUTH_OTHER: other authentication method
|
||||
// AUTH_DENIED: always return 401 Unauthorized
|
||||
// if a method is set but no username or password is set, authentication will be ignored
|
||||
void setAuthType(AsyncAuthType authMethod) { _authMethod = authMethod; }
|
||||
void setAuthType(AsyncAuthType authMethod) {
|
||||
_authMethod = authMethod;
|
||||
}
|
||||
|
||||
// precompute and store the hash value based on the username, password, realm.
|
||||
// can be used for DIGEST and BASIC to avoid recomputing the hash for each request.
|
||||
|
@ -583,7 +748,9 @@ class AsyncAuthenticationMiddleware : public AsyncMiddleware {
|
|||
bool generateHash();
|
||||
|
||||
// returns true if the username and password (or hash) are set
|
||||
bool hasCredentials() const { return _hasCreds; }
|
||||
bool hasCredentials() const {
|
||||
return _hasCreds;
|
||||
}
|
||||
|
||||
bool allowed(AsyncWebServerRequest *request) const;
|
||||
|
||||
|
@ -607,7 +774,9 @@ class AsyncAuthorizationMiddleware : public AsyncMiddleware {
|
|||
AsyncAuthorizationMiddleware(ArAuthorizeFunction authorizeConnectHandler) : _code(403), _authz(authorizeConnectHandler) {}
|
||||
AsyncAuthorizationMiddleware(int code, ArAuthorizeFunction authorizeConnectHandler) : _code(code), _authz(authorizeConnectHandler) {}
|
||||
|
||||
void run(AsyncWebServerRequest* request, ArMiddlewareNext next) { return _authz && !_authz(request) ? request->send(_code) : next(); }
|
||||
void run(AsyncWebServerRequest *request, ArMiddlewareNext next) {
|
||||
return _authz && !_authz(request) ? request->send(_code) : next();
|
||||
}
|
||||
|
||||
private:
|
||||
int _code;
|
||||
|
@ -617,33 +786,47 @@ class AsyncAuthorizationMiddleware : public AsyncMiddleware {
|
|||
// remove all headers from the incoming request except the ones provided in the constructor
|
||||
class AsyncHeaderFreeMiddleware : 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 keep(const char *name) {
|
||||
_toKeep.push_back(name);
|
||||
}
|
||||
void unKeep(const char *name) {
|
||||
_toKeep.remove(name);
|
||||
}
|
||||
|
||||
void run(AsyncWebServerRequest *request, ArMiddlewareNext next);
|
||||
|
||||
private:
|
||||
std::vector<const char*> _toKeep;
|
||||
std::list<const char *> _toKeep;
|
||||
};
|
||||
|
||||
// filter out specific headers from the incoming request
|
||||
class AsyncHeaderFilterMiddleware : 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 filter(const char *name) {
|
||||
_toRemove.push_back(name);
|
||||
}
|
||||
void unFilter(const char *name) {
|
||||
_toRemove.remove(name);
|
||||
}
|
||||
|
||||
void run(AsyncWebServerRequest *request, ArMiddlewareNext next);
|
||||
|
||||
private:
|
||||
std::vector<const char*> _toRemove;
|
||||
std::list<const char *> _toRemove;
|
||||
};
|
||||
|
||||
// curl-like logging of incoming requests
|
||||
class AsyncLoggingMiddleware : public AsyncMiddleware {
|
||||
public:
|
||||
void setOutput(Print& output) { _out = &output; }
|
||||
void setEnabled(bool enabled) { _enabled = enabled; }
|
||||
bool isEnabled() const { return _enabled && _out; }
|
||||
void setOutput(Print &output) {
|
||||
_out = &output;
|
||||
}
|
||||
void setEnabled(bool enabled) {
|
||||
_enabled = enabled;
|
||||
}
|
||||
bool isEnabled() const {
|
||||
return _enabled && _out;
|
||||
}
|
||||
|
||||
void run(AsyncWebServerRequest *request, ArMiddlewareNext next);
|
||||
|
||||
|
@ -655,11 +838,21 @@ class AsyncLoggingMiddleware : public AsyncMiddleware {
|
|||
// CORS Middleware
|
||||
class AsyncCorsMiddleware : 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 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);
|
||||
|
||||
|
@ -676,8 +869,12 @@ class AsyncCorsMiddleware : public AsyncMiddleware {
|
|||
// Rate limit Middleware
|
||||
class AsyncRateLimitMiddleware : public AsyncMiddleware {
|
||||
public:
|
||||
void setMaxRequests(size_t maxRequests) { _maxRequests = maxRequests; }
|
||||
void setWindowSize(uint32_t seconds) { _windowSizeMillis = seconds * 1000; }
|
||||
void setMaxRequests(size_t maxRequests) {
|
||||
_maxRequests = maxRequests;
|
||||
}
|
||||
void setWindowSize(uint32_t seconds) {
|
||||
_windowSizeMillis = seconds * 1000;
|
||||
}
|
||||
|
||||
bool isRequestAllowed(uint32_t &retryAfterSeconds);
|
||||
|
||||
|
@ -713,11 +910,21 @@ class AsyncWebRewrite {
|
|||
_filter = fn;
|
||||
return *this;
|
||||
}
|
||||
bool filter(AsyncWebServerRequest* request) const { return _filter == NULL || _filter(request); }
|
||||
const String& from(void) const { return _from; }
|
||||
const String& toUrl(void) const { return _toUrl; }
|
||||
const String& params(void) const { return _params; }
|
||||
virtual bool match(AsyncWebServerRequest* request) { return from() == request->url() && filter(request); }
|
||||
bool filter(AsyncWebServerRequest *request) const {
|
||||
return _filter == NULL || _filter(request);
|
||||
}
|
||||
const String &from(void) const {
|
||||
return _from;
|
||||
}
|
||||
const String &toUrl(void) const {
|
||||
return _toUrl;
|
||||
}
|
||||
const String ¶ms(void) const {
|
||||
return _params;
|
||||
}
|
||||
virtual bool match(AsyncWebServerRequest *request) {
|
||||
return from() == request->url() && filter(request);
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -728,19 +935,42 @@ class AsyncWebHandler : public AsyncMiddlewareChain {
|
|||
protected:
|
||||
ArRequestFilterFunction _filter = nullptr;
|
||||
AsyncAuthenticationMiddleware *_authMiddleware = nullptr;
|
||||
bool _skipServerMiddlewares = false;
|
||||
|
||||
public:
|
||||
AsyncWebHandler() {}
|
||||
virtual ~AsyncWebHandler() {}
|
||||
AsyncWebHandler &setFilter(ArRequestFilterFunction fn);
|
||||
AsyncWebHandler &setAuthentication(const char *username, const char *password, AsyncAuthType authMethod = AsyncAuthType::AUTH_DIGEST);
|
||||
AsyncWebHandler& setAuthentication(const String& username, const String& password, AsyncAuthType authMethod = AsyncAuthType::AUTH_DIGEST) { return setAuthentication(username.c_str(), password.c_str(), authMethod); };
|
||||
bool filter(AsyncWebServerRequest* request) { return _filter == NULL || _filter(request); }
|
||||
virtual bool canHandle(AsyncWebServerRequest* request __attribute__((unused))) const { return false; }
|
||||
AsyncWebHandler &setAuthentication(const String &username, const String &password, AsyncAuthType authMethod = AsyncAuthType::AUTH_DIGEST) {
|
||||
return setAuthentication(username.c_str(), password.c_str(), authMethod);
|
||||
};
|
||||
AsyncWebHandler &setSkipServerMiddlewares(bool state) {
|
||||
_skipServerMiddlewares = state;
|
||||
return *this;
|
||||
}
|
||||
// skip all globally defined server middlewares for this handler and only execute those defined for this handler specifically
|
||||
AsyncWebHandler &skipServerMiddlewares() {
|
||||
return setSkipServerMiddlewares(true);
|
||||
}
|
||||
bool mustSkipServerMiddlewares() const {
|
||||
return _skipServerMiddlewares;
|
||||
}
|
||||
bool filter(AsyncWebServerRequest *request) {
|
||||
return _filter == NULL || _filter(request);
|
||||
}
|
||||
virtual bool canHandle(AsyncWebServerRequest *request __attribute__((unused))) const {
|
||||
return false;
|
||||
}
|
||||
virtual void handleRequest(__unused AsyncWebServerRequest *request) {}
|
||||
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(
|
||||
__unused AsyncWebServerRequest *request, __unused const String &filename, __unused size_t index, __unused uint8_t *data, __unused size_t len,
|
||||
__unused bool final
|
||||
) {}
|
||||
virtual void handleBody(__unused AsyncWebServerRequest *request, __unused uint8_t *data, __unused size_t len, __unused size_t index, __unused size_t total) {}
|
||||
virtual bool isRequestHandlerTrivial() const { return true; }
|
||||
virtual bool isRequestHandlerTrivial() const {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -770,6 +1000,8 @@ class AsyncWebServerResponse {
|
|||
size_t _writtenLength;
|
||||
WebResponseState _state;
|
||||
|
||||
static bool headerMustBePresentOnce(const String &name);
|
||||
|
||||
public:
|
||||
static const char *responseCodeToString(int code);
|
||||
|
||||
|
@ -777,17 +1009,30 @@ class AsyncWebServerResponse {
|
|||
AsyncWebServerResponse();
|
||||
virtual ~AsyncWebServerResponse() {}
|
||||
void setCode(int code);
|
||||
int code() const { return _code; }
|
||||
int code() const {
|
||||
return _code;
|
||||
}
|
||||
void setContentLength(size_t len);
|
||||
void setContentType(const String& type) { setContentType(type.c_str()); }
|
||||
void setContentType(const String &type) {
|
||||
setContentType(type.c_str());
|
||||
}
|
||||
void setContentType(const char *type);
|
||||
bool addHeader(const char *name, const char *value, bool replaceExisting = true);
|
||||
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); }
|
||||
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);
|
||||
}
|
||||
bool removeHeader(const char *name);
|
||||
bool removeHeader(const char *name, const char *value);
|
||||
const AsyncWebHeader *getHeader(const char *name) const;
|
||||
const std::list<AsyncWebHeader>& getHeaders() const { return _headers; }
|
||||
const std::list<AsyncWebHeader> &getHeaders() const {
|
||||
return _headers;
|
||||
}
|
||||
|
||||
#ifndef ESP8266
|
||||
[[deprecated("Use instead: _assembleHead(String& buffer, uint8_t version)")]]
|
||||
|
@ -812,7 +1057,8 @@ class AsyncWebServerResponse {
|
|||
* */
|
||||
|
||||
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;
|
||||
|
||||
class AsyncWebServer : public AsyncMiddlewareChain {
|
||||
|
@ -878,14 +1124,21 @@ class AsyncWebServer : public AsyncMiddlewareChain {
|
|||
AsyncWebHandler &addHandler(AsyncWebHandler *handler);
|
||||
bool removeHandler(AsyncWebHandler *handler);
|
||||
|
||||
AsyncCallbackWebHandler& on(const char* uri, ArRequestHandlerFunction onRequest) { return on(uri, HTTP_ANY, onRequest); }
|
||||
AsyncCallbackWebHandler& on(const char* uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest, ArUploadHandlerFunction onUpload = nullptr, ArBodyHandlerFunction onBody = nullptr);
|
||||
AsyncCallbackWebHandler &on(const char *uri, ArRequestHandlerFunction onRequest) {
|
||||
return on(uri, HTTP_ANY, onRequest);
|
||||
}
|
||||
AsyncCallbackWebHandler &on(
|
||||
const char *uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest, ArUploadHandlerFunction onUpload = nullptr,
|
||||
ArBodyHandlerFunction onBody = nullptr
|
||||
);
|
||||
|
||||
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 onFileUpload(ArUploadHandlerFunction fn); // handle file uploads
|
||||
void onRequestBody(ArBodyHandlerFunction fn); // handle posts with plain body content (JSON often transmitted this way as a request)
|
||||
// give access to the handler used to catch all requests, so that middleware can be added to it
|
||||
AsyncWebHandler &catchAllHandler() const;
|
||||
|
||||
void reset(); // remove all writers and handlers, with onNotFound/onFileUpload/onRequestBody
|
||||
|
||||
|
@ -907,8 +1160,12 @@ class DefaultHeaders {
|
|||
_headers.emplace_back(name, value);
|
||||
}
|
||||
|
||||
ConstIterator begin() const { return _headers.begin(); }
|
||||
ConstIterator end() const { return _headers.end(); }
|
||||
ConstIterator begin() const {
|
||||
return _headers.begin();
|
||||
}
|
||||
ConstIterator end() const {
|
||||
return _headers.end();
|
||||
}
|
||||
|
||||
DefaultHeaders(DefaultHeaders const &) = delete;
|
||||
DefaultHeaders &operator=(DefaultHeaders const &) = delete;
|
||||
|
|
|
@ -1,11 +1,16 @@
|
|||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// Copyright 2016-2025 Hristo Gochkov, Mathieu Carbou, Emil Muratov
|
||||
|
||||
#include "WebAuthentication.h"
|
||||
#include "ESPAsyncWebServer.h"
|
||||
#include <ESPAsyncWebServer.h>
|
||||
|
||||
AsyncMiddlewareChain::~AsyncMiddlewareChain() {
|
||||
for (AsyncMiddleware* m : _middlewares)
|
||||
if (m->_freeOnRemoval)
|
||||
for (AsyncMiddleware *m : _middlewares) {
|
||||
if (m->_freeOnRemoval) {
|
||||
delete m;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AsyncMiddlewareChain::addMiddleware(ArMiddlewareCallback fn) {
|
||||
AsyncMiddlewareFunction *m = new AsyncMiddlewareFunction(fn);
|
||||
|
@ -14,38 +19,48 @@ void AsyncMiddlewareChain::addMiddleware(ArMiddlewareCallback fn) {
|
|||
}
|
||||
|
||||
void AsyncMiddlewareChain::addMiddleware(AsyncMiddleware *middleware) {
|
||||
if (middleware)
|
||||
if (middleware) {
|
||||
_middlewares.emplace_back(middleware);
|
||||
}
|
||||
}
|
||||
|
||||
void AsyncMiddlewareChain::addMiddlewares(std::vector<AsyncMiddleware *> middlewares) {
|
||||
for (AsyncMiddleware* m : 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) {
|
||||
_middlewares.erase(
|
||||
std::remove_if(
|
||||
_middlewares.begin(), _middlewares.end(),
|
||||
[middleware](AsyncMiddleware *m) {
|
||||
if (m == middleware) {
|
||||
if (m->_freeOnRemoval)
|
||||
if (m->_freeOnRemoval) {
|
||||
delete m;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}),
|
||||
_middlewares.end());
|
||||
}
|
||||
),
|
||||
_middlewares.end()
|
||||
);
|
||||
return size != _middlewares.size();
|
||||
}
|
||||
|
||||
void AsyncMiddlewareChain::_runChain(AsyncWebServerRequest *request, ArMiddlewareNext finalizer) {
|
||||
if (!_middlewares.size())
|
||||
if (!_middlewares.size()) {
|
||||
return finalizer();
|
||||
}
|
||||
ArMiddlewareNext next;
|
||||
std::list<AsyncMiddleware *>::iterator it = _middlewares.begin();
|
||||
next = [this, &next, &it, request, finalizer]() {
|
||||
if (it == _middlewares.end())
|
||||
if (it == _middlewares.end()) {
|
||||
return finalizer();
|
||||
}
|
||||
AsyncMiddleware *m = *it;
|
||||
it++;
|
||||
return m->run(request, next);
|
||||
|
@ -72,38 +87,50 @@ void AsyncAuthenticationMiddleware::setPasswordHash(const char* hash) {
|
|||
|
||||
bool AsyncAuthenticationMiddleware::generateHash() {
|
||||
// ensure we have all the necessary data
|
||||
if (!_hasCreds)
|
||||
if (!_hasCreds) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// if we already have a hash, do nothing
|
||||
if (_hash)
|
||||
if (_hash) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (_authMethod) {
|
||||
case AsyncAuthType::AUTH_DIGEST:
|
||||
_credentials = generateDigestHash(_username.c_str(), _credentials.c_str(), _realm.c_str());
|
||||
if (_credentials.length()) {
|
||||
_hash = true;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
case AsyncAuthType::AUTH_BASIC:
|
||||
_credentials = generateBasicHash(_username.c_str(), _credentials.c_str());
|
||||
if (_credentials.length()) {
|
||||
_hash = true;
|
||||
return true;
|
||||
|
||||
default:
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool AsyncAuthenticationMiddleware::allowed(AsyncWebServerRequest *request) const {
|
||||
if (_authMethod == AsyncAuthType::AUTH_NONE)
|
||||
if (_authMethod == AsyncAuthType::AUTH_NONE) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_authMethod == AsyncAuthType::AUTH_DENIED)
|
||||
if (_authMethod == AsyncAuthType::AUTH_DENIED) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!_hasCreds)
|
||||
if (!_hasCreds) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return request->authenticate(_username.c_str(), _credentials.c_str(), _realm.c_str(), _hash);
|
||||
}
|
||||
|
@ -113,26 +140,29 @@ void AsyncAuthenticationMiddleware::run(AsyncWebServerRequest* request, ArMiddle
|
|||
}
|
||||
|
||||
void AsyncHeaderFreeMiddleware::run(AsyncWebServerRequest *request, ArMiddlewareNext next) {
|
||||
std::vector<const char*> reqHeaders;
|
||||
request->getHeaderNames(reqHeaders);
|
||||
for (const char* h : reqHeaders) {
|
||||
std::list<const char *> toRemove;
|
||||
for (auto &h : request->getHeaders()) {
|
||||
bool keep = false;
|
||||
for (const char *k : _toKeep) {
|
||||
if (strcasecmp(h, k) == 0) {
|
||||
if (strcasecmp(h.name().c_str(), k) == 0) {
|
||||
keep = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!keep) {
|
||||
request->removeHeader(h);
|
||||
toRemove.push_back(h.name().c_str());
|
||||
}
|
||||
}
|
||||
for (const char *h : toRemove) {
|
||||
request->removeHeader(h);
|
||||
}
|
||||
next();
|
||||
}
|
||||
|
||||
void AsyncHeaderFilterMiddleware::run(AsyncWebServerRequest *request, ArMiddlewareNext next) {
|
||||
for (auto it = _toRemove.begin(); it != _toRemove.end(); ++it)
|
||||
for (auto it = _toRemove.begin(); it != _toRemove.end(); ++it) {
|
||||
request->removeHeader(*it);
|
||||
}
|
||||
next();
|
||||
}
|
||||
|
||||
|
@ -229,8 +259,9 @@ void AsyncCorsMiddleware::run(AsyncWebServerRequest* request, ArMiddlewareNext n
|
|||
bool AsyncRateLimitMiddleware::isRequestAllowed(uint32_t &retryAfterSeconds) {
|
||||
uint32_t now = millis();
|
||||
|
||||
while (!_requestTimes.empty() && _requestTimes.front() <= now - _windowSizeMillis)
|
||||
while (!_requestTimes.empty() && _requestTimes.front() <= now - _windowSizeMillis) {
|
||||
_requestTimes.pop_front();
|
||||
}
|
||||
|
||||
_requestTimes.push_back(now);
|
||||
|
||||
|
|
|
@ -1,26 +1,9 @@
|
|||
/*
|
||||
Asynchronous WebServer library for Espressif MCUs
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// Copyright 2016-2025 Hristo Gochkov, Mathieu Carbou, Emil Muratov
|
||||
|
||||
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
|
||||
*/
|
||||
#include "WebAuthentication.h"
|
||||
#include <libb64/cencode.h>
|
||||
#if defined(ESP32) || defined(TARGET_RP2040)
|
||||
#if defined(ESP32) || defined(TARGET_RP2040) || defined(TARGET_RP2350) || defined(PICO_RP2040) || defined(PICO_RP2350)
|
||||
#include <MD5Builder.h>
|
||||
#else
|
||||
#include "md5.h"
|
||||
|
@ -32,14 +15,16 @@ using namespace asyncsrv;
|
|||
// Basic Auth hash = base64("username: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 generateBasicHash(username, password).equalsIgnoreCase(hash);
|
||||
}
|
||||
|
||||
String generateBasicHash(const char *username, const char *password) {
|
||||
if (username == NULL || password == NULL)
|
||||
if (username == NULL || password == NULL) {
|
||||
return emptyString;
|
||||
}
|
||||
|
||||
size_t toencodeLen = strlen(username) + strlen(password) + 1;
|
||||
|
||||
|
@ -65,7 +50,7 @@ String generateBasicHash(const char* username, const char* password) {
|
|||
}
|
||||
|
||||
static bool getMD5(uint8_t *data, uint16_t len, char *output) { // 33 bytes or more
|
||||
#if defined(ESP32) || defined(TARGET_RP2040)
|
||||
#if defined(ESP32) || defined(TARGET_RP2040) || defined(TARGET_RP2350) || defined(PICO_RP2040) || defined(PICO_RP2350)
|
||||
MD5Builder md5;
|
||||
md5.begin();
|
||||
md5.add(data, len);
|
||||
|
@ -75,8 +60,9 @@ static bool getMD5(uint8_t* data, uint16_t len, char* output) { // 33 bytes or m
|
|||
md5_context_t _ctx;
|
||||
|
||||
uint8_t *_buf = (uint8_t *)malloc(16);
|
||||
if (_buf == NULL)
|
||||
if (_buf == NULL) {
|
||||
return false;
|
||||
}
|
||||
memset(_buf, 0x00, 16);
|
||||
|
||||
MD5Init(&_ctx);
|
||||
|
@ -99,8 +85,12 @@ String genRandomMD5() {
|
|||
uint32_t r = rand();
|
||||
#endif
|
||||
char *out = (char *)malloc(33);
|
||||
if (out == NULL || !getMD5((uint8_t*)(&r), 4, out))
|
||||
if (out == NULL || !getMD5((uint8_t *)(&r), 4, out)) {
|
||||
#ifdef ESP32
|
||||
log_e("Failed to allocate");
|
||||
#endif
|
||||
return emptyString;
|
||||
}
|
||||
String res = String(out);
|
||||
free(out);
|
||||
return res;
|
||||
|
@ -108,8 +98,12 @@ String genRandomMD5() {
|
|||
|
||||
static String stringMD5(const String &in) {
|
||||
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)) {
|
||||
#ifdef ESP32
|
||||
log_e("Failed to allocate");
|
||||
#endif
|
||||
return emptyString;
|
||||
}
|
||||
String res = String(out);
|
||||
free(out);
|
||||
return res;
|
||||
|
@ -120,27 +114,47 @@ String generateDigestHash(const char* username, const char* password, const char
|
|||
return emptyString;
|
||||
}
|
||||
char *out = (char *)malloc(33);
|
||||
if (out == NULL) {
|
||||
#ifdef ESP32
|
||||
log_e("Failed to allocate");
|
||||
#endif
|
||||
return emptyString;
|
||||
}
|
||||
|
||||
String in;
|
||||
in.reserve(strlen(username) + strlen(realm) + strlen(password) + 2);
|
||||
if (!in.reserve(strlen(username) + strlen(realm) + strlen(password) + 2)) {
|
||||
#ifdef ESP32
|
||||
log_e("Failed to allocate");
|
||||
#endif
|
||||
free(out);
|
||||
return emptyString;
|
||||
}
|
||||
|
||||
in.concat(username);
|
||||
in.concat(':');
|
||||
in.concat(realm);
|
||||
in.concat(':');
|
||||
in.concat(password);
|
||||
|
||||
if (out == NULL || !getMD5((uint8_t*)(in.c_str()), in.length(), out))
|
||||
if (!getMD5((uint8_t *)(in.c_str()), in.length(), out)) {
|
||||
#ifdef ESP32
|
||||
log_e("Failed to allocate");
|
||||
#endif
|
||||
free(out);
|
||||
return emptyString;
|
||||
}
|
||||
|
||||
in = String(out);
|
||||
free(out);
|
||||
return in;
|
||||
}
|
||||
|
||||
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
|
||||
) {
|
||||
if (username == NULL || password == NULL || header == NULL || method == NULL) {
|
||||
// os_printf("AUTH FAIL: missing requred fields\n");
|
||||
// os_printf("AUTH FAIL: missing required fields\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,23 +1,5 @@
|
|||
/*
|
||||
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
|
||||
*/
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// Copyright 2016-2025 Hristo Gochkov, Mathieu Carbou, Emil Muratov
|
||||
|
||||
#ifndef WEB_AUTHENTICATION_H_
|
||||
#define WEB_AUTHENTICATION_H_
|
||||
|
@ -26,7 +8,10 @@
|
|||
|
||||
bool checkBasicAuthentication(const char *header, const char *username, const char *password);
|
||||
|
||||
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);
|
||||
|
|
|
@ -1,23 +1,6 @@
|
|||
/*
|
||||
Asynchronous WebServer library for Espressif MCUs
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// Copyright 2016-2025 Hristo Gochkov, Mathieu Carbou, Emil Muratov
|
||||
|
||||
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 ASYNCWEBSERVERHANDLERIMPL_H_
|
||||
#define ASYNCWEBSERVERHANDLERIMPL_H_
|
||||
|
||||
|
@ -67,7 +50,7 @@ class AsyncStaticWebHandler : public AsyncWebHandler {
|
|||
AsyncStaticWebHandler &setLastModified(const char *last_modified);
|
||||
AsyncStaticWebHandler &setLastModified(struct tm *last_modified);
|
||||
AsyncStaticWebHandler &setLastModified(time_t last_modified);
|
||||
// sets to current time. Make sure sntp is runing and time is updated
|
||||
// sets to current time. Make sure sntp is running and time is updated
|
||||
AsyncStaticWebHandler &setLastModified();
|
||||
|
||||
AsyncStaticWebHandler &setTemplateProcessor(AwsTemplateProcessor newCallback);
|
||||
|
@ -86,16 +69,26 @@ class AsyncCallbackWebHandler : public AsyncWebHandler {
|
|||
public:
|
||||
AsyncCallbackWebHandler() : _uri(), _method(HTTP_ANY), _onRequest(NULL), _onUpload(NULL), _onBody(NULL), _isRegex(false) {}
|
||||
void setUri(const String &uri);
|
||||
void setMethod(WebRequestMethodComposite method) { _method = method; }
|
||||
void onRequest(ArRequestHandlerFunction fn) { _onRequest = fn; }
|
||||
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;
|
||||
}
|
||||
|
||||
bool canHandle(AsyncWebServerRequest *request) const override final;
|
||||
void handleRequest(AsyncWebServerRequest *request) override final;
|
||||
void handleUpload(AsyncWebServerRequest *request, const String &filename, size_t index, uint8_t *data, size_t len, bool final) override final;
|
||||
void handleBody(AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total) override final;
|
||||
bool isRequestHandlerTrivial() const override final { return !_onRequest; }
|
||||
bool isRequestHandlerTrivial() const override final {
|
||||
return !_onRequest;
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* ASYNCWEBSERVERHANDLERIMPL_H_ */
|
||||
|
|
|
@ -1,23 +1,6 @@
|
|||
/*
|
||||
Asynchronous WebServer library for Espressif MCUs
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// Copyright 2016-2025 Hristo Gochkov, Mathieu Carbou, Emil Muratov
|
||||
|
||||
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
|
||||
*/
|
||||
#include "ESPAsyncWebServer.h"
|
||||
#include "WebHandlerImpl.h"
|
||||
|
||||
|
@ -42,10 +25,12 @@ AsyncWebHandler& AsyncWebHandler::setAuthentication(const char* username, const
|
|||
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) {
|
||||
// Ensure leading '/'
|
||||
if (_uri.length() == 0 || _uri[0] != '/')
|
||||
if (_uri.length() == 0 || _uri[0] != '/') {
|
||||
_uri = String('/') + _uri;
|
||||
if (_path.length() == 0 || _path[0] != '/')
|
||||
}
|
||||
if (_path.length() == 0 || _path[0] != '/') {
|
||||
_path = String('/') + _path;
|
||||
}
|
||||
|
||||
// If path ends with '/' we assume a hint that this is a directory to improve performance.
|
||||
// However - if it does not end with '/' we, can't assume a file, path can still be a directory.
|
||||
|
@ -53,11 +38,13 @@ AsyncStaticWebHandler::AsyncStaticWebHandler(const char* uri, FS& fs, const char
|
|||
|
||||
// Remove the trailing '/' so we can handle default file
|
||||
// Notice that root will be "" not "/"
|
||||
if (_uri[_uri.length() - 1] == '/')
|
||||
if (_uri[_uri.length() - 1] == '/') {
|
||||
_uri = _uri.substring(0, _uri.length() - 1);
|
||||
if (_path[_path.length() - 1] == '/')
|
||||
}
|
||||
if (_path[_path.length() - 1] == '/') {
|
||||
_path = _path.substring(0, _path.length() - 1);
|
||||
}
|
||||
}
|
||||
|
||||
AsyncStaticWebHandler &AsyncStaticWebHandler::setTryGzipFirst(bool value) {
|
||||
_tryGzipFirst = value;
|
||||
|
@ -105,8 +92,9 @@ AsyncStaticWebHandler& AsyncStaticWebHandler::setLastModified(time_t last_modifi
|
|||
|
||||
AsyncStaticWebHandler &AsyncStaticWebHandler::setLastModified() {
|
||||
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 setLastModified(last_modified);
|
||||
}
|
||||
|
||||
|
@ -124,16 +112,19 @@ bool AsyncStaticWebHandler::_getFile(AsyncWebServerRequest* request) const {
|
|||
path = _path + path;
|
||||
|
||||
// Do we have a file or .gz file
|
||||
if (!canSkipFileCheck && const_cast<AsyncStaticWebHandler*>(this)->_searchFile(request, path))
|
||||
if (!canSkipFileCheck && const_cast<AsyncStaticWebHandler *>(this)->_searchFile(request, path)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Can't handle if not default file
|
||||
if (_default_file.length() == 0)
|
||||
if (_default_file.length() == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Try to add default file, ensure there is a trailing '/' ot the path.
|
||||
if (path.length() == 0 || path[path.length() - 1] != '/')
|
||||
// Try to add default file, ensure there is a trailing '/' to the path.
|
||||
if (path.length() == 0 || path[path.length() - 1] != '/') {
|
||||
path += String('/');
|
||||
}
|
||||
path += _default_file;
|
||||
|
||||
return const_cast<AsyncStaticWebHandler *>(this)->_searchFile(request, path);
|
||||
|
@ -181,6 +172,14 @@ bool AsyncStaticWebHandler::_searchFile(AsyncWebServerRequest* request, const St
|
|||
// Extract the file name from the path and keep it in _tempObject
|
||||
size_t pathLen = path.length();
|
||||
char *_tempPath = (char *)malloc(pathLen + 1);
|
||||
if (_tempPath == NULL) {
|
||||
#ifdef ESP32
|
||||
log_e("Failed to allocate");
|
||||
#endif
|
||||
request->abort();
|
||||
request->_tempFile.close();
|
||||
return false;
|
||||
}
|
||||
snprintf_P(_tempPath, pathLen + 1, PSTR("%s"), path.c_str());
|
||||
request->_tempObject = (void *)_tempPath;
|
||||
}
|
||||
|
@ -191,8 +190,9 @@ bool AsyncStaticWebHandler::_searchFile(AsyncWebServerRequest* request, const St
|
|||
uint8_t AsyncStaticWebHandler::_countBits(const uint8_t value) const {
|
||||
uint8_t w = value;
|
||||
uint8_t n;
|
||||
for (n = 0; w != 0; n++)
|
||||
for (n = 0; w != 0; n++) {
|
||||
w &= w - 1;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
|
@ -212,7 +212,7 @@ void AsyncStaticWebHandler::handleRequest(AsyncWebServerRequest* request) {
|
|||
String etag;
|
||||
if (lw) {
|
||||
setLastModified(lw);
|
||||
#if defined(TARGET_RP2040)
|
||||
#if defined(TARGET_RP2040) || defined(TARGET_RP2350) || defined(PICO_RP2040) || defined(PICO_RP2350)
|
||||
// time_t == long long int
|
||||
constexpr size_t len = 1 + 8 * sizeof(time_t);
|
||||
char buf[len];
|
||||
|
@ -222,16 +222,21 @@ void AsyncStaticWebHandler::handleRequest(AsyncWebServerRequest* request) {
|
|||
etag = lw ^ request->_tempFile.size(); // etag combines file size and lastmod timestamp
|
||||
#endif
|
||||
} else {
|
||||
#if defined(TARGET_RP2040) || defined(TARGET_RP2350) || defined(PICO_RP2040) || defined(PICO_RP2350)
|
||||
etag = String(request->_tempFile.size());
|
||||
#else
|
||||
etag = request->_tempFile.size();
|
||||
#endif
|
||||
}
|
||||
|
||||
bool not_modified = false;
|
||||
|
||||
// if-none-match has precedence over if-modified-since
|
||||
if (request->hasHeader(T_INM))
|
||||
if (request->hasHeader(T_INM)) {
|
||||
not_modified = request->header(T_INM).equals(etag);
|
||||
else if (_last_modified.length())
|
||||
} else if (_last_modified.length()) {
|
||||
not_modified = request->header(T_IMS).equals(_last_modified);
|
||||
}
|
||||
|
||||
AsyncWebServerResponse *response;
|
||||
|
||||
|
@ -242,15 +247,24 @@ void AsyncStaticWebHandler::handleRequest(AsyncWebServerRequest* request) {
|
|||
response = new AsyncFileResponse(request->_tempFile, filename, emptyString, false, _callback);
|
||||
}
|
||||
|
||||
if (!response) {
|
||||
#ifdef ESP32
|
||||
log_e("Failed to allocate");
|
||||
#endif
|
||||
request->abort();
|
||||
return;
|
||||
}
|
||||
|
||||
response->addHeader(T_ETag, etag.c_str());
|
||||
|
||||
if (_last_modified.length())
|
||||
if (_last_modified.length()) {
|
||||
response->addHeader(T_Last_Modified, _last_modified.c_str());
|
||||
if (_cache_control.length())
|
||||
}
|
||||
if (_cache_control.length()) {
|
||||
response->addHeader(T_Cache_Control, _cache_control.c_str());
|
||||
}
|
||||
|
||||
request->send(response);
|
||||
|
||||
}
|
||||
|
||||
AsyncStaticWebHandler &AsyncStaticWebHandler::setTemplateProcessor(AwsTemplateProcessor newCallback) {
|
||||
|
@ -264,8 +278,9 @@ void AsyncCallbackWebHandler::setUri(const String& uri) {
|
|||
}
|
||||
|
||||
bool AsyncCallbackWebHandler::canHandle(AsyncWebServerRequest *request) const {
|
||||
if (!_onRequest || !request->isHTTP() || !(_method & request->method()))
|
||||
if (!_onRequest || !request->isHTTP() || !(_method & request->method())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef ASYNCWEBSERVER_REGEX
|
||||
if (_isRegex) {
|
||||
|
@ -284,31 +299,37 @@ bool AsyncCallbackWebHandler::canHandle(AsyncWebServerRequest* request) const {
|
|||
if (_uri.length() && _uri.startsWith("/*.")) {
|
||||
String uriTemplate = String(_uri);
|
||||
uriTemplate = uriTemplate.substring(uriTemplate.lastIndexOf("."));
|
||||
if (!request->url().endsWith(uriTemplate))
|
||||
if (!request->url().endsWith(uriTemplate)) {
|
||||
return false;
|
||||
}
|
||||
} else if (_uri.length() && _uri.endsWith("*")) {
|
||||
String uriTemplate = String(_uri);
|
||||
uriTemplate = uriTemplate.substring(0, uriTemplate.length() - 1);
|
||||
if (!request->url().startsWith(uriTemplate))
|
||||
if (!request->url().startsWith(uriTemplate)) {
|
||||
return false;
|
||||
} else if (_uri.length() && (_uri != request->url() && !request->url().startsWith(_uri + "/")))
|
||||
}
|
||||
} else if (_uri.length() && (_uri != request->url() && !request->url().startsWith(_uri + "/"))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void AsyncCallbackWebHandler::handleRequest(AsyncWebServerRequest *request) {
|
||||
if (_onRequest)
|
||||
if (_onRequest) {
|
||||
_onRequest(request);
|
||||
else
|
||||
request->send(500);
|
||||
} else {
|
||||
request->send(404, T_text_plain, "Not found");
|
||||
}
|
||||
}
|
||||
void AsyncCallbackWebHandler::handleUpload(AsyncWebServerRequest *request, const String &filename, size_t index, uint8_t *data, size_t len, bool final) {
|
||||
if (_onUpload)
|
||||
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) {
|
||||
// ESP_LOGD("AsyncWebServer", "AsyncCallbackWebHandler::handleBody");
|
||||
if (_onBody)
|
||||
if (_onBody) {
|
||||
_onBody(request, data, len, index, total);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,23 +1,6 @@
|
|||
/*
|
||||
Asynchronous WebServer library for Espressif MCUs
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// Copyright 2016-2025 Hristo Gochkov, Mathieu Carbou, Emil Muratov
|
||||
|
||||
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
|
||||
*/
|
||||
#include "ESPAsyncWebServer.h"
|
||||
#include "WebAuthentication.h"
|
||||
#include "WebResponseImpl.h"
|
||||
|
@ -26,25 +9,84 @@
|
|||
|
||||
#define __is_param_char(c) ((c) && ((c) != '{') && ((c) != '[') && ((c) != '&') && ((c) != '='))
|
||||
|
||||
static void doNotDelete(AsyncWebServerRequest *) {}
|
||||
|
||||
using namespace asyncsrv;
|
||||
|
||||
enum { PARSE_REQ_START = 0,
|
||||
enum {
|
||||
PARSE_REQ_START = 0,
|
||||
PARSE_REQ_HEADERS = 1,
|
||||
PARSE_REQ_BODY = 2,
|
||||
PARSE_REQ_END = 3,
|
||||
PARSE_REQ_FAIL = 4 };
|
||||
PARSE_REQ_FAIL = 4
|
||||
};
|
||||
|
||||
AsyncWebServerRequest::AsyncWebServerRequest(AsyncWebServer *s, AsyncClient *c)
|
||||
: _client(c), _server(s), _handler(NULL), _response(NULL), _temp(), _parseState(PARSE_REQ_START), _version(0), _method(HTTP_ANY), _url(), _host(), _contentType(), _boundary(), _authorization(), _reqconntype(RCT_HTTP), _authMethod(AsyncAuthType::AUTH_NONE), _isMultipart(false), _isPlainPost(false), _expectingContinue(false), _contentLength(0), _parsedLength(0), _multiParseState(0), _boundaryPosition(0), _itemStartIndex(0), _itemSize(0), _itemName(), _itemFilename(), _itemType(), _itemValue(), _itemBuffer(0), _itemBufferIndex(0), _itemIsFile(false), _tempObject(NULL) {
|
||||
c->onError([](void* r, AsyncClient* c, int8_t error) { (void)c; AsyncWebServerRequest *req = (AsyncWebServerRequest*)r; req->_onError(error); }, this);
|
||||
c->onAck([](void* r, AsyncClient* c, size_t len, uint32_t time) { (void)c; AsyncWebServerRequest *req = (AsyncWebServerRequest*)r; req->_onAck(len, time); }, this);
|
||||
c->onDisconnect([](void* r, AsyncClient* c) { AsyncWebServerRequest *req = (AsyncWebServerRequest*)r; req->_onDisconnect(); delete c; }, this);
|
||||
c->onTimeout([](void* r, AsyncClient* c, uint32_t time) { (void)c; AsyncWebServerRequest *req = (AsyncWebServerRequest*)r; req->_onTimeout(time); }, this);
|
||||
c->onData([](void* r, AsyncClient* c, void* buf, size_t len) { (void)c; AsyncWebServerRequest *req = (AsyncWebServerRequest*)r; req->_onData(buf, len); }, this);
|
||||
c->onPoll([](void* r, AsyncClient* c) { (void)c; AsyncWebServerRequest *req = ( AsyncWebServerRequest*)r; req->_onPoll(); }, this);
|
||||
: _client(c), _server(s), _handler(NULL), _response(NULL), _temp(), _parseState(PARSE_REQ_START), _version(0), _method(HTTP_ANY), _url(), _host(),
|
||||
_contentType(), _boundary(), _authorization(), _reqconntype(RCT_HTTP), _authMethod(AsyncAuthType::AUTH_NONE), _isMultipart(false), _isPlainPost(false),
|
||||
_expectingContinue(false), _contentLength(0), _parsedLength(0), _multiParseState(0), _boundaryPosition(0), _itemStartIndex(0), _itemSize(0), _itemName(),
|
||||
_itemFilename(), _itemType(), _itemValue(), _itemBuffer(0), _itemBufferIndex(0), _itemIsFile(false), _tempObject(NULL) {
|
||||
c->onError(
|
||||
[](void *r, AsyncClient *c, int8_t error) {
|
||||
(void)c;
|
||||
// log_e("AsyncWebServerRequest::_onError");
|
||||
AsyncWebServerRequest *req = (AsyncWebServerRequest *)r;
|
||||
req->_onError(error);
|
||||
},
|
||||
this
|
||||
);
|
||||
c->onAck(
|
||||
[](void *r, AsyncClient *c, size_t len, uint32_t time) {
|
||||
(void)c;
|
||||
// log_e("AsyncWebServerRequest::_onAck");
|
||||
AsyncWebServerRequest *req = (AsyncWebServerRequest *)r;
|
||||
req->_onAck(len, time);
|
||||
},
|
||||
this
|
||||
);
|
||||
c->onDisconnect(
|
||||
[](void *r, AsyncClient *c) {
|
||||
// log_e("AsyncWebServerRequest::_onDisconnect");
|
||||
AsyncWebServerRequest *req = (AsyncWebServerRequest *)r;
|
||||
req->_onDisconnect();
|
||||
delete c;
|
||||
},
|
||||
this
|
||||
);
|
||||
c->onTimeout(
|
||||
[](void *r, AsyncClient *c, uint32_t time) {
|
||||
(void)c;
|
||||
// log_e("AsyncWebServerRequest::_onTimeout");
|
||||
AsyncWebServerRequest *req = (AsyncWebServerRequest *)r;
|
||||
req->_onTimeout(time);
|
||||
},
|
||||
this
|
||||
);
|
||||
c->onData(
|
||||
[](void *r, AsyncClient *c, void *buf, size_t len) {
|
||||
(void)c;
|
||||
// log_e("AsyncWebServerRequest::_onData");
|
||||
AsyncWebServerRequest *req = (AsyncWebServerRequest *)r;
|
||||
req->_onData(buf, len);
|
||||
},
|
||||
this
|
||||
);
|
||||
c->onPoll(
|
||||
[](void *r, AsyncClient *c) {
|
||||
(void)c;
|
||||
// log_e("AsyncWebServerRequest::_onPoll");
|
||||
AsyncWebServerRequest *req = (AsyncWebServerRequest *)r;
|
||||
req->_onPoll();
|
||||
},
|
||||
this
|
||||
);
|
||||
}
|
||||
|
||||
AsyncWebServerRequest::~AsyncWebServerRequest() {
|
||||
// log_e("AsyncWebServerRequest::~AsyncWebServerRequest");
|
||||
|
||||
_this.reset();
|
||||
|
||||
_headers.clear();
|
||||
|
||||
_pathParams.clear();
|
||||
|
@ -74,7 +116,7 @@ void AsyncWebServerRequest::_onData(void* buf, size_t len) {
|
|||
log_d("SSL/TLS handshake detected: resetting connection");
|
||||
#endif
|
||||
_parseState = PARSE_REQ_FAIL;
|
||||
_client->abort();
|
||||
abort();
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
@ -89,7 +131,7 @@ void AsyncWebServerRequest::_onData(void* buf, size_t len) {
|
|||
// Check for null characters in header
|
||||
if (!str[i]) {
|
||||
_parseState = PARSE_REQ_FAIL;
|
||||
_client->abort();
|
||||
abort();
|
||||
return;
|
||||
}
|
||||
if (str[i] == '\n') {
|
||||
|
@ -99,7 +141,14 @@ void AsyncWebServerRequest::_onData(void* buf, size_t len) {
|
|||
if (i == len) { // No new line, just add the buffer in _temp
|
||||
char ch = str[len - 1];
|
||||
str[len - 1] = 0;
|
||||
_temp.reserve(_temp.length() + len);
|
||||
if (!_temp.reserve(_temp.length() + len)) {
|
||||
#ifdef ESP32
|
||||
log_e("Failed to allocate");
|
||||
#endif
|
||||
_parseState = PARSE_REQ_FAIL;
|
||||
abort();
|
||||
return;
|
||||
}
|
||||
_temp.concat(str);
|
||||
_temp.concat(ch);
|
||||
} else { // Found new line - extract it and parse
|
||||
|
@ -118,6 +167,8 @@ void AsyncWebServerRequest::_onData(void* buf, size_t len) {
|
|||
// A handler should be already attached at this point in _parseLine function.
|
||||
// If handler does nothing (_onRequest is NULL), we don't need to really parse the body.
|
||||
const bool needParse = _handler && !_handler->isRequestHandlerTrivial();
|
||||
// Discard any bytes after content length; handlers may overrun their buffers
|
||||
len = std::min(len, _contentLength - _parsedLength);
|
||||
if (_isMultipart) {
|
||||
if (needParse) {
|
||||
size_t i;
|
||||
|
@ -125,16 +176,16 @@ void AsyncWebServerRequest::_onData(void* buf, size_t len) {
|
|||
_parseMultipartPostByte(((uint8_t *)buf)[i], i == len - 1);
|
||||
_parsedLength++;
|
||||
}
|
||||
} else
|
||||
} else {
|
||||
_parsedLength += len;
|
||||
}
|
||||
} else {
|
||||
if (_parsedLength == 0) {
|
||||
if (_contentType.startsWith(T_app_xform_urlencoded)) {
|
||||
_isPlainPost = true;
|
||||
} else if (_contentType == T_text_plain && __is_param_char(((char *)buf)[0])) {
|
||||
size_t i = 0;
|
||||
while (i < len && __is_param_char(((char*)buf)[i++]))
|
||||
;
|
||||
while (i < len && __is_param_char(((char *)buf)[i++]));
|
||||
if (i < len && ((char *)buf)[i - 1] == '=') {
|
||||
_isPlainPost = true;
|
||||
}
|
||||
|
@ -142,8 +193,9 @@ void AsyncWebServerRequest::_onData(void* buf, size_t len) {
|
|||
}
|
||||
if (!_isPlainPost) {
|
||||
// ESP_LOGD("AsyncWebServer", "_isPlainPost: %d, _handler: %p", _isPlainPost, _handler);
|
||||
if (_handler)
|
||||
if (_handler) {
|
||||
_handler->handleBody(this, (uint8_t *)buf, len, _parsedLength, _contentLength);
|
||||
}
|
||||
_parsedLength += len;
|
||||
} else if (needParse) {
|
||||
size_t i;
|
||||
|
@ -157,16 +209,8 @@ void AsyncWebServerRequest::_onData(void* buf, size_t len) {
|
|||
}
|
||||
if (_parsedLength == _contentLength) {
|
||||
_parseState = PARSE_REQ_END;
|
||||
_server->_runChain(this, [this]() { return _handler ? _handler->_runChain(this, [this]() { _handler->handleRequest(this); }) : send(501); });
|
||||
if (!_sent) {
|
||||
if (!_response)
|
||||
send(501, T_text_plain, "Handler did not handle the request");
|
||||
else if (!_response->_sourceValid())
|
||||
send(500, T_text_plain, "Invalid data in handler");
|
||||
_client->setRxTimeout(0);
|
||||
_response->_respond(this);
|
||||
_sent = true;
|
||||
}
|
||||
_runMiddlewareChain();
|
||||
_send();
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
@ -233,14 +277,18 @@ void AsyncWebServerRequest::_addGetParams(const String& params) {
|
|||
size_t start = 0;
|
||||
while (start < params.length()) {
|
||||
int end = params.indexOf('&', start);
|
||||
if (end < 0)
|
||||
if (end < 0) {
|
||||
end = params.length();
|
||||
}
|
||||
int equal = params.indexOf('=', start);
|
||||
if (equal < 0 || equal > end)
|
||||
if (equal < 0 || equal > end) {
|
||||
equal = end;
|
||||
String name(params.substring(start, equal));
|
||||
String value(equal + 1 < end ? params.substring(equal + 1, end) : String());
|
||||
_params.emplace_back(urlDecode(name), urlDecode(value));
|
||||
}
|
||||
String name = urlDecode(params.substring(start, equal));
|
||||
String value = urlDecode(equal + 1 < end ? params.substring(equal + 1, end) : emptyString);
|
||||
if (name.length()) {
|
||||
_params.emplace_back(name, value);
|
||||
}
|
||||
start = end + 1;
|
||||
}
|
||||
}
|
||||
|
@ -280,11 +328,13 @@ bool AsyncWebServerRequest::_parseReqHead() {
|
|||
_url = urlDecode(u);
|
||||
_addGetParams(g);
|
||||
|
||||
if (!_url.length())
|
||||
if (!_url.length()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!_temp.startsWith(T_HTTP_1_0))
|
||||
if (!_temp.startsWith(T_HTTP_1_0)) {
|
||||
_version = 1;
|
||||
}
|
||||
|
||||
_temp = emptyString;
|
||||
return true;
|
||||
|
@ -344,18 +394,19 @@ bool AsyncWebServerRequest::_parseReqHeader() {
|
|||
}
|
||||
_headers.emplace_back(name, value);
|
||||
}
|
||||
#ifndef TARGET_RP2040
|
||||
_temp.clear();
|
||||
#else
|
||||
#if defined(TARGET_RP2040) || defined(TARGET_RP2350) || defined(PICO_RP2040) || defined(PICO_RP2350)
|
||||
// Ancient PRI core does not have String::clear() method 8-()
|
||||
_temp = emptyString;
|
||||
#else
|
||||
_temp.clear();
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
void AsyncWebServerRequest::_parsePlainPostChar(uint8_t data) {
|
||||
if (data && (char)data != '&')
|
||||
if (data && (char)data != '&') {
|
||||
_temp += (char)data;
|
||||
}
|
||||
if (!data || (char)data == '&' || _parsedLength == _contentLength) {
|
||||
String name(T_BODY);
|
||||
String value(_temp);
|
||||
|
@ -363,13 +414,16 @@ void AsyncWebServerRequest::_parsePlainPostChar(uint8_t data) {
|
|||
name = _temp.substring(0, _temp.indexOf('='));
|
||||
value = _temp.substring(_temp.indexOf('=') + 1);
|
||||
}
|
||||
_params.emplace_back(urlDecode(name), urlDecode(value), true);
|
||||
name = urlDecode(name);
|
||||
if (name.length()) {
|
||||
_params.emplace_back(name, urlDecode(value), true);
|
||||
}
|
||||
|
||||
#ifndef TARGET_RP2040
|
||||
_temp.clear();
|
||||
#else
|
||||
#if defined(TARGET_RP2040) || defined(TARGET_RP2350) || defined(PICO_RP2040) || defined(PICO_RP2350)
|
||||
// Ancient PRI core does not have String::clear() method 8-()
|
||||
_temp = emptyString;
|
||||
#else
|
||||
_temp.clear();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
@ -379,8 +433,9 @@ void AsyncWebServerRequest::_handleUploadByte(uint8_t data, bool last) {
|
|||
|
||||
if (last || _itemBufferIndex == RESPONSE_STREAM_BUFFER_SIZE) {
|
||||
// check if authenticated before calling the upload
|
||||
if (_handler)
|
||||
if (_handler) {
|
||||
_handler->handleUpload(this, _itemFilename, _itemSize - _itemBufferIndex, _itemBuffer, _itemBufferIndex, false);
|
||||
}
|
||||
_itemBufferIndex = 0;
|
||||
}
|
||||
}
|
||||
|
@ -442,8 +497,9 @@ void AsyncWebServerRequest::_parseMultipartPostByte(uint8_t data, bool last) {
|
|||
_itemIsFile = false;
|
||||
}
|
||||
} else if (_multiParseState == PARSE_HEADERS) {
|
||||
if ((char)data != '\r' && (char)data != '\n')
|
||||
if ((char)data != '\r' && (char)data != '\n') {
|
||||
_temp += (char)data;
|
||||
}
|
||||
if ((char)data == '\n') {
|
||||
if (_temp.length()) {
|
||||
if (_temp.length() > 12 && _temp.substring(0, 12).equalsIgnoreCase(T_Content_Type)) {
|
||||
|
@ -479,11 +535,16 @@ void AsyncWebServerRequest::_parseMultipartPostByte(uint8_t data, bool last) {
|
|||
_itemStartIndex = _parsedLength;
|
||||
_itemValue = emptyString;
|
||||
if (_itemIsFile) {
|
||||
if (_itemBuffer)
|
||||
if (_itemBuffer) {
|
||||
free(_itemBuffer);
|
||||
}
|
||||
_itemBuffer = (uint8_t *)malloc(RESPONSE_STREAM_BUFFER_SIZE);
|
||||
if (_itemBuffer == NULL) {
|
||||
#ifdef ESP32
|
||||
log_e("Failed to allocate");
|
||||
#endif
|
||||
_multiParseState = PARSE_ERROR;
|
||||
abort();
|
||||
return;
|
||||
}
|
||||
_itemBufferIndex = 0;
|
||||
|
@ -526,8 +587,9 @@ void AsyncWebServerRequest::_parseMultipartPostByte(uint8_t data, bool last) {
|
|||
itemWriteByte('-');
|
||||
itemWriteByte('-');
|
||||
uint8_t i;
|
||||
for (i = 0; i < _boundaryPosition; i++)
|
||||
for (i = 0; i < _boundaryPosition; i++) {
|
||||
itemWriteByte(_boundary.c_str()[i]);
|
||||
}
|
||||
_parseMultipartPostByte(data, last);
|
||||
} else if (_boundaryPosition == _boundary.length() - 1) {
|
||||
_multiParseState = DASH3_OR_RETURN2;
|
||||
|
@ -535,8 +597,9 @@ void AsyncWebServerRequest::_parseMultipartPostByte(uint8_t data, bool last) {
|
|||
_params.emplace_back(_itemName, _itemValue, true);
|
||||
} else {
|
||||
if (_itemSize) {
|
||||
if (_handler)
|
||||
if (_handler) {
|
||||
_handler->handleUpload(this, _itemFilename, _itemSize - _itemBufferIndex, _itemBuffer, _itemBufferIndex, true);
|
||||
}
|
||||
_itemBufferIndex = 0;
|
||||
_params.emplace_back(_itemName, _itemFilename, true, true, _itemSize);
|
||||
}
|
||||
|
@ -563,8 +626,9 @@ void AsyncWebServerRequest::_parseMultipartPostByte(uint8_t data, bool last) {
|
|||
itemWriteByte('-');
|
||||
itemWriteByte('-');
|
||||
uint8_t i;
|
||||
for (i = 0; i < _boundary.length(); i++)
|
||||
for (i = 0; i < _boundary.length(); i++) {
|
||||
itemWriteByte(_boundary.c_str()[i]);
|
||||
}
|
||||
_parseMultipartPostByte(data, last);
|
||||
}
|
||||
} else if (_multiParseState == EXPECT_FEED2) {
|
||||
|
@ -578,8 +642,9 @@ void AsyncWebServerRequest::_parseMultipartPostByte(uint8_t data, bool last) {
|
|||
itemWriteByte('-');
|
||||
itemWriteByte('-');
|
||||
uint8_t i;
|
||||
for (i = 0; i < _boundary.length(); i++)
|
||||
for (i = 0; i < _boundary.length(); i++) {
|
||||
itemWriteByte(_boundary.c_str()[i]);
|
||||
}
|
||||
itemWriteByte('\r');
|
||||
_parseMultipartPostByte(data, last);
|
||||
}
|
||||
|
@ -590,13 +655,13 @@ void AsyncWebServerRequest::_parseLine() {
|
|||
if (_parseState == PARSE_REQ_START) {
|
||||
if (!_temp.length()) {
|
||||
_parseState = PARSE_REQ_FAIL;
|
||||
_client->abort();
|
||||
abort();
|
||||
} else {
|
||||
if (_parseReqHead()) {
|
||||
_parseState = PARSE_REQ_HEADERS;
|
||||
} else {
|
||||
_parseState = PARSE_REQ_FAIL;
|
||||
_client->abort();
|
||||
abort();
|
||||
}
|
||||
}
|
||||
return;
|
||||
|
@ -615,19 +680,71 @@ void AsyncWebServerRequest::_parseLine() {
|
|||
_parseState = PARSE_REQ_BODY;
|
||||
} else {
|
||||
_parseState = PARSE_REQ_END;
|
||||
_server->_runChain(this, [this]() { return _handler ? _handler->_runChain(this, [this]() { _handler->handleRequest(this); }) : send(501); });
|
||||
if (!_sent) {
|
||||
if (!_response)
|
||||
_runMiddlewareChain();
|
||||
_send();
|
||||
}
|
||||
} else {
|
||||
_parseReqHeader();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AsyncWebServerRequest::_runMiddlewareChain() {
|
||||
if (_handler && _handler->mustSkipServerMiddlewares()) {
|
||||
_handler->_runChain(this, [this]() {
|
||||
_handler->handleRequest(this);
|
||||
});
|
||||
} else {
|
||||
_server->_runChain(this, [this]() {
|
||||
if (_handler) {
|
||||
_handler->_runChain(this, [this]() {
|
||||
_handler->handleRequest(this);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void AsyncWebServerRequest::_send() {
|
||||
if (!_sent && !_paused) {
|
||||
// log_d("AsyncWebServerRequest::_send()");
|
||||
|
||||
// user did not create a response ?
|
||||
if (!_response) {
|
||||
send(501, T_text_plain, "Handler did not handle the request");
|
||||
else if (!_response->_sourceValid())
|
||||
}
|
||||
|
||||
// response is not valid ?
|
||||
if (!_response->_sourceValid()) {
|
||||
send(500, T_text_plain, "Invalid data in handler");
|
||||
}
|
||||
|
||||
// here, we either have a response give nfrom user or one of the two above
|
||||
_client->setRxTimeout(0);
|
||||
_response->_respond(this);
|
||||
_sent = true;
|
||||
}
|
||||
}
|
||||
} else
|
||||
_parseReqHeader();
|
||||
|
||||
AsyncWebServerRequestPtr AsyncWebServerRequest::pause() {
|
||||
if (_paused) {
|
||||
return _this;
|
||||
}
|
||||
client()->setRxTimeout(0);
|
||||
// this shared ptr will hold the request pointer until it gets destroyed following a disconnect.
|
||||
// this is just used as a holder providing weak observers, so the deleter is a no-op.
|
||||
_this = std::shared_ptr<AsyncWebServerRequest>(this, doNotDelete);
|
||||
_paused = true;
|
||||
return _this;
|
||||
}
|
||||
|
||||
void AsyncWebServerRequest::abort() {
|
||||
if (!_sent) {
|
||||
_sent = true;
|
||||
_paused = false;
|
||||
_this.reset();
|
||||
// log_e("AsyncWebServerRequest::abort");
|
||||
_client->abort();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -651,7 +768,9 @@ bool AsyncWebServerRequest::hasHeader(const __FlashStringHelper* data) const {
|
|||
#endif
|
||||
|
||||
const AsyncWebHeader *AsyncWebServerRequest::getHeader(const char *name) const {
|
||||
auto iter = std::find_if(std::begin(_headers), std::end(_headers), [&name](const AsyncWebHeader& header) { return header.name().equalsIgnoreCase(name); });
|
||||
auto iter = std::find_if(std::begin(_headers), std::end(_headers), [&name](const AsyncWebHeader &header) {
|
||||
return header.name().equalsIgnoreCase(name);
|
||||
});
|
||||
return (iter == std::end(_headers)) ? nullptr : &(*iter);
|
||||
}
|
||||
|
||||
|
@ -672,23 +791,25 @@ const AsyncWebHeader* AsyncWebServerRequest::getHeader(const __FlashStringHelper
|
|||
#endif
|
||||
|
||||
const AsyncWebHeader *AsyncWebServerRequest::getHeader(size_t num) const {
|
||||
if (num >= _headers.size())
|
||||
if (num >= _headers.size()) {
|
||||
return nullptr;
|
||||
}
|
||||
return &(*std::next(_headers.cbegin(), num));
|
||||
}
|
||||
|
||||
size_t AsyncWebServerRequest::getHeaderNames(std::vector<const char *> &names) const {
|
||||
const size_t size = _headers.size();
|
||||
names.reserve(size);
|
||||
const size_t size = names.size();
|
||||
for (const auto &h : _headers) {
|
||||
names.push_back(h.name().c_str());
|
||||
}
|
||||
return size;
|
||||
return names.size() - size;
|
||||
}
|
||||
|
||||
bool AsyncWebServerRequest::removeHeader(const char *name) {
|
||||
const size_t size = _headers.size();
|
||||
_headers.remove_if([name](const AsyncWebHeader& header) { return header.name().equalsIgnoreCase(name); });
|
||||
_headers.remove_if([name](const AsyncWebHeader &header) {
|
||||
return header.name().equalsIgnoreCase(name);
|
||||
});
|
||||
return size != _headers.size();
|
||||
}
|
||||
|
||||
|
@ -721,8 +842,9 @@ const AsyncWebParameter* AsyncWebServerRequest::getParam(const __FlashStringHelp
|
|||
#endif
|
||||
|
||||
const AsyncWebParameter *AsyncWebServerRequest::getParam(size_t num) const {
|
||||
if (num >= _params.size())
|
||||
if (num >= _params.size()) {
|
||||
return nullptr;
|
||||
}
|
||||
return &(*std::next(_params.cbegin(), num));
|
||||
}
|
||||
|
||||
|
@ -748,24 +870,30 @@ double AsyncWebServerRequest::getAttribute(const char* name, double defaultValue
|
|||
}
|
||||
|
||||
AsyncWebServerResponse *AsyncWebServerRequest::beginResponse(int code, const char *contentType, const char *content, AwsTemplateProcessor callback) {
|
||||
if (callback)
|
||||
if (callback) {
|
||||
return new AsyncProgmemResponse(code, contentType, (const uint8_t *)content, strlen(content), callback);
|
||||
}
|
||||
return new AsyncBasicResponse(code, contentType, content);
|
||||
}
|
||||
|
||||
AsyncWebServerResponse* AsyncWebServerRequest::beginResponse(int code, const char* contentType, const uint8_t* content, size_t len, AwsTemplateProcessor callback) {
|
||||
AsyncWebServerResponse *
|
||||
AsyncWebServerRequest::beginResponse(int code, const char *contentType, const uint8_t *content, size_t len, AwsTemplateProcessor callback) {
|
||||
return new AsyncProgmemResponse(code, contentType, content, len, callback);
|
||||
}
|
||||
|
||||
AsyncWebServerResponse* AsyncWebServerRequest::beginResponse(FS& fs, const String& path, const char* contentType, bool download, AwsTemplateProcessor callback) {
|
||||
if (fs.exists(path) || (!download && fs.exists(path + T__gz)))
|
||||
AsyncWebServerResponse *
|
||||
AsyncWebServerRequest::beginResponse(FS &fs, const String &path, const char *contentType, bool download, AwsTemplateProcessor callback) {
|
||||
if (fs.exists(path) || (!download && fs.exists(path + T__gz))) {
|
||||
return new AsyncFileResponse(fs, path, contentType, download, callback);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
AsyncWebServerResponse* AsyncWebServerRequest::beginResponse(File content, const String& path, const char* contentType, bool download, AwsTemplateProcessor callback) {
|
||||
if (content == true)
|
||||
AsyncWebServerResponse *
|
||||
AsyncWebServerRequest::beginResponse(File content, const String &path, const char *contentType, bool download, AwsTemplateProcessor callback) {
|
||||
if (content == true) {
|
||||
return new AsyncFileResponse(content, path, contentType, download, callback);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -773,13 +901,16 @@ AsyncWebServerResponse* AsyncWebServerRequest::beginResponse(Stream& stream, con
|
|||
return new AsyncStreamResponse(stream, contentType, len, callback);
|
||||
}
|
||||
|
||||
AsyncWebServerResponse* AsyncWebServerRequest::beginResponse(const char* contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback) {
|
||||
AsyncWebServerResponse *
|
||||
AsyncWebServerRequest::beginResponse(const char *contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback) {
|
||||
return new AsyncCallbackResponse(contentType, len, callback, templateCallback);
|
||||
}
|
||||
|
||||
AsyncWebServerResponse* AsyncWebServerRequest::beginChunkedResponse(const char* contentType, AwsResponseFiller callback, AwsTemplateProcessor templateCallback) {
|
||||
if (_version)
|
||||
AsyncWebServerResponse *
|
||||
AsyncWebServerRequest::beginChunkedResponse(const char *contentType, AwsResponseFiller callback, AwsTemplateProcessor templateCallback) {
|
||||
if (_version) {
|
||||
return new AsyncChunkedResponse(contentType, callback, templateCallback);
|
||||
}
|
||||
return new AsyncCallbackResponse(contentType, 0, callback, templateCallback);
|
||||
}
|
||||
|
||||
|
@ -792,11 +923,22 @@ AsyncWebServerResponse* AsyncWebServerRequest::beginResponse_P(int code, const S
|
|||
}
|
||||
|
||||
void AsyncWebServerRequest::send(AsyncWebServerResponse *response) {
|
||||
if (_sent)
|
||||
// request is already sent on the wire ?
|
||||
if (_sent) {
|
||||
return;
|
||||
if (_response)
|
||||
}
|
||||
|
||||
// if we already had a response, delete it and replace it with the new one
|
||||
if (_response) {
|
||||
delete _response;
|
||||
}
|
||||
_response = response;
|
||||
|
||||
// if request was paused, we need to send the response now
|
||||
if (_paused) {
|
||||
_paused = false;
|
||||
_send();
|
||||
}
|
||||
}
|
||||
|
||||
void AsyncWebServerRequest::redirect(const char *url, int code) {
|
||||
|
@ -807,30 +949,34 @@ void AsyncWebServerRequest::redirect(const char* url, int code) {
|
|||
|
||||
bool AsyncWebServerRequest::authenticate(const char *username, const char *password, const char *realm, bool passwordIsHash) const {
|
||||
if (_authorization.length()) {
|
||||
if (_authMethod == AsyncAuthType::AUTH_DIGEST)
|
||||
if (_authMethod == AsyncAuthType::AUTH_DIGEST) {
|
||||
return checkDigestAuthentication(_authorization.c_str(), methodToString(), username, password, realm, passwordIsHash, NULL, NULL, NULL);
|
||||
else if (!passwordIsHash)
|
||||
} else if (!passwordIsHash) {
|
||||
return checkBasicAuthentication(_authorization.c_str(), username, password);
|
||||
else
|
||||
} else {
|
||||
return _authorization.equals(password);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AsyncWebServerRequest::authenticate(const char *hash) const {
|
||||
if (!_authorization.length() || hash == NULL)
|
||||
if (!_authorization.length() || hash == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_authMethod == AsyncAuthType::AUTH_DIGEST) {
|
||||
String hStr = String(hash);
|
||||
int separator = hStr.indexOf(':');
|
||||
if (separator <= 0)
|
||||
if (separator <= 0) {
|
||||
return false;
|
||||
}
|
||||
String username = hStr.substring(0, separator);
|
||||
hStr = hStr.substring(separator + 1);
|
||||
separator = hStr.indexOf(':');
|
||||
if (separator <= 0)
|
||||
if (separator <= 0) {
|
||||
return false;
|
||||
}
|
||||
String realm = hStr.substring(0, separator);
|
||||
hStr = hStr.substring(separator + 1);
|
||||
return checkDigestAuthentication(_authorization.c_str(), methodToString(), username.c_str(), hStr.c_str(), realm.c_str(), true, NULL, NULL, NULL);
|
||||
|
@ -841,38 +987,57 @@ bool AsyncWebServerRequest::authenticate(const char* hash) const {
|
|||
}
|
||||
|
||||
void AsyncWebServerRequest::requestAuthentication(AsyncAuthType method, const char *realm, const char *_authFailMsg) {
|
||||
if (!realm)
|
||||
if (!realm) {
|
||||
realm = T_LOGIN_REQ;
|
||||
}
|
||||
|
||||
AsyncWebServerResponse *r = _authFailMsg ? beginResponse(401, T_text_html, _authFailMsg) : beginResponse(401);
|
||||
|
||||
switch (method) {
|
||||
case AsyncAuthType::AUTH_BASIC: {
|
||||
case AsyncAuthType::AUTH_BASIC:
|
||||
{
|
||||
String header;
|
||||
header.reserve(strlen(T_BASIC_REALM) + strlen(realm) + 1);
|
||||
if (header.reserve(strlen(T_BASIC_REALM) + strlen(realm) + 1)) {
|
||||
header.concat(T_BASIC_REALM);
|
||||
header.concat(realm);
|
||||
header.concat('"');
|
||||
r->addHeader(T_WWW_AUTH, header.c_str());
|
||||
} else {
|
||||
#ifdef ESP32
|
||||
log_e("Failed to allocate");
|
||||
#endif
|
||||
abort();
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case AsyncAuthType::AUTH_DIGEST: {
|
||||
case AsyncAuthType::AUTH_DIGEST:
|
||||
{
|
||||
size_t len = strlen(T_DIGEST_) + strlen(T_realm__) + strlen(T_auth_nonce) + 32 + strlen(T__opaque) + 32 + 1;
|
||||
String header;
|
||||
header.reserve(len + strlen(realm));
|
||||
if (header.reserve(len + strlen(realm))) {
|
||||
const String nonce = genRandomMD5();
|
||||
const String opaque = genRandomMD5();
|
||||
if (nonce.length() && opaque.length()) {
|
||||
header.concat(T_DIGEST_);
|
||||
header.concat(T_realm__);
|
||||
header.concat(realm);
|
||||
header.concat(T_auth_nonce);
|
||||
header.concat(genRandomMD5());
|
||||
header.concat(nonce);
|
||||
header.concat(T__opaque);
|
||||
header.concat(genRandomMD5());
|
||||
header.concat(opaque);
|
||||
header.concat((char)0x22); // '"'
|
||||
r->addHeader(T_WWW_AUTH, header.c_str());
|
||||
} else {
|
||||
#ifdef ESP32
|
||||
log_e("Failed to allocate");
|
||||
#endif
|
||||
abort();
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
send(r);
|
||||
|
@ -917,7 +1082,12 @@ const String& AsyncWebServerRequest::argName(size_t i) const {
|
|||
}
|
||||
|
||||
const String &AsyncWebServerRequest::pathArg(size_t i) const {
|
||||
return i < _pathParams.size() ? _pathParams[i] : emptyString;
|
||||
if (i >= _pathParams.size()) {
|
||||
return emptyString;
|
||||
}
|
||||
auto it = _pathParams.begin();
|
||||
std::advance(it, i);
|
||||
return *it;
|
||||
}
|
||||
|
||||
const String &AsyncWebServerRequest::header(const char *name) const {
|
||||
|
@ -946,7 +1116,13 @@ String AsyncWebServerRequest::urlDecode(const String& text) const {
|
|||
unsigned int len = text.length();
|
||||
unsigned int i = 0;
|
||||
String decoded;
|
||||
decoded.reserve(len); // Allocate the string internal buffer - never longer from source text
|
||||
// Allocate the string internal buffer - never longer from source text
|
||||
if (!decoded.reserve(len)) {
|
||||
#ifdef ESP32
|
||||
log_e("Failed to allocate");
|
||||
#endif
|
||||
return emptyString;
|
||||
}
|
||||
while (i < len) {
|
||||
char decodedChar;
|
||||
char encodedChar = text.charAt(i++);
|
||||
|
@ -965,44 +1141,45 @@ String AsyncWebServerRequest::urlDecode(const String& text) const {
|
|||
}
|
||||
|
||||
const char *AsyncWebServerRequest::methodToString() const {
|
||||
if (_method == HTTP_ANY)
|
||||
if (_method == HTTP_ANY) {
|
||||
return T_ANY;
|
||||
if (_method & HTTP_GET)
|
||||
}
|
||||
if (_method & HTTP_GET) {
|
||||
return T_GET;
|
||||
if (_method & HTTP_POST)
|
||||
}
|
||||
if (_method & HTTP_POST) {
|
||||
return T_POST;
|
||||
if (_method & HTTP_DELETE)
|
||||
}
|
||||
if (_method & HTTP_DELETE) {
|
||||
return T_DELETE;
|
||||
if (_method & HTTP_PUT)
|
||||
}
|
||||
if (_method & HTTP_PUT) {
|
||||
return T_PUT;
|
||||
if (_method & HTTP_PATCH)
|
||||
}
|
||||
if (_method & HTTP_PATCH) {
|
||||
return T_PATCH;
|
||||
if (_method & HTTP_HEAD)
|
||||
}
|
||||
if (_method & HTTP_HEAD) {
|
||||
return T_HEAD;
|
||||
if (_method & HTTP_OPTIONS)
|
||||
}
|
||||
if (_method & HTTP_OPTIONS) {
|
||||
return T_OPTIONS;
|
||||
}
|
||||
return T_UNKNOWN;
|
||||
}
|
||||
|
||||
const char *AsyncWebServerRequest::requestedConnTypeToString() const {
|
||||
switch (_reqconntype) {
|
||||
case RCT_NOT_USED:
|
||||
return T_RCT_NOT_USED;
|
||||
case RCT_DEFAULT:
|
||||
return T_RCT_DEFAULT;
|
||||
case RCT_HTTP:
|
||||
return T_RCT_HTTP;
|
||||
case RCT_WS:
|
||||
return T_RCT_WS;
|
||||
case RCT_EVENT:
|
||||
return T_RCT_EVENT;
|
||||
default:
|
||||
return T_ERROR;
|
||||
case RCT_NOT_USED: return T_RCT_NOT_USED;
|
||||
case RCT_DEFAULT: return T_RCT_DEFAULT;
|
||||
case RCT_HTTP: return T_RCT_HTTP;
|
||||
case RCT_WS: return T_RCT_WS;
|
||||
case RCT_EVENT: return T_RCT_EVENT;
|
||||
default: return T_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
bool AsyncWebServerRequest::isExpectedRequestedConnType(RequestedConnectionType erct1, RequestedConnectionType erct2, RequestedConnectionType erct3) const {
|
||||
return ((erct1 != RCT_NOT_USED) && (erct1 == _reqconntype)) ||
|
||||
((erct2 != RCT_NOT_USED) && (erct2 == _reqconntype)) ||
|
||||
((erct3 != RCT_NOT_USED) && (erct3 == _reqconntype));
|
||||
return ((erct1 != RCT_NOT_USED) && (erct1 == _reqconntype)) || ((erct2 != RCT_NOT_USED) && (erct2 == _reqconntype))
|
||||
|| ((erct3 != RCT_NOT_USED) && (erct3 == _reqconntype));
|
||||
}
|
||||
|
|
|
@ -1,23 +1,6 @@
|
|||
/*
|
||||
Asynchronous WebServer library for Espressif MCUs
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// Copyright 2016-2025 Hristo Gochkov, Mathieu Carbou, Emil Muratov
|
||||
|
||||
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 ASYNCWEBSERVERRESPONSEIMPL_H_
|
||||
#define ASYNCWEBSERVERRESPONSEIMPL_H_
|
||||
|
||||
|
@ -39,16 +22,19 @@ class AsyncBasicResponse : public AsyncWebServerResponse {
|
|||
|
||||
public:
|
||||
explicit AsyncBasicResponse(int code, const char *contentType = asyncsrv::empty, const char *content = asyncsrv::empty);
|
||||
AsyncBasicResponse(int code, const String& contentType, const String& content = emptyString) : AsyncBasicResponse(code, contentType.c_str(), content.c_str()) {}
|
||||
AsyncBasicResponse(int code, const String &contentType, const String &content = emptyString)
|
||||
: AsyncBasicResponse(code, contentType.c_str(), content.c_str()) {}
|
||||
void _respond(AsyncWebServerRequest *request) override final;
|
||||
size_t _ack(AsyncWebServerRequest *request, size_t len, uint32_t time) override final;
|
||||
bool _sourceValid() const override final { return true; }
|
||||
bool _sourceValid() const override final {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
class AsyncAbstractResponse : public AsyncWebServerResponse {
|
||||
private:
|
||||
#if ASYNCWEBSERVER_USE_CHUNK_INFLIGHT
|
||||
// amount of responce data in-flight, i.e. sent, but not acked yet
|
||||
// amount of response data in-flight, i.e. sent, but not acked yet
|
||||
size_t _in_flight{0};
|
||||
// in-flight queue credits
|
||||
size_t _in_flight_credit{2};
|
||||
|
@ -70,8 +56,12 @@ class AsyncAbstractResponse : public AsyncWebServerResponse {
|
|||
virtual ~AsyncAbstractResponse() {}
|
||||
void _respond(AsyncWebServerRequest *request) override final;
|
||||
size_t _ack(AsyncWebServerRequest *request, size_t len, uint32_t time) override final;
|
||||
virtual bool _sourceValid() const { return false; }
|
||||
virtual size_t _fillBuffer(uint8_t* buf __attribute__((unused)), size_t maxLen __attribute__((unused))) { return 0; }
|
||||
virtual bool _sourceValid() const {
|
||||
return false;
|
||||
}
|
||||
virtual size_t _fillBuffer(uint8_t *buf __attribute__((unused)), size_t maxLen __attribute__((unused))) {
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
#ifndef TEMPLATE_PLACEHOLDER
|
||||
|
@ -90,11 +80,19 @@ class AsyncFileResponse : public AsyncAbstractResponse {
|
|||
|
||||
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, bool download = false, AwsTemplateProcessor callback = nullptr) : AsyncFileResponse(fs, path, contentType.c_str(), download, callback) {}
|
||||
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() { _content.close(); }
|
||||
bool _sourceValid() const override final { return !!(_content); }
|
||||
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 char *contentType = asyncsrv::empty, bool download = false, AwsTemplateProcessor callback = nullptr
|
||||
);
|
||||
AsyncFileResponse(File content, const String &path, const String &contentType, bool download = false, AwsTemplateProcessor callback = nullptr)
|
||||
: AsyncFileResponse(content, path, contentType.c_str(), download, callback) {}
|
||||
~AsyncFileResponse() {
|
||||
_content.close();
|
||||
}
|
||||
bool _sourceValid() const override final {
|
||||
return !!(_content);
|
||||
}
|
||||
size_t _fillBuffer(uint8_t *buf, size_t maxLen) override final;
|
||||
};
|
||||
|
||||
|
@ -104,8 +102,11 @@ class AsyncStreamResponse : public AsyncAbstractResponse {
|
|||
|
||||
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, contentType.c_str(), len, callback) {}
|
||||
bool _sourceValid() const override final { return !!(_content); }
|
||||
AsyncStreamResponse(Stream &stream, const String &contentType, size_t len, AwsTemplateProcessor callback = nullptr)
|
||||
: AsyncStreamResponse(stream, contentType.c_str(), len, callback) {}
|
||||
bool _sourceValid() const override final {
|
||||
return !!(_content);
|
||||
}
|
||||
size_t _fillBuffer(uint8_t *buf, size_t maxLen) override final;
|
||||
};
|
||||
|
||||
|
@ -116,8 +117,11 @@ class AsyncCallbackResponse : public AsyncAbstractResponse {
|
|||
|
||||
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(contentType.c_str(), len, callback, templateCallback) {}
|
||||
bool _sourceValid() const override final { return !!(_content); }
|
||||
AsyncCallbackResponse(const String &contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback = nullptr)
|
||||
: AsyncCallbackResponse(contentType.c_str(), len, callback, templateCallback) {}
|
||||
bool _sourceValid() const override final {
|
||||
return !!(_content);
|
||||
}
|
||||
size_t _fillBuffer(uint8_t *buf, size_t maxLen) override final;
|
||||
};
|
||||
|
||||
|
@ -128,8 +132,11 @@ class AsyncChunkedResponse : public AsyncAbstractResponse {
|
|||
|
||||
public:
|
||||
AsyncChunkedResponse(const char *contentType, AwsResponseFiller callback, AwsTemplateProcessor templateCallback = nullptr);
|
||||
AsyncChunkedResponse(const String& contentType, AwsResponseFiller callback, AwsTemplateProcessor templateCallback = nullptr) : AsyncChunkedResponse(contentType.c_str(), callback, templateCallback) {}
|
||||
bool _sourceValid() const override final { return !!(_content); }
|
||||
AsyncChunkedResponse(const String &contentType, AwsResponseFiller callback, AwsTemplateProcessor templateCallback = nullptr)
|
||||
: AsyncChunkedResponse(contentType.c_str(), callback, templateCallback) {}
|
||||
bool _sourceValid() const override final {
|
||||
return !!(_content);
|
||||
}
|
||||
size_t _fillBuffer(uint8_t *buf, size_t maxLen) override final;
|
||||
};
|
||||
|
||||
|
@ -140,8 +147,11 @@ class AsyncProgmemResponse : public AsyncAbstractResponse {
|
|||
|
||||
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(code, contentType.c_str(), content, len, callback) {}
|
||||
bool _sourceValid() const override final { return true; }
|
||||
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 override final {
|
||||
return true;
|
||||
}
|
||||
size_t _fillBuffer(uint8_t *buf, size_t maxLen) override final;
|
||||
};
|
||||
|
||||
|
@ -152,7 +162,9 @@ class AsyncResponseStream : public AsyncAbstractResponse, public Print {
|
|||
public:
|
||||
AsyncResponseStream(const char *contentType, size_t bufferSize);
|
||||
AsyncResponseStream(const String &contentType, size_t bufferSize) : AsyncResponseStream(contentType.c_str(), bufferSize) {}
|
||||
bool _sourceValid() const override final { return (_state < RESPONSE_END); }
|
||||
bool _sourceValid() const override final {
|
||||
return (_state < RESPONSE_END);
|
||||
}
|
||||
size_t _fillBuffer(uint8_t *buf, size_t maxLen) override final;
|
||||
size_t write(const uint8_t *data, size_t len);
|
||||
size_t write(uint8_t data);
|
||||
|
|
|
@ -1,23 +1,6 @@
|
|||
/*
|
||||
Asynchronous WebServer library for Espressif MCUs
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// Copyright 2016-2025 Hristo Gochkov, Mathieu Carbou, Emil Muratov
|
||||
|
||||
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
|
||||
*/
|
||||
#include "ESPAsyncWebServer.h"
|
||||
#include "WebResponseImpl.h"
|
||||
|
||||
|
@ -26,9 +9,11 @@ using namespace asyncsrv;
|
|||
// Since ESP8266 does not link memchr by default, here's its implementation.
|
||||
void *memchr(void *ptr, int ch, size_t count) {
|
||||
unsigned char *p = static_cast<unsigned char *>(ptr);
|
||||
while (count--)
|
||||
if (*p++ == static_cast<unsigned char>(ch))
|
||||
while (count--) {
|
||||
if (*p++ == static_cast<unsigned char>(ch)) {
|
||||
return --p;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
@ -39,118 +24,93 @@ void* memchr(void* ptr, int ch, size_t count) {
|
|||
|
||||
const char *AsyncWebServerResponse::responseCodeToString(int code) {
|
||||
switch (code) {
|
||||
case 100:
|
||||
return T_HTTP_CODE_100;
|
||||
case 101:
|
||||
return T_HTTP_CODE_101;
|
||||
case 200:
|
||||
return T_HTTP_CODE_200;
|
||||
case 201:
|
||||
return T_HTTP_CODE_201;
|
||||
case 202:
|
||||
return T_HTTP_CODE_202;
|
||||
case 203:
|
||||
return T_HTTP_CODE_203;
|
||||
case 204:
|
||||
return T_HTTP_CODE_204;
|
||||
case 205:
|
||||
return T_HTTP_CODE_205;
|
||||
case 206:
|
||||
return T_HTTP_CODE_206;
|
||||
case 300:
|
||||
return T_HTTP_CODE_300;
|
||||
case 301:
|
||||
return T_HTTP_CODE_301;
|
||||
case 302:
|
||||
return T_HTTP_CODE_302;
|
||||
case 303:
|
||||
return T_HTTP_CODE_303;
|
||||
case 304:
|
||||
return T_HTTP_CODE_304;
|
||||
case 305:
|
||||
return T_HTTP_CODE_305;
|
||||
case 307:
|
||||
return T_HTTP_CODE_307;
|
||||
case 400:
|
||||
return T_HTTP_CODE_400;
|
||||
case 401:
|
||||
return T_HTTP_CODE_401;
|
||||
case 402:
|
||||
return T_HTTP_CODE_402;
|
||||
case 403:
|
||||
return T_HTTP_CODE_403;
|
||||
case 404:
|
||||
return T_HTTP_CODE_404;
|
||||
case 405:
|
||||
return T_HTTP_CODE_405;
|
||||
case 406:
|
||||
return T_HTTP_CODE_406;
|
||||
case 407:
|
||||
return T_HTTP_CODE_407;
|
||||
case 408:
|
||||
return T_HTTP_CODE_408;
|
||||
case 409:
|
||||
return T_HTTP_CODE_409;
|
||||
case 410:
|
||||
return T_HTTP_CODE_410;
|
||||
case 411:
|
||||
return T_HTTP_CODE_411;
|
||||
case 412:
|
||||
return T_HTTP_CODE_412;
|
||||
case 413:
|
||||
return T_HTTP_CODE_413;
|
||||
case 414:
|
||||
return T_HTTP_CODE_414;
|
||||
case 415:
|
||||
return T_HTTP_CODE_415;
|
||||
case 416:
|
||||
return T_HTTP_CODE_416;
|
||||
case 417:
|
||||
return T_HTTP_CODE_417;
|
||||
case 429:
|
||||
return T_HTTP_CODE_429;
|
||||
case 500:
|
||||
return T_HTTP_CODE_500;
|
||||
case 501:
|
||||
return T_HTTP_CODE_501;
|
||||
case 502:
|
||||
return T_HTTP_CODE_502;
|
||||
case 503:
|
||||
return T_HTTP_CODE_503;
|
||||
case 504:
|
||||
return T_HTTP_CODE_504;
|
||||
case 505:
|
||||
return T_HTTP_CODE_505;
|
||||
default:
|
||||
return T_HTTP_CODE_ANY;
|
||||
case 100: return T_HTTP_CODE_100;
|
||||
case 101: return T_HTTP_CODE_101;
|
||||
case 200: return T_HTTP_CODE_200;
|
||||
case 201: return T_HTTP_CODE_201;
|
||||
case 202: return T_HTTP_CODE_202;
|
||||
case 203: return T_HTTP_CODE_203;
|
||||
case 204: return T_HTTP_CODE_204;
|
||||
case 205: return T_HTTP_CODE_205;
|
||||
case 206: return T_HTTP_CODE_206;
|
||||
case 300: return T_HTTP_CODE_300;
|
||||
case 301: return T_HTTP_CODE_301;
|
||||
case 302: return T_HTTP_CODE_302;
|
||||
case 303: return T_HTTP_CODE_303;
|
||||
case 304: return T_HTTP_CODE_304;
|
||||
case 305: return T_HTTP_CODE_305;
|
||||
case 307: return T_HTTP_CODE_307;
|
||||
case 400: return T_HTTP_CODE_400;
|
||||
case 401: return T_HTTP_CODE_401;
|
||||
case 402: return T_HTTP_CODE_402;
|
||||
case 403: return T_HTTP_CODE_403;
|
||||
case 404: return T_HTTP_CODE_404;
|
||||
case 405: return T_HTTP_CODE_405;
|
||||
case 406: return T_HTTP_CODE_406;
|
||||
case 407: return T_HTTP_CODE_407;
|
||||
case 408: return T_HTTP_CODE_408;
|
||||
case 409: return T_HTTP_CODE_409;
|
||||
case 410: return T_HTTP_CODE_410;
|
||||
case 411: return T_HTTP_CODE_411;
|
||||
case 412: return T_HTTP_CODE_412;
|
||||
case 413: return T_HTTP_CODE_413;
|
||||
case 414: return T_HTTP_CODE_414;
|
||||
case 415: return T_HTTP_CODE_415;
|
||||
case 416: return T_HTTP_CODE_416;
|
||||
case 417: return T_HTTP_CODE_417;
|
||||
case 429: return T_HTTP_CODE_429;
|
||||
case 500: return T_HTTP_CODE_500;
|
||||
case 501: return T_HTTP_CODE_501;
|
||||
case 502: return T_HTTP_CODE_502;
|
||||
case 503: return T_HTTP_CODE_503;
|
||||
case 504: return T_HTTP_CODE_504;
|
||||
case 505: return T_HTTP_CODE_505;
|
||||
default: return T_HTTP_CODE_ANY;
|
||||
}
|
||||
}
|
||||
|
||||
AsyncWebServerResponse::AsyncWebServerResponse()
|
||||
: _code(0), _contentType(), _contentLength(0), _sendContentLength(true), _chunked(false), _headLength(0), _sentLength(0), _ackedLength(0), _writtenLength(0), _state(RESPONSE_SETUP) {
|
||||
: _code(0), _contentType(), _contentLength(0), _sendContentLength(true), _chunked(false), _headLength(0), _sentLength(0), _ackedLength(0), _writtenLength(0),
|
||||
_state(RESPONSE_SETUP) {
|
||||
for (const auto &header : DefaultHeaders::Instance()) {
|
||||
_headers.emplace_back(header);
|
||||
}
|
||||
}
|
||||
|
||||
void AsyncWebServerResponse::setCode(int code) {
|
||||
if (_state == RESPONSE_SETUP)
|
||||
if (_state == RESPONSE_SETUP) {
|
||||
_code = code;
|
||||
}
|
||||
}
|
||||
|
||||
void AsyncWebServerResponse::setContentLength(size_t len) {
|
||||
if (_state == RESPONSE_SETUP && addHeader(T_Content_Length, len, true))
|
||||
if (_state == RESPONSE_SETUP && addHeader(T_Content_Length, len, true)) {
|
||||
_contentLength = len;
|
||||
}
|
||||
}
|
||||
|
||||
void AsyncWebServerResponse::setContentType(const char *type) {
|
||||
if (_state == RESPONSE_SETUP && addHeader(T_Content_Type, type, true))
|
||||
if (_state == RESPONSE_SETUP && addHeader(T_Content_Type, type, true)) {
|
||||
_contentType = type;
|
||||
}
|
||||
}
|
||||
|
||||
bool AsyncWebServerResponse::removeHeader(const char *name) {
|
||||
for (auto i = _headers.begin(); i != _headers.end(); ++i) {
|
||||
bool h_erased = false;
|
||||
for (auto i = _headers.begin(); i != _headers.end();) {
|
||||
if (i->name().equalsIgnoreCase(name)) {
|
||||
_headers.erase(i);
|
||||
h_erased = true;
|
||||
} else {
|
||||
++i;
|
||||
}
|
||||
}
|
||||
return h_erased;
|
||||
}
|
||||
|
||||
bool AsyncWebServerResponse::removeHeader(const char *name, const char *value) {
|
||||
for (auto i = _headers.begin(); i != _headers.end(); ++i) {
|
||||
if (i->name().equalsIgnoreCase(name) && i->value().equalsIgnoreCase(value)) {
|
||||
_headers.erase(i);
|
||||
return true;
|
||||
}
|
||||
|
@ -159,10 +119,21 @@ bool AsyncWebServerResponse::removeHeader(const char* name) {
|
|||
}
|
||||
|
||||
const AsyncWebHeader *AsyncWebServerResponse::getHeader(const char *name) const {
|
||||
auto iter = std::find_if(std::begin(_headers), std::end(_headers), [&name](const AsyncWebHeader& header) { return header.name().equalsIgnoreCase(name); });
|
||||
auto iter = std::find_if(std::begin(_headers), std::end(_headers), [&name](const AsyncWebHeader &header) {
|
||||
return header.name().equalsIgnoreCase(name);
|
||||
});
|
||||
return (iter == std::end(_headers)) ? nullptr : &(*iter);
|
||||
}
|
||||
|
||||
bool AsyncWebServerResponse::headerMustBePresentOnce(const String &name) {
|
||||
for (uint8_t i = 0; i < T_only_once_headers_len; i++) {
|
||||
if (name.equalsIgnoreCase(T_only_once_headers[i])) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AsyncWebServerResponse::addHeader(const char *name, const char *value, bool replaceExisting) {
|
||||
for (auto i = _headers.begin(); i != _headers.end(); ++i) {
|
||||
if (i->name().equalsIgnoreCase(name)) {
|
||||
|
@ -171,9 +142,11 @@ bool AsyncWebServerResponse::addHeader(const char* name, const char* value, bool
|
|||
// remove, break and add the new one
|
||||
_headers.erase(i);
|
||||
break;
|
||||
} else {
|
||||
} else if (headerMustBePresentOnce(i->name())) { // we can have only one header with that name
|
||||
// do not update
|
||||
return false;
|
||||
} else {
|
||||
break; // accept multiple headers with the same name
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -185,21 +158,25 @@ bool AsyncWebServerResponse::addHeader(const char* name, const char* value, bool
|
|||
void AsyncWebServerResponse::_assembleHead(String &buffer, uint8_t version) {
|
||||
if (version) {
|
||||
addHeader(T_Accept_Ranges, T_none, false);
|
||||
if (_chunked)
|
||||
if (_chunked) {
|
||||
addHeader(T_Transfer_Encoding, T_chunked, false);
|
||||
}
|
||||
}
|
||||
|
||||
if (_sendContentLength)
|
||||
if (_sendContentLength) {
|
||||
addHeader(T_Content_Length, String(_contentLength), false);
|
||||
}
|
||||
|
||||
if (_contentType.length())
|
||||
if (_contentType.length()) {
|
||||
addHeader(T_Content_Type, _contentType.c_str(), false);
|
||||
}
|
||||
|
||||
// precompute buffer size to avoid reallocations by String class
|
||||
size_t len = 0;
|
||||
len += 50; // HTTP/1.1 200 <reason>\r\n
|
||||
for (const auto& header : _headers)
|
||||
for (const auto &header : _headers) {
|
||||
len += header.name().length() + header.value().length() + 4;
|
||||
}
|
||||
|
||||
// prepare buffer
|
||||
buffer.reserve(len);
|
||||
|
@ -233,10 +210,18 @@ void AsyncWebServerResponse::_assembleHead(String& buffer, uint8_t version) {
|
|||
_headLength = buffer.length();
|
||||
}
|
||||
|
||||
bool AsyncWebServerResponse::_started() const { return _state > RESPONSE_SETUP; }
|
||||
bool AsyncWebServerResponse::_finished() const { return _state > RESPONSE_WAIT_ACK; }
|
||||
bool AsyncWebServerResponse::_failed() const { return _state == RESPONSE_FAILED; }
|
||||
bool AsyncWebServerResponse::_sourceValid() const { return false; }
|
||||
bool AsyncWebServerResponse::_started() const {
|
||||
return _state > RESPONSE_SETUP;
|
||||
}
|
||||
bool AsyncWebServerResponse::_finished() const {
|
||||
return _state > RESPONSE_WAIT_ACK;
|
||||
}
|
||||
bool AsyncWebServerResponse::_failed() const {
|
||||
return _state == RESPONSE_FAILED;
|
||||
}
|
||||
bool AsyncWebServerResponse::_sourceValid() const {
|
||||
return false;
|
||||
}
|
||||
void AsyncWebServerResponse::_respond(AsyncWebServerRequest *request) {
|
||||
_state = RESPONSE_END;
|
||||
request->client()->close();
|
||||
|
@ -257,9 +242,10 @@ AsyncBasicResponse::AsyncBasicResponse(int code, const char* contentType, const
|
|||
_contentType = contentType;
|
||||
if (_content.length()) {
|
||||
_contentLength = _content.length();
|
||||
if (!_contentType.length())
|
||||
if (!_contentType.length()) {
|
||||
_contentType = T_text_plain;
|
||||
}
|
||||
}
|
||||
addHeader(T_Connection, T_close, false);
|
||||
}
|
||||
|
||||
|
@ -355,8 +341,9 @@ size_t AsyncAbstractResponse::_ack(AsyncWebServerRequest* request, size_t len, u
|
|||
|
||||
#if ASYNCWEBSERVER_USE_CHUNK_INFLIGHT
|
||||
// return a credit for each chunk of acked data (polls does not give any credits)
|
||||
if (len)
|
||||
if (len) {
|
||||
++_in_flight_credit;
|
||||
}
|
||||
|
||||
// for chunked responses ignore acks if there are no _in_flight_credits left
|
||||
if (_chunked && !_in_flight_credit) {
|
||||
|
@ -399,8 +386,9 @@ size_t AsyncAbstractResponse::_ack(AsyncWebServerRequest* request, size_t len, u
|
|||
if (_in_flight > space) {
|
||||
// log_d("defer user call %u/%u", _in_flight, space);
|
||||
// take the credit back since we are ignoring this ack and rely on other inflight data
|
||||
if (len)
|
||||
if (len) {
|
||||
--_in_flight_credit;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
@ -420,7 +408,10 @@ size_t AsyncAbstractResponse::_ack(AsyncWebServerRequest* request, size_t len, u
|
|||
|
||||
uint8_t *buf = (uint8_t *)malloc(outLen + headLen);
|
||||
if (!buf) {
|
||||
// os_printf("_ack malloc %d failed\n", outLen+headLen);
|
||||
#ifdef ESP32
|
||||
log_e("Failed to allocate");
|
||||
#endif
|
||||
request->abort();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -481,10 +472,11 @@ size_t AsyncAbstractResponse::_ack(AsyncWebServerRequest* request, size_t len, u
|
|||
} else if (_state == RESPONSE_WAIT_ACK) {
|
||||
if (!_sendContentLength || _ackedLength >= _writtenLength) {
|
||||
_state = RESPONSE_END;
|
||||
if (!_chunked && !_sendContentLength)
|
||||
if (!_chunked && !_sendContentLength) {
|
||||
request->client()->close(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -502,16 +494,19 @@ size_t AsyncAbstractResponse::_readDataFromCacheOrContent(uint8_t* data, const s
|
|||
}
|
||||
|
||||
size_t AsyncAbstractResponse::_fillBufferAndProcessTemplates(uint8_t *data, size_t len) {
|
||||
if (!_callback)
|
||||
if (!_callback) {
|
||||
return _fillBuffer(data, len);
|
||||
}
|
||||
|
||||
const size_t originalLen = len;
|
||||
len = _readDataFromCacheOrContent(data, len);
|
||||
// Now we've read 'len' bytes, either from cache or from file
|
||||
// Search for template placeholders
|
||||
uint8_t *pTemplateStart = data;
|
||||
while ((pTemplateStart < &data[len]) && (pTemplateStart = (uint8_t*)memchr(pTemplateStart, TEMPLATE_PLACEHOLDER, &data[len - 1] - pTemplateStart + 1))) { // data[0] ... data[len - 1]
|
||||
uint8_t* pTemplateEnd = (pTemplateStart < &data[len - 1]) ? (uint8_t*)memchr(pTemplateStart + 1, TEMPLATE_PLACEHOLDER, &data[len - 1] - pTemplateStart) : nullptr;
|
||||
while ((pTemplateStart < &data[len]) && (pTemplateStart = (uint8_t *)memchr(pTemplateStart, TEMPLATE_PLACEHOLDER, &data[len - 1] - pTemplateStart + 1))
|
||||
) { // data[0] ... data[len - 1]
|
||||
uint8_t *pTemplateEnd =
|
||||
(pTemplateStart < &data[len - 1]) ? (uint8_t *)memchr(pTemplateStart + 1, TEMPLATE_PLACEHOLDER, &data[len - 1] - pTemplateStart) : nullptr;
|
||||
// temporary buffer to hold parameter name
|
||||
uint8_t buf[TEMPLATE_PARAM_NAME_LENGTH + 1];
|
||||
String paramName;
|
||||
|
@ -529,9 +524,11 @@ size_t AsyncAbstractResponse::_fillBufferAndProcessTemplates(uint8_t* data, size
|
|||
len += _readDataFromCacheOrContent(&data[len - 1], 1) - 1;
|
||||
++pTemplateStart;
|
||||
}
|
||||
} else if (&data[len - 1] - pTemplateStart + 1 < TEMPLATE_PARAM_NAME_LENGTH + 2) { // closing placeholder not found, check if it's in the remaining file data
|
||||
} else if (&data[len - 1] - pTemplateStart + 1
|
||||
< TEMPLATE_PARAM_NAME_LENGTH + 2) { // closing placeholder not found, check if it's in the remaining file data
|
||||
memcpy(buf, pTemplateStart + 1, &data[len - 1] - pTemplateStart);
|
||||
const size_t readFromCacheOrContent = _readDataFromCacheOrContent(buf + (&data[len - 1] - pTemplateStart), TEMPLATE_PARAM_NAME_LENGTH + 2 - (&data[len - 1] - pTemplateStart + 1));
|
||||
const size_t readFromCacheOrContent =
|
||||
_readDataFromCacheOrContent(buf + (&data[len - 1] - pTemplateStart), TEMPLATE_PARAM_NAME_LENGTH + 2 - (&data[len - 1] - pTemplateStart + 1));
|
||||
if (readFromCacheOrContent) {
|
||||
pTemplateEnd = (uint8_t *)memchr(buf + (&data[len - 1] - pTemplateStart), TEMPLATE_PLACEHOLDER, readFromCacheOrContent);
|
||||
if (pTemplateEnd) {
|
||||
|
@ -547,10 +544,12 @@ size_t AsyncAbstractResponse::_fillBufferAndProcessTemplates(uint8_t* data, size
|
|||
_cache.insert(_cache.begin(), buf + (&data[len - 1] - pTemplateStart), buf + (&data[len - 1] - pTemplateStart) + readFromCacheOrContent);
|
||||
++pTemplateStart;
|
||||
}
|
||||
} else // closing placeholder not found in content data, store found percent symbol as is and advance to the next position
|
||||
} else { // closing placeholder not found in content data, store found percent symbol as is and advance to the next position
|
||||
++pTemplateStart;
|
||||
} else // closing placeholder not found in content data, store found percent symbol as is and advance to the next position
|
||||
}
|
||||
} else { // closing placeholder not found in content data, store found percent symbol as is and advance to the next position
|
||||
++pTemplateStart;
|
||||
}
|
||||
if (paramName.length()) {
|
||||
// call callback and replace with result.
|
||||
// Everything in range [pTemplateStart, pTemplateEnd] can be safely replaced with parameter value.
|
||||
|
@ -568,10 +567,11 @@ size_t AsyncAbstractResponse::_fillBufferAndProcessTemplates(uint8_t* data, size
|
|||
// 2. parameter value is longer than placeholder text, push the data after placeholder which not saved into cache further to the end
|
||||
memmove(pTemplateStart + numBytesCopied, pTemplateEnd + 1, &data[originalLen] - pTemplateStart - numBytesCopied);
|
||||
len = originalLen; // fix issue with truncated data, not sure if it has any side effects
|
||||
} else if (pTemplateEnd + 1 != pTemplateStart + numBytesCopied)
|
||||
} else if (pTemplateEnd + 1 != pTemplateStart + numBytesCopied) {
|
||||
// 2. Either parameter value is shorter than placeholder text OR there is enough free space in buffer to fit.
|
||||
// Move the entire data after the placeholder
|
||||
memmove(pTemplateStart + numBytesCopied, pTemplateEnd + 1, &data[len] - pTemplateEnd - 1);
|
||||
}
|
||||
// 3. replace placeholder with actual value
|
||||
memcpy(pTemplateStart, pvstr, numBytesCopied);
|
||||
// If result is longer than buffer, copy the remainder into cache (this could happen only if placeholder text itself did not fit entirely in buffer)
|
||||
|
@ -604,48 +604,50 @@ void AsyncFileResponse::_setContentTypeFromPath(const String& path) {
|
|||
#endif
|
||||
_contentType = getContentType(path);
|
||||
#else
|
||||
if (path.endsWith(T__html))
|
||||
if (path.endsWith(T__html)) {
|
||||
_contentType = T_text_html;
|
||||
else if (path.endsWith(T__htm))
|
||||
} else if (path.endsWith(T__htm)) {
|
||||
_contentType = T_text_html;
|
||||
else if (path.endsWith(T__css))
|
||||
} else if (path.endsWith(T__css)) {
|
||||
_contentType = T_text_css;
|
||||
else if (path.endsWith(T__json))
|
||||
} else if (path.endsWith(T__json)) {
|
||||
_contentType = T_application_json;
|
||||
else if (path.endsWith(T__js))
|
||||
} else if (path.endsWith(T__js)) {
|
||||
_contentType = T_application_javascript;
|
||||
else if (path.endsWith(T__png))
|
||||
} else if (path.endsWith(T__png)) {
|
||||
_contentType = T_image_png;
|
||||
else if (path.endsWith(T__gif))
|
||||
} else if (path.endsWith(T__gif)) {
|
||||
_contentType = T_image_gif;
|
||||
else if (path.endsWith(T__jpg))
|
||||
} else if (path.endsWith(T__jpg)) {
|
||||
_contentType = T_image_jpeg;
|
||||
else if (path.endsWith(T__ico))
|
||||
} else if (path.endsWith(T__ico)) {
|
||||
_contentType = T_image_x_icon;
|
||||
else if (path.endsWith(T__svg))
|
||||
} else if (path.endsWith(T__svg)) {
|
||||
_contentType = T_image_svg_xml;
|
||||
else if (path.endsWith(T__eot))
|
||||
} else if (path.endsWith(T__eot)) {
|
||||
_contentType = T_font_eot;
|
||||
else if (path.endsWith(T__woff))
|
||||
} else if (path.endsWith(T__woff)) {
|
||||
_contentType = T_font_woff;
|
||||
else if (path.endsWith(T__woff2))
|
||||
} else if (path.endsWith(T__woff2)) {
|
||||
_contentType = T_font_woff2;
|
||||
else if (path.endsWith(T__ttf))
|
||||
} else if (path.endsWith(T__ttf)) {
|
||||
_contentType = T_font_ttf;
|
||||
else if (path.endsWith(T__xml))
|
||||
} else if (path.endsWith(T__xml)) {
|
||||
_contentType = T_text_xml;
|
||||
else if (path.endsWith(T__pdf))
|
||||
} else if (path.endsWith(T__pdf)) {
|
||||
_contentType = T_application_pdf;
|
||||
else if (path.endsWith(T__zip))
|
||||
} else if (path.endsWith(T__zip)) {
|
||||
_contentType = T_application_zip;
|
||||
else if (path.endsWith(T__gz))
|
||||
} else if (path.endsWith(T__gz)) {
|
||||
_contentType = T_application_x_gzip;
|
||||
else
|
||||
} else {
|
||||
_contentType = T_text_plain;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
AsyncFileResponse::AsyncFileResponse(FS& fs, const String& path, const char* contentType, bool download, AwsTemplateProcessor callback) : AsyncAbstractResponse(callback) {
|
||||
AsyncFileResponse::AsyncFileResponse(FS &fs, const String &path, const char *contentType, bool download, AwsTemplateProcessor callback)
|
||||
: AsyncAbstractResponse(callback) {
|
||||
_code = 200;
|
||||
_path = path;
|
||||
|
||||
|
@ -660,10 +662,11 @@ AsyncFileResponse::AsyncFileResponse(FS& fs, const String& path, const char* con
|
|||
_content = fs.open(_path, fs::FileOpenMode::read);
|
||||
_contentLength = _content.size();
|
||||
|
||||
if (strlen(contentType) == 0)
|
||||
if (strlen(contentType) == 0) {
|
||||
_setContentTypeFromPath(path);
|
||||
else
|
||||
} else {
|
||||
_contentType = contentType;
|
||||
}
|
||||
|
||||
int filenameStart = path.lastIndexOf('/') + 1;
|
||||
char buf[26 + path.length() - filenameStart];
|
||||
|
@ -679,7 +682,8 @@ AsyncFileResponse::AsyncFileResponse(FS& fs, const String& path, const char* con
|
|||
addHeader(T_Content_Disposition, buf, false);
|
||||
}
|
||||
|
||||
AsyncFileResponse::AsyncFileResponse(File content, const String& path, const char* contentType, bool download, AwsTemplateProcessor callback) : AsyncAbstractResponse(callback) {
|
||||
AsyncFileResponse::AsyncFileResponse(File content, const String &path, const char *contentType, bool download, AwsTemplateProcessor callback)
|
||||
: AsyncAbstractResponse(callback) {
|
||||
_code = 200;
|
||||
_path = path;
|
||||
|
||||
|
@ -693,10 +697,11 @@ AsyncFileResponse::AsyncFileResponse(File content, const String& path, const cha
|
|||
_content = content;
|
||||
_contentLength = _content.size();
|
||||
|
||||
if (strlen(contentType) == 0)
|
||||
if (strlen(contentType) == 0) {
|
||||
_setContentTypeFromPath(path);
|
||||
else
|
||||
} else {
|
||||
_contentType = contentType;
|
||||
}
|
||||
|
||||
int filenameStart = path.lastIndexOf('/') + 1;
|
||||
char buf[26 + path.length() - filenameStart];
|
||||
|
@ -729,8 +734,9 @@ size_t AsyncStreamResponse::_fillBuffer(uint8_t* data, size_t len) {
|
|||
size_t available = _content->available();
|
||||
size_t outLen = (available > len) ? len : available;
|
||||
size_t i;
|
||||
for (i = 0; i < outLen; i++)
|
||||
for (i = 0; i < outLen; i++) {
|
||||
data[i] = _content->read();
|
||||
}
|
||||
return outLen;
|
||||
}
|
||||
|
||||
|
@ -738,12 +744,14 @@ size_t AsyncStreamResponse::_fillBuffer(uint8_t* data, size_t len) {
|
|||
* Callback Response
|
||||
* */
|
||||
|
||||
AsyncCallbackResponse::AsyncCallbackResponse(const char* contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback) : AsyncAbstractResponse(templateCallback) {
|
||||
AsyncCallbackResponse::AsyncCallbackResponse(const char *contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback)
|
||||
: AsyncAbstractResponse(templateCallback) {
|
||||
_code = 200;
|
||||
_content = callback;
|
||||
_contentLength = len;
|
||||
if (!len)
|
||||
if (!len) {
|
||||
_sendContentLength = false;
|
||||
}
|
||||
_contentType = contentType;
|
||||
_filledLength = 0;
|
||||
}
|
||||
|
@ -760,7 +768,8 @@ size_t AsyncCallbackResponse::_fillBuffer(uint8_t* data, size_t len) {
|
|||
* Chunked Response
|
||||
* */
|
||||
|
||||
AsyncChunkedResponse::AsyncChunkedResponse(const char* contentType, AwsResponseFiller callback, AwsTemplateProcessor processorCallback) : AsyncAbstractResponse(processorCallback) {
|
||||
AsyncChunkedResponse::AsyncChunkedResponse(const char *contentType, AwsResponseFiller callback, AwsTemplateProcessor processorCallback)
|
||||
: AsyncAbstractResponse(processorCallback) {
|
||||
_code = 200;
|
||||
_content = callback;
|
||||
_contentLength = 0;
|
||||
|
@ -782,7 +791,8 @@ size_t AsyncChunkedResponse::_fillBuffer(uint8_t* data, size_t len) {
|
|||
* Progmem Response
|
||||
* */
|
||||
|
||||
AsyncProgmemResponse::AsyncProgmemResponse(int code, const char* contentType, const uint8_t* content, size_t len, AwsTemplateProcessor callback) : AsyncAbstractResponse(callback) {
|
||||
AsyncProgmemResponse::AsyncProgmemResponse(int code, const char *contentType, const uint8_t *content, size_t len, AwsTemplateProcessor callback)
|
||||
: AsyncAbstractResponse(callback) {
|
||||
_code = code;
|
||||
_content = content;
|
||||
_contentType = contentType;
|
||||
|
@ -810,7 +820,11 @@ AsyncResponseStream::AsyncResponseStream(const char* contentType, size_t bufferS
|
|||
_code = 200;
|
||||
_contentLength = 0;
|
||||
_contentType = contentType;
|
||||
_content.reserve(bufferSize);
|
||||
if (!_content.reserve(bufferSize)) {
|
||||
#ifdef ESP32
|
||||
log_e("Failed to allocate");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
size_t AsyncResponseStream::_fillBuffer(uint8_t *buf, size_t maxLen) {
|
||||
|
@ -818,8 +832,9 @@ size_t AsyncResponseStream::_fillBuffer(uint8_t* buf, size_t maxLen) {
|
|||
}
|
||||
|
||||
size_t AsyncResponseStream::write(const uint8_t *data, size_t len) {
|
||||
if (_started())
|
||||
if (_started()) {
|
||||
return 0;
|
||||
}
|
||||
size_t written = _content.write(data, len);
|
||||
_contentLength += written;
|
||||
return written;
|
||||
|
|
|
@ -1,23 +1,6 @@
|
|||
/*
|
||||
Asynchronous WebServer library for Espressif MCUs
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// Copyright 2016-2025 Hristo Gochkov, Mathieu Carbou, Emil Muratov
|
||||
|
||||
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
|
||||
*/
|
||||
#include "ESPAsyncWebServer.h"
|
||||
#include "WebHandlerImpl.h"
|
||||
|
||||
|
@ -45,14 +28,13 @@ const char* fs::FileOpenMode::write = "w";
|
|||
const char *fs::FileOpenMode::append = "a";
|
||||
#endif
|
||||
|
||||
AsyncWebServer::AsyncWebServer(uint16_t port)
|
||||
: _server(port) {
|
||||
AsyncWebServer::AsyncWebServer(uint16_t port) : _server(port) {
|
||||
_catchAllHandler = new AsyncCallbackWebHandler();
|
||||
if (_catchAllHandler == NULL)
|
||||
return;
|
||||
_server.onClient([](void* s, AsyncClient* c) {
|
||||
if (c == NULL)
|
||||
_server.onClient(
|
||||
[](void *s, AsyncClient *c) {
|
||||
if (c == NULL) {
|
||||
return;
|
||||
}
|
||||
c->setRxTimeout(3);
|
||||
AsyncWebServerRequest *r = new AsyncWebServerRequest((AsyncWebServer *)s, c);
|
||||
if (r == NULL) {
|
||||
|
@ -60,14 +42,15 @@ AsyncWebServer::AsyncWebServer(uint16_t port)
|
|||
delete c;
|
||||
}
|
||||
},
|
||||
this);
|
||||
this
|
||||
);
|
||||
}
|
||||
|
||||
AsyncWebServer::~AsyncWebServer() {
|
||||
reset();
|
||||
end();
|
||||
if (_catchAllHandler)
|
||||
delete _catchAllHandler;
|
||||
_catchAllHandler = nullptr; // Prevent potential use-after-free
|
||||
}
|
||||
|
||||
AsyncWebRewrite &AsyncWebServer::addRewrite(std::shared_ptr<AsyncWebRewrite> rewrite) {
|
||||
|
@ -138,6 +121,8 @@ void AsyncWebServer::_handleDisconnect(AsyncWebServerRequest* request) {
|
|||
}
|
||||
|
||||
void AsyncWebServer::_rewriteRequest(AsyncWebServerRequest *request) {
|
||||
// the last rewrite that matches the request will be used
|
||||
// we do not break the loop to allow for multiple rewrites to be applied and only the last one to be used (allows overriding)
|
||||
for (const auto &r : _rewrites) {
|
||||
if (r->match(request)) {
|
||||
request->_url = r->toUrl();
|
||||
|
@ -157,7 +142,9 @@ void AsyncWebServer::_attachHandler(AsyncWebServerRequest* request) {
|
|||
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();
|
||||
handler->setUri(uri);
|
||||
handler->setMethod(method);
|
||||
|
@ -186,13 +173,15 @@ void AsyncWebServer::onRequestBody(ArBodyHandlerFunction fn) {
|
|||
_catchAllHandler->onBody(fn);
|
||||
}
|
||||
|
||||
AsyncWebHandler &AsyncWebServer::catchAllHandler() const {
|
||||
return *_catchAllHandler;
|
||||
}
|
||||
|
||||
void AsyncWebServer::reset() {
|
||||
_rewrites.clear();
|
||||
_handlers.clear();
|
||||
|
||||
if (_catchAllHandler != NULL) {
|
||||
_catchAllHandler->onRequest(NULL);
|
||||
_catchAllHandler->onUpload(NULL);
|
||||
_catchAllHandler->onBody(NULL);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// Copyright 2016-2025 Hristo Gochkov, Mathieu Carbou, Emil Muratov
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace asyncsrv {
|
||||
|
@ -25,6 +28,7 @@ namespace asyncsrv {
|
|||
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_Content_Location = "content-location";
|
||||
static constexpr const char *T_Cookie = "cookie";
|
||||
static constexpr const char *T_CORS_ACAC = "access-control-allow-credentials";
|
||||
static constexpr const char *T_CORS_ACAH = "access-control-allow-headers";
|
||||
|
@ -33,6 +37,7 @@ namespace asyncsrv {
|
|||
static constexpr const char *T_CORS_ACMA = "access-control-max-age";
|
||||
static constexpr const char *T_CORS_O = "origin";
|
||||
static constexpr const char *T_data_ = "data: ";
|
||||
static constexpr const char *T_Date = "date";
|
||||
static constexpr const char *T_DIGEST = "digest";
|
||||
static constexpr const char *T_DIGEST_ = "digest ";
|
||||
static constexpr const char *T_ETag = "etag";
|
||||
|
@ -68,6 +73,7 @@ namespace asyncsrv {
|
|||
static constexpr const char *T_nn = "\n\n";
|
||||
static constexpr const char *T_rn = "\r\n";
|
||||
static constexpr const char *T_rnrn = "\r\n\r\n";
|
||||
static constexpr const char *T_Server = "server";
|
||||
static constexpr const char *T_Transfer_Encoding = "transfer-encoding";
|
||||
static constexpr const char *T_TRUE = "true";
|
||||
static constexpr const char *T_UPGRADE = "upgrade";
|
||||
|
@ -96,7 +102,7 @@ namespace asyncsrv {
|
|||
static constexpr const char *T_RCT_EVENT = "RCT_EVENT";
|
||||
static constexpr const char *T_ERROR = "ERROR";
|
||||
|
||||
// extentions & MIME-Types
|
||||
// extensions & MIME-Types
|
||||
static constexpr const char *T__css = ".css";
|
||||
static constexpr const char *T__eot = ".eot";
|
||||
static constexpr const char *T__gif = ".gif";
|
||||
|
@ -136,7 +142,7 @@ namespace asyncsrv {
|
|||
static constexpr const char *T_text_plain = "text/plain";
|
||||
static constexpr const char *T_text_xml = "text/xml";
|
||||
|
||||
// Responce codes
|
||||
// Response 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";
|
||||
|
@ -180,4 +186,8 @@ namespace asyncsrv {
|
|||
static constexpr const char *T_HTTP_CODE_505 = "HTTP Version not supported";
|
||||
static constexpr const char *T_HTTP_CODE_ANY = "Unknown code";
|
||||
|
||||
} // namespace asyncsrv {}
|
||||
static constexpr const uint8_t T_only_once_headers_len = 11;
|
||||
static constexpr const char *T_only_once_headers[] = {T_Content_Length, T_Content_Type, T_Date, T_ETag, T_Last_Modified, T_LOCATION, T_retry_after,
|
||||
T_Transfer_Encoding, T_Content_Location, T_Server, T_WWW_AUTH};
|
||||
|
||||
} // namespace asyncsrv
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue