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