Update ESP32AsyncWebServer to v3.8.0

This commit is contained in:
Daniel Öster 2025-08-13 18:04:18 +03:00
parent 9ce4f98e19
commit 146420b91b
10 changed files with 172 additions and 160 deletions

View file

@ -1,6 +1,6 @@
{
"name": "ESPAsyncWebServer",
"version": "3.7.10",
"version": "3.8.0",
"description": "Asynchronous HTTP and WebSocket Server Library for ESP32, ESP8266 and RP2040. Supports: WebSocket, SSE, Authentication, Arduino Json 7, File Upload, Static File serving, URL Rewrite, URL Redirect, etc.",
"keywords": "http,async,websocket,webserver",
"homepage": "https://github.com/ESP32Async/ESPAsyncWebServer",
@ -25,7 +25,7 @@
{
"owner": "ESP32Async",
"name": "AsyncTCP",
"version": "^3.4.5",
"version": "^3.4.7",
"platforms": [
"espressif32",
"libretiny"

View file

@ -1,6 +1,6 @@
name=ESP Async WebServer
includes=ESPAsyncWebServer.h
version=3.7.10
version=3.8.0
author=ESP32Async
maintainer=ESP32Async
sentence=Asynchronous HTTP and WebSocket Server Library for ESP32, ESP8266 and RP2040

View file

@ -15,14 +15,14 @@ const AsyncWebHeader AsyncWebHeader::parse(const char *data) {
if (strchr(data, '\n') || strchr(data, '\r')) {
return AsyncWebHeader(); // Invalid header format
}
char *colon = strchr(data, ':');
const char *colon = strchr(data, ':');
if (!colon) {
return AsyncWebHeader(); // separator not found
}
if (colon == data) {
return AsyncWebHeader(); // Header name cannot be empty
}
char *startOfValue = colon + 1; // Skip the colon
const char *startOfValue = colon + 1; // Skip the colon
// skip one optional whitespace after the colon
if (*startOfValue == ' ') {
startOfValue++;

View file

@ -27,7 +27,7 @@ void AsyncWebServerRequest::send(FS &fs, const String &path, const char *content
// Handle compressed version
const String gzPath = path + asyncsrv::T__gz;
File gzFile = fs.open(gzPath, "r");
File gzFile = fs.open(gzPath, fs::FileOpenMode::read);
// Compressed file not found or invalid
if (!gzFile.seek(gzFile.size() - 8)) {

View file

@ -10,9 +10,9 @@ extern "C" {
/** Major version number (X.x.x) */
#define ASYNCWEBSERVER_VERSION_MAJOR 3
/** Minor version number (x.X.x) */
#define ASYNCWEBSERVER_VERSION_MINOR 7
#define ASYNCWEBSERVER_VERSION_MINOR 8
/** Patch version number (x.x.X) */
#define ASYNCWEBSERVER_VERSION_PATCH 10
#define ASYNCWEBSERVER_VERSION_PATCH 0
/**
* Macro to convert version number into an integer

View file

@ -120,6 +120,11 @@ size_t webSocketSendFrame(AsyncClient *client, bool final, uint8_t opcode, bool
return len;
}
size_t AsyncWebSocketControl::send(AsyncClient *client) {
_finished = true;
return webSocketSendFrame(client, true, _opcode & 0x0F, _mask, _data, _len);
}
/*
* AsyncWebSocketMessageBuffer
*/
@ -146,65 +151,6 @@ bool AsyncWebSocketMessageBuffer::reserve(size_t size) {
return _buffer->capacity() >= size;
}
/*
* Control Frame
*/
class AsyncWebSocketControl {
private:
uint8_t _opcode;
uint8_t *_data;
size_t _len;
bool _mask;
bool _finished;
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) {
_len = 0;
}
if (_len) {
if (_len > 125) {
_len = 125;
}
_data = (uint8_t *)malloc(_len);
if (_data == NULL) {
#ifdef ESP32
log_e("Failed to allocate");
#endif
_len = 0;
} else {
memcpy(_data, data, len);
}
} else {
_data = NULL;
}
}
~AsyncWebSocketControl() {
if (_data != NULL) {
free(_data);
}
}
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);
}
};
/*
* AsyncWebSocketMessage Message
*/

View file

@ -53,7 +53,62 @@ using AsyncWebSocketSharedBuffer = std::shared_ptr<std::vector<uint8_t>>;
class AsyncWebSocket;
class AsyncWebSocketResponse;
class AsyncWebSocketClient;
class AsyncWebSocketControl;
/*
* Control Frame
*/
class AsyncWebSocketControl {
private:
uint8_t _opcode;
uint8_t *_data;
size_t _len;
bool _mask;
bool _finished;
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) {
_len = 0;
}
if (_len) {
if (_len > 125) {
_len = 125;
}
_data = (uint8_t *)malloc(_len);
if (_data == NULL) {
#ifdef ESP32
log_e("Failed to allocate");
#endif
_len = 0;
} else {
memcpy(_data, data, len);
}
} else {
_data = NULL;
}
}
~AsyncWebSocketControl() {
if (_data != NULL) {
free(_data);
}
}
bool finished() const {
return _finished;
}
uint8_t opcode() {
return _opcode;
}
uint8_t len() {
return _len + 2;
}
size_t send(AsyncClient *client);
};
typedef struct {
/** Message type as defined by enum AwsFrameType.

View file

@ -75,7 +75,6 @@ class AsyncFileResponse : public AsyncAbstractResponse {
private:
File _content;
String _path;
void _setContentTypeFromPath(const String &path);
public:

View file

@ -6,17 +6,6 @@
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)) {
return --p;
}
}
return nullptr;
}
/*
* Abstract Response
*
@ -642,7 +631,7 @@ void AsyncFileResponse::_setContentTypeFromPath(const String &path) {
const char *dot = strrchr(cpath, '.');
if (!dot) {
_contentType = T_text_plain;
_contentType = T_application_octet_stream;
return;
}
@ -662,6 +651,10 @@ void AsyncFileResponse::_setContentTypeFromPath(const String &path) {
_contentType = T_image_svg_xml;
} else if (strcmp(dot, T__jpg) == 0) {
_contentType = T_image_jpeg;
} else if (strcmp(dot, T__webp) == 0) {
_contentType = T_image_webp;
} else if (strcmp(dot, T__avif) == 0) {
_contentType = T_image_avif;
} else if (strcmp(dot, T__gif) == 0) {
_contentType = T_image_gif;
} else if (strcmp(dot, T__woff2) == 0) {
@ -670,18 +663,20 @@ void AsyncFileResponse::_setContentTypeFromPath(const String &path) {
_contentType = T_font_woff;
} else if (strcmp(dot, T__ttf) == 0) {
_contentType = T_font_ttf;
} else if (strcmp(dot, T__eot) == 0) {
_contentType = T_font_eot;
} else if (strcmp(dot, T__xml) == 0) {
_contentType = T_text_xml;
} else if (strcmp(dot, T__pdf) == 0) {
_contentType = T_application_pdf;
} else if (strcmp(dot, T__zip) == 0) {
_contentType = T_application_zip;
} else if (strcmp(dot, T__gz) == 0) {
_contentType = T_application_x_gzip;
} else {
} else if (strcmp(dot, T__mp4) == 0) {
_contentType = T_video_mp4;
} else if (strcmp(dot, T__opus) == 0) {
_contentType = T_audio_opus;
} else if (strcmp(dot, T__webm) == 0) {
_contentType = T_video_webm;
} else if (strcmp(dot, T__txt) == 0) {
_contentType = T_text_plain;
} else {
_contentType = T_application_octet_stream;
}
#endif
}
@ -701,15 +696,19 @@ void AsyncFileResponse::_setContentTypeFromPath(const String &path) {
*/
AsyncFileResponse::AsyncFileResponse(FS &fs, const String &path, const char *contentType, bool download, AwsTemplateProcessor callback)
: AsyncAbstractResponse(callback) {
// Try to open the uncompressed version first
_content = fs.open(path, fs::FileOpenMode::read);
if (_content.available()) {
_path = path;
_contentLength = _content.size();
} else {
// Try to open the compressed version (.gz)
_path = path + asyncsrv::T__gz;
_content = fs.open(_path, fs::FileOpenMode::read);
String gzPath;
uint16_t pathLen = path.length();
gzPath.reserve(pathLen + 3);
gzPath.concat(path);
gzPath.concat(asyncsrv::T__gz);
_content = fs.open(gzPath, fs::FileOpenMode::read);
_contentLength = _content.size();
if (_content.seek(_contentLength - 8)) {
@ -734,7 +733,7 @@ AsyncFileResponse::AsyncFileResponse(FS &fs, const String &path, const char *con
}
}
if (*contentType != '\0') {
if (*contentType == '\0') {
_setContentTypeFromPath(path);
} else {
_contentType = contentType;
@ -745,11 +744,11 @@ AsyncFileResponse::AsyncFileResponse(FS &fs, const String &path, const char *con
int filenameStart = path.lastIndexOf('/') + 1;
char buf[26 + path.length() - filenameStart];
char *filename = (char *)path.c_str() + filenameStart;
snprintf_P(buf, sizeof(buf), PSTR("attachment; filename=\"%s\""), filename);
snprintf(buf, sizeof(buf), T_attachment, filename);
addHeader(T_Content_Disposition, buf, false);
} else {
// Serve file inline (display in browser)
addHeader(T_Content_Disposition, PSTR("inline"), false);
addHeader(T_Content_Disposition, T_inline, false);
}
_code = 200;
@ -758,9 +757,8 @@ AsyncFileResponse::AsyncFileResponse(FS &fs, const String &path, const char *con
AsyncFileResponse::AsyncFileResponse(File content, const String &path, const char *contentType, bool download, AwsTemplateProcessor callback)
: AsyncAbstractResponse(callback) {
_code = 200;
_path = path;
if (!download && String(content.name()).endsWith(T__gz) && !path.endsWith(T__gz)) {
if (String(content.name()).endsWith(T__gz) && !path.endsWith(T__gz)) {
addHeader(T_Content_Encoding, T_gzip, false);
_callback = nullptr; // Unable to process gzipped templates
_sendContentLength = true;

View file

@ -10,39 +10,39 @@ static constexpr const char *empty = "";
static constexpr const char *T__opaque = "\", opaque=\"";
static constexpr const char *T_100_CONTINUE = "100-continue";
static constexpr const char *T_13 = "13";
static constexpr const char *T_ACCEPT = "accept";
static constexpr const char *T_Accept_Ranges = "accept-ranges";
static constexpr const char *T_app_xform_urlencoded = "application/x-www-form-urlencoded";
static constexpr const char *T_AUTH = "authorization";
static constexpr const char *T_ACCEPT = "Accept";
static constexpr const char *T_Accept_Ranges = "Accept-Ranges";
static constexpr const char *T_attachment = "attachment; filename=\"%s\"";
static constexpr const char *T_AUTH = "Authorization";
static constexpr const char *T_auth_nonce = "\", qop=\"auth\", nonce=\"";
static constexpr const char *T_BASIC = "basic";
static constexpr const char *T_BASIC_REALM = "basic realm=\"";
static constexpr const char *T_BEARER = "bearer";
static constexpr const char *T_BASIC = "Basic";
static constexpr const char *T_BASIC_REALM = "Basic realm=\"";
static constexpr const char *T_BEARER = "Bearer";
static constexpr const char *T_BODY = "body";
static constexpr const char *T_Cache_Control = "cache-control";
static constexpr const char *T_Cache_Control = "Cache-Control";
static constexpr const char *T_chunked = "chunked";
static constexpr const char *T_close = "close";
static constexpr const char *T_cnonce = "cnonce";
static constexpr const char *T_Connection = "connection";
static constexpr const char *T_Content_Disposition = "content-disposition";
static constexpr const char *T_Content_Encoding = "content-encoding";
static constexpr const char *T_Content_Length = "content-length";
static constexpr const char *T_Content_Type = "content-type";
static constexpr const char *T_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";
static constexpr const char *T_CORS_ACAM = "access-control-allow-methods";
static constexpr const char *T_CORS_ACAO = "access-control-allow-origin";
static constexpr const char *T_CORS_ACMA = "access-control-max-age";
static constexpr const char *T_CORS_O = "origin";
static constexpr const char *T_Connection = "Connection";
static constexpr const char *T_Content_Disposition = "Content-Disposition";
static constexpr const char *T_Content_Encoding = "Content-Encoding";
static constexpr const char *T_Content_Length = "Content-Length";
static constexpr const char *T_Content_Type = "Content-Type";
static constexpr const char *T_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";
static constexpr const char *T_CORS_ACAM = "Access-Control-Allow-Methods";
static constexpr const char *T_CORS_ACAO = "Access-Control-Allow-Origin";
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";
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";
static constexpr const char *T_event_ = "event: ";
static constexpr const char *T_EXPECT = "expect";
static constexpr const char *T_EXPECT = "Expect";
static constexpr const char *T_FALSE = "false";
static constexpr const char *T_filename = "filename";
static constexpr const char *T_gzip = "gzip";
@ -50,12 +50,13 @@ static constexpr const char *T_Host = "host";
static constexpr const char *T_HTTP_1_0 = "HTTP/1.0";
static constexpr const char *T_HTTP_100_CONT = "HTTP/1.1 100 Continue\r\n\r\n";
static constexpr const char *T_id__ = "id: ";
static constexpr const char *T_IMS = "if-modified-since";
static constexpr const char *T_INM = "if-none-match";
static constexpr const char *T_IMS = "If-Modified-Since";
static constexpr const char *T_INM = "If-None-Match";
static constexpr const char *T_inline = "inline";
static constexpr const char *T_keep_alive = "keep-alive";
static constexpr const char *T_Last_Event_ID = "last-event-id";
static constexpr const char *T_Last_Modified = "last-modified";
static constexpr const char *T_LOCATION = "location";
static constexpr const char *T_Last_Event_ID = "Last-Event-ID";
static constexpr const char *T_Last_Modified = "Last-Modified";
static constexpr const char *T_LOCATION = "Location";
static constexpr const char *T_LOGIN_REQ = "Login Required";
static constexpr const char *T_MULTIPART_ = "multipart/";
static constexpr const char *T_name = "name";
@ -69,21 +70,20 @@ static constexpr const char *T_realm = "realm";
static constexpr const char *T_realm__ = "realm=\"";
static constexpr const char *T_response = "response";
static constexpr const char *T_retry_ = "retry: ";
static constexpr const char *T_retry_after = "retry-after";
static constexpr const char *T_retry_after = "Retry-After";
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_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";
static constexpr const char *T_UPGRADE = "Upgrade";
static constexpr const char *T_uri = "uri";
static constexpr const char *T_username = "username";
static constexpr const char *T_WS = "websocket";
static constexpr const char *T_WWW_AUTH = "www-authenticate";
static constexpr const char *T_WWW_AUTH = "WWW-Authenticate";
// HTTP Methods
static constexpr const char *T_ANY = "ANY";
static constexpr const char *T_GET = "GET";
static constexpr const char *T_POST = "POST";
@ -103,44 +103,55 @@ static constexpr const char *T_RCT_EVENT = "RCT_EVENT";
static constexpr const char *T_ERROR = "ERROR";
// extensions & MIME-Types
static constexpr const char *T__css = ".css";
static constexpr const char *T__eot = ".eot";
static constexpr const char *T__gif = ".gif";
static constexpr const char *T__gz = ".gz";
static constexpr const char *T__htm = ".htm";
static constexpr const char *T__html = ".html";
static constexpr const char *T__ico = ".ico";
static constexpr const char *T__jpg = ".jpg";
static constexpr const char *T__js = ".js";
static constexpr const char *T__json = ".json";
static constexpr const char *T__pdf = ".pdf";
static constexpr const char *T__png = ".png";
static constexpr const char *T__svg = ".svg";
static constexpr const char *T__ttf = ".ttf";
static constexpr const char *T__woff = ".woff";
static constexpr const char *T__woff2 = ".woff2";
static constexpr const char *T__xml = ".xml";
static constexpr const char *T__zip = ".zip";
static constexpr const char *T_application_javascript = "application/javascript";
static constexpr const char *T__avif = ".avif"; // AVIF: Highly compressed images. Compatible with all modern browsers.
static constexpr const char *T__csv = ".csv"; // CSV: Data logging and configuration
static constexpr const char *T__css = ".css"; // CSS: Styling for web interfaces
static constexpr const char *T__gif = ".gif"; // GIF: Simple animations. Legacy support
static constexpr const char *T__gz = ".gz"; // GZ: compressed files
static constexpr const char *T__htm = ".htm"; // HTM: Web interface files
static constexpr const char *T__html = ".html"; // HTML: Web interface files
static constexpr const char *T__ico = ".ico"; // ICO: Favicons, system icons. Legacy support
static constexpr const char *T__jpg = ".jpg"; // JPEG/JPG: Photos. Legacy support
static constexpr const char *T__js = ".js"; // JavaScript: Interactive functionality
static constexpr const char *T__json = ".json"; // JSON: Data exchange format
static constexpr const char *T__mp4 = ".mp4"; // MP4: Proprietary format. Worse compression than WEBM.
static constexpr const char *T__opus = ".opus"; // OPUS: High compression audio format
static constexpr const char *T__pdf = ".pdf"; // PDF: Universal document format
static constexpr const char *T__png = ".png"; // PNG: Icons, logos, transparency. Legacy support
static constexpr const char *T__svg = ".svg"; // SVG: Vector graphics, icons (scalable, tiny file sizes)
static constexpr const char *T__ttf = ".ttf"; // TTF: Font file. Legacy support
static constexpr const char *T__txt = ".txt"; // TXT: Plain text files
static constexpr const char *T__webm = ".webm"; // WebM: Video. Open source, optimized for web. Compatible with all modern browsers.
static constexpr const char *T__webp = ".webp"; // WebP: Highly compressed images. Compatible with all modern browsers.
static constexpr const char *T__woff = ".woff"; // WOFF: Font file. Legacy support
static constexpr const char *T__woff2 = ".woff2"; // WOFF2: Better compression. Compatible with all modern browsers.
static constexpr const char *T__xml = ".xml"; // XML: Configuration and data files
static constexpr const char *T_application_javascript = "application/javascript"; // Obsolete type for JavaScript
static constexpr const char *T_application_json = "application/json";
static constexpr const char *T_application_msgpack = "application/msgpack";
static constexpr const char *T_application_octet_stream = "application/octet-stream";
static constexpr const char *T_application_pdf = "application/pdf";
static constexpr const char *T_application_x_gzip = "application/x-gzip";
static constexpr const char *T_application_zip = "application/zip";
static constexpr const char *T_font_eot = "font/eot";
static constexpr const char *T_app_xform_urlencoded = "application/x-www-form-urlencoded";
static constexpr const char *T_audio_opus = "audio/opus";
static constexpr const char *T_font_ttf = "font/ttf";
static constexpr const char *T_font_woff = "font/woff";
static constexpr const char *T_font_woff2 = "font/woff2";
static constexpr const char *T_image_avif = "image/avif";
static constexpr const char *T_image_gif = "image/gif";
static constexpr const char *T_image_jpeg = "image/jpeg";
static constexpr const char *T_image_png = "image/png";
static constexpr const char *T_image_svg_xml = "image/svg+xml";
static constexpr const char *T_image_webp = "image/webp";
static constexpr const char *T_image_x_icon = "image/x-icon";
static constexpr const char *T_text_css = "text/css";
static constexpr const char *T_text_csv = "text/csv";
static constexpr const char *T_text_event_stream = "text/event-stream";
static constexpr const char *T_text_html = "text/html";
static constexpr const char *T_text_javascript = "text/javascript";
static constexpr const char *T_text_plain = "text/plain";
static constexpr const char *T_text_xml = "text/xml";
static constexpr const char *T_video_mp4 = "video/mp4";
static constexpr const char *T_video_webm = "video/webm";
// Response codes
static constexpr const char *T_HTTP_CODE_100 = "Continue";
@ -175,7 +186,7 @@ static constexpr const char *T_HTTP_CODE_412 = "Precondition Failed";
static constexpr const char *T_HTTP_CODE_413 = "Request Entity Too Large";
static constexpr const char *T_HTTP_CODE_414 = "Request-URI Too Large";
static constexpr const char *T_HTTP_CODE_415 = "Unsupported Media Type";
static constexpr const char *T_HTTP_CODE_416 = "Requested range not satisfiable";
static constexpr const char *T_HTTP_CODE_416 = "Requested Range Not Satisfiable";
static constexpr const char *T_HTTP_CODE_417 = "Expectation Failed";
static constexpr const char *T_HTTP_CODE_429 = "Too Many Requests";
static constexpr const char *T_HTTP_CODE_500 = "Internal Server Error";
@ -183,11 +194,14 @@ static constexpr const char *T_HTTP_CODE_501 = "Not Implemented";
static constexpr const char *T_HTTP_CODE_502 = "Bad Gateway";
static constexpr const char *T_HTTP_CODE_503 = "Service Unavailable";
static constexpr const char *T_HTTP_CODE_504 = "Gateway Time-out";
static constexpr const char *T_HTTP_CODE_505 = "HTTP Version not supported";
static constexpr const char *T_HTTP_CODE_505 = "HTTP Version Not Supported";
static constexpr const char *T_HTTP_CODE_ANY = "Unknown code";
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};
static constexpr const char *T_only_once_headers[] = {
T_Accept_Ranges, T_Content_Length, T_Content_Type, T_Connection, T_CORS_ACAC, T_CORS_ACAH, T_CORS_ACAM, T_CORS_ACAO,
T_CORS_ACMA, T_CORS_O, T_Date, T_DIGEST, T_ETag, T_Last_Modified, T_LOCATION, T_retry_after,
T_Transfer_Encoding, T_Content_Location, T_Server, T_WWW_AUTH
};
static constexpr size_t T_only_once_headers_len = sizeof(T_only_once_headers) / sizeof(T_only_once_headers[0]);
} // namespace asyncsrv