Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
1<?php
2
3/**
4 * SPDX-License-Identifier: EUPL-1.2
5 *
6 * This file is part of icap-flow.
7 *
8 * Licensed under the EUPL, Version 1.2 only (the "Licence");
9 * you may not use this work except in compliance with the Licence.
10 * You may obtain a copy of the Licence at:
11 *
12 *     https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
13 *
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the Licence is distributed on an "AS IS" basis,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 */
18
19declare(strict_types=1);
20
21namespace Ndrstmr\Icap\Cache;
22
23use Ndrstmr\Icap\DTO\IcapResponse;
24
25/**
26 * Storage for cached ICAP OPTIONS responses (RFC 3507 §4.10.2).
27 *
28 * The library ships a process-local implementation ({@see InMemoryOptionsCache}) and adapters for PSR-6 and PSR-16
29 * ({@see Psr6OptionsCache}, {@see Psr16OptionsCache}) for caches that processes share.
30 *
31 * The contract:
32 *  - An entry is valid for the `$ttlSeconds` of {@see set()}, counted from when it is stored, for at most the
33 *    `Options-TTL` of the response if it has one, and for at most {@see MAX_TTL_SECONDS}. Reading never extends it;
34 *    {@see get()} returns null once it ended. A change of the service
35 *    (its ISTag, `Preview`, `Allow`) shows when the entry ends at the latest, which is the only consistency promised:
36 *    nothing invalidates an entry earlier, and processes that share a backend are not told. Writers of one key replace
37 *    the entry as a whole; the last one wins.
38 *  - The client stores a response only if it has status 200, fits a budget (64 KiB of fields and body in at most 100
39 *    field values) and does not contradict itself (`Options-TTL`, `ISTag`, `Preview`). It uses what {@see get()}
40 *    returns only if that passes the same check and the limits of the client (`maxResponseSize`, `maxHeaderCount`,
41 *    `maxHeaderLineLength`) would have taken it from the server. An entry that is none, however a custom cache came
42 *    by it, is a miss.
43 *  - The ISTag (RFC 3507 §4.7) belongs to the service that `$key` names. It never invalidates the entry of another key,
44 *    and an implementation must not use it for that.
45 *  - The key names the endpoint, the service (path and query) and the TLS profile of the client, so that a plain and
46 *    a TLS client, or clients with other TLS settings, do not share an entry. It does not know a tenant, a proxy or
47 *    anything else your transport does: give each security domain its own cache, or its own prefix with the PSR
48 *    adapters.
49 *  - How many entries a cache holds is the cache's to bound ({@see InMemoryOptionsCache} does; the PSR adapters bound
50 *    the lifetime and size of an entry, and leave the total to the backend).
51 *
52 * A failure of the backend is reported as an {@see \Ndrstmr\Icap\Exception\IcapStorageException} with the cause as its
53 * previous exception, as the PSR adapters do; anything else passes unchanged.
54 *
55 * The client names an entry with a key of 64 hex digits ({@see OptionsCacheKey::forService()}). Treat the key as
56 * opaque: it holds nothing a PSR-6 or PSR-16 backend, a file name or a Redis key space could refuse.
57 */
58interface OptionsCacheInterface
59{
60    /**
61     * The longest time in seconds that an entry is valid, whatever the server says: its `Options-TTL` can only shorten
62     * it. An hour is the value of the example in RFC 3507 §4.10.2 and the longest a changed service goes unnoticed.
63     */
64    public const int MAX_TTL_SECONDS = 3600;
65
66    /**
67     * Return the cached response for $key, or null on cache miss
68     * (entry absent or expired).
69     */
70    public function get(string $key): ?IcapResponse;
71
72    /**
73     * Store $response under $key for at most $ttlSeconds, and never for longer than its `Options-TTL` or
74     * {@see MAX_TTL_SECONDS}. A value <= 0 means "do not cache".
75     *
76     * $istag is the ISTag of the service $key names, if the response has one. It informs; it invalidates nothing.
77     */
78    public function set(string $key, IcapResponse $response, int $ttlSeconds, ?string $istag = null): void;
79
80    /**
81     * Remove the cached entry for $key. Idempotent.
82     */
83    public function delete(string $key): void;
84}