diff --git a/libraries/ESP8266WebServer/examples/PathArgServer/PathArgServer.ino b/libraries/ESP8266WebServer/examples/PathArgServer/PathArgServer.ino new file mode 100644 index 000000000..c2c7f5688 --- /dev/null +++ b/libraries/ESP8266WebServer/examples/PathArgServer/PathArgServer.ino @@ -0,0 +1,61 @@ +#include +#include +#include +#include + +#include +#include + +#ifndef STASSID +#define STASSID "your-ssid" +#define STAPSK "your-password" +#endif + +const char *ssid = STASSID; +const char *password = STAPSK; + +ESP8266WebServer server(80); + +void setup(void) { + Serial.begin(115200); + WiFi.mode(WIFI_STA); + WiFi.begin(ssid, password); + Serial.println(""); + + // Wait for connection + while (WiFi.status() != WL_CONNECTED) { + delay(500); + Serial.print("."); + } + Serial.println(""); + Serial.print("Connected to "); + Serial.println(ssid); + Serial.print("IP address: "); + Serial.println(WiFi.localIP()); + + if (MDNS.begin("esp8266")) { + Serial.println("MDNS responder started"); + } + + server.on("/", []() { + server.send(200, "text/plain", "hello from esp8266!"); + }); + + server.on(UriBraces("/users/{}"), []() { + String user = server.pathArg(0); + server.send(200, "text/plain", "User: '" + user + "'"); + }); + + 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 + "'"); + }); + + server.begin(); + Serial.println("HTTP server started"); +} + +void loop(void) { + server.handleClient(); +} diff --git a/libraries/ESP8266WebServer/src/ESP8266WebServer-impl.h b/libraries/ESP8266WebServer/src/ESP8266WebServer-impl.h index 440d9d9bb..f085a8afe 100644 --- a/libraries/ESP8266WebServer/src/ESP8266WebServer-impl.h +++ b/libraries/ESP8266WebServer/src/ESP8266WebServer-impl.h @@ -263,17 +263,17 @@ void ESP8266WebServerTemplate::requestAuthentication(HTTPAuthMethod } template -void ESP8266WebServerTemplate::on(const String &uri, ESP8266WebServerTemplate::THandlerFunction handler) { +void ESP8266WebServerTemplate::on(const Uri &uri, ESP8266WebServerTemplate::THandlerFunction handler) { on(uri, HTTP_ANY, handler); } template -void ESP8266WebServerTemplate::on(const String &uri, HTTPMethod method, ESP8266WebServerTemplate::THandlerFunction fn) { +void ESP8266WebServerTemplate::on(const Uri &uri, HTTPMethod method, ESP8266WebServerTemplate::THandlerFunction fn) { on(uri, method, fn, _fileUploadHandler); } template -void ESP8266WebServerTemplate::on(const String &uri, HTTPMethod method, ESP8266WebServerTemplate::THandlerFunction fn, ESP8266WebServerTemplate::THandlerFunction ufn) { +void ESP8266WebServerTemplate::on(const Uri &uri, HTTPMethod method, ESP8266WebServerTemplate::THandlerFunction fn, ESP8266WebServerTemplate::THandlerFunction ufn) { _addRequestHandler(new FunctionRequestHandler(fn, ufn, uri, method)); } @@ -544,6 +544,12 @@ void ESP8266WebServerTemplate::_streamFileCore(const size_t fileSize send(200, contentType, emptyString); } +template +const String& ESP8266WebServerTemplate::pathArg(unsigned int i) const { + if (_currentHandler != nullptr) + return _currentHandler->pathArg(i); + return emptyString; +} template const String& ESP8266WebServerTemplate::arg(const String& name) const { diff --git a/libraries/ESP8266WebServer/src/ESP8266WebServer.h b/libraries/ESP8266WebServer/src/ESP8266WebServer.h index 2366ea93a..eefcba2ba 100644 --- a/libraries/ESP8266WebServer/src/ESP8266WebServer.h +++ b/libraries/ESP8266WebServer/src/ESP8266WebServer.h @@ -29,6 +29,7 @@ #include #include #include "detail/mimetable.h" +#include "Uri.h" enum HTTPMethod { HTTP_ANY, HTTP_GET, HTTP_HEAD, HTTP_POST, HTTP_PUT, HTTP_PATCH, HTTP_DELETE, HTTP_OPTIONS }; enum HTTPUploadStatus { UPLOAD_FILE_START, UPLOAD_FILE_WRITE, UPLOAD_FILE_END, @@ -91,9 +92,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(RequestHandlerType* 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 @@ -107,6 +108,7 @@ public: // Allows setting server options (i.e. SSL keys) by the instantiator ServerType &getServer() { return _server; } + const String& pathArg(unsigned int i) const; // get request path argument by number const String& arg(const String& name) const; // get request argument value by name const String& arg(int i) const; // get request argument value by number const String& argName(int i) const; // get request argument name by number diff --git a/libraries/ESP8266WebServer/src/Uri.h b/libraries/ESP8266WebServer/src/Uri.h new file mode 100644 index 000000000..cb9b68c05 --- /dev/null +++ b/libraries/ESP8266WebServer/src/Uri.h @@ -0,0 +1,26 @@ +#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 bool canHandle(const String &requestUri, __attribute__((unused)) std::vector &pathArgs) { + return _uri == requestUri; + } +}; + +#endif diff --git a/libraries/ESP8266WebServer/src/detail/RequestHandler.h b/libraries/ESP8266WebServer/src/detail/RequestHandler.h index db840af2a..9202f623b 100644 --- a/libraries/ESP8266WebServer/src/detail/RequestHandler.h +++ b/libraries/ESP8266WebServer/src/detail/RequestHandler.h @@ -2,6 +2,8 @@ #define REQUESTHANDLER_H #include +#include +#include template class RequestHandler { @@ -18,6 +20,15 @@ public: private: RequestHandler* _next = nullptr; + +protected: + std::vector pathArgs; + +public: + const String& pathArg(unsigned int i) { + assert(i < pathArgs.size()); + return pathArgs[i]; + } }; #endif //REQUESTHANDLER_H diff --git a/libraries/ESP8266WebServer/src/detail/RequestHandlersImpl.h b/libraries/ESP8266WebServer/src/detail/RequestHandlersImpl.h index 1c44f7833..e19639317 100644 --- a/libraries/ESP8266WebServer/src/detail/RequestHandlersImpl.h +++ b/libraries/ESP8266WebServer/src/detail/RequestHandlersImpl.h @@ -5,6 +5,7 @@ #include "RequestHandler.h" #include "mimetable.h" #include "WString.h" +#include "Uri.h" using namespace mime; @@ -12,22 +13,23 @@ template class FunctionRequestHandler : public RequestHandler { using WebServerType = ESP8266WebServerTemplate; public: - FunctionRequestHandler(typename WebServerType::THandlerFunction fn, typename WebServerType::THandlerFunction ufn, const String &uri, HTTPMethod method) + FunctionRequestHandler(typename WebServerType::THandlerFunction fn, typename WebServerType::THandlerFunction ufn, const Uri &uri, HTTPMethod method) : _fn(fn) , _ufn(ufn) - , _uri(uri) + , _uri(uri.clone()) , _method(method) { } + ~FunctionRequestHandler() { + delete _uri; + } + bool canHandle(HTTPMethod requestMethod, String requestUri) override { if (_method != HTTP_ANY && _method != requestMethod) return false; - if (requestUri != _uri) - return false; - - return true; + return _uri->canHandle(requestUri, RequestHandler::pathArgs); } bool canUpload(String requestUri) override { @@ -56,7 +58,7 @@ public: protected: typename WebServerType::THandlerFunction _fn; typename WebServerType::THandlerFunction _ufn; - String _uri; + Uri *_uri; HTTPMethod _method; }; diff --git a/libraries/ESP8266WebServer/src/uri/UriBraces.h b/libraries/ESP8266WebServer/src/uri/UriBraces.h new file mode 100644 index 000000000..29652efc7 --- /dev/null +++ b/libraries/ESP8266WebServer/src/uri/UriBraces.h @@ -0,0 +1,54 @@ +#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); + }; + + bool canHandle(const String &requestUri, std::vector &pathArgs) override final { + if (Uri::canHandle(requestUri, pathArgs)) + return true; + + pathArgs.clear(); + + size_t uriLength = _uri.length(); + 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.push_back(requestUri.substring(requestUriIndex)); + return pathArgs.back().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.push_back(requestUri.substring(requestUriIndex, uriIndex)); + requestUriIndex = (unsigned int) uriIndex; + } + } + + return requestUriIndex >= requestUri.length(); + } +}; + +#endif diff --git a/libraries/ESP8266WebServer/src/uri/UriGlob.h b/libraries/ESP8266WebServer/src/uri/UriGlob.h new file mode 100644 index 000000000..1e222cbab --- /dev/null +++ b/libraries/ESP8266WebServer/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/ESP8266WebServer/src/uri/UriRegex.h b/libraries/ESP8266WebServer/src/uri/UriRegex.h new file mode 100644 index 000000000..eef1b516d --- /dev/null +++ b/libraries/ESP8266WebServer/src/uri/UriRegex.h @@ -0,0 +1,54 @@ +#ifndef URI_REGEX_H +#define URI_REGEX_H + +#include "Uri.h" +#include +#include + +#ifndef REGEX_MAX_GROUPS +#define REGEX_MAX_GROUPS 10 +#endif + +class UriRegex : public Uri { + + private: + regex_t _regexCompiled; + + public: + explicit UriRegex(const char *uri) : Uri(uri) { + assert(regcomp(&_regexCompiled, uri, REG_EXTENDED) == 0); + }; + explicit UriRegex(const String &uri) : UriRegex(uri.c_str()) {}; + + ~UriRegex() { + regfree(&_regexCompiled); + } + + Uri* clone() const override final { + return new UriRegex(_uri); + }; + + bool canHandle(const String &requestUri, std::vector &pathArgs) override final { + if (Uri::canHandle(requestUri, pathArgs)) + return true; + + regmatch_t groupArray[REGEX_MAX_GROUPS]; + if (regexec(&_regexCompiled, requestUri.c_str(), REGEX_MAX_GROUPS, groupArray, 0) == 0) { + // matches + pathArgs.clear(); + + unsigned int g = 1; + for (; g < REGEX_MAX_GROUPS; g++) { + if (groupArray[g].rm_so == (long int)-1) + break; // No more groups + + pathArgs.push_back(requestUri.substring(groupArray[g].rm_so, groupArray[g].rm_eo)); + } + + return true; + } + return false; + } +}; + +#endif