* Jordi Boggiano * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Composer\Test\Util\Http; use Composer\Util\Http\ProxyManager; use Composer\Test\TestCase; /** * @phpstan-import-type contextOptions from \Composer\Util\Http\RequestProxy */ class ProxyManagerTest extends TestCase { // isTransitional can be removed after the transition period /** @var bool */ private $isTransitional = true; protected function setUp(): void { unset( $_SERVER['HTTP_PROXY'], $_SERVER['http_proxy'], $_SERVER['HTTPS_PROXY'], $_SERVER['https_proxy'], $_SERVER['NO_PROXY'], $_SERVER['no_proxy'], $_SERVER['CGI_HTTP_PROXY'], $_SERVER['cgi_http_proxy'] ); ProxyManager::reset(); } protected function tearDown(): void { parent::tearDown(); unset( $_SERVER['HTTP_PROXY'], $_SERVER['http_proxy'], $_SERVER['HTTPS_PROXY'], $_SERVER['https_proxy'], $_SERVER['NO_PROXY'], $_SERVER['no_proxy'], $_SERVER['CGI_HTTP_PROXY'], $_SERVER['cgi_http_proxy'] ); ProxyManager::reset(); } public function testInstantiation(): void { $originalInstance = ProxyManager::getInstance(); $sameInstance = ProxyManager::getInstance(); self::assertTrue($originalInstance === $sameInstance); ProxyManager::reset(); $newInstance = ProxyManager::getInstance(); self::assertFalse($sameInstance === $newInstance); } public function testGetProxyForRequestThrowsOnBadProxyUrl(): void { $_SERVER['http_proxy'] = 'localhost'; $proxyManager = ProxyManager::getInstance(); self::expectException('Composer\Downloader\TransportException'); $proxyManager->getProxyForRequest('http://example.com'); } /** * @dataProvider dataCaseOverrides * * @param array $server * @param non-empty-string $url */ public function testLowercaseOverridesUppercase(array $server, string $url, string $expectedUrl): void { $_SERVER = array_merge($_SERVER, $server); $proxyManager = ProxyManager::getInstance(); $proxy = $proxyManager->getProxyForRequest($url); self::assertSame($expectedUrl, $proxy->getStatus()); } /** * @return list, 1: string, 2: string}> */ public static function dataCaseOverrides(): array { // server, url, expectedUrl return [ [['HTTP_PROXY' => 'http://upper.com', 'http_proxy' => 'http://lower.com'], 'http://repo.org', 'http://lower.com:80'], [['CGI_HTTP_PROXY' => 'http://upper.com', 'cgi_http_proxy' => 'http://lower.com'], 'http://repo.org', 'http://lower.com:80'], [['HTTPS_PROXY' => 'http://upper.com', 'https_proxy' => 'http://lower.com'], 'https://repo.org', 'http://lower.com:80'], ]; } /** * @dataProvider dataCGIProxy * * @param array $server */ public function testCGIProxyIsOnlyUsedWhenNoHttpProxy(array $server, string $expectedUrl): void { $_SERVER = array_merge($_SERVER, $server); $proxyManager = ProxyManager::getInstance(); $proxy = $proxyManager->getProxyForRequest('http://repo.org'); self::assertSame($expectedUrl, $proxy->getStatus()); } /** * @return list, 1: string}> */ public static function dataCGIProxy(): array { // server, expectedUrl return [ [['CGI_HTTP_PROXY' => 'http://cgi.com:80'], 'http://cgi.com:80'], [['http_proxy' => 'http://http.com:80', 'CGI_HTTP_PROXY' => 'http://cgi.com:80'], 'http://http.com:80'], ]; } public function testNoHttpProxyDoesNotUseHttpsProxy(): void { $_SERVER['https_proxy'] = 'https://proxy.com:443'; $proxyManager = ProxyManager::getInstance(); $proxy = $proxyManager->getProxyForRequest('http://repo.org'); self::assertSame('', $proxy->getStatus()); } public function testNoHttpsProxyDoesNotUseHttpProxy(): void { $_SERVER['http_proxy'] = 'http://proxy.com:80'; // This can be removed after the transition period. // An empty https_proxy value prevents using any http_proxy if ($this->isTransitional) { $_SERVER['https_proxy'] = ''; } $proxyManager = ProxyManager::getInstance(); $proxy = $proxyManager->getProxyForRequest('https://repo.org'); self::assertSame('', $proxy->getStatus()); } /** * This test can be removed after the transition period */ public function testTransitional(): void { $_SERVER['http_proxy'] = 'http://proxy.com:80'; $proxyManager = ProxyManager::getInstance(); $proxy = $proxyManager->getProxyForRequest('https://repo.org'); self::assertSame('http://proxy.com:80', $proxy->getStatus()); self::assertTrue($proxyManager->needsTransitionWarning()); } /** * @dataProvider dataRequest * * @param array $server * @param non-empty-string $url * @param ?contextOptions $options */ public function testGetProxyForRequest(array $server, string $url, ?array $options, string $status, bool $excluded): void { $_SERVER = array_merge($_SERVER, $server); $proxyManager = ProxyManager::getInstance(); $proxy = $proxyManager->getProxyForRequest($url); self::assertSame($options, $proxy->getContextOptions()); self::assertSame($status, $proxy->getStatus()); self::assertSame($excluded, $proxy->isExcludedByNoProxy()); } /** * Tests context options. curl options are tested in RequestProxyTest.php * * @return list, 1: string, 2: ?contextOptions, 3: string, 4: bool}> */ public static function dataRequest(): array { $server = [ 'http_proxy' => 'http://user:p%40ss@proxy.com', 'https_proxy' => 'https://proxy.com:443', 'no_proxy' => 'other.repo.org', ]; // server, url, options, status, excluded return [ [[], 'http://repo.org', null, '', false], [$server, 'http://repo.org', ['http' => [ 'proxy' => 'tcp://proxy.com:80', 'header' => 'Proxy-Authorization: Basic dXNlcjpwQHNz', 'request_fulluri' => true, ]], 'http://***:***@proxy.com:80', false, ], [$server, 'https://repo.org', ['http' => [ 'proxy' => 'ssl://proxy.com:443', ]], 'https://proxy.com:443', false, ], [$server, 'https://other.repo.org', null, 'excluded by no_proxy', true], ]; } }