diff --git a/components/esp_http_server/include/esp_http_server.h b/components/esp_http_server/include/esp_http_server.h index d583bf1d60..1c8b78583f 100644 --- a/components/esp_http_server/include/esp_http_server.h +++ b/components/esp_http_server/include/esp_http_server.h @@ -1603,6 +1603,11 @@ typedef struct httpd_ws_frame { size_t len; /*!< Length of the WebSocket data */ } httpd_ws_frame_t; +/** + * @brief Transfer complete callback + */ +typedef void (*transfer_complete_cb)(esp_err_t err, int socket, void *arg); + /** * @brief Receive and parse a WebSocket frame * @@ -1663,6 +1668,35 @@ esp_err_t httpd_ws_send_frame_async(httpd_handle_t hd, int fd, httpd_ws_frame_t */ httpd_ws_client_info_t httpd_ws_get_fd_info(httpd_handle_t hd, int fd); +/** + * @brief Sends data to to specified websocket synchronously + * + * @param[in] handle Server instance data + * @param[in] socket Socket descriptor + * @param[in] frame Websocket frame + * @return + * - ESP_OK : On successful + * - ESP_FAIL : When socket errors occurs + * - ESP_ERR_NO_MEM : Unable to allocate memory + */ +esp_err_t httpd_ws_send_data(httpd_handle_t handle, int socket, httpd_ws_frame_t *frame); + +/** + * @brief Sends data to to specified websocket asynchronously + * + * @param[in] handle Server instance data + * @param[in] socket Socket descriptor + * @param[in] frame Websocket frame + * @param[in] callback Callback invoked after sending data + * @param[in] arg User data passed to provided callback + * @return + * - ESP_OK : On successful + * - ESP_FAIL : When socket errors occurs + * - ESP_ERR_NO_MEM : Unable to allocate memory + */ +esp_err_t httpd_ws_send_data_async(httpd_handle_t handle, int socket, httpd_ws_frame_t *frame, + transfer_complete_cb callback, void *arg); + #endif /* CONFIG_HTTPD_WS_SUPPORT */ /** End of WebSocket related stuff * @} diff --git a/components/esp_http_server/src/httpd_ws.c b/components/esp_http_server/src/httpd_ws.c index d4597b464b..4a4c2373df 100644 --- a/components/esp_http_server/src/httpd_ws.c +++ b/components/esp_http_server/src/httpd_ws.c @@ -16,9 +16,23 @@ #include #include "esp_httpd_priv.h" +#include "freertos/event_groups.h" #ifdef CONFIG_HTTPD_WS_SUPPORT +#define WS_SEND_OK (1 << 0) +#define WS_SEND_FAILED (1 << 1) + +typedef struct { + httpd_ws_frame_t frame; + httpd_handle_t handle; + int socket; + transfer_complete_cb callback; + void *arg; + bool blocking; + EventGroupHandle_t transfer_done; +} async_transfer_t; + static const char *TAG="httpd_ws"; /* @@ -484,4 +498,77 @@ httpd_ws_client_info_t httpd_ws_get_fd_info(httpd_handle_t hd, int fd) return is_active_ws ? HTTPD_WS_CLIENT_WEBSOCKET : HTTPD_WS_CLIENT_HTTP; } +static void httpd_ws_send_cb(void *arg) +{ + async_transfer_t *trans = arg; + + esp_err_t err = httpd_ws_send_frame_async(trans->handle, trans->socket, &trans->frame); + + if (trans->blocking) { + xEventGroupSetBits(trans->transfer_done, err ? WS_SEND_FAILED : WS_SEND_OK); + } else if (trans->callback) { + trans->callback(err, trans->socket, trans->arg); + } + + free(trans); +} + +esp_err_t httpd_ws_send_data(httpd_handle_t handle, int socket, httpd_ws_frame_t *frame) +{ + async_transfer_t *transfer = calloc(1, sizeof(async_transfer_t)); + if (transfer == NULL) { + return ESP_ERR_NO_MEM; + } + + EventGroupHandle_t transfer_done = xEventGroupCreate(); + if (!transfer_done) { + free(transfer); + return ESP_ERR_NO_MEM; + } + + transfer->blocking = true; + transfer->handle = handle; + transfer->socket = socket; + transfer->transfer_done = transfer_done; + memcpy(&transfer->frame, frame, sizeof(httpd_ws_frame_t)); + + esp_err_t err = httpd_queue_work(handle, httpd_ws_send_cb, transfer); + if (err != ESP_OK) { + vEventGroupDelete(transfer_done); + free(transfer); + return err; + } + + EventBits_t status = xEventGroupWaitBits(transfer_done, WS_SEND_OK | WS_SEND_FAILED, + pdTRUE, pdFALSE, portMAX_DELAY); + + vEventGroupDelete(transfer_done); + + return (status & WS_SEND_OK) ? ESP_OK : ESP_FAIL; +} + +esp_err_t httpd_ws_send_data_async(httpd_handle_t handle, int socket, httpd_ws_frame_t *frame, + transfer_complete_cb callback, void *arg) +{ + async_transfer_t *transfer = calloc(1, sizeof(async_transfer_t)); + if (transfer == NULL) { + return ESP_ERR_NO_MEM; + } + + transfer->arg = arg; + transfer->callback = callback; + transfer->handle = handle; + transfer->socket = socket; + memcpy(&transfer->frame, frame, sizeof(httpd_ws_frame_t)); + + esp_err_t err = httpd_queue_work(handle, httpd_ws_send_cb, transfer); + + if (err) { + free(transfer); + return err; + } + + return ESP_OK; +} + #endif /* CONFIG_HTTPD_WS_SUPPORT */