From 72613b5f6d896b72964344b24382c1b6fd36c9c1 Mon Sep 17 00:00:00 2001 From: Bob Mooij Date: Fri, 1 Nov 2019 15:51:56 +0100 Subject: [PATCH 1/4] Add Uri with support for staticUri, regexUri and globUri --- 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 +++++++++++++ 7 files changed, 177 insertions(+), 52 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/src/Uri.h b/libraries/WebServer/src/Uri.h new file mode 100644 index 00000000000..65d23441953 --- /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(std::vector &pathArgs) {} + + virtual bool canHandle(const String &requestUri, std::vector &pathArgs) { + return _uri == requestUri; + } +}; + +#endif diff --git a/libraries/WebServer/src/WebServer.cpp b/libraries/WebServer/src/WebServer.cpp index a510f109235..e686f2fcd8c 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 97aa032e1f4..a0d3a1c1cfe 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 @@ class WebServer 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 babca4e27e3..153fbce63d0 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 @@ class FunctionRequestHandler : public RequestHandler { 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 00000000000..189b480c43e --- /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 \ No newline at end of file diff --git a/libraries/WebServer/src/uri/UriGlob.h b/libraries/WebServer/src/uri/UriGlob.h new file mode 100644 index 00000000000..823f45e1e3c --- /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, std::vector &pathArgs) override final { + return fnmatch(_uri.c_str(), requestUri.c_str(), 0) == 0; + } +}; + +#endif \ No newline at end of file diff --git a/libraries/WebServer/src/uri/UriRegex.h b/libraries/WebServer/src/uri/UriRegex.h new file mode 100644 index 00000000000..34423d245f5 --- /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 \ No newline at end of file From d0e64a7bb73100458704bb17e74e9d38cd9cc47d Mon Sep 17 00:00:00 2001 From: Bob Mooij Date: Fri, 1 Nov 2019 20:35:53 +0100 Subject: [PATCH 2/4] Add newline to end of files --- libraries/WebServer/src/uri/UriBraces.h | 2 +- libraries/WebServer/src/uri/UriGlob.h | 2 +- libraries/WebServer/src/uri/UriRegex.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/libraries/WebServer/src/uri/UriBraces.h b/libraries/WebServer/src/uri/UriBraces.h index 189b480c43e..4a6049b3c46 100644 --- a/libraries/WebServer/src/uri/UriBraces.h +++ b/libraries/WebServer/src/uri/UriBraces.h @@ -63,4 +63,4 @@ class UriBraces : public Uri { } }; -#endif \ No newline at end of file +#endif diff --git a/libraries/WebServer/src/uri/UriGlob.h b/libraries/WebServer/src/uri/UriGlob.h index 823f45e1e3c..0730164c213 100644 --- a/libraries/WebServer/src/uri/UriGlob.h +++ b/libraries/WebServer/src/uri/UriGlob.h @@ -19,4 +19,4 @@ class UriGlob : public Uri { } }; -#endif \ No newline at end of file +#endif diff --git a/libraries/WebServer/src/uri/UriRegex.h b/libraries/WebServer/src/uri/UriRegex.h index 34423d245f5..05702017f4f 100644 --- a/libraries/WebServer/src/uri/UriRegex.h +++ b/libraries/WebServer/src/uri/UriRegex.h @@ -41,4 +41,4 @@ class UriRegex : public Uri { } }; -#endif \ No newline at end of file +#endif From f6bc95710cc6815346b28da66b11b489ed6d815c Mon Sep 17 00:00:00 2001 From: Bob Mooij Date: Fri, 1 Nov 2019 20:37:47 +0100 Subject: [PATCH 3/4] Update example --- .../WebServer/examples/PathArgServer/PathArgServer.ino | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/libraries/WebServer/examples/PathArgServer/PathArgServer.ino b/libraries/WebServer/examples/PathArgServer/PathArgServer.ino index 8f22f1bea92..6e120c3f801 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 + "'"); From 59ae9f58f3c4125a54dd8a3ceeff4211e64f8fa5 Mon Sep 17 00:00:00 2001 From: Bob Mooij Date: Thu, 7 Nov 2019 15:08:26 +0100 Subject: [PATCH 4/4] Suppress gcc warnings (unused params) --- libraries/WebServer/src/Uri.h | 4 ++-- libraries/WebServer/src/uri/UriGlob.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/libraries/WebServer/src/Uri.h b/libraries/WebServer/src/Uri.h index 65d23441953..83772b2348e 100644 --- a/libraries/WebServer/src/Uri.h +++ b/libraries/WebServer/src/Uri.h @@ -18,9 +18,9 @@ class Uri { return new Uri(_uri); }; - virtual void initPathArgs(std::vector &pathArgs) {} + virtual void initPathArgs(__attribute__((unused)) std::vector &pathArgs) {} - virtual bool canHandle(const String &requestUri, std::vector &pathArgs) { + virtual bool canHandle(const String &requestUri, __attribute__((unused)) std::vector &pathArgs) { return _uri == requestUri; } }; diff --git a/libraries/WebServer/src/uri/UriGlob.h b/libraries/WebServer/src/uri/UriGlob.h index 0730164c213..1e222cbabd1 100644 --- a/libraries/WebServer/src/uri/UriGlob.h +++ b/libraries/WebServer/src/uri/UriGlob.h @@ -14,7 +14,7 @@ class UriGlob : public Uri { return new UriGlob(_uri); }; - bool canHandle(const String &requestUri, std::vector &pathArgs) override final { + bool canHandle(const String &requestUri, __attribute__((unused)) std::vector &pathArgs) override final { return fnmatch(_uri.c_str(), requestUri.c_str(), 0) == 0; } };