From 1e7caef0a95f9f2435f73d5234f05cb2fdb62683 Mon Sep 17 00:00:00 2001 From: Yoann MOROCUTTI Date: Wed, 26 Apr 2023 10:27:28 +0200 Subject: [PATCH 1/2] fix: Use StreamContextFactory from composer package to create stream context as this method already support HTTP proxy settings [repman-http-proxy] --- src/Service/Downloader/ReactDownloader.php | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/src/Service/Downloader/ReactDownloader.php b/src/Service/Downloader/ReactDownloader.php index 5fc964e7..46b3119e 100644 --- a/src/Service/Downloader/ReactDownloader.php +++ b/src/Service/Downloader/ReactDownloader.php @@ -7,6 +7,7 @@ use Buddy\Repman\Kernel; use Buddy\Repman\Service\Downloader; use Clue\React\Mq\Queue; +use Composer\Util\StreamContextFactory; use Munus\Control\Option; use Psr\Http\Message\ResponseInterface; use React\EventLoop\Loop; @@ -39,7 +40,7 @@ public function getContents(string $url, array $headers = [], callable $notFound { $retries = 3; do { - $stream = @fopen($url, 'r', false, $this->createContext($headers)); + $stream = @fopen($url, 'r', false, $this->createContext($url, $headers)); if ($stream !== false) { return Option::some($stream); } @@ -88,15 +89,18 @@ public function run(): void * * @return resource */ - private function createContext(array $headers = []) + private function createContext(string $url, array $headers = []) { - return stream_context_create([ - 'http' => [ - 'header' => array_merge([sprintf('User-Agent: %s', $this->userAgent())], $headers), - 'follow_location' => 1, - 'max_redirects' => 20, - ], - ]); + return StreamContextFactory::getContext( + $url, + [ + 'http' => [ + 'header' => array_merge([sprintf('User-Agent: %s', $this->userAgent())], $headers), + 'follow_location' => 1, + 'max_redirects' => 20, + ], + ] + ); } private function userAgent(): string From 8d05acdcd20434f34b9ac6f663cbd172924710d3 Mon Sep 17 00:00:00 2001 From: Yoann MOROCUTTI Date: Wed, 7 Jun 2023 11:09:42 +0200 Subject: [PATCH 2/2] test(Unit/Service/Downloader/ReactDownloaderTest.php): Create multiple tests to check that stream context is correctly built when using http proxy [repman-http-proxy] --- .../Downloader/ReactDownloaderTest.php | 86 +++++++++++++++++++ 1 file changed, 86 insertions(+) diff --git a/tests/Unit/Service/Downloader/ReactDownloaderTest.php b/tests/Unit/Service/Downloader/ReactDownloaderTest.php index f6d8b2a1..9cd0b580 100644 --- a/tests/Unit/Service/Downloader/ReactDownloaderTest.php +++ b/tests/Unit/Service/Downloader/ReactDownloaderTest.php @@ -4,6 +4,7 @@ namespace Buddy\Repman\Tests\Unit\Service\Downloader; +use Buddy\Repman\Kernel; use Buddy\Repman\Service\Downloader\ReactDownloader; use Munus\Control\Option; use PHPUnit\Framework\TestCase; @@ -55,4 +56,89 @@ public function testAsyncContent(): void }); $downloader->run(); } + + /** + * @throws \ReflectionException + */ + public function testStreamContext(): void + { + $_SERVER['HTTP_PROXY'] = $_SERVER['http_proxy'] = $_SERVER['HTTPS_PROXY'] = $_SERVER['https_proxy'] = null; + + $downloader = new ReactDownloader(); + $createContextMethod = new \ReflectionMethod(ReactDownloader::class, 'createContext'); + $createContextMethod->setAccessible(true); + + $context = $createContextMethod->invoke($downloader, 'https://repman.io'); + $options = stream_context_get_options($context); + self::assertEquals(20, $options['http']['max_redirects']); + self::assertEquals(1, $options['http']['follow_location']); + self::assertEquals( + sprintf( + 'User-Agent: Repman/%s (%s; %s; %s)', + Kernel::REPMAN_VERSION, + php_uname('s'), + php_uname('r'), + 'PHP '.PHP_MAJOR_VERSION.'.'.PHP_MINOR_VERSION.'.'.PHP_RELEASE_VERSION + ), + $options['http']['header'][0] + ); + self::assertArrayNotHasKey('proxy', $options['http']); + } + + /** + * @throws \ReflectionException + */ + public function testStreamContextHttpProxy(): void + { + $_SERVER['HTTP_PROXY'] = $_SERVER['http_proxy'] = $_SERVER['HTTPS_PROXY'] = $_SERVER['https_proxy'] = 'http://proxy.repman.io'; + + $downloader = new ReactDownloader(); + $createContextMethod = new \ReflectionMethod(ReactDownloader::class, 'createContext'); + $createContextMethod->setAccessible(true); + + $context = $createContextMethod->invoke($downloader, 'https://repman.io'); + $options = stream_context_get_options($context); + self::assertEquals(20, $options['http']['max_redirects']); + self::assertEquals(1, $options['http']['follow_location']); + self::assertEquals( + sprintf( + 'User-Agent: Repman/%s (%s; %s; %s)', + Kernel::REPMAN_VERSION, + php_uname('s'), + php_uname('r'), + 'PHP '.PHP_MAJOR_VERSION.'.'.PHP_MINOR_VERSION.'.'.PHP_RELEASE_VERSION + ), + $options['http']['header'][0] + ); + self::assertEquals('tcp://proxy.repman.io:80', $options['http']['proxy']); + } + + /** + * @throws \ReflectionException + */ + public function testStreamContextNoProxy(): void + { + $_SERVER['HTTP_PROXY'] = $_SERVER['http_proxy'] = $_SERVER['HTTPS_PROXY'] = $_SERVER['https_proxy'] = 'http://proxy.repman.io'; + $_SERVER['NO_PROXY'] = $_SERVER['no_proxy'] = '.repman.io'; + + $downloader = new ReactDownloader(); + $createContextMethod = new \ReflectionMethod(ReactDownloader::class, 'createContext'); + $createContextMethod->setAccessible(true); + + $context = $createContextMethod->invoke($downloader, 'https://repman.io'); + $options = stream_context_get_options($context); + self::assertEquals(20, $options['http']['max_redirects']); + self::assertEquals(1, $options['http']['follow_location']); + self::assertEquals( + sprintf( + 'User-Agent: Repman/%s (%s; %s; %s)', + Kernel::REPMAN_VERSION, + php_uname('s'), + php_uname('r'), + 'PHP '.PHP_MAJOR_VERSION.'.'.PHP_MINOR_VERSION.'.'.PHP_RELEASE_VERSION + ), + $options['http']['header'][0] + ); + self::assertArrayNotHasKey('proxy', $options['http']); + } }