httpd_main.c
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 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 httpd_sess_update_lru_counter(hd->hd_sd->handle, new_fd); 71 72 ESP_LOGD(TAG, LOG_FMT("complete")); 73 return ESP_OK; 74 } 75 76 struct httpd_ctrl_data { 77 enum httpd_ctrl_msg { 78 HTTPD_CTRL_SHUTDOWN, 79 HTTPD_CTRL_WORK, 80 } hc_msg; 81 httpd_work_fn_t hc_work; 82 void *hc_work_arg; 83 }; 84 85 esp_err_t httpd_queue_work(httpd_handle_t handle, httpd_work_fn_t work, void *arg) 86 { 87 if (handle == NULL || work == NULL) { 88 return ESP_ERR_INVALID_ARG; 89 } 90 91 struct httpd_data *hd = (struct httpd_data *) handle; 92 struct httpd_ctrl_data msg = { 93 .hc_msg = HTTPD_CTRL_WORK, 94 .hc_work = work, 95 .hc_work_arg = arg, 96 }; 97 98 int ret = cs_send_to_ctrl_sock(hd->msg_fd, hd->config.ctrl_port, &msg, sizeof(msg)); 99 if (ret < 0) { 100 ESP_LOGW(TAG, LOG_FMT("failed to queue work")); 101 return ESP_FAIL; 102 } 103 104 return ESP_OK; 105 } 106 107 esp_err_t httpd_get_client_list(httpd_handle_t handle, size_t *fds, int *client_fds) 108 { 109 struct httpd_data *hd = (struct httpd_data *) handle; 110 if (hd == NULL || fds == NULL || *fds == 0 || client_fds == NULL || *fds < hd->config.max_open_sockets) { 111 return ESP_ERR_INVALID_ARG; 112 } 113 size_t max_fds = *fds; 114 *fds = 0; 115 for (int i = 0; i < hd->config.max_open_sockets; ++i) { 116 if (hd->hd_sd[i].fd != -1) { 117 if (*fds < max_fds) { 118 client_fds[(*fds)++] = hd->hd_sd[i].fd; 119 } else { 120 return ESP_ERR_INVALID_ARG; 121 } 122 } 123 } 124 return ESP_OK; 125 } 126 127 void *httpd_get_global_user_ctx(httpd_handle_t handle) 128 { 129 return ((struct httpd_data *)handle)->config.global_user_ctx; 130 } 131 132 void *httpd_get_global_transport_ctx(httpd_handle_t handle) 133 { 134 return ((struct httpd_data *)handle)->config.global_transport_ctx; 135 } 136 137 static void httpd_close_all_sessions(struct httpd_data *hd) 138 { 139 int fd = -1; 140 while ((fd = httpd_sess_iterate(hd, fd)) != -1) { 141 ESP_LOGD(TAG, LOG_FMT("cleaning up socket %d"), fd); 142 httpd_sess_delete(hd, fd); 143 close(fd); 144 } 145 } 146 147 static void httpd_process_ctrl_msg(struct httpd_data *hd) 148 { 149 struct httpd_ctrl_data msg; 150 int ret = recv(hd->ctrl_fd, &msg, sizeof(msg), 0); 151 if (ret <= 0) { 152 ESP_LOGW(TAG, LOG_FMT("error in recv (%d)"), errno); 153 return; 154 } 155 if (ret != sizeof(msg)) { 156 ESP_LOGW(TAG, LOG_FMT("incomplete msg")); 157 return; 158 } 159 160 switch (msg.hc_msg) { 161 case HTTPD_CTRL_WORK: 162 if (msg.hc_work) { 163 ESP_LOGD(TAG, LOG_FMT("work")); 164 (*msg.hc_work)(msg.hc_work_arg); 165 } 166 break; 167 case HTTPD_CTRL_SHUTDOWN: 168 ESP_LOGD(TAG, LOG_FMT("shutdown")); 169 hd->hd_td.status = THREAD_STOPPING; 170 break; 171 default: 172 break; 173 } 174 } 175 176 /* Manage in-coming connection or data requests */ 177 static esp_err_t httpd_server(struct httpd_data *hd) 178 { 179 fd_set read_set; 180 FD_ZERO(&read_set); 181 if (hd->config.lru_purge_enable || httpd_is_sess_available(hd)) { 182 /* Only listen for new connections if server has capacity to 183 * handle more (or when LRU purge is enabled, in which case 184 * older connections will be closed) */ 185 FD_SET(hd->listen_fd, &read_set); 186 } 187 FD_SET(hd->ctrl_fd, &read_set); 188 189 int tmp_max_fd; 190 httpd_sess_set_descriptors(hd, &read_set, &tmp_max_fd); 191 int maxfd = MAX(hd->listen_fd, tmp_max_fd); 192 tmp_max_fd = maxfd; 193 maxfd = MAX(hd->ctrl_fd, tmp_max_fd); 194 195 ESP_LOGD(TAG, LOG_FMT("doing select maxfd+1 = %d"), maxfd + 1); 196 int active_cnt = select(maxfd + 1, &read_set, NULL, NULL, NULL); 197 if (active_cnt < 0) { 198 ESP_LOGE(TAG, LOG_FMT("error in select (%d)"), errno); 199 httpd_sess_delete_invalid(hd); 200 return ESP_OK; 201 } 202 203 /* Case0: Do we have a control message? */ 204 if (FD_ISSET(hd->ctrl_fd, &read_set)) { 205 ESP_LOGD(TAG, LOG_FMT("processing ctrl message")); 206 httpd_process_ctrl_msg(hd); 207 if (hd->hd_td.status == THREAD_STOPPING) { 208 ESP_LOGD(TAG, LOG_FMT("stopping thread")); 209 return ESP_FAIL; 210 } 211 } 212 213 /* Case1: Do we have any activity on the current data 214 * sessions? */ 215 int fd = -1; 216 while ((fd = httpd_sess_iterate(hd, fd)) != -1) { 217 if (FD_ISSET(fd, &read_set) || (httpd_sess_pending(hd, fd))) { 218 ESP_LOGD(TAG, LOG_FMT("processing socket %d"), fd); 219 if (httpd_sess_process(hd, fd) != ESP_OK) { 220 ESP_LOGD(TAG, LOG_FMT("closing socket %d"), fd); 221 close(fd); 222 /* Delete session and update fd to that 223 * preceding the one being deleted */ 224 fd = httpd_sess_delete(hd, fd); 225 } 226 } 227 } 228 229 /* Case2: Do we have any incoming connection requests to 230 * process? */ 231 if (FD_ISSET(hd->listen_fd, &read_set)) { 232 ESP_LOGD(TAG, LOG_FMT("processing listen socket %d"), hd->listen_fd); 233 if (httpd_accept_conn(hd, hd->listen_fd) != ESP_OK) { 234 ESP_LOGW(TAG, LOG_FMT("error accepting new connection")); 235 } 236 } 237 return ESP_OK; 238 } 239 240 /* The main HTTPD thread */ 241 static void httpd_thread(void *arg) 242 { 243 int ret; 244 struct httpd_data *hd = (struct httpd_data *) arg; 245 hd->hd_td.status = THREAD_RUNNING; 246 247 ESP_LOGD(TAG, LOG_FMT("web server started")); 248 while (1) { 249 ret = httpd_server(hd); 250 if (ret != ESP_OK) { 251 break; 252 } 253 } 254 255 ESP_LOGD(TAG, LOG_FMT("web server exiting")); 256 close(hd->msg_fd); 257 cs_free_ctrl_sock(hd->ctrl_fd); 258 httpd_close_all_sessions(hd); 259 close(hd->listen_fd); 260 hd->hd_td.status = THREAD_STOPPED; 261 httpd_os_thread_delete(); 262 } 263 264 static esp_err_t httpd_server_init(struct httpd_data *hd) 265 { 266 int fd = socket(PF_INET6, SOCK_STREAM, 0); 267 if (fd < 0) { 268 ESP_LOGE(TAG, LOG_FMT("error in socket (%d)"), errno); 269 return ESP_FAIL; 270 } 271 272 struct in6_addr inaddr_any = IN6ADDR_ANY_INIT; 273 struct sockaddr_in6 serv_addr = { 274 .sin6_family = PF_INET6, 275 .sin6_addr = inaddr_any, 276 .sin6_port = htons(hd->config.server_port) 277 }; 278 279 /* Enable SO_REUSEADDR to allow binding to the same 280 * address and port when restarting the server */ 281 int enable = 1; 282 if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(enable)) < 0) { 283 /* This will fail if CONFIG_LWIP_SO_REUSE is not enabled. But 284 * it does not affect the normal working of the HTTP Server */ 285 ESP_LOGW(TAG, LOG_FMT("error enabling SO_REUSEADDR (%d)"), errno); 286 } 287 288 int ret = bind(fd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)); 289 if (ret < 0) { 290 ESP_LOGE(TAG, LOG_FMT("error in bind (%d)"), errno); 291 close(fd); 292 return ESP_FAIL; 293 } 294 295 ret = listen(fd, hd->config.backlog_conn); 296 if (ret < 0) { 297 ESP_LOGE(TAG, LOG_FMT("error in listen (%d)"), errno); 298 close(fd); 299 return ESP_FAIL; 300 } 301 302 int ctrl_fd = cs_create_ctrl_sock(hd->config.ctrl_port); 303 if (ctrl_fd < 0) { 304 ESP_LOGE(TAG, LOG_FMT("error in creating ctrl socket (%d)"), errno); 305 close(fd); 306 return ESP_FAIL; 307 } 308 309 int msg_fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); 310 if (msg_fd < 0) { 311 ESP_LOGE(TAG, LOG_FMT("error in creating msg socket (%d)"), errno); 312 close(fd); 313 close(ctrl_fd); 314 return ESP_FAIL; 315 } 316 317 hd->listen_fd = fd; 318 hd->ctrl_fd = ctrl_fd; 319 hd->msg_fd = msg_fd; 320 return ESP_OK; 321 } 322 323 static struct httpd_data *httpd_create(const httpd_config_t *config) 324 { 325 /* Allocate memory for httpd instance data */ 326 struct httpd_data *hd = calloc(1, sizeof(struct httpd_data)); 327 if (!hd) { 328 ESP_LOGE(TAG, LOG_FMT("Failed to allocate memory for HTTP server instance")); 329 return NULL; 330 } 331 hd->hd_calls = calloc(config->max_uri_handlers, sizeof(httpd_uri_t *)); 332 if (!hd->hd_calls) { 333 ESP_LOGE(TAG, LOG_FMT("Failed to allocate memory for HTTP URI handlers")); 334 free(hd); 335 return NULL; 336 } 337 hd->hd_sd = calloc(config->max_open_sockets, sizeof(struct sock_db)); 338 if (!hd->hd_sd) { 339 ESP_LOGE(TAG, LOG_FMT("Failed to allocate memory for HTTP session data")); 340 free(hd->hd_calls); 341 free(hd); 342 return NULL; 343 } 344 struct httpd_req_aux *ra = &hd->hd_req_aux; 345 ra->resp_hdrs = calloc(config->max_resp_headers, sizeof(struct resp_hdr)); 346 if (!ra->resp_hdrs) { 347 ESP_LOGE(TAG, LOG_FMT("Failed to allocate memory for HTTP response headers")); 348 free(hd->hd_sd); 349 free(hd->hd_calls); 350 free(hd); 351 return NULL; 352 } 353 hd->err_handler_fns = calloc(HTTPD_ERR_CODE_MAX, sizeof(httpd_err_handler_func_t)); 354 if (!hd->err_handler_fns) { 355 ESP_LOGE(TAG, LOG_FMT("Failed to allocate memory for HTTP error handlers")); 356 free(ra->resp_hdrs); 357 free(hd->hd_sd); 358 free(hd->hd_calls); 359 free(hd); 360 return NULL; 361 } 362 /* Save the configuration for this instance */ 363 hd->config = *config; 364 return hd; 365 } 366 367 static void httpd_delete(struct httpd_data *hd) 368 { 369 struct httpd_req_aux *ra = &hd->hd_req_aux; 370 /* Free memory of httpd instance data */ 371 free(hd->err_handler_fns); 372 free(ra->resp_hdrs); 373 free(hd->hd_sd); 374 375 /* Free registered URI handlers */ 376 httpd_unregister_all_uri_handlers(hd); 377 free(hd->hd_calls); 378 free(hd); 379 } 380 381 esp_err_t httpd_start(httpd_handle_t *handle, const httpd_config_t *config) 382 { 383 if (handle == NULL || config == NULL) { 384 return ESP_ERR_INVALID_ARG; 385 } 386 387 /* Sanity check about whether LWIP is configured for providing the 388 * maximum number of open sockets sufficient for the server. Though, 389 * this check doesn't guarantee that many sockets will actually be 390 * available at runtime as other processes may use up some sockets. 391 * Note that server also uses 3 sockets for its internal use : 392 * 1) listening for new TCP connections 393 * 2) for sending control messages over UDP 394 * 3) for receiving control messages over UDP 395 * So the total number of required sockets is max_open_sockets + 3 396 */ 397 if (CONFIG_LWIP_MAX_SOCKETS < config->max_open_sockets + 3) { 398 ESP_LOGE(TAG, "Configuration option max_open_sockets is too large (max allowed %d)\n\t" 399 "Either decrease this or configure LWIP_MAX_SOCKETS to a larger value", 400 CONFIG_LWIP_MAX_SOCKETS - 3); 401 return ESP_ERR_INVALID_ARG; 402 } 403 404 struct httpd_data *hd = httpd_create(config); 405 if (hd == NULL) { 406 /* Failed to allocate memory */ 407 return ESP_ERR_HTTPD_ALLOC_MEM; 408 } 409 410 if (httpd_server_init(hd) != ESP_OK) { 411 httpd_delete(hd); 412 return ESP_FAIL; 413 } 414 415 httpd_sess_init(hd); 416 if (httpd_os_thread_create(&hd->hd_td.handle, "httpd", 417 hd->config.stack_size, 418 hd->config.task_priority, 419 httpd_thread, hd, 420 hd->config.core_id) != ESP_OK) { 421 /* Failed to launch task */ 422 httpd_delete(hd); 423 return ESP_ERR_HTTPD_TASK; 424 } 425 426 *handle = (httpd_handle_t *)hd; 427 return ESP_OK; 428 } 429 430 esp_err_t httpd_stop(httpd_handle_t handle) 431 { 432 struct httpd_data *hd = (struct httpd_data *) handle; 433 if (hd == NULL) { 434 return ESP_ERR_INVALID_ARG; 435 } 436 437 struct httpd_ctrl_data msg; 438 memset(&msg, 0, sizeof(msg)); 439 msg.hc_msg = HTTPD_CTRL_SHUTDOWN; 440 cs_send_to_ctrl_sock(hd->msg_fd, hd->config.ctrl_port, &msg, sizeof(msg)); 441 442 ESP_LOGD(TAG, LOG_FMT("sent control msg to stop server")); 443 while (hd->hd_td.status != THREAD_STOPPED) { 444 httpd_os_thread_sleep(100); 445 } 446 447 /* Release global user context, if not NULL */ 448 if (hd->config.global_user_ctx) { 449 if (hd->config.global_user_ctx_free_fn) { 450 hd->config.global_user_ctx_free_fn(hd->config.global_user_ctx); 451 } else { 452 free(hd->config.global_user_ctx); 453 } 454 hd->config.global_user_ctx = NULL; 455 } 456 457 /* Release global transport context, if not NULL */ 458 if (hd->config.global_transport_ctx) { 459 if (hd->config.global_transport_ctx_free_fn) { 460 hd->config.global_transport_ctx_free_fn(hd->config.global_transport_ctx); 461 } else { 462 free(hd->config.global_transport_ctx); 463 } 464 hd->config.global_transport_ctx = NULL; 465 } 466 467 ESP_LOGD(TAG, LOG_FMT("server stopped")); 468 httpd_delete(hd); 469 return ESP_OK; 470 }