/* * Copyright (c) 2024 Nordic Semiconductor ASA * * SPDX-License-Identifier: Apache-2.0 */ #include #include #include #include #include #include #include #include #include #include #include #include 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; }