httpd_main.c 15 KB

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