From 4da38b6769d9ce8434ca98f59e2b0d054e31748b Mon Sep 17 00:00:00 2001 From: David Cermak Date: Fri, 19 Jun 2020 08:20:33 +0200 Subject: [PATCH 1/2] http_server example: WebSocket server to set final flag in async messages Closes https://github.com/espressif/esp-idf/issues/5405 --- examples/protocols/http_server/ws_echo_server/README.md | 1 + .../protocols/http_server/ws_echo_server/main/ws_echo_server.c | 1 + 2 files changed, 2 insertions(+) diff --git a/examples/protocols/http_server/ws_echo_server/README.md b/examples/protocols/http_server/ws_echo_server/README.md index 61bcc0fbbd..2d330be4d9 100644 --- a/examples/protocols/http_server/ws_echo_server/README.md +++ b/examples/protocols/http_server/ws_echo_server/README.md @@ -10,6 +10,7 @@ ws_server_example_test.py could be used as a simple WS client). The server registers WebSocket handler which echoes back the received WebSocket frame. It also demonstrates use of asynchronous send, which is triggered on reception of a certain message. +Please note that the WebSocket HTTPD server does not automatically fragment messages, so the application code must deal with fragmentation, setting `final` flag as appropriate. Single (complete/unfragmented) WebSocket frames must have the `FIN` flag set to be accepted as valid (see [RFC6455, section 5.4](https://tools.ietf.org/html/rfc6455#section-5.4)). ### Hardware Required diff --git a/examples/protocols/http_server/ws_echo_server/main/ws_echo_server.c b/examples/protocols/http_server/ws_echo_server/main/ws_echo_server.c index e10b0ba606..cab642101b 100644 --- a/examples/protocols/http_server/ws_echo_server/main/ws_echo_server.c +++ b/examples/protocols/http_server/ws_echo_server/main/ws_echo_server.c @@ -48,6 +48,7 @@ static void ws_async_send(void *arg) ws_pkt.payload = (uint8_t*)data; ws_pkt.len = strlen(data); ws_pkt.type = HTTPD_WS_TYPE_TEXT; + ws_pkt.final = true; httpd_ws_send_frame_async(hd, fd, &ws_pkt); free(resp_arg); From ec2262e5a401d4de024eac12c6c31d7a2354c411 Mon Sep 17 00:00:00 2001 From: David Cermak Date: Fri, 19 Jun 2020 10:54:47 +0200 Subject: [PATCH 2/2] http_server: WebSocket server to set flag in transmitted messages by default Add logic to set `FIN` flag automatically for transmitted WS frames, but if `fragmented` option set indicating an expert/manual mode, then the `FIN` flag is set according to the `final` option. --- components/esp_http_server/include/esp_http_server.h | 10 +++++++++- components/esp_http_server/src/httpd_ws.c | 6 ++++-- .../protocols/http_server/ws_echo_server/README.md | 7 ++++++- .../http_server/ws_echo_server/main/ws_echo_server.c | 1 - 4 files changed, 19 insertions(+), 5 deletions(-) diff --git a/components/esp_http_server/include/esp_http_server.h b/components/esp_http_server/include/esp_http_server.h index e1e5c288d9..c89b78c530 100644 --- a/components/esp_http_server/include/esp_http_server.h +++ b/components/esp_http_server/include/esp_http_server.h @@ -1483,7 +1483,15 @@ typedef enum { * @brief WebSocket frame format */ typedef struct httpd_ws_frame { - bool final; /*!< Final frame */ + bool final; /*!< Final frame: + For received frames this field indicates whether the `FIN` flag was set. + For frames to be transmitted, this field is only used if the `fragmented` + option is set as well. If `fragmented` is false, the `FIN` flag is set + by default, marking the ws_frame as a complete/unfragmented message + (esp_http_server doesn't automatically fragment messages) */ + bool fragmented; /*!< Indication that the frame allocated for transmission is a message fragment, + so the `FIN` flag is set manually according to the `final` option. + This flag is never set for received messages */ httpd_ws_type_t type; /*!< WebSocket frame type */ uint8_t *payload; /*!< Pre-allocated data buffer */ size_t len; /*!< Length of the WebSocket data */ diff --git a/components/esp_http_server/src/httpd_ws.c b/components/esp_http_server/src/httpd_ws.c index d9a1cc7676..a6af5c5d9e 100644 --- a/components/esp_http_server/src/httpd_ws.c +++ b/components/esp_http_server/src/httpd_ws.c @@ -32,6 +32,7 @@ static const char *TAG="httpd_ws"; * Bit masks for WebSocket frames. * Please refer to RFC6455 Section 5.2 for more details. */ +#define HTTPD_WS_CONTINUE 0x00U #define HTTPD_WS_FIN_BIT 0x80U #define HTTPD_WS_OPCODE_BITS 0x0fU #define HTTPD_WS_MASK_BIT 0x80U @@ -279,7 +280,8 @@ esp_err_t httpd_ws_send_frame_async(httpd_handle_t hd, int fd, httpd_ws_frame_t /* Prepare Tx buffer - maximum length is 14, which includes 2 bytes header, 8 bytes length, 4 bytes mask key */ uint8_t tx_len = 0; uint8_t header_buf[10] = {0 }; - header_buf[0] |= frame->final ? HTTPD_WS_FIN_BIT : 0; /* Final (FIN) bit */ + /* Set the `FIN` bit by default if message is not fragmented. Else, set it as per the `final` field */ + header_buf[0] |= (!frame->fragmented) ? HTTPD_WS_FIN_BIT : (frame->final? HTTPD_WS_FIN_BIT: HTTPD_WS_CONTINUE); header_buf[0] |= frame->type; /* Type (opcode): 4 bits */ if (frame->len <= 125) { @@ -381,4 +383,4 @@ esp_err_t httpd_ws_get_frame_type(httpd_req_t *req) return ESP_OK; } -#endif /* CONFIG_HTTPD_WS_SUPPORT */ \ No newline at end of file +#endif /* CONFIG_HTTPD_WS_SUPPORT */ diff --git a/examples/protocols/http_server/ws_echo_server/README.md b/examples/protocols/http_server/ws_echo_server/README.md index 2d330be4d9..8fa4219337 100644 --- a/examples/protocols/http_server/ws_echo_server/README.md +++ b/examples/protocols/http_server/ws_echo_server/README.md @@ -10,7 +10,12 @@ ws_server_example_test.py could be used as a simple WS client). The server registers WebSocket handler which echoes back the received WebSocket frame. It also demonstrates use of asynchronous send, which is triggered on reception of a certain message. -Please note that the WebSocket HTTPD server does not automatically fragment messages, so the application code must deal with fragmentation, setting `final` flag as appropriate. Single (complete/unfragmented) WebSocket frames must have the `FIN` flag set to be accepted as valid (see [RFC6455, section 5.4](https://tools.ietf.org/html/rfc6455#section-5.4)). + +Please note that the WebSocket HTTP server does not automatically fragment messages. +Each outgoing frame has the FIN flag set by default. +In case an application wants to send fragmented data, it must be done manually by setting the +`fragmented` option and using the `final` flag as described in [RFC6455, section 5.4](https://tools.ietf.org/html/rfc6455#section-5.4). + ### Hardware Required diff --git a/examples/protocols/http_server/ws_echo_server/main/ws_echo_server.c b/examples/protocols/http_server/ws_echo_server/main/ws_echo_server.c index cab642101b..e10b0ba606 100644 --- a/examples/protocols/http_server/ws_echo_server/main/ws_echo_server.c +++ b/examples/protocols/http_server/ws_echo_server/main/ws_echo_server.c @@ -48,7 +48,6 @@ static void ws_async_send(void *arg) ws_pkt.payload = (uint8_t*)data; ws_pkt.len = strlen(data); ws_pkt.type = HTTPD_WS_TYPE_TEXT; - ws_pkt.final = true; httpd_ws_send_frame_async(hd, fd, &ws_pkt); free(resp_arg);