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;
7270class AsyncCallbackWebHandler ;
7371class AsyncResponseStream ;
7472class AsyncMiddlewareChain ;
73+ class AsyncURIMatcher ;
7574
7675#if defined(TARGET_RP2040) || defined(TARGET_RP2350) || defined(PICO_RP2040) || defined(PICO_RP2350)
7776typedef enum http_method WebRequestMethod;
@@ -222,6 +221,7 @@ class AsyncWebServerRequest {
222221 friend class AsyncCallbackWebHandler ;
223222 friend class AsyncFileResponse ;
224223 friend class AsyncStaticWebHandler ;
224+ friend class AsyncURIMatcher ;
225225
226226private:
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 ();
@@ -615,10 +615,19 @@ class AsyncWebServerRequest {
615615 bool hasArg (const __FlashStringHelper *data) const ; // check if F(argument) exists
616616#endif
617617
618- const String &ASYNCWEBSERVER_REGEX_ATTRIBUTE pathArg (size_t i) const ;
619- const String &ASYNCWEBSERVER_REGEX_ATTRIBUTE pathArg (int i) const {
618+ #ifdef ASYNCWEBSERVER_REGEX
619+ const String &pathArg (size_t i) const {
620+ if (i >= _pathParams.size ()) {
621+ return emptyString;
622+ }
623+ auto it = _pathParams.begin ();
624+ std::advance (it, i);
625+ return *it;
626+ }
627+ const String &pathArg (int i) const {
620628 return i < 0 ? emptyString : pathArg ((size_t )i);
621629 }
630+ #endif
622631
623632 // get request header value by name
624633 const String &header (const char *name) const ;
@@ -719,6 +728,104 @@ class AsyncWebServerRequest {
719728 String urlDecode (const String &text) const ;
720729};
721730
731+ class AsyncURIMatcher {
732+ public:
733+ AsyncURIMatcher () {}
734+ AsyncURIMatcher (const char *uri, bool ignoreCase = false ) : _value(uri), _ignoreCase(ignoreCase) {
735+ if (_ignoreCase) {
736+ _value.toLowerCase ();
737+ }
738+ #ifdef ASYNCWEBSERVER_REGEX
739+ if (isRegex ()) {
740+ pattern = _ignoreCase ? std::regex (_value.c_str (), std::regex::icase) : std::regex (_value.c_str ());
741+ }
742+ #endif
743+ }
744+ AsyncURIMatcher (String uri, bool ignoreCase = false ) : _value(std::move(uri)), _ignoreCase(ignoreCase) {
745+ if (_ignoreCase) {
746+ _value.toLowerCase ();
747+ }
748+ #ifdef ASYNCWEBSERVER_REGEX
749+ if (isRegex ()) {
750+ pattern = _ignoreCase ? std::regex (_value.c_str (), std::regex::icase) : std::regex (_value.c_str ());
751+ }
752+ #endif
753+ }
754+
755+ AsyncURIMatcher (const AsyncURIMatcher &) = default ;
756+ AsyncURIMatcher (AsyncURIMatcher &&) = default ;
757+ ~AsyncURIMatcher () = default ;
758+
759+ AsyncURIMatcher &operator =(const AsyncURIMatcher &) = default ;
760+ AsyncURIMatcher &operator =(AsyncURIMatcher &&) = default ;
761+
762+ #ifdef ASYNCWEBSERVER_REGEX
763+ bool isRegex () const {
764+ return _value.startsWith (" ^" ) && _value.endsWith (" $" );
765+ }
766+ #endif
767+
768+ bool matches (AsyncWebServerRequest *request) const {
769+ #ifdef ASYNCWEBSERVER_REGEX
770+ if (isRegex ()) {
771+ std::smatch matches;
772+ std::string s (request->url ().c_str ());
773+ if (std::regex_search (s, matches, pattern)) {
774+ for (size_t i = 1 ; i < matches.size (); ++i) {
775+ request->_pathParams .emplace_back (matches[i].str ().c_str ());
776+ }
777+ return true ;
778+ } else {
779+ return false ;
780+ }
781+ }
782+ #endif
783+
784+ // empty URI matches everything
785+ if (!_value.length ()) {
786+ return true ;
787+ }
788+
789+ String path = request->url ();
790+ if (_ignoreCase) {
791+ path.toLowerCase ();
792+ }
793+
794+ // exact match (should be the most common case)
795+ if (_value == path) {
796+ return true ;
797+ }
798+
799+ // wildcard match with * at the end
800+ if (_value.endsWith (" *" )) {
801+ return path.startsWith (_value.substring (0 , _value.length () - 1 ));
802+ }
803+
804+ // prefix match with /*.ext
805+ // matches any path ending with .ext
806+ // e.g. /images/*.png will match /images/pic.png and /images/2023/pic.png but not /img/pic.png
807+ if (_value.startsWith (" /*." )) {
808+ return path.endsWith (_value.substring (_value.lastIndexOf (" ." )));
809+ }
810+
811+ // finally check for prefix match with / at the end
812+ // e.g. /images will also match /images/pic.png and /images/2023/pic.png but not /img/pic.png
813+ if (path.startsWith (_value + " /" )) {
814+ return true ;
815+ }
816+
817+ // we did not match
818+ return false ;
819+ }
820+
821+ private:
822+ String _value;
823+ bool _ignoreCase = false ;
824+ #ifdef ASYNCWEBSERVER_REGEX
825+ std::regex pattern;
826+ #endif
827+ };
828+
722829/*
723830 * FILTER :: Callback to filter AsyncWebRewrite and AsyncWebHandler (done by the Server)
724831 * */
@@ -1251,16 +1358,16 @@ class AsyncWebServer : public AsyncMiddlewareChain {
12511358 AsyncWebHandler &addHandler (AsyncWebHandler *handler);
12521359 bool removeHandler (AsyncWebHandler *handler);
12531360
1254- AsyncCallbackWebHandler &on (const char * uri, ArRequestHandlerFunction onRequest) {
1255- return on (uri, HTTP_ANY, onRequest);
1361+ AsyncCallbackWebHandler &on (AsyncURIMatcher uri, ArRequestHandlerFunction onRequest) {
1362+ return on (std::move ( uri) , HTTP_ANY, onRequest);
12561363 }
12571364 AsyncCallbackWebHandler &on (
1258- const char * uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest, ArUploadHandlerFunction onUpload = nullptr ,
1365+ AsyncURIMatcher uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest, ArUploadHandlerFunction onUpload = nullptr ,
12591366 ArBodyHandlerFunction onBody = nullptr
12601367 );
12611368
12621369#if ASYNC_JSON_SUPPORT == 1
1263- AsyncCallbackJsonWebHandler &on (const char * uri, WebRequestMethodComposite method, ArJsonRequestHandlerFunction onBody);
1370+ AsyncCallbackJsonWebHandler &on (AsyncURIMatcher uri, WebRequestMethodComposite method, ArJsonRequestHandlerFunction onBody);
12641371#endif
12651372
12661373 AsyncStaticWebHandler &serveStatic (const char *uri, fs::FS &fs, const char *path, const char *cache_control = NULL );
0 commit comments