zephyr/subsys/net/lib/http/http_server_ws.c
Robert Lubos 4178ede259 net: http_server: Add support for generic HTTP1 500 response
In case of internal server errors during HTTP1 request processing,
send 500 Internal Server Error response before shutting down the
connection.

Make sure http1_headers_sent is set whenever sever starts replying, to
avoid duplicate response in case of errors, as that would be protocol
violation.

Signed-off-by: Robert Lubos <robert.lubos@nordicsemi.no>
2025-01-16 14:52:10 +01:00

133 lines
3.3 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;
}
client->http1_headers_sent = true;
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;
struct http_request_ctx request_ctx;
int ws_sock;
char *params;
size_t params_len;
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;
}
memset(&request_ctx, 0, sizeof(request_ctx));
params = &client->url_buffer[client->current_detail->path_len];
params_len = strlen(params);
populate_request_ctx(&request_ctx, params, params_len, &client->header_capture_ctx);
ret = ws_detail->cb(ws_sock, &request_ctx, ws_detail->user_data);
http_server_release_client(client);
if (ret < 0) {
NET_DBG("WS connection failed (%d)", ret);
websocket_unregister(ws_sock);
goto error;
}
}
return 0;
error:
return ret;
}