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