1 /*
2  * Copyright (c) 2023 Meta
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <string.h>
8 
9 #include <zephyr/ztest.h>
10 #include <zephyr/net/http/service.h>
11 
12 #define DETAIL(n) (void *)((0xde7a11 * 10) + (n))
13 #define RES(n)	  (void *)((0x2e5 * 10) + (n))
14 
15 /*
16  * Two separate HTTP server instances (A and B), each with different static
17  * resources, listening on different ports. Static resources could be, for
18  * example, gzip compressed html, javascript, css, or image files which
19  * have fixed paths known at build time.
20  *
21  * REST endpoints could be considered as static resources, as long as
22  * the paths (and implementation-specific details) are known at compile time.
23  */
24 static const uint16_t service_A_port = 4242;
25 HTTP_SERVICE_DEFINE(service_A, "a.service.com", &service_A_port, 4, 2, DETAIL(0));
26 HTTP_RESOURCE_DEFINE(resource_0, service_A, "/", RES(0));
27 HTTP_RESOURCE_DEFINE(resource_1, service_A, "/index.html", RES(1));
28 
29 /* ephemeral port of 0 */
30 static uint16_t service_B_port;
31 HTTP_SERVICE_DEFINE(service_B, "b.service.com", &service_B_port, 7, 3, DETAIL(1));
32 HTTP_RESOURCE_DEFINE(resource_2, service_B, "/foo.htm", RES(2));
33 HTTP_RESOURCE_DEFINE(resource_3, service_B, "/bar/baz.php", RES(3));
34 
35 /*
36  * An "empty" HTTP service is one without static resources. For example, a
37  * service which loads resources from a filesystem that are determined at
38  * runtime.
39  */
40 static const uint16_t service_C_port = 5959;
41 HTTP_SERVICE_DEFINE_EMPTY(service_C, "192.168.1.1", &service_C_port, 5, 9, DETAIL(2));
42 
ZTEST(http_service,test_HTTP_SERVICE_DEFINE)43 ZTEST(http_service, test_HTTP_SERVICE_DEFINE)
44 {
45 	zassert_ok(strcmp(service_A.host, "a.service.com"));
46 	zassert_equal(service_A.port, &service_A_port);
47 	zassert_equal(*service_A.port, 4242);
48 	zassert_equal(service_A.detail, DETAIL(0));
49 	zassert_equal(service_A.concurrent, 4);
50 	zassert_equal(service_A.backlog, 2);
51 
52 	zassert_ok(strcmp(service_B.host, "b.service.com"));
53 	zassert_equal(service_B.port, &service_B_port);
54 	zassert_equal(*service_B.port, 0);
55 	zassert_equal(service_B.detail, DETAIL(1));
56 	zassert_equal(service_B.concurrent, 7);
57 	zassert_equal(service_B.backlog, 3);
58 
59 	zassert_ok(strcmp(service_C.host, "192.168.1.1"));
60 	zassert_equal(service_C.port, &service_C_port);
61 	zassert_equal(*service_C.port, 5959);
62 	zassert_equal(service_C.detail, DETAIL(2));
63 	zassert_equal(service_C.concurrent, 5);
64 	zassert_equal(service_C.backlog, 9);
65 	zassert_equal(service_C.res_begin, NULL);
66 	zassert_equal(service_C.res_end, NULL);
67 }
68 
ZTEST(http_service,test_HTTP_SERVICE_COUNT)69 ZTEST(http_service, test_HTTP_SERVICE_COUNT)
70 {
71 	size_t n_svc;
72 
73 	n_svc = 4273;
74 	HTTP_SERVICE_COUNT(&n_svc);
75 	zassert_equal(n_svc, 3);
76 }
77 
ZTEST(http_service,test_HTTP_SERVICE_RESOURCE_COUNT)78 ZTEST(http_service, test_HTTP_SERVICE_RESOURCE_COUNT)
79 {
80 	zassert_equal(HTTP_SERVICE_RESOURCE_COUNT(&service_A), 2);
81 	zassert_equal(HTTP_SERVICE_RESOURCE_COUNT(&service_B), 2);
82 	zassert_equal(HTTP_SERVICE_RESOURCE_COUNT(&service_C), 0);
83 }
84 
ZTEST(http_service,test_HTTP_SERVICE_FOREACH)85 ZTEST(http_service, test_HTTP_SERVICE_FOREACH)
86 {
87 	size_t n_svc = 0;
88 	size_t have_service_A = 0;
89 	size_t have_service_B = 0;
90 	size_t have_service_C = 0;
91 
92 	HTTP_SERVICE_FOREACH(svc) {
93 		if (svc == &service_A) {
94 			have_service_A = 1;
95 		} else if (svc == &service_B) {
96 			have_service_B = 1;
97 		} else if (svc == &service_C) {
98 			have_service_C = 1;
99 		} else {
100 			zassert_unreachable("svc (%p) not equal to &service_A (%p), &service_B "
101 					    "(%p), or &service_C (%p)",
102 					    svc, &service_A, &service_B, &service_C);
103 		}
104 
105 		n_svc++;
106 	}
107 
108 	zassert_equal(n_svc, 3);
109 	zassert_equal(have_service_A + have_service_B + have_service_C, n_svc);
110 }
111 
ZTEST(http_service,test_HTTP_RESOURCE_FOREACH)112 ZTEST(http_service, test_HTTP_RESOURCE_FOREACH)
113 {
114 	size_t first_res, second_res, n_res;
115 
116 	n_res = 0;
117 	first_res = 0;
118 	second_res = 0;
119 	HTTP_RESOURCE_FOREACH(service_A, res) {
120 		if (res == &resource_0) {
121 			first_res = 1;
122 		} else if (res == &resource_1) {
123 			second_res = 1;
124 		} else {
125 			zassert_unreachable(
126 				"res (%p) not equal to &resource_0 (%p) or &resource_1 (%p)", res,
127 				&resource_0, &resource_1);
128 		}
129 
130 		n_res++;
131 	}
132 
133 	zassert_equal(n_res, 2);
134 	zassert_equal(first_res + second_res, n_res);
135 
136 	n_res = 0;
137 	first_res = 0;
138 	second_res = 0;
139 	HTTP_RESOURCE_FOREACH(service_B, res) {
140 		if (res == &resource_2) {
141 			first_res = 1;
142 		} else if (res == &resource_3) {
143 			second_res = 1;
144 		} else {
145 			zassert_unreachable(
146 				"res (%p) not equal to &resource_2 (%p) or &resource_3 (%p)", res,
147 				&resource_2, &resource_3);
148 		}
149 
150 		n_res++;
151 	}
152 
153 	zassert_equal(n_res, 2);
154 	zassert_equal(first_res + second_res, n_res);
155 
156 	n_res = 0;
157 	HTTP_SERVICE_FOREACH_RESOURCE(&service_C, res) {
158 		zassert_unreachable("service_C does not have any resources");
159 		n_res++;
160 	}
161 
162 	zassert_equal(n_res, 0);
163 }
164 
ZTEST(http_service,test_HTTP_SERVICE_FOREACH_RESOURCE)165 ZTEST(http_service, test_HTTP_SERVICE_FOREACH_RESOURCE)
166 {
167 	size_t first_res, second_res, n_res;
168 
169 	n_res = 0;
170 	first_res = 0;
171 	second_res = 0;
172 	HTTP_SERVICE_FOREACH_RESOURCE(&service_A, res) {
173 		if (res == &resource_0) {
174 			first_res = 1;
175 		} else if (res == &resource_1) {
176 			second_res = 1;
177 		} else {
178 			zassert_unreachable(
179 				"res (%p) not equal to &resource_0 (%p) or &resource_1 (%p)", res,
180 				&resource_0, &resource_1);
181 		}
182 
183 		n_res++;
184 	}
185 
186 	zassert_equal(n_res, 2);
187 	zassert_equal(first_res + second_res, n_res);
188 
189 	n_res = 0;
190 	first_res = 0;
191 	second_res = 0;
192 	HTTP_SERVICE_FOREACH_RESOURCE(&service_B, res) {
193 		if (res == &resource_2) {
194 			first_res = 1;
195 		} else if (res == &resource_3) {
196 			second_res = 1;
197 		} else {
198 			zassert_unreachable(
199 				"res (%p) not equal to &resource_2 (%p) or &resource_3 (%p)", res,
200 				&resource_2, &resource_3);
201 		}
202 
203 		n_res++;
204 	}
205 
206 	zassert_equal(n_res, 2);
207 	zassert_equal(first_res + second_res, n_res);
208 
209 	n_res = 0;
210 	HTTP_SERVICE_FOREACH_RESOURCE(&service_C, res) {
211 		zassert_unreachable("service_C does not have any resources");
212 		n_res++;
213 	}
214 
215 	zassert_equal(n_res, 0);
216 }
217 
ZTEST(http_service,test_HTTP_RESOURCE_DEFINE)218 ZTEST(http_service, test_HTTP_RESOURCE_DEFINE)
219 {
220 	HTTP_SERVICE_FOREACH_RESOURCE(&service_A, res) {
221 		if (res == &resource_0) {
222 			zassert_ok(strcmp(res->resource, "/"));
223 			zassert_equal(res->detail, RES(0));
224 		} else if (res == &resource_1) {
225 			zassert_ok(strcmp(res->resource, "/index.html"));
226 			zassert_equal(res->detail, RES(1));
227 		} else {
228 			zassert_unreachable(
229 				"res (%p) not equal to &resource_0 (%p) or &resource_1 (%p)", res,
230 				&resource_0, &resource_1);
231 		}
232 	}
233 
234 	HTTP_SERVICE_FOREACH_RESOURCE(&service_B, res) {
235 		if (res == &resource_2) {
236 			zassert_ok(strcmp(res->resource, "/foo.htm"));
237 			zassert_equal(res->detail, RES(2));
238 		} else if (res == &resource_3) {
239 			zassert_ok(strcmp(res->resource, "/bar/baz.php"));
240 			zassert_equal(res->detail, RES(3));
241 		} else {
242 			zassert_unreachable(
243 				"res (%p) not equal to &resource_2 (%p) or &resource_3 (%p)", res,
244 				&resource_2, &resource_3);
245 		}
246 	}
247 }
248 
249 ZTEST_SUITE(http_service, NULL, NULL, NULL, NULL, NULL);
250