main.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353
  1. /* Async Request Handlers HTTP Server Example
  2. This example code is in the Public Domain (or CC0 licensed, at your option.)
  3. Unless required by applicable law or agreed to in writing, this
  4. software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
  5. CONDITIONS OF ANY KIND, either express or implied.
  6. */
  7. #include "freertos/FreeRTOS.h"
  8. #include "freertos/semphr.h"
  9. #include <esp_wifi.h>
  10. #include <esp_event.h>
  11. #include <esp_log.h>
  12. #include <esp_system.h>
  13. #include <nvs_flash.h>
  14. #include <sys/param.h>
  15. #include "nvs_flash.h"
  16. #include "esp_netif.h"
  17. #include "esp_eth.h"
  18. #include "protocol_examples_common.h"
  19. #include "esp_tls_crypto.h"
  20. #include <esp_http_server.h>
  21. /* An example that demonstrates multiple
  22. long running http requests running in parallel.
  23. In this example, multiple long http request can run at
  24. the same time. (uri: /long)
  25. While these long requests are running, the server can still
  26. respond to other incoming synchronous requests. (uri: /quick)
  27. */
  28. #define ASYNC_WORKER_TASK_PRIORITY 5
  29. #define ASYNC_WORKER_TASK_STACK_SIZE 2048
  30. static const char *TAG = "example";
  31. // Async reqeusts are queued here while they wait to
  32. // be processed by the workers
  33. static QueueHandle_t async_req_queue;
  34. // Track the number of free workers at any given time
  35. static SemaphoreHandle_t worker_ready_count;
  36. // Each worker has its own thread
  37. static TaskHandle_t worker_handles[CONFIG_EXAMPLE_MAX_ASYNC_REQUESTS];
  38. typedef esp_err_t (*httpd_req_handler_t)(httpd_req_t *req);
  39. typedef struct {
  40. httpd_req_t* req;
  41. httpd_req_handler_t handler;
  42. } httpd_async_req_t;
  43. static bool is_on_async_worker_thread(void)
  44. {
  45. // is our handle one of the known async handles?
  46. TaskHandle_t handle = xTaskGetCurrentTaskHandle();
  47. for (int i = 0; i < CONFIG_EXAMPLE_MAX_ASYNC_REQUESTS; i++) {
  48. if (worker_handles[i] == handle) {
  49. return true;
  50. }
  51. }
  52. return false;
  53. }
  54. // Submit an HTTP req to the async worker queue
  55. static esp_err_t submit_async_req(httpd_req_t *req, httpd_req_handler_t handler)
  56. {
  57. // must create a copy of the request that we own
  58. httpd_req_t* copy = NULL;
  59. esp_err_t err = httpd_req_async_handler_begin(req, &copy);
  60. if (err != ESP_OK) {
  61. return err;
  62. }
  63. httpd_async_req_t async_req = {
  64. .req = copy,
  65. .handler = handler,
  66. };
  67. // How should we handle resource exhaustion?
  68. // In this example, we immediately respond with an
  69. // http error if no workers are available.
  70. int ticks = 0;
  71. // counting semaphore: if success, we know 1 or
  72. // more asyncReqTaskWorkers are available.
  73. if (xSemaphoreTake(worker_ready_count, ticks) == false) {
  74. ESP_LOGE(TAG, "No workers are available");
  75. httpd_req_async_handler_complete(copy); // cleanup
  76. return ESP_FAIL;
  77. }
  78. // Since worker_ready_count > 0 the queue should already have space.
  79. // But lets wait up to 100ms just to be safe.
  80. if (xQueueSend(async_req_queue, &async_req, pdMS_TO_TICKS(100)) == false) {
  81. ESP_LOGE(TAG, "worker queue is full");
  82. httpd_req_async_handler_complete(copy); // cleanup
  83. return ESP_FAIL;
  84. }
  85. return ESP_OK;
  86. }
  87. /* A long running HTTP GET handler */
  88. static esp_err_t long_async_handler(httpd_req_t *req)
  89. {
  90. ESP_LOGI(TAG, "uri: /long");
  91. // This handler is first invoked on the httpd thread.
  92. // In order to free the httpd thread to handle other requests,
  93. // we must resubmit our request to be handled on an async worker thread.
  94. if (is_on_async_worker_thread() == false) {
  95. // submit
  96. if (submit_async_req(req, long_async_handler) == ESP_OK) {
  97. return ESP_OK;
  98. } else {
  99. httpd_resp_set_status(req, "503 Busy");
  100. httpd_resp_sendstr(req, "<div> no workers available. server busy.</div>");
  101. return ESP_OK;
  102. }
  103. }
  104. // track the number of long requests
  105. static uint8_t req_count = 0;
  106. req_count++;
  107. // send a request count
  108. char s[100];
  109. snprintf(s, sizeof(s), "<div>req: %u</div>\n", req_count);
  110. httpd_resp_sendstr_chunk(req, s);
  111. // then every second, send a "tick"
  112. for (int i = 0; i < 60; i++) {
  113. // This delay makes this a "long running task".
  114. // In a real application, this may be a long calculation,
  115. // or some IO dependent code for instance.
  116. vTaskDelay(pdMS_TO_TICKS(1000));
  117. // send a tick
  118. snprintf(s, sizeof(s), "<div>%u</div>\n", i);
  119. httpd_resp_sendstr_chunk(req, s);
  120. }
  121. // send "complete"
  122. httpd_resp_sendstr_chunk(req, NULL);
  123. return ESP_OK;
  124. }
  125. static void async_req_worker_task(void *p)
  126. {
  127. ESP_LOGI(TAG, "starting async req task worker");
  128. while (true) {
  129. // counting semaphore - this signals that a worker
  130. // is ready to accept work
  131. xSemaphoreGive(worker_ready_count);
  132. // wait for a request
  133. httpd_async_req_t async_req;
  134. if (xQueueReceive(async_req_queue, &async_req, portMAX_DELAY)) {
  135. ESP_LOGI(TAG, "invoking %s", async_req.req->uri);
  136. // call the handler
  137. async_req.handler(async_req.req);
  138. // Inform the server that it can purge the socket used for
  139. // this request, if needed.
  140. if (httpd_req_async_handler_complete(async_req.req) != ESP_OK) {
  141. ESP_LOGE(TAG, "failed to complete async req");
  142. }
  143. }
  144. }
  145. ESP_LOGW(TAG, "worker stopped");
  146. vTaskDelete(NULL);
  147. }
  148. static void start_async_req_workers(void)
  149. {
  150. // counting semaphore keeps track of available workers
  151. worker_ready_count = xSemaphoreCreateCounting(
  152. CONFIG_EXAMPLE_MAX_ASYNC_REQUESTS, // Max Count
  153. 0); // Initial Count
  154. if (worker_ready_count == NULL) {
  155. ESP_LOGE(TAG, "Failed to create workers counting Semaphore");
  156. return;
  157. }
  158. // create queue
  159. async_req_queue = xQueueCreate(1, sizeof(httpd_async_req_t));
  160. if (async_req_queue == NULL){
  161. ESP_LOGE(TAG, "Failed to create async_req_queue");
  162. vSemaphoreDelete(worker_ready_count);
  163. return;
  164. }
  165. // start worker tasks
  166. for (int i = 0; i < CONFIG_EXAMPLE_MAX_ASYNC_REQUESTS; i++) {
  167. bool success = xTaskCreate(async_req_worker_task, "async_req_worker",
  168. ASYNC_WORKER_TASK_STACK_SIZE, // stack size
  169. (void *)0, // argument
  170. ASYNC_WORKER_TASK_PRIORITY, // priority
  171. &worker_handles[i]);
  172. if (!success) {
  173. ESP_LOGE(TAG, "Failed to start asyncReqWorker");
  174. continue;
  175. }
  176. }
  177. }
  178. /* A quick HTTP GET handler, which does not
  179. use any asynchronous features */
  180. static esp_err_t quick_handler(httpd_req_t *req)
  181. {
  182. ESP_LOGI(TAG, "uri: /quick");
  183. char s[100];
  184. snprintf(s, sizeof(s), "random: %u\n", rand());
  185. httpd_resp_sendstr(req, s);
  186. return ESP_OK;
  187. }
  188. static esp_err_t index_handler(httpd_req_t *req)
  189. {
  190. ESP_LOGI(TAG, "uri: /");
  191. const char* html = "<div><a href=\"/long\">long</a></div>"
  192. "<div><a href=\"/quick\">quick</a></div>";
  193. httpd_resp_sendstr(req, html);
  194. return ESP_OK;
  195. }
  196. static httpd_handle_t start_webserver(void)
  197. {
  198. httpd_handle_t server = NULL;
  199. httpd_config_t config = HTTPD_DEFAULT_CONFIG();
  200. config.lru_purge_enable = true;
  201. // It is advisable that httpd_config_t->max_open_sockets > MAX_ASYNC_REQUESTS
  202. // Why? This leaves at least one socket still available to handle
  203. // quick synchronous requests. Otherwise, all the sockets will
  204. // get taken by the long async handlers, and your server will no
  205. // longer be responsive.
  206. config.max_open_sockets = CONFIG_EXAMPLE_MAX_ASYNC_REQUESTS + 1;
  207. // Start the httpd server
  208. ESP_LOGI(TAG, "Starting server on port: '%d'", config.server_port);
  209. if (httpd_start(&server, &config) != ESP_OK) {
  210. ESP_LOGI(TAG, "Error starting server!");
  211. return NULL;
  212. }
  213. const httpd_uri_t index_uri = {
  214. .uri = "/",
  215. .method = HTTP_GET,
  216. .handler = index_handler,
  217. };
  218. const httpd_uri_t long_uri = {
  219. .uri = "/long",
  220. .method = HTTP_GET,
  221. .handler = long_async_handler,
  222. };
  223. const httpd_uri_t quick_uri = {
  224. .uri = "/quick",
  225. .method = HTTP_GET,
  226. .handler = quick_handler,
  227. };
  228. // Set URI handlers
  229. ESP_LOGI(TAG, "Registering URI handlers");
  230. httpd_register_uri_handler(server, &index_uri);
  231. httpd_register_uri_handler(server, &long_uri);
  232. httpd_register_uri_handler(server, &quick_uri);
  233. return server;
  234. }
  235. static esp_err_t stop_webserver(httpd_handle_t server)
  236. {
  237. // Stop the httpd server
  238. return httpd_stop(server);
  239. }
  240. static void disconnect_handler(void* arg, esp_event_base_t event_base,
  241. int32_t event_id, void* event_data)
  242. {
  243. httpd_handle_t* server = (httpd_handle_t*) arg;
  244. if (*server) {
  245. ESP_LOGI(TAG, "Stopping webserver");
  246. if (stop_webserver(*server) == ESP_OK) {
  247. *server = NULL;
  248. } else {
  249. ESP_LOGE(TAG, "Failed to stop http server");
  250. }
  251. }
  252. }
  253. static void connect_handler(void* arg, esp_event_base_t event_base,
  254. int32_t event_id, void* event_data)
  255. {
  256. httpd_handle_t* server = (httpd_handle_t*) arg;
  257. if (*server == NULL) {
  258. ESP_LOGI(TAG, "Starting webserver");
  259. *server = start_webserver();
  260. }
  261. }
  262. void app_main(void)
  263. {
  264. static httpd_handle_t server = NULL;
  265. ESP_ERROR_CHECK(nvs_flash_init());
  266. ESP_ERROR_CHECK(esp_netif_init());
  267. ESP_ERROR_CHECK(esp_event_loop_create_default());
  268. /* This helper function configures Wi-Fi or Ethernet, as selected in menuconfig.
  269. * Read "Establishing Wi-Fi or Ethernet Connection" section in
  270. * examples/protocols/README.md for more information about this function.
  271. */
  272. ESP_ERROR_CHECK(example_connect());
  273. /* Register event handlers to stop the server when Wi-Fi or Ethernet is disconnected,
  274. * and re-start it upon connection.
  275. */
  276. #ifdef CONFIG_EXAMPLE_CONNECT_WIFI
  277. ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &connect_handler, &server));
  278. ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_STA_DISCONNECTED, &disconnect_handler, &server));
  279. #endif // CONFIG_EXAMPLE_CONNECT_WIFI
  280. #ifdef CONFIG_EXAMPLE_CONNECT_ETHERNET
  281. ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_ETH_GOT_IP, &connect_handler, &server));
  282. ESP_ERROR_CHECK(esp_event_handler_register(ETH_EVENT, ETHERNET_EVENT_DISCONNECTED, &disconnect_handler, &server));
  283. #endif // CONFIG_EXAMPLE_CONNECT_ETHERNET
  284. // start workers
  285. start_async_req_workers();
  286. /* Start the server for the first time */
  287. server = start_webserver();
  288. }