Completed
Push — master ( f735c4...3825d6 )
by Hiraku
7s
created

HttpGetRequest::getCurlOpts()   B

Complexity

Conditions 5
Paths 2

Size

Total Lines 26
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 19
CRAP Score 5.0634

Importance

Changes 4
Bugs 1 Features 0
Metric Value
c 4
b 1
f 0
dl 0
loc 26
ccs 19
cts 22
cp 0.8636
rs 8.439
cc 5
eloc 18
nc 2
nop 0
crap 5.0634
1
<?php
2
/*
3
 * hirak/prestissimo
4
 * @author Hiraku NAKANO
5
 * @license MIT https://github.com/hirak/prestissimo
6
 */
7
namespace Hirak\Prestissimo\Aspects;
8
9
use Composer\IO;
10
use Composer\Composer;
11
use Composer\Config as CConfig;
12
use Composer\Downloader;
13
14
/**
15
 * Simple Container for http-get request
16
 */
17
class HttpGetRequest
18
{
19
    public $origin;
20
    public $scheme = 'http';
21
    public $host = 'example.com';
22
    public $port = 80;
23
    public $path = '/';
24
25
    public $query = array();
26
    public $headers = array();
27
28
    public $curlOpts = array();
29
30
    public $username = null;
31
    public $password = null;
32
33
    public $maybePublic = false;
34
    public $verbose = false;
35
36
    /** @var CConfig */
37
    protected $config;
38
39
    /** @internal */
40
    const TOKEN_LABEL = 'access_token';
41
42
    /**
43
     * normalize url and authentication info
44
     * @param string $origin domain text
45
     * @param string $url
46
     * @param IO\IOInterface $io
47
     */
48 35
    public function __construct($origin, $url, IO\IOInterface $io)
49
    {
50 35
        $this->origin = $origin;
51 35
        $this->importURL($url);
52
53 35
        if ($this->username && $this->password) {
54 3
            $io->setAuthentication($origin, $this->username, $this->password);
55 35
        } elseif ($io->hasAuthentication($origin)) {
56 2
            $auth = $io->getAuthentication($origin);
57 2
            $this->username = $auth['username'];
58 2
            $this->password = $auth['password'];
59 2
        }
60 35
    }
61
62
    /**
63
     * @param string $url
64
     */
65 35
    public function importURL($url)
66
    {
67 35
        $struct = parse_url($url);
68
        // @codeCoverageIgnoreStart
69
        if (!$struct) {
70
            throw new \InvalidArgumentException("$url is not valid URL");
71
        }
72
        // @codeCoverageIgnoreEnd
73
74 35
        $this->scheme = self::setOr($struct, 'scheme');
75 35
        $this->host = self::setOr($struct, 'host');
76 35
        $this->port = self::setOr($struct, 'port');
77 35
        $this->path = self::setOr($struct, 'path');
78 35
        $this->username = self::setOr($struct, 'user');
79 35
        $this->password = self::setOr($struct, 'pass');
80
81 35
        if (!empty($struct['query'])) {
82 9
            parse_str($struct['query'], $this->query);
83 9
        }
84 35
    }
85
86
87
    /**
88
     * @param array $struct
89
     * @param string $key
90
     * @param string $default
91
     * @return mixed
92
     */
93 35
    private static function setOr(array $struct, $key, $default = null)
94
    {
95 35
        if (!empty($struct[$key])) {
96 35
            return $struct[$key];
97
        }
98
99 35
        return $default;
100
    }
101
102
    /**
103
     * process option for RemortFileSystem
104
     * @param array $options
105
     * @return void
106
     */
107 8
    public function processRFSOption(array $options)
108
    {
109 8
        if (isset($options[static::TOKEN_LABEL])) {
110 2
            $this->query['access_token'] = $options[static::TOKEN_LABEL];
111 2
        }
112 8
    }
113
114
    /**
115
     * @return array
116
     */
117 10
    public function getCurlOpts()
118
    {
119 10
        $headers = $this->headers;
120 10
        if ($this->username && $this->password) {
121 1
            foreach ($headers as $i => $header) {
122
                if (0 === strpos($header, 'Authentication:')) {
123
                    unset($headers[$i]);
124
                }
125 1
            }
126 1
            $headers[] = 'Authentication: Basic ' . base64_encode("$this->username:$this->password");
127 1
        }
128
129 10
        $curlOpts = $this->curlOpts + array(
130 10
            CURLOPT_HTTPGET => true,
131 10
            CURLOPT_FOLLOWLOCATION => true,
132 10
            CURLOPT_MAXREDIRS => 20,
133 10
            CURLOPT_ENCODING => 'gzip',
134 10
            CURLOPT_HTTPHEADER => $headers,
135 10
            CURLOPT_USERAGENT => $this->genUA(),
136 10
            CURLOPT_VERBOSE => (bool)$this->verbose,
137 10
            CURLOPT_URL => $this->getUrl(),
138 10
        );
139 10
        unset($curlOpts[CURLOPT_USERPWD]);
140
141 10
        return $curlOpts;
142
    }
143
144 18
    public function getURL()
145
    {
146 18
        $url = '';
147 18
        if ($this->scheme) {
148 18
            $url .= "$this->scheme://";
149 18
        }
150 18
        $url .= $this->host;
151
152 18
        if ($this->port) {
153 9
            $url .= ":$this->port";
154 9
        }
155
156 18
        $url .= $this->path;
157
158 18
        if ($this->query) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->query of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
159 9
            $url .= '?' . http_build_query($this->query);
160 9
        }
161
162 18
        return $url;
163
    }
164
165 5
    public function setConfig(CConfig $config)
166
    {
167 5
        $this->config = $config;
168 5
    }
169
170 6
    public function promptAuth(HttpGetResponse $res, IO\IOInterface $io)
171
    {
172 6
        $httpCode = $res->info['http_code'];
173
        // 404s are only handled for github
174 6
        if (404 === $httpCode) {
175 1
            return false;
176
        }
177
178
        // fail if the console is not interactive
179 5
        if (!$io->isInteractive() && ($httpCode === 401 || $httpCode === 403)) {
180 3
            $message = "The '{$this->getURL()}' URL required authentication.\nYou must be using the interactive console to authenticate";
181 3
            throw new Downloader\TransportException($message, $httpCode);
182
        }
183
184
        // fail if we already have auth
185 2
        if ($io->hasAuthentication($this->origin)) {
186 1
            throw new Downloader\TransportException("Invalid credentials for '{$this->getURL()}', aborting.", $httpCode);
187
        }
188
189 1
        $io->overwrite("    Authentication required (<info>$this->host</info>):");
190 1
        $username = $io->ask('      Username: ');
191 1
        $password = $io->askAndHideAnswer('      Password: ');
192 1
        $io->setAuthentication($this->origin, $username, $password);
193 1
        return true;
194
    }
195
196
    /**
197
     * @internal
198
     * @param int $privateCode 404|403
199
     * @param Composer\Util\GitHub|Composer\Util\GitLab $util
200
     * @param HttpGetResponse $res
201
     * @param IO\IOInterface $io
202
     * @throws Composer\Downloader\TransportException
203
     * @return bool
204
     */
205 2
    public function promptAuthWithUtil($privateCode, $util, HttpGetResponse $res, IO\IOInterface $io)
206
    {
207 2
        $httpCode = $res->info['http_code'];
208 2
        $message = "\nCould not fetch {$this->getURL()}, enter your $this->origin credentials ";
209 2
        if ($privateCode === $httpCode) {
210 1
            $message .= 'to access private repos';
211 1
        } else {
212 1
            $message .= 'to go over the API rate limit';
213
        }
214 2
        if ($util->authorizeOAuth($this->origin)) {
215 1
            return true;
216
        }
217 2
        if ($io->isInteractive() &&
218 2
            $util->authorizeOAuthInteractively($this->origin, $message)) {
219 1
            return true;
220
        }
221
222 1
        throw new Downloader\TransportException("Could not authenticate against $this->origin", $httpCode);
223
    }
224
225
    /**
226
     * @return string
227
     */
228 10
    public static function genUA()
229
    {
230 10
        static $ua;
231 10
        if ($ua) {
232 9
            return $ua;
233
        }
234 2
        $phpVersion = defined('HHVM_VERSION') ? 'HHVM ' . HHVM_VERSION : 'PHP ' . PHP_VERSION;
235
236 2
        return $ua = sprintf(
237 2
            'Composer/%s (%s; %s; %s)',
238 2
            str_replace('@package_version@', 'source', Composer::VERSION),
239 2
            php_uname('s'),
240 2
            php_uname('r'),
241
            $phpVersion
242 2
        );
243
    }
244
}
245