Merge branch 'feature/ws_server_subprotocols' into 'master'

http_server: adds support for setting websocket subprotocol

See merge request espressif/esp-idf!10783
pull/6275/head
David Čermák 2020-11-19 16:29:19 +08:00
commit 4f0428a811
4 zmienionych plików z 98 dodań i 5 usunięć

Wyświetl plik

@ -412,6 +412,11 @@ typedef struct httpd_uri {
* This is used if a custom processing of the control frames is needed
*/
bool handle_ws_control_frames;
/**
* Pointer to subprotocol supported by URI
*/
const char *supported_subprotocol;
#endif
} httpd_uri_t;

Wyświetl plik

@ -492,7 +492,8 @@ int httpd_default_recv(httpd_handle_t hd, int sockfd, char *buf, size_t buf_len,
/**
* @brief This function is for responding a WebSocket handshake
*
* @param[in] req Pointer to handshake request that will be handled
* @param[in] req Pointer to handshake request that will be handled
* @param[in] supported_subprotocol Pointer to the subprotocol supported by this URI
* @return
* - ESP_OK : When handshake is sucessful
* - ESP_ERR_NOT_FOUND : When some headers (Sec-WebSocket-*) are not found
@ -501,7 +502,7 @@ int httpd_default_recv(httpd_handle_t hd, int sockfd, char *buf, size_t buf_len,
* - 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);
esp_err_t httpd_ws_respond_server_handshake(httpd_req_t *req, const char *supported_subprotocol);
/**
* @brief This function is for getting a frame type

Wyświetl plik

@ -175,6 +175,11 @@ esp_err_t httpd_register_uri_handler(httpd_handle_t handle,
#ifdef CONFIG_HTTPD_WS_SUPPORT
hd->hd_calls[i]->is_websocket = uri_handler->is_websocket;
hd->hd_calls[i]->handle_ws_control_frames = uri_handler->handle_ws_control_frames;
if (uri_handler->supported_subprotocol) {
hd->hd_calls[i]->supported_subprotocol = strdup(uri_handler->supported_subprotocol);
} else {
hd->hd_calls[i]->supported_subprotocol = NULL;
}
#endif
ESP_LOGD(TAG, LOG_FMT("[%d] installed %s"), i, uri_handler->uri);
return ESP_OK;
@ -316,7 +321,7 @@ esp_err_t httpd_uri(struct httpd_data *hd)
struct httpd_req_aux *aux = req->aux;
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);
esp_err_t ret = httpd_ws_respond_server_handshake(&hd->hd_req, uri->supported_subprotocol);
if (ret != ESP_OK) {
return ret;
}

Wyświetl plik

@ -15,6 +15,7 @@
#include <stdlib.h>
#include <string.h>
#include <sys/random.h>
#include <esp_log.h>
#include <esp_err.h>
@ -44,7 +45,50 @@ static const char *TAG="httpd_ws";
*/
static const char ws_magic_uuid[] = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
esp_err_t httpd_ws_respond_server_handshake(httpd_req_t *req)
/* Checks if any subprotocols from the comma seperated list matches the supported one
*
* Returns true if the response should contain a protocol field
*/
/**
* @brief Checks if any subprotocols from the comma seperated list matches the supported one
*
* @param supported_subprotocol[in] The subprotocol supported by the URI
* @param subprotocol[in], [in]: A comma seperate list of subprotocols requested
* @param buf_len Length of the buffer
* @return true: found a matching subprotocol
* @return false
*/
static bool httpd_ws_get_response_subprotocol(const char *supported_subprotocol, char *subprotocol, size_t buf_len)
{
/* Request didnt contain any subprotocols */
if (strnlen(subprotocol, buf_len) == 0) {
return false;
}
if (supported_subprotocol == NULL) {
ESP_LOGW(TAG, "Sec-WebSocket-Protocol %s not supported, URI do not support any subprotocols", subprotocol);
return false;
}
/* Get first subprotocol from comma seperated list */
char *rest = NULL;
char *s = strtok_r(subprotocol, ", ", &rest);
do {
if (strncmp(s, supported_subprotocol, sizeof(subprotocol)) == 0) {
ESP_LOGD(TAG, "Requested subprotocol supported: %s", s);
return true;
}
} while ((s = strtok_r(NULL, ", ", &rest)) != NULL);
ESP_LOGW(TAG, "Sec-WebSocket-Protocol %s not supported, supported subprotocol is %s", subprotocol, supported_subprotocol);
/* No matches */
return false;
}
esp_err_t httpd_ws_respond_server_handshake(httpd_req_t *req, const char *supported_subprotocol)
{
/* Probe if input parameters are valid or not */
if (!req || !req->aux) {
@ -101,18 +145,56 @@ esp_err_t httpd_ws_respond_server_handshake(httpd_req_t *req)
ESP_LOGD(TAG, LOG_FMT("Generated server key: %s"), server_key_encoded);
char subprotocol[50] = { '\0' };
if (httpd_req_get_hdr_value_str(req, "Sec-WebSocket-Protocol", subprotocol, sizeof(subprotocol) - 1) == ESP_ERR_HTTPD_RESULT_TRUNC) {
ESP_LOGW(TAG, "Sec-WebSocket-Protocol length exceeded buffer size of %d, was trunctated", sizeof(subprotocol));
}
/* 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);
"Sec-WebSocket-Accept: %s\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;
}
if ( httpd_ws_get_response_subprotocol(supported_subprotocol, subprotocol, sizeof(subprotocol))) {
ESP_LOGD(TAG, "subprotocol: %s", subprotocol);
int r = snprintf(tx_buf + fmt_len, sizeof(tx_buf) - fmt_len, "Sec-WebSocket-Protocol: %s\r\n", supported_subprotocol);
if (r <= 0) {
ESP_LOGE(TAG, "Error in response generation"
"(snprintf of subprotocol returned %d, buffer size: %d", r, sizeof(tx_buf));
return ESP_FAIL;
}
fmt_len += r;
if (fmt_len >= sizeof(tx_buf)) {
ESP_LOGE(TAG, "Error in response generation"
"(snprintf of subprotocol returned %d, desired response len: %d, buffer size: %d", r, fmt_len, sizeof(tx_buf));
return ESP_FAIL;
}
}
int r = snprintf(tx_buf + fmt_len, sizeof(tx_buf) - fmt_len, "\r\n");
if (r <= 0) {
ESP_LOGE(TAG, "Error in response generation"
"(snprintf of subprotocol returned %d, buffer size: %d", r, sizeof(tx_buf));
return ESP_FAIL;
}
fmt_len += r;
if (fmt_len >= sizeof(tx_buf)) {
ESP_LOGE(TAG, "Error in response generation"
"(snprintf of header terminal returned %d, desired response len: %d, buffer size: %d", r, fmt_len, sizeof(tx_buf));
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"));