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