Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit bc2dc4b

Browse filesBrowse files
committed
feat: Middleware support for WebServer
Moving default middlewares (cors, authc, logging) to examples
1 parent def319a commit bc2dc4b
Copy full SHA for bc2dc4b

File tree

Expand file treeCollapse file tree

9 files changed

+729
-85
lines changed
Filter options
Expand file treeCollapse file tree

9 files changed

+729
-85
lines changed
+54Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
#include <WiFi.h>
2+
#include <WebServer.h>
3+
#include <Middlewares.h>
4+
5+
// Your AP WiFi Credentials
6+
// ( This is the AP your ESP will broadcast )
7+
const char *ap_ssid = "ESP32_Demo";
8+
const char *ap_password = "";
9+
10+
WebServer server(80);
11+
12+
LoggingMiddleware logger(Serial);
13+
CorsMiddleware cors;
14+
AuthenticationMiddleware auth;
15+
16+
void setup(void) {
17+
Serial.begin(115200);
18+
WiFi.softAP(ap_ssid, ap_password);
19+
20+
Serial.print("IP address: ");
21+
Serial.println(WiFi.AP.localIP());
22+
23+
cors.origin("http://192.168.4.1");
24+
cors.methods("POST, GET, OPTIONS, DELETE");
25+
cors.headers("X-Custom-Header");
26+
cors.allowCredentials(false);
27+
cors.maxAge(600);
28+
29+
auth.authenticate("admin", "admin");
30+
31+
server
32+
.on(
33+
"/",
34+
[]() {
35+
server.send(200, "text/plain", "Home");
36+
}
37+
)
38+
.addMiddleware(&logger)
39+
.addMiddleware(&cors)
40+
.addMiddleware(&auth);
41+
42+
server.onNotFound([]() {
43+
server.send(404, "text/plain", "Page not found");
44+
});
45+
46+
server.collectAllHeaders();
47+
server.begin();
48+
Serial.println("HTTP server started");
49+
}
50+
51+
void loop(void) {
52+
server.handleClient();
53+
delay(2); //allow the cpu to switch to other tasks
54+
}
+208Lines changed: 208 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,208 @@
1+
#ifndef MIDDLEWARES_H
2+
#define MIDDLEWARES_H
3+
4+
#include <WebServer.h>
5+
#include <Stream.h>
6+
7+
#include <assert.h>
8+
9+
// curl-like logging middleware
10+
class LoggingMiddleware : public Middleware {
11+
private:
12+
Stream *_out;
13+
14+
public:
15+
explicit LoggingMiddleware(Stream &out) : _out(&out) {}
16+
17+
bool run(WebServer &server, Middleware::Callback next) override {
18+
_out->print(F("* Connection from "));
19+
_out->print(server.client().remoteIP().toString());
20+
_out->print(F(":"));
21+
_out->println(server.client().remotePort());
22+
23+
_out->print(F("< "));
24+
HTTPMethod method = server.method();
25+
if (method == HTTP_ANY) {
26+
_out->print(F("HTTP_ANY"));
27+
} else {
28+
_out->print(http_method_str((http_method)method));
29+
}
30+
_out->print(F(" "));
31+
_out->print(server.uri());
32+
_out->print(F(" "));
33+
_out->println(server.version());
34+
35+
int n = server.headers();
36+
for (int i = 0; i < n; i++) {
37+
String v = server.header(i);
38+
if (!v.isEmpty()) {
39+
// because these 2 are always there, eventually empty: "Authorization", "If-None-Match"
40+
_out->print(F("< "));
41+
_out->print(server.headerName(i));
42+
_out->print(F(": "));
43+
_out->println(server.header(i));
44+
}
45+
}
46+
47+
_out->println(F("<"));
48+
49+
bool ret = next();
50+
51+
if (ret) {
52+
_out->println(F("* Processed!"));
53+
54+
_out->print(F("> "));
55+
_out->print(F("HTTP/1."));
56+
_out->print(server.version());
57+
_out->print(F(" "));
58+
_out->print(server.responseCode());
59+
_out->print(F(" "));
60+
_out->println(WebServer::responseCodeToString(server.responseCode()));
61+
62+
n = server.responseHeaders();
63+
for (int i = 0; i < n; i++) {
64+
_out->print(F("> "));
65+
_out->print(server.responseHeaderName(i));
66+
_out->print(F(": "));
67+
_out->println(server.responseHeader(i));
68+
}
69+
70+
_out->println(F(">"));
71+
72+
} else {
73+
_out->println(F("* Not processed!"));
74+
}
75+
76+
return ret;
77+
}
78+
};
79+
80+
class AuthenticationMiddleware : public Middleware {
81+
private:
82+
// authenticate state
83+
// 0: not authenticated
84+
// 1: callback
85+
// 2: username/password
86+
// 3: sha1
87+
int _auth = 0;
88+
WebServer::THandlerFunctionAuthCheck _fn;
89+
String _username;
90+
String _password;
91+
String _sha1;
92+
93+
// authenticate request
94+
HTTPAuthMethod _mode = BASIC_AUTH;
95+
String _realm;
96+
String _authFailMsg;
97+
98+
public:
99+
AuthenticationMiddleware &authenticate(WebServer::THandlerFunctionAuthCheck fn) {
100+
assert(fn);
101+
_fn = fn;
102+
_auth = 1;
103+
return *this;
104+
}
105+
106+
AuthenticationMiddleware &authenticate(const char *username, const char *password) {
107+
if (strlen(username) == 0 || strlen(password) == 0) {
108+
_auth = 0;
109+
return *this;
110+
} else {
111+
_username = username;
112+
_password = password;
113+
_auth = 2;
114+
return *this;
115+
}
116+
}
117+
118+
AuthenticationMiddleware &authenticateBasicSHA1(const char *username, const char *sha1AsBase64orHex) {
119+
if (strlen(username) == 0 || strlen(sha1AsBase64orHex) == 0) {
120+
_auth = 0;
121+
return *this;
122+
}
123+
_username = username;
124+
_sha1 = sha1AsBase64orHex;
125+
_auth = 3;
126+
return *this;
127+
}
128+
129+
bool run(WebServer &server, Middleware::Callback next) override {
130+
switch (_auth) {
131+
case 1:
132+
if (server.authenticate(_fn)) {
133+
return next();
134+
} else {
135+
server.requestAuthentication(_mode, _realm.c_str(), _authFailMsg);
136+
return true;
137+
}
138+
139+
case 2:
140+
if (server.authenticate(_username.c_str(), _password.c_str())) {
141+
return next();
142+
} else {
143+
server.requestAuthentication(_mode, _realm.c_str(), _authFailMsg);
144+
return true;
145+
}
146+
147+
case 3:
148+
if (server.authenticate(_username.c_str(), _sha1.c_str())) {
149+
return next();
150+
} else {
151+
server.requestAuthentication(_mode, _realm.c_str(), _authFailMsg);
152+
return true;
153+
}
154+
155+
default: return next();
156+
}
157+
}
158+
};
159+
160+
class CorsMiddleware : public Middleware {
161+
private:
162+
String _origin = F("*");
163+
String _methods = F("*");
164+
String _headers = F("*");
165+
bool _credentials = true;
166+
uint32_t _maxAge = 86400;
167+
168+
public:
169+
CorsMiddleware &origin(const char *origin) {
170+
_origin = origin;
171+
return *this;
172+
}
173+
174+
CorsMiddleware &methods(const char *methods) {
175+
_methods = methods;
176+
return *this;
177+
}
178+
179+
CorsMiddleware &headers(const char *headers) {
180+
_headers = headers;
181+
return *this;
182+
}
183+
184+
CorsMiddleware &allowCredentials(bool credentials) {
185+
_credentials = credentials;
186+
return *this;
187+
}
188+
189+
CorsMiddleware &maxAge(uint32_t seconds) {
190+
_maxAge = seconds;
191+
return *this;
192+
}
193+
194+
bool run(WebServer &server, Middleware::Callback next) override {
195+
if (server.method() == HTTP_OPTIONS) {
196+
server.sendHeader(F("Access-Control-Allow-Origin"), _origin.c_str());
197+
server.sendHeader(F("Access-Control-Allow-Methods"), _methods.c_str());
198+
server.sendHeader(F("Access-Control-Allow-Headers"), _headers.c_str());
199+
server.sendHeader(F("Access-Control-Allow-Credentials"), _credentials ? F("true") : F("false"));
200+
server.sendHeader(F("Access-Control-Max-Age"), String(_maxAge).c_str());
201+
server.send(200);
202+
return true;
203+
}
204+
return next();
205+
}
206+
};
207+
208+
#endif
+109Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
This example shows how to load all request headers and use middleware.
2+
3+
### CORS Middleware
4+
5+
```bash
6+
❯ curl -i -X OPTIONS http://192.168.4.1
7+
HTTP/1.1 200 OK
8+
Content-Type: text/html
9+
Access-Control-Allow-Origin: http://192.168.4.1
10+
Access-Control-Allow-Methods: POST, GET, OPTIONS, DELETE
11+
Access-Control-Allow-Headers: X-Custom-Header
12+
Access-Control-Allow-Credentials: false
13+
Access-Control-Max-Age: 600
14+
Content-Length: 0
15+
Connection: close
16+
```
17+
18+
Output of logger middleware:
19+
20+
```
21+
* Connection from 192.168.4.2:57597
22+
< OPTIONS / HTTP/1.1
23+
< Host: 192.168.4.1
24+
< User-Agent: curl/8.9.1
25+
< Accept: */*
26+
<
27+
* Processed!
28+
> HTTP/1.HTTP/1.1 200 OK
29+
> Content-Type: text/html
30+
> Access-Control-Allow-Origin: http://192.168.4.1
31+
> Access-Control-Allow-Methods: POST, GET, OPTIONS, DELETE
32+
> Access-Control-Allow-Headers: X-Custom-Header
33+
> Access-Control-Allow-Credentials: false
34+
> Access-Control-Max-Age: 600
35+
> Content-Length: 0
36+
> Connection: close
37+
>
38+
```
39+
40+
### Authentication Middleware
41+
42+
```bash
43+
❯ curl -i -X GET http://192.168.4.1
44+
HTTP/1.1 401 Unauthorized
45+
Content-Type: text/html
46+
WWW-Authenticate: Basic realm=""
47+
Content-Length: 0
48+
Connection: close
49+
```
50+
51+
Output of logger middleware:
52+
53+
```
54+
* Connection from 192.168.4.2:57705
55+
< GET / HTTP/1.1
56+
< Host: 192.168.4.1
57+
< User-Agent: curl/8.9.1
58+
< Accept: */*
59+
<
60+
* Processed!
61+
> HTTP/1.HTTP/1.1 401 Unauthorized
62+
> Content-Type: text/html
63+
> WWW-Authenticate: Basic realm=""
64+
> Content-Length: 0
65+
> Connection: close
66+
>
67+
```
68+
69+
Sending auth...
70+
71+
```bash
72+
Note: Unnecessary use of -X or --request, GET is already inferred.
73+
* Trying 192.168.4.1:80...
74+
* Connected to 192.168.4.1 (192.168.4.1) port 80
75+
* Server auth using Basic with user 'admin'
76+
> GET / HTTP/1.1
77+
> Host: 192.168.4.1
78+
> Authorization: Basic YWRtaW46YWRtaW4=
79+
> User-Agent: curl/8.9.1
80+
> Accept: */*
81+
>
82+
* Request completely sent off
83+
< HTTP/1.1 200 OK
84+
< Content-Type: text/plain
85+
< Content-Length: 4
86+
< Connection: close
87+
<
88+
* shutting down connection #0
89+
Home
90+
91+
```
92+
93+
Output of logger middleware:
94+
95+
```
96+
* Connection from 192.168.4.2:62099
97+
< GET / HTTP/1.1
98+
< Authorization: Basic YWRtaW46YWRtaW4=
99+
< Host: 192.168.4.1
100+
< User-Agent: curl/8.9.1
101+
< Accept: */*
102+
<
103+
* Processed!
104+
> HTTP/1.HTTP/1.1 200 OK
105+
> Content-Type: text/plain
106+
> Content-Length: 4
107+
> Connection: close
108+
>
109+
```
+5Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"targets": {
3+
"esp32h2": false
4+
}
5+
}

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.