Blame | Last modification | View Log | RSS feed
// SPDX-License-Identifier: LGPL-3.0-or-later// Copyright 2016-2026 Hristo Gochkov, Mathieu Carbou, Emil Muratov, Will Miles#include "ESPAsyncWebServer.h"#include "WebHandlerImpl.h"#include <string>#include <utility>#if defined(ESP32) || defined(TARGET_RP2040) || defined(TARGET_RP2350) || defined(PICO_RP2040) || defined(PICO_RP2350) || defined(LIBRETINY) || defined(HOST)#include <WiFi.h>#elif defined(ESP8266)#include <ESP8266WiFi.h>#else#error Platform not supported#endifusing namespace asyncsrv;bool ON_STA_FILTER(AsyncWebServerRequest *request) {#if ASYNCWEBSERVER_WIFI_SUPPORTEDreturn WiFi.localIP() == request->client()->localIP();#elsereturn false;#endif}bool ON_AP_FILTER(AsyncWebServerRequest *request) {#if ASYNCWEBSERVER_WIFI_SUPPORTEDreturn WiFi.localIP() != request->client()->localIP();#elsereturn false;#endif}#ifndef HAVE_FS_FILE_OPEN_MODEconst char *fs::FileOpenMode::read = "r";const char *fs::FileOpenMode::write = "w";const char *fs::FileOpenMode::append = "a";#endifAsyncWebServer::AsyncWebServer(uint16_t port) : _server(port) {_catchAllHandler = new AsyncCallbackWebHandler();_server.onClient([](void *s, AsyncClient *c) {if (c == NULL) {return;}c->setRxTimeout(3);AsyncWebServerRequest *r = new AsyncWebServerRequest((AsyncWebServer *)s, c);if (r == NULL) {c->abort();delete c;}},this);}AsyncWebServer::~AsyncWebServer() {reset();end();delete _catchAllHandler;_catchAllHandler = nullptr; // Prevent potential use-after-free}AsyncWebRewrite &AsyncWebServer::addRewrite(std::shared_ptr<AsyncWebRewrite> rewrite) {_rewrites.emplace_back(rewrite);return *_rewrites.back().get();}AsyncWebRewrite &AsyncWebServer::addRewrite(AsyncWebRewrite *rewrite) {_rewrites.emplace_back(rewrite);return *_rewrites.back().get();}bool AsyncWebServer::removeRewrite(AsyncWebRewrite *rewrite) {return removeRewrite(rewrite->from().c_str(), rewrite->toUrl().c_str());}bool AsyncWebServer::removeRewrite(const char *from, const char *to) {for (auto r = _rewrites.begin(); r != _rewrites.end(); ++r) {if (r->get()->from() == from && r->get()->toUrl() == to) {_rewrites.erase(r);return true;}}return false;}AsyncWebRewrite &AsyncWebServer::rewrite(const char *from, const char *to) {_rewrites.emplace_back(std::make_shared<AsyncWebRewrite>(from, to));return *_rewrites.back().get();}AsyncWebHandler &AsyncWebServer::addHandler(AsyncWebHandler *handler) {_handlers.emplace_back(handler);return *(_handlers.back().get());}bool AsyncWebServer::removeHandler(AsyncWebHandler *handler) {for (auto i = _handlers.begin(); i != _handlers.end(); ++i) {if (i->get() == handler) {_handlers.erase(i);return true;}}return false;}void AsyncWebServer::begin() {_server.setNoDelay(true);_server.begin();}void AsyncWebServer::end() {_server.end();}#if ASYNC_TCP_SSL_ENABLEDvoid AsyncWebServer::onSslFileRequest(AcSSlFileHandler cb, void *arg) {_server.onSslFileRequest(cb, arg);}void AsyncWebServer::beginSecure(const char *cert, const char *key, const char *password) {_server.beginSecure(cert, key, password);}#endifvoid AsyncWebServer::_handleDisconnect(AsyncWebServerRequest *request) {delete 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();request->_addGetParams(r->params());}}}void AsyncWebServer::_attachHandler(AsyncWebServerRequest *request) {for (auto &h : _handlers) {if (h->filter(request) && h->canHandle(request)) {request->setHandler(h.get());return;}}// ESP_LOGD("AsyncWebServer", "No handler found for %s, using _catchAllHandler pointer: %p", request->url().c_str(), _catchAllHandler);request->setHandler(_catchAllHandler);}AsyncCallbackWebHandler &AsyncWebServer::on(AsyncURIMatcher uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest, ArUploadHandlerFunction onUpload, ArBodyHandlerFunction onBody) {AsyncCallbackWebHandler *handler = new AsyncCallbackWebHandler();handler->setUri(std::move(uri));handler->setMethod(std::move(method));handler->onRequest(onRequest);handler->onUpload(onUpload);handler->onBody(onBody);addHandler(handler);return *handler;}#if ASYNC_JSON_SUPPORT == 1AsyncCallbackJsonWebHandler &AsyncWebServer::on(AsyncURIMatcher uri, WebRequestMethodComposite method, ArJsonRequestHandlerFunction onBody) {AsyncCallbackJsonWebHandler *handler = new AsyncCallbackJsonWebHandler(std::move(uri), onBody);handler->setMethod(std::move(method));addHandler(handler);return *handler;}#endifAsyncStaticWebHandler &AsyncWebServer::serveStatic(const char *uri, fs::FS &fs, const char *path, const char *cache_control) {AsyncStaticWebHandler *handler = new AsyncStaticWebHandler(uri, fs, path, cache_control);addHandler(handler);return *handler;}void AsyncWebServer::onNotFound(ArRequestHandlerFunction fn) {_catchAllHandler->onRequest(fn);}void AsyncWebServer::onFileUpload(ArUploadHandlerFunction fn) {_catchAllHandler->onUpload(fn);}void AsyncWebServer::onRequestBody(ArBodyHandlerFunction fn) {_catchAllHandler->onBody(fn);}AsyncWebHandler &AsyncWebServer::catchAllHandler() const {return *_catchAllHandler;}void AsyncWebServer::reset() {_rewrites.clear();_handlers.clear();_catchAllHandler->onRequest(NULL);_catchAllHandler->onUpload(NULL);_catchAllHandler->onBody(NULL);}AsyncURIMatcher::AsyncURIMatcher(String uri, uint16_t modifiers) : _value(std::move(uri)) {#ifdef ASYNCWEBSERVER_REGEXif (_value.startsWith("^") && _value.endsWith("$")) {pattern = new std::regex(_value.c_str(), (modifiers & CaseInsensitive) ? (std::regex::icase | std::regex::optimize) : (std::regex::optimize));return; // no additional processing - flags are overwritten by pattern pointer}#endifif (modifiers & CaseInsensitive) {_value.toLowerCase();}// Inspect _value to set flags// empty URI matches everythingif (!_value.length()) {_flags = _toFlags(Type::All, modifiers);} else if (_value.endsWith("*")) {// wildcard match with * at the end_flags = _toFlags(Type::Prefix, modifiers);_value = _value.substring(0, _value.length() - 1);} else if (_value.lastIndexOf("/*.") >= 0) {// prefix match with /*.ext// matches any path ending with .ext// e.g. /images/*.png will match /images/pic.png and /images/2023/pic.png but not /img/pic.png_flags = _toFlags(Type::Extension, modifiers);} else {// backward compatible use case: exact match or prefix with trailing /_flags = _toFlags(Type::BackwardCompatible, modifiers);}}AsyncURIMatcher::AsyncURIMatcher(String uri, Type type, uint16_t modifiers) : _value(std::move(uri)), _flags(_toFlags(type, modifiers)) {#ifdef ASYNCWEBSERVER_REGEXif (type == Type::Regex) {pattern = new std::regex(_value.c_str(), (modifiers & CaseInsensitive) ? (std::regex::icase | std::regex::optimize) : (std::regex::optimize));return; // no additional processing - flags are overwritten by pattern pointer}#endifif (modifiers & CaseInsensitive) {_value.toLowerCase();}}#ifdef ASYNCWEBSERVER_REGEXAsyncURIMatcher::AsyncURIMatcher(const AsyncURIMatcher &c) : _value(c._value), _flags(c._flags) {if (_isRegex()) {pattern = new std::regex(*pattern);}}AsyncURIMatcher::AsyncURIMatcher(AsyncURIMatcher &&c) : _value(std::move(c._value)), _flags(c._flags) {c._flags = _toFlags(Type::None, None);}AsyncURIMatcher::~AsyncURIMatcher() {if (_isRegex()) {delete pattern;}}AsyncURIMatcher &AsyncURIMatcher::operator=(const AsyncURIMatcher &r) {_value = r._value;if (r._isRegex()) {// Allocate first before we delete our current stateauto p = new std::regex(*r.pattern);// Safely reassign our patternif (_isRegex()) {delete pattern;}pattern = p;} else {if (_isRegex()) {delete pattern;}_flags = r._flags;}return *this;}AsyncURIMatcher &AsyncURIMatcher::operator=(AsyncURIMatcher &&r) {_value = std::move(r._value);if (_isRegex()) {delete pattern;}_flags = r._flags;if (r._isRegex()) {// We have adopted itr._flags = _toFlags(Type::None, None);}return *this;}#endifbool AsyncURIMatcher::matches(AsyncWebServerRequest *request) const {#ifdef ASYNCWEBSERVER_REGEXif (_isRegex()) {// when type == Type::Regex, or when _value was auto-detected as regexstd::smatch matches;std::string s(request->url().c_str());if (std::regex_search(s, matches, *pattern)) {for (size_t i = 1; i < matches.size(); ++i) {request->_pathParams.emplace_back(matches[i].str().c_str());}return true;}return false;}#endif// extract matcher type from _flagsType type;uint16_t modifiers;std::tie(type, modifiers) = _fromFlags(_flags);// apply modifiersString path = request->url();if (modifiers & CaseInsensitive) {path.toLowerCase();}switch (type) {case Type::All: return true;case Type::None: return false;case Type::Exact: return (_value == path);case Type::Prefix: return path.startsWith(_value);case Type::Extension:{int split = _value.lastIndexOf("/*.");return (split >= 0 && path.startsWith(_value.substring(0, split)) && path.endsWith(_value.substring(split + 2)));}case Type::BackwardCompatible: return (_value == path) || path.startsWith(_value + "/");default:// Should never happen - programming errorassert("Invalid type");return false;}}