xref: /aosp_15_r20/external/libwebsockets/minimal-examples/api-tests/api-test-lws_cache/main.c (revision 1c60b9aca93fdbc9b5f19b2d2194c91294b22281)
1 /*
2  * lws-api-test-lws_cache
3  *
4  * Written in 2010-2021 by Andy Green <[email protected]>
5  *
6  * This file is made available under the Creative Commons CC0 1.0
7  * Universal Public Domain Dedication.
8  */
9 
10 #include <libwebsockets.h>
11 
12 static struct lws_context *cx;
13 static int tests, fail;
14 
15 static int
test_just_l1(void)16 test_just_l1(void)
17 {
18 	struct lws_cache_creation_info ci;
19 	struct lws_cache_ttl_lru *l1;
20 	int ret = 1;
21 	size_t size;
22 	char *po;
23 
24 	lwsl_user("%s\n", __func__);
25 
26 	tests++;
27 
28 	/* just create a heap cache "L1" */
29 
30 	memset(&ci, 0, sizeof(ci));
31 	ci.cx = cx;
32 	ci.ops = &lws_cache_ops_heap;
33 	ci.name = "L1";
34 
35 	l1 = lws_cache_create(&ci);
36 	if (!l1)
37 		goto cdone;
38 
39 	/* add two items, a has 1s expiry and b has 2s */
40 
41 	if (lws_cache_write_through(l1, "a", (const uint8_t *)"is_a", 5,
42 				    lws_now_usecs() + LWS_US_PER_SEC, NULL))
43 		goto cdone;
44 
45 	if (lws_cache_write_through(l1, "b", (const uint8_t *)"is_b", 5,
46 				    lws_now_usecs() + LWS_US_PER_SEC * 2, NULL))
47 		goto cdone;
48 
49 	/* check they exist as intended */
50 
51 	if (lws_cache_item_get(l1, "a", (const void **)&po, &size) ||
52 	    size != 5 || strcmp(po, "is_a"))
53 		goto cdone;
54 
55 	if (lws_cache_item_get(l1, "b", (const void **)&po, &size) ||
56 	    size != 5 || strcmp(po, "is_b"))
57 		goto cdone;
58 
59 	/* wait for 1.2s to pass, working the event loop by hand */
60 
61 	lws_cancel_service(cx);
62 	if (lws_service(cx, 0) < 0)
63 		goto cdone;
64 #if defined(WIN32)
65 	Sleep(1200);
66 #else
67 	/* netbsd cares about < 1M */
68 	usleep(999999);
69 	usleep(200001);
70 #endif
71 	lws_cancel_service(cx);
72 	if (lws_service(cx, 0) < 0)
73 		goto cdone;
74 
75 	lws_cancel_service(cx);
76 	if (lws_service(cx, 0) < 0)
77 		goto cdone;
78 
79 	/* a only had 1s lifetime, he should be gone */
80 
81 	if (!lws_cache_item_get(l1, "a", (const void **)&po, &size)) {
82 		lwsl_err("%s: cache: a still exists after expiry\n", __func__);
83 		fail++;
84 		goto cdone;
85 	}
86 
87 	/* that's ok then */
88 
89 	ret = 0;
90 
91 cdone:
92 	lws_cache_destroy(&l1);
93 
94 	if (ret)
95 		lwsl_warn("%s: fail\n", __func__);
96 
97 	return ret;
98 }
99 
100 static int
test_just_l1_limits(void)101 test_just_l1_limits(void)
102 {
103 	struct lws_cache_creation_info ci;
104 	struct lws_cache_ttl_lru *l1;
105 	int ret = 1;
106 	size_t size;
107 	char *po;
108 
109 	lwsl_user("%s\n", __func__);
110 	tests++;
111 
112 	/* just create a heap cache "L1" */
113 
114 	memset(&ci, 0, sizeof(ci));
115 	ci.cx = cx;
116 	ci.ops = &lws_cache_ops_heap;
117 	ci.name = "L1_lim";
118 	ci.max_items = 1; /* ie, adding a second item destroys the first */
119 
120 	l1 = lws_cache_create(&ci);
121 	if (!l1)
122 		goto cdone;
123 
124 	/* add two items, a has 1s expiry and b has 2s */
125 
126 	if (lws_cache_write_through(l1, "a", (const uint8_t *)"is_a", 5,
127 				    lws_now_usecs() + LWS_US_PER_SEC, NULL))
128 		goto cdone;
129 
130 	if (lws_cache_write_through(l1, "b", (const uint8_t *)"is_b", 5,
131 				    lws_now_usecs() + LWS_US_PER_SEC * 2, NULL))
132 		goto cdone;
133 
134 	/* only b should exit, since we limit to cache to just one entry */
135 
136 	if (!lws_cache_item_get(l1, "a", (const void **)&po, &size))
137 		goto cdone;
138 
139 	if (lws_cache_item_get(l1, "b", (const void **)&po, &size) ||
140 	    size != 5 || strcmp(po, "is_b"))
141 		goto cdone;
142 
143 	/* that's ok then */
144 
145 	ret = 0;
146 
147 cdone:
148 	lws_cache_destroy(&l1);
149 
150 	if (ret)
151 		lwsl_warn("%s: fail\n", __func__);
152 
153 	return ret;
154 }
155 
156 #if defined(LWS_WITH_CACHE_NSCOOKIEJAR)
157 
158 static const char
159 	*cookie1 = "host.com\tFALSE\t/\tTRUE\t4000000000\tmycookie\tmycookievalue",
160 	*tag_cookie1 = "host.com|/|mycookie",
161 	*cookie2 = "host.com\tFALSE\t/xxx\tTRUE\t4000000000\tmycookie\tmyxxxcookievalue",
162 	*tag_cookie2 = "host.com|/xxx|mycookie",
163 	*cookie3 = "host.com\tFALSE\t/\tTRUE\t4000000000\textra\tcookie3value",
164 	*tag_cookie3 = "host.com|/|extra",
165 	*cookie4 = "host.com\tFALSE\t/yyy\tTRUE\t4000000000\tnewcookie\tnewcookievalue",
166 	*tag_cookie4 = "host.com|/yyy|newcookie"
167 ;
168 
169 static int
test_nsc1(void)170 test_nsc1(void)
171 {
172 	struct lws_cache_creation_info ci;
173 	struct lws_cache_ttl_lru *l1 = NULL, *nsc;
174 	lws_cache_results_t cr;
175 	int n, ret = 1;
176 	size_t size;
177 	char *po;
178 
179 	lwsl_user("%s\n", __func__);
180 	tests++;
181 
182 	/* First create a netscape cookie cache object */
183 
184 	memset(&ci, 0, sizeof(ci));
185 	ci.cx = cx;
186 	ci.ops = &lws_cache_ops_nscookiejar;
187 	ci.name = "NSC";
188 	ci.u.nscookiejar.filepath = "./cookies.txt";
189 
190 	nsc = lws_cache_create(&ci);
191 	if (!nsc)
192 		goto cdone;
193 
194 	/* Then a heap cache "L1" as a child of nsc */
195 
196 	ci.ops = &lws_cache_ops_heap;
197 	ci.name = "L1";
198 	ci.parent = nsc;
199 
200 	l1 = lws_cache_create(&ci);
201 	if (!l1)
202 		goto cdone;
203 
204 	lws_cache_debug_dump(nsc);
205 	lws_cache_debug_dump(l1);
206 
207 	lwsl_user("%s: add cookies to L1\n", __func__);
208 
209 	/* add three cookies */
210 
211 	if (lws_cache_write_through(l1, tag_cookie1,
212 				    (const uint8_t *)cookie1, strlen(cookie1),
213 				    lws_now_usecs() + LWS_US_PER_SEC, NULL)) {
214 		lwsl_err("%s: write1 failed\n", __func__);
215 		goto cdone;
216 	}
217 
218 	lws_cache_debug_dump(nsc);
219 	lws_cache_debug_dump(l1);
220 
221 	if (lws_cache_write_through(l1, tag_cookie2,
222 				    (const uint8_t *)cookie2, strlen(cookie2),
223 				    lws_now_usecs() + LWS_US_PER_SEC * 2, NULL)) {
224 		lwsl_err("%s: write2 failed\n", __func__);
225 		goto cdone;
226 	}
227 
228 	lws_cache_debug_dump(nsc);
229 	lws_cache_debug_dump(l1);
230 
231 	if (lws_cache_write_through(l1, tag_cookie3,
232 				    (const uint8_t *)cookie3, strlen(cookie3),
233 				    lws_now_usecs() + LWS_US_PER_SEC * 2, NULL)) {
234 		lwsl_err("%s: write3 failed\n", __func__);
235 		goto cdone;
236 	}
237 
238 	lws_cache_debug_dump(nsc);
239 	lws_cache_debug_dump(l1);
240 
241 	lwsl_user("%s: check cookies in L1\n", __func__);
242 
243 	/* confirm that the cookies are individually in L1 */
244 
245 	if (lws_cache_item_get(l1, tag_cookie1, (const void **)&po, &size) ||
246 	    size != strlen(cookie1) || memcmp(po, cookie1, size)) {
247 		lwsl_err("%s: L1 '%s' missing, size %llu, po %s\n", __func__,
248 			 tag_cookie1, (unsigned long long)size, po);
249 		goto cdone;
250 	}
251 
252 	if (lws_cache_item_get(l1, tag_cookie2, (const void **)&po, &size) ||
253 	    size != strlen(cookie2) || memcmp(po, cookie2, size)) {
254 		lwsl_err("%s: L1 '%s' missing\n", __func__, tag_cookie2);
255 		goto cdone;
256 	}
257 
258 	if (lws_cache_item_get(l1, tag_cookie3, (const void **)&po, &size) ||
259 	    size != strlen(cookie3) || memcmp(po, cookie3, size)) {
260 		lwsl_err("%s: L1 '%s' missing\n", __func__, tag_cookie3);
261 		goto cdone;
262 	}
263 
264 	/* confirm that the cookies are individually in L2 / NSC... normally
265 	 * we don't do this but check via L1 so we can get it from there if
266 	 * present.  But as a unit test, we want to make sure it's in L2 / NSC
267 	 */
268 
269 	lwsl_user("%s: check cookies written thru to NSC\n", __func__);
270 
271 	if (lws_cache_item_get(nsc, tag_cookie1, (const void **)&po, &size) ||
272 	    size != strlen(cookie1) || memcmp(po, cookie1, size)) {
273 		lwsl_err("%s: NSC '%s' missing, size %llu, po %s\n", __func__,
274 			 tag_cookie1, (unsigned long long)size, po);
275 		goto cdone;
276 	}
277 
278 	if (lws_cache_item_get(nsc, tag_cookie2, (const void **)&po, &size) ||
279 	    size != strlen(cookie2) || memcmp(po, cookie2, size)) {
280 		lwsl_err("%s: NSC '%s' missing\n", __func__, tag_cookie2);
281 		goto cdone;
282 	}
283 
284 	if (lws_cache_item_get(nsc, tag_cookie3, (const void **)&po, &size) ||
285 	    size != strlen(cookie3) || memcmp(po, cookie3, size)) {
286 		lwsl_err("%s: NSC '%s' missing\n", __func__, tag_cookie3);
287 		goto cdone;
288 	}
289 
290 	/* let's do a lookup with no results */
291 
292 	lwsl_user("%s: nonexistant get must not pass\n", __func__);
293 
294 	if (!lws_cache_item_get(l1, "x.com|y|z", (const void **)&po, &size)) {
295 		lwsl_err("%s: nonexistant found size %llu, po %s\n", __func__,
296 			 (unsigned long long)size, po);
297 		goto cdone;
298 	}
299 
300 	/*
301 	 * let's try some url paths and check we get the right results set...
302 	 * for / and any cookie, we expect only c1 and c3 to be listed
303 	 */
304 
305 	lwsl_user("%s: wildcard lookup 1\n", __func__);
306 
307 	n = lws_cache_lookup(l1, "host.com|/|*",
308 			     (const void **)&cr.ptr, &cr.size);
309 	if (n) {
310 		lwsl_err("%s: lookup failed %d\n", __func__, n);
311 		goto cdone;
312 	}
313 	lwsl_hexdump_notice(cr.ptr, size);
314 
315 	if (cr.size != 53)
316 		goto cdone;
317 
318 	while (!lws_cache_results_walk(&cr))
319 		lwsl_notice("  %s (%d)\n", (const char *)cr.tag,
320 					   (int)cr.payload_len);
321 
322 	/*
323 	 * for /xxx and any cookie, we expect all 3 listed
324 	 */
325 
326 	lwsl_user("%s: wildcard lookup 2\n", __func__);
327 
328 	n = lws_cache_lookup(l1, "host.com|/xxx|*",
329 			     (const void **)&cr.ptr, &cr.size);
330 	if (n) {
331 		lwsl_err("%s: lookup failed %d\n", __func__, n);
332 		goto cdone;
333 	}
334 
335 	if (cr.size != 84)
336 		goto cdone;
337 
338 	while (!lws_cache_results_walk(&cr))
339 		lwsl_notice("  %s (%d)\n", (const char *)cr.tag,
340 					   (int)cr.payload_len);
341 
342 	/*
343 	 * for /yyyy and any cookie, we expect only c1 and c3
344 	 */
345 
346 	lwsl_user("%s: wildcard lookup 3\n", __func__);
347 
348 	n = lws_cache_lookup(l1, "host.com|/yyyy|*",
349 			     (const void **)&cr.ptr, &cr.size);
350 	if (n) {
351 		lwsl_err("%s: lookup failed %d\n", __func__, n);
352 		goto cdone;
353 	}
354 
355 	if (cr.size != 53)
356 		goto cdone;
357 
358 	while (!lws_cache_results_walk(&cr))
359 		lwsl_notice("  %s (%d)\n", (const char *)cr.tag,
360 					   (int)cr.payload_len);
361 
362 	/*
363 	 * repeat the above test, results should come from cache
364 	 */
365 
366 	lwsl_user("%s: wildcard lookup 4\n", __func__);
367 
368 	n = lws_cache_lookup(l1, "host.com|/yyyy|*",
369 			     (const void **)&cr.ptr, &cr.size);
370 	if (n) {
371 		lwsl_err("%s: lookup failed %d\n", __func__, n);
372 		goto cdone;
373 	}
374 
375 	if (cr.size != 53)
376 		goto cdone;
377 
378 	while (!lws_cache_results_walk(&cr))
379 		lwsl_notice("  %s (%d)\n", (const char *)cr.tag,
380 					   (int)cr.payload_len);
381 
382 	/* now let's try deleting cookie 1 */
383 
384 	if (lws_cache_item_remove(l1, tag_cookie1))
385 		goto cdone;
386 
387 	lws_cache_debug_dump(nsc);
388 	lws_cache_debug_dump(l1);
389 
390 	/* with c1 gone, we should only get c3 */
391 
392 	lwsl_user("%s: wildcard lookup 5\n", __func__);
393 
394 	n = lws_cache_lookup(l1, "host.com|/|*",
395 			     (const void **)&cr.ptr, &cr.size);
396 	if (n) {
397 		lwsl_err("%s: lookup failed %d\n", __func__, n);
398 		goto cdone;
399 	}
400 
401 	if (cr.size != 25)
402 		goto cdone;
403 
404 	while (!lws_cache_results_walk(&cr))
405 		lwsl_notice("  %s (%d)\n", (const char *)cr.tag,
406 					   (int)cr.payload_len);
407 
408 	/*
409 	 * let's add a fourth cookie (third in cache now we deleted one)
410 	 */
411 
412 	if (lws_cache_write_through(l1, tag_cookie4,
413 				    (const uint8_t *)cookie4, strlen(cookie4),
414 				    lws_now_usecs() + LWS_US_PER_SEC * 2, NULL)) {
415 		lwsl_err("%s: write4 failed\n", __func__);
416 		goto cdone;
417 	}
418 
419 	/*
420 	 * for /yy and any cookie, we expect only c3
421 	 */
422 
423 	lwsl_user("%s: wildcard lookup 6\n", __func__);
424 
425 	n = lws_cache_lookup(l1, "host.com|/yy|*",
426 			     (const void **)&cr.ptr, &cr.size);
427 	if (n) {
428 		lwsl_err("%s: lookup failed %d\n", __func__, n);
429 		goto cdone;
430 	}
431 
432 	if (cr.size != 25)
433 		goto cdone;
434 
435 	while (!lws_cache_results_walk(&cr))
436 		lwsl_notice("  %s (%d)\n", (const char *)cr.tag,
437 					   (int)cr.payload_len);
438 
439 	/*
440 	 * for /yyy and any cookie, we expect  c3 and c4
441 	 */
442 
443 	lwsl_user("%s: wildcard lookup 7\n", __func__);
444 
445 	n = lws_cache_lookup(l1, "host.com|/yyy|*",
446 			     (const void **)&cr.ptr, &cr.size);
447 	if (n) {
448 		lwsl_err("%s: lookup failed %d\n", __func__, n);
449 		goto cdone;
450 	}
451 
452 	if (cr.size != 57)
453 		goto cdone;
454 
455 	while (!lws_cache_results_walk(&cr))
456 		lwsl_notice("  %s (%d)\n", (const char *)cr.tag,
457 					   (int)cr.payload_len);
458 
459 	/* that's ok then */
460 
461 	lwsl_user("%s: done\n", __func__);
462 
463 	ret = 0;
464 
465 cdone:
466 	lws_cache_destroy(&nsc);
467 	lws_cache_destroy(&l1);
468 
469 	if (ret)
470 		lwsl_warn("%s: fail\n", __func__);
471 
472 	return ret;
473 }
474 #endif
475 
476 
main(int argc,const char ** argv)477 int main(int argc, const char **argv)
478 {
479 	struct lws_context_creation_info info;
480 
481 	memset(&info, 0, sizeof info);
482 	lws_cmdline_option_handle_builtin(argc, argv, &info);
483 	info.fd_limit_per_thread = 1 + 6 + 1;
484 	info.port = CONTEXT_PORT_NO_LISTEN;
485 
486 	lwsl_user("LWS API selftest: lws_cache\n");
487 
488 	cx = lws_create_context(&info);
489 	if (!cx) {
490 		lwsl_err("lws init failed\n");
491 		return 1;
492 	}
493 
494 	if (test_just_l1())
495 		fail++;
496 	if (test_just_l1_limits())
497 		fail++;
498 
499 #if defined(LWS_WITH_CACHE_NSCOOKIEJAR)
500 	if (test_nsc1())
501 		fail++;
502 #endif
503 
504 	lws_context_destroy(cx);
505 
506 	if (tests && !fail)
507 		lwsl_user("Completed: PASS\n");
508 	else
509 		lwsl_err("Completed: FAIL %d / %d\n", fail, tests);
510 
511 	return 0;
512 }
513