SensESP 3.0.1
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 String ap_ip = WiFi.softAPIP().toString();
63
64 bool auth_required;
65
66 // Don't require authentication for the captive portal
67 if (ap_ip != ipstr) {
68 auth_required = auth_required_;
69 } else {
70 auth_required = false;
71 }
72
73 if (auth_required) {
74 bool success;
77 if (!success) {
78 // Authentication failed; do not continue but return success.
79 // The client already received a 401 response.
80 return ESP_OK;
81 }
82 }
83
84 // dispatch request to the appropriate handler
85
86 String uri = req->uri;
87
88 // decode the uri
89 char decoded_uri[uri.length() + 1];
90 urldecode2(decoded_uri, uri.c_str());
91 String decoded_uri_str = String(decoded_uri);
92
93 // Drop the query part of the URI
94 int query_pos = decoded_uri_str.indexOf('?');
95 if (query_pos != -1) {
96 decoded_uri_str = decoded_uri_str.substring(0, query_pos);
97 }
98
99 for (auto handler : handlers_) {
100 String match_uri = handler->match_uri_;
101
102 // Check if the match uri ends with a wildcard
103 if (match_uri.endsWith("*")) {
104 // Remove the wildcard from the match uri
105 match_uri = match_uri.substring(0, match_uri.length() - 1);
106 // Check if the request uri starts with the match uri
107 if (decoded_uri_str.startsWith(match_uri)) {
108 if (handler->method_mask_ & (1 << req->method)) {
109 return handler->call(req);
110 }
111 }
112 } else if (handler->match_uri_ == decoded_uri_str) {
113 if (handler->method_mask_ & (1 << req->method)) {
114 return handler->call(req);
115 }
116 }
117 }
118
119 return httpd_resp_send_404(req);
120}
121
122bool HTTPServer::handle_captive_portal(httpd_req_t* req) {
123 // Get HTTP Host header
124 char host[100];
125 if (httpd_req_get_hdr_value_str(req, "Host", host, sizeof(host)) != ESP_OK) {
126 return false;
127 }
128 String host_hdr = host;
129 // Drop port number
130 int pos = host_hdr.indexOf(':');
131 if (pos != -1) {
132 host_hdr = host_hdr.substring(0, pos);
133 }
134
135 // Get the HTTP request socket local IP address
136 int sockfd = httpd_req_to_sockfd(req);
137 char ipstr[INET6_ADDRSTRLEN];
138 struct sockaddr_in6 addr; // esp_http_server uses IPv6 addressing
139 socklen_t addr_size = sizeof(addr);
140 if (getsockname(sockfd, (struct sockaddr*)&addr, &addr_size) < 0) {
141 ESP_LOGE(__FILENAME__, "Error getting client IP");
142 return false;
143 }
144 inet_ntop(AF_INET, &addr.sin6_addr.un.u32_addr[3], ipstr, sizeof(ipstr));
145
146 String ap_ip = WiFi.softAPIP().toString();
147
148 // We should only apply the captive portal to the soft AP IP address
149 if (ap_ip != ipstr) {
150 return false;
151 }
152
153 // Check if the host matches our soft AP IP address
154 if (host_hdr != ap_ip) {
155 // Redirect the client to the captive portal url /wifi
156 httpd_resp_set_status(req, "302 Found");
157 String destination = String("http://") + ap_ip + "/wifi";
158 httpd_resp_set_hdr(req, "Location", destination.c_str());
159 httpd_resp_sendstr(req, "Redirecting to captive portal");
160 return true;
161 }
162 return false;
163}
164
166 HTTPAuthenticator* auth, std::function<esp_err_t(httpd_req_t*)> handler,
167 httpd_req_t* req) {
168 bool continue_ = false;
169 if (auth == nullptr) {
170 continue_ = true;
171 } else {
172 continue_ = auth->authenticate_request(req);
173 }
174
175 return continue_;
176}
177
178esp_err_t call_request_dispatcher(httpd_req_t* req) {
179 HTTPServer* server = (HTTPServer*)req->user_ctx;
180 return server->dispatch_request(req);
181}
182
183String get_content_type(httpd_req_t* req) {
184 if (httpd_req_get_hdr_value_len(req, "Content-Type") != 16) {
185 ESP_LOGE(__FILENAME__, "Invalid content type");
186 return "";
187 } else {
188 char content_type[32];
189 httpd_req_get_hdr_value_str(req, "Content-Type", content_type, 32);
190 return String(content_type);
191 }
192}
193
194} // 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:61
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_
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)