diff --git a/src/Composer/Util/StreamContextFactory.php b/src/Composer/Util/StreamContextFactory.php index 1c3c20e44..c3b9e2773 100644 --- a/src/Composer/Util/StreamContextFactory.php +++ b/src/Composer/Util/StreamContextFactory.php @@ -43,6 +43,19 @@ final class StreamContextFactory $proxy = parse_url(!empty($_SERVER['http_proxy']) ? $_SERVER['http_proxy'] : $_SERVER['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'] : ''; @@ -64,48 +77,38 @@ final class StreamContextFactory $options['http']['proxy'] = $proxyURL; - // Handle 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($options['http']['proxy']); - } + // 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 request_fulluri and authentication if we still have a proxy to connect to - if (!empty($options['http']['proxy'])) { - // 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; + // 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); - 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}"); + // 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}"); } } } diff --git a/tests/Composer/Test/Util/StreamContextFactoryTest.php b/tests/Composer/Test/Util/StreamContextFactoryTest.php index 5415a8c11..83148bced 100644 --- a/tests/Composer/Test/Util/StreamContextFactoryTest.php +++ b/tests/Composer/Test/Util/StreamContextFactoryTest.php @@ -20,6 +20,8 @@ class StreamContextFactoryTest extends \PHPUnit_Framework_TestCase { unset($_SERVER['HTTP_PROXY']); unset($_SERVER['http_proxy']); + unset($_SERVER['HTTPS_PROXY']); + unset($_SERVER['https_proxy']); unset($_SERVER['no_proxy']); } @@ -27,6 +29,8 @@ class StreamContextFactoryTest extends \PHPUnit_Framework_TestCase { unset($_SERVER['HTTP_PROXY']); unset($_SERVER['http_proxy']); + unset($_SERVER['HTTPS_PROXY']); + unset($_SERVER['https_proxy']); unset($_SERVER['no_proxy']); } @@ -126,7 +130,7 @@ class StreamContextFactoryTest extends \PHPUnit_Framework_TestCase { $_SERVER['http_proxy'] = 'http://username:password@proxyserver.net'; - $context = StreamContextFactory::getContext('http://example.org', array('http' => array('method' => 'GET'))); + $context = StreamContextFactory::getContext('https://example.org', array('http' => array('method' => 'GET'))); $options = stream_context_get_options($context); $this->assertEquals(array('http' => array( @@ -139,6 +143,23 @@ class StreamContextFactoryTest extends \PHPUnit_Framework_TestCase )), $options); } + public function testHttpsProxyOverride() + { + $_SERVER['http_proxy'] = 'http://username:password@proxyserver.net'; + $_SERVER['http_proxy'] = 'https://woopproxy.net'; + + $context = StreamContextFactory::getContext('https://example.org', array('http' => array('method' => 'GET'))); + $options = stream_context_get_options($context); + + $this->assertEquals(array('http' => array( + 'proxy' => 'ssl://woopproxy.net:443', + 'request_fulluri' => true, + 'method' => 'GET', + 'max_redirects' => 20, + 'follow_location' => 1, + )), $options); + } + /** * @dataProvider dataSSLProxy */