Skip to content

Commit 4178ede

Browse files
rluboskartben
authored andcommitted
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 <[email protected]>
1 parent 11eb043 commit 4178ede

File tree

4 files changed

+103
-12
lines changed

4 files changed

+103
-12
lines changed

subsys/net/lib/http/Kconfig

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,13 @@ config HTTP_SERVER_TLS_USE_ALPN
188188
protocol for TLS connections. Web browsers use this mechanism to determine
189189
whether HTTP2 is supported.
190190

191+
config HTTP_SERVER_REPORT_FAILURE_REASON
192+
bool "Report failure reason in HTTP 500 Internal Server Error reply"
193+
help
194+
If enabled, the server will include the failure reason within
195+
HTTP 500 Internal Server Error response. Otherwise, no information
196+
is provided within the message.
197+
191198
config WEBSOCKET_CONSOLE
192199
bool
193200
default y if HTTP_SERVER_WEBSOCKET && SHELL_BACKEND_WEBSOCKET

subsys/net/lib/http/http_server_http1.c

Lines changed: 92 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,51 @@ static int send_http1_409(struct http_client_ctx *client)
7171
sizeof(conflict_response) - 1);
7272
}
7373

74+
static void send_http1_500(struct http_client_ctx *client, int error_code)
75+
{
76+
#define HTTP_500_RESPONSE_TEMPLATE \
77+
"HTTP/1.1 500 Internal Server Error\r\n" \
78+
"Content-Type: text/plain\r\n" \
79+
"Content-Length: %d\r\n\r\n" \
80+
"Internal Server Error%s%s\r\n"
81+
#define MAX_ERROR_DESC_LEN 32
82+
83+
/* Placeholder for the error description. */
84+
char error_str[] = "xxx";
85+
char http_response[sizeof(HTTP_500_RESPONSE_TEMPLATE) +
86+
sizeof("xx") + /* Content-Length */
87+
MAX_ERROR_DESC_LEN + 1]; /* For the error description */
88+
const char *error_desc;
89+
const char *desc_separator;
90+
int desc_len;
91+
92+
if (IS_ENABLED(CONFIG_HTTP_SERVER_REPORT_FAILURE_REASON)) {
93+
/* Try to fetch error description, fallback to error number if
94+
* not available
95+
*/
96+
error_desc = strerror(error_code);
97+
if (strlen(error_desc) == 0) {
98+
/* Cast error value to uint8_t to avoid truncation warnings. */
99+
(void)snprintk(error_str, sizeof(error_str), "%u",
100+
(uint8_t)error_code);
101+
error_desc = error_str;
102+
}
103+
desc_separator = ": ";
104+
desc_len = MIN(MAX_ERROR_DESC_LEN, strlen(error_desc));
105+
desc_len += 2; /* For ": " */
106+
} else {
107+
error_desc = "";
108+
desc_separator = "";
109+
desc_len = 0;
110+
}
111+
112+
(void)snprintk(http_response, sizeof(http_response),
113+
HTTP_500_RESPONSE_TEMPLATE,
114+
sizeof("Internal Server Error\r\n") - 1 + desc_len,
115+
desc_separator, error_desc);
116+
(void)http_server_sendall(client, http_response, strlen(http_response));
117+
}
118+
74119
static int handle_http1_static_resource(
75120
struct http_resource_detail_static *static_detail,
76121
struct http_client_ctx *client)
@@ -119,6 +164,8 @@ static int handle_http1_static_resource(
119164
return ret;
120165
}
121166

167+
client->http1_headers_sent = true;
168+
122169
ret = http_server_sendall(client, data, len);
123170
if (ret < 0) {
124171
return ret;
@@ -483,6 +530,8 @@ int handle_http1_static_fs_resource(struct http_resource_detail_static_fs *stati
483530
goto close;
484531
}
485532

533+
client->http1_headers_sent = true;
534+
486535
/* read and send file */
487536
remaining = file_size;
488537
while (remaining > 0) {
@@ -545,6 +594,7 @@ static int handle_http1_dynamic_resource(
545594
return ret;
546595
}
547596

597+
client->http1_headers_sent = true;
548598
dynamic_detail->holder = NULL;
549599

550600
return 0;
@@ -839,12 +889,14 @@ int handle_http1_request(struct http_client_ctx *client)
839889

840890
if (parsed > client->data_len) {
841891
LOG_ERR("HTTP/1 parser error, too much data consumed");
842-
return -EBADMSG;
892+
ret = -EBADMSG;
893+
goto error;
843894
}
844895

845896
if (client->parser.http_errno != HPE_OK) {
846897
LOG_ERR("HTTP/1 parsing error, %d", client->parser.http_errno);
847-
return -EBADMSG;
898+
ret = -EBADMSG;
899+
goto error;
848900
}
849901

850902
if (client->parser_state < HTTP1_RECEIVED_HEADER_STATE) {
@@ -863,7 +915,8 @@ int handle_http1_request(struct http_client_ctx *client)
863915
size_t frag_headers_len;
864916

865917
if (parsed < client->http1_frag_data_len) {
866-
return -EBADMSG;
918+
ret = -EBADMSG;
919+
goto error;
867920
}
868921

869922
frag_headers_len = parsed - client->http1_frag_data_len;
@@ -892,36 +945,49 @@ int handle_http1_request(struct http_client_ctx *client)
892945

893946
detail->path_len = path_len;
894947
client->current_detail = detail;
895-
return handle_http1_to_websocket_upgrade(client);
948+
949+
ret = handle_http1_to_websocket_upgrade(client);
950+
if (ret < 0) {
951+
goto error;
952+
}
953+
954+
return 0;
896955
}
897956

898957
goto upgrade_not_found;
899958
}
900959

901960
if (client->http2_upgrade) {
902-
return handle_http1_to_http2_upgrade(client);
961+
ret = handle_http1_to_http2_upgrade(client);
962+
if (ret < 0) {
963+
goto error;
964+
}
965+
966+
return 0;
903967
}
904968

905969
upgrade_not_found:
906970
ret = http_server_sendall(client, upgrade_required,
907971
sizeof(upgrade_required) - 1);
908972
if (ret < 0) {
909973
LOG_DBG("Cannot write to socket (%d)", ret);
910-
return ret;
974+
goto error;
911975
}
912976

977+
client->http1_headers_sent = true;
978+
913979
ret = http_server_sendall(client, needed_upgrade,
914980
strlen(needed_upgrade));
915981
if (ret < 0) {
916982
LOG_DBG("Cannot write to socket (%d)", ret);
917-
return ret;
983+
goto error;
918984
}
919985

920986
ret = http_server_sendall(client, upgrade_msg,
921987
sizeof(upgrade_msg) - 1);
922988
if (ret < 0) {
923989
LOG_DBG("Cannot write to socket (%d)", ret);
924-
return ret;
990+
goto error;
925991
}
926992
}
927993

@@ -934,30 +1000,32 @@ int handle_http1_request(struct http_client_ctx *client)
9341000
(struct http_resource_detail_static *)detail,
9351001
client);
9361002
if (ret < 0) {
937-
return ret;
1003+
goto error;
9381004
}
9391005
#if defined(CONFIG_FILE_SYSTEM)
9401006
} else if (detail->type == HTTP_RESOURCE_TYPE_STATIC_FS) {
9411007
ret = handle_http1_static_fs_resource(
9421008
(struct http_resource_detail_static_fs *)detail, client);
9431009
if (ret < 0) {
944-
return ret;
1010+
goto error;
9451011
}
9461012
#endif
9471013
} else if (detail->type == HTTP_RESOURCE_TYPE_DYNAMIC) {
9481014
ret = handle_http1_dynamic_resource(
9491015
(struct http_resource_detail_dynamic *)detail,
9501016
client);
9511017
if (ret < 0) {
952-
return ret;
1018+
goto error;
9531019
}
9541020
}
9551021
} else {
9561022
not_found: ; /* Add extra semicolon to make clang to compile when using label */
9571023
ret = send_http1_404(client);
9581024
if (ret < 0) {
959-
return ret;
1025+
goto error;
9601026
}
1027+
1028+
client->http1_headers_sent = true;
9611029
}
9621030

9631031
client->cursor += parsed;
@@ -974,4 +1042,16 @@ not_found: ; /* Add extra semicolon to make clang to compile when using label */
9741042
}
9751043

9761044
return 0;
1045+
1046+
error:
1047+
/* Best effort try to send HTTP 500 Internal Server Error response in
1048+
* case of errors. This can only be done however if we haven't sent
1049+
* response header elsewhere (i. e. can't send another reply if the
1050+
* error ocurred amid resource processing).
1051+
*/
1052+
if (ret != -EAGAIN && !client->http1_headers_sent) {
1053+
send_http1_500(client, -ret);
1054+
}
1055+
1056+
return ret;
9771057
}

subsys/net/lib/http/http_server_http2.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1018,6 +1018,8 @@ int handle_http1_to_http2_upgrade(struct http_client_ctx *client)
10181018
goto error;
10191019
}
10201020

1021+
client->http1_headers_sent = true;
1022+
10211023
/* The first HTTP/2 frame sent by the server MUST be a server connection
10221024
* preface.
10231025
*/

subsys/net/lib/http/http_server_ws.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,8 @@ int handle_http1_to_websocket_upgrade(struct http_client_ctx *client)
7070
goto error;
7171
}
7272

73+
client->http1_headers_sent = true;
74+
7375
ret = http_server_sendall(client, tmp, strlen(tmp));
7476
if (ret < 0) {
7577
NET_DBG("Cannot write to socket (%d)", ret);

0 commit comments

Comments
 (0)