Skip to content

Commit e64dffa

Browse files
authored
Merge pull request #4 from pvogt09/feature/functional-middleware
Allow std::functional for middleware callbacks.
2 parents 07f042b + 707b295 commit e64dffa

File tree

4 files changed

+282
-9
lines changed

4 files changed

+282
-9
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
/**
2+
* Example for the ESP32 HTTP(S) Webserver
3+
*
4+
* IMPORTANT NOTE:
5+
* To run this script, your need to
6+
* 1) Enter your WiFi SSID and PSK below this comment
7+
* 2) Make sure to have certificate data available. You will find a
8+
* shell script and instructions to do so in the library folder
9+
* under extras/
10+
*
11+
* This script will install an HTTPS Server on your ESP32 with the following
12+
* functionalities:
13+
* - Do the same as the Middleware.ino example
14+
* - Show how to add and remove additional middlewares with different datatypes
15+
*/
16+
17+
// TODO: Configure your WiFi here
18+
#define WIFI_SSID "<your ssid goes here>"
19+
#define WIFI_PSK "<your pre-shared key goes here>"
20+
21+
// Include certificate data (see note above)
22+
#include "cert.h"
23+
#include "private_key.h"
24+
25+
// We will use wifi
26+
#include <WiFi.h>
27+
28+
// For the middleware
29+
#include <functional>
30+
31+
// Includes for the server
32+
#include <HTTPSServer.hpp>
33+
#include <SSLCert.hpp>
34+
#include <HTTPRequest.hpp>
35+
#include <HTTPResponse.hpp>
36+
#include <HTTPMiddlewareFunction.hpp>
37+
38+
// The HTTPS Server comes in a separate namespace. For easier use, include it here.
39+
using namespace httpsserver;
40+
41+
// Create an SSL certificate object from the files included above
42+
SSLCert cert = SSLCert(
43+
example_crt_DER, example_crt_DER_len,
44+
example_key_DER, example_key_DER_len
45+
);
46+
47+
// Create an SSL-enabled server that uses the certificate
48+
// The contstructor takes some more parameters, but we go for default values here.
49+
HTTPSServer secureServer = HTTPSServer(&cert);
50+
51+
// Declare some handler functions for the various URLs on the server
52+
void handleRoot(HTTPRequest * req, HTTPResponse * res);
53+
void handle404(HTTPRequest * req, HTTPResponse * res);
54+
55+
// Declare a middleware function.
56+
// Parameters:
57+
// req: Request data, can be used to access URL, HTTP Method, Headers, ...
58+
// res: Response data, can be used to access HTTP Status, Headers, ...
59+
// next: This function is used to pass control down the chain. If you have done your work
60+
// with the request object, you may decide if you want to process the request.
61+
// If you do so, you call the next() function, and the next middleware function (if
62+
// there is any) or the actual requestHandler will be called.
63+
// If you want to skip the request, you do not call next, and set for example status
64+
// code 403 on the response to show that the user is not allowed to access a specific
65+
// resource.
66+
// The Authentication examples provides more details on this.
67+
void middlewareLogging(HTTPRequest * req, HTTPResponse * res, std::function<void()> next);
68+
69+
void middlewareRawPointer(HTTPRequest * req, HTTPResponse * res, std::function<void()> next);
70+
71+
void setup() {
72+
// For logging
73+
Serial.begin(115200);
74+
75+
// Connect to WiFi
76+
Serial.println("Setting up WiFi");
77+
WiFi.begin(WIFI_SSID, WIFI_PSK);
78+
while (WiFi.status() != WL_CONNECTED) {
79+
Serial.print(".");
80+
delay(500);
81+
}
82+
Serial.print("Connected. IP=");
83+
Serial.println(WiFi.localIP());
84+
85+
// For every resource available on the server, we need to create a ResourceNode
86+
// The ResourceNode links URL and HTTP method to a handler function
87+
ResourceNode * nodeRoot = new ResourceNode("/", "GET", &handleRoot);
88+
ResourceNode * node404 = new ResourceNode("", "GET", &handle404);
89+
90+
// Add the root node to the server
91+
secureServer.registerNode(nodeRoot);
92+
93+
// Add the 404 not found node to the server.
94+
// The path is ignored for the default node.
95+
secureServer.setDefaultNode(node404);
96+
97+
// Add the middleware. The function will be called globally for every request
98+
// Note: The functions are called in the order they are added to the server.
99+
// Also, if you want a middleware to handle only specific requests, you can check
100+
// the URL within the middleware function.
101+
secureServer.addMiddleware(middlewareLogging);
102+
secureServer.addMiddleware(middlewareRawPointer);
103+
// add a std::function middleware function
104+
const HTTPSMiddlewareFunction std_function{middlewareLogging};
105+
secureServer.addMiddleware(std_function);
106+
const auto outside_variable = 10;
107+
// Add a lambda middleware function
108+
const auto lamda = [outside_variable](HTTPRequest * req, HTTPResponse * res, std::function<void()> next) {
109+
Serial.print("Middleware Lambda with outside variable ");
110+
Serial.println(outside_variable);
111+
};
112+
secureServer.addMiddleware(lamda);
113+
114+
Serial.println("Removing middlewares...");
115+
// Remove the raw function pointer middleware
116+
secureServer.removeMiddleware(middlewareRawPointer);
117+
// Remove the std::function middleware
118+
secureServer.removeMiddleware(std_function);
119+
// Remove the lambda middleware
120+
secureServer.removeMiddleware(lamda);
121+
122+
Serial.println("Starting server...");
123+
secureServer.start();
124+
if (secureServer.isRunning()) {
125+
Serial.println("Server ready.");
126+
}
127+
}
128+
129+
void loop() {
130+
// This call will let the server do its work
131+
secureServer.loop();
132+
133+
// Other code would go here...
134+
delay(1);
135+
}
136+
137+
// We want to log the following information for every request:
138+
// - Response Status
139+
// - Request Method
140+
// - Request String (URL + Parameters)
141+
void middlewareLogging(HTTPRequest * req, HTTPResponse * res, std::function<void()> next) {
142+
// We want to print the response status, so we need to call next() first.
143+
next();
144+
// After the call, the status is (hopefully) set by the handler function, so we can
145+
// access it for logging.
146+
Serial.printf("middlewareLogging(): %3d\t%s\t\t%s\n",
147+
// Status code (like: 200)
148+
res->getStatusCode(),
149+
// Method used for the request (like: GET)
150+
req->getMethod().c_str(),
151+
// Request string (like /index.html)
152+
req->getRequestString().c_str());
153+
}
154+
155+
void middlewareRawPointer(HTTPRequest * req, HTTPResponse * res, std::function<void()> next) {
156+
Serial.print("Middleware Raw Pointer");
157+
next();
158+
}
159+
160+
// For details on the implementation of the hanlder functions, refer to the Static-Page example.
161+
void handleRoot(HTTPRequest * req, HTTPResponse * res) {
162+
res->setHeader("Content-Type", "text/html");
163+
res->println("<!DOCTYPE html>");
164+
res->println("<html>");
165+
res->println("<head><title>Hello World!</title></head>");
166+
res->println("<body>");
167+
res->println("<h1>Hello World!</h1>");
168+
res->print("<p>Your server is running for ");
169+
res->print((int)(millis()/1000), DEC);
170+
res->println(" seconds.</p>");
171+
res->println("</body>");
172+
res->println("</html>");
173+
}
174+
175+
void handle404(HTTPRequest * req, HTTPResponse * res) {
176+
req->discardRequestBody();
177+
res->setStatusCode(404);
178+
res->setStatusText("Not Found");
179+
res->setHeader("Content-Type", "text/html");
180+
res->println("<!DOCTYPE html>");
181+
res->println("<html>");
182+
res->println("<head><title>Not Found</title></head>");
183+
res->println("<body><h1>404 Not Found</h1><p>The requested resource was not found on this server.</p></body>");
184+
res->println("</html>");
185+
}

src/HTTPMiddlewareFunction.hpp

+2-1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
namespace httpsserver {
1111
class HTTPRequest;
12+
using HTTPSMiddlewareFunctionType = void(HTTPRequest * req, HTTPResponse * res, std::function<void()> next);
1213
/**
1314
* \brief A middleware function that can be registered at the server.
1415
*
@@ -21,6 +22,6 @@ namespace httpsserver {
2122
* handling in case of missing authentication. Don't forget to call next in case you want to access your
2223
* resources, though.
2324
*/
24-
typedef void (HTTPSMiddlewareFunction)(HTTPRequest * req, HTTPResponse * res, std::function<void()> next);
25+
typedef std::function<HTTPSMiddlewareFunctionType> HTTPSMiddlewareFunction;
2526
}
2627
#endif /* SRC_HTTPMIDDLEWAREFUNCTION_HPP_ */

src/ResourceResolver.cpp

+75-4
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,21 @@
22

33
namespace httpsserver {
44

5+
ResourceResolver::HTTPSMiddlewareFunctionCallback::HTTPSMiddlewareFunctionCallback(const HTTPSMiddlewareFunction callback, const HTTPSMiddlewareFunction* callback_std_function, const HTTPSMiddlewareFunctionType* callback_raw_pointer) : _callback(callback), _callback_std_function(callback_std_function), _callback_raw_pointer(callback_raw_pointer) {
6+
};
7+
8+
HTTPSMiddlewareFunction ResourceResolver::HTTPSMiddlewareFunctionCallback::getCallback() {
9+
return _callback;
10+
};
11+
12+
const HTTPSMiddlewareFunction* ResourceResolver::HTTPSMiddlewareFunctionCallback::getStdFunctionPointer() {
13+
return _callback_std_function;
14+
};
15+
16+
const HTTPSMiddlewareFunctionType* ResourceResolver::HTTPSMiddlewareFunctionCallback::getRawFunctionPointer() {
17+
return _callback_raw_pointer;
18+
};
19+
520
ResourceResolver::ResourceResolver() {
621
_nodes = new std::vector<HTTPNode *>();
722
_defaultNode = NULL;
@@ -160,15 +175,71 @@ void ResourceResolver::resolveNode(const std::string &method, const std::string
160175
}
161176
}
162177

163-
void ResourceResolver::addMiddleware(const HTTPSMiddlewareFunction * mwFunction) {
178+
void ResourceResolver::updateMiddlewareList() {
179+
_middleware.clear();
180+
_middleware.reserve(_middleware_callback.size());
181+
for (auto& callback : _middleware_callback) {
182+
_middleware.push_back(callback.getCallback());
183+
}
184+
}
185+
186+
void ResourceResolver::addMiddleware(const HTTPSMiddlewareFunction &mwFunction) {
187+
const HTTPSMiddlewareFunctionCallback callback{
188+
mwFunction,
189+
&mwFunction,
190+
nullptr
191+
};
164192
_middleware.push_back(mwFunction);
193+
_middleware_callback.push_back(callback);
165194
}
166195

167-
void ResourceResolver::removeMiddleware(const HTTPSMiddlewareFunction * mwFunction) {
168-
_middleware.erase(std::remove(_middleware.begin(), _middleware.end(), mwFunction), _middleware.end());
196+
void ResourceResolver::addMiddleware(void (*mwFunction)(HTTPRequest * req, HTTPResponse * res, std::function<void()> next)) {
197+
auto mwFunction_callback = HTTPSMiddlewareFunction(mwFunction);
198+
const HTTPSMiddlewareFunctionCallback callback{
199+
mwFunction_callback,
200+
&mwFunction_callback,
201+
mwFunction
202+
};
203+
_middleware.push_back(mwFunction_callback);
204+
_middleware_callback.push_back(callback);
205+
}
206+
207+
void ResourceResolver::removeMiddleware(const HTTPSMiddlewareFunction &mwFunction) {
208+
bool found = false;
209+
for (auto it = _middleware_callback.begin(); it != _middleware_callback.end();) {
210+
auto element = *it;
211+
const auto callback = element.getStdFunctionPointer();
212+
const auto callback_supplied = &mwFunction;
213+
if (callback != nullptr && callback == callback_supplied) {
214+
it = _middleware_callback.erase(it);
215+
found = true;
216+
} else {
217+
++it;
218+
}
219+
}
220+
if (found) {
221+
updateMiddlewareList();
222+
}
223+
}
224+
225+
void ResourceResolver::removeMiddleware(void (*mwFunction)(HTTPRequest * req, HTTPResponse * res, std::function<void()> next)) {
226+
bool found = false;
227+
for (auto it = _middleware_callback.begin(); it != _middleware_callback.end();) {
228+
auto element = *it;
229+
auto callback = element.getRawFunctionPointer();
230+
if (callback != nullptr && callback == mwFunction) {
231+
it = _middleware_callback.erase(it);
232+
found = true;
233+
} else {
234+
++it;
235+
}
236+
}
237+
if (found) {
238+
updateMiddlewareList();
239+
}
169240
}
170241

171-
const std::vector<HTTPSMiddlewareFunction*> ResourceResolver::getMiddleware() {
242+
const std::vector<HTTPSMiddlewareFunction> ResourceResolver::getMiddleware() {
172243
return _middleware;
173244
}
174245

src/ResourceResolver.hpp

+20-4
Original file line numberDiff line numberDiff line change
@@ -30,20 +30,36 @@ class ResourceResolver {
3030
void resolveNode(const std::string &method, const std::string &url, ResolvedResource &resolvedResource, HTTPNodeType nodeType);
3131

3232
/** Add a middleware function to the end of the middleware function chain. See HTTPSMiddlewareFunction.hpp for details. */
33-
void addMiddleware(const HTTPSMiddlewareFunction * mwFunction);
33+
void addMiddleware(const HTTPSMiddlewareFunction &mwFunction);
34+
void addMiddleware(void (*mwFunction)(HTTPRequest *req, HTTPResponse *res, std::function<void()> next));
3435
/** Remove a specific function from the middleware function chain. */
35-
void removeMiddleware(const HTTPSMiddlewareFunction * mwFunction);
36+
void removeMiddleware(const HTTPSMiddlewareFunction &mwFunction);
37+
void removeMiddleware(void (*mwFunction)(HTTPRequest * req, HTTPResponse * res, std::function<void()> next));
3638
/** Get the current middleware chain with a resource function at the end */
37-
const std::vector<HTTPSMiddlewareFunction*> getMiddleware();
39+
const std::vector<HTTPSMiddlewareFunction> getMiddleware();
3840

3941
private:
42+
class HTTPSMiddlewareFunctionCallback {
43+
private:
44+
HTTPSMiddlewareFunction _callback;
45+
const HTTPSMiddlewareFunction* _callback_std_function;
46+
const HTTPSMiddlewareFunctionType* _callback_raw_pointer;
47+
public:
48+
HTTPSMiddlewareFunctionCallback(HTTPSMiddlewareFunction callback, const HTTPSMiddlewareFunction* const callback_std_function, const HTTPSMiddlewareFunctionType* callback_raw_pointer);
49+
HTTPSMiddlewareFunction getCallback();
50+
const HTTPSMiddlewareFunction* getStdFunctionPointer();
51+
const HTTPSMiddlewareFunctionType* getRawFunctionPointer();
52+
};
4053

4154
// This vector holds all nodes (with callbacks) that are registered
4255
std::vector<HTTPNode*> * _nodes;
4356
HTTPNode * _defaultNode;
4457

4558
// Middleware functions, if any are registered. Will be called in order of the vector.
46-
std::vector<const HTTPSMiddlewareFunction*> _middleware;
59+
std::vector<HTTPSMiddlewareFunction> _middleware;
60+
std::vector<HTTPSMiddlewareFunctionCallback> _middleware_callback;
61+
62+
void updateMiddlewareList();
4763
};
4864

4965
} /* namespace httpsserver */

0 commit comments

Comments
 (0)