diff --git a/config/config.sample.php b/config/config.sample.php index e1c620871081b..856655a3cb1dc 100644 --- a/config/config.sample.php +++ b/config/config.sample.php @@ -532,6 +532,13 @@ */ 'proxyuserpwd' => '', +/** + * Exclude list from hosts that should not be handled by the proxy. + * The format is: ``['host1', 'host2']``. + * + * Defaults to ``'null`` + */ +'noproxy' => null, /** * Deleted Items (trash bin) diff --git a/lib/private/Http/Client/Client.php b/lib/private/Http/Client/Client.php index c52d90ff8ecd8..e6227f99a1df1 100644 --- a/lib/private/Http/Client/Client.php +++ b/lib/private/Http/Client/Client.php @@ -65,8 +65,13 @@ public function __construct( } private function buildRequestOptions(array $options): array { + $proxyUri = $this->getProxyUri(); $defaults = [ - RequestOptions::PROXY => $this->getProxyUri(), + RequestOptions::PROXY => $proxyUri ? [ + 'http' => $proxyUri, + 'https' => $proxyUri, + 'no' => $this->getNoProxy(), + ] : null, RequestOptions::VERIFY => $this->getCertBundle(), RequestOptions::TIMEOUT => 30, ]; @@ -116,6 +121,19 @@ private function getProxyUri(): ?string { return $proxyUserPwd . '@' . $proxyHost; } + private function getNoProxy(): ?array { + $noProxy = $this->config->getSystemValue('noproxy', null); + + if ($noProxy === null) { + if ($noProxy = getenv('NO_PROXY')) { + $cleanedNoProxy = str_replace(' ', '', $noProxy); + return explode(',', $cleanedNoProxy); + } + return null; + } + return $noProxy; + } + /** * Sends a GET request * diff --git a/tests/lib/Http/Client/ClientTest.php b/tests/lib/Http/Client/ClientTest.php index 2a7bd6a185d5d..120bc4a3bfe8d 100644 --- a/tests/lib/Http/Client/ClientTest.php +++ b/tests/lib/Http/Client/ClientTest.php @@ -52,6 +52,15 @@ public function testGetProxyUri(): void { $this->assertNull(self::invokePrivate($this->client, 'getProxyUri')); } + public function testGetNoProxy(): void { + $this->config + ->expects($this->at(0)) + ->method('getSystemValue') + ->with('noproxy', null) + ->willReturn(null); + $this->assertNull(self::invokePrivate($this->client, 'getNoProxy')); + } + public function testGetProxyUriProxyHostEmptyPassword(): void { $this->config ->expects($this->at(0)) @@ -109,7 +118,11 @@ private function setUpDefaultRequestOptions(): void { $this->defaultRequestOptions = [ 'verify' => '/my/path.crt', - 'proxy' => 'foo', + 'proxy' => [ + 'http' => 'foo', + 'https' => 'foo', + 'no' => null + ], 'headers' => [ 'User-Agent' => 'Nextcloud Server Crawler', ], @@ -131,7 +144,11 @@ public function testGetWithOptions(): void { $options = array_merge($this->defaultRequestOptions, [ 'verify' => false, - 'proxy' => 'bar', + 'proxy' => [ + 'http' => 'foo', + 'https' => 'foo', + 'no' => null + ], ]); $this->guzzleClient->method('request') @@ -295,7 +312,47 @@ public function testSetDefaultOptionsWithProxy(): void { $this->assertEquals([ 'verify' => '/my/path.crt', - 'proxy' => 'foo', + 'proxy' => [ + 'http' => 'foo', + 'https' => 'foo', + 'no' => null + ], + 'headers' => [ + 'User-Agent' => 'Nextcloud Server Crawler' + ], + 'timeout' => 30, + ], self::invokePrivate($this->client, 'buildRequestOptions', [[]])); + } + + public function testSetDefaultOptionsWithProxyAndNoProxy(): void { + $this->config + ->expects($this->at(0)) + ->method('getSystemValue') + ->with('proxy', null) + ->willReturn('foo'); + $this->config + ->expects($this->at(1)) + ->method('getSystemValue') + ->with('proxyuserpwd', null) + ->willReturn(null); + $this->config + ->expects($this->at(2)) + ->method('getSystemValue') + ->with('noproxy', null) + ->willReturn(['bar']); + $this->certificateManager + ->expects($this->once()) + ->method('getAbsoluteBundlePath') + ->with(null) + ->willReturn('/my/path.crt'); + + $this->assertEquals([ + 'verify' => '/my/path.crt', + 'proxy' => [ + 'http' => 'foo', + 'https' => 'foo', + 'no' => ['bar'] + ], 'headers' => [ 'User-Agent' => 'Nextcloud Server Crawler' ],