1 /*
2 * Copyright (c) 2023, Emna Rekik
3 * Copyright (c) 2024, Nordic Semiconductor
4 *
5 * SPDX-License-Identifier: Apache-2.0
6 */
7
8 #include <stdio.h>
9 #include <inttypes.h>
10
11 #include <zephyr/kernel.h>
12 #include <zephyr/net/tls_credentials.h>
13 #include <zephyr/net/http/server.h>
14 #include <zephyr/net/http/service.h>
15 #include <zephyr/net/net_ip.h>
16 #include <zephyr/net/socket.h>
17 #include "zephyr/device.h"
18 #include "zephyr/sys/util.h"
19 #include <zephyr/drivers/led.h>
20 #include <zephyr/data/json.h>
21 #include <zephyr/sys/util_macro.h>
22
23 #include "ws.h"
24
25 #include <zephyr/logging/log.h>
26 LOG_MODULE_REGISTER(net_http_server_sample, LOG_LEVEL_DBG);
27
28 struct led_command {
29 int led_num;
30 bool led_state;
31 };
32
33 static const struct json_obj_descr led_command_descr[] = {
34 JSON_OBJ_DESCR_PRIM(struct led_command, led_num, JSON_TOK_NUMBER),
35 JSON_OBJ_DESCR_PRIM(struct led_command, led_state, JSON_TOK_TRUE),
36 };
37
38 static const struct device *leds_dev = DEVICE_DT_GET_ANY(gpio_leds);
39
40 static uint8_t index_html_gz[] = {
41 #include "index.html.gz.inc"
42 };
43
44 static uint8_t main_js_gz[] = {
45 #include "main.js.gz.inc"
46 };
47
48 static struct http_resource_detail_static index_html_gz_resource_detail = {
49 .common = {
50 .type = HTTP_RESOURCE_TYPE_STATIC,
51 .bitmask_of_supported_http_methods = BIT(HTTP_GET),
52 .content_encoding = "gzip",
53 .content_type = "text/html",
54 },
55 .static_data = index_html_gz,
56 .static_data_len = sizeof(index_html_gz),
57 };
58
59 static struct http_resource_detail_static main_js_gz_resource_detail = {
60 .common = {
61 .type = HTTP_RESOURCE_TYPE_STATIC,
62 .bitmask_of_supported_http_methods = BIT(HTTP_GET),
63 .content_encoding = "gzip",
64 .content_type = "text/javascript",
65 },
66 .static_data = main_js_gz,
67 .static_data_len = sizeof(main_js_gz),
68 };
69
echo_handler(struct http_client_ctx * client,enum http_data_status status,const struct http_request_ctx * request_ctx,struct http_response_ctx * response_ctx,void * user_data)70 static int echo_handler(struct http_client_ctx *client, enum http_data_status status,
71 const struct http_request_ctx *request_ctx,
72 struct http_response_ctx *response_ctx, void *user_data)
73 {
74 #define MAX_TEMP_PRINT_LEN 32
75 static char print_str[MAX_TEMP_PRINT_LEN];
76 enum http_method method = client->method;
77 static size_t processed;
78
79 if (status == HTTP_SERVER_DATA_ABORTED) {
80 LOG_DBG("Transaction aborted after %zd bytes.", processed);
81 processed = 0;
82 return 0;
83 }
84
85 __ASSERT_NO_MSG(buffer != NULL);
86
87 processed += request_ctx->data_len;
88
89 snprintf(print_str, sizeof(print_str), "%s received (%zd bytes)", http_method_str(method),
90 request_ctx->data_len);
91 LOG_HEXDUMP_DBG(request_ctx->data, request_ctx->data_len, print_str);
92
93 if (status == HTTP_SERVER_DATA_FINAL) {
94 LOG_DBG("All data received (%zd bytes).", processed);
95 processed = 0;
96 }
97
98 /* Echo data back to client */
99 response_ctx->body = request_ctx->data;
100 response_ctx->body_len = request_ctx->data_len;
101 response_ctx->final_chunk = (status == HTTP_SERVER_DATA_FINAL);
102
103 return 0;
104 }
105
106 static struct http_resource_detail_dynamic echo_resource_detail = {
107 .common = {
108 .type = HTTP_RESOURCE_TYPE_DYNAMIC,
109 .bitmask_of_supported_http_methods = BIT(HTTP_GET) | BIT(HTTP_POST),
110 },
111 .cb = echo_handler,
112 .user_data = NULL,
113 };
114
uptime_handler(struct http_client_ctx * client,enum http_data_status status,const struct http_request_ctx * request_ctx,struct http_response_ctx * response_ctx,void * user_data)115 static int uptime_handler(struct http_client_ctx *client, enum http_data_status status,
116 const struct http_request_ctx *request_ctx,
117 struct http_response_ctx *response_ctx, void *user_data)
118 {
119 int ret;
120 static uint8_t uptime_buf[sizeof(STRINGIFY(INT64_MAX))];
121
122 LOG_DBG("Uptime handler status %d", status);
123
124 /* A payload is not expected with the GET request. Ignore any data and wait until
125 * final callback before sending response
126 */
127 if (status == HTTP_SERVER_DATA_FINAL) {
128 ret = snprintf(uptime_buf, sizeof(uptime_buf), "%" PRId64, k_uptime_get());
129 if (ret < 0) {
130 LOG_ERR("Failed to snprintf uptime, err %d", ret);
131 return ret;
132 }
133
134 response_ctx->body = uptime_buf;
135 response_ctx->body_len = ret;
136 response_ctx->final_chunk = true;
137 }
138
139 return 0;
140 }
141
142 static struct http_resource_detail_dynamic uptime_resource_detail = {
143 .common = {
144 .type = HTTP_RESOURCE_TYPE_DYNAMIC,
145 .bitmask_of_supported_http_methods = BIT(HTTP_GET),
146 },
147 .cb = uptime_handler,
148 .user_data = NULL,
149 };
150
parse_led_post(uint8_t * buf,size_t len)151 static void parse_led_post(uint8_t *buf, size_t len)
152 {
153 int ret;
154 struct led_command cmd;
155 const int expected_return_code = BIT_MASK(ARRAY_SIZE(led_command_descr));
156
157 ret = json_obj_parse(buf, len, led_command_descr, ARRAY_SIZE(led_command_descr), &cmd);
158 if (ret != expected_return_code) {
159 LOG_WRN("Failed to fully parse JSON payload, ret=%d", ret);
160 return;
161 }
162
163 LOG_INF("POST request setting LED %d to state %d", cmd.led_num, cmd.led_state);
164
165 if (leds_dev != NULL) {
166 if (cmd.led_state) {
167 led_on(leds_dev, cmd.led_num);
168 } else {
169 led_off(leds_dev, cmd.led_num);
170 }
171 }
172 }
173
led_handler(struct http_client_ctx * client,enum http_data_status status,const struct http_request_ctx * request_ctx,struct http_response_ctx * response_ctx,void * user_data)174 static int led_handler(struct http_client_ctx *client, enum http_data_status status,
175 const struct http_request_ctx *request_ctx,
176 struct http_response_ctx *response_ctx, void *user_data)
177 {
178 static uint8_t post_payload_buf[32];
179 static size_t cursor;
180
181 LOG_DBG("LED handler status %d, size %zu", status, request_ctx->data_len);
182
183 if (status == HTTP_SERVER_DATA_ABORTED) {
184 cursor = 0;
185 return 0;
186 }
187
188 if (request_ctx->data_len + cursor > sizeof(post_payload_buf)) {
189 cursor = 0;
190 return -ENOMEM;
191 }
192
193 /* Copy payload to our buffer. Note that even for a small payload, it may arrive split into
194 * chunks (e.g. if the header size was such that the whole HTTP request exceeds the size of
195 * the client buffer).
196 */
197 memcpy(post_payload_buf + cursor, request_ctx->data, request_ctx->data_len);
198 cursor += request_ctx->data_len;
199
200 if (status == HTTP_SERVER_DATA_FINAL) {
201 parse_led_post(post_payload_buf, cursor);
202 cursor = 0;
203 }
204
205 return 0;
206 }
207
208 static struct http_resource_detail_dynamic led_resource_detail = {
209 .common = {
210 .type = HTTP_RESOURCE_TYPE_DYNAMIC,
211 .bitmask_of_supported_http_methods = BIT(HTTP_POST),
212 },
213 .cb = led_handler,
214 .user_data = NULL,
215 };
216
217 #if defined(CONFIG_NET_SAMPLE_WEBSOCKET_SERVICE)
218 static uint8_t ws_echo_buffer[1024];
219
220 struct http_resource_detail_websocket ws_echo_resource_detail = {
221 .common = {
222 .type = HTTP_RESOURCE_TYPE_WEBSOCKET,
223
224 /* We need HTTP/1.1 Get method for upgrading */
225 .bitmask_of_supported_http_methods = BIT(HTTP_GET),
226 },
227 .cb = ws_echo_setup,
228 .data_buffer = ws_echo_buffer,
229 .data_buffer_len = sizeof(ws_echo_buffer),
230 .user_data = NULL, /* Fill this for any user specific data */
231 };
232
233 static uint8_t ws_netstats_buffer[128];
234
235 struct http_resource_detail_websocket ws_netstats_resource_detail = {
236 .common = {
237 .type = HTTP_RESOURCE_TYPE_WEBSOCKET,
238 .bitmask_of_supported_http_methods = BIT(HTTP_GET),
239 },
240 .cb = ws_netstats_setup,
241 .data_buffer = ws_netstats_buffer,
242 .data_buffer_len = sizeof(ws_netstats_buffer),
243 .user_data = NULL,
244 };
245
246 #endif /* CONFIG_NET_SAMPLE_WEBSOCKET_SERVICE */
247
248 #if defined(CONFIG_NET_SAMPLE_HTTP_SERVICE)
249 static uint16_t test_http_service_port = CONFIG_NET_SAMPLE_HTTP_SERVER_SERVICE_PORT;
250 HTTP_SERVICE_DEFINE(test_http_service, NULL, &test_http_service_port, 1,
251 10, NULL);
252
253 HTTP_RESOURCE_DEFINE(index_html_gz_resource, test_http_service, "/",
254 &index_html_gz_resource_detail);
255
256 HTTP_RESOURCE_DEFINE(main_js_gz_resource, test_http_service, "/main.js",
257 &main_js_gz_resource_detail);
258
259 HTTP_RESOURCE_DEFINE(echo_resource, test_http_service, "/dynamic", &echo_resource_detail);
260
261 HTTP_RESOURCE_DEFINE(uptime_resource, test_http_service, "/uptime", &uptime_resource_detail);
262
263 HTTP_RESOURCE_DEFINE(led_resource, test_http_service, "/led", &led_resource_detail);
264
265 #if defined(CONFIG_NET_SAMPLE_WEBSOCKET_SERVICE)
266 HTTP_RESOURCE_DEFINE(ws_echo_resource, test_http_service, "/ws_echo", &ws_echo_resource_detail);
267
268 HTTP_RESOURCE_DEFINE(ws_netstats_resource, test_http_service, "/", &ws_netstats_resource_detail);
269 #endif /* CONFIG_NET_SAMPLE_WEBSOCKET_SERVICE */
270 #endif /* CONFIG_NET_SAMPLE_HTTP_SERVICE */
271
272 #if defined(CONFIG_NET_SAMPLE_HTTPS_SERVICE)
273 #include "certificate.h"
274
275 static const sec_tag_t sec_tag_list_verify_none[] = {
276 HTTP_SERVER_CERTIFICATE_TAG,
277 #if defined(CONFIG_MBEDTLS_KEY_EXCHANGE_PSK_ENABLED)
278 PSK_TAG,
279 #endif
280 };
281
282 static uint16_t test_https_service_port = CONFIG_NET_SAMPLE_HTTPS_SERVER_SERVICE_PORT;
283 HTTPS_SERVICE_DEFINE(test_https_service, NULL,
284 &test_https_service_port, 1, 10, NULL,
285 sec_tag_list_verify_none, sizeof(sec_tag_list_verify_none));
286
287 HTTP_RESOURCE_DEFINE(index_html_gz_resource_https, test_https_service, "/",
288 &index_html_gz_resource_detail);
289
290 HTTP_RESOURCE_DEFINE(main_js_gz_resource_https, test_https_service, "/main.js",
291 &main_js_gz_resource_detail);
292
293 HTTP_RESOURCE_DEFINE(echo_resource_https, test_https_service, "/dynamic", &echo_resource_detail);
294
295 HTTP_RESOURCE_DEFINE(uptime_resource_https, test_https_service, "/uptime", &uptime_resource_detail);
296
297 HTTP_RESOURCE_DEFINE(led_resource_https, test_https_service, "/led", &led_resource_detail);
298
299 #if defined(CONFIG_NET_SAMPLE_WEBSOCKET_SERVICE)
300 HTTP_RESOURCE_DEFINE(ws_echo_resource_https, test_https_service, "/ws_echo",
301 &ws_echo_resource_detail);
302
303 HTTP_RESOURCE_DEFINE(ws_netstats_resource_https, test_https_service, "/",
304 &ws_netstats_resource_detail);
305 #endif /* CONFIG_NET_SAMPLE_WEBSOCKET_SERVICE */
306 #endif /* CONFIG_NET_SAMPLE_HTTPS_SERVICE */
307
setup_tls(void)308 static void setup_tls(void)
309 {
310 #if defined(CONFIG_NET_SAMPLE_HTTPS_SERVICE)
311 #if defined(CONFIG_NET_SOCKETS_SOCKOPT_TLS)
312 int err;
313
314 err = tls_credential_add(HTTP_SERVER_CERTIFICATE_TAG,
315 TLS_CREDENTIAL_SERVER_CERTIFICATE,
316 server_certificate,
317 sizeof(server_certificate));
318 if (err < 0) {
319 LOG_ERR("Failed to register public certificate: %d", err);
320 }
321
322 err = tls_credential_add(HTTP_SERVER_CERTIFICATE_TAG,
323 TLS_CREDENTIAL_PRIVATE_KEY,
324 private_key, sizeof(private_key));
325 if (err < 0) {
326 LOG_ERR("Failed to register private key: %d", err);
327 }
328
329 #if defined(CONFIG_MBEDTLS_KEY_EXCHANGE_PSK_ENABLED)
330 err = tls_credential_add(PSK_TAG,
331 TLS_CREDENTIAL_PSK,
332 psk,
333 sizeof(psk));
334 if (err < 0) {
335 LOG_ERR("Failed to register PSK: %d", err);
336 }
337
338 err = tls_credential_add(PSK_TAG,
339 TLS_CREDENTIAL_PSK_ID,
340 psk_id,
341 sizeof(psk_id) - 1);
342 if (err < 0) {
343 LOG_ERR("Failed to register PSK ID: %d", err);
344 }
345 #endif /* defined(CONFIG_MBEDTLS_KEY_EXCHANGE_PSK_ENABLED) */
346 #endif /* defined(CONFIG_NET_SOCKETS_SOCKOPT_TLS) */
347 #endif /* defined(CONFIG_NET_SAMPLE_HTTPS_SERVICE) */
348 }
349
350 #if defined(CONFIG_USB_DEVICE_STACK)
351 int init_usb(void);
352 #else
init_usb(void)353 static inline int init_usb(void)
354 {
355 return 0;
356 }
357 #endif /* CONFIG_USB_DEVICE_STACK */
358
main(void)359 int main(void)
360 {
361 init_usb();
362
363 setup_tls();
364 http_server_start();
365 return 0;
366 }
367