Skip to content

Add Uri with support for regexUri and globUri #3441

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Jan 20, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions libraries/WebServer/examples/PathArgServer/PathArgServer.ino
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
#include <WebServer.h>
#include <ESPmDNS.h>

#include <uri/UriBraces.h>
#include <uri/UriRegex.h>

const char *ssid = "........";
const char *password = "........";

Expand Down Expand Up @@ -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 + "'");
Expand Down
28 changes: 28 additions & 0 deletions libraries/WebServer/src/Uri.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#ifndef URI_H
#define URI_H

#include <Arduino.h>
#include <vector>

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<String> &pathArgs) {}

virtual bool canHandle(const String &requestUri, __attribute__((unused)) std::vector<String> &pathArgs) {
return _uri == requestUri;
}
};

#endif
6 changes: 3 additions & 3 deletions libraries/WebServer/src/WebServer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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));
}

Expand Down
7 changes: 4 additions & 3 deletions libraries/WebServer/src/WebServer.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
#include <memory>
#include <WiFi.h>
#include "HTTP_Method.h"
#include "Uri.h"

enum HTTPUploadStatus { UPLOAD_FILE_START, UPLOAD_FILE_WRITE, UPLOAD_FILE_END,
UPLOAD_FILE_ABORTED };
Expand Down Expand Up @@ -84,9 +85,9 @@ class WebServer
void requestAuthentication(HTTPAuthMethod mode = BASIC_AUTH, const char* realm = NULL, const String& authFailMsg = String("") );

typedef std::function<void(void)> 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
Expand Down
56 changes: 10 additions & 46 deletions libraries/WebServer/src/detail/RequestHandlersImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -92,7 +56,7 @@ class FunctionRequestHandler : public RequestHandler {
protected:
WebServer::THandlerFunction _fn;
WebServer::THandlerFunction _ufn;
String _uri;
Uri *_uri;
HTTPMethod _method;
};

Expand Down
66 changes: 66 additions & 0 deletions libraries/WebServer/src/uri/UriBraces.h
Original file line number Diff line number Diff line change
@@ -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<String> &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<String> &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
22 changes: 22 additions & 0 deletions libraries/WebServer/src/uri/UriGlob.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#ifndef URI_GLOB_H
#define URI_GLOB_H

#include "Uri.h"
#include <fnmatch.h>

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<String> &pathArgs) override final {
return fnmatch(_uri.c_str(), requestUri.c_str(), 0) == 0;
}
};

#endif
44 changes: 44 additions & 0 deletions libraries/WebServer/src/uri/UriRegex.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#ifndef URI_REGEX_H
#define URI_REGEX_H

#include "Uri.h"
#include <regex>

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<String> &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<String> &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