@@ -70,6 +70,7 @@ class AsyncStaticWebHandler;
7070class AsyncCallbackWebHandler ;
7171class AsyncResponseStream ;
7272class AsyncMiddlewareChain ;
73+ class AsyncURIMatcher ;
7374
7475#if defined(TARGET_RP2040) || defined(TARGET_RP2350) || defined(PICO_RP2040) || defined(PICO_RP2350)
7576typedef enum http_method WebRequestMethod;
@@ -220,6 +221,7 @@ class AsyncWebServerRequest {
220221 friend class AsyncCallbackWebHandler ;
221222 friend class AsyncFileResponse ;
222223 friend class AsyncStaticWebHandler ;
224+ friend class AsyncURIMatcher ;
223225
224226private:
225227 AsyncClient *_client;
@@ -729,6 +731,103 @@ class AsyncWebServerRequest {
729731 String urlDecode (const String &text) const ;
730732};
731733
734+ class AsyncURIMatcher {
735+ public:
736+ AsyncURIMatcher () {}
737+ AsyncURIMatcher (const char *uri, bool ignoreCase = false ) : _value(uri), _ignoreCase(ignoreCase) {
738+ if (_ignoreCase) {
739+ _value.toLowerCase ();
740+ }
741+ #ifdef ASYNCWEBSERVER_REGEX
742+ if (isRegex ()) {
743+ pattern = _ignoreCase ? std::regex (_value.c_str (), std::regex::icase) : std::regex (_value.c_str ());
744+ }
745+ #endif
746+ }
747+ AsyncURIMatcher (String uri, bool ignoreCase = false ) : _value(std::move(uri)), _ignoreCase(ignoreCase) {
748+ if (_ignoreCase) {
749+ _value.toLowerCase ();
750+ }
751+ #ifdef ASYNCWEBSERVER_REGEX
752+ if (isRegex ()) {
753+ pattern = _ignoreCase ? std::regex (_value.c_str (), std::regex::icase) : std::regex (_value.c_str ());
754+ }
755+ #endif
756+ }
757+
758+ AsyncURIMatcher (const AsyncURIMatcher &) = default ;
759+ AsyncURIMatcher (AsyncURIMatcher &&) = default ;
760+ ~AsyncURIMatcher () = default ;
761+
762+ AsyncURIMatcher &operator =(const AsyncURIMatcher &) = default ;
763+ AsyncURIMatcher &operator =(AsyncURIMatcher &&) = default ;
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+ }
782+ return false ;
783+ }
784+ #endif
785+
786+ // empty URI matches everything
787+ if (!_value.length ()) {
788+ return true ;
789+ }
790+
791+ String path = request->url ();
792+ if (_ignoreCase) {
793+ path.toLowerCase ();
794+ }
795+
796+ // exact match (should be the most common case)
797+ if (_value == path) {
798+ return true ;
799+ }
800+
801+ // wildcard match with * at the end
802+ if (_value.endsWith (" *" )) {
803+ return path.startsWith (_value.substring (0 , _value.length () - 1 ));
804+ }
805+
806+ // prefix match with /*.ext
807+ // matches any path ending with .ext
808+ // e.g. /images/*.png will match /images/pic.png and /images/2023/pic.png but not /img/pic.png
809+ if (_value.startsWith (" /*." )) {
810+ return path.endsWith (_value.substring (_value.lastIndexOf (" ." )));
811+ }
812+
813+ // finally check for prefix match with / at the end
814+ // e.g. /images will also match /images/pic.png and /images/2023/pic.png but not /img/pic.png
815+ if (path.startsWith (_value + " /" )) {
816+ return true ;
817+ }
818+
819+ // we did not match
820+ return false ;
821+ }
822+
823+ private:
824+ String _value;
825+ bool _ignoreCase = false ;
826+ #ifdef ASYNCWEBSERVER_REGEX
827+ std::regex pattern;
828+ #endif
829+ };
830+
732831/*
733832 * FILTER :: Callback to filter AsyncWebRewrite and AsyncWebHandler (done by the Server)
734833 * */
@@ -1261,16 +1360,16 @@ class AsyncWebServer : public AsyncMiddlewareChain {
12611360 AsyncWebHandler &addHandler (AsyncWebHandler *handler);
12621361 bool removeHandler (AsyncWebHandler *handler);
12631362
1264- AsyncCallbackWebHandler &on (const char * uri, ArRequestHandlerFunction onRequest) {
1265- return on (uri, HTTP_ANY, onRequest);
1363+ AsyncCallbackWebHandler &on (AsyncURIMatcher uri, ArRequestHandlerFunction onRequest) {
1364+ return on (std::move ( uri) , HTTP_ANY, onRequest);
12661365 }
12671366 AsyncCallbackWebHandler &on (
1268- const char * uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest, ArUploadHandlerFunction onUpload = nullptr ,
1367+ AsyncURIMatcher uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest, ArUploadHandlerFunction onUpload = nullptr ,
12691368 ArBodyHandlerFunction onBody = nullptr
12701369 );
12711370
12721371#if ASYNC_JSON_SUPPORT == 1
1273- AsyncCallbackJsonWebHandler &on (const char * uri, WebRequestMethodComposite method, ArJsonRequestHandlerFunction onBody);
1372+ AsyncCallbackJsonWebHandler &on (AsyncURIMatcher uri, WebRequestMethodComposite method, ArJsonRequestHandlerFunction onBody);
12741373#endif
12751374
12761375 AsyncStaticWebHandler &serveStatic (const char *uri, fs::FS &fs, const char *path, const char *cache_control = NULL );
0 commit comments