http_server: adds WebSocket support

This commit adds the WebSocket support for esp_http_server
library. It mainly does:

- Handling WebSocket handshake
  - Parsing HTTP upgrade request
  - Reply the upgrade request
- Receive WebSocket packets
  - Parse header, decode to a struct
  - Unmask payload (if required)
- Send WebSocket frames
- Receive WebSocket frame
- Automatic control frame handling

Merges https://github.com/espressif/esp-idf/pull/4306
Closes https://github.com/espressif/esp-idf/issues/4819
This commit is contained in:
Jackson Ming Hu
2019-11-04 20:51:59 +11:00
committed by bot
parent 5172724092
commit e983042af2
8 changed files with 597 additions and 8 deletions
@@ -71,6 +71,11 @@ struct sock_db {
uint64_t lru_counter; /*!< LRU Counter indicating when the socket was last used */
char pending_data[PARSER_BLOCK_SIZE]; /*!< Buffer for pending data to be received */
size_t pending_len; /*!< Length of pending data to be received */
#ifdef CONFIG_HTTPD_WS_SUPPORT
bool ws_handshake_done; /*!< True if it has done WebSocket handshake (if this socket is a valid WS) */
bool ws_close; /*!< Set to true to close the socket later (when WS Close frame received) */
esp_err_t (*ws_handler)(httpd_req_t *r); /*!< WebSocket handler, leave to null if it's not WebSocket */
#endif
};
/**
@@ -91,6 +96,11 @@ struct httpd_req_aux {
const char *value;
} *resp_hdrs; /*!< Additional headers in response packet */
struct http_parser_url url_parse_res; /*!< URL parsing result, used for retrieving URL elements */
#ifdef CONFIG_HTTPD_WS_SUPPORT
bool ws_handshake_detect; /*!< WebSocket handshake detection flag */
httpd_ws_type_t ws_type; /*!< WebSocket frame type */
bool ws_final; /*!< WebSocket FIN bit (final frame or not) */
#endif
};
/**
@@ -471,6 +481,44 @@ int httpd_default_recv(httpd_handle_t hd, int sockfd, char *buf, size_t buf_len,
* @}
*/
/* ************** Group: WebSocket ************** */
/** @name WebSocket
* Functions for WebSocket header parsing
* @{
*/
/**
* @brief This function is for responding a WebSocket handshake
*
* @param[in] req Pointer to handshake request that will be handled
* @return
* - ESP_OK : When handshake is sucessful
* - ESP_ERR_NOT_FOUND : When some headers (Sec-WebSocket-*) are not found
* - ESP_ERR_INVALID_VERSION : The WebSocket version is not "13"
* - ESP_ERR_INVALID_STATE : Handshake was done beforehand
* - ESP_ERR_INVALID_ARG : Argument is invalid (null or non-WebSocket)
* - ESP_FAIL : Socket failures
*/
esp_err_t httpd_ws_respond_server_handshake(httpd_req_t *req);
/**
* @brief This function is for getting a frame type
* and responding a WebSocket control frame automatically
*
* @param[in] req Pointer to handshake request that will be handled
* @return
* - ESP_OK : When handshake is sucessful
* - ESP_ERR_INVALID_ARG : Argument is invalid (null or non-WebSocket)
* - ESP_ERR_INVALID_STATE : Received only some parts of a control frame
* - ESP_FAIL : Socket failures
*/
esp_err_t httpd_ws_get_frame_type(httpd_req_t *req);
/** End of WebSocket related functions
* @}
*/
#ifdef __cplusplus
}
#endif
+76 -6
View File
@@ -374,13 +374,36 @@ static esp_err_t cb_headers_complete(http_parser *parser)
ESP_LOGD(TAG, LOG_FMT("bytes read = %d"), parser->nread);
ESP_LOGD(TAG, LOG_FMT("content length = %zu"), r->content_len);
/* Handle upgrade requests - only WebSocket is supported for now */
if (parser->upgrade) {
ESP_LOGW(TAG, LOG_FMT("upgrade from HTTP not supported"));
/* There is no specific HTTP error code to notify the client that
* upgrade is not supported, thus sending 400 Bad Request */
#ifdef CONFIG_HTTPD_WS_SUPPORT
ESP_LOGD(TAG, LOG_FMT("Got an upgrade request"));
/* If there's no "Upgrade" header field, then it's not WebSocket. */
char ws_upgrade_hdr_val[] = "websocket";
if (httpd_req_get_hdr_value_str(r, "Upgrade", ws_upgrade_hdr_val, sizeof(ws_upgrade_hdr_val)) != ESP_OK) {
ESP_LOGW(TAG, LOG_FMT("Upgrade header does not match the length of \"websocket\""));
parser_data->error = HTTPD_400_BAD_REQUEST;
parser_data->status = PARSING_FAILED;
return ESP_FAIL;
}
/* If "Upgrade" field's key is not "websocket", then we should also forget about it. */
if (strcasecmp("websocket", ws_upgrade_hdr_val) != 0) {
ESP_LOGW(TAG, LOG_FMT("Upgrade header found but it's %s"), ws_upgrade_hdr_val);
parser_data->error = HTTPD_400_BAD_REQUEST;
parser_data->status = PARSING_FAILED;
return ESP_FAIL;
}
/* Now set handshake flag to true */
ra->ws_handshake_detect = true;
#else
ESP_LOGD(TAG, LOG_FMT("WS functions has been disabled, Upgrade request is not supported."));
parser_data->error = HTTPD_400_BAD_REQUEST;
parser_data->status = PARSING_FAILED;
return ESP_FAIL;
#endif
}
parser_data->status = PARSING_BODY;
@@ -667,6 +690,7 @@ static void init_req_aux(struct httpd_req_aux *ra, httpd_config_t *config)
ra->first_chunk_sent = 0;
ra->req_hdrs_count = 0;
ra->resp_hdrs_count = 0;
ra->ws_handshake_detect = false;
memset(ra->resp_hdrs, 0, config->max_resp_headers * sizeof(struct resp_hdr));
}
@@ -678,6 +702,13 @@ static void httpd_req_cleanup(httpd_req_t *r)
if ((r->ignore_sess_ctx_changes == false) && (ra->sd->ctx != r->sess_ctx)) {
httpd_sess_free_ctx(ra->sd->ctx, ra->sd->free_ctx);
}
/* Close the socket when a WebSocket Close request is received */
if (ra->sd->ws_close) {
ESP_LOGD(TAG, LOG_FMT("Try closing WS connection at FD: %d"), ra->sd->fd);
httpd_sess_trigger_close(r->handle, ra->sd->fd);
}
/* Retrieve session info from the request into the socket database. */
ra->sd->ctx = r->sess_ctx;
ra->sd->free_ctx = r->free_ctx;
@@ -699,23 +730,62 @@ esp_err_t httpd_req_new(struct httpd_data *hd, struct sock_db *sd)
init_req_aux(&hd->hd_req_aux, &hd->config);
r->handle = hd;
r->aux = &hd->hd_req_aux;
/* Associate the request to the socket */
struct httpd_req_aux *ra = r->aux;
ra->sd = sd;
/* Set defaults */
ra->status = (char *)HTTPD_200;
ra->content_type = (char *)HTTPD_TYPE_TEXT;
ra->first_chunk_sent = false;
/* Copy session info to the request */
r->sess_ctx = sd->ctx;
r->free_ctx = sd->free_ctx;
r->ignore_sess_ctx_changes = sd->ignore_sess_ctx_changes;
esp_err_t ret;
#ifdef CONFIG_HTTPD_WS_SUPPORT
/* Handle WebSocket */
ESP_LOGD(TAG, LOG_FMT("New request, has WS? %s, sd->ws_handler valid? %s, sd->ws_close? %s"),
sd->ws_handshake_done ? "Yes" : "No",
sd->ws_handler != NULL ? "Yes" : "No",
sd->ws_close ? "Yes" : "No");
if (sd->ws_handshake_done && sd->ws_handler != NULL) {
ESP_LOGD(TAG, LOG_FMT("New WS request from existing socket"));
ret = httpd_ws_get_frame_type(r);
/* Stop and return here immediately if it's a CLOSE frame */
if (ra->ws_type == HTTPD_WS_TYPE_CLOSE) {
sd->ws_close = true;
return ret;
}
/* Ignore PONG frame, as this is a server */
if (ra->ws_type == HTTPD_WS_TYPE_PONG) {
return ret;
}
/* Call handler if it's a non-control frame */
if (ret == ESP_OK && ra->ws_type < HTTPD_WS_TYPE_CLOSE) {
ret = sd->ws_handler(r);
}
if (ret != ESP_OK) {
httpd_req_cleanup(r);
}
return ret;
}
#endif
/* Parse request */
esp_err_t err = httpd_parse_req(hd);
if (err != ESP_OK) {
ret = httpd_parse_req(hd);
if (ret != ESP_OK) {
httpd_req_cleanup(r);
}
return err;
return ret;
}
/* Function that resets the http request data
+3 -1
View File
@@ -281,7 +281,9 @@ bool httpd_sess_pending(struct httpd_data *hd, int fd)
if (sd->pending_fn) {
// test if there's any data to be read (besides read() function, which is handled by select() in the main httpd loop)
// this should check e.g. for the SSL data buffer
if (sd->pending_fn(hd, fd) > 0) return true;
if (sd->pending_fn(hd, fd) > 0) {
return true;
}
}
return (sd->pending_len != 0);
@@ -172,6 +172,9 @@ esp_err_t httpd_register_uri_handler(httpd_handle_t handle,
hd->hd_calls[i]->method = uri_handler->method;
hd->hd_calls[i]->handler = uri_handler->handler;
hd->hd_calls[i]->user_ctx = uri_handler->user_ctx;
#ifdef CONFIG_HTTPD_WS_SUPPORT
hd->hd_calls[i]->is_websocket = uri_handler->is_websocket;
#endif
ESP_LOGD(TAG, LOG_FMT("[%d] installed %s"), i, uri_handler->uri);
return ESP_OK;
}
@@ -276,6 +279,7 @@ esp_err_t httpd_uri(struct httpd_data *hd)
{
httpd_uri_t *uri = NULL;
httpd_req_t *req = &hd->hd_req;
struct httpd_req_aux *aux = req->aux;
struct http_parser_url *res = &hd->hd_req_aux.url_parse_res;
/* For conveying URI not found/method not allowed */
@@ -307,6 +311,23 @@ esp_err_t httpd_uri(struct httpd_data *hd)
/* Attach user context data (passed during URI registration) into request */
req->user_ctx = uri->user_ctx;
/* Final step for a WebSocket handshake verification */
#ifdef CONFIG_HTTPD_WS_SUPPORT
if (uri->is_websocket && aux->ws_handshake_detect && uri->method == HTTP_GET) {
ESP_LOGD(TAG, LOG_FMT("Responding WS handshake to sock %d"), aux->sd->fd);
esp_err_t ret = httpd_ws_respond_server_handshake(&hd->hd_req);
if (ret != ESP_OK) {
return ret;
}
aux->sd->ws_handshake_done = true;
aux->sd->ws_handler = uri->handler;
/* Return immediately after handshake, no need to call handler here */
return ESP_OK;
}
#endif
/* Invoke handler */
if (uri->handler(req) != ESP_OK) {
/* Handler returns error, this socket should be closed */
+374
View File
@@ -0,0 +1,374 @@
// Copyright 2020 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <stdlib.h>
#include <sys/random.h>
#include <esp_log.h>
#include <esp_err.h>
#include <mbedtls/sha1.h>
#include <mbedtls/base64.h>
#include <esp_http_server.h>
#include "esp_httpd_priv.h"
#ifdef CONFIG_HTTPD_WS_SUPPORT
#define TAG "httpd_ws"
/*
* Bit masks for WebSocket frames.
* Please refer to RFC6455 Section 5.2 for more details.
*/
#define HTTPD_WS_FIN_BIT 0x80U
#define HTTPD_WS_OPCODE_BITS 0x0fU
#define HTTPD_WS_MASK_BIT 0x80U
#define HTTPD_WS_LENGTH_BITS 0x7fU
/*
* The magic GUID string used for handshake
* Please refer to RFC6455 Section 1.3 for more details.
*/
static const char ws_magic_uuid[] = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
esp_err_t httpd_ws_respond_server_handshake(httpd_req_t *req)
{
/* Probe if input parameters are valid or not */
if (!req || !req->aux) {
ESP_LOGW(TAG, LOG_FMT("Argument is invalid"));
return ESP_ERR_INVALID_ARG;
}
/* Detect handshake - reject if handshake was ALREADY performed */
struct httpd_req_aux *req_aux = req->aux;
if (req_aux->sd->ws_handshake_done) {
ESP_LOGW(TAG, LOG_FMT("State is invalid - Handshake has been performed"));
return ESP_ERR_INVALID_STATE;
}
/* Detect WS version existence */
char version_val[3] = { '\0' };
if (httpd_req_get_hdr_value_str(req, "Sec-WebSocket-Version", version_val, sizeof(version_val)) != ESP_OK) {
ESP_LOGW(TAG, LOG_FMT("\"Sec-WebSocket-Version\" is not found"));
return ESP_ERR_NOT_FOUND;
}
/* Detect if WS version is "13" or not.
* WS version must be 13 for now. Please refer to RFC6455 Section 4.1, Page 18 for more details. */
if (strcasecmp(version_val, "13") != 0) {
ESP_LOGW(TAG, LOG_FMT("\"Sec-WebSocket-Version\" is not \"13\", it is: %s"), version_val);
return ESP_ERR_INVALID_VERSION;
}
/* Grab Sec-WebSocket-Key (client key) from the header */
/* Size of base64 coded string is equal '((input_size * 4) / 3) + (input_size / 96) + 6' including Z-term */
char sec_key_encoded[28] = { '\0' };
if (httpd_req_get_hdr_value_str(req, "Sec-WebSocket-Key", sec_key_encoded, sizeof(sec_key_encoded)) != ESP_OK) {
ESP_LOGW(TAG, LOG_FMT("Cannot find client key"));
return ESP_ERR_NOT_FOUND;
}
/* Prepare server key (Sec-WebSocket-Accept), concat the string */
char server_key_encoded[33] = { '\0' };
uint8_t server_key_hash[20] = { 0 };
char server_raw_text[sizeof(sec_key_encoded) + sizeof(ws_magic_uuid) + 1] = { '\0' };
strcpy(server_raw_text, sec_key_encoded);
strcat(server_raw_text, ws_magic_uuid);
ESP_LOGD(TAG, LOG_FMT("Server key before encoding: %s"), server_raw_text);
/* Generate SHA-1 first and then encode to Base64 */
size_t key_len = strlen(server_raw_text);
mbedtls_sha1_ret((uint8_t *)server_raw_text, key_len, server_key_hash);
size_t encoded_len = 0;
mbedtls_base64_encode((uint8_t *)server_key_encoded, sizeof(server_key_encoded), &encoded_len,
server_key_hash, sizeof(server_key_hash));
ESP_LOGD(TAG, LOG_FMT("Generated server key: %s"), server_key_encoded);
/* Prepare the Switching Protocol response */
char tx_buf[192] = { '\0' };
int fmt_len = snprintf(tx_buf, sizeof(tx_buf),
"HTTP/1.1 101 Switching Protocols\r\n"
"Upgrade: websocket\r\n"
"Connection: Upgrade\r\n"
"Sec-WebSocket-Accept: %s\r\n\r\n", server_key_encoded);
if (fmt_len < 0 || fmt_len > sizeof(tx_buf)) {
ESP_LOGW(TAG, LOG_FMT("Failed to prepare Tx buffer"));
return ESP_FAIL;
}
/* Send off the response */
if (httpd_send(req, tx_buf, fmt_len) < 0) {
ESP_LOGW(TAG, LOG_FMT("Failed to send the response"));
return ESP_FAIL;
}
return ESP_OK;
}
static esp_err_t httpd_ws_check_req(httpd_req_t *req)
{
/* Probe if input parameters are valid or not */
if (!req || !req->aux) {
ESP_LOGW(TAG, LOG_FMT("Argument is null"));
return ESP_ERR_INVALID_ARG;
}
/* Detect handshake - reject if handshake was NOT YET performed */
struct httpd_req_aux *req_aux = req->aux;
if (!req_aux->sd->ws_handshake_done) {
ESP_LOGW(TAG, LOG_FMT("State is invalid - No handshake performed"));
return ESP_ERR_INVALID_STATE;
}
return ESP_OK;
}
static esp_err_t httpd_ws_unmask_payload(uint8_t *payload, size_t len, const uint8_t *mask_key)
{
if (len < 1 || !payload) {
ESP_LOGW(TAG, LOG_FMT("Invalid payload provided"));
return ESP_ERR_INVALID_ARG;
}
for (size_t idx = 0; idx < len; idx++) {
payload[idx] = (payload[idx] ^ mask_key[idx % 4]);
}
return ESP_OK;
}
esp_err_t httpd_ws_recv_frame(httpd_req_t *req, httpd_ws_frame_t *frame, size_t max_len)
{
esp_err_t ret = httpd_ws_check_req(req);
if (ret != ESP_OK) {
return ret;
}
struct httpd_req_aux *aux = req->aux;
if (aux == NULL) {
ESP_LOGW(TAG, LOG_FMT("Invalid Aux pointer"));
return ESP_ERR_INVALID_ARG;
}
if (!frame) {
ESP_LOGW(TAG, LOG_FMT("Frame pointer is invalid"));
return ESP_ERR_INVALID_ARG;
}
/* Assign the frame info from the previous reading */
frame->type = aux->ws_type;
frame->final = aux->ws_final;
/* Grab the second byte */
uint8_t second_byte = 0;
if (httpd_recv_with_opt(req, (char *)&second_byte, sizeof(second_byte), false) <= 0) {
ESP_LOGW(TAG, LOG_FMT("Failed to receive the second byte"));
return ESP_FAIL;
}
/* Parse the second byte */
/* Please refer to RFC6455 Section 5.2 for more details */
bool masked = (second_byte & HTTPD_WS_MASK_BIT) != 0;
/* Interpret length */
uint8_t init_len = second_byte & HTTPD_WS_LENGTH_BITS;
if (init_len < 126) {
/* Case 1: If length is 0-125, then this length bit is 7 bits */
frame->len = init_len;
} else if (init_len == 126) {
/* Case 2: If length byte is 126, then this frame's length bit is 16 bits */
uint8_t length_bytes[2] = { 0 };
if (httpd_recv_with_opt(req, (char *)length_bytes, sizeof(length_bytes), false) <= 0) {
ESP_LOGW(TAG, LOG_FMT("Failed to receive 2 bytes length"));
return ESP_FAIL;
}
frame->len = ((uint32_t)(length_bytes[0] << 8U) | (length_bytes[1]));
} else if (init_len == 127) {
/* Case 3: If length is byte 127, then this frame's length bit is 64 bits */
uint8_t length_bytes[8] = { 0 };
if (httpd_recv_with_opt(req, (char *)length_bytes, sizeof(length_bytes), false) <= 0) {
ESP_LOGW(TAG, LOG_FMT("Failed to receive 2 bytes length"));
return ESP_FAIL;
}
frame->len = (((uint64_t)length_bytes[0] << 56U) |
((uint64_t)length_bytes[1] << 48U) |
((uint64_t)length_bytes[2] << 40U) |
((uint64_t)length_bytes[3] << 32U) |
((uint64_t)length_bytes[4] << 24U) |
((uint64_t)length_bytes[5] << 16U) |
((uint64_t)length_bytes[6] << 8U) |
((uint64_t)length_bytes[7]));
}
/* We only accept the incoming packet length that is smaller than the max_len (or it will overflow the buffer!) */
if (frame->len > max_len) {
ESP_LOGW(TAG, LOG_FMT("WS Message too long"));
return ESP_ERR_INVALID_SIZE;
}
/* If this frame is masked, dump the mask as well */
uint8_t mask_key[4] = { 0 };
if (masked) {
if (httpd_recv_with_opt(req, (char *)mask_key, sizeof(mask_key), false) <= 0) {
ESP_LOGW(TAG, LOG_FMT("Failed to receive mask key"));
return ESP_FAIL;
}
} else {
/* If the WS frame from client to server is not masked, it should be rejected.
* Please refer to RFC6455 Section 5.2 for more details. */
ESP_LOGW(TAG, LOG_FMT("WS frame is not properly masked."));
return ESP_ERR_INVALID_STATE;
}
/* Receive buffer */
/* If there's nothing to receive, return and stop here. */
if (frame->len == 0) {
return ESP_OK;
}
if (frame->payload == NULL) {
ESP_LOGW(TAG, LOG_FMT("Payload buffer is null"));
return ESP_FAIL;
}
if (httpd_recv_with_opt(req, (char *)frame->payload, frame->len, false) <= 0) {
ESP_LOGW(TAG, LOG_FMT("Failed to receive payload"));
return ESP_FAIL;
}
/* Unmask payload */
httpd_ws_unmask_payload(frame->payload, frame->len, mask_key);
return ESP_OK;
}
esp_err_t httpd_ws_send_frame(httpd_req_t *req, httpd_ws_frame_t *frame)
{
esp_err_t ret = httpd_ws_check_req(req);
if (ret != ESP_OK) {
return ret;
}
if (!frame) {
ESP_LOGW(TAG, LOG_FMT("Argument is invalid"));
return ESP_ERR_INVALID_ARG;
}
/* 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 */
header_buf[0] |= frame->type; /* Type (opcode): 4 bits */
if (frame->len <= 125) {
header_buf[1] = frame->len & 0x7fU; /* Length for 7 bits */
tx_len = 2;
} else if (frame->len > 125 && frame->len < UINT16_MAX) {
header_buf[1] = 126; /* Length for 16 bits */
header_buf[2] = (frame->len >> 8U) & 0xffU;
header_buf[3] = frame->len & 0xffU;
tx_len = 4;
} else {
header_buf[1] = 127; /* Length for 64 bits */
uint8_t shift_idx = sizeof(uint64_t) - 1; /* Shift index starts at 7 */
for (int8_t idx = 2; idx > 9; idx--) {
/* Now do shifting (be careful of endianess, i.e. when buffer index is 2, frame length shift index is 7) */
header_buf[idx] = (frame->len >> (uint8_t)(shift_idx * 8)) & 0xffU;
shift_idx--;
}
tx_len = 10;
}
/* WebSocket server does not required to mask response payload, so leave the MASK bit as 0. */
header_buf[1] &= (~HTTPD_WS_MASK_BIT);
/* Send off header */
if (httpd_send(req, (const char *)header_buf, tx_len) < 0) {
ESP_LOGW(TAG, LOG_FMT("Failed to send WS header"));
return ESP_FAIL;
}
/* Send off payload */
if(frame->len > 0 && frame->payload != NULL) {
if (httpd_send(req, (const char *)frame->payload, frame->len) < 0) {
ESP_LOGW(TAG, LOG_FMT("Failed to send WS payload"));
return ESP_FAIL;
}
}
return ESP_OK;
}
esp_err_t httpd_ws_get_frame_type(httpd_req_t *req)
{
esp_err_t ret = httpd_ws_check_req(req);
if (ret != ESP_OK) {
return ret;
}
struct httpd_req_aux *aux = req->aux;
if (aux == NULL) {
ESP_LOGW(TAG, LOG_FMT("Invalid Aux pointer"));
return ESP_ERR_INVALID_ARG;
}
/* Read the first byte from the frame to get the FIN flag and Opcode */
/* Please refer to RFC6455 Section 5.2 for more details */
uint8_t first_byte = 0;
if (httpd_recv_with_opt(req, (char *)&first_byte, sizeof(first_byte), false) <= 0) {
/* If the recv() return code is <= 0, then this socket FD is invalid (i.e. a broken connection) */
/* Here we mark it as a Close message and close it later. */
ESP_LOGW(TAG, LOG_FMT("Failed to read header byte (socket FD invalid), closing socket now"));
aux->ws_final = true;
aux->ws_type = HTTPD_WS_TYPE_CLOSE;
return ESP_OK;
}
ESP_LOGD(TAG, LOG_FMT("First byte received: 0x%02X"), first_byte);
/* Decode the FIN flag and Opcode from the byte */
aux->ws_final = (first_byte & HTTPD_WS_FIN_BIT) != 0;
aux->ws_type = (first_byte & HTTPD_WS_OPCODE_BITS);
/* Reply to PING. For PONG and CLOSE, it will be handled elsewhere. */
if(aux->ws_type == HTTPD_WS_TYPE_PING) {
ESP_LOGD(TAG, LOG_FMT("Got a WS PING frame, Replying PONG..."));
/* Read the rest of the PING frame, for PONG to reply back. */
/* Please refer to RFC6455 Section 5.5.2 for more details */
httpd_ws_frame_t frame;
uint8_t frame_buf[128] = { 0 };
memset(&frame, 0, sizeof(httpd_ws_frame_t));
frame.payload = frame_buf;
if(httpd_ws_recv_frame(req, &frame, 126) != ESP_OK) {
ESP_LOGD(TAG, LOG_FMT("Cannot receive the full PING frame"));
return ESP_ERR_INVALID_STATE;
}
/* Now turn the frame to PONG */
frame.type = HTTPD_WS_TYPE_PONG;
return httpd_ws_send_frame(req, &frame);
}
return ESP_OK;
}
#endif /* CONFIG_HTTPD_WS_SUPPORT */