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 | |
| 19 | declare(strict_types=1); |
| 20 | |
| 21 | namespace Ndrstmr\Icap; |
| 22 | |
| 23 | use Amp\Cancellation; |
| 24 | use Amp\Future; |
| 25 | use Ndrstmr\Icap\DTO\IcapRequest; |
| 26 | use Ndrstmr\Icap\DTO\IcapResponse; |
| 27 | use Ndrstmr\Icap\DTO\ScanResult; |
| 28 | |
| 29 | /** |
| 30 | * Public contract for ICAP clients. |
| 31 | * |
| 32 | * Implementations are async-native and return Amp Futures. The synchronous |
| 33 | * decorator {@see SynchronousIcapClient} awaits those Futures behind a |
| 34 | * blocking facade. |
| 35 | * |
| 36 | * Every method takes an optional {@see Cancellation} so a caller can |
| 37 | * abort an in-flight scan from the outside (e.g. when an upstream HTTP |
| 38 | * upload has been aborted). A call ends at the first of three things: the |
| 39 | * caller's cancellation, which stays an `Amp\CancelledException` and wins |
| 40 | * when more than one applies; the end of the operation |
| 41 | * ({@see Config::getOperationTimeout()}, fixed when the method is called and |
| 42 | * covering every phase, and in the retry decorator every attempt and |
| 43 | * pause); and the limit of a phase ({@see Config::getStreamTimeout()}, the |
| 44 | * TLS handshake, the start of the answer). The last two are an |
| 45 | * {@see \Ndrstmr\Icap\Exception\IcapTimeoutException}. An expired call |
| 46 | * closes its socket and its file handle. Code that holds the event loop and |
| 47 | * does not return (a request-body producer, a slow cache) cannot be |
| 48 | * interrupted; the end is looked at when control comes back, and a result |
| 49 | * that comes in after it is not returned. A call into the operating system |
| 50 | * that hangs does not hold the loop, but is not interrupted either: the |
| 51 | * operation ends, and the file is closed when the call returns. |
| 52 | * |
| 53 | * Expected operational failures ({@see \Ndrstmr\Icap\Exception\IcapExceptionInterface}) |
| 54 | * fail the returned Future, also when the answer comes from the OPTIONS cache; |
| 55 | * only invalid arguments throw at the call. What the code you pass in throws |
| 56 | * (producer, logger, strategy, cache, connector) passes unchanged. |
| 57 | */ |
| 58 | interface IcapClientInterface |
| 59 | { |
| 60 | /** |
| 61 | * Send a raw ICAP request. |
| 62 | * |
| 63 | * A stream resource in the encapsulated body is read, never closed: it |
| 64 | * stays the caller's, whatever the outcome. |
| 65 | * |
| 66 | * @return Future<ScanResult> |
| 67 | */ |
| 68 | public function request(IcapRequest $request, ?Cancellation $cancellation = null): Future; |
| 69 | |
| 70 | /** |
| 71 | * Issue an OPTIONS request against the given service. Returns the raw |
| 72 | * {@see IcapResponse} on success so callers can inspect capability |
| 73 | * headers (`Preview`, `Options-TTL`, `Methods`, `Allow`, `Service`, |
| 74 | * `ISTag`, …) directly. Only a `200` is an answer to OPTIONS, whether it |
| 75 | * comes from the server or from the cache: 4xx throws |
| 76 | * {@see IcapClientException}, 5xx throws {@see IcapServerException}, and |
| 77 | * every other status, including `100 Continue`, `204` and `206`, throws |
| 78 | * {@see IcapProtocolException}. |
| 79 | * |
| 80 | * @return Future<IcapResponse> |
| 81 | */ |
| 82 | public function options(string $service, ?Cancellation $cancellation = null): Future; |
| 83 | |
| 84 | /** |
| 85 | * Scan a local file via RESPMOD. |
| 86 | * |
| 87 | * The file is opened once, inside the returned Future, read through |
| 88 | * amphp/file and closed when the operation ends, whatever the outcome. |
| 89 | * Its size is that of the open file, and the body goes out with exactly |
| 90 | * that many bytes. The application keeps the file unchanged until the |
| 91 | * Future completes and relates the result to that version: a change of |
| 92 | * size is detected, new content of the same size is not. Only regular, |
| 93 | * seekable files of the local file system are supported; a relative path |
| 94 | * is taken from the working directory of the call. The Future fails with |
| 95 | * an {@see \Ndrstmr\Icap\Exception\IcapStorageException} when the path |
| 96 | * is not a regular file, the file cannot be opened, sized, read to its end |
| 97 | * or closed, has another size than when it was opened, or the request did |
| 98 | * not read it to its end; |
| 99 | * nothing is sent as if it were an empty, shorter or longer file. The body |
| 100 | * of the response is kept, within `Config::getMaxResponseSize()`; for a |
| 101 | * server that sends a large file back, see {@see \Ndrstmr\Icap\DiscardingIcapClient}. |
| 102 | * See docs/file-sources.md. |
| 103 | * |
| 104 | * @param array<string, string|string[]> $extraHeaders Extra ICAP request |
| 105 | * headers, e.g. `['X-Client-IP' => '203.0.113.5']`. `Host` and |
| 106 | * `Encapsulated` are always set by the client; caller values for |
| 107 | * them are dropped in any spelling. No preview is sent, so a |
| 108 | * caller `Preview` is dropped as well; `Allow` passes. |
| 109 | * @return Future<ScanResult> |
| 110 | */ |
| 111 | public function scanFile( |
| 112 | string $service, |
| 113 | string $filePath, |
| 114 | array $extraHeaders = [], |
| 115 | ?Cancellation $cancellation = null, |
| 116 | ): Future; |
| 117 | |
| 118 | /** |
| 119 | * Scan a file using preview mode, streaming the remainder on demand. |
| 120 | * |
| 121 | * When $previewSize is null the client queries the OPTIONS cache for |
| 122 | * the server's advertised `Preview` header (RFC 3507 §4.10.2) and |
| 123 | * uses that value. Falls back to 1024 when the cache has no entry |
| 124 | * or the response carries no `Preview` header. |
| 125 | * |
| 126 | * The preview is 0 to 1 MiB (1048576 bytes). `0` sends the headers |
| 127 | * alone. An explicit size outside that range throws an |
| 128 | * \InvalidArgumentException before the file is opened; a cached value |
| 129 | * above it is clamped to it, and a cached value that is not a |
| 130 | * non-negative decimal integer throws an |
| 131 | * {@see \Ndrstmr\Icap\Exception\IcapMalformedResponseException}. The |
| 132 | * file is handled as in {@see scanFile()}, `ieof` is sent only |
| 133 | * after the end of the file was observed, and the rest of the file goes |
| 134 | * out with the length its envelope announced, on the connection of the |
| 135 | * preview (RFC 3507 §4.5): a client whose transport cannot do that throws |
| 136 | * a \LogicException at the call, before it opens the file. |
| 137 | * |
| 138 | * @param array<string, string|string[]> $extraHeaders See {@see scanFile()}. |
| 139 | * `Preview` and `Allow` are library-managed and cannot be |
| 140 | * overridden via this parameter. |
| 141 | * @return Future<ScanResult> |
| 142 | */ |
| 143 | public function scanFileWithPreview( |
| 144 | string $service, |
| 145 | string $filePath, |
| 146 | ?int $previewSize = null, |
| 147 | array $extraHeaders = [], |
| 148 | ?Cancellation $cancellation = null, |
| 149 | ): Future; |
| 150 | } |