# Varnish Module (VMOD) `memstore` ```vcl // Place import statement at the top of your VCL file // This loads vmod from a standard location import memstore; // Or load vmod from a specific file import memstore from "path/to/libmemstore.so"; ``` ## Object `cidr_set` ### Constructor `memstore.cidr_set(STRING path, [BOOL allow_missing])` Load a set of CIDR prefixes from `path`. ```vcl sub vcl_init { new trusted = memstore.cidr_set("/etc/varnish/trusted.cidrs"); new pending = memstore.cidr_set("/var/lib/foo/late.cidrs", allow_missing = true); } ``` ### Method `BOOL .contains([IP ip])` Is `ip` covered by one of the prefixes? ```vcl if (trusted.contains(client.ip)) { ... } ``` ### Method `BOOL .contains_str([STRING ip])` Like `contains()`, but parses the address from a string -- useful for values pulled out of a header such as `X-Forwarded-For`. An unset or unparseable argument returns `false` -- including one that is not valid UTF-8, which cannot be an address anyway. ### Method `BOOL .reload()` Re-read the file and atomically swap the new set in. Returns `true` on success. On failure the previous set keeps serving and the reason is logged to VSL under `Error`, so a bad edit degrades to "stale but working" rather than taking traffic down. ### Method `INT .count()` Number of distinct prefixes currently loaded. ### Method `STRING .path()` The file this set was loaded from. ## Object `kv_store` ### Constructor `memstore.kv_store(STRING path, [STRING separator], [BOOL allow_missing])` Load a key/value store from `path`. ```vcl sub vcl_init { new redirects = memstore.kv_store("/etc/varnish/redirects.txt"); new hosts = memstore.kv_store("/etc/varnish/hosts.txt", ":"); new late = memstore.kv_store("/var/lib/foo/late.txt", allow_missing = true); } ``` ### Method `STRING .get(STRING key)` Value for `key`, or the empty string if it is not present. Use `exists()` to tell a missing key from one holding an empty value. ### Method `STRING .get_or(STRING key, STRING fallback)` Value for `key`, or `fallback` if it is not present. ### Method `BOOL .exists(STRING key)` Is `key` present? ### Method `BOOL .set(STRING key, STRING value)` Insert or overwrite `key`. Returns `true` if it replaced an existing value. ### Method `BOOL .add(STRING key, STRING value)` Insert `key` only if it is absent. Returns `true` if it was inserted. ### Method `BOOL .update(STRING key, STRING value)` Overwrite `key` only if it already exists. Returns `true` if it was updated. ### Method `BOOL .delete(STRING key)` Remove `key`. Returns `true` if it was present. ### Method `VOID .clear()` Drop every entry, leaving the file untouched. ### Method `BOOL .reload()` Re-read the file, discarding runtime changes made with `set()`/`add()`/`update()`/`delete()`. Returns `true` on success. On failure the current contents keep serving and the reason is logged to VSL under `Error`. ### Method `INT .count()` Number of entries currently loaded. ### Method `STRING .path()` The file this store was loaded from. ## Object `regex_set` ### Constructor `memstore.regex_set(STRING path, [BOOL case_insensitive], [BOOL allow_missing])` Compile a list of regular expressions from `path`. The file is line based, one pattern per line, in [Rust regex syntax](https://docs.rs/regex/latest/regex/#syntax) -- the same dialect VCL's `~` uses for the common cases. Blank lines are ignored, and `#` starts a comment only at the *beginning* of a line, since `#` is a legal regex character. Lines are trimmed, so write a significant leading or trailing space as `\s`, `[ ]` or `\x20`. `case_insensitive` defaults to `false` and applies to every pattern in the instance; an inline `(?i)` works per pattern regardless. A pattern that fails to compile fails VCL loading, so a broken file is caught at `vcl.load` time rather than at request time. A *missing* file does too, unless `allow_missing` is set. `allow_missing` defaults to `false`. Set it when something else creates the file after Varnish starts: the set then loads empty (`count() == 0`, every `matches()` false) instead of failing `vcl.load`, and a later `reload()` picks the file up once it appears. It only excuses a file that is *not there* -- a file that exists but cannot be read, or one with a bad pattern in it, still fails. It applies to construction only, never to `reload()`. ```vcl sub vcl_init { new bots = memstore.regex_set("/etc/varnish/bots.re", case_insensitive = true); new late = memstore.regex_set("/var/lib/foo/late.re", allow_missing = true); } ``` ### Method `BOOL .matches([STRING subject])` Does any pattern match `subject`? Matching is unanchored, like `~` in VCL: the pattern has to be found *in* the subject, not to cover all of it. An unset argument returns `false`. The subject is matched as raw bytes, so a header that is not valid UTF-8 -- legal per RFC 9110 -- is matched rather than failing the request. All patterns are tested in a single pass over the subject, so the cost depends on the subject length rather than on how many patterns are loaded. ```vcl if (bots.matches(req.http.User-Agent)) { ... } ``` ### Method `STRING .which([STRING subject])` The first pattern (in file order) that matches `subject`, as written in the file -- useful for logging *why* a request was classified. Returns the empty string when nothing matches. This does more work than `matches()`, which can stop at the first hit. ### Method `STRING .which_all([STRING subject], [STRING separator])` Every matching pattern, in file order, joined by `separator` (default `,`). Returns the empty string when nothing matches. ### Method `BOOL .reload()` Re-read the file and atomically swap the new set in. Returns `true` on success. On failure the previous set keeps serving and the reason is logged to VSL under `Error`, so a bad edit degrades to "stale but working" rather than taking traffic down. ### Method `INT .count()` Number of patterns currently loaded. ### Method `STRING .path()` The file this set was loaded from.