1
0
mirror of https://github.com/esp8266/Arduino.git synced 2025-04-19 23:22:16 +03:00

Add Uri with support for regexUri and globUri (#6696)

* Add path args

* Add example

* Update code format

* Add missing include

* Fix codestyle and unsigned int

* fix unsigned int

* Remove tabs

* use vector<>.resize

* rename j to requestUriIndex

* using assert checking the path argument index

* Add missing include "assert.h"

* The order no longer matters.
Path arguments may not contain the value '/'
Updated the example

* make pathArg return a const

* Update PathArgServer.ino

fix trailing space

* const String&

* Add regex support

* Fix to match templating

* Add Uri with support for staticUri, regexUri and globUri

* Update example

* Add deconstructor to remove _uri pointer

* Add newline to end of files

* Suppress gcc warnings (unused params)

* Replace regex with regex.h

* Use the standard STASSID/PSK settings for example

Make the example match the existing examples which allow setting
the SSID/PSK in the local platform.txt file.

* Use 115.2Kbaud for example, match others

Co-authored-by: david gauchard <gauchard@laas.fr>
Co-authored-by: Earle F. Philhower, III <earlephilhower@yahoo.com>
This commit is contained in:
Bob 2020-02-22 20:51:47 +01:00 committed by GitHub
parent a40663b65f
commit 4eca62cb53
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 251 additions and 13 deletions

View File

@ -0,0 +1,61 @@
#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ESP8266WebServer.h>
#include <ESP8266mDNS.h>
#include <uri/UriBraces.h>
#include <uri/UriRegex.h>
#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();
}

View File

@ -263,17 +263,17 @@ void ESP8266WebServerTemplate<ServerType>::requestAuthentication(HTTPAuthMethod
} }
template <typename ServerType> template <typename ServerType>
void ESP8266WebServerTemplate<ServerType>::on(const String &uri, ESP8266WebServerTemplate<ServerType>::THandlerFunction handler) { void ESP8266WebServerTemplate<ServerType>::on(const Uri &uri, ESP8266WebServerTemplate<ServerType>::THandlerFunction handler) {
on(uri, HTTP_ANY, handler); on(uri, HTTP_ANY, handler);
} }
template <typename ServerType> template <typename ServerType>
void ESP8266WebServerTemplate<ServerType>::on(const String &uri, HTTPMethod method, ESP8266WebServerTemplate<ServerType>::THandlerFunction fn) { void ESP8266WebServerTemplate<ServerType>::on(const Uri &uri, HTTPMethod method, ESP8266WebServerTemplate<ServerType>::THandlerFunction fn) {
on(uri, method, fn, _fileUploadHandler); on(uri, method, fn, _fileUploadHandler);
} }
template <typename ServerType> template <typename ServerType>
void ESP8266WebServerTemplate<ServerType>::on(const String &uri, HTTPMethod method, ESP8266WebServerTemplate<ServerType>::THandlerFunction fn, ESP8266WebServerTemplate<ServerType>::THandlerFunction ufn) { void ESP8266WebServerTemplate<ServerType>::on(const Uri &uri, HTTPMethod method, ESP8266WebServerTemplate<ServerType>::THandlerFunction fn, ESP8266WebServerTemplate<ServerType>::THandlerFunction ufn) {
_addRequestHandler(new FunctionRequestHandler<ServerType>(fn, ufn, uri, method)); _addRequestHandler(new FunctionRequestHandler<ServerType>(fn, ufn, uri, method));
} }
@ -544,6 +544,12 @@ void ESP8266WebServerTemplate<ServerType>::_streamFileCore(const size_t fileSize
send(200, contentType, emptyString); send(200, contentType, emptyString);
} }
template <typename ServerType>
const String& ESP8266WebServerTemplate<ServerType>::pathArg(unsigned int i) const {
if (_currentHandler != nullptr)
return _currentHandler->pathArg(i);
return emptyString;
}
template <typename ServerType> template <typename ServerType>
const String& ESP8266WebServerTemplate<ServerType>::arg(const String& name) const { const String& ESP8266WebServerTemplate<ServerType>::arg(const String& name) const {

View File

@ -29,6 +29,7 @@
#include <ESP8266WiFi.h> #include <ESP8266WiFi.h>
#include <FS.h> #include <FS.h>
#include "detail/mimetable.h" #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 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, 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("") ); void requestAuthentication(HTTPAuthMethod mode = BASIC_AUTH, const char* realm = NULL, const String& authFailMsg = String("") );
typedef std::function<void(void)> THandlerFunction; typedef std::function<void(void)> THandlerFunction;
void on(const String &uri, THandlerFunction handler); void on(const Uri &uri, THandlerFunction handler);
void on(const String &uri, HTTPMethod method, THandlerFunction fn); void on(const Uri &uri, HTTPMethod method, THandlerFunction fn);
void on(const String &uri, HTTPMethod method, THandlerFunction fn, THandlerFunction ufn); void on(const Uri &uri, HTTPMethod method, THandlerFunction fn, THandlerFunction ufn);
void addHandler(RequestHandlerType* handler); void addHandler(RequestHandlerType* handler);
void serveStatic(const char* uri, fs::FS& fs, const char* path, const char* cache_header = NULL ); 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 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 // Allows setting server options (i.e. SSL keys) by the instantiator
ServerType &getServer() { return _server; } 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(const String& name) const; // get request argument value by name
const String& arg(int i) const; // get request argument value by number const String& arg(int i) const; // get request argument value by number
const String& argName(int i) const; // get request argument name by number const String& argName(int i) const; // get request argument name by number

View File

@ -0,0 +1,26 @@
#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 bool canHandle(const String &requestUri, __attribute__((unused)) std::vector<String> &pathArgs) {
return _uri == requestUri;
}
};
#endif

View File

@ -2,6 +2,8 @@
#define REQUESTHANDLER_H #define REQUESTHANDLER_H
#include <ESP8266WebServer.h> #include <ESP8266WebServer.h>
#include <vector>
#include <assert.h>
template<typename ServerType> template<typename ServerType>
class RequestHandler { class RequestHandler {
@ -18,6 +20,15 @@ public:
private: private:
RequestHandler<ServerType>* _next = nullptr; RequestHandler<ServerType>* _next = nullptr;
protected:
std::vector<String> pathArgs;
public:
const String& pathArg(unsigned int i) {
assert(i < pathArgs.size());
return pathArgs[i];
}
}; };
#endif //REQUESTHANDLER_H #endif //REQUESTHANDLER_H

View File

@ -5,6 +5,7 @@
#include "RequestHandler.h" #include "RequestHandler.h"
#include "mimetable.h" #include "mimetable.h"
#include "WString.h" #include "WString.h"
#include "Uri.h"
using namespace mime; using namespace mime;
@ -12,22 +13,23 @@ template<typename ServerType>
class FunctionRequestHandler : public RequestHandler<ServerType> { class FunctionRequestHandler : public RequestHandler<ServerType> {
using WebServerType = ESP8266WebServerTemplate<ServerType>; using WebServerType = ESP8266WebServerTemplate<ServerType>;
public: 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) : _fn(fn)
, _ufn(ufn) , _ufn(ufn)
, _uri(uri) , _uri(uri.clone())
, _method(method) , _method(method)
{ {
} }
~FunctionRequestHandler() {
delete _uri;
}
bool canHandle(HTTPMethod requestMethod, String requestUri) override { bool canHandle(HTTPMethod requestMethod, String requestUri) override {
if (_method != HTTP_ANY && _method != requestMethod) if (_method != HTTP_ANY && _method != requestMethod)
return false; return false;
if (requestUri != _uri) return _uri->canHandle(requestUri, RequestHandler<ServerType>::pathArgs);
return false;
return true;
} }
bool canUpload(String requestUri) override { bool canUpload(String requestUri) override {
@ -56,7 +58,7 @@ public:
protected: protected:
typename WebServerType::THandlerFunction _fn; typename WebServerType::THandlerFunction _fn;
typename WebServerType::THandlerFunction _ufn; typename WebServerType::THandlerFunction _ufn;
String _uri; Uri *_uri;
HTTPMethod _method; HTTPMethod _method;
}; };

View File

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

View File

@ -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

View File

@ -0,0 +1,54 @@
#ifndef URI_REGEX_H
#define URI_REGEX_H
#include "Uri.h"
#include <regex.h>
#include <assert.h>
#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<String> &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