Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
83.87% covered (warning)
83.87%
26 / 31
66.67% covered (warning)
66.67%
4 / 6
CRAP
0.00% covered (danger)
0.00%
0 / 1
HttpRequestExecutor
83.87% covered (warning)
83.87%
26 / 31
66.67% covered (warning)
66.67%
4 / 6
12.60
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
1
 setLogger
n/a
0 / 0
n/a
0 / 0
1
 execute
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 executePost
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 executeAndSave
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
 executeHttpRequest
90.48% covered (success)
90.48%
19 / 21
0.00% covered (danger)
0.00%
0 / 1
6.03
 buildUserAgentString
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2
3namespace FileImporter\Services\Http;
4
5use FileImporter\Exceptions\HttpRequestException;
6use MediaWiki\Http\HttpRequestFactory;
7use MWHttpRequest;
8use Psr\Log\LoggerAwareInterface;
9use Psr\Log\LoggerInterface;
10use Psr\Log\NullLogger;
11
12/**
13 * @license GPL-2.0-or-later
14 * @author Addshore
15 */
16class HttpRequestExecutor implements LoggerAwareInterface {
17
18    private HttpRequestFactory $httpRequestFactory;
19    private LoggerInterface $logger;
20    private int $maxFileSize;
21    private array $httpOptions;
22
23    /**
24     * @param HttpRequestFactory $httpRequestFactory
25     * @param array $httpOptions in the following format:
26     * [
27     *     'originalRequest' => WebRequest|string[] When in array form, it's expected to have the
28     *         keys 'ip' and 'userAgent', {@see MWHttpRequest::setOriginalRequest},
29     *     'proxy' => string|false,
30     *     'timeout' => int|false Timeout of HTTP requests in seconds, false for default,
31     * ]
32     * @param int $maxFileSize in bytes
33     */
34    public function __construct(
35        HttpRequestFactory $httpRequestFactory,
36        array $httpOptions,
37        int $maxFileSize
38    ) {
39        $this->httpRequestFactory = $httpRequestFactory;
40        $this->logger = new NullLogger();
41        $this->maxFileSize = $maxFileSize;
42        $this->httpOptions = $httpOptions;
43    }
44
45    /**
46     * @codeCoverageIgnore
47     */
48    public function setLogger( LoggerInterface $logger ): void {
49        $this->logger = $logger;
50    }
51
52    /**
53     * @throws HttpRequestException
54     */
55    public function execute( string $url, array $parameters = [] ): MWHttpRequest {
56        return $this->executeHttpRequest( wfAppendQuery( $url, $parameters ) );
57    }
58
59    public function executePost( string $url, array $postData ): MWHttpRequest {
60        return $this->executeHttpRequest( $url, null, $postData );
61    }
62
63    /**
64     * @throws HttpRequestException
65     */
66    public function executeAndSave( string $url, string $filePath ): MWHttpRequest {
67        $chunkSaver = new FileChunkSaver( $filePath, $this->maxFileSize );
68        $chunkSaver->setLogger( $this->logger );
69        return $this->executeHttpRequest( $url, [ $chunkSaver, 'saveFileChunk' ] );
70    }
71
72    /**
73     * @throws HttpRequestException
74     */
75    private function executeHttpRequest(
76        string $url,
77        ?callable $callback = null,
78        ?array $postData = null
79    ): MWHttpRequest {
80        $options = [
81            'logger' => $this->logger,
82            'followRedirects' => true,
83        ];
84        if ( isset( $this->httpOptions['originalRequest'] ) ) {
85            $options['originalRequest'] = $this->httpOptions['originalRequest'];
86        }
87        if ( !empty( $this->httpOptions['proxy'] ) ) {
88            $options['proxy'] = $this->httpOptions['proxy'];
89        }
90        $timeout = $this->httpOptions['timeout'] ?? false;
91        if ( $timeout !== false ) {
92            $options['timeout'] = $timeout;
93        }
94        if ( $postData !== null ) {
95            $options['method'] = 'POST';
96            $options['postData'] = $postData;
97        }
98        $options['userAgent'] = $this->buildUserAgentString();
99
100        /** @var MWHttpRequest $request */
101        $request = $this->httpRequestFactory->create( $url, $options, __METHOD__ );
102
103        $request->setCallback( $callback );
104
105        $status = $request->execute();
106        if ( !$status->isOK() ) {
107            throw new HttpRequestException( $status, $request );
108        }
109
110        return $request;
111    }
112
113    private function buildUserAgentString(): string {
114        // TODO: Pull URL and version from ExtensionRegistry.
115        return 'mw-ext-FileImporter/* (https://www.mediawiki.org/wiki/Extension:FileImporter)';
116    }
117
118}