123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176 |
- <?php
- /*
- * This file is part of Composer.
- *
- * (c) Nils Adermann <naderman@naderman.de>
- * Jordi Boggiano <j.boggiano@seld.be>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
- namespace Composer\Util;
- use Composer\Composer;
- /**
- * Allows the creation of a basic context supporting http proxy
- *
- * @author Jordan Alliot <jordan.alliot@gmail.com>
- * @author Markus Tacker <m@coderbyheart.de>
- */
- final class StreamContextFactory
- {
- /**
- * Creates a context supporting HTTP proxies
- *
- * @param string $url URL the context is to be used for
- * @param array $defaultOptions Options to merge with the default
- * @param array $defaultParams Parameters to specify on the context
- * @throws \RuntimeException if https proxy required and OpenSSL uninstalled
- * @return resource Default context
- */
- public static function getContext($url, array $defaultOptions = array(), array $defaultParams = array())
- {
- $options = array('http' => array(
- // specify defaults again to try and work better with curlwrappers enabled
- 'follow_location' => 1,
- 'max_redirects' => 20,
- ));
- // Handle HTTP_PROXY/http_proxy on CLI only for security reasons
- if (PHP_SAPI === 'cli' && (!empty($_SERVER['HTTP_PROXY']) || !empty($_SERVER['http_proxy']))) {
- $proxy = parse_url(!empty($_SERVER['http_proxy']) ? $_SERVER['http_proxy'] : $_SERVER['HTTP_PROXY']);
- }
- // Prefer CGI_HTTP_PROXY if available
- if (!empty($_SERVER['CGI_HTTP_PROXY'])) {
- $proxy = parse_url($_SERVER['CGI_HTTP_PROXY']);
- }
- // Override with HTTPS proxy if present and URL is https
- if (preg_match('{^https://}i', $url) && (!empty($_SERVER['HTTPS_PROXY']) || !empty($_SERVER['https_proxy']))) {
- $proxy = parse_url(!empty($_SERVER['https_proxy']) ? $_SERVER['https_proxy'] : $_SERVER['HTTPS_PROXY']);
- }
- // Remove proxy if URL matches no_proxy directive
- if (!empty($_SERVER['no_proxy']) && parse_url($url, PHP_URL_HOST)) {
- $pattern = new NoProxyPattern($_SERVER['no_proxy']);
- if ($pattern->test($url)) {
- unset($proxy);
- }
- }
- if (!empty($proxy)) {
- $proxyURL = isset($proxy['scheme']) ? $proxy['scheme'] . '://' : '';
- $proxyURL .= isset($proxy['host']) ? $proxy['host'] : '';
- if (isset($proxy['port'])) {
- $proxyURL .= ":" . $proxy['port'];
- } elseif ('http://' == substr($proxyURL, 0, 7)) {
- $proxyURL .= ":80";
- } elseif ('https://' == substr($proxyURL, 0, 8)) {
- $proxyURL .= ":443";
- }
- // http(s):// is not supported in proxy
- $proxyURL = str_replace(array('http://', 'https://'), array('tcp://', 'ssl://'), $proxyURL);
- if (0 === strpos($proxyURL, 'ssl:') && !extension_loaded('openssl')) {
- throw new \RuntimeException('You must enable the openssl extension to use a proxy over https');
- }
- $options['http']['proxy'] = $proxyURL;
- // enabled request_fulluri unless it is explicitly disabled
- switch (parse_url($url, PHP_URL_SCHEME)) {
- case 'http': // default request_fulluri to true
- $reqFullUriEnv = getenv('HTTP_PROXY_REQUEST_FULLURI');
- if ($reqFullUriEnv === false || $reqFullUriEnv === '' || (strtolower($reqFullUriEnv) !== 'false' && (bool) $reqFullUriEnv)) {
- $options['http']['request_fulluri'] = true;
- }
- break;
- case 'https': // default request_fulluri to true
- $reqFullUriEnv = getenv('HTTPS_PROXY_REQUEST_FULLURI');
- if ($reqFullUriEnv === false || $reqFullUriEnv === '' || (strtolower($reqFullUriEnv) !== 'false' && (bool) $reqFullUriEnv)) {
- $options['http']['request_fulluri'] = true;
- }
- break;
- }
- // add SNI opts for https URLs
- if ('https' === parse_url($url, PHP_URL_SCHEME)) {
- $options['ssl']['SNI_enabled'] = true;
- if (PHP_VERSION_ID < 50600) {
- $options['ssl']['SNI_server_name'] = parse_url($url, PHP_URL_HOST);
- }
- }
- // handle proxy auth if present
- if (isset($proxy['user'])) {
- $auth = urldecode($proxy['user']);
- if (isset($proxy['pass'])) {
- $auth .= ':' . urldecode($proxy['pass']);
- }
- $auth = base64_encode($auth);
- // Preserve headers if already set in default options
- if (isset($defaultOptions['http']['header'])) {
- if (is_string($defaultOptions['http']['header'])) {
- $defaultOptions['http']['header'] = array($defaultOptions['http']['header']);
- }
- $defaultOptions['http']['header'][] = "Proxy-Authorization: Basic {$auth}";
- } else {
- $options['http']['header'] = array("Proxy-Authorization: Basic {$auth}");
- }
- }
- }
- $options = array_replace_recursive($options, $defaultOptions);
- if (isset($options['http']['header'])) {
- $options['http']['header'] = self::fixHttpHeaderField($options['http']['header']);
- }
- if (defined('HHVM_VERSION')) {
- $phpVersion = 'HHVM ' . HHVM_VERSION;
- } else {
- $phpVersion = 'PHP ' . PHP_MAJOR_VERSION . '.' . PHP_MINOR_VERSION . '.' . PHP_RELEASE_VERSION;
- }
- if (!isset($options['http']['header']) || false === strpos(strtolower(implode('', $options['http']['header'])), 'user-agent')) {
- $options['http']['header'][] = sprintf(
- 'User-Agent: Composer/%s (%s; %s; %s)',
- Composer::VERSION === '@package_version@' ? 'source' : Composer::VERSION,
- php_uname('s'),
- php_uname('r'),
- $phpVersion
- );
- }
- return stream_context_create($options, $defaultParams);
- }
- /**
- * A bug in PHP prevents the headers from correctly being sent when a content-type header is present and
- * NOT at the end of the array
- *
- * This method fixes the array by moving the content-type header to the end
- *
- * @link https://bugs.php.net/bug.php?id=61548
- * @param $header
- * @return array
- */
- private static function fixHttpHeaderField($header)
- {
- if (!is_array($header)) {
- $header = explode("\r\n", $header);
- }
- uasort($header, function ($el) {
- return preg_match('{^content-type}i', $el) ? 1 : -1;
- });
- return $header;
- }
- }
|