Completed
Pull Request — master (#80)
by Hiraku
02:11
created

HttpGetRequest::getCurlOpts()   B

Complexity

Conditions 6
Paths 4

Size

Total Lines 30
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 20
CRAP Score 6.288

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 30
ccs 20
cts 25
cp 0.8
rs 8.439
cc 6
eloc 20
nc 4
nop 0
crap 6.288
1
<?php
2
/*
3
 * hirak/prestissimo
4
 * @author Hiraku NAKANO
5
 * @license MIT https://github.com/hirak/prestissimo
6
 */
7
namespace Hirak\Prestissimo;
8
9
use Composer\IO;
10
use Composer\Composer;
11
use Composer\Config as CConfig;
12
use Composer\Downloader;
13
use Composer\Util\NoProxyPattern;
14
15
/**
16
 * Simple Container for http-get request
17
 */
18
class HttpGetRequest
19
{
20
    public $origin;
21
    public $scheme = 'http';
22
    public $host = 'example.com';
23
    public $port = 80;
24
    public $path = '/';
25
26
    public $query = array();
27
    public $headers = array();
28
29
    public $curlOpts = array();
30
31
    public $username = null;
32
    public $password = null;
33
34
    public $maybePublic = false;
35
    public $verbose = false;
36
37
    /** @var CConfig */
38
    protected $config;
39
40
    /** @internal */
41
    const TOKEN_LABEL = 'access_token';
42
43
    /**
44
     * normalize url and authentication info
45
     * @param string $origin domain text
46
     * @param string $url
47
     * @param IO\IOInterface $io
48
     */
49 16
    public function __construct($origin, $url, IO\IOInterface $io)
50
    {
51 16
        $this->origin = $origin;
52 16
        $this->importURL($url);
53 16
        $this->setupProxy();
54
55 16
        if ($this->username && $this->password) {
56 3
            $io->setAuthentication($origin, $this->username, $this->password);
57 16
        } elseif ($io->hasAuthentication($origin)) {
58 1
            $auth = $io->getAuthentication($origin);
59 1
            $this->username = $auth['username'];
60 1
            $this->password = $auth['password'];
61 1
        }
62 16
    }
63
64 16
    private function setupProxy()
0 ignored issues
show
Coding Style introduced by
setupProxy uses the super-global variable $_SERVER which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
65
    {
66
        // no_proxy skip
67 16
        if (isset($_SERVER['no_proxy'])) {
68
            $pattern = new NoProxyPattern($_SERVER['no_proxy']);
69
            if ($pattern->test($this->getURL())) {
70
                unset($this->curlOpts[CURLOPT_PROXY]);
71
                return;
72
            }
73
        }
74
75 16
        $httpProxy = self::issetOr($_SERVER, 'http_proxy', 'HTTP_PROXY');
76 16
        if ($httpProxy && $this->scheme === 'http') {
77 1
            $this->curlOpts[CURLOPT_PROXY] = $httpProxy;
78 1
            return;
79
        }
80
81 15
        $httpsProxy = self::issetOr($_SERVER, 'https_proxy', 'HTTPS_PROXY');
82 15
        if ($httpsProxy && $this->scheme === 'https') {
83 1
            $this->curlOpts[CURLOPT_PROXY] = $httpsProxy;
84 1
            return;
85
        }
86
87 14
        unset($this->curlOpts[CURLOPT_PROXY]);
88 14
        unset($this->curlOpts[CURLOPT_PROXYUSERPWD]);
89 14
    }
90
91 16
    private static function issetOr(array $arr, $key1, $key2)
92
    {
93 16
        if (isset($arr[$key1])) {
94 2
            return $arr[$key1];
95
        }
96 16
        if (isset($arr[$key2])) {
97 2
            return $arr[$key2];
98
        }
99 15
        return null;
100
    }
101
102
    /**
103
     * @param string $url
104
     */
105 16
    public function importURL($url)
106
    {
107 16
        $struct = parse_url($url);
108
        // @codeCoverageIgnoreStart
109
        if (!$struct) {
110
            throw new \InvalidArgumentException("$url is not valid URL");
111
        }
112
        // @codeCoverageIgnoreEnd
113
114 16
        $this->scheme = self::setOr($struct, 'scheme');
115 16
        $this->host = self::setOr($struct, 'host');
116 16
        $this->port = self::setOr($struct, 'port');
117 16
        $this->path = self::setOr($struct, 'path');
118 16
        $this->username = self::setOr($struct, 'user');
119 16
        $this->password = self::setOr($struct, 'pass');
120
121 16
        if (!empty($struct['query'])) {
122 3
            parse_str($struct['query'], $this->query);
123 3
        }
124 16
    }
125
126
127
    /**
128
     * @param array $struct
129
     * @param string $key
130
     * @param string $default
131
     * @return mixed
132
     */
133 16
    private static function setOr(array $struct, $key, $default = null)
134
    {
135 16
        if (!empty($struct[$key])) {
136 16
            return $struct[$key];
137
        }
138
139 16
        return $default;
140
    }
141
142
    /**
143
     * process option for RemortFileSystem
144
     * @param array $options
145
     * @return void
146
     */
147 2
    public function processRFSOption(array $options)
148
    {
149 2
        if (isset($options[static::TOKEN_LABEL])) {
150 2
            $this->query['access_token'] = $options[static::TOKEN_LABEL];
151 2
        }
152 2
    }
153
154
    /**
155
     * @return array
156
     */
157 4
    public function getCurlOpts()
158
    {
159 4
        $headers = $this->headers;
160 4
        if ($this->username && $this->password) {
161 1
            foreach ($headers as $i => $header) {
162
                if (0 === strpos($header, 'Authorization:')) {
163
                    unset($headers[$i]);
164
                }
165 1
            }
166 1
            $headers[] = 'Authorization: Basic ' . base64_encode("$this->username:$this->password");
167 1
        }
168
169 4
        $curlOpts = $this->curlOpts + array(
170 4
            CURLOPT_HTTPGET => true,
171 4
            CURLOPT_FOLLOWLOCATION => true,
172 4
            CURLOPT_MAXREDIRS => 20,
173 4
            CURLOPT_ENCODING => 'gzip',
174 4
            CURLOPT_HTTPHEADER => $headers,
175 4
            CURLOPT_USERAGENT => $this->genUA(),
176 4
            CURLOPT_VERBOSE => (bool)$this->verbose,
177 4
            CURLOPT_URL => $this->getUrl(),
178 4
        );
179 4
        unset($curlOpts[CURLOPT_USERPWD]);
180
181 4
        if ($ciphers = $this->nssCiphers()) {
182
            $curlOpts[CURLOPT_SSL_CIPHER_LIST] = $ciphers;
183
        }
184
185 4
        return $curlOpts;
186
    }
187
188
    /**
189
     * enable ECC cipher suites in cURL/NSS
190
     */
191 4
    public function nssCiphers()
192
    {
193 4
        static $cache;
194 4
        if (isset($cache)) {
195 3
            return $cache;
196
        }
197 2
        $ver = curl_version();
198 2
        if (preg_match('/^NSS.*Basic ECC$/', $ver['ssl_version'])) {
199
            $ciphers = array();
200
            foreach (new \SplFileObject(__DIR__ . '/../../res/nss_ciphers.txt') as $line) {
201
                $line = trim($line);
202
                if ($line) {
203
                    $ciphers[] = $line;
204
                }
205
            }
206
            return $cache = implode(',', $ciphers);
207
        }
208 2
        return $cache = false;
209
    }
210
211 7
    public function getURL()
212
    {
213 7
        $url = '';
214 7
        if ($this->scheme) {
215 7
            $url .= "$this->scheme://";
216 7
        }
217 7
        $url .= $this->host;
218
219 7
        if ($this->port) {
220 3
            $url .= ":$this->port";
221 3
        }
222
223 7
        $url .= $this->path;
224
225 7
        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...
226 3
            $url .= '?' . http_build_query($this->query);
227 3
        }
228
229 7
        return $url;
230
    }
231
232 1
    public function setConfig(CConfig $config)
233
    {
234 1
        $this->config = $config;
235 1
    }
236
237
    /**
238
     * @return string
239
     */
240 4
    public static function genUA()
241
    {
242 4
        static $ua;
243 4
        if ($ua) {
244 3
            return $ua;
245
        }
246 2
        $phpVersion = defined('HHVM_VERSION') ? 'HHVM ' . HHVM_VERSION : 'PHP ' . PHP_VERSION;
247
248 2
        return $ua = sprintf(
249 2
            'Composer/%s (%s; %s; %s)',
250 2
            str_replace('@package_version@', 'source', Composer::VERSION),
251 2
            php_uname('s'),
252 2
            php_uname('r'),
253
            $phpVersion
254 2
        );
255
    }
256
}
257