httpd_ws.c 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574
  1. /*
  2. * SPDX-FileCopyrightText: 2020-2021 Espressif Systems (Shanghai) CO LTD
  3. *
  4. * SPDX-License-Identifier: Apache-2.0
  5. */
  6. #include <stdlib.h>
  7. #include <string.h>
  8. #include <sys/random.h>
  9. #include <esp_log.h>
  10. #include <esp_err.h>
  11. #include <mbedtls/sha1.h>
  12. #include <mbedtls/base64.h>
  13. #include <esp_http_server.h>
  14. #include "esp_httpd_priv.h"
  15. #include "freertos/event_groups.h"
  16. #ifdef CONFIG_HTTPD_WS_SUPPORT
  17. #define WS_SEND_OK (1 << 0)
  18. #define WS_SEND_FAILED (1 << 1)
  19. typedef struct {
  20. httpd_ws_frame_t frame;
  21. httpd_handle_t handle;
  22. int socket;
  23. transfer_complete_cb callback;
  24. void *arg;
  25. bool blocking;
  26. EventGroupHandle_t transfer_done;
  27. } async_transfer_t;
  28. static const char *TAG="httpd_ws";
  29. /*
  30. * Bit masks for WebSocket frames.
  31. * Please refer to RFC6455 Section 5.2 for more details.
  32. */
  33. #define HTTPD_WS_CONTINUE 0x00U
  34. #define HTTPD_WS_FIN_BIT 0x80U
  35. #define HTTPD_WS_OPCODE_BITS 0x0fU
  36. #define HTTPD_WS_MASK_BIT 0x80U
  37. #define HTTPD_WS_LENGTH_BITS 0x7fU
  38. /*
  39. * The magic GUID string used for handshake
  40. * Please refer to RFC6455 Section 1.3 for more details.
  41. */
  42. static const char ws_magic_uuid[] = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
  43. /* Checks if any subprotocols from the comma seperated list matches the supported one
  44. *
  45. * Returns true if the response should contain a protocol field
  46. */
  47. /**
  48. * @brief Checks if any subprotocols from the comma seperated list matches the supported one
  49. *
  50. * @param supported_subprotocol[in] The subprotocol supported by the URI
  51. * @param subprotocol[in], [in]: A comma seperate list of subprotocols requested
  52. * @param buf_len Length of the buffer
  53. * @return true: found a matching subprotocol
  54. * @return false
  55. */
  56. static bool httpd_ws_get_response_subprotocol(const char *supported_subprotocol, char *subprotocol, size_t buf_len)
  57. {
  58. /* Request didnt contain any subprotocols */
  59. if (strnlen(subprotocol, buf_len) == 0) {
  60. return false;
  61. }
  62. if (supported_subprotocol == NULL) {
  63. ESP_LOGW(TAG, "Sec-WebSocket-Protocol %s not supported, URI do not support any subprotocols", subprotocol);
  64. return false;
  65. }
  66. /* Get first subprotocol from comma seperated list */
  67. char *rest = NULL;
  68. char *s = strtok_r(subprotocol, ", ", &rest);
  69. do {
  70. if (strncmp(s, supported_subprotocol, sizeof(subprotocol)) == 0) {
  71. ESP_LOGD(TAG, "Requested subprotocol supported: %s", s);
  72. return true;
  73. }
  74. } while ((s = strtok_r(NULL, ", ", &rest)) != NULL);
  75. ESP_LOGW(TAG, "Sec-WebSocket-Protocol %s not supported, supported subprotocol is %s", subprotocol, supported_subprotocol);
  76. /* No matches */
  77. return false;
  78. }
  79. esp_err_t httpd_ws_respond_server_handshake(httpd_req_t *req, const char *supported_subprotocol)
  80. {
  81. /* Probe if input parameters are valid or not */
  82. if (!req || !req->aux) {
  83. ESP_LOGW(TAG, LOG_FMT("Argument is invalid"));
  84. return ESP_ERR_INVALID_ARG;
  85. }
  86. /* Detect handshake - reject if handshake was ALREADY performed */
  87. struct httpd_req_aux *req_aux = req->aux;
  88. if (req_aux->sd->ws_handshake_done) {
  89. ESP_LOGW(TAG, LOG_FMT("State is invalid - Handshake has been performed"));
  90. return ESP_ERR_INVALID_STATE;
  91. }
  92. /* Detect WS version existence */
  93. char version_val[3] = { '\0' };
  94. if (httpd_req_get_hdr_value_str(req, "Sec-WebSocket-Version", version_val, sizeof(version_val)) != ESP_OK) {
  95. ESP_LOGW(TAG, LOG_FMT("\"Sec-WebSocket-Version\" is not found"));
  96. return ESP_ERR_NOT_FOUND;
  97. }
  98. /* Detect if WS version is "13" or not.
  99. * WS version must be 13 for now. Please refer to RFC6455 Section 4.1, Page 18 for more details. */
  100. if (strcasecmp(version_val, "13") != 0) {
  101. ESP_LOGW(TAG, LOG_FMT("\"Sec-WebSocket-Version\" is not \"13\", it is: %s"), version_val);
  102. return ESP_ERR_INVALID_VERSION;
  103. }
  104. /* Grab Sec-WebSocket-Key (client key) from the header */
  105. /* Size of base64 coded string is equal '((input_size * 4) / 3) + (input_size / 96) + 6' including Z-term */
  106. char sec_key_encoded[28] = { '\0' };
  107. if (httpd_req_get_hdr_value_str(req, "Sec-WebSocket-Key", sec_key_encoded, sizeof(sec_key_encoded)) != ESP_OK) {
  108. ESP_LOGW(TAG, LOG_FMT("Cannot find client key"));
  109. return ESP_ERR_NOT_FOUND;
  110. }
  111. /* Prepare server key (Sec-WebSocket-Accept), concat the string */
  112. char server_key_encoded[33] = { '\0' };
  113. uint8_t server_key_hash[20] = { 0 };
  114. char server_raw_text[sizeof(sec_key_encoded) + sizeof(ws_magic_uuid) + 1] = { '\0' };
  115. strcpy(server_raw_text, sec_key_encoded);
  116. strcat(server_raw_text, ws_magic_uuid);
  117. ESP_LOGD(TAG, LOG_FMT("Server key before encoding: %s"), server_raw_text);
  118. /* Generate SHA-1 first and then encode to Base64 */
  119. size_t key_len = strlen(server_raw_text);
  120. mbedtls_sha1_ret((uint8_t *)server_raw_text, key_len, server_key_hash);
  121. size_t encoded_len = 0;
  122. mbedtls_base64_encode((uint8_t *)server_key_encoded, sizeof(server_key_encoded), &encoded_len,
  123. server_key_hash, sizeof(server_key_hash));
  124. ESP_LOGD(TAG, LOG_FMT("Generated server key: %s"), server_key_encoded);
  125. char subprotocol[50] = { '\0' };
  126. if (httpd_req_get_hdr_value_str(req, "Sec-WebSocket-Protocol", subprotocol, sizeof(subprotocol) - 1) == ESP_ERR_HTTPD_RESULT_TRUNC) {
  127. ESP_LOGW(TAG, "Sec-WebSocket-Protocol length exceeded buffer size of %d, was trunctated", sizeof(subprotocol));
  128. }
  129. /* Prepare the Switching Protocol response */
  130. char tx_buf[192] = { '\0' };
  131. int fmt_len = snprintf(tx_buf, sizeof(tx_buf),
  132. "HTTP/1.1 101 Switching Protocols\r\n"
  133. "Upgrade: websocket\r\n"
  134. "Connection: Upgrade\r\n"
  135. "Sec-WebSocket-Accept: %s\r\n", server_key_encoded);
  136. if (fmt_len < 0 || fmt_len > sizeof(tx_buf)) {
  137. ESP_LOGW(TAG, LOG_FMT("Failed to prepare Tx buffer"));
  138. return ESP_FAIL;
  139. }
  140. if ( httpd_ws_get_response_subprotocol(supported_subprotocol, subprotocol, sizeof(subprotocol))) {
  141. ESP_LOGD(TAG, "subprotocol: %s", subprotocol);
  142. int r = snprintf(tx_buf + fmt_len, sizeof(tx_buf) - fmt_len, "Sec-WebSocket-Protocol: %s\r\n", supported_subprotocol);
  143. if (r <= 0) {
  144. ESP_LOGE(TAG, "Error in response generation"
  145. "(snprintf of subprotocol returned %d, buffer size: %d", r, sizeof(tx_buf));
  146. return ESP_FAIL;
  147. }
  148. fmt_len += r;
  149. if (fmt_len >= sizeof(tx_buf)) {
  150. ESP_LOGE(TAG, "Error in response generation"
  151. "(snprintf of subprotocol returned %d, desired response len: %d, buffer size: %d", r, fmt_len, sizeof(tx_buf));
  152. return ESP_FAIL;
  153. }
  154. }
  155. int r = snprintf(tx_buf + fmt_len, sizeof(tx_buf) - fmt_len, "\r\n");
  156. if (r <= 0) {
  157. ESP_LOGE(TAG, "Error in response generation"
  158. "(snprintf of subprotocol returned %d, buffer size: %d", r, sizeof(tx_buf));
  159. return ESP_FAIL;
  160. }
  161. fmt_len += r;
  162. if (fmt_len >= sizeof(tx_buf)) {
  163. ESP_LOGE(TAG, "Error in response generation"
  164. "(snprintf of header terminal returned %d, desired response len: %d, buffer size: %d", r, fmt_len, sizeof(tx_buf));
  165. return ESP_FAIL;
  166. }
  167. /* Send off the response */
  168. if (httpd_send(req, tx_buf, fmt_len) < 0) {
  169. ESP_LOGW(TAG, LOG_FMT("Failed to send the response"));
  170. return ESP_FAIL;
  171. }
  172. return ESP_OK;
  173. }
  174. static esp_err_t httpd_ws_check_req(httpd_req_t *req)
  175. {
  176. /* Probe if input parameters are valid or not */
  177. if (!req || !req->aux) {
  178. ESP_LOGW(TAG, LOG_FMT("Argument is null"));
  179. return ESP_ERR_INVALID_ARG;
  180. }
  181. /* Detect handshake - reject if handshake was NOT YET performed */
  182. struct httpd_req_aux *req_aux = req->aux;
  183. if (!req_aux->sd->ws_handshake_done) {
  184. ESP_LOGW(TAG, LOG_FMT("State is invalid - No handshake performed"));
  185. return ESP_ERR_INVALID_STATE;
  186. }
  187. return ESP_OK;
  188. }
  189. static esp_err_t httpd_ws_unmask_payload(uint8_t *payload, size_t len, const uint8_t *mask_key)
  190. {
  191. if (len < 1 || !payload) {
  192. ESP_LOGW(TAG, LOG_FMT("Invalid payload provided"));
  193. return ESP_ERR_INVALID_ARG;
  194. }
  195. for (size_t idx = 0; idx < len; idx++) {
  196. payload[idx] = (payload[idx] ^ mask_key[idx % 4]);
  197. }
  198. return ESP_OK;
  199. }
  200. esp_err_t httpd_ws_recv_frame(httpd_req_t *req, httpd_ws_frame_t *frame, size_t max_len)
  201. {
  202. esp_err_t ret = httpd_ws_check_req(req);
  203. if (ret != ESP_OK) {
  204. return ret;
  205. }
  206. struct httpd_req_aux *aux = req->aux;
  207. if (aux == NULL) {
  208. ESP_LOGW(TAG, LOG_FMT("Invalid Aux pointer"));
  209. return ESP_ERR_INVALID_ARG;
  210. }
  211. if (!frame) {
  212. ESP_LOGW(TAG, LOG_FMT("Frame pointer is invalid"));
  213. return ESP_ERR_INVALID_ARG;
  214. }
  215. /* If frame len is 0, will get frame len from req. Otherwise regard frame len already achieved by calling httpd_ws_recv_frame before */
  216. if (frame->len == 0) {
  217. /* Assign the frame info from the previous reading */
  218. frame->type = aux->ws_type;
  219. frame->final = aux->ws_final;
  220. /* Grab the second byte */
  221. uint8_t second_byte = 0;
  222. if (httpd_recv_with_opt(req, (char *)&second_byte, sizeof(second_byte), false) <= 0) {
  223. ESP_LOGW(TAG, LOG_FMT("Failed to receive the second byte"));
  224. return ESP_FAIL;
  225. }
  226. /* Parse the second byte */
  227. /* Please refer to RFC6455 Section 5.2 for more details */
  228. bool masked = (second_byte & HTTPD_WS_MASK_BIT) != 0;
  229. /* Interpret length */
  230. uint8_t init_len = second_byte & HTTPD_WS_LENGTH_BITS;
  231. if (init_len < 126) {
  232. /* Case 1: If length is 0-125, then this length bit is 7 bits */
  233. frame->len = init_len;
  234. } else if (init_len == 126) {
  235. /* Case 2: If length byte is 126, then this frame's length bit is 16 bits */
  236. uint8_t length_bytes[2] = { 0 };
  237. if (httpd_recv_with_opt(req, (char *)length_bytes, sizeof(length_bytes), false) <= 0) {
  238. ESP_LOGW(TAG, LOG_FMT("Failed to receive 2 bytes length"));
  239. return ESP_FAIL;
  240. }
  241. frame->len = ((uint32_t)(length_bytes[0] << 8U) | (length_bytes[1]));
  242. } else if (init_len == 127) {
  243. /* Case 3: If length is byte 127, then this frame's length bit is 64 bits */
  244. uint8_t length_bytes[8] = { 0 };
  245. if (httpd_recv_with_opt(req, (char *)length_bytes, sizeof(length_bytes), false) <= 0) {
  246. ESP_LOGW(TAG, LOG_FMT("Failed to receive 2 bytes length"));
  247. return ESP_FAIL;
  248. }
  249. frame->len = (((uint64_t)length_bytes[0] << 56U) |
  250. ((uint64_t)length_bytes[1] << 48U) |
  251. ((uint64_t)length_bytes[2] << 40U) |
  252. ((uint64_t)length_bytes[3] << 32U) |
  253. ((uint64_t)length_bytes[4] << 24U) |
  254. ((uint64_t)length_bytes[5] << 16U) |
  255. ((uint64_t)length_bytes[6] << 8U) |
  256. ((uint64_t)length_bytes[7]));
  257. }
  258. /* If this frame is masked, dump the mask as well */
  259. if (masked) {
  260. if (httpd_recv_with_opt(req, (char *)aux->mask_key, sizeof(aux->mask_key), false) <= 0) {
  261. ESP_LOGW(TAG, LOG_FMT("Failed to receive mask key"));
  262. return ESP_FAIL;
  263. }
  264. } else {
  265. /* If the WS frame from client to server is not masked, it should be rejected.
  266. * Please refer to RFC6455 Section 5.2 for more details. */
  267. ESP_LOGW(TAG, LOG_FMT("WS frame is not properly masked."));
  268. return ESP_ERR_INVALID_STATE;
  269. }
  270. }
  271. /* We only accept the incoming packet length that is smaller than the max_len (or it will overflow the buffer!) */
  272. /* If max_len is 0, regard it OK for userspace to get frame len */
  273. if (frame->len > max_len) {
  274. if (max_len == 0) {
  275. ESP_LOGD(TAG, "regard max_len == 0 is OK for user to get frame len");
  276. return ESP_OK;
  277. }
  278. ESP_LOGW(TAG, LOG_FMT("WS Message too long"));
  279. return ESP_ERR_INVALID_SIZE;
  280. }
  281. /* Receive buffer */
  282. /* If there's nothing to receive, return and stop here. */
  283. if (frame->len == 0) {
  284. return ESP_OK;
  285. }
  286. if (frame->payload == NULL) {
  287. ESP_LOGW(TAG, LOG_FMT("Payload buffer is null"));
  288. return ESP_FAIL;
  289. }
  290. size_t left_len = frame->len;
  291. size_t offset = 0;
  292. while (left_len > 0) {
  293. int read_len = httpd_recv_with_opt(req, (char *)frame->payload + offset, left_len, false);
  294. if (read_len <= 0) {
  295. ESP_LOGW(TAG, LOG_FMT("Failed to receive payload"));
  296. return ESP_FAIL;
  297. }
  298. offset += read_len;
  299. left_len -= read_len;
  300. ESP_LOGD(TAG, "Frame length: %d, Bytes Read: %d", frame->len, offset);
  301. }
  302. /* Unmask payload */
  303. httpd_ws_unmask_payload(frame->payload, frame->len, aux->mask_key);
  304. return ESP_OK;
  305. }
  306. esp_err_t httpd_ws_send_frame(httpd_req_t *req, httpd_ws_frame_t *frame)
  307. {
  308. esp_err_t ret = httpd_ws_check_req(req);
  309. if (ret != ESP_OK) {
  310. return ret;
  311. }
  312. return httpd_ws_send_frame_async(req->handle, httpd_req_to_sockfd(req), frame);
  313. }
  314. esp_err_t httpd_ws_send_frame_async(httpd_handle_t hd, int fd, httpd_ws_frame_t *frame)
  315. {
  316. if (!frame) {
  317. ESP_LOGW(TAG, LOG_FMT("Argument is invalid"));
  318. return ESP_ERR_INVALID_ARG;
  319. }
  320. /* Prepare Tx buffer - maximum length is 14, which includes 2 bytes header, 8 bytes length, 4 bytes mask key */
  321. uint8_t tx_len = 0;
  322. uint8_t header_buf[10] = {0 };
  323. /* Set the `FIN` bit by default if message is not fragmented. Else, set it as per the `final` field */
  324. header_buf[0] |= (!frame->fragmented) ? HTTPD_WS_FIN_BIT : (frame->final? HTTPD_WS_FIN_BIT: HTTPD_WS_CONTINUE);
  325. header_buf[0] |= frame->type; /* Type (opcode): 4 bits */
  326. if (frame->len <= 125) {
  327. header_buf[1] = frame->len & 0x7fU; /* Length for 7 bits */
  328. tx_len = 2;
  329. } else if (frame->len > 125 && frame->len < UINT16_MAX) {
  330. header_buf[1] = 126; /* Length for 16 bits */
  331. header_buf[2] = (frame->len >> 8U) & 0xffU;
  332. header_buf[3] = frame->len & 0xffU;
  333. tx_len = 4;
  334. } else {
  335. header_buf[1] = 127; /* Length for 64 bits */
  336. uint8_t shift_idx = sizeof(uint64_t) - 1; /* Shift index starts at 7 */
  337. uint64_t len64 = frame->len; /* Raise variable size to make sure we won't shift by more bits
  338. * than the length has (to avoid undefined behaviour) */
  339. for (int8_t idx = 2; idx <= 9; idx++) {
  340. /* Now do shifting (be careful of endianness, i.e. when buffer index is 2, frame length shift index is 7) */
  341. header_buf[idx] = (len64 >> (shift_idx * 8)) & 0xffU;
  342. shift_idx--;
  343. }
  344. tx_len = 10;
  345. }
  346. /* WebSocket server does not required to mask response payload, so leave the MASK bit as 0. */
  347. header_buf[1] &= (~HTTPD_WS_MASK_BIT);
  348. struct sock_db *sess = httpd_sess_get(hd, fd);
  349. if (!sess) {
  350. return ESP_ERR_INVALID_ARG;
  351. }
  352. /* Send off header */
  353. if (sess->send_fn(hd, fd, (const char *)header_buf, tx_len, 0) < 0) {
  354. ESP_LOGW(TAG, LOG_FMT("Failed to send WS header"));
  355. return ESP_FAIL;
  356. }
  357. /* Send off payload */
  358. if(frame->len > 0 && frame->payload != NULL) {
  359. if (sess->send_fn(hd, fd, (const char *)frame->payload, frame->len, 0) < 0) {
  360. ESP_LOGW(TAG, LOG_FMT("Failed to send WS payload"));
  361. return ESP_FAIL;
  362. }
  363. }
  364. return ESP_OK;
  365. }
  366. esp_err_t httpd_ws_get_frame_type(httpd_req_t *req)
  367. {
  368. esp_err_t ret = httpd_ws_check_req(req);
  369. if (ret != ESP_OK) {
  370. return ret;
  371. }
  372. struct httpd_req_aux *aux = req->aux;
  373. if (aux == NULL) {
  374. ESP_LOGW(TAG, LOG_FMT("Invalid Aux pointer"));
  375. return ESP_ERR_INVALID_ARG;
  376. }
  377. /* Read the first byte from the frame to get the FIN flag and Opcode */
  378. /* Please refer to RFC6455 Section 5.2 for more details */
  379. uint8_t first_byte = 0;
  380. if (httpd_recv_with_opt(req, (char *)&first_byte, sizeof(first_byte), false) <= 0) {
  381. /* If the recv() return code is <= 0, then this socket FD is invalid (i.e. a broken connection) */
  382. /* Here we mark it as a Close message and close it later. */
  383. ESP_LOGW(TAG, LOG_FMT("Failed to read header byte (socket FD invalid), closing socket now"));
  384. aux->ws_final = true;
  385. aux->ws_type = HTTPD_WS_TYPE_CLOSE;
  386. return ESP_OK;
  387. }
  388. ESP_LOGD(TAG, LOG_FMT("First byte received: 0x%02X"), first_byte);
  389. /* Decode the FIN flag and Opcode from the byte */
  390. aux->ws_final = (first_byte & HTTPD_WS_FIN_BIT) != 0;
  391. aux->ws_type = (first_byte & HTTPD_WS_OPCODE_BITS);
  392. /* Reply to PING. For PONG and CLOSE, it will be handled elsewhere. */
  393. if(aux->ws_type == HTTPD_WS_TYPE_PING) {
  394. ESP_LOGD(TAG, LOG_FMT("Got a WS PING frame, Replying PONG..."));
  395. /* Read the rest of the PING frame, for PONG to reply back. */
  396. /* Please refer to RFC6455 Section 5.5.2 for more details */
  397. httpd_ws_frame_t frame;
  398. uint8_t frame_buf[128] = { 0 };
  399. memset(&frame, 0, sizeof(httpd_ws_frame_t));
  400. frame.payload = frame_buf;
  401. if(httpd_ws_recv_frame(req, &frame, 126) != ESP_OK) {
  402. ESP_LOGD(TAG, LOG_FMT("Cannot receive the full PING frame"));
  403. return ESP_ERR_INVALID_STATE;
  404. }
  405. /* Now turn the frame to PONG */
  406. frame.type = HTTPD_WS_TYPE_PONG;
  407. return httpd_ws_send_frame(req, &frame);
  408. }
  409. return ESP_OK;
  410. }
  411. httpd_ws_client_info_t httpd_ws_get_fd_info(httpd_handle_t hd, int fd)
  412. {
  413. struct sock_db *sess = httpd_sess_get(hd, fd);
  414. if (sess == NULL) {
  415. return HTTPD_WS_CLIENT_INVALID;
  416. }
  417. bool is_active_ws = sess->ws_handshake_done && (!sess->ws_close);
  418. return is_active_ws ? HTTPD_WS_CLIENT_WEBSOCKET : HTTPD_WS_CLIENT_HTTP;
  419. }
  420. static void httpd_ws_send_cb(void *arg)
  421. {
  422. async_transfer_t *trans = arg;
  423. esp_err_t err = httpd_ws_send_frame_async(trans->handle, trans->socket, &trans->frame);
  424. if (trans->blocking) {
  425. xEventGroupSetBits(trans->transfer_done, err ? WS_SEND_FAILED : WS_SEND_OK);
  426. } else if (trans->callback) {
  427. trans->callback(err, trans->socket, trans->arg);
  428. }
  429. free(trans);
  430. }
  431. esp_err_t httpd_ws_send_data(httpd_handle_t handle, int socket, httpd_ws_frame_t *frame)
  432. {
  433. async_transfer_t *transfer = calloc(1, sizeof(async_transfer_t));
  434. if (transfer == NULL) {
  435. return ESP_ERR_NO_MEM;
  436. }
  437. EventGroupHandle_t transfer_done = xEventGroupCreate();
  438. if (!transfer_done) {
  439. free(transfer);
  440. return ESP_ERR_NO_MEM;
  441. }
  442. transfer->blocking = true;
  443. transfer->handle = handle;
  444. transfer->socket = socket;
  445. transfer->transfer_done = transfer_done;
  446. memcpy(&transfer->frame, frame, sizeof(httpd_ws_frame_t));
  447. esp_err_t err = httpd_queue_work(handle, httpd_ws_send_cb, transfer);
  448. if (err != ESP_OK) {
  449. vEventGroupDelete(transfer_done);
  450. free(transfer);
  451. return err;
  452. }
  453. EventBits_t status = xEventGroupWaitBits(transfer_done, WS_SEND_OK | WS_SEND_FAILED,
  454. pdTRUE, pdFALSE, portMAX_DELAY);
  455. vEventGroupDelete(transfer_done);
  456. return (status & WS_SEND_OK) ? ESP_OK : ESP_FAIL;
  457. }
  458. esp_err_t httpd_ws_send_data_async(httpd_handle_t handle, int socket, httpd_ws_frame_t *frame,
  459. transfer_complete_cb callback, void *arg)
  460. {
  461. async_transfer_t *transfer = calloc(1, sizeof(async_transfer_t));
  462. if (transfer == NULL) {
  463. return ESP_ERR_NO_MEM;
  464. }
  465. transfer->arg = arg;
  466. transfer->callback = callback;
  467. transfer->handle = handle;
  468. transfer->socket = socket;
  469. memcpy(&transfer->frame, frame, sizeof(httpd_ws_frame_t));
  470. esp_err_t err = httpd_queue_work(handle, httpd_ws_send_cb, transfer);
  471. if (err) {
  472. free(transfer);
  473. return err;
  474. }
  475. return ESP_OK;
  476. }
  477. #endif /* CONFIG_HTTPD_WS_SUPPORT */