123 lines
2.9 KiB
C
123 lines
2.9 KiB
C
/*
|
|
* Copyright (c) 2024 Nordic Semiconductor ASA
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#include <errno.h>
|
|
#include <stdbool.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <strings.h>
|
|
|
|
#include <zephyr/kernel.h>
|
|
#include <zephyr/logging/log.h>
|
|
#include <zephyr/net/http/service.h>
|
|
#include <zephyr/sys/base64.h>
|
|
#include <mbedtls/sha1.h>
|
|
#include <zephyr/net/websocket.h>
|
|
|
|
LOG_MODULE_DECLARE(net_http_server, CONFIG_NET_HTTP_SERVER_LOG_LEVEL);
|
|
|
|
#include "headers/server_internal.h"
|
|
|
|
#if !defined(ZEPHYR_USER_AGENT)
|
|
#define ZEPHYR_USER_AGENT "Zephyr"
|
|
#endif
|
|
|
|
/* From RFC 6455 chapter 4.2.2 */
|
|
#define WS_MAGIC "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
|
|
|
|
/* Handle upgrade from HTTP/1.1 to Websocket, see RFC 6455
|
|
*/
|
|
int handle_http1_to_websocket_upgrade(struct http_client_ctx *client)
|
|
{
|
|
static const char switching_protocols[] =
|
|
"HTTP/1.1 101 Switching Protocols\r\n"
|
|
"Connection: Upgrade\r\n"
|
|
"Upgrade: websocket\r\n"
|
|
"Sec-WebSocket-Accept: ";
|
|
char key_accept[HTTP_SERVER_WS_MAX_SEC_KEY_LEN + sizeof(WS_MAGIC)];
|
|
char accept[20];
|
|
char tmp[64];
|
|
size_t key_len;
|
|
size_t olen;
|
|
int ret;
|
|
|
|
key_len = MIN(sizeof(key_accept) - 1, sizeof(client->ws_sec_key));
|
|
strncpy(key_accept, client->ws_sec_key, key_len);
|
|
key_len = strlen(key_accept);
|
|
|
|
olen = MIN(sizeof(key_accept) - 1 - key_len, sizeof(WS_MAGIC) - 1);
|
|
strncpy(key_accept + key_len, WS_MAGIC, olen);
|
|
|
|
mbedtls_sha1(key_accept, olen + key_len, accept);
|
|
|
|
ret = base64_encode(tmp, sizeof(tmp) - 1, &olen, accept, sizeof(accept));
|
|
if (ret) {
|
|
if (ret == -ENOMEM) {
|
|
NET_DBG("[%p] Too short buffer olen %zd", client, olen);
|
|
}
|
|
|
|
goto error;
|
|
}
|
|
|
|
ret = http_server_sendall(client, switching_protocols,
|
|
sizeof(switching_protocols) - 1);
|
|
if (ret < 0) {
|
|
NET_DBG("Cannot write to socket (%d)", ret);
|
|
goto error;
|
|
}
|
|
|
|
ret = http_server_sendall(client, tmp, strlen(tmp));
|
|
if (ret < 0) {
|
|
NET_DBG("Cannot write to socket (%d)", ret);
|
|
goto error;
|
|
}
|
|
|
|
ret = snprintk(tmp, sizeof(tmp), "\r\nUser-Agent: %s\r\n\r\n",
|
|
ZEPHYR_USER_AGENT);
|
|
if (ret < 0 || ret >= sizeof(tmp)) {
|
|
goto error;
|
|
}
|
|
|
|
ret = http_server_sendall(client, tmp, strlen(tmp));
|
|
if (ret < 0) {
|
|
NET_DBG("Cannot write to socket (%d)", ret);
|
|
goto error;
|
|
}
|
|
|
|
/* Only after the complete HTTP1 payload has been processed, switch
|
|
* to Websocket.
|
|
*/
|
|
if (client->parser_state == HTTP1_MESSAGE_COMPLETE_STATE) {
|
|
struct http_resource_detail_websocket *ws_detail;
|
|
int ws_sock;
|
|
|
|
ws_detail = (struct http_resource_detail_websocket *)client->current_detail;
|
|
|
|
ret = ws_sock = websocket_register(client->fd,
|
|
ws_detail->data_buffer,
|
|
ws_detail->data_buffer_len);
|
|
if (ret < 0) {
|
|
NET_DBG("Cannot register websocket (%d)", ret);
|
|
goto error;
|
|
}
|
|
|
|
http_server_release_client(client);
|
|
|
|
ret = ws_detail->cb(ws_sock, ws_detail->user_data);
|
|
if (ret < 0) {
|
|
NET_DBG("WS connection failed (%d)", ret);
|
|
websocket_unregister(ws_sock);
|
|
goto error;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
|
|
error:
|
|
return ret;
|
|
}
|