1 // Copyright 2018 Espressif Systems (Shanghai) PTE LTD
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include <string.h>
16 #include <sys/socket.h>
17 #include <sys/param.h>
18 #include <errno.h>
19 #include <esp_log.h>
20 #include <esp_err.h>
21 #include <assert.h>
22 
23 #include <esp_http_server.h>
24 #include "esp_httpd_priv.h"
25 #include "ctrl_sock.h"
26 
27 static const char *TAG = "httpd";
28 
httpd_accept_conn(struct httpd_data * hd,int listen_fd)29 static esp_err_t httpd_accept_conn(struct httpd_data *hd, int listen_fd)
30 {
31     /* If no space is available for new session, close the least recently used one */
32     if (hd->config.lru_purge_enable == true) {
33         if (!httpd_is_sess_available(hd)) {
34             /* Queue asynchronous closure of the least recently used session */
35             return httpd_sess_close_lru(hd);
36             /* Returning from this allowes the main server thread to process
37              * the queued asynchronous control message for closing LRU session.
38              * Since connection request hasn't been addressed yet using accept()
39              * therefore httpd_accept_conn() will be called again, but this time
40              * with space available for one session
41              */
42        }
43     }
44 
45     struct sockaddr_in addr_from;
46     socklen_t addr_from_len = sizeof(addr_from);
47     int new_fd = accept(listen_fd, (struct sockaddr *)&addr_from, &addr_from_len);
48     if (new_fd < 0) {
49         ESP_LOGW(TAG, LOG_FMT("error in accept (%d)"), errno);
50         return ESP_FAIL;
51     }
52     ESP_LOGD(TAG, LOG_FMT("newfd = %d"), new_fd);
53 
54     struct timeval tv;
55     /* Set recv timeout of this fd as per config */
56     tv.tv_sec = hd->config.recv_wait_timeout;
57     tv.tv_usec = 0;
58     setsockopt(new_fd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof(tv));
59 
60     /* Set send timeout of this fd as per config */
61     tv.tv_sec = hd->config.send_wait_timeout;
62     tv.tv_usec = 0;
63     setsockopt(new_fd, SOL_SOCKET, SO_SNDTIMEO, (const char*)&tv, sizeof(tv));
64 
65     if (ESP_OK != httpd_sess_new(hd, new_fd)) {
66         ESP_LOGW(TAG, LOG_FMT("session creation failed"));
67         close(new_fd);
68         return ESP_FAIL;
69     }
70     ESP_LOGD(TAG, LOG_FMT("complete"));
71     return ESP_OK;
72 }
73 
74 struct httpd_ctrl_data {
75     enum httpd_ctrl_msg {
76         HTTPD_CTRL_SHUTDOWN,
77         HTTPD_CTRL_WORK,
78     } hc_msg;
79     httpd_work_fn_t hc_work;
80     void *hc_work_arg;
81 };
82 
httpd_queue_work(httpd_handle_t handle,httpd_work_fn_t work,void * arg)83 esp_err_t httpd_queue_work(httpd_handle_t handle, httpd_work_fn_t work, void *arg)
84 {
85     if (handle == NULL || work == NULL) {
86         return ESP_ERR_INVALID_ARG;
87     }
88 
89     struct httpd_data *hd = (struct httpd_data *) handle;
90     struct httpd_ctrl_data msg = {
91         .hc_msg = HTTPD_CTRL_WORK,
92         .hc_work = work,
93         .hc_work_arg = arg,
94     };
95 
96     int ret = cs_send_to_ctrl_sock(hd->msg_fd, hd->config.ctrl_port, &msg, sizeof(msg));
97     if (ret < 0) {
98         ESP_LOGW(TAG, LOG_FMT("failed to queue work"));
99         return ESP_FAIL;
100     }
101 
102     return ESP_OK;
103 }
104 
httpd_get_client_list(httpd_handle_t handle,size_t * fds,int * client_fds)105 esp_err_t httpd_get_client_list(httpd_handle_t handle, size_t *fds, int *client_fds)
106 {
107     struct httpd_data *hd = (struct httpd_data *) handle;
108     if (hd == NULL || fds == NULL || *fds == 0 || client_fds == NULL || *fds < hd->config.max_open_sockets) {
109         return ESP_ERR_INVALID_ARG;
110     }
111     size_t max_fds = *fds;
112     *fds = 0;
113     for (int i = 0; i < hd->config.max_open_sockets; ++i) {
114         if (hd->hd_sd[i].fd != -1) {
115             if (*fds < max_fds) {
116                 client_fds[(*fds)++] = hd->hd_sd[i].fd;
117             } else {
118                 return ESP_ERR_INVALID_ARG;
119             }
120         }
121     }
122     return ESP_OK;
123 }
124 
httpd_get_global_user_ctx(httpd_handle_t handle)125 void *httpd_get_global_user_ctx(httpd_handle_t handle)
126 {
127     return ((struct httpd_data *)handle)->config.global_user_ctx;
128 }
129 
httpd_get_global_transport_ctx(httpd_handle_t handle)130 void *httpd_get_global_transport_ctx(httpd_handle_t handle)
131 {
132     return ((struct httpd_data *)handle)->config.global_transport_ctx;
133 }
134 
httpd_close_all_sessions(struct httpd_data * hd)135 static void httpd_close_all_sessions(struct httpd_data *hd)
136 {
137     int fd = -1;
138     while ((fd = httpd_sess_iterate(hd, fd)) != -1) {
139         ESP_LOGD(TAG, LOG_FMT("cleaning up socket %d"), fd);
140         httpd_sess_delete(hd, fd);
141         close(fd);
142     }
143 }
144 
httpd_process_ctrl_msg(struct httpd_data * hd)145 static void httpd_process_ctrl_msg(struct httpd_data *hd)
146 {
147     struct httpd_ctrl_data msg;
148     int ret = recv(hd->ctrl_fd, &msg, sizeof(msg), 0);
149     if (ret <= 0) {
150         ESP_LOGW(TAG, LOG_FMT("error in recv (%d)"), errno);
151         return;
152     }
153     if (ret != sizeof(msg)) {
154         ESP_LOGW(TAG, LOG_FMT("incomplete msg"));
155         return;
156     }
157 
158     switch (msg.hc_msg) {
159     case HTTPD_CTRL_WORK:
160         if (msg.hc_work) {
161             ESP_LOGD(TAG, LOG_FMT("work"));
162             (*msg.hc_work)(msg.hc_work_arg);
163         }
164         break;
165     case HTTPD_CTRL_SHUTDOWN:
166         ESP_LOGD(TAG, LOG_FMT("shutdown"));
167         hd->hd_td.status = THREAD_STOPPING;
168         break;
169     default:
170         break;
171     }
172 }
173 
174 /* Manage in-coming connection or data requests */
httpd_server(struct httpd_data * hd)175 static esp_err_t httpd_server(struct httpd_data *hd)
176 {
177     fd_set read_set;
178     FD_ZERO(&read_set);
179     if (hd->config.lru_purge_enable || httpd_is_sess_available(hd)) {
180         /* Only listen for new connections if server has capacity to
181          * handle more (or when LRU purge is enabled, in which case
182          * older connections will be closed) */
183         FD_SET(hd->listen_fd, &read_set);
184     }
185     FD_SET(hd->ctrl_fd, &read_set);
186 
187     int tmp_max_fd;
188     httpd_sess_set_descriptors(hd, &read_set, &tmp_max_fd);
189     int maxfd = MAX(hd->listen_fd, tmp_max_fd);
190     tmp_max_fd = maxfd;
191     maxfd = MAX(hd->ctrl_fd, tmp_max_fd);
192 
193     ESP_LOGD(TAG, LOG_FMT("doing select maxfd+1 = %d"), maxfd + 1);
194     int active_cnt = select(maxfd + 1, &read_set, NULL, NULL, NULL);
195     if (active_cnt < 0) {
196         ESP_LOGE(TAG, LOG_FMT("error in select (%d)"), errno);
197         httpd_sess_delete_invalid(hd);
198         return ESP_OK;
199     }
200 
201     /* Case0: Do we have a control message? */
202     if (FD_ISSET(hd->ctrl_fd, &read_set)) {
203         ESP_LOGD(TAG, LOG_FMT("processing ctrl message"));
204         httpd_process_ctrl_msg(hd);
205         if (hd->hd_td.status == THREAD_STOPPING) {
206             ESP_LOGD(TAG, LOG_FMT("stopping thread"));
207             return ESP_FAIL;
208         }
209     }
210 
211     /* Case1: Do we have any activity on the current data
212      * sessions? */
213     int fd = -1;
214     while ((fd = httpd_sess_iterate(hd, fd)) != -1) {
215         if (FD_ISSET(fd, &read_set) || (httpd_sess_pending(hd, fd))) {
216             ESP_LOGD(TAG, LOG_FMT("processing socket %d"), fd);
217             if (httpd_sess_process(hd, fd) != ESP_OK) {
218                 ESP_LOGD(TAG, LOG_FMT("closing socket %d"), fd);
219                 close(fd);
220                 /* Delete session and update fd to that
221                  * preceding the one being deleted */
222                 fd = httpd_sess_delete(hd, fd);
223             }
224         }
225     }
226 
227     /* Case2: Do we have any incoming connection requests to
228      * process? */
229     if (FD_ISSET(hd->listen_fd, &read_set)) {
230         ESP_LOGD(TAG, LOG_FMT("processing listen socket %d"), hd->listen_fd);
231         if (httpd_accept_conn(hd, hd->listen_fd) != ESP_OK) {
232             ESP_LOGW(TAG, LOG_FMT("error accepting new connection"));
233         }
234     }
235     return ESP_OK;
236 }
237 
238 /* The main HTTPD thread */
httpd_thread(void * arg)239 static void httpd_thread(void *arg)
240 {
241     int ret;
242     struct httpd_data *hd = (struct httpd_data *) arg;
243     hd->hd_td.status = THREAD_RUNNING;
244 
245     ESP_LOGD(TAG, LOG_FMT("web server started"));
246     while (1) {
247         ret = httpd_server(hd);
248         if (ret != ESP_OK) {
249             break;
250         }
251     }
252 
253     ESP_LOGD(TAG, LOG_FMT("web server exiting"));
254     close(hd->msg_fd);
255     cs_free_ctrl_sock(hd->ctrl_fd);
256     httpd_close_all_sessions(hd);
257     close(hd->listen_fd);
258     hd->hd_td.status = THREAD_STOPPED;
259     httpd_os_thread_delete();
260 }
261 
httpd_server_init(struct httpd_data * hd)262 static esp_err_t httpd_server_init(struct httpd_data *hd)
263 {
264 #if CONFIG_LWIP_IPV6
265     int fd = socket(PF_INET6, SOCK_STREAM, 0);
266 #else
267     int fd = socket(PF_INET, SOCK_STREAM, 0);
268 #endif
269     if (fd < 0) {
270         ESP_LOGE(TAG, LOG_FMT("error in socket (%d)"), errno);
271         return ESP_FAIL;
272     }
273 #if CONFIG_LWIP_IPV6
274     struct in6_addr inaddr_any = IN6ADDR_ANY_INIT;
275     struct sockaddr_in6 serv_addr = {
276         .sin6_family  = PF_INET6,
277         .sin6_addr    = inaddr_any,
278         .sin6_port    = htons(hd->config.server_port)
279     };
280 #else
281     struct sockaddr_in serv_addr = {
282         .sin_family   = PF_INET,
283         .sin_addr     = {
284             .s_addr = htonl(INADDR_ANY)
285         },
286         .sin_port     = htons(hd->config.server_port)
287     };
288 #endif
289     /* Enable SO_REUSEADDR to allow binding to the same
290      * address and port when restarting the server */
291     int enable = 1;
292     if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(enable)) < 0) {
293         /* This will fail if CONFIG_LWIP_SO_REUSE is not enabled. But
294          * it does not affect the normal working of the HTTP Server */
295         ESP_LOGW(TAG, LOG_FMT("error enabling SO_REUSEADDR (%d)"), errno);
296     }
297 
298     int ret = bind(fd, (struct sockaddr *)&serv_addr, sizeof(serv_addr));
299     if (ret < 0) {
300         ESP_LOGE(TAG, LOG_FMT("error in bind (%d)"), errno);
301         close(fd);
302         return ESP_FAIL;
303     }
304 
305     ret = listen(fd, hd->config.backlog_conn);
306     if (ret < 0) {
307         ESP_LOGE(TAG, LOG_FMT("error in listen (%d)"), errno);
308         close(fd);
309         return ESP_FAIL;
310     }
311 
312     int ctrl_fd = cs_create_ctrl_sock(hd->config.ctrl_port);
313     if (ctrl_fd < 0) {
314         ESP_LOGE(TAG, LOG_FMT("error in creating ctrl socket (%d)"), errno);
315         close(fd);
316         return ESP_FAIL;
317     }
318 
319     int msg_fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
320     if (msg_fd < 0) {
321         ESP_LOGE(TAG, LOG_FMT("error in creating msg socket (%d)"), errno);
322         close(fd);
323         close(ctrl_fd);
324         return ESP_FAIL;
325     }
326 
327     hd->listen_fd = fd;
328     hd->ctrl_fd = ctrl_fd;
329     hd->msg_fd  = msg_fd;
330     return ESP_OK;
331 }
332 
httpd_create(const httpd_config_t * config)333 static struct httpd_data *httpd_create(const httpd_config_t *config)
334 {
335     /* Allocate memory for httpd instance data */
336     struct httpd_data *hd = calloc(1, sizeof(struct httpd_data));
337     if (!hd) {
338         ESP_LOGE(TAG, LOG_FMT("Failed to allocate memory for HTTP server instance"));
339         return NULL;
340     }
341     hd->hd_calls = calloc(config->max_uri_handlers, sizeof(httpd_uri_t *));
342     if (!hd->hd_calls) {
343         ESP_LOGE(TAG, LOG_FMT("Failed to allocate memory for HTTP URI handlers"));
344         free(hd);
345         return NULL;
346     }
347     hd->hd_sd = calloc(config->max_open_sockets, sizeof(struct sock_db));
348     if (!hd->hd_sd) {
349         ESP_LOGE(TAG, LOG_FMT("Failed to allocate memory for HTTP session data"));
350         free(hd->hd_calls);
351         free(hd);
352         return NULL;
353     }
354     struct httpd_req_aux *ra = &hd->hd_req_aux;
355     ra->resp_hdrs = calloc(config->max_resp_headers, sizeof(struct resp_hdr));
356     if (!ra->resp_hdrs) {
357         ESP_LOGE(TAG, LOG_FMT("Failed to allocate memory for HTTP response headers"));
358         free(hd->hd_sd);
359         free(hd->hd_calls);
360         free(hd);
361         return NULL;
362     }
363     hd->err_handler_fns = calloc(HTTPD_ERR_CODE_MAX, sizeof(httpd_err_handler_func_t));
364     if (!hd->err_handler_fns) {
365         ESP_LOGE(TAG, LOG_FMT("Failed to allocate memory for HTTP error handlers"));
366         free(ra->resp_hdrs);
367         free(hd->hd_sd);
368         free(hd->hd_calls);
369         free(hd);
370         return NULL;
371     }
372     /* Save the configuration for this instance */
373     hd->config = *config;
374     return hd;
375 }
376 
httpd_delete(struct httpd_data * hd)377 static void httpd_delete(struct httpd_data *hd)
378 {
379     struct httpd_req_aux *ra = &hd->hd_req_aux;
380     /* Free memory of httpd instance data */
381     free(hd->err_handler_fns);
382     free(ra->resp_hdrs);
383     free(hd->hd_sd);
384 
385     /* Free registered URI handlers */
386     httpd_unregister_all_uri_handlers(hd);
387     free(hd->hd_calls);
388     free(hd);
389 }
390 
httpd_start(httpd_handle_t * handle,const httpd_config_t * config)391 esp_err_t httpd_start(httpd_handle_t *handle, const httpd_config_t *config)
392 {
393     if (handle == NULL || config == NULL) {
394         return ESP_ERR_INVALID_ARG;
395     }
396 
397     /* Sanity check about whether LWIP is configured for providing the
398      * maximum number of open sockets sufficient for the server. Though,
399      * this check doesn't guarantee that many sockets will actually be
400      * available at runtime as other processes may use up some sockets.
401      * Note that server also uses 3 sockets for its internal use :
402      *     1) listening for new TCP connections
403      *     2) for sending control messages over UDP
404      *     3) for receiving control messages over UDP
405      * So the total number of required sockets is max_open_sockets + 3
406      */
407     if (CONFIG_LWIP_MAX_SOCKETS < config->max_open_sockets + 3) {
408         ESP_LOGE(TAG, "Configuration option max_open_sockets is too large (max allowed %d)\n\t"
409                       "Either decrease this or configure LWIP_MAX_SOCKETS to a larger value",
410                       CONFIG_LWIP_MAX_SOCKETS - 3);
411         return ESP_ERR_INVALID_ARG;
412     }
413 
414     struct httpd_data *hd = httpd_create(config);
415     if (hd == NULL) {
416         /* Failed to allocate memory */
417         return ESP_ERR_HTTPD_ALLOC_MEM;
418     }
419 
420     if (httpd_server_init(hd) != ESP_OK) {
421         httpd_delete(hd);
422         return ESP_FAIL;
423     }
424 
425     httpd_sess_init(hd);
426     if (httpd_os_thread_create(&hd->hd_td.handle, "httpd",
427                                hd->config.stack_size,
428                                hd->config.task_priority,
429                                httpd_thread, hd,
430                                hd->config.core_id) != ESP_OK) {
431         /* Failed to launch task */
432         httpd_delete(hd);
433         return ESP_ERR_HTTPD_TASK;
434     }
435 
436     *handle = (httpd_handle_t *)hd;
437     return ESP_OK;
438 }
439 
httpd_stop(httpd_handle_t handle)440 esp_err_t httpd_stop(httpd_handle_t handle)
441 {
442     struct httpd_data *hd = (struct httpd_data *) handle;
443     if (hd == NULL) {
444         return ESP_ERR_INVALID_ARG;
445     }
446 
447     struct httpd_ctrl_data msg;
448     memset(&msg, 0, sizeof(msg));
449     msg.hc_msg = HTTPD_CTRL_SHUTDOWN;
450     cs_send_to_ctrl_sock(hd->msg_fd, hd->config.ctrl_port, &msg, sizeof(msg));
451 
452     ESP_LOGD(TAG, LOG_FMT("sent control msg to stop server"));
453     while (hd->hd_td.status != THREAD_STOPPED) {
454         httpd_os_thread_sleep(100);
455     }
456 
457     /* Release global user context, if not NULL */
458     if (hd->config.global_user_ctx) {
459         if (hd->config.global_user_ctx_free_fn) {
460             hd->config.global_user_ctx_free_fn(hd->config.global_user_ctx);
461         } else {
462             free(hd->config.global_user_ctx);
463         }
464         hd->config.global_user_ctx = NULL;
465     }
466 
467     /* Release global transport context, if not NULL */
468     if (hd->config.global_transport_ctx) {
469         if (hd->config.global_transport_ctx_free_fn) {
470             hd->config.global_transport_ctx_free_fn(hd->config.global_transport_ctx);
471         } else {
472             free(hd->config.global_transport_ctx);
473         }
474         hd->config.global_transport_ctx = NULL;
475     }
476 
477     ESP_LOGD(TAG, LOG_FMT("server stopped"));
478     httpd_delete(hd);
479     return ESP_OK;
480 }
481