From cfe8526ec8bf1c899f78d610051f6ab79ef022d7 Mon Sep 17 00:00:00 2001 From: Bob Date: Mon, 20 Jan 2020 14:21:01 +0100 Subject: [PATCH] Add Uri with support for regexUri and globUri (#3441) * Add Uri with support for staticUri, regexUri and globUri * Add newline to end of files * Update example * Suppress gcc warnings (unused params) --- .../examples/PathArgServer/PathArgServer.ino | 7 +- libraries/WebServer/src/Uri.h | 28 ++++++++ libraries/WebServer/src/WebServer.cpp | 6 +- libraries/WebServer/src/WebServer.h | 7 +- .../src/detail/RequestHandlersImpl.h | 56 +++------------- libraries/WebServer/src/uri/UriBraces.h | 66 +++++++++++++++++++ libraries/WebServer/src/uri/UriGlob.h | 22 +++++++ libraries/WebServer/src/uri/UriRegex.h | 44 +++++++++++++ 8 files changed, 182 insertions(+), 54 deletions(-) create mode 100644 libraries/WebServer/src/Uri.h create mode 100644 libraries/WebServer/src/uri/UriBraces.h create mode 100644 libraries/WebServer/src/uri/UriGlob.h create mode 100644 libraries/WebServer/src/uri/UriRegex.h diff --git a/libraries/WebServer/examples/PathArgServer/PathArgServer.ino b/libraries/WebServer/examples/PathArgServer/PathArgServer.ino index 8f22f1be..6e120c3f 100644 --- a/libraries/WebServer/examples/PathArgServer/PathArgServer.ino +++ b/libraries/WebServer/examples/PathArgServer/PathArgServer.ino @@ -3,6 +3,9 @@ #include #include +#include +#include + const char *ssid = "........"; const char *password = "........"; @@ -33,12 +36,12 @@ void setup(void) { server.send(200, "text/plain", "hello from esp32!"); }); - server.on("/users/{}", []() { + server.on(UriBraces("/users/{}"), []() { String user = server.pathArg(0); server.send(200, "text/plain", "User: '" + user + "'"); }); - server.on("/users/{}/devices/{}", []() { + server.on(UriRegex("^\\/users\\/([0-9]+)\\/devices\\/([0-9]+)$"), []() { String user = server.pathArg(0); String device = server.pathArg(1); server.send(200, "text/plain", "User: '" + user + "' and Device: '" + device + "'"); diff --git a/libraries/WebServer/src/Uri.h b/libraries/WebServer/src/Uri.h new file mode 100644 index 00000000..83772b23 --- /dev/null +++ b/libraries/WebServer/src/Uri.h @@ -0,0 +1,28 @@ +#ifndef URI_H +#define URI_H + +#include +#include + +class Uri { + + protected: + const String _uri; + + public: + Uri(const char *uri) : _uri(uri) {} + Uri(const String &uri) : _uri(uri) {} + virtual ~Uri() {} + + virtual Uri* clone() const { + return new Uri(_uri); + }; + + virtual void initPathArgs(__attribute__((unused)) std::vector &pathArgs) {} + + virtual bool canHandle(const String &requestUri, __attribute__((unused)) std::vector &pathArgs) { + return _uri == requestUri; + } +}; + +#endif diff --git a/libraries/WebServer/src/WebServer.cpp b/libraries/WebServer/src/WebServer.cpp index a510f109..e686f2fc 100644 --- a/libraries/WebServer/src/WebServer.cpp +++ b/libraries/WebServer/src/WebServer.cpp @@ -245,15 +245,15 @@ void WebServer::requestAuthentication(HTTPAuthMethod mode, const char* realm, co send(401, String(FPSTR(mimeTable[html].mimeType)), authFailMsg); } -void WebServer::on(const String &uri, WebServer::THandlerFunction handler) { +void WebServer::on(const Uri &uri, WebServer::THandlerFunction handler) { on(uri, HTTP_ANY, handler); } -void WebServer::on(const String &uri, HTTPMethod method, WebServer::THandlerFunction fn) { +void WebServer::on(const Uri &uri, HTTPMethod method, WebServer::THandlerFunction fn) { on(uri, method, fn, _fileUploadHandler); } -void WebServer::on(const String &uri, HTTPMethod method, WebServer::THandlerFunction fn, WebServer::THandlerFunction ufn) { +void WebServer::on(const Uri &uri, HTTPMethod method, WebServer::THandlerFunction fn, WebServer::THandlerFunction ufn) { _addRequestHandler(new FunctionRequestHandler(fn, ufn, uri, method)); } diff --git a/libraries/WebServer/src/WebServer.h b/libraries/WebServer/src/WebServer.h index 97aa032e..a0d3a1c1 100644 --- a/libraries/WebServer/src/WebServer.h +++ b/libraries/WebServer/src/WebServer.h @@ -28,6 +28,7 @@ #include #include #include "HTTP_Method.h" +#include "Uri.h" enum HTTPUploadStatus { UPLOAD_FILE_START, UPLOAD_FILE_WRITE, UPLOAD_FILE_END, UPLOAD_FILE_ABORTED }; @@ -84,9 +85,9 @@ public: void requestAuthentication(HTTPAuthMethod mode = BASIC_AUTH, const char* realm = NULL, const String& authFailMsg = String("") ); typedef std::function THandlerFunction; - void on(const String &uri, THandlerFunction handler); - void on(const String &uri, HTTPMethod method, THandlerFunction fn); - void on(const String &uri, HTTPMethod method, THandlerFunction fn, THandlerFunction ufn); + void on(const Uri &uri, THandlerFunction handler); + void on(const Uri &uri, HTTPMethod method, THandlerFunction fn); + void on(const Uri &uri, HTTPMethod method, THandlerFunction fn, THandlerFunction ufn); void addHandler(RequestHandler* handler); void serveStatic(const char* uri, fs::FS& fs, const char* path, const char* cache_header = NULL ); void onNotFound(THandlerFunction fn); //called when handler is not assigned diff --git a/libraries/WebServer/src/detail/RequestHandlersImpl.h b/libraries/WebServer/src/detail/RequestHandlersImpl.h index babca4e2..153fbce6 100644 --- a/libraries/WebServer/src/detail/RequestHandlersImpl.h +++ b/libraries/WebServer/src/detail/RequestHandlersImpl.h @@ -4,66 +4,30 @@ #include "RequestHandler.h" #include "mimetable.h" #include "WString.h" +#include "Uri.h" using namespace mime; class FunctionRequestHandler : public RequestHandler { public: - FunctionRequestHandler(WebServer::THandlerFunction fn, WebServer::THandlerFunction ufn, const String &uri, HTTPMethod method) + FunctionRequestHandler(WebServer::THandlerFunction fn, WebServer::THandlerFunction ufn, const Uri &uri, HTTPMethod method) : _fn(fn) , _ufn(ufn) - , _uri(uri) + , _uri(uri.clone()) , _method(method) { - int numParams = 0, start = 0; - do { - start = _uri.indexOf("{}", start); - if (start > 0) { - numParams++; - start += 2; - } - } while (start > 0); - pathArgs.resize(numParams); + _uri->initPathArgs(pathArgs); + } + + ~FunctionRequestHandler() { + delete _uri; } bool canHandle(HTTPMethod requestMethod, String requestUri) override { if (_method != HTTP_ANY && _method != requestMethod) return false; - if (_uri == requestUri) - return true; - - size_t uriLength = _uri.length(); - unsigned int pathArgIndex = 0; - unsigned int requestUriIndex = 0; - for (unsigned int i = 0; i < uriLength; i++, requestUriIndex++) { - char uriChar = _uri[i]; - char requestUriChar = requestUri[requestUriIndex]; - - if (uriChar == requestUriChar) - continue; - if (uriChar != '{') - return false; - - i += 2; // index of char after '}' - if (i >= uriLength) { - // there is no char after '}' - pathArgs[pathArgIndex] = requestUri.substring(requestUriIndex); - return pathArgs[pathArgIndex].indexOf("/") == -1; // path argument may not contain a '/' - } - else - { - char charEnd = _uri[i]; - int uriIndex = requestUri.indexOf(charEnd, requestUriIndex); - if (uriIndex < 0) - return false; - pathArgs[pathArgIndex] = requestUri.substring(requestUriIndex, uriIndex); - requestUriIndex = (unsigned int) uriIndex; - } - pathArgIndex++; - } - - return requestUriIndex >= requestUri.length(); + return _uri->canHandle(requestUri, pathArgs); } bool canUpload(String requestUri) override { @@ -92,7 +56,7 @@ public: protected: WebServer::THandlerFunction _fn; WebServer::THandlerFunction _ufn; - String _uri; + Uri *_uri; HTTPMethod _method; }; diff --git a/libraries/WebServer/src/uri/UriBraces.h b/libraries/WebServer/src/uri/UriBraces.h new file mode 100644 index 00000000..4a6049b3 --- /dev/null +++ b/libraries/WebServer/src/uri/UriBraces.h @@ -0,0 +1,66 @@ +#ifndef URI_BRACES_H +#define URI_BRACES_H + +#include "Uri.h" + +class UriBraces : public Uri { + + public: + explicit UriBraces(const char *uri) : Uri(uri) {}; + explicit UriBraces(const String &uri) : Uri(uri) {}; + + Uri* clone() const override final { + return new UriBraces(_uri); + }; + + void initPathArgs(std::vector &pathArgs) override final { + int numParams = 0, start = 0; + do { + start = _uri.indexOf("{}", start); + if (start > 0) { + numParams++; + start += 2; + } + } while (start > 0); + pathArgs.resize(numParams); + } + + bool canHandle(const String &requestUri, std::vector &pathArgs) override final { + if (Uri::canHandle(requestUri, pathArgs)) + return true; + + size_t uriLength = _uri.length(); + unsigned int pathArgIndex = 0; + unsigned int requestUriIndex = 0; + for (unsigned int i = 0; i < uriLength; i++, requestUriIndex++) { + char uriChar = _uri[i]; + char requestUriChar = requestUri[requestUriIndex]; + + if (uriChar == requestUriChar) + continue; + if (uriChar != '{') + return false; + + i += 2; // index of char after '}' + if (i >= uriLength) { + // there is no char after '}' + pathArgs[pathArgIndex] = requestUri.substring(requestUriIndex); + return pathArgs[pathArgIndex].indexOf("/") == -1; // path argument may not contain a '/' + } + else + { + char charEnd = _uri[i]; + int uriIndex = requestUri.indexOf(charEnd, requestUriIndex); + if (uriIndex < 0) + return false; + pathArgs[pathArgIndex] = requestUri.substring(requestUriIndex, uriIndex); + requestUriIndex = (unsigned int) uriIndex; + } + pathArgIndex++; + } + + return requestUriIndex >= requestUri.length(); + } +}; + +#endif diff --git a/libraries/WebServer/src/uri/UriGlob.h b/libraries/WebServer/src/uri/UriGlob.h new file mode 100644 index 00000000..1e222cba --- /dev/null +++ b/libraries/WebServer/src/uri/UriGlob.h @@ -0,0 +1,22 @@ +#ifndef URI_GLOB_H +#define URI_GLOB_H + +#include "Uri.h" +#include + +class UriGlob : public Uri { + + public: + explicit UriGlob(const char *uri) : Uri(uri) {}; + explicit UriGlob(const String &uri) : Uri(uri) {}; + + Uri* clone() const override final { + return new UriGlob(_uri); + }; + + bool canHandle(const String &requestUri, __attribute__((unused)) std::vector &pathArgs) override final { + return fnmatch(_uri.c_str(), requestUri.c_str(), 0) == 0; + } +}; + +#endif diff --git a/libraries/WebServer/src/uri/UriRegex.h b/libraries/WebServer/src/uri/UriRegex.h new file mode 100644 index 00000000..05702017 --- /dev/null +++ b/libraries/WebServer/src/uri/UriRegex.h @@ -0,0 +1,44 @@ +#ifndef URI_REGEX_H +#define URI_REGEX_H + +#include "Uri.h" +#include + +class UriRegex : public Uri { + + public: + explicit UriRegex(const char *uri) : Uri(uri) {}; + explicit UriRegex(const String &uri) : Uri(uri) {}; + + Uri* clone() const override final { + return new UriRegex(_uri); + }; + + void initPathArgs(std::vector &pathArgs) override final { + std::regex rgx((_uri + "|").c_str()); + std::smatch matches; + std::string s{""}; + std::regex_search(s, matches, rgx); + pathArgs.resize(matches.size() - 1); + } + + bool canHandle(const String &requestUri, std::vector &pathArgs) override final { + if (Uri::canHandle(requestUri, pathArgs)) + return true; + + unsigned int pathArgIndex = 0; + std::regex rgx(_uri.c_str()); + std::smatch matches; + std::string s(requestUri.c_str()); + if (std::regex_search(s, matches, rgx)) { + for (size_t i = 1; i < matches.size(); ++i) { // skip first + pathArgs[pathArgIndex] = String(matches[i].str().c_str()); + pathArgIndex++; + } + return true; + } + return false; + } +}; + +#endif