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