nquic_token_cache (nquic v1.0.0)
View SourcePer-instance NEW_TOKEN cache for QUIC client reconnection (RFC 9000 §8.1.3).
Mirrors nquic_session_cache: each cache is a gen_server that owns
one named ETS table and runs a periodic eviction sweep. The cache is
opt-in; the caller starts it under its own supervisor and references
it by name on nquic:connect/3.
Usage
[nquic_token_cache:child_spec(my_quic_tokens, #{sweep_ms => 60_000})].{ok, Conn} = nquic:connect(Host, Port, #{token_cache => my_quic_tokens}).The client records any NEW_TOKEN frame received from the server into
the cache. A subsequent connect/4 to the same {Host, Port} reads
back the token and attaches it to the outgoing Initial; the server
validates it against its static key, treats the source address as
already-validated, and skips the Retry round-trip.
The cache stores raw token bytes; the server's HMAC binds the token
to a specific address + lifetime, so a stale or address-mismatched
token simply fails server-side validation and triggers a normal
Retry. Keep the client TTL shorter than the server's Lifetime
(default 24 h) to avoid presenting tokens the server will reject.
Custom backends
The token_cache connect option also accepts {module, Mod} where
Mod exports:
store(Host, Port, Token) -> ok.
lookup(Host, Port) -> {ok, binary()} | {error, not_found}.
delete(Host, Port) -> ok.
Summary
Functions
Standard child spec with the default sweep interval.
Standard child spec with custom options.
Drop every entry in cache Name. Raises badarg if not started.
Delete a token from cache Name. Raises badarg if not started.
Look up a NEW_TOKEN for {Host, Port} in cache Name.
Returns {ok, Token} for a live token, {error, not_found} for a
miss or an expired entry. Raises badarg if Name is not a live
cache.
If Opts does not already carry a client_token, resolve the
configured token_cache selector and inject any cached NEW_TOKEN
into Opts. Returns Opts unchanged on miss or when no cache is
configured.
Accepts the same selector shape as the connect/3,4 option:
false (no cache), an atom naming a started cache, or
{module, Mod} for a custom backend exporting lookup/2.
Return the number of entries in cache Name.
Start a cache registered under Name with default options.
Start a cache registered under Name.
Accepted options
Stop the cache and tear down its ETS table.
Store a NEW_TOKEN under {Host, Port} for the cache Name.
The optional TTL (seconds) bounds how long the cache will hand the
token back; default 24 h. Keep this less-than-or-equal to the
server's new_token_lifetime so the cache never serves a token the
server would reject.
Types
-type cache_name() :: atom().
-type host() :: inet:hostname() | inet:ip_address().
Functions
-spec child_spec(cache_name()) -> supervisor:child_spec().
Standard child spec with the default sweep interval.
-spec child_spec(cache_name(), map()) -> supervisor:child_spec().
Standard child spec with custom options.
-spec clear(cache_name()) -> ok.
Drop every entry in cache Name. Raises badarg if not started.
-spec delete(cache_name(), host(), inet:port_number()) -> ok.
Delete a token from cache Name. Raises badarg if not started.
-spec handle_call(term(), gen_server:from(), #state{name :: cache_name(), sweep_ms :: pos_integer(), sweep_ref :: reference() | undefined}) -> {reply, {error, unknown_request}, #state{name :: cache_name(), sweep_ms :: pos_integer(), sweep_ref :: reference() | undefined}}.
-spec handle_cast(term(), #state{name :: cache_name(), sweep_ms :: pos_integer(), sweep_ref :: reference() | undefined}) -> {noreply, #state{name :: cache_name(), sweep_ms :: pos_integer(), sweep_ref :: reference() | undefined}}.
-spec handle_info(sweep | term(), #state{name :: cache_name(), sweep_ms :: pos_integer(), sweep_ref :: reference() | undefined}) -> {noreply, #state{name :: cache_name(), sweep_ms :: pos_integer(), sweep_ref :: reference() | undefined}}.
-spec init({cache_name(), map()}) -> {ok, #state{name :: cache_name(), sweep_ms :: pos_integer(), sweep_ref :: reference() | undefined}}.
-spec lookup(cache_name(), host(), inet:port_number()) -> {ok, binary()} | {error, not_found}.
Look up a NEW_TOKEN for {Host, Port} in cache Name.
Returns {ok, Token} for a live token, {error, not_found} for a
miss or an expired entry. Raises badarg if Name is not a live
cache.
-spec maybe_load_token(host(), inet:port_number(), map()) -> map().
If Opts does not already carry a client_token, resolve the
configured token_cache selector and inject any cached NEW_TOKEN
into Opts. Returns Opts unchanged on miss or when no cache is
configured.
Accepts the same selector shape as the connect/3,4 option:
false (no cache), an atom naming a started cache, or
{module, Mod} for a custom backend exporting lookup/2.
-spec size(cache_name()) -> non_neg_integer().
Return the number of entries in cache Name.
-spec start_link(cache_name()) -> {ok, pid()} | ignore | {error, term()}.
Start a cache registered under Name with default options.
-spec start_link(cache_name(), map()) -> {ok, pid()} | ignore | {error, term()}.
Start a cache registered under Name.
Accepted options:
sweep_ms- eviction interval in milliseconds (default 60_000). Returns{error, {already_started, Pid}}if a cache with this name is already registered.
-spec stop(cache_name()) -> ok.
Stop the cache and tear down its ETS table.
-spec store(cache_name(), host(), inet:port_number(), binary()) -> ok.
Store a NEW_TOKEN under {Host, Port} for the cache Name.
The optional TTL (seconds) bounds how long the cache will hand the
token back; default 24 h. Keep this less-than-or-equal to the
server's new_token_lifetime so the cache never serves a token the
server would reject.
-spec terminate(term(), #state{name :: cache_name(), sweep_ms :: pos_integer(), sweep_ref :: reference() | undefined}) -> ok.