1 /*
2 * Copyright (c) 2024 Nordic Semiconductor ASA
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <errno.h>
8 #include <stdbool.h>
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <strings.h>
13
14 #include <zephyr/kernel.h>
15 #include <zephyr/logging/log.h>
16 #include <zephyr/net/http/service.h>
17 #include <zephyr/sys/base64.h>
18 #include <mbedtls/sha1.h>
19 #include <zephyr/net/websocket.h>
20
21 LOG_MODULE_DECLARE(net_http_server, CONFIG_NET_HTTP_SERVER_LOG_LEVEL);
22
23 #include "headers/server_internal.h"
24
25 #if !defined(ZEPHYR_USER_AGENT)
26 #define ZEPHYR_USER_AGENT "Zephyr"
27 #endif
28
29 /* From RFC 6455 chapter 4.2.2 */
30 #define WS_MAGIC "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
31
32 /* Handle upgrade from HTTP/1.1 to Websocket, see RFC 6455
33 */
handle_http1_to_websocket_upgrade(struct http_client_ctx * client)34 int handle_http1_to_websocket_upgrade(struct http_client_ctx *client)
35 {
36 static const char switching_protocols[] =
37 "HTTP/1.1 101 Switching Protocols\r\n"
38 "Connection: Upgrade\r\n"
39 "Upgrade: websocket\r\n"
40 "Sec-WebSocket-Accept: ";
41 char key_accept[HTTP_SERVER_WS_MAX_SEC_KEY_LEN + sizeof(WS_MAGIC)];
42 char accept[20];
43 char tmp[64];
44 size_t key_len;
45 size_t olen;
46 int ret;
47
48 key_len = MIN(sizeof(key_accept) - 1, sizeof(client->ws_sec_key));
49 strncpy(key_accept, client->ws_sec_key, key_len);
50 key_len = strlen(key_accept);
51
52 olen = MIN(sizeof(key_accept) - 1 - key_len, sizeof(WS_MAGIC) - 1);
53 strncpy(key_accept + key_len, WS_MAGIC, olen);
54
55 mbedtls_sha1(key_accept, olen + key_len, accept);
56
57 ret = base64_encode(tmp, sizeof(tmp) - 1, &olen, accept, sizeof(accept));
58 if (ret) {
59 if (ret == -ENOMEM) {
60 NET_DBG("[%p] Too short buffer olen %zd", client, olen);
61 }
62
63 goto error;
64 }
65
66 ret = http_server_sendall(client, switching_protocols,
67 sizeof(switching_protocols) - 1);
68 if (ret < 0) {
69 NET_DBG("Cannot write to socket (%d)", ret);
70 goto error;
71 }
72
73 ret = http_server_sendall(client, tmp, strlen(tmp));
74 if (ret < 0) {
75 NET_DBG("Cannot write to socket (%d)", ret);
76 goto error;
77 }
78
79 ret = snprintk(tmp, sizeof(tmp), "\r\nUser-Agent: %s\r\n\r\n",
80 ZEPHYR_USER_AGENT);
81 if (ret < 0 || ret >= sizeof(tmp)) {
82 goto error;
83 }
84
85 ret = http_server_sendall(client, tmp, strlen(tmp));
86 if (ret < 0) {
87 NET_DBG("Cannot write to socket (%d)", ret);
88 goto error;
89 }
90
91 /* Only after the complete HTTP1 payload has been processed, switch
92 * to Websocket.
93 */
94 if (client->parser_state == HTTP1_MESSAGE_COMPLETE_STATE) {
95 struct http_resource_detail_websocket *ws_detail;
96 int ws_sock;
97
98 ws_detail = (struct http_resource_detail_websocket *)client->current_detail;
99
100 ret = ws_sock = websocket_register(client->fd,
101 ws_detail->data_buffer,
102 ws_detail->data_buffer_len);
103 if (ret < 0) {
104 NET_DBG("Cannot register websocket (%d)", ret);
105 goto error;
106 }
107
108 http_server_release_client(client);
109
110 ret = ws_detail->cb(ws_sock, ws_detail->user_data);
111 if (ret < 0) {
112 NET_DBG("WS connection failed (%d)", ret);
113 websocket_unregister(ws_sock);
114 goto error;
115 }
116 }
117
118 return 0;
119
120 error:
121 return ret;
122 }
123