From b25079c4b4ee219c1a6895e50ecbb894c1d21e69 Mon Sep 17 00:00:00 2001 From: Andy Green Date: Wed, 26 May 2021 09:13:03 +0100 Subject: [PATCH] lws_cache_ttl --- .sai.json | 3 + CMakeLists.txt | 3 +- READMEs/README.fault-injection.md | 4 +- READMEs/README.lws_cache.md | 160 +++ cmake/lws_config.h.in | 1 + doc-assets/lws_cache-1.png | Bin 0 -> 74386 bytes doc-assets/lws_cache-2.png | Bin 0 -> 120480 bytes include/libwebsockets.h | 1 + include/libwebsockets/lws-cache-ttl.h | 348 +++++++ lib/core-net/private-lib-core-net.h | 1 - lib/core-net/service.c | 8 +- lib/misc/CMakeLists.txt | 15 + lib/misc/cache-ttl/file.c | 942 ++++++++++++++++++ lib/misc/cache-ttl/heap.c | 612 ++++++++++++ lib/misc/cache-ttl/lws-cache-ttl.c | 300 ++++++ .../cache-ttl/private-lib-misc-cache-ttl.h | 98 ++ .../api-test-lws_cache/CMakeLists.txt | 19 + .../api-tests/api-test-lws_cache/README.md | 22 + .../api-tests/api-test-lws_cache/main.c | 512 ++++++++++ .../api-tests/api-test-lws_cache/text1.txt | 3 + 20 files changed, 3048 insertions(+), 4 deletions(-) create mode 100644 READMEs/README.lws_cache.md create mode 100644 doc-assets/lws_cache-1.png create mode 100644 doc-assets/lws_cache-2.png create mode 100644 include/libwebsockets/lws-cache-ttl.h create mode 100644 lib/misc/cache-ttl/file.c create mode 100644 lib/misc/cache-ttl/heap.c create mode 100644 lib/misc/cache-ttl/lws-cache-ttl.c create mode 100644 lib/misc/cache-ttl/private-lib-misc-cache-ttl.h create mode 100644 minimal-examples/api-tests/api-test-lws_cache/CMakeLists.txt create mode 100644 minimal-examples/api-tests/api-test-lws_cache/README.md create mode 100644 minimal-examples/api-tests/api-test-lws_cache/main.c create mode 100644 minimal-examples/api-tests/api-test-lws_cache/text1.txt diff --git a/.sai.json b/.sai.json index 1d9fcfc98..a71769472 100644 --- a/.sai.json +++ b/.sai.json @@ -279,6 +279,9 @@ "nologs": { "cmake": "-DLWS_WITH_NO_LOGS=ON" }, + "cookiejar": { + "cmake": "-DLWS_WITH_CACHE_NSCOOKIEJAR=ON" + }, "smp": { "cmake": "-DLWS_MAX_SMP=32 -DLWS_WITH_MINIMAL_EXAMPLES=1" }, diff --git a/CMakeLists.txt b/CMakeLists.txt index d30f49366..789978d6c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -257,7 +257,7 @@ option(LWS_REPRODUCIBLE "Build libwebsockets reproducible. It removes the build option(LWS_WITH_MINIMAL_EXAMPLES "Also build the normally standalone minimal examples, for QA" OFF) option(LWS_WITH_LWSAC "lwsac Chunk Allocation api" ON) option(LWS_WITH_CUSTOM_HEADERS "Store and allow querying custom HTTP headers (H1 only)" ON) -option(LWS_WITH_DISKCACHE "Hashed cache directory with lazy LRU deletion to size limit" OFF) +option(LWS_WITH_DISKCACHE "Hashed cache directory with lazy LRU deletion to size limit (unrelated to lws_cache_ttl)" OFF) option(LWS_WITH_ASAN "Build with gcc runtime sanitizer options enabled (needs libasan)" OFF) option(LWS_WITH_LEJP_CONF "With LEJP configuration parser as used by lwsws" OFF) option(LWS_WITH_ZLIB "Include zlib support (required for extensions)" OFF) @@ -278,6 +278,7 @@ option(LWS_WITH_SUL_DEBUGGING "Enable zombie lws_sul checking on object deletion option(LWS_WITH_PLUGINS_API "Build generic lws_plugins apis (see LWS_WITH_PLUGINS to also build protocol plugins)" OFF) option(LWS_WITH_CONMON "Collect introspectable connection latency stats on individual client connections" ON) option(LWS_WITHOUT_EVENTFD "Force using pipe instead of eventfd" OFF) +option(LWS_WITH_CACHE_NSCOOKIEJAR "Build file-backed lws-cache-ttl that uses netscape cookie jar format (linux-only)" OFF) if (${CMAKE_SYSTEM_NAME} MATCHES "Linux") option(LWS_WITH_NETLINK "Monitor Netlink for Routing Table changes" ON) diff --git a/READMEs/README.fault-injection.md b/READMEs/README.fault-injection.md index b7e3f3076..381d459a9 100644 --- a/READMEs/README.fault-injection.md +++ b/READMEs/README.fault-injection.md @@ -262,6 +262,9 @@ interpreted by your shell. |context||`ctx_createfail_ss_pol1`|Fail context creation due to ss policy parse start failed (if policy enabled)| |context||`ctx_createfail_ss_pol2`|Fail context creation due to ss policy parse failed (if policy enabled)| |context||`ctx_createfail_ss_pol3`|Fail context creation due to ss policy set failed (if policy enabled)| +|context||`cache_createfail`|Fail `lws_cache` creation due to OOM| +|context||`cache_lookup_oom`|Fail `lws_cache` lookup due to OOM| +|vhost|`vh`|`vh_create_oom`|Fail vh creation on vh object alloc OOM| |vhost|`vh`|`vh_create_oom`|Fail vh creation on vh object alloc OOM| |vhost|`vh`|`vh_create_pcols_oom`|Fail vh creation at protocols alloc OOM| |vhost|`vh`|`vh_create_access_log_open_fail`|Fail vh creation due to unable to open access log (LWS_WITH_ACCESS_LOG)| @@ -294,7 +297,6 @@ interpreted by your shell. |ssproxy|`ss`|`ssproxy_onward_conn_fail`|Act as if proxy onward client connection failed immediately| |ssproxy|`ss`|`ssproxy_dsh_c2p_pay_oom`|Cause proxy's DSH alloc for C->P payload to fail| - ## Well-known namespace targets Namespaces can be used to target these more precisely, for example even though diff --git a/READMEs/README.lws_cache.md b/READMEs/README.lws_cache.md new file mode 100644 index 000000000..9a471c121 --- /dev/null +++ b/READMEs/README.lws_cache.md @@ -0,0 +1,160 @@ +# lws_cache: Flexible single and multilevel caching + +lws_cache implements a single- or multi-level cache for generic payload items +that are **keyed by a unique string**. + +![lws_cache overview](../doc-assets/lws_cache-1.png) + +L1 cache is always stored on heap, but it may be hooked up to additional levels +of cache objects with different backing storage. The last level always contains +a complete set of cached items, earlier levels may be empty or contain a partial +set of objects. + +User code can define its own subclassed lws_cache objects with custom storage +formats and media, while being able to take advantage of a suitably-sized L1 +heap cache to minimize the cost of repeated access. + +![lws_cache overview](../doc-assets/lws_cache-2.png) + +You can find examples of how to create, use and destroy single and multilevel +caches in `minimal-examples/api-tests/api-test-lws_cache` + +## Cache size restriction, LRU and TTL + +The max heap footprint of its items and max number of items can be capped. LRU +tracking is performed so the least recently relevant items are evicted first. +It's also possible to limit the maximum size of any single payload. + +Time To Live (TTL) tracking is also performed automatically, so cached items +auto-expire if a non-zero TTL is provided when the object is created. A user +callback can be defined to get called when an item is about to be removed from +a particular cache level, in case any housekeeping needed. + +## Atomicity + +Items in L1 can be accessed in heap casually and reliably if the following is +borne in mind: + + - Any return to the event loop may perform removal of cache items due to TTL +expiry + + - Any operation that writes new items may evict items from non-last +cache levels which have limits to the footprint or item count to make room for +it, using LRU ordering. + +In short process cache results before returning to the event loop or writing +or removing items in the cache. + +## Cache creation + +Caches are created using an info struct `struct lws_cache_creation_info` +that should be zeroed down. Most members are optional and can be left at zero, +a pointer to the lws_context and a short cache name are mandatory. + +``` +struct lws_cache_ttl_lru * +lws_cache_create(const struct lws_cache_creation_info *info); +``` + +How caches work is defined by an "ops struct" that the cache is bound to at +creation time. `lws_cache_ops_heap` ops struct is provided by lws, you can +define your own to implement your own specialized cache level. See +`./include/libwebsockets/lws-cache-ttl.h` for the definition. + +## Cache destruction + +Created cache levels should be destroyed when you are finished with them. + +``` +void +lws_cache_destroy(struct lws_cache_ttl_lru **cache); +``` + +For L1, in heap, this frees any allocations. For other levels, eg, with file +storage for the items, this would close the file and leave any entries as they +are. + +## Writethrough + +``` +int +lws_cache_write_through(struct lws_cache_ttl_lru *cache, + const char *specific_key, const uint8_t *source, + size_t size, lws_usec_t expiry, void **ppay); +``` + +The combined caches are always accessed via the L1 cache, writing new items is +done at L1 and writes through to each cache layer immediately, so new items go +into the backing store without delay, but are available from heap for read. + +If existing keys are rewritten, the previous item of the same key is deleted +from all levels of the cache before writing the new one. + +## Removal + +Removal also is performed at all cache levels at once. + +``` +int +lws_cache_item_remove(struct lws_cache_ttl_lru *cache, const char *wildcard_key); +``` + +internally earlier cache levels can evict cached items just at their level, but +this is triggered automatically and not by api. + +A wildcard key is supported, removing all items matching, eg "myitem*". + +## Get by key + +``` +int +lws_cache_item_get(struct lws_cache_ttl_lru *cache, const char *specific_key, + const void **pdata, size_t *psize); +``` + +Apis are provided to get the blob related to a specific key, if it exists at +any cache layer. Again this should use L1, it will bring a copy of the item +into L1 if one is not already there, so it can be accessed from heap. + +## Lookup with wildcards + +``` +int +lws_cache_lookup(struct lws_cache_ttl_lru *cache, const char *wildcard_key, + const void **pdata, size_t *psize); +``` + +lws_cache also supports **lookup** queries that contain wildcards or otherwise match +on multiple keys according to cache-specific rules. These queries do not return +a single item, instead they return lists of keys that match, in a blob of its +own that is also cached in L1. + +The user can walk the lookup results blob using a provided helper api + +``` +int +lws_cache_results_walk(lws_cache_results_t *walk_ctx); +``` + +After recovering each result key this way, the user code can use the _get api +to access the blob for each indiviudally. + +The lookup results themselves are cached in L1, any new key that matches the +wildcard lookup in any cached results, or any deletion of items with keys +matching the cached wildcard lookup invalidate the affected cached lookup +results so they will be regenerated next time. + +In the typical case after a lookup, at least for a while the lookup results blob +and all items mentioned in the lookup results will already be in L1 and cheaply +accessible. + +## Expunging + +An api is also provided to "expunge" or completely empty all cache levels and +corresponding backing stores. + +``` +int +lws_cache_expunge(struct lws_cache_ttl_lru *cache); +``` + diff --git a/cmake/lws_config.h.in b/cmake/lws_config.h.in index 6ea42574e..7f480e10d 100644 --- a/cmake/lws_config.h.in +++ b/cmake/lws_config.h.in @@ -175,6 +175,7 @@ #cmakedefine LWS_WITH_NETLINK #cmakedefine LWS_WITH_NETWORK #cmakedefine LWS_WITH_NO_LOGS +#cmakedefine LWS_WITH_CACHE_NSCOOKIEJAR #cmakedefine LWS_WITH_CLIENT #cmakedefine LWS_WITHOUT_EXTENSIONS #cmakedefine LWS_WITH_SERVER diff --git a/doc-assets/lws_cache-1.png b/doc-assets/lws_cache-1.png new file mode 100644 index 0000000000000000000000000000000000000000..260677d858671192c7a07b34a8a617e3da1cb18c GIT binary patch literal 74386 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 literal 0 HcmV?d00001 diff --git a/doc-assets/lws_cache-2.png b/doc-assets/lws_cache-2.png new file mode 100644 index 0000000000000000000000000000000000000000..ab69c3dc1c10c0787c71bba25d789bdbb93af6b7 GIT binary patch literal 120480 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

78?Y@$aj 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%0D3>az>j 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ʏxGR=$G8^YWlLbTTqC>uY*p z>5ao}jDNW_HIPc>RK&KTo=(tILNO6Om+$&BWy8 z=ktLG&|y&2z>P_PAbjX_>=#V}o%m&EX9ooaz5)YMfk`LLlC#>{&vtI#9$h$IzS%E^ zw+7uLqZ}&e6FhN-NyVm$_IBOsx;l}9v3l`dO}tsV1xezr#{ts9pC*2YL)gS;5(diW z6el>6T~Xr6?NN9l808qVFQi{~U5fPusr|-a6Bh|P`Cbe!Q-T2Y*yLob;7?-O4=q6@EVC1{vtr52tgOCe z#`}&O8I3YjwdFa7#sw9@XFH>Y^;dQ%6Ym@cFBp0(m|@XW*VM$kefu;dBt&mCp+pZL z038ePgr)rm*hdKbTfA@{G-EdAYnlO;vjH*D3AeQwP^#SEga?V>p({+mY)*+A7)s*^ z@3}o^fSY~|(mVJhV8C#P?OlE#csSh0sjx12Ffszy8Ky1>PDxsNIjwiI7a&E1`SCf7 ztc?7mt4U30wGw7gnbR^VxVcqNPY;0e@KDjxXhFQJg{dhqCjn4H)>caYMiW;x-XeY! zo!q~hsHq8gj3`M2%s3k%=HJ(6tZ!g|ah5%@9+AYrVACRuZHcWz5$ZX$-diEO62@d0 zzo))G;K;E5c^W4yi=-p37vHUUNKt;Nsr}(UJq@eRSE5Qlk-0h6Q;$-V2Tl*T#T=?i zU8F=Xsw*B>Q#1$jCn(})CBPI!{rmBQn508_{Rt4`ykaOfxb&*)pqdZdZ>lOTJQgiI z)rR7V8rRx0xNsum<}qPCUAoj}+nK0pItj&)_)v*kAeRyImxeCi*TW-Yr|kjtQe~v- z+c)MS!$wfiw!-$k1!uFi295Jv=V0!;cCSp8h?|7`>Bk<*}4v*d(lZZ6v$nJ zh3F}!Tf&5|*41FCUc+1wk9uS@cn+pHKELv)6Spvcwhb2RtoCej@xt$IMnzm?@|Z*K zRYeWlw>-;zvC_rG#qe+izI;h`ds(wQLiSWh|8@+*B)&GFYgnG2Fn2<6)6FndE>a2G zWzxOQIlJF)AX-61d`XT0tgP!dX;blbNB?_-tir zbyfB#%NC|vE60u=#SohEE%37k=@{Ln zfHfmV)9^ls4)!gqI$^xpKIozGR?RTYM_7EKqodczV;=IclGMioJO=_*_nk5Ir+r>| ze?FmY)5h)%6%W^)tWf=^5)`+dZ+bt~#KDITtj>8W-iwmf3dC6cj*d=~l!NO;T4@jE z44rJ-p!>99``_8<(czPs{@hksY~Qka8|{1Q3OUBarhe1klfMJ%GPt%seubhQcj0e$ zLFmwsm6%=ErNik-Nk34k2%|LK-8;yXF}x3*;yQN zS3+7^fc&e{(o&KJ!3SRZr$vt6F*xYj6AtMF`=>eFB`NZYF> zRQe>hQ^fy!asF3IrAgb%>o@H4xlZ%v5Sjbm>uQ?F`fRtO_Nfj!9-uQdf7;8>A@1Lk zD05Abu5IGff54}@s<5GisyE~i<=^Y^zljaL>lv0oP1+amlfRGIxIaRj>)&&!{C1A! z@);j6ExY&tN9ZUGd#Mrmc-ViB@|XTB>@-e)EOL=UK5vsFHGS`Sq5s~&?k8T2@*BQI z-4^E_I=n%``@RHv%gz)Yc;(%vFWiMDc;S&thB21iZrtv@zB?~6*PU; zeX*fiSFbP~BE|dtoTuqi?b~H8?E2sRQ%NJa)4R|7N?1uLY+6_P8kdIpCCy7}r}X9r z|2~ZySF9ZUxb24Tzmvs0Qa5@j+wY+>7X3^sLNi7)My*xq?b)3B?-$oD?kc^#mwtYY kltvo3W6kL^?b^XN9?6%xLb71d9r$s2qBA8#3b #include #include +#include #if defined(LWS_WITH_SYS_SMD) #include #endif diff --git a/include/libwebsockets/lws-cache-ttl.h b/include/libwebsockets/lws-cache-ttl.h new file mode 100644 index 000000000..9942dc7d7 --- /dev/null +++ b/include/libwebsockets/lws-cache-ttl.h @@ -0,0 +1,348 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010 - 2021 Andy Green + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +/** \defgroup lws_cache_ttl Cache supporting expiry + * ##Cache supporting expiry + * + * These apis let you quickly and reliably implement caches of named objects, + * that have a "destroy-by date" and cache limits that will be observed. + * + * You can instantiate as many caches as you need. The first one must be an + * L1 / heap cache type, it can have parents and grandparents of other types + * which are accessible why writing / looking up and getting from the L1 cache. + * The outer "cache" layer may persistently store items to a backing store. + * + * Allocated object memory is entirely for the use of user code, up to the + * requested size. + * + * The key name for the listed objects may be any string chosen by the user, + * there is no special length limit as it is also allocated. + * + * Both expiry and LRU orderings are kept so it is easy to find out usage + * ordering and when the next object that will expire. + * + * Cached objects may be destroyed any time you go around the event loop, when + * you allocate new objects (to keep the whole cache under the specified limit), + * or when their expiry time arrives. So you shouldn't keep copies of pointers + * to cached objects after returning to the event loop. + */ +///@{ + + +struct lws_cache_ttl_lru; + +/** + * lws_cache_write_through() - add a new cache item object in all layers + * + * \param cache: the existing cache to allocate the object in + * \param specific_key: a key string that identifies the item in the cache + * \param source: optional payload for the cached item, NULL means caller will + * write the payload + * \param size: the size of the object to allocate + * \param expiry: the usec time that the object will autodestroy + * \param ppay: NULL, or a pointer to a void * to be set to the L1 payload + * + * If an item with the key already exists, it is destroyed before allocating a + * new one. + * + * Returns 0 if successful. The written entry will be scheduled to be auto- + * destroyed when \p expiry occurs. + * + * Adding or removing cache items may cause invalidation of cached queries. + */ +LWS_VISIBLE LWS_EXTERN int /* only valid until return to event loop */ +lws_cache_write_through(struct lws_cache_ttl_lru *cache, + const char *specific_key, const uint8_t *source, + size_t size, lws_usec_t expiry, void **ppay); + +typedef struct lws_cache_match { + lws_dll2_t list; + lws_usec_t expiry; + /* earliest expiry amongst results */ + size_t payload_size; + /**< the payload is not attached here. This is a hint about what + * (*get)() will return for this tag name. + */ + size_t tag_size; + + /* tag name + NUL is overcommitted */ +} lws_cache_match_t; + +/** + * lws_cache_heap_lookup() - get a list of matching items + * + * \param cache: the cache to search for the key + * \param wildcard_key: the item key string, may contain wildcards + * \param pdata: pointer to pointer to be set to the serialized result list + * \param psize: pointer to size_t to receive length of serialized result list + * + * This finds all unique items in the final cache that match search_key, which + * may contain wildcards. It does not return the payloads for matching items, + * just a list of specific tags in the that match. + * + * If successful, results are provided in a serialized list format, in no + * particular order, each result has the following fields + * + * - BE32: payload size in bytes (payload itself is not included) + * - BE32: specific tag name length in bytes + * - chars: tag name with terminating NUL + * + * These serialized results are themselves cached in L1 cache (only) and the + * result pointers are set pointing into that. If the results are still in L1 + * cache next time this api is called, the results will be returned directly + * from that without repeating the expensive lookup on the backup store. That + * is why the results are provided in serialized form. + * + * The cached results list expiry is set to the earliest expiry of any listed + * item. Additionally any cached results are invalidated on addition or + * deletion (update is done as addition + deletion) of any item that would + * match the results' original wildcard_key. For the typical case new items + * are rare compared to lookups, this is efficient. + * + * Lookup matching does not itself affect LRU or cache status of the result + * itsems. Typically user code will get the lookup results, and then perform + * get operations on each item in its desired order, that will bring the items + * to the head of the LRU list and occupy L1 cache. + * + * Returns 0 if proceeded alright, or nonzero if error. If there was an error, + * any partial results set has been deallocated cleanly before returning. + */ +LWS_VISIBLE LWS_EXTERN int +lws_cache_lookup(struct lws_cache_ttl_lru *cache, const char *wildcard_key, + const void **pdata, size_t *psize); + +/** + * lws_cache_item_get() - bring a specific item into L1 and get payload info + * + * \param cache: the cache to search for the key + * \param specific_key: the key string of the item to get + * \param pdata: pointer to a void * to be set to the payload in L1 cache + * \param psize: pointer to a size_t to be set to the payload size + * + * If the cache still has an item matching the key string, it will be destroyed. + * + * Adding or removing cache items may cause invalidation of cached queries. + * + * Notice the cache payload is a blob of the given size. If you are storing + * strings, there is no NUL termination unless you stored them with it. + * + * Returns 0 if successful. + */ +LWS_VISIBLE LWS_EXTERN int +lws_cache_item_get(struct lws_cache_ttl_lru *cache, const char *specific_key, + const void **pdata, size_t *psize); + +/** + * lws_cache_item_remove() - remove item from all cache levels + * + * \param cache: the cache to search for the key + * \param wildcard_key: the item key string + * + * Removes any copy of any item matching the \p wildcard_key from any cache + * level in one step. + * + * Adding or removing cache items may cause invalidation of cached queries + * that could refer to the removed item. + */ +LWS_VISIBLE LWS_EXTERN int +lws_cache_item_remove(struct lws_cache_ttl_lru *cache, const char *wildcard_key); + +/** + * lws_cache_footprint() - query the amount of storage used by the cache layer + * + * \param cache: cache to query + * + * Returns number of payload bytes stored in cache currently + */ +LWS_VISIBLE LWS_EXTERN uint64_t +lws_cache_footprint(struct lws_cache_ttl_lru *cache); + +/** + * lws_cache_debug_dump() - if built in debug mode dump cache contents to log + * + * \param cache: cache to dump + * + * If lws was built in debug mode, dump cache to log, otherwise a NOP. + */ +LWS_VISIBLE LWS_EXTERN void +lws_cache_debug_dump(struct lws_cache_ttl_lru *cache); + +typedef struct lws_cache_results { + const uint8_t *ptr; /* set before using walk api */ + size_t size; /* set before using walk api */ + + size_t payload_len; + size_t tag_len; + const uint8_t *tag; +} lws_cache_results_t; + +/** + * lws_cache_results_walk() - parse next result + * + * \param walk_ctx: the context of the results blob to walk + * + * Caller must initialize \p walk_ctx.ptr and \p walk_ctx.size before calling. + * These are set to the results returned from a _lookup api call. + * + * The call returns 0 if the struct elements have been set to a result, or 1 + * if there where no more results in the blob to walk. + * + * If successful, after the call \p payload_len is set to the length of the + * payload related to this result key (the payload itself is not present), + * \p tag_len is set to the length of the result key name, and \p tag is set + * to the result tag name, with a terminating NUL. + */ +LWS_VISIBLE LWS_EXTERN int +lws_cache_results_walk(lws_cache_results_t *walk_ctx); + +typedef void (*lws_cache_item_destroy_cb)(void *item, size_t size); +struct lws_cache_creation_info { + struct lws_context *cx; + /**< Mandatory: the lws_context */ + const char *name; + /**< Mandatory: short cache name */ + lws_cache_item_destroy_cb cb; + /**< NULL, or a callback that can hook cache item destory */ + struct lws_cache_ttl_lru *parent; + /**< NULL, or next cache level */ + const struct lws_cache_ops *ops; + /**< NULL for default, heap-based ops, else custom cache storage and + * query implementation */ + + union { + struct { + const char *filepath; + /**< the filepath to store items in */ + } nscookiejar; + } u; + /**< these are extra configuration for specific cache types */ + + size_t max_footprint; + /**< 0, or the max heap allocation allowed before destroying + * lru items to keep it under the limit */ + size_t max_items; + /**< 0, or the max number of items allowed in the cache before + * destroying lru items to keep it under the limit */ + size_t max_payload; + /**< 0, or the max allowed payload size for one item */ + int tsi; + /**< 0 unless using SMP, then tsi to bind sul to */ +}; + +struct lws_cache_ops { + struct lws_cache_ttl_lru * + (*create)(const struct lws_cache_creation_info *info); + /**< create an instance of the cache type specified in info */ + + void + (*destroy)(struct lws_cache_ttl_lru **_cache); + /**< destroy the logical cache instance pointed to by *_cache, doesn't + * affect any NV backing storage */ + + int + (*expunge)(struct lws_cache_ttl_lru *cache); + /**< completely delete any backing storage related to the cache + * instance, eg, delete the backing file */ + + int + (*write)(struct lws_cache_ttl_lru *cache, const char *specific_key, + const uint8_t *source, size_t size, lws_usec_t expiry, + void **ppvoid); + /**< create an entry in the cache level according to the given info */ + int + (*tag_match)(struct lws_cache_ttl_lru *cache, const char *wc, + const char *tag, char lookup_rules); + /**< Just tell us if tag would match wildcard, using whatever special + * rules the backing store might use for tag matching. 0 indicates + * it is a match on wildcard, nonzero means does not match. + */ + int + (*lookup)(struct lws_cache_ttl_lru *cache, const char *wildcard_key, + lws_dll2_owner_t *results_owner); + /**+ add keys for search_key matches not already listed in the results + * owner */ + int + (*invalidate)(struct lws_cache_ttl_lru *cache, const char *wildcard_key); + /**< remove matching item(s) from cache level */ + + int + (*get)(struct lws_cache_ttl_lru *cache, const char *specific_key, + const void **pdata, size_t *psize); + /**< if it has the item, fills L1 with item. updates LRU, and returns + * pointer to payload in L1 */ + + void + (*debug_dump)(struct lws_cache_ttl_lru *cache); + /**< Helper to dump the whole cache contents to log, useful for debug */ +}; + +/** + * lws_cache_create() - create an empty cache you can allocate items in + * + * \param info: a struct describing the cache to create + * + * Create an empty cache you can allocate items in. The cache will be kept + * below the max_footprint and max_items limits if they are nonzero, by + * destroying least-recently-used items until it remains below the limits. + * + * Items will auto-destroy when their expiry time is reached. + * + * When items are destroyed from the cache, if \p cb is non-NULL, it will be + * called back with the item pointer after it has been removed from the cache, + * but before it is deallocated and destroyed. + * + * context and tsi are used when scheduling expiry callbacks + */ +LWS_VISIBLE LWS_EXTERN struct lws_cache_ttl_lru * +lws_cache_create(const struct lws_cache_creation_info *info); + +/** + * lws_cache_destroy() - destroy a previously created cache + * + * \param cache: pointer to the cache + * + * Everything in the cache is destroyed, then the cache itself is destroyed, + * and *cache set to NULL. + */ +LWS_VISIBLE LWS_EXTERN void +lws_cache_destroy(struct lws_cache_ttl_lru **cache); + +/** + * lws_cache_expunge() - destroy all items in cache and parents + * + * \param cache: pointer to the cache + * + * Everything in the cache and parents is destroyed, leaving it empty. + * If the cache has a backing store, it is deleted. + * + * Returns 0 if no problems reported at any cache layer, else nonzero. + */ +LWS_VISIBLE LWS_EXTERN int +lws_cache_expunge(struct lws_cache_ttl_lru *cache); + +LWS_VISIBLE extern const struct lws_cache_ops lws_cache_ops_heap, + lws_cache_ops_nscookiejar; + +///@} + diff --git a/lib/core-net/private-lib-core-net.h b/lib/core-net/private-lib-core-net.h index 7bfb7ee65..b232bfe6a 100644 --- a/lib/core-net/private-lib-core-net.h +++ b/lib/core-net/private-lib-core-net.h @@ -238,7 +238,6 @@ struct client_info_stash { #define LWS_H2_FRAME_HEADER_LENGTH 9 - lws_usec_t __lws_sul_service_ripe(lws_dll2_owner_t *own, int num_own, lws_usec_t usnow); diff --git a/lib/core-net/service.c b/lib/core-net/service.c index 4a8f66dae..01e3d3c61 100644 --- a/lib/core-net/service.c +++ b/lib/core-net/service.c @@ -300,10 +300,16 @@ lws_service_adjust_timeout(struct lws_context *context, int timeout_ms, int tsi) pt = &context->pt[tsi]; if (pt->evlib_pt) { - lws_usec_t u = __lws_sul_service_ripe(pt->pt_sul_owner, + lws_usec_t u; + + lws_pt_lock(pt, __func__); /* -------------- pt { */ + + u = __lws_sul_service_ripe(pt->pt_sul_owner, LWS_COUNT_PT_SUL_OWNERS, lws_now_usecs()); if (u < (lws_usec_t)timeout_ms * (lws_usec_t)1000) timeout_ms = (int)(u / 1000); + + lws_pt_unlock(pt); } /* diff --git a/lib/misc/CMakeLists.txt b/lib/misc/CMakeLists.txt index cd040aa58..fcdb2a5f7 100644 --- a/lib/misc/CMakeLists.txt +++ b/lib/misc/CMakeLists.txt @@ -36,12 +36,27 @@ list(APPEND SOURCES misc/prng.c misc/lws-ring.c) +if (LWS_WITH_NETWORK) + list(APPEND SOURCES + misc/cache-ttl/lws-cache-ttl.c + misc/cache-ttl/heap.c + ) + + if (LWS_WITH_CACHE_NSCOOKIEJAR) + list(APPEND SOURCES + misc/cache-ttl/file.c) + endif() + +endif() + if (LWS_WITH_FTS) list(APPEND SOURCES misc/fts/trie.c misc/fts/trie-fd.c) endif() +# this is an older, standalone hashed disk cache +# implementation unrelated to lws-cache-ttl if (LWS_WITH_DISKCACHE) list(APPEND SOURCES misc/diskcache.c) diff --git a/lib/misc/cache-ttl/file.c b/lib/misc/cache-ttl/file.c new file mode 100644 index 000000000..58a4b6942 --- /dev/null +++ b/lib/misc/cache-ttl/file.c @@ -0,0 +1,942 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010 - 2021 Andy Green + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * Implements a cache backing store compatible with netscape cookies.txt format + * There is one entry per "line", and fields are tab-delimited + * + * We need to know the format here, because while the unique cookie tag consists + * of "hostname|urlpath|cookiename", that does not appear like that in the file; + * we have to go parse the fields and synthesize the corresponding tag. + * + * We rely on all the fields except the cookie value fitting in a 256 byte + * buffer, and allow eating multiple buffers to get a huge cookie values. + * + * Because the cookie file is a device-wide asset, although lws will change it + * from the lws thread without conflict, there may be other processes that will + * change it by removal and regenerating the file asynchronously. For that + * reason, file handles are opened fresh each time we want to use the file, so + * we always get the latest version. + * + * When updating the file ourselves, we use a lockfile to ensure our process + * has exclusive access. + * + * + * Tag Matching rules + * + * There are three kinds of tag matching rules + * + * 1) specific - tag strigs must be the same + * 2) wilcard - tags matched using optional wildcards + * 3) wildcard + lookup - wildcard, but path part matches using cookie scope rules + * + */ + +#include +#include "private-lib-misc-cache-ttl.h" + +typedef enum nsc_iterator_ret { + NIR_CONTINUE = 0, + NIR_FINISH_OK = 1, + NIR_FINISH_ERROR = -1 +} nsc_iterator_ret_t; + +typedef enum cbreason { + LCN_SOL = (1 << 0), + LCN_EOL = (1 << 1) +} cbreason_t; + +typedef int (*nsc_cb_t)(lws_cache_nscookiejar_t *cache, void *opaque, int flags, + const char *buf, size_t size); + +static void +expiry_cb(lws_sorted_usec_list_t *sul); + +static int +nsc_backing_open_lock(lws_cache_nscookiejar_t *cache, int mode, const char *par) +{ + int sanity = 50; + char lock[128]; + int fd_lock, fd; + + lwsl_debug("%s: %s\n", __func__, par); + + lws_snprintf(lock, sizeof(lock), "%s.LCK", + cache->cache.info.u.nscookiejar.filepath); + + do { + fd_lock = open(lock, LWS_O_CREAT | O_EXCL, 0600); + if (fd_lock != LWS_INVALID_FILE) { + close(fd_lock); + break; + } + + if (!sanity--) { + lwsl_warn("%s: unable to lock %s: errno %d\n", __func__, + lock, errno); + return LWS_INVALID_FILE; + } + + usleep(100000); + } while (1); + + fd = open(cache->cache.info.u.nscookiejar.filepath, + LWS_O_CREAT | mode, 0600); + + if (fd == LWS_INVALID_FILE) { + lwsl_warn("%s: unable to open or create %s\n", __func__, + cache->cache.info.u.nscookiejar.filepath); + unlink(lock); + } + + return fd; +} + +static void +nsc_backing_close_unlock(lws_cache_nscookiejar_t *cache, int fd) +{ + char lock[128]; + + lwsl_debug("%s\n", __func__); + + lws_snprintf(lock, sizeof(lock), "%s.LCK", + cache->cache.info.u.nscookiejar.filepath); + close(fd); + unlink(lock); +} + +/* + * We're going to call the callback with chunks of the file with flags + * indicating we're giving it the start of a line and / or giving it the end + * of a line. + * + * It's like this because the cookie value may be huge (and to a lesser extent + * the path may also be big). + * + * If it's the start of a line (flags on the cb has LCN_SOL), then the buffer + * contains up to the first 256 chars of the line, it's enough to match with. + * + * We cannot hold the file open inbetweentimes, since other processes may + * regenerate it, so we need to bind to a new inode. We open it with an + * exclusive flock() so other processes can't replace conflicting changes + * while we also write changes, without having to wait and see our changes. + */ + +static int +nscookiejar_iterate(lws_cache_nscookiejar_t *cache, int fd, + nsc_cb_t cb, void *opaque) +{ + int m = 0, n = 0, e, r = LCN_SOL, ignore = 0, ret = 0; + char temp[256], eof = 0; + + lseek(fd, 0, SEEK_SET); + + do { /* for as many buffers in the file */ + + int n1; + + lwsl_debug("%s: n %d, m %d\n", __func__, n, m); + + n1 = (int)read(fd, temp + m, sizeof(temp) - (size_t)m); + + lwsl_debug("%s: n1 %d\n", __func__, n1); + + if (n1 <= 0) { + eof = 1; + if (m == n) + continue; + } else + n += n1; + + while (m < n) { + + m++; + + if (temp[m - 1] != '\n') + continue; + + /* ie, we hit EOL */ + + if (temp[0] == '#') + /* lines starting with # are comments */ + e = 0; + else + e = cb(cache, opaque, r | LCN_EOL, temp, + (size_t)m - 1); + r = LCN_SOL; + ignore = 0; + /* + * Move back remainder and prefill the gap that opened + * up: we want to pass enough in the start chunk so the + * cb can classify it even if it can't get all the + * value part in one go + */ + memmove(temp, temp + m, (size_t)(n - m)); + n -= m; + m = 0; + + if (e) { + ret = e; + goto bail; + } + } + + if (m) { + /* we ran out of buffer */ + if (ignore || (r == LCN_SOL && n && temp[0] == '#')) { + e = 0; + ignore = 1; + } else { + e = cb(cache, opaque, + r | (n == m && eof ? LCN_EOL : 0), + temp, (size_t)m); + + m = 0; + n = 0; + } + + if (e) { + /* + * We have to call off the whole thing if any + * step, eg, OOMs + */ + ret = e; + goto bail; + } + r = 0; + } + + } while (!eof || n != m); + + ret = 0; + +bail: + + return ret; +} + +/* + * lookup() just handles wildcard resolution, it doesn't deal with moving the + * hits to L1. That has to be done individually by non-wildcard names. + */ + +enum { + NSC_COL_HOST = 0, /* wc idx 0 */ + NSC_COL_PATH = 2, /* wc idx 1 */ + NSC_COL_EXPIRY = 4, + NSC_COL_NAME = 5, /* wc idx 2 */ + + NSC_COL_COUNT = 6 +}; + +/* + * This performs the specialized wildcard that knows about cookie path match + * rules. + * + * To defeat the lookup path matching, lie to it about idx being NSC_COL_PATH + */ + +static int +nsc_match(const char *wc, size_t wc_len, const char *col, size_t col_len, + int idx) +{ + size_t n = 0; + + if (idx != NSC_COL_PATH) + return lws_strcmp_wildcard(wc, wc_len, col, col_len); + + /* + * Cookie path match is special, if we lookup on a path like /my/path, + * we must match on cookie paths for every dir level including /, so + * match on /, /my, and /my/path. But we must not match on /m or + * /my/pa etc. If we lookup on /, we must not match /my/path + * + * Let's go through wc checking at / and for every complete subpath if + * it is an explicit match + */ + + if (!strcmp(col, wc)) + return 0; /* exact hit */ + + while (n <= wc_len) { + if (n == wc_len || wc[n] == '/') { + if (n && col_len <= n && !strncmp(wc, col, n)) + return 0; /* hit */ + + if (n != wc_len && col_len <= n + 1 && + !strncmp(wc, col, n + 1)) /* check for trailing / */ + return 0; /* hit */ + } + n++; + } + + return 1; /* fail */ +} + +static const uint8_t nsc_cols[] = { NSC_COL_HOST, NSC_COL_PATH, NSC_COL_NAME }; + +static int +lws_cache_nscookiejar_tag_match(struct lws_cache_ttl_lru *cache, + const char *wc, const char *tag, char lookup) +{ + const char *wc_end = wc + strlen(wc), *tag_end = tag + strlen(tag), + *start_wc, *start_tag; + int n = 0; + + lwsl_cache("%s: '%s' vs '%s'\n", __func__, wc, tag); + + /* + * Given a well-formed host|path|name tag and a wildcard term, + * make the determination if the tag matches the wildcard or not, + * using lookup rules that apply at this cache level. + */ + + while (n < 3) { + start_wc = wc; + while (wc < wc_end && *wc != LWSCTAG_SEP) + wc++; + + start_tag = tag; + while (tag < tag_end && *tag != LWSCTAG_SEP) + tag++; + + lwsl_cache("%s: '%.*s' vs '%.*s'\n", __func__, + lws_ptr_diff(wc, start_wc), start_wc, + lws_ptr_diff(tag, start_tag), start_tag); + if (nsc_match(start_wc, lws_ptr_diff_size_t(wc, start_wc), + start_tag, lws_ptr_diff_size_t(tag, start_tag), + lookup ? nsc_cols[n] : NSC_COL_HOST)) { + lwsl_cache("%s: fail\n", __func__); + return 1; + } + + if (wc < wc_end) + wc++; + if (tag < tag_end) + tag++; + + n++; + } + + lwsl_cache("%s: hit\n", __func__); + + return 0; /* match */ +} + +/* + * Converts the start of a cookie file line into a tag + */ + +static int +nsc_line_to_tag(const char *buf, size_t size, char *tag, size_t max_tag, + lws_usec_t *pexpiry) +{ + int n, idx = 0, tl = 0; + lws_usec_t expiry = 0; + size_t bn = 0; + char col[64]; + + if (size < 3) + return 1; + + while (bn < size && idx <= NSC_COL_NAME) { + + n = 0; + while (bn < size && n < (int)sizeof(col) - 1 && + buf[bn] != '\t') + col[n++] = buf[bn++]; + col[n] = '\0'; + if (buf[bn] == '\t') + bn++; + + switch (idx) { + case NSC_COL_EXPIRY: + expiry = (lws_usec_t)((unsigned long long)atoll(col) * + (lws_usec_t)LWS_US_PER_SEC); + break; + + case NSC_COL_HOST: + case NSC_COL_PATH: + case NSC_COL_NAME: + + /* + * As we match the pieces of the wildcard, + * compose the matches into a specific tag + */ + + if (tl + n + 2 > (int)max_tag) + return 1; + if (tl) + tag[tl++] = LWSCTAG_SEP; + memcpy(tag + tl, col, (size_t)n); + tl += n; + tag[tl] = '\0'; + break; + default: + break; + } + + idx++; + } + + if (pexpiry) + *pexpiry = expiry; + + lwsl_info("%s: %.*s: tag '%s'\n", __func__, (int)size, buf, tag); + + return 0; +} + +struct nsc_lookup_ctx { + const char *wildcard_key; + lws_dll2_owner_t *results_owner; + lws_cache_match_t *match; /* current match if any */ + size_t wklen; +}; + + +static int +nsc_lookup_cb(lws_cache_nscookiejar_t *cache, void *opaque, int flags, + const char *buf, size_t size) +{ + struct nsc_lookup_ctx *ctx = (struct nsc_lookup_ctx *)opaque; + lws_usec_t expiry; + char tag[200]; + int tl; + + if (!(flags & LCN_SOL)) { + if (ctx->match) + ctx->match->payload_size += size; + + return NIR_CONTINUE; + } + + /* + * There should be enough in buf to match or reject it... let's + * synthesize a tag from the text "line" and then check the tags for + * a match + */ + + ctx->match = NULL; /* new SOL means stop tracking payload len */ + + if (nsc_line_to_tag(buf, size, tag, sizeof(tag), &expiry)) + return NIR_CONTINUE; + + if (lws_cache_nscookiejar_tag_match(&cache->cache, + ctx->wildcard_key, tag, 1)) + return NIR_CONTINUE; + + tl = (int)strlen(tag); + + /* + * ... it looks like a match then... create new match + * object with the specific tag, and add it to the owner list + */ + + ctx->match = lws_fi(&cache->cache.info.cx->fic, "cache_lookup_oom") ? NULL : + lws_malloc(sizeof(*ctx->match) + (unsigned int)tl + 1u, + __func__); + if (!ctx->match) + /* caller of lookup will clean results list on fail */ + return NIR_FINISH_ERROR; + + ctx->match->payload_size = size; + ctx->match->tag_size = (size_t)tl; + ctx->match->expiry = expiry; + + memset(&ctx->match->list, 0, sizeof(ctx->match->list)); + memcpy(&ctx->match[1], tag, (size_t)tl + 1u); + lws_dll2_add_tail(&ctx->match->list, ctx->results_owner); + + return NIR_CONTINUE; +} + +static int +lws_cache_nscookiejar_lookup(struct lws_cache_ttl_lru *_c, + const char *wildcard_key, + lws_dll2_owner_t *results_owner) +{ + lws_cache_nscookiejar_t *cache = (lws_cache_nscookiejar_t *)_c; + struct nsc_lookup_ctx ctx; + int ret, fd; + + fd = nsc_backing_open_lock(cache, LWS_O_RDONLY, __func__); + if (fd == LWS_INVALID_FILE) + return 1; + + ctx.wildcard_key = wildcard_key; + ctx.results_owner = results_owner; + ctx.wklen = strlen(wildcard_key); + + ret = nscookiejar_iterate(cache, fd, nsc_lookup_cb, &ctx); + /* + * The cb can fail, eg, with OOM, making the whole lookup + * invalid and returning fail. Caller will clean + * results_owner on fail. + */ + nsc_backing_close_unlock(cache, fd); + + return ret == NIR_FINISH_ERROR; +} + +/* + * It's pretty horrible having to implement add or remove individual items by + * file regeneration, but if we don't want to keep it all in heap, and we want + * this cookie jar format, that is what we are into. + * + * Allow to optionally add a "line", optionally wildcard delete tags, and always + * delete expired entries. + * + * Although we can rely on the lws thread to be doing this, multiple processes + * may be using the cookie jar and can tread on each other. So we use flock() + * (linux only) to get exclusive access while we are processing this. + * + * We leave the existing file alone and generate a new one alongside it, with a + * fixed name.tmp format so it can't leak, if that went OK then we unlink the + * old and rename the new. + */ + +struct nsc_regen_ctx { + const char *wildcard_key_delete; + const void *add_data; + lws_usec_t curr; + size_t add_size; + int fdt; + char drop; +}; + +/* only used by nsc_regen() */ + +static int +nsc_regen_cb(lws_cache_nscookiejar_t *cache, void *opaque, int flags, + const char *buf, size_t size) +{ + struct nsc_regen_ctx *ctx = (struct nsc_regen_ctx *)opaque; + char tag[256]; + lws_usec_t expiry; + + if (flags & LCN_SOL) { + + ctx->drop = 0; + + if (nsc_line_to_tag(buf, size, tag, sizeof(tag), &expiry)) + /* filter it out if it is unparseable */ + goto drop; + + /* routinely track the earliest expiry */ + + if (!cache->earliest_expiry || + (expiry && cache->earliest_expiry > expiry)) + cache->earliest_expiry = expiry; + + if (expiry < ctx->curr) + /* routinely strip anything beyond its expiry */ + goto drop; + + if (ctx->wildcard_key_delete) + lwsl_cache("%s: %s vs %s\n", __func__, + tag, ctx->wildcard_key_delete); + if (ctx->wildcard_key_delete && + !lws_cache_nscookiejar_tag_match(&cache->cache, + ctx->wildcard_key_delete, + tag, 0)) { + lwsl_cache("%s: %s matches wc delete %s\n", __func__, + tag, ctx->wildcard_key_delete); + goto drop; + } + } + + if (ctx->drop) + return 0; + + cache->cache.current_footprint += (uint64_t)size; + + if ((size_t)write(ctx->fdt, buf, size) != size) + return NIR_FINISH_ERROR; + + if (flags & LCN_EOL) + if ((size_t)write(ctx->fdt, "\n", 1) != 1) + return NIR_FINISH_ERROR; + + return 0; + +drop: + ctx->drop = 1; + + return NIR_CONTINUE; +} + +static int +nsc_regen(lws_cache_nscookiejar_t *cache, const char *wc_delete, + const void *pay, size_t pay_size) +{ + struct nsc_regen_ctx ctx; + char filepath[128]; + int fd, ret = 1; + + fd = nsc_backing_open_lock(cache, LWS_O_RDONLY, __func__); + if (fd == LWS_INVALID_FILE) + return 1; + + lws_snprintf(filepath, sizeof(filepath), "%s.tmp", + cache->cache.info.u.nscookiejar.filepath); + unlink(filepath); + + if (lws_fi(&cache->cache.info.cx->fic, "cache_regen_temp_open")) + goto bail; + + ctx.fdt = open(filepath, LWS_O_CREAT | LWS_O_WRONLY, 0600); + if (ctx.fdt == LWS_INVALID_FILE) + goto bail; + + /* magic header */ + + if (lws_fi(&cache->cache.info.cx->fic, "cache_regen_temp_write") || + /* other consumers insist to see this at start of cookie jar */ + write(ctx.fdt, "# Netscape HTTP Cookie File\n", 28) != 28) + goto bail1; + + /* if we are adding something, put it first */ + + if (pay && (size_t)write(ctx.fdt, pay, pay_size) != pay_size) + goto bail1; + if (pay && (size_t)write(ctx.fdt, "\n", 1) != 1) + goto bail1; + + cache->cache.current_footprint = 0; + + ctx.wildcard_key_delete = wc_delete; + ctx.add_data = pay; + ctx.add_size = pay_size; + ctx.curr = lws_now_usecs(); + ctx.drop = 0; + + cache->earliest_expiry = 0; + + if (lws_fi(&cache->cache.info.cx->fic, "cache_regen_iter_fail") || + nscookiejar_iterate(cache, fd, nsc_regen_cb, &ctx)) + goto bail1; + + close(ctx.fdt); + + unlink(cache->cache.info.u.nscookiejar.filepath); + rename(filepath, cache->cache.info.u.nscookiejar.filepath); + + if (cache->earliest_expiry) + lws_cache_schedule(&cache->cache, expiry_cb, + cache->earliest_expiry); + + ret = 0; + goto bail1; + +bail1: + close(ctx.fdt); +bail: + unlink(filepath); + + nsc_backing_close_unlock(cache, fd); + + return ret; +} + +static void +expiry_cb(lws_sorted_usec_list_t *sul) +{ + lws_cache_nscookiejar_t *cache = lws_container_of(sul, + lws_cache_nscookiejar_t, cache.sul); + + /* + * regen the cookie jar without changes, so expired are removed and + * new earliest expired computed + */ + if (nsc_regen(cache, NULL, NULL, 0)) + return; + + if (cache->earliest_expiry) + lws_cache_schedule(&cache->cache, expiry_cb, + cache->earliest_expiry); +} + + +/* specific_key and expiry are ignored, since it must be encoded in payload */ + +static int +lws_cache_nscookiejar_write(struct lws_cache_ttl_lru *_c, + const char *specific_key, const uint8_t *source, + size_t size, lws_usec_t expiry, void **ppvoid) +{ + lws_cache_nscookiejar_t *cache = (lws_cache_nscookiejar_t *)_c; + char tag[128]; + + lwsl_cache("%s: %s: len %d\n", __func__, _c->info.name, (int)size); + + assert(source); + + if (nsc_line_to_tag((const char *)source, size, tag, sizeof(tag), NULL)) + return 1; + + if (ppvoid) + *ppvoid = NULL; + + if (nsc_regen(cache, tag, source, size)) { + lwsl_err("%s: regen failed\n", __func__); + + return 1; + } + + return 0; +} + +struct nsc_get_ctx { + struct lws_buflist *buflist; + const char *specific_key; + const void **pdata; + size_t *psize; + lws_cache_ttl_lru_t *l1; + lws_usec_t expiry; +}; + +/* + * We're looking for a specific key, if found, we want to make an entry for it + * in L1 and return information about that + */ + +static int +nsc_get_cb(lws_cache_nscookiejar_t *cache, void *opaque, int flags, + const char *buf, size_t size) +{ + struct nsc_get_ctx *ctx = (struct nsc_get_ctx *)opaque; + char tag[200]; + uint8_t *q; + + if (ctx->buflist) + goto collect; + + if (!(flags & LCN_SOL)) + return NIR_CONTINUE; + + if (nsc_line_to_tag(buf, size, tag, sizeof(tag), &ctx->expiry)) { + lwsl_err("%s: can't get tag\n", __func__); + return NIR_CONTINUE; + } + + lwsl_cache("%s: %s %s\n", __func__, ctx->specific_key, tag); + + if (strcmp(ctx->specific_key, tag)) { + lwsl_cache("%s: no match\n", __func__); + return NIR_CONTINUE; + } + + /* it's a match */ + + lwsl_cache("%s: IS match\n", __func__); + + if (!(flags & LCN_EOL)) + goto collect; + + /* it all fit in the buffer, let's create it in L1 now */ + + *ctx->psize = size; + if (ctx->l1->info.ops->write(ctx->l1, + ctx->specific_key, (const uint8_t *)buf, + size, ctx->expiry, (void **)ctx->pdata)) + return NIR_FINISH_ERROR; + + return NIR_FINISH_OK; + +collect: + /* + * it's bigger than one buffer-load, we have to stash what we're getting + * on a buflist and create it when we have it all + */ + + if (lws_buflist_append_segment(&ctx->buflist, (const uint8_t *)buf, + size)) + goto cleanup; + + if (!(flags & LCN_EOL)) + return NIR_CONTINUE; + + /* we have all the payload, create the L1 entry without payload yet */ + + *ctx->psize = size; + if (ctx->l1->info.ops->write(ctx->l1, ctx->specific_key, NULL, + lws_buflist_total_len(&ctx->buflist), + ctx->expiry, (void **)&q)) + goto cleanup; + *ctx->pdata = q; + + /* dump the buflist into the L1 cache entry */ + + do { + uint8_t *p; + size_t len = lws_buflist_next_segment_len(&ctx->buflist, &p); + + memcpy(q, p, len); + q += len; + + lws_buflist_use_segment(&ctx->buflist, len); + } while (ctx->buflist); + + return NIR_FINISH_OK; + +cleanup: + lws_buflist_destroy_all_segments(&ctx->buflist); + + return NIR_FINISH_ERROR; +} + +static int +lws_cache_nscookiejar_get(struct lws_cache_ttl_lru *_c, + const char *specific_key, const void **pdata, + size_t *psize) +{ + lws_cache_nscookiejar_t *cache = (lws_cache_nscookiejar_t *)_c; + struct nsc_get_ctx ctx; + int ret, fd; + + fd = nsc_backing_open_lock(cache, LWS_O_RDONLY, __func__); + if (fd == LWS_INVALID_FILE) + return 1; + + /* get a pointer to l1 */ + ctx.l1 = &cache->cache; + while (ctx.l1->child) + ctx.l1 = ctx.l1->child; + + ctx.pdata = pdata; + ctx.psize = psize; + ctx.specific_key = specific_key; + ctx.buflist = NULL; + ctx.expiry = 0; + + ret = nscookiejar_iterate(cache, fd, nsc_get_cb, &ctx); + + nsc_backing_close_unlock(cache, fd); + + return ret != NIR_FINISH_OK; +} + +static int +lws_cache_nscookiejar_invalidate(struct lws_cache_ttl_lru *_c, + const char *wc_key) +{ + lws_cache_nscookiejar_t *cache = (lws_cache_nscookiejar_t *)_c; + + return nsc_regen(cache, wc_key, NULL, 0); +} + +static struct lws_cache_ttl_lru * +lws_cache_nscookiejar_create(const struct lws_cache_creation_info *info) +{ + lws_cache_nscookiejar_t *cache; + + cache = lws_fi(&info->cx->fic, "cache_createfail") ? NULL : + lws_zalloc(sizeof(*cache), __func__); + if (!cache) + return NULL; + + cache->cache.info = *info; + + /* + * We need to scan the file, if it exists, and find the earliest + * expiry while cleaning out any expired entries + */ + expiry_cb(&cache->cache.sul); + + lwsl_notice("%s: create %s\n", __func__, info->name ? info->name : "?"); + + return (struct lws_cache_ttl_lru *)cache; +} + +static int +lws_cache_nscookiejar_expunge(struct lws_cache_ttl_lru *_c) +{ + lws_cache_nscookiejar_t *cache = (lws_cache_nscookiejar_t *)_c; + int r; + + if (!cache) + return 0; + + r = unlink(cache->cache.info.u.nscookiejar.filepath); + if (r) + lwsl_warn("%s: failed to unlink %s\n", __func__, + cache->cache.info.u.nscookiejar.filepath); + + return r; +} + +static void +lws_cache_nscookiejar_destroy(struct lws_cache_ttl_lru **_pc) +{ + lws_cache_nscookiejar_t *cache = (lws_cache_nscookiejar_t *)*_pc; + + if (!cache) + return; + + lws_sul_cancel(&cache->cache.sul); + + lws_free_set_NULL(*_pc); +} + +#if defined(_DEBUG) + +static int +nsc_dump_cb(lws_cache_nscookiejar_t *cache, void *opaque, int flags, + const char *buf, size_t size) +{ + lwsl_hexdump_cache(buf, size); + + return 0; +} + +static void +lws_cache_nscookiejar_debug_dump(struct lws_cache_ttl_lru *_c) +{ + lws_cache_nscookiejar_t *cache = (lws_cache_nscookiejar_t *)_c; + int fd = nsc_backing_open_lock(cache, LWS_O_RDONLY, __func__); + + if (fd == LWS_INVALID_FILE) + return; + + lwsl_cache("%s: %s\n", __func__, _c->info.name); + + nscookiejar_iterate(cache, fd, nsc_dump_cb, NULL); + + nsc_backing_close_unlock(cache, fd); +} +#endif + +const struct lws_cache_ops lws_cache_ops_nscookiejar = { + .create = lws_cache_nscookiejar_create, + .destroy = lws_cache_nscookiejar_destroy, + .expunge = lws_cache_nscookiejar_expunge, + + .write = lws_cache_nscookiejar_write, + .tag_match = lws_cache_nscookiejar_tag_match, + .lookup = lws_cache_nscookiejar_lookup, + .invalidate = lws_cache_nscookiejar_invalidate, + .get = lws_cache_nscookiejar_get, +#if defined(_DEBUG) + .debug_dump = lws_cache_nscookiejar_debug_dump, +#endif +}; diff --git a/lib/misc/cache-ttl/heap.c b/lib/misc/cache-ttl/heap.c new file mode 100644 index 000000000..b3a32394c --- /dev/null +++ b/lib/misc/cache-ttl/heap.c @@ -0,0 +1,612 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010 - 2021 Andy Green + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include +#include "private-lib-misc-cache-ttl.h" + +#if defined(write) +#undef write +#endif + +static void +update_sul(lws_cache_ttl_lru_t_heap_t *cache); + +static int +lws_cache_heap_invalidate(struct lws_cache_ttl_lru *_c, const char *key); + +static int +sort_expiry(const lws_dll2_t *a, const lws_dll2_t *b) +{ + const lws_cache_ttl_item_heap_t + *c = lws_container_of(a, lws_cache_ttl_item_heap_t, list_expiry), + *d = lws_container_of(b, lws_cache_ttl_item_heap_t, list_expiry); + + if (c->expiry > d->expiry) + return 1; + if (c->expiry < d->expiry) + return -1; + + return 0; +} + +static void +_lws_cache_heap_item_destroy(lws_cache_ttl_lru_t_heap_t *cache, + lws_cache_ttl_item_heap_t *item) +{ + lwsl_cache("%s: %s (%s)\n", __func__, cache->cache.info.name, + (const char *)&item[1] + item->size); + + lws_dll2_remove(&item->list_expiry); + lws_dll2_remove(&item->list_lru); + + cache->cache.current_footprint -= item->size; + + update_sul(cache); + + if (cache->cache.info.cb) + cache->cache.info.cb((void *)((uint8_t *)&item[1]), item->size); + + lws_free(item); +} + +static void +lws_cache_heap_item_destroy(lws_cache_ttl_lru_t_heap_t *cache, + lws_cache_ttl_item_heap_t *item, int parent_too) +{ + struct lws_cache_ttl_lru *backing = &cache->cache; + const char *tag = ((const char *)&item[1]) + item->size; + + /* + * We're destroying a normal item? + */ + + if (*tag == META_ITEM_LEADING) + /* no, nothing to check here then */ + goto post; + + if (backing->info.parent) + backing = backing->info.parent; + + /* + * We need to check any cached meta-results from lookups that + * include this normal item, and if any, invalidate the meta-results + * since they have to be recalculated before being used again. + */ + + lws_start_foreach_dll_safe(struct lws_dll2 *, d, d1, + cache->items_lru.head) { + lws_cache_ttl_item_heap_t *i = lws_container_of(d, + lws_cache_ttl_item_heap_t, + list_lru); + const char *iname = ((const char *)&item[1]) + item->size; + uint8_t *pay = (uint8_t *)&item[1], *end = pay + item->size; + + if (*iname == META_ITEM_LEADING) { + size_t taglen = strlen(iname); + + /* + * If the item about to be destroyed makes an + * appearance on the meta results list, we must kill + * the meta result item to force recalc next time + */ + + while (pay < end) { + uint32_t tlen = lws_ser_ru32be(pay + 4); + + if (tlen == taglen && + !strcmp((const char *)pay + 8, iname)) { +#if defined(_DEBUG) + /* + * Sanity check that the item tag is + * really a match for that meta results + * item + */ + + assert (!backing->info.ops->tag_match( + backing, iname + 1, tag, 1)); +#endif + _lws_cache_heap_item_destroy(cache, i); + break; + } + pay += 8 + tlen + 1; + } + +#if defined(_DEBUG) + /* + * Sanity check that the item tag really isn't a match + * for that meta results item + */ + + assert (backing->info.ops->tag_match(backing, iname + 1, + tag, 1)); +#endif + } + + } lws_end_foreach_dll_safe(d, d1); + +post: + _lws_cache_heap_item_destroy(cache, item); +} + +static void +lws_cache_item_evict_lru(lws_cache_ttl_lru_t_heap_t *cache) +{ + lws_cache_ttl_item_heap_t *ei; + + if (!cache->items_lru.head) + return; + + ei = lws_container_of(cache->items_lru.head, + lws_cache_ttl_item_heap_t, list_lru); + + lws_cache_heap_item_destroy(cache, ei, 0); +} + +/* + * We need to weed out expired entries in the backing file + */ + +static void +expiry_cb(lws_sorted_usec_list_t *sul) +{ + lws_cache_ttl_lru_t_heap_t *cache = lws_container_of(sul, + lws_cache_ttl_lru_t_heap_t, cache.sul); + lws_usec_t now = lws_now_usecs(); + + lwsl_cache("%s: %s\n", __func__, cache->cache.info.name); + + while (cache->items_expiry.head) { + lws_cache_ttl_item_heap_t *item; + + item = lws_container_of(cache->items_expiry.head, + lws_cache_ttl_item_heap_t, list_expiry); + + if (item->expiry > now) + return; + + lws_cache_heap_item_destroy(cache, item, 1); + } +} + +/* + * Let's figure out what the earliest next expiry is + */ + +static int +earliest_expiry(lws_cache_ttl_lru_t_heap_t *cache, lws_usec_t *pearliest) +{ + lws_cache_ttl_item_heap_t *item; + + if (!cache->items_expiry.head) + return 1; + + item = lws_container_of(cache->items_expiry.head, + lws_cache_ttl_item_heap_t, list_expiry); + + *pearliest = item->expiry; + + return 0; +} + +static void +update_sul(lws_cache_ttl_lru_t_heap_t *cache) +{ + lws_usec_t earliest; + + /* weed out any newly-expired */ + expiry_cb(&cache->cache.sul); + + /* figure out the next soonest expiring item */ + if (earliest_expiry(cache, &earliest)) { + lws_sul_cancel(&cache->cache.sul); + return; + } + + lwsl_debug("%s: setting exp %llu\n", __func__, + (unsigned long long)earliest); + + if (earliest) + lws_cache_schedule(&cache->cache, expiry_cb, earliest); +} + +static lws_cache_ttl_item_heap_t * +lws_cache_heap_specific(lws_cache_ttl_lru_t_heap_t *cache, + const char *specific_key) +{ + lws_start_foreach_dll(struct lws_dll2 *, d, cache->items_lru.head) { + lws_cache_ttl_item_heap_t *item = lws_container_of(d, + lws_cache_ttl_item_heap_t, + list_lru); + const char *iname = ((const char *)&item[1]) + item->size; + + if (!strcmp(specific_key, iname)) + return item; + + } lws_end_foreach_dll(d); + + return NULL; +} + +static int +lws_cache_heap_tag_match(struct lws_cache_ttl_lru *cache, const char *wc, + const char *tag, char lookup_rules) +{ + return lws_strcmp_wildcard(wc, strlen(wc), tag, strlen(tag)); +} + +static int +lws_cache_heap_lookup(struct lws_cache_ttl_lru *_c, const char *wildcard_key, + lws_dll2_owner_t *results_owner) +{ + lws_cache_ttl_lru_t_heap_t *cache = (lws_cache_ttl_lru_t_heap_t *)_c; + size_t sklen = strlen(wildcard_key); + + lws_start_foreach_dll(struct lws_dll2 *, d, cache->items_lru.head) { + lws_cache_ttl_item_heap_t *item = lws_container_of(d, + lws_cache_ttl_item_heap_t, + list_lru); + const char *iname = ((const char *)&item[1]) + item->size; + + if (!lws_strcmp_wildcard(wildcard_key, sklen, iname, + strlen(iname))) { + size_t ilen = strlen(iname); + lws_cache_match_t *m; + char hit = 0; + + /* + * It musn't already be on the list from an earlier + * cache level + */ + + lws_start_foreach_dll(struct lws_dll2 *, e, + results_owner->head) { + lws_cache_match_t *i = lws_container_of(e, + lws_cache_match_t, list); + if (i->tag_size == ilen && + !strcmp(iname, ((const char *)&i[1]))) { + hit = 1; + break; + } + } lws_end_foreach_dll(e); + + if (!hit) { + + /* + * it's unique, instantiate a record for it + */ + + m = lws_fi(&_c->info.cx->fic, + "cache_lookup_oom") ? NULL : + lws_malloc(sizeof(*m) + ilen + 1, + __func__); + if (!m) { + lws_cache_clear_matches(results_owner); + return 1; + } + + memset(&m->list, 0, sizeof(m->list)); + m->tag_size = ilen; + memcpy(&m[1], iname, ilen + 1); + + lws_dll2_add_tail(&m->list, results_owner); + } + } + + } lws_end_foreach_dll(d); + + return 0; +} + +static int +lws_cache_heap_write(struct lws_cache_ttl_lru *_c, const char *specific_key, + const uint8_t *source, size_t size, lws_usec_t expiry, + void **ppvoid) +{ + lws_cache_ttl_lru_t_heap_t *cache = (lws_cache_ttl_lru_t_heap_t *)_c; + struct lws_cache_ttl_lru *backing = _c; + lws_cache_ttl_item_heap_t *item, *ei; + size_t kl = strlen(specific_key); + char *p; + + lwsl_cache("%s: %s: len %d\n", __func__, _c->info.name, (int)size); + + /* + * Is this new tag going to invalidate any existing cached meta-results? + * + * If so, let's destroy any of those first to recover the heap + */ + + if (backing->info.parent) + backing = backing->info.parent; + + lws_start_foreach_dll_safe(struct lws_dll2 *, d, d1, + cache->items_lru.head) { + lws_cache_ttl_item_heap_t *i = lws_container_of(d, + lws_cache_ttl_item_heap_t, + list_lru); + const char *iname = ((const char *)&i[1]) + i->size; + + if (*iname == META_ITEM_LEADING) { + + /* + * If the item about to be added would match any cached + * results from before it was added, we have to + * invalidate them. To check this, we have to use the + * matching rules at the backing store level + */ + + if (!backing->info.ops->tag_match(backing, iname + 1, + specific_key, 1)) + _lws_cache_heap_item_destroy(cache, i); + } + + } lws_end_foreach_dll_safe(d, d1); + + + /* + * Keep us under the limit if possible... note this will always allow + * caching a single large item even if it is above the limits + */ + + while ((cache->cache.info.max_footprint && + cache->cache.current_footprint + size > + cache->cache.info.max_footprint) || + (cache->cache.info.max_items && + cache->items_lru.count + 1 > cache->cache.info.max_items)) + lws_cache_item_evict_lru(cache); + + /* remove any existing entry of the same key */ + + lws_cache_heap_invalidate(&cache->cache, specific_key); + + item = lws_fi(&_c->info.cx->fic, "cache_write_oom") ? NULL : + lws_malloc(sizeof(*item) + kl + 1u + size, __func__); + if (!item) + return 1; + + cache->cache.current_footprint += item->size; + + /* only need to zero down our item object */ + memset(item, 0, sizeof(*item)); + + p = (char *)&item[1]; + if (ppvoid) + *ppvoid = p; + + /* copy the payload into place */ + if (source) + memcpy(p, source, size); + + /* copy the key string into place, with terminating NUL */ + memcpy(p + size, specific_key, kl + 1); + + item->expiry = expiry; + item->key_len = kl; + item->size = size; + + if (expiry) { + /* adding to expiry is optional, on nonzero expiry */ + lws_dll2_add_sorted(&item->list_expiry, &cache->items_expiry, + sort_expiry); + ei = lws_container_of(cache->items_expiry.head, + lws_cache_ttl_item_heap_t, list_expiry); + lwsl_debug("%s: setting exp %llu\n", __func__, + (unsigned long long)ei->expiry); + lws_cache_schedule(&cache->cache, expiry_cb, ei->expiry); + } + + /* always add outselves to head of lru list */ + lws_dll2_add_head(&item->list_lru, &cache->items_lru); + + return 0; +} + +static int +lws_cache_heap_get(struct lws_cache_ttl_lru *_c, const char *specific_key, + const void **pdata, size_t *psize) +{ + lws_cache_ttl_lru_t_heap_t *cache = (lws_cache_ttl_lru_t_heap_t *)_c; + lws_cache_ttl_item_heap_t *item; + + item = lws_cache_heap_specific(cache, specific_key); + if (!item) + return 1; + + /* we are using it, move it to lru head */ + lws_dll2_remove(&item->list_lru); + lws_dll2_add_head(&item->list_lru, &cache->items_lru); + + if (pdata) { + *pdata = (const void *)&item[1]; + *psize = item->size; + } + + return 0; +} + +static int +lws_cache_heap_invalidate(struct lws_cache_ttl_lru *_c, const char *specific_key) +{ + lws_cache_ttl_lru_t_heap_t *cache = (lws_cache_ttl_lru_t_heap_t *)_c; + struct lws_cache_ttl_lru *backing = _c; + lws_cache_ttl_item_heap_t *item; + const void *user; + size_t size; + + if (lws_cache_heap_get(_c, specific_key, &user, &size)) + return 0; + + if (backing->info.parent) + backing = backing->info.parent; + + item = (lws_cache_ttl_item_heap_t *)(((uint8_t *)user) - sizeof(*item)); + + /* + * We must invalidate any cached results that would have included this + */ + + lws_start_foreach_dll_safe(struct lws_dll2 *, d, d1, + cache->items_lru.head) { + lws_cache_ttl_item_heap_t *i = lws_container_of(d, + lws_cache_ttl_item_heap_t, + list_lru); + const char *iname = ((const char *)&i[1]) + i->size; + + if (*iname == META_ITEM_LEADING) { + + /* + * If the item about to be added would match any cached + * results from before it was added, we have to + * invalidate them. To check this, we have to use the + * matching rules at the backing store level + */ + + if (!backing->info.ops->tag_match(backing, iname + 1, + specific_key, 1)) + _lws_cache_heap_item_destroy(cache, i); + } + + } lws_end_foreach_dll_safe(d, d1); + + lws_cache_heap_item_destroy(cache, item, 0); + + return 0; +} + +static struct lws_cache_ttl_lru * +lws_cache_heap_create(const struct lws_cache_creation_info *info) +{ + lws_cache_ttl_lru_t_heap_t *cache; + + assert(info->cx); + assert(info->name); + + cache = lws_fi(&info->cx->fic, "cache_createfail") ? NULL : + lws_zalloc(sizeof(*cache), __func__); + if (!cache) + return NULL; + + cache->cache.info = *info; + if (info->parent) + info->parent->child = &cache->cache; + + lwsl_cache("%s: create %s\n", __func__, info->name); + + return (struct lws_cache_ttl_lru *)cache; +} + +static int +destroy_dll(struct lws_dll2 *d, void *user) +{ + lws_cache_ttl_lru_t *_c = (struct lws_cache_ttl_lru *)user; + lws_cache_ttl_lru_t_heap_t *cache = (lws_cache_ttl_lru_t_heap_t *)_c; + lws_cache_ttl_item_heap_t *item = + lws_container_of(d, lws_cache_ttl_item_heap_t, list_lru); + + lws_cache_heap_item_destroy(cache, item, 0); + + return 0; +} + +static int +lws_cache_heap_expunge(struct lws_cache_ttl_lru *_c) +{ + lws_cache_ttl_lru_t_heap_t *cache = (lws_cache_ttl_lru_t_heap_t *)_c; + + lws_dll2_foreach_safe(&cache->items_lru, cache, destroy_dll); + + return 0; +} + +static void +lws_cache_heap_destroy(struct lws_cache_ttl_lru **_cache) +{ + lws_cache_ttl_lru_t *c = *_cache; + lws_cache_ttl_lru_t_heap_t *cache = (lws_cache_ttl_lru_t_heap_t *)c; + + if (!cache) + return; + + lws_sul_cancel(&c->sul); + + lws_dll2_foreach_safe(&cache->items_lru, cache, destroy_dll); + + lwsl_cache("%s: destroy %s\n", __func__, c->info.name ? + c->info.name : "?"); + + lws_free_set_NULL(*_cache); +} + +#if defined(_DEBUG) +static int +dump_dll(struct lws_dll2 *d, void *user) +{ + lws_cache_ttl_item_heap_t *item = + lws_container_of(d, lws_cache_ttl_item_heap_t, list_lru); + + lwsl_cache(" %s: size %d, exp %llu\n", + (const char *)&item[1] + item->size, + (int)item->size, (unsigned long long)item->expiry); + + lwsl_hexdump_cache((const char *)&item[1], item->size); + + return 0; +} + +static void +lws_cache_heap_debug_dump(struct lws_cache_ttl_lru *_c) +{ + lws_cache_ttl_lru_t_heap_t *cache = (lws_cache_ttl_lru_t_heap_t *)_c; +#if !defined(LWS_WITH_NO_LOGS) + lws_cache_ttl_item_heap_t *item = NULL; + + lws_dll2_t *d = cache->items_expiry.head; + + if (d) + item = lws_container_of(d, lws_cache_ttl_item_heap_t, + list_expiry); + + lwsl_cache("%s: %s: items %d, earliest %llu\n", __func__, + cache->cache.info.name, (int)cache->items_lru.count, + item ? (unsigned long long)item->expiry : 0ull); +#endif + + lws_dll2_foreach_safe(&cache->items_lru, cache, dump_dll); +} +#endif + +const struct lws_cache_ops lws_cache_ops_heap = { + .create = lws_cache_heap_create, + .destroy = lws_cache_heap_destroy, + .expunge = lws_cache_heap_expunge, + + .write = lws_cache_heap_write, + .tag_match = lws_cache_heap_tag_match, + .lookup = lws_cache_heap_lookup, + .invalidate = lws_cache_heap_invalidate, + .get = lws_cache_heap_get, +#if defined(_DEBUG) + .debug_dump = lws_cache_heap_debug_dump, +#endif +}; diff --git a/lib/misc/cache-ttl/lws-cache-ttl.c b/lib/misc/cache-ttl/lws-cache-ttl.c new file mode 100644 index 000000000..0bc80ab5f --- /dev/null +++ b/lib/misc/cache-ttl/lws-cache-ttl.c @@ -0,0 +1,300 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010 - 2021 Andy Green + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include +#include "private-lib-misc-cache-ttl.h" + +#include + +#if defined(write) +#undef write +#endif + +void +lws_cache_clear_matches(lws_dll2_owner_t *results_owner) +{ + lws_start_foreach_dll_safe(struct lws_dll2 *, d, d1, results_owner->head) { + lws_cache_match_t *item = lws_container_of(d, lws_cache_match_t, + list); + lws_dll2_remove(d); + lws_free(item); + } lws_end_foreach_dll_safe(d, d1); +} + +void +lws_cache_schedule(struct lws_cache_ttl_lru *cache, sul_cb_t cb, lws_usec_t e) +{ + lwsl_cache("%s: %s schedule %llu\n", __func__, cache->info.name, + (unsigned long long)e); + + lws_sul_schedule(cache->info.cx, cache->info.tsi, &cache->sul, cb, + e - lws_now_usecs()); +} + +int +lws_cache_write_through(struct lws_cache_ttl_lru *cache, + const char *specific_key, const uint8_t *source, + size_t size, lws_usec_t expiry, void **ppay) +{ + struct lws_cache_ttl_lru *levels[LWS_CACHE_MAX_LEVELS], *c = cache; + int n = 0, r = 0; + + lws_cache_item_remove(cache, specific_key); + + /* starting from L1 */ + + do { + levels[n++] = c; + c = c->info.parent; + } while (c && n < (int)LWS_ARRAY_SIZE(levels)); + + /* starting from outermost cache level */ + + while (n) { + n--; + r = levels[n]->info.ops->write(levels[n], specific_key, + source, size, expiry, ppay); + } + + return r; +} + +/* + * We want to make a list of unique keys that exist at any cache level + * matching a wildcard search key. + * + * If L1 has a cached version though, we will just use that. + */ + +int +lws_cache_lookup(struct lws_cache_ttl_lru *cache, const char *wildcard_key, + const void **pdata, size_t *psize) +{ + struct lws_cache_ttl_lru *l1 = cache; + lws_dll2_owner_t results_owner; + lws_usec_t expiry = 0; + char meta_key[128]; + uint8_t *p, *temp; + size_t sum = 0; + int n; + + memset(&results_owner, 0, sizeof(results_owner)); + meta_key[0] = META_ITEM_LEADING; + lws_strncpy(&meta_key[1], wildcard_key, sizeof(meta_key) - 2); + + /* + * If we have a cached result set in L1 already, return that + */ + + if (!l1->info.ops->get(l1, meta_key, pdata, psize)) + return 0; + + /* + * No, we have to do the actual lookup work in the backing store layer + * to get results for this... + */ + + while (cache->info.parent) + cache = cache->info.parent; + + if (cache->info.ops->lookup(cache, wildcard_key, &results_owner)) { + /* eg, OOM */ + + lwsl_cache("%s: bs lookup fail\n", __func__); + + lws_cache_clear_matches(&results_owner); + return 1; + } + + /* + * Scan the results, we want to know how big a payload it needs in + * the cache, and we want to know the earliest expiry of any of the + * component parts, so the meta cache entry for these results can be + * expired when any of the results would expire. + */ + + lws_start_foreach_dll(struct lws_dll2 *, d, results_owner.head) { + lws_cache_match_t *m = lws_container_of(d, lws_cache_match_t, + list); + sum += 8; /* payload size, name length */ + sum += m->tag_size + 1; + + if (m->expiry && (!expiry || expiry < m->expiry)) + expiry = m->expiry; + + } lws_end_foreach_dll(d); + + lwsl_cache("%s: results %d, size %d\n", __func__, + (int)results_owner.count, (int)sum); + + temp = lws_malloc(sum, __func__); + if (!temp) { + lws_cache_clear_matches(&results_owner); + return 1; + } + + /* + * Fill temp with the serialized results + */ + + p = temp; + lws_start_foreach_dll(struct lws_dll2 *, d, results_owner.head) { + lws_cache_match_t *m = lws_container_of(d, lws_cache_match_t, + list); + + /* we don't copy the payload in, but take note of its size */ + lws_ser_wu32be(p, (uint32_t)m->payload_size); + p += 4; + /* length of the tag name (there is an uncounted NUL after) */ + lws_ser_wu32be(p, (uint32_t)m->tag_size); + p += 4; + + /* then the tag name, plus the extra NUL */ + memcpy(p, &m[1], m->tag_size + 1); + p += m->tag_size + 1; + + } lws_end_foreach_dll(d); + + lws_cache_clear_matches(&results_owner); + + /* + * Create the right amount of space for an L1 record of these results, + * with its expiry set to the earliest of the results, and copy it in + * from temp + */ + + n = l1->info.ops->write(l1, meta_key, temp, sum, expiry, (void **)&p); + /* done with temp */ + lws_free(temp); + + if (n) + return 1; + + /* point to the results in L1 */ + + *pdata = p; + *psize = sum; + + return 0; +} + +int +lws_cache_item_get(struct lws_cache_ttl_lru *cache, const char *specific_key, + const void **pdata, size_t *psize) +{ + while (cache) { + if (!cache->info.ops->get(cache, specific_key, pdata, psize)) { + lwsl_cache("%s: hit\n", __func__); + return 0; + } + + cache = cache->info.parent; + } + + return 1; +} + +int +lws_cache_expunge(struct lws_cache_ttl_lru *cache) +{ + int ret = 0; + + while (cache) { + ret |= cache->info.ops->expunge(cache); + + cache = cache->info.parent; + } + + return ret; +} + +int +lws_cache_item_remove(struct lws_cache_ttl_lru *cache, const char *wildcard_key) +{ + while (cache) { + if (cache->info.ops->invalidate(cache, wildcard_key)) + return 1; + + cache = cache->info.parent; + } + + return 0; +} + +uint64_t +lws_cache_footprint(struct lws_cache_ttl_lru *cache) +{ + return cache->current_footprint; +} + +void +lws_cache_debug_dump(struct lws_cache_ttl_lru *cache) +{ +#if defined(_DEBUG) + if (cache->info.ops->debug_dump) + cache->info.ops->debug_dump(cache); +#endif +} + +int +lws_cache_results_walk(lws_cache_results_t *walk_ctx) +{ + if (!walk_ctx->size) + return 1; + + walk_ctx->payload_len = lws_ser_ru32be(walk_ctx->ptr); + walk_ctx->tag_len = lws_ser_ru32be(walk_ctx->ptr + 4); + walk_ctx->tag = walk_ctx->ptr + 8; + + walk_ctx->ptr += walk_ctx->tag_len + 1 + 8; + walk_ctx->size -= walk_ctx->tag_len + 1 + 8; + + return 0; +} + +struct lws_cache_ttl_lru * +lws_cache_create(const struct lws_cache_creation_info *info) +{ + assert(info); + assert(info->ops); + assert(info->name); + assert(info->ops->create); + + return info->ops->create(info); +} + +void +lws_cache_destroy(struct lws_cache_ttl_lru **_cache) +{ + lws_cache_ttl_lru_t *cache = *_cache; + + if (!cache) + return; + + assert(cache->info.ops->destroy); + + lws_sul_cancel(&cache->sul); + + cache->info.ops->destroy(_cache); +} diff --git a/lib/misc/cache-ttl/private-lib-misc-cache-ttl.h b/lib/misc/cache-ttl/private-lib-misc-cache-ttl.h new file mode 100644 index 000000000..9ff356de7 --- /dev/null +++ b/lib/misc/cache-ttl/private-lib-misc-cache-ttl.h @@ -0,0 +1,98 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010 - 2021 Andy Green + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#define lwsl_cache lwsl_debug +#define lwsl_hexdump_cache lwsl_hexdump_debug + +#define LWS_CACHE_MAX_LEVELS 3 + +/* + * If we need structure inside the cache tag names, use this character as a + * separator + */ +#define LWSCTAG_SEP '|' + +/* + * Our synthetic cache result items all have tags starting with this char + */ +#define META_ITEM_LEADING '!' + +typedef struct lws_cache_ttl_item_heap { + lws_dll2_t list_expiry; + lws_dll2_t list_lru; + + lws_usec_t expiry; + size_t key_len; + size_t size; + + /* + * len + key_len + 1 bytes of data overcommitted, user object first + * so it is well-aligned, then the NUL-terminated key name + */ +} lws_cache_ttl_item_heap_t; + +/* this is a "base class", all cache implementations have one at the start */ + +typedef struct lws_cache_ttl_lru { + struct lws_cache_creation_info info; + lws_sorted_usec_list_t sul; + struct lws_cache_ttl_lru *child; + uint64_t current_footprint; +} lws_cache_ttl_lru_t; + +/* + * The heap-backed cache uses lws_dll2 linked-lists to track items that are + * in it. + */ + +typedef struct lws_cache_ttl_lru_heap { + lws_cache_ttl_lru_t cache; + + lws_dll2_owner_t items_expiry; + lws_dll2_owner_t items_lru; +} lws_cache_ttl_lru_t_heap_t; + +/* + * We want to be able to work with a large file-backed implementation even on + * devices that don't have heap to track what is in it. It means if lookups + * reach this cache layer, we will be scanning a potentially large file. + * + * L1 caching of lookups (including null result list) reduces the expense of + * this on average. We keep a copy of the last computed earliest expiry. + * + * We can't keep an open file handle here. Because other processes may change + * the cookie file by deleting and replacing it, we have to open it fresh each + * time. + */ +typedef struct lws_cache_nscookiejar { + lws_cache_ttl_lru_t cache; + + lws_usec_t earliest_expiry; +} lws_cache_nscookiejar_t; + +void +lws_cache_clear_matches(lws_dll2_owner_t *results_owner); + +void +lws_cache_schedule(struct lws_cache_ttl_lru *cache, sul_cb_t cb, lws_usec_t e); diff --git a/minimal-examples/api-tests/api-test-lws_cache/CMakeLists.txt b/minimal-examples/api-tests/api-test-lws_cache/CMakeLists.txt new file mode 100644 index 000000000..72ae86a56 --- /dev/null +++ b/minimal-examples/api-tests/api-test-lws_cache/CMakeLists.txt @@ -0,0 +1,19 @@ +project(lws-api-test-lws_cache C) +cmake_minimum_required(VERSION 2.8.12) +find_package(libwebsockets CONFIG REQUIRED) +list(APPEND CMAKE_MODULE_PATH ${LWS_CMAKE_DIR}) +include(CheckCSourceCompiles) +include(LwsCheckRequirements) + +set(SAMP lws-api-test-lws_cache) +set(SRCS main.c) + +add_executable(${SAMP} ${SRCS}) +add_test(NAME api-test-lws_cache COMMAND lws-api-test-lws_cache) + +if (websockets_shared) + target_link_libraries(${SAMP} websockets_shared ${LIBWEBSOCKETS_DEP_LIBS}) + add_dependencies(${SAMP} websockets_shared) +else() + target_link_libraries(${SAMP} websockets ${LIBWEBSOCKETS_DEP_LIBS}) +endif() diff --git a/minimal-examples/api-tests/api-test-lws_cache/README.md b/minimal-examples/api-tests/api-test-lws_cache/README.md new file mode 100644 index 000000000..74034c793 --- /dev/null +++ b/minimal-examples/api-tests/api-test-lws_cache/README.md @@ -0,0 +1,22 @@ +# lws api test lwsac + +Demonstrates how to use and performs selftests for lwsac + +## build + +``` + $ cmake . && make +``` + +## usage + +Commandline option|Meaning +---|--- +-d |Debug verbosity in decimal, eg, -d15 + +``` + $ ./lws-api-test-lwsac +[2018/10/09 09:14:17:4834] USER: LWS API selftest: lwsac +[2018/10/09 09:14:17:4835] USER: Completed: PASS +``` + diff --git a/minimal-examples/api-tests/api-test-lws_cache/main.c b/minimal-examples/api-tests/api-test-lws_cache/main.c new file mode 100644 index 000000000..c5c2099f6 --- /dev/null +++ b/minimal-examples/api-tests/api-test-lws_cache/main.c @@ -0,0 +1,512 @@ +/* + * lws-api-test-lws_cache + * + * Written in 2010-2021 by Andy Green + * + * This file is made available under the Creative Commons CC0 1.0 + * Universal Public Domain Dedication. + */ + +#include + +static struct lws_context *cx; +static int tests, fail; + +static int +test_just_l1(void) +{ + struct lws_cache_creation_info ci; + struct lws_cache_ttl_lru *l1; + int ret = 1; + size_t size; + char *po; + + lwsl_user("%s\n", __func__); + + tests++; + + /* just create a heap cache "L1" */ + + memset(&ci, 0, sizeof(ci)); + ci.cx = cx; + ci.ops = &lws_cache_ops_heap; + ci.name = "L1"; + + l1 = lws_cache_create(&ci); + if (!l1) + goto cdone; + + /* add two items, a has 1s expiry and b has 2s */ + + if (lws_cache_write_through(l1, "a", (const uint8_t *)"is_a", 5, + lws_now_usecs() + LWS_US_PER_SEC, NULL)) + goto cdone; + + if (lws_cache_write_through(l1, "b", (const uint8_t *)"is_b", 5, + lws_now_usecs() + LWS_US_PER_SEC * 2, NULL)) + goto cdone; + + /* check they exist as intended */ + + if (lws_cache_item_get(l1, "a", (const void **)&po, &size) || + size != 5 || strcmp(po, "is_a")) + goto cdone; + + if (lws_cache_item_get(l1, "b", (const void **)&po, &size) || + size != 5 || strcmp(po, "is_b")) + goto cdone; + + /* wait for 1.2s to pass, working the event loop by hand */ + + lws_cancel_service(cx); + if (lws_service(cx, 0) < 0) + goto cdone; +#if defined(WIN32) + Sleep(1200); +#else + /* netbsd cares about < 1M */ + usleep(999999); + usleep(200001); +#endif + lws_cancel_service(cx); + if (lws_service(cx, 0) < 0) + goto cdone; + + lws_cancel_service(cx); + if (lws_service(cx, 0) < 0) + goto cdone; + + /* a only had 1s lifetime, he should be gone */ + + if (!lws_cache_item_get(l1, "a", (const void **)&po, &size)) { + lwsl_err("%s: cache: a still exists after expiry\n", __func__); + fail++; + goto cdone; + } + + /* that's ok then */ + + ret = 0; + +cdone: + lws_cache_destroy(&l1); + + if (ret) + lwsl_warn("%s: fail\n", __func__); + + return ret; +} + +static int +test_just_l1_limits(void) +{ + struct lws_cache_creation_info ci; + struct lws_cache_ttl_lru *l1; + int ret = 1; + size_t size; + char *po; + + lwsl_user("%s\n", __func__); + tests++; + + /* just create a heap cache "L1" */ + + memset(&ci, 0, sizeof(ci)); + ci.cx = cx; + ci.ops = &lws_cache_ops_heap; + ci.name = "L1_lim"; + ci.max_items = 1; /* ie, adding a second item destroys the first */ + + l1 = lws_cache_create(&ci); + if (!l1) + goto cdone; + + /* add two items, a has 1s expiry and b has 2s */ + + if (lws_cache_write_through(l1, "a", (const uint8_t *)"is_a", 5, + lws_now_usecs() + LWS_US_PER_SEC, NULL)) + goto cdone; + + if (lws_cache_write_through(l1, "b", (const uint8_t *)"is_b", 5, + lws_now_usecs() + LWS_US_PER_SEC * 2, NULL)) + goto cdone; + + /* only b should exit, since we limit to cache to just one entry */ + + if (!lws_cache_item_get(l1, "a", (const void **)&po, &size)) + goto cdone; + + if (lws_cache_item_get(l1, "b", (const void **)&po, &size) || + size != 5 || strcmp(po, "is_b")) + goto cdone; + + /* that's ok then */ + + ret = 0; + +cdone: + lws_cache_destroy(&l1); + + if (ret) + lwsl_warn("%s: fail\n", __func__); + + return ret; +} + +#if defined(LWS_WITH_CACHE_NSCOOKIEJAR) + +static const char + *cookie1 = "host.com\tFALSE\t/\tTRUE\t4000000000\tmycookie\tmycookievalue", + *tag_cookie1 = "host.com|/|mycookie", + *cookie2 = "host.com\tFALSE\t/xxx\tTRUE\t4000000000\tmycookie\tmyxxxcookievalue", + *tag_cookie2 = "host.com|/xxx|mycookie", + *cookie3 = "host.com\tFALSE\t/\tTRUE\t4000000000\textra\tcookie3value", + *tag_cookie3 = "host.com|/|extra", + *cookie4 = "host.com\tFALSE\t/yyy\tTRUE\t4000000000\tnewcookie\tnewcookievalue", + *tag_cookie4 = "host.com|/yyy|newcookie" +; + +static int +test_nsc1(void) +{ + struct lws_cache_creation_info ci; + struct lws_cache_ttl_lru *l1, *nsc; + lws_cache_results_t cr; + int n, ret = 1; + size_t size; + char *po; + + lwsl_user("%s\n", __func__); + tests++; + + /* First create a netscape cookie cache object */ + + memset(&ci, 0, sizeof(ci)); + ci.cx = cx; + ci.ops = &lws_cache_ops_nscookiejar; + ci.name = "NSC"; + ci.u.nscookiejar.filepath = "./cookies.txt"; + + nsc = lws_cache_create(&ci); + if (!nsc) + goto cdone; + + /* Then a heap cache "L1" as a child of nsc */ + + ci.ops = &lws_cache_ops_heap; + ci.name = "L1"; + ci.parent = nsc; + + l1 = lws_cache_create(&ci); + if (!l1) + goto cdone; + + lws_cache_debug_dump(nsc); + lws_cache_debug_dump(l1); + + lwsl_user("%s: add cookies to L1\n", __func__); + + /* add three cookies */ + + if (lws_cache_write_through(l1, tag_cookie1, + (const uint8_t *)cookie1, strlen(cookie1), + lws_now_usecs() + LWS_US_PER_SEC, NULL)) { + lwsl_err("%s: write1 failed\n", __func__); + goto cdone; + } + + lws_cache_debug_dump(nsc); + lws_cache_debug_dump(l1); + + if (lws_cache_write_through(l1, tag_cookie2, + (const uint8_t *)cookie2, strlen(cookie2), + lws_now_usecs() + LWS_US_PER_SEC * 2, NULL)) { + lwsl_err("%s: write2 failed\n", __func__); + goto cdone; + } + + lws_cache_debug_dump(nsc); + lws_cache_debug_dump(l1); + + if (lws_cache_write_through(l1, tag_cookie3, + (const uint8_t *)cookie3, strlen(cookie3), + lws_now_usecs() + LWS_US_PER_SEC * 2, NULL)) { + lwsl_err("%s: write3 failed\n", __func__); + goto cdone; + } + + lws_cache_debug_dump(nsc); + lws_cache_debug_dump(l1); + + lwsl_user("%s: check cookies in L1\n", __func__); + + /* confirm that the cookies are individually in L1 */ + + if (lws_cache_item_get(l1, tag_cookie1, (const void **)&po, &size) || + size != strlen(cookie1) || memcmp(po, cookie1, size)) { + lwsl_err("%s: L1 '%s' missing, size %llu, po %s\n", __func__, + tag_cookie1, (unsigned long long)size, po); + goto cdone; + } + + if (lws_cache_item_get(l1, tag_cookie2, (const void **)&po, &size) || + size != strlen(cookie2) || memcmp(po, cookie2, size)) { + lwsl_err("%s: L1 '%s' missing\n", __func__, tag_cookie2); + goto cdone; + } + + if (lws_cache_item_get(l1, tag_cookie3, (const void **)&po, &size) || + size != strlen(cookie3) || memcmp(po, cookie3, size)) { + lwsl_err("%s: L1 '%s' missing\n", __func__, tag_cookie3); + goto cdone; + } + + /* confirm that the cookies are individually in L2 / NSC... normally + * we don't do this but check via L1 so we can get it from there if + * present. But as a unit test, we want to make sure it's in L2 / NSC + */ + + lwsl_user("%s: check cookies written thru to NSC\n", __func__); + + if (lws_cache_item_get(nsc, tag_cookie1, (const void **)&po, &size) || + size != strlen(cookie1) || memcmp(po, cookie1, size)) { + lwsl_err("%s: NSC '%s' missing, size %llu, po %s\n", __func__, + tag_cookie1, (unsigned long long)size, po); + goto cdone; + } + + if (lws_cache_item_get(nsc, tag_cookie2, (const void **)&po, &size) || + size != strlen(cookie2) || memcmp(po, cookie2, size)) { + lwsl_err("%s: NSC '%s' missing\n", __func__, tag_cookie2); + goto cdone; + } + + if (lws_cache_item_get(nsc, tag_cookie3, (const void **)&po, &size) || + size != strlen(cookie3) || memcmp(po, cookie3, size)) { + lwsl_err("%s: NSC '%s' missing\n", __func__, tag_cookie3); + goto cdone; + } + + /* let's do a lookup with no results */ + + lwsl_user("%s: nonexistant get must not pass\n", __func__); + + if (!lws_cache_item_get(l1, "x.com|y|z", (const void **)&po, &size)) { + lwsl_err("%s: nonexistant found size %llu, po %s\n", __func__, + (unsigned long long)size, po); + goto cdone; + } + + /* + * let's try some url paths and check we get the right results set... + * for / and any cookie, we expect only c1 and c3 to be listed + */ + + lwsl_user("%s: wildcard lookup 1\n", __func__); + + n = lws_cache_lookup(l1, "host.com|/|*", + (const void **)&cr.ptr, &cr.size); + if (n) { + lwsl_err("%s: lookup failed %d\n", __func__, n); + goto cdone; + } + lwsl_hexdump_notice(cr.ptr, size); + + if (cr.size != 53) + goto cdone; + + while (!lws_cache_results_walk(&cr)) + lwsl_notice(" %s (%d)\n", (const char *)cr.tag, + (int)cr.payload_len); + + /* + * for /xxx and any cookie, we expect all 3 listed + */ + + lwsl_user("%s: wildcard lookup 2\n", __func__); + + n = lws_cache_lookup(l1, "host.com|/xxx|*", + (const void **)&cr.ptr, &cr.size); + if (n) { + lwsl_err("%s: lookup failed %d\n", __func__, n); + goto cdone; + } + + if (cr.size != 84) + goto cdone; + + while (!lws_cache_results_walk(&cr)) + lwsl_notice(" %s (%d)\n", (const char *)cr.tag, + (int)cr.payload_len); + + /* + * for /yyyy and any cookie, we expect only c1 and c3 + */ + + lwsl_user("%s: wildcard lookup 3\n", __func__); + + n = lws_cache_lookup(l1, "host.com|/yyyy|*", + (const void **)&cr.ptr, &cr.size); + if (n) { + lwsl_err("%s: lookup failed %d\n", __func__, n); + goto cdone; + } + + if (cr.size != 53) + goto cdone; + + while (!lws_cache_results_walk(&cr)) + lwsl_notice(" %s (%d)\n", (const char *)cr.tag, + (int)cr.payload_len); + + /* + * repeat the above test, results should come from cache + */ + + lwsl_user("%s: wildcard lookup 4\n", __func__); + + n = lws_cache_lookup(l1, "host.com|/yyyy|*", + (const void **)&cr.ptr, &cr.size); + if (n) { + lwsl_err("%s: lookup failed %d\n", __func__, n); + goto cdone; + } + + if (cr.size != 53) + goto cdone; + + while (!lws_cache_results_walk(&cr)) + lwsl_notice(" %s (%d)\n", (const char *)cr.tag, + (int)cr.payload_len); + + /* now let's try deleting cookie 1 */ + + if (lws_cache_item_remove(l1, tag_cookie1)) + goto cdone; + + lws_cache_debug_dump(nsc); + lws_cache_debug_dump(l1); + + /* with c1 gone, we should only get c3 */ + + lwsl_user("%s: wildcard lookup 5\n", __func__); + + n = lws_cache_lookup(l1, "host.com|/|*", + (const void **)&cr.ptr, &cr.size); + if (n) { + lwsl_err("%s: lookup failed %d\n", __func__, n); + goto cdone; + } + + if (cr.size != 25) + goto cdone; + + while (!lws_cache_results_walk(&cr)) + lwsl_notice(" %s (%d)\n", (const char *)cr.tag, + (int)cr.payload_len); + + /* + * let's add a fourth cookie (third in cache now we deleted one) + */ + + if (lws_cache_write_through(l1, tag_cookie4, + (const uint8_t *)cookie4, strlen(cookie4), + lws_now_usecs() + LWS_US_PER_SEC * 2, NULL)) { + lwsl_err("%s: write4 failed\n", __func__); + goto cdone; + } + + /* + * for /yy and any cookie, we expect only c3 + */ + + lwsl_user("%s: wildcard lookup 6\n", __func__); + + n = lws_cache_lookup(l1, "host.com|/yy|*", + (const void **)&cr.ptr, &cr.size); + if (n) { + lwsl_err("%s: lookup failed %d\n", __func__, n); + goto cdone; + } + + if (cr.size != 25) + goto cdone; + + while (!lws_cache_results_walk(&cr)) + lwsl_notice(" %s (%d)\n", (const char *)cr.tag, + (int)cr.payload_len); + + /* + * for /yyy and any cookie, we expect c3 and c4 + */ + + lwsl_user("%s: wildcard lookup 7\n", __func__); + + n = lws_cache_lookup(l1, "host.com|/yyy|*", + (const void **)&cr.ptr, &cr.size); + if (n) { + lwsl_err("%s: lookup failed %d\n", __func__, n); + goto cdone; + } + + if (cr.size != 57) + goto cdone; + + while (!lws_cache_results_walk(&cr)) + lwsl_notice(" %s (%d)\n", (const char *)cr.tag, + (int)cr.payload_len); + + /* that's ok then */ + + lwsl_user("%s: done\n", __func__); + + ret = 0; + +cdone: + lws_cache_destroy(&nsc); + lws_cache_destroy(&l1); + + if (ret) + lwsl_warn("%s: fail\n", __func__); + + return ret; +} +#endif + + +int main(int argc, const char **argv) +{ + struct lws_context_creation_info info; + + memset(&info, 0, sizeof info); + lws_cmdline_option_handle_builtin(argc, argv, &info); + info.fd_limit_per_thread = 1 + 6 + 1; + info.port = CONTEXT_PORT_NO_LISTEN; + + lwsl_user("LWS API selftest: lws_cache\n"); + + cx = lws_create_context(&info); + if (!cx) { + lwsl_err("lws init failed\n"); + return 1; + } + + if (test_just_l1()) + fail++; + if (test_just_l1_limits()) + fail++; + +#if defined(LWS_WITH_CACHE_NSCOOKIEJAR) + if (test_nsc1()) + fail++; +#endif + + lws_context_destroy(cx); + + if (tests && !fail) + lwsl_user("Completed: PASS\n"); + else + lwsl_err("Completed: FAIL %d / %d\n", fail, tests); + + return 0; +} diff --git a/minimal-examples/api-tests/api-test-lws_cache/text1.txt b/minimal-examples/api-tests/api-test-lws_cache/text1.txt new file mode 100644 index 000000000..c4079a203 --- /dev/null +++ b/minimal-examples/api-tests/api-test-lws_cache/text1.txt @@ -0,0 +1,3 @@ +# Netscape HTTP Cookie File +host.com FALSE / FALSE 1234 mycookie value +host.com FALSE /xxx FALSE 1234 mycookie valuexxx