Scrutinizer GitHub App not installed

We could not synchronize checks via GitHub's checks API since Scrutinizer's GitHub App is not installed for this repository.

Install GitHub App

GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Passed
Pull Request — master (#758)
by
unknown
02:43
created

PageViewProxy::handleProxyRequest()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 30
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 0 Features 0
Metric Value
eloc 13
c 3
b 0
f 0
dl 0
loc 30
rs 9.8333
cc 1
nc 1
nop 2
1
<?php
2
3
/**
4
 * (c) Kitodo. Key to digital objects e.V. <[email protected]>
5
 *
6
 * This file is part of the Kitodo and TYPO3 projects.
7
 *
8
 * @license GNU General Public License version 3 or later.
9
 * For the full copyright and license information, please read the
10
 * LICENSE.txt file that was distributed with this source code.
11
 */
12
13
namespace Kitodo\Dlf\Plugin\Eid;
14
15
use Kitodo\Dlf\Common\StdOutStream;
16
use Psr\Http\Message\ResponseInterface;
17
use Psr\Http\Message\ServerRequestInterface;
18
use TYPO3\CMS\Core\Configuration\ExtensionConfiguration;
19
use TYPO3\CMS\Core\Http\JsonResponse;
20
use TYPO3\CMS\Core\Http\RequestFactory;
21
use TYPO3\CMS\Core\Http\Response;
22
use TYPO3\CMS\Core\Utility\GeneralUtility;
23
use TYPO3\CMS\Core\Utility\MathUtility;
24
25
/**
26
 * eID image proxy for plugin 'Page View' of the 'dlf' extension
27
 *
28
 * Supported query parameters:
29
 * - `url` (mandatory): The URL to be proxied
30
 * - `header` (optional)
31
 *   - `1`: Request headers and body (via GET)
32
 *   - `2`: Request only headers (via HEAD) and dump them to response body
33
 *
34
 * @author Alexander Bigga <[email protected]>
35
 * @package TYPO3
36
 * @subpackage dlf
37
 * @access public
38
 */
39
class PageViewProxy
40
{
41
    /**
42
     * @var RequestFactory
43
     */
44
    protected $requestFactory;
45
46
    /**
47
     * @var mixed
48
     */
49
    protected $extConf;
50
51
    public function __construct()
52
    {
53
        $this->requestFactory = GeneralUtility::makeInstance(RequestFactory::class);
54
        $this->extConf = GeneralUtility::makeInstance(ExtensionConfiguration::class)->get('dlf');
55
    }
56
57
    /**
58
     * Get headers to be sent to the proxied target.
59
     *
60
     * @return array
61
     */
62
    protected function getRequestHeaders(): array
63
    {
64
        return [
65
            'User-Agent' => $this->extConf['useragent'] ?? 'Kitodo.Presentation Proxy',
66
        ];
67
    }
68
69
    /**
70
     * Return a response that is derived from $response and contains CORS
71
     * headers to be sent to the client.
72
     *
73
     * @return ResponseInterface $response
74
     * @return ServerRequestInterface $request The incoming request.
75
     * @return ResponseInterface
76
     */
77
    protected function withCorsResponseHeaders(
78
        ResponseInterface $response,
79
        ServerRequestInterface $request
80
    ): ResponseInterface {
81
        $origin = (string) ($request->getHeaderLine('Origin') ?: '*');
82
83
        return $response
84
            ->withHeader('Access-Control-Allow-Methods', 'GET, OPTIONS')
85
            ->withHeader('Access-Control-Allow-Origin', $origin)
86
            ->withHeader('Access-Control-Max-Age', '86400');
87
    }
88
89
    /**
90
     * Takes headers listed in $headerNames from $fromResponse, adds them to
91
     * $toResponse and returns the result.
92
     *
93
     * @param ResponseInterface $fromResponse
94
     * @param ResponseInterface $toResponse
95
     * @param array $headerNames
96
     * @return ResponseInterface
97
     */
98
    protected function copyHeaders(
99
        ResponseInterface $fromResponse,
100
        ResponseInterface $toResponse,
101
        array $headerNames
102
    ) {
103
        $result = $toResponse;
104
105
        foreach ($headerNames as $headerName) {
106
            $headerValues = $fromResponse->getHeader($headerName);
107
            $result = $result->withAddedHeader($headerName, $headerValues);
108
        }
109
110
        return $result;
111
    }
112
113
    /**
114
     * Handle an OPTIONS request.
115
     *
116
     * @param ServerRequestInterface $request
117
     * @return ResponseInterface
118
     */
119
    protected function handleOptions(ServerRequestInterface $request): ResponseInterface
120
    {
121
        // 204 No Content
122
        $response = GeneralUtility::makeInstance(Response::class)
123
            ->withStatus(204);
124
        return $this->withCorsResponseHeaders($response, $request);
125
    }
126
127
    /**
128
     * Handle a GET request.
129
     *
130
     * @param ServerRequestInterface $request
131
     * @return ResponseInterface
132
     */
133
    protected function handleGet(ServerRequestInterface $request): ResponseInterface
134
    {
135
        $queryParams = $request->getQueryParams();
136
        $url = (string) ($queryParams['url'] ?? '');
137
138
        if (!GeneralUtility::isValidUrl($url)) {
139
            return new JsonResponse(['message' => 'Did not receive a valid URL.'], 400);
140
        }
141
142
        // This has previously been used as parameter for `GeneralUtility::getUrl()`
143
        // A value of 0 would indicate not to fetch headers, which we'll ignore.
144
        $header = (int) ($queryParams['header'] ?? 1);
145
        $headerOnly = MathUtility::forceIntegerInRange($header, 0, 2, 0) === 2;
146
147
        try {
148
            return $headerOnly
149
                ? $this->handleHeaderRequest($url, $request)
150
                : $this->handleProxyRequest($url, $request);
151
        } catch (\Exception $e) {
152
            return new JsonResponse(['message' => 'Could not fetch resource of given URL.'], 500);
153
        }
154
    }
155
156
    /**
157
     * Handle a request to proxy the target resource.
158
     *
159
     * @param string $url
160
     * @param ServerRequestInterface $request
161
     * @return ResponseInterface
162
     */
163
    protected function handleProxyRequest(
164
        string $url,
165
        ServerRequestInterface $request
166
    ): ResponseInterface {
167
        $targetResponse = $this->requestFactory->request($url, 'GET', [
168
            'headers' => $this->getRequestHeaders(),
169
170
            // For performance, don't download content up-front. Rather, we'll
171
            // download and upload simultaneously.
172
            // https://docs.guzzlephp.org/en/6.5/request-options.html#stream
173
            'stream' => true,
174
175
            // Don't throw exceptions when a non-success status code is
176
            // received. We handle these manually.
177
            'http_errors' => false,
178
        ]);
179
180
        $body = new StdOutStream($targetResponse->getBody());
181
182
        $clientResponse = GeneralUtility::makeInstance(Response::class)
183
            ->withStatus($targetResponse->getStatusCode())
184
            ->withBody($body);
185
186
        $clientResponse = $this->copyHeaders($targetResponse, $clientResponse, [
187
            'Content-Length',
188
            'Content-Type',
189
            'Last-Modified',
190
        ]);
191
192
        return $this->withCorsResponseHeaders($clientResponse, $request);
193
    }
194
195
    /**
196
     * Handle a request to forward headers from target.
197
     *
198
     * @param string $url
199
     * @param ServerRequestInterface $request
200
     * @return ResponseInterface
201
     */
202
    protected function handleHeaderRequest(
203
        string $url,
204
        ServerRequestInterface $request
205
    ): ResponseInterface {
206
        $targetResponse = $this->requestFactory->request($url, 'HEAD', [
207
            'headers' => $this->getRequestHeaders(),
208
        ]);
209
210
        $clientResponse = GeneralUtility::makeInstance(Response::class)
211
            ->withStatus(200);
212
213
        $body = $clientResponse->getBody();
214
        foreach ($targetResponse->getHeaders() as $name => $values) {
215
            $body->write($name . ': ' . implode(', ', $values) . "\n");
216
        }
217
218
        return $this->withCorsResponseHeaders($clientResponse, $request);
219
    }
220
221
    /**
222
     * The main method of the eID script
223
     *
224
     * @access public
225
     *
226
     * @param ServerRequestInterface $request
227
     * @return ResponseInterface
228
     */
229
    public function main(ServerRequestInterface $request)
230
    {
231
        switch ($request->getMethod()) {
232
            case 'OPTIONS':
233
                return $this->handleOptions($request);
234
235
            case 'GET':
236
                return $this->handleGet($request);
237
238
            default:
239
                // 405 Method Not Allowed
240
                return GeneralUtility::makeInstance(Response::class)
241
                    ->withStatus(405);
242
        }
243
    }
244
}
245