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