Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
18 / 18
100.00% covered (success)
100.00%
9 / 9
CRAP
100.00% covered (success)
100.00%
1 / 1
SynchronousIcapClient
100.00% covered (success)
100.00%
18 / 18
100.00% covered (success)
100.00%
9 / 9
10
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 create
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
1
 request
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 options
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 scanFile
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 scanFileDiscardingBody
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 scanFileWithPreviewDiscardingBody
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 discardingClient
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 scanFileWithPreview
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
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;
22
23use Amp\Cancellation;
24use Amp\Future;
25use Ndrstmr\Icap\DTO\IcapRequest;
26use Ndrstmr\Icap\DTO\IcapResponse;
27use Ndrstmr\Icap\DTO\ScanResult;
28use Ndrstmr\Icap\Config;
29use Ndrstmr\Icap\Transport\SynchronousStreamTransport;
30use Ndrstmr\Icap\RequestFormatter;
31use Ndrstmr\Icap\ResponseParser;
32use Ndrstmr\Icap\DefaultPreviewStrategy;
33
34/**
35 * Simple synchronous wrapper around {@link IcapClient}.
36 *
37 * Each call is one operation of the wrapped client and ends at the latest after its `Config::getOperationTimeout()`
38 * (60 seconds by default), with an {@see \Ndrstmr\Icap\Exception\IcapTimeoutException}: raise it for large
39 * files or a slow scanner. With the synchronous transport the waits of the socket are bounded by it, but code that
40 * does not return (a request-body producer) is only noticed afterwards.
41 */
42final class SynchronousIcapClient
43{
44    private IcapClientInterface $asyncClient;
45
46    /**
47     * @param IcapClientInterface $asyncClient Underlying asynchronous client
48     */
49    public function __construct(IcapClientInterface $asyncClient)
50    {
51        $this->asyncClient = $asyncClient;
52    }
53
54    /**
55     * Create a client with default configuration.
56     */
57    public static function create(): self
58    {
59        $asyncClient = new IcapClient(
60            new Config('127.0.0.1'),
61            new SynchronousStreamTransport(),
62            new RequestFormatter(),
63            new ResponseParser(),
64            new DefaultPreviewStrategy(),
65        );
66
67        return new self($asyncClient);
68    }
69
70    public function request(IcapRequest $request, ?Cancellation $cancellation = null): ScanResult
71    {
72        return $this->asyncClient->request($request, $cancellation)->await();
73    }
74
75    public function options(string $service, ?Cancellation $cancellation = null): IcapResponse
76    {
77        return $this->asyncClient->options($service, $cancellation)->await();
78    }
79
80    /**
81     * @param array<string, string|string[]> $extraHeaders
82     * @throws \Ndrstmr\Icap\Exception\IcapStorageException When the file cannot be opened, sized or read to its end
83     */
84    public function scanFile(
85        string $service,
86        string $filePath,
87        array $extraHeaders = [],
88        ?Cancellation $cancellation = null,
89    ): ScanResult {
90        return $this->asyncClient->scanFile($service, $filePath, $extraHeaders, $cancellation)->await();
91    }
92
93    /**
94     * {@see DiscardingIcapClient::scanFileDiscardingBody()}, awaited.
95     *
96     * @param array<string, string|string[]> $extraHeaders
97     * @throws \LogicException When the wrapped client does not implement {@see DiscardingIcapClient}
98     */
99    public function scanFileDiscardingBody(
100        string $service,
101        string $filePath,
102        int $maxResponseBytes,
103        array $extraHeaders = [],
104        ?Cancellation $cancellation = null,
105    ): ScanResult {
106        return $this->discardingClient()->scanFileDiscardingBody($service, $filePath, $maxResponseBytes, $extraHeaders, $cancellation)->await();
107    }
108
109    /**
110     * {@see DiscardingIcapClient::scanFileWithPreviewDiscardingBody()}, awaited.
111     *
112     * @param array<string, string|string[]> $extraHeaders
113     * @throws \LogicException When the wrapped client does not implement {@see DiscardingIcapClient}
114     */
115    public function scanFileWithPreviewDiscardingBody(
116        string $service,
117        string $filePath,
118        int $maxResponseBytes,
119        ?int $previewSize = null,
120        array $extraHeaders = [],
121        ?Cancellation $cancellation = null,
122    ): ScanResult {
123        return $this->discardingClient()->scanFileWithPreviewDiscardingBody($service, $filePath, $maxResponseBytes, $previewSize, $extraHeaders, $cancellation)->await();
124    }
125
126    private function discardingClient(): DiscardingIcapClient
127    {
128        return $this->asyncClient instanceof DiscardingIcapClient
129            ? $this->asyncClient
130            : throw new \LogicException($this->asyncClient::class . ' cannot discard the body of a response; it does not implement ' . DiscardingIcapClient::class . '.');
131    }
132
133    /**
134     * @param array<string, string|string[]> $extraHeaders
135     * @throws \Ndrstmr\Icap\Exception\IcapStorageException When the file cannot be opened, sized or read to its end
136     * @throws \LogicException When the transport of the wrapped client cannot continue a preview on its connection
137     */
138    public function scanFileWithPreview(
139        string $service,
140        string $filePath,
141        ?int $previewSize = null,
142        array $extraHeaders = [],
143        ?Cancellation $cancellation = null,
144    ): ScanResult {
145        return $this->asyncClient->scanFileWithPreview($service, $filePath, $previewSize, $extraHeaders, $cancellation)->await();
146    }
147}