SensESP 3.4.1-alpha
Universal Signal K sensor toolkit ESP32
Loading...
Searching...
No Matches
http_server.cpp
Go to the documentation of this file.
1#include "sensesp.h"
2
4
5#include <cstring>
6#include <functional>
7#include <list>
8#include <lwip/sockets.h>
9
10namespace sensesp {
11
12// from:
13// https://stackoverflow.com/questions/2673207/c-c-url-decode-library/2766963
14void urldecode2(char* dst, const char* src) {
15 char a, b;
16 while (*src) {
17 if ((*src == '%') && ((a = src[1]) && (b = src[2])) &&
18 (isxdigit(a) && isxdigit(b))) {
19 if (a >= 'a') a -= 'a' - 'A';
20 if (a >= 'A')
21 a -= ('A' - 10);
22 else
23 a -= '0';
24 if (b >= 'a') b -= 'a' - 'A';
25 if (b >= 'A')
26 b -= ('A' - 10);
27 else
28 b -= '0';
29 *dst++ = 16 * a + b;
30 src += 3;
31 } else if (*src == '+') {
32 *dst++ = ' ';
33 src++;
34 } else {
35 *dst++ = *src++;
36 }
37 }
38 *dst++ = '\0';
39}
40
41esp_err_t HTTPServer::dispatch_request(httpd_req_t* req) {
42 // Log only the path, not the query string: the captured log is served over
43 // HTTP (and the server is unauthenticated by default), so a future endpoint
44 // carrying a secret as a query parameter must not echo it into the log.
45 const char* query = strchr(req->uri, '?');
46 int path_len = query ? static_cast<int>(query - req->uri)
47 : static_cast<int>(strlen(req->uri));
48 ESP_LOGI(__FILENAME__, "Handling request: %.*s", path_len, req->uri);
49
50 if (captive_portal_) {
51 bool captured;
52 captured = handle_captive_portal(req);
53 if (captured) {
54 return ESP_OK;
55 }
56 }
57
58 // Get the HTTP request socket local IP address
59 int sockfd = httpd_req_to_sockfd(req);
60 char ipstr[INET6_ADDRSTRLEN];
61 struct sockaddr_in6 addr; // esp_http_server uses IPv6 addressing
62 socklen_t addr_size = sizeof(addr);
63 if (getsockname(sockfd, (struct sockaddr*)&addr, &addr_size) < 0) {
64 ESP_LOGE(__FILENAME__, "Error getting client IP");
65 return false;
66 }
67 inet_ntop(AF_INET, &addr.sin6_addr.un.u32_addr[3], ipstr, sizeof(ipstr));
68
69 // Captive-portal AP IP is supplied by the network provisioner via
70 // set_captive_portal(); zero on transports without a soft AP.
71 String ap_ip = captive_portal_ap_ip_.toString();
72
73 bool auth_required;
74
75 // Don't require authentication for the captive portal
76 if (ap_ip != ipstr) {
77 auth_required = auth_required_;
78 } else {
79 auth_required = false;
80 }
81
82 if (auth_required) {
83 bool success;
86 if (!success) {
87 // Authentication failed; do not continue but return success.
88 // The client already received a 401 response.
89 return ESP_OK;
90 }
91 }
92
93 // dispatch request to the appropriate handler
94
95 String uri = req->uri;
96
97 // decode the uri
98 char decoded_uri[uri.length() + 1];
99 urldecode2(decoded_uri, uri.c_str());
100 String decoded_uri_str = String(decoded_uri);
101
102 // Drop the query part of the URI
103 int query_pos = decoded_uri_str.indexOf('?');
104 if (query_pos != -1) {
105 decoded_uri_str = decoded_uri_str.substring(0, query_pos);
106 }
107
108 for (auto handler : handlers_) {
109 String match_uri = handler->match_uri_;
110
111 // Check if the match uri ends with a wildcard
112 if (match_uri.endsWith("*")) {
113 // Remove the wildcard from the match uri
114 match_uri = match_uri.substring(0, match_uri.length() - 1);
115 // Check if the request uri starts with the match uri
116 if (decoded_uri_str.startsWith(match_uri)) {
117 if (handler->method_mask_ & (1 << req->method)) {
118 return handler->call(req);
119 }
120 }
121 } else if (handler->match_uri_ == decoded_uri_str) {
122 if (handler->method_mask_ & (1 << req->method)) {
123 return handler->call(req);
124 }
125 }
126 }
127
128 return httpd_resp_send_404(req);
129}
130
131bool HTTPServer::handle_captive_portal(httpd_req_t* req) {
132 // Get HTTP Host header
133 char host[100];
134 if (httpd_req_get_hdr_value_str(req, "Host", host, sizeof(host)) != ESP_OK) {
135 return false;
136 }
137 String host_hdr = host;
138 // Drop port number
139 int pos = host_hdr.indexOf(':');
140 if (pos != -1) {
141 host_hdr = host_hdr.substring(0, pos);
142 }
143
144 // Get the HTTP request socket local IP address
145 int sockfd = httpd_req_to_sockfd(req);
146 char ipstr[INET6_ADDRSTRLEN];
147 struct sockaddr_in6 addr; // esp_http_server uses IPv6 addressing
148 socklen_t addr_size = sizeof(addr);
149 if (getsockname(sockfd, (struct sockaddr*)&addr, &addr_size) < 0) {
150 ESP_LOGE(__FILENAME__, "Error getting client IP");
151 return false;
152 }
153 inet_ntop(AF_INET, &addr.sin6_addr.un.u32_addr[3], ipstr, sizeof(ipstr));
154
155 String ap_ip = captive_portal_ap_ip_.toString();
156
157 // We should only apply the captive portal to the soft AP IP address
158 if (ap_ip != ipstr) {
159 return false;
160 }
161
162 // Check if the host matches our soft AP IP address
163 if (host_hdr != ap_ip) {
164 // Redirect the client to the captive portal url /wifi
165 httpd_resp_set_status(req, "302 Found");
166 String destination = String("http://") + ap_ip + "/wifi";
167 httpd_resp_set_hdr(req, "Location", destination.c_str());
168 httpd_resp_sendstr(req, "Redirecting to captive portal");
169 return true;
170 }
171 return false;
172}
173
175 HTTPAuthenticator* auth, std::function<esp_err_t(httpd_req_t*)> handler,
176 httpd_req_t* req) {
177 bool continue_ = false;
178 if (auth == nullptr) {
179 continue_ = true;
180 } else {
181 continue_ = auth->authenticate_request(req);
182 }
183
184 return continue_;
185}
186
187esp_err_t call_request_dispatcher(httpd_req_t* req) {
188 HTTPServer* server = (HTTPServer*)req->user_ctx;
189 return server->dispatch_request(req);
190}
191
192String get_content_type(httpd_req_t* req) {
193 char content_type[32];
194 if (httpd_req_get_hdr_value_str(req, "Content-Type", content_type,
195 sizeof(content_type)) != ESP_OK) {
196 ESP_LOGE(__FILENAME__, "Invalid content type");
197 return "";
198 }
199 return String(content_type);
200}
201
202} // namespace sensesp
HTTP Authenticator base class.
virtual bool authenticate_request(httpd_req_t *req)=0
Authenticate an incoming request.
HTTP server class wrapping the esp-idf http server.
Definition http_server.h:83
esp_err_t dispatch_request(httpd_req_t *req)
Dispatcher method that captures all requests and forwards them to the appropriate handlers.
std::list< std::shared_ptr< HTTPRequestHandler > > handlers_
IPAddress captive_portal_ap_ip_
bool authenticate_request(HTTPAuthenticator *auth, std::function< esp_err_t(httpd_req_t *)> handler, httpd_req_t *req)
friend esp_err_t call_request_dispatcher(httpd_req_t *req)
std::unique_ptr< HTTPAuthenticator > authenticator_
bool handle_captive_portal(httpd_req_t *req)
Check if the request is for the captive portal and handle it if it.
esp_err_t call_request_dispatcher(httpd_req_t *req)
void urldecode2(char *dst, const char *src)
String get_content_type(httpd_req_t *req)