Skip to content

Commit ec1aed9

Browse files
committed
Introduce AsyncURIMatcher class to encapsulate the URI matching logic with and without regex support (-D ASYNCWEBSERVER_REGEX=1)
1 parent b18bfeb commit ec1aed9

8 files changed

Lines changed: 146 additions & 106 deletions

File tree

platformio.ini

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ build_flags =
5151
-D CONFIG_ASYNC_TCP_QUEUE_SIZE=64
5252
-D CONFIG_ASYNC_TCP_RUNNING_CORE=1
5353
-D CONFIG_ASYNC_TCP_STACK_SIZE=4096
54+
-D ASYNCWEBSERVER_REGEX=1
5455
; -D CONFIG_ASYNC_TCP_USE_WDT=0
5556
; -D CONFIG_ARDUHAL_LOG_COLORS
5657
; -D CORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_VERBOSE

src/AsyncJson.cpp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -112,19 +112,19 @@ size_t AsyncMessagePackResponse::_fillBuffer(uint8_t *data, size_t len) {
112112
// Body handler supporting both content types: JSON and MessagePack
113113

114114
#if ARDUINOJSON_VERSION_MAJOR == 6
115-
AsyncCallbackJsonWebHandler::AsyncCallbackJsonWebHandler(const String &uri, ArJsonRequestHandlerFunction onRequest, size_t maxJsonBufferSize)
116-
: _uri(uri), _method(HTTP_GET | HTTP_POST | HTTP_PUT | HTTP_PATCH), _onRequest(onRequest), maxJsonBufferSize(maxJsonBufferSize), _maxContentLength(16384) {}
115+
AsyncCallbackJsonWebHandler::AsyncCallbackJsonWebHandler(AsyncURIMatcher uri, ArJsonRequestHandlerFunction onRequest, size_t maxJsonBufferSize)
116+
: _uri(std::move(uri)), _method(HTTP_GET | HTTP_POST | HTTP_PUT | HTTP_PATCH), _onRequest(onRequest), maxJsonBufferSize(maxJsonBufferSize), _maxContentLength(16384) {}
117117
#else
118-
AsyncCallbackJsonWebHandler::AsyncCallbackJsonWebHandler(const String &uri, ArJsonRequestHandlerFunction onRequest)
119-
: _uri(uri), _method(HTTP_GET | HTTP_POST | HTTP_PUT | HTTP_PATCH), _onRequest(onRequest), _maxContentLength(16384) {}
118+
AsyncCallbackJsonWebHandler::AsyncCallbackJsonWebHandler(AsyncURIMatcher uri, ArJsonRequestHandlerFunction onRequest)
119+
: _uri(std::move(uri)), _method(HTTP_GET | HTTP_POST | HTTP_PUT | HTTP_PATCH), _onRequest(onRequest), _maxContentLength(16384) {}
120120
#endif
121121

122122
bool AsyncCallbackJsonWebHandler::canHandle(AsyncWebServerRequest *request) const {
123123
if (!_onRequest || !request->isHTTP() || !(_method & request->method())) {
124124
return false;
125125
}
126126

127-
if (_uri.length() && (_uri != request->url() && !request->url().startsWith(_uri + "/"))) {
127+
if (_uri.length() && !_uri.matches(request)) {
128128
return false;
129129
}
130130

src/AsyncJson.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ class AsyncMessagePackResponse : public AsyncJsonResponse {
8888

8989
class AsyncCallbackJsonWebHandler : public AsyncWebHandler {
9090
protected:
91-
String _uri;
91+
AsyncURIMatcher _uri;
9292
WebRequestMethodComposite _method;
9393
ArJsonRequestHandlerFunction _onRequest;
9494
#if ARDUINOJSON_VERSION_MAJOR == 6
@@ -98,9 +98,9 @@ class AsyncCallbackJsonWebHandler : public AsyncWebHandler {
9898

9999
public:
100100
#if ARDUINOJSON_VERSION_MAJOR == 6
101-
AsyncCallbackJsonWebHandler(const String &uri, ArJsonRequestHandlerFunction onRequest = nullptr, size_t maxJsonBufferSize = DYNAMIC_JSON_DOCUMENT_SIZE);
101+
AsyncCallbackJsonWebHandler(AsyncURIMatcher uri, ArJsonRequestHandlerFunction onRequest = nullptr, size_t maxJsonBufferSize = DYNAMIC_JSON_DOCUMENT_SIZE);
102102
#else
103-
AsyncCallbackJsonWebHandler(const String &uri, ArJsonRequestHandlerFunction onRequest = nullptr);
103+
AsyncCallbackJsonWebHandler(AsyncURIMatcher uri, ArJsonRequestHandlerFunction onRequest = nullptr);
104104
#endif
105105

106106
void setMethod(WebRequestMethodComposite method) {

src/ESPAsyncWebServer.h

Lines changed: 113 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,7 @@
5050
#define ASYNCWEBSERVER_FORK_ESP32Async
5151

5252
#ifdef ASYNCWEBSERVER_REGEX
53-
#define ASYNCWEBSERVER_REGEX_ATTRIBUTE
54-
#else
55-
#define ASYNCWEBSERVER_REGEX_ATTRIBUTE __attribute__((warning("ASYNCWEBSERVER_REGEX not defined")))
53+
#include <regex>
5654
#endif
5755

5856
// See https://github.com/ESP32Async/ESPAsyncWebServer/commit/3d3456e9e81502a477f6498c44d0691499dda8f9#diff-646b25b11691c11dce25529e3abce843f0ba4bd07ab75ec9eee7e72b06dbf13fR388-R392
@@ -72,6 +70,7 @@ class AsyncStaticWebHandler;
7270
class AsyncCallbackWebHandler;
7371
class AsyncResponseStream;
7472
class AsyncMiddlewareChain;
73+
class AsyncURIMatcher;
7574

7675
#if defined(TARGET_RP2040) || defined(TARGET_RP2350) || defined(PICO_RP2040) || defined(PICO_RP2350)
7776
typedef enum http_method WebRequestMethod;
@@ -162,7 +161,7 @@ class AsyncWebHeader {
162161
[[deprecated("Use AsyncWebHeader::parse(data) instead")]]
163162
#endif
164163
AsyncWebHeader(const String &data)
165-
: AsyncWebHeader(parse(data)){};
164+
: AsyncWebHeader(parse(data)) {};
166165

167166
AsyncWebHeader &operator=(const AsyncWebHeader &) = default;
168167
AsyncWebHeader &operator=(AsyncWebHeader &&other) = default;
@@ -222,6 +221,7 @@ class AsyncWebServerRequest {
222221
friend class AsyncCallbackWebHandler;
223222
friend class AsyncFileResponse;
224223
friend class AsyncStaticWebHandler;
224+
friend class AsyncURIMatcher;
225225

226226
private:
227227
AsyncClient *_client;
@@ -254,7 +254,9 @@ class AsyncWebServerRequest {
254254

255255
std::list<AsyncWebHeader> _headers;
256256
std::list<AsyncWebParameter> _params;
257+
#ifdef ASYNCWEBSERVER_REGEX
257258
std::list<String> _pathParams;
259+
#endif
258260

259261
std::unordered_map<const char *, String, std::hash<const char *>, std::equal_to<const char *>> _attributes;
260262

@@ -277,8 +279,6 @@ class AsyncWebServerRequest {
277279
void _onDisconnect();
278280
void _onData(void *buf, size_t len);
279281

280-
void _addPathParam(const char *param);
281-
282282
bool _parseReqHead();
283283
bool _parseReqHeader();
284284
void _parseLine();
@@ -333,8 +333,9 @@ class AsyncWebServerRequest {
333333
RequestedConnectionType requestedConnType() const {
334334
return _reqconntype;
335335
}
336-
bool isExpectedRequestedConnType(RequestedConnectionType erct1, RequestedConnectionType erct2 = RCT_NOT_USED, RequestedConnectionType erct3 = RCT_NOT_USED)
337-
const;
336+
bool isExpectedRequestedConnType(
337+
RequestedConnectionType erct1, RequestedConnectionType erct2 = RCT_NOT_USED, RequestedConnectionType erct3 = RCT_NOT_USED
338+
) const;
338339
bool isWebSocketUpgrade() const {
339340
return _method == HTTP_GET && isExpectedRequestedConnType(RCT_WS);
340341
}
@@ -527,7 +528,8 @@ class AsyncWebServerRequest {
527528
return beginResponse(code, contentType.c_str(), content, len, callback);
528529
}
529530
#ifndef ESP8266
530-
[[deprecated("Replaced by beginResponse(int code, const String& contentType, const char* content = asyncsrv::empty, AwsTemplateProcessor callback = nullptr)"
531+
[[deprecated(
532+
"Replaced by beginResponse(int code, const String& contentType, const char* content = asyncsrv::empty, AwsTemplateProcessor callback = nullptr)"
531533
)]]
532534
#endif
533535
AsyncWebServerResponse *beginResponse_P(int code, const String &contentType, PGM_P content, AwsTemplateProcessor callback = nullptr);
@@ -615,10 +617,19 @@ class AsyncWebServerRequest {
615617
bool hasArg(const __FlashStringHelper *data) const; // check if F(argument) exists
616618
#endif
617619

618-
const String &ASYNCWEBSERVER_REGEX_ATTRIBUTE pathArg(size_t i) const;
619-
const String &ASYNCWEBSERVER_REGEX_ATTRIBUTE pathArg(int i) const {
620+
#ifdef ASYNCWEBSERVER_REGEX
621+
const String &pathArg(size_t i) const {
622+
if (i >= _pathParams.size()) {
623+
return emptyString;
624+
}
625+
auto it = _pathParams.begin();
626+
std::advance(it, i);
627+
return *it;
628+
}
629+
const String &pathArg(int i) const {
620630
return i < 0 ? emptyString : pathArg((size_t)i);
621631
}
632+
#endif
622633

623634
// get request header value by name
624635
const String &header(const char *name) const;
@@ -719,6 +730,86 @@ class AsyncWebServerRequest {
719730
String urlDecode(const String &text) const;
720731
};
721732

733+
class AsyncURIMatcher {
734+
public:
735+
AsyncURIMatcher() {}
736+
AsyncURIMatcher(const char *uri) : _value(uri) {
737+
#ifdef ASYNCWEBSERVER_REGEX
738+
if (isRegex()) {
739+
pattern = std::regex(_value.c_str());
740+
}
741+
#endif
742+
}
743+
AsyncURIMatcher(String uri) : _value(std::move(uri)) {
744+
#ifdef ASYNCWEBSERVER_REGEX
745+
if (isRegex()) {
746+
pattern = std::regex(_value.c_str());
747+
}
748+
#endif
749+
}
750+
751+
AsyncURIMatcher(const AsyncURIMatcher &) = default;
752+
AsyncURIMatcher(AsyncURIMatcher &&) = default;
753+
~AsyncURIMatcher() = default;
754+
755+
AsyncURIMatcher &operator=(const AsyncURIMatcher &) = default;
756+
AsyncURIMatcher &operator=(AsyncURIMatcher &&) = default;
757+
758+
const String &value() const {
759+
return _value;
760+
}
761+
size_t length() const {
762+
return _value.length();
763+
}
764+
765+
#ifdef ASYNCWEBSERVER_REGEX
766+
bool isRegex() const {
767+
return _value.startsWith("^") && _value.endsWith("$");
768+
}
769+
#endif
770+
771+
bool matches(AsyncWebServerRequest *request) const {
772+
#ifdef ASYNCWEBSERVER_REGEX
773+
if (isRegex()) {
774+
std::smatch matches;
775+
std::string s(request->url().c_str());
776+
if (std::regex_search(s, matches, pattern)) {
777+
for (size_t i = 1; i < matches.size(); ++i) {
778+
request->_pathParams.emplace_back(matches[i].str().c_str());
779+
}
780+
return true;
781+
} else {
782+
return false;
783+
}
784+
}
785+
#endif
786+
787+
if (!_value.length()) {
788+
return true;
789+
}
790+
791+
if (_value.startsWith("/*.")) {
792+
if (!request->url().endsWith(_value.substring(_value.lastIndexOf(".")))) {
793+
return false;
794+
}
795+
} else if (_value.endsWith("*")) {
796+
if (!request->url().startsWith(_value.substring(0, _value.length() - 1))) {
797+
return false;
798+
}
799+
} else if (_value != request->url() && !request->url().startsWith(_value + "/")) {
800+
return false;
801+
}
802+
803+
return true;
804+
}
805+
806+
private:
807+
String _value;
808+
#ifdef ASYNCWEBSERVER_REGEX
809+
std::regex pattern;
810+
#endif
811+
};
812+
722813
/*
723814
* FILTER :: Callback to filter AsyncWebRewrite and AsyncWebHandler (done by the Server)
724815
* */
@@ -992,13 +1083,13 @@ class AsyncRateLimitMiddleware : public AsyncMiddleware {
9921083

9931084
class AsyncWebRewrite {
9941085
protected:
995-
String _from;
1086+
AsyncURIMatcher _from;
9961087
String _toUrl;
9971088
String _params;
9981089
ArRequestFilterFunction _filter{nullptr};
9991090

10001091
public:
1001-
AsyncWebRewrite(const char *from, const char *to) : _from(from), _toUrl(to) {
1092+
AsyncWebRewrite(AsyncURIMatcher from, const char *to) : _from(std::move(from)), _toUrl(to) {
10021093
int index = _toUrl.indexOf('?');
10031094
if (index > 0) {
10041095
_params = _toUrl.substring(index + 1);
@@ -1014,7 +1105,7 @@ class AsyncWebRewrite {
10141105
return _filter == NULL || _filter(request);
10151106
}
10161107
const String &from(void) const {
1017-
return _from;
1108+
return _from.value();
10181109
}
10191110
const String &toUrl(void) const {
10201111
return _toUrl;
@@ -1023,7 +1114,7 @@ class AsyncWebRewrite {
10231114
return _params;
10241115
}
10251116
virtual bool match(AsyncWebServerRequest *request) {
1026-
return from() == request->url() && filter(request);
1117+
return _from.matches(request) && filter(request);
10271118
}
10281119
};
10291120

@@ -1226,7 +1317,7 @@ class AsyncWebServer : public AsyncMiddlewareChain {
12261317
* @param to
12271318
* @return AsyncWebRewrite&
12281319
*/
1229-
AsyncWebRewrite &rewrite(const char *from, const char *to);
1320+
AsyncWebRewrite &rewrite(AsyncURIMatcher from, const char *to);
12301321

12311322
/**
12321323
* @brief (compat) remove rewrite rule via referenced object
@@ -1246,24 +1337,24 @@ class AsyncWebServer : public AsyncMiddlewareChain {
12461337
* @return true
12471338
* @return false
12481339
*/
1249-
bool removeRewrite(const char *from, const char *to);
1340+
bool removeRewrite(AsyncURIMatcher from, const char *to);
12501341

12511342
AsyncWebHandler &addHandler(AsyncWebHandler *handler);
12521343
bool removeHandler(AsyncWebHandler *handler);
12531344

1254-
AsyncCallbackWebHandler &on(const char *uri, ArRequestHandlerFunction onRequest) {
1255-
return on(uri, HTTP_ANY, onRequest);
1345+
AsyncCallbackWebHandler &on(AsyncURIMatcher uri, ArRequestHandlerFunction onRequest) {
1346+
return on(std::move(uri), HTTP_ANY, onRequest);
12561347
}
12571348
AsyncCallbackWebHandler &on(
1258-
const char *uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest, ArUploadHandlerFunction onUpload = nullptr,
1349+
AsyncURIMatcher uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest, ArUploadHandlerFunction onUpload = nullptr,
12591350
ArBodyHandlerFunction onBody = nullptr
12601351
);
12611352

12621353
#if ASYNC_JSON_SUPPORT == 1
1263-
AsyncCallbackJsonWebHandler &on(const char *uri, WebRequestMethodComposite method, ArJsonRequestHandlerFunction onBody);
1354+
AsyncCallbackJsonWebHandler &on(AsyncURIMatcher uri, WebRequestMethodComposite method, ArJsonRequestHandlerFunction onBody);
12641355
#endif
12651356

1266-
AsyncStaticWebHandler &serveStatic(const char *uri, fs::FS &fs, const char *path, const char *cache_control = NULL);
1357+
AsyncStaticWebHandler &serveStatic(AsyncURIMatcher uri, fs::FS &fs, const char *path, const char *cache_control = NULL);
12671358

12681359
void onNotFound(ArRequestHandlerFunction fn); // called when handler is not assigned
12691360
void onFileUpload(ArUploadHandlerFunction fn); // handle file uploads

src/WebHandlerImpl.h

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,6 @@
55
#define ASYNCWEBSERVERHANDLERIMPL_H_
66

77
#include <string>
8-
#ifdef ASYNCWEBSERVER_REGEX
9-
#include <regex>
10-
#endif
11-
128
#include "stddef.h"
139
#include <time.h>
1410

@@ -22,7 +18,7 @@ class AsyncStaticWebHandler : public AsyncWebHandler {
2218

2319
protected:
2420
FS _fs;
25-
String _uri;
21+
AsyncURIMatcher _uri;
2622
String _path;
2723
String _default_file;
2824
String _cache_control;
@@ -32,7 +28,7 @@ class AsyncStaticWebHandler : public AsyncWebHandler {
3228
bool _tryGzipFirst = true;
3329

3430
public:
35-
AsyncStaticWebHandler(const char *uri, FS &fs, const char *path, const char *cache_control);
31+
AsyncStaticWebHandler(AsyncURIMatcher uri, FS &fs, const char *path, const char *cache_control);
3632
bool canHandle(AsyncWebServerRequest *request) const override final;
3733
void handleRequest(AsyncWebServerRequest *request) override final;
3834
AsyncStaticWebHandler &setTryGzipFirst(bool value);
@@ -58,7 +54,7 @@ class AsyncStaticWebHandler : public AsyncWebHandler {
5854
class AsyncCallbackWebHandler : public AsyncWebHandler {
5955
private:
6056
protected:
61-
String _uri;
57+
AsyncURIMatcher _uri;
6258
WebRequestMethodComposite _method;
6359
ArRequestHandlerFunction _onRequest;
6460
ArUploadHandlerFunction _onUpload;
@@ -67,7 +63,7 @@ class AsyncCallbackWebHandler : public AsyncWebHandler {
6763

6864
public:
6965
AsyncCallbackWebHandler() : _uri(), _method(HTTP_ANY), _onRequest(NULL), _onUpload(NULL), _onBody(NULL), _isRegex(false) {}
70-
void setUri(const String &uri);
66+
void setUri(AsyncURIMatcher uri);
7167
void setMethod(WebRequestMethodComposite method) {
7268
_method = method;
7369
}

0 commit comments

Comments
 (0)