Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit a4178f1

Browse filesBrowse files
committed
[HttpClient] add "max_duration" option
1 parent f64d3fc commit a4178f1
Copy full SHA for a4178f1

File tree

15 files changed

+81
-2
lines changed
Filter options

15 files changed

+81
-2
lines changed

‎src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php
+6Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1367,6 +1367,9 @@ private function addHttpClientSection(ArrayNodeDefinition $rootNode)
13671367
->floatNode('timeout')
13681368
->info('The idle timeout, defaults to the "default_socket_timeout" ini parameter.')
13691369
->end()
1370+
->floatNode('max_duration')
1371+
->info('The maximum execution time for the request+response as a whole.')
1372+
->end()
13701373
->scalarNode('bindto')
13711374
->info('A network interface name, IP address, a host name or a UNIX socket to bind to.')
13721375
->end()
@@ -1503,6 +1506,9 @@ private function addHttpClientSection(ArrayNodeDefinition $rootNode)
15031506
->floatNode('timeout')
15041507
->info('Defaults to "default_socket_timeout" ini parameter.')
15051508
->end()
1509+
->floatNode('max_duration')
1510+
->info('The maximum execution time for the request+response as a whole.')
1511+
->end()
15061512
->scalarNode('bindto')
15071513
->info('A network interface name, IP address, a host name or a UNIX socket to bind to.')
15081514
->end()

‎src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -495,6 +495,7 @@
495495
<xsd:attribute name="proxy" type="xsd:string" />
496496
<xsd:attribute name="no-proxy" type="xsd:string" />
497497
<xsd:attribute name="timeout" type="xsd:float" />
498+
<xsd:attribute name="max-duration" type="xsd:float" />
498499
<xsd:attribute name="bindto" type="xsd:string" />
499500
<xsd:attribute name="verify-peer" type="xsd:boolean" />
500501
<xsd:attribute name="verify-host" type="xsd:boolean" />

‎src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/http_client_full_default_options.php

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/http_client_full_default_options.php
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
'resolve' => ['localhost' => '127.0.0.1'],
1010
'proxy' => 'proxy.org',
1111
'timeout' => 3.5,
12+
'max_duration' => 10.1,
1213
'bindto' => '127.0.0.1',
1314
'verify_peer' => true,
1415
'verify_host' => true,

‎src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/http_client_full_default_options.xml

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/http_client_full_default_options.xml
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
proxy="proxy.org"
1212
bindto="127.0.0.1"
1313
timeout="3.5"
14+
max-duration="10.1"
1415
verify-peer="true"
1516
max-redirects="2"
1617
http-version="2.0"

‎src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/http_client_full_default_options.yml

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/http_client_full_default_options.yml
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ framework:
88
resolve: {'localhost': '127.0.0.1'}
99
proxy: proxy.org
1010
timeout: 3.5
11+
max_duration: 10.1
1112
bindto: 127.0.0.1
1213
verify_peer: true
1314
verify_host: true

‎src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1547,6 +1547,7 @@ public function testHttpClientFullDefaultOptions()
15471547
$this->assertSame(['localhost' => '127.0.0.1'], $defaultOptions['resolve']);
15481548
$this->assertSame('proxy.org', $defaultOptions['proxy']);
15491549
$this->assertSame(3.5, $defaultOptions['timeout']);
1550+
$this->assertSame(10.1, $defaultOptions['max_duration']);
15501551
$this->assertSame('127.0.0.1', $defaultOptions['bindto']);
15511552
$this->assertTrue($defaultOptions['verify_peer']);
15521553
$this->assertTrue($defaultOptions['verify_host']);

‎src/Symfony/Component/HttpClient/CHANGELOG.md

Copy file name to clipboardExpand all lines: src/Symfony/Component/HttpClient/CHANGELOG.md
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ CHANGELOG
99
* added support for NTLM authentication
1010
* added `$response->toStream()` to cast responses to regular PHP streams
1111
* made `Psr18Client` implement relevant PSR-17 factories and have streaming responses
12+
* added `max_duration` option
1213

1314
4.3.0
1415
-----

‎src/Symfony/Component/HttpClient/CurlHttpClient.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/HttpClient/CurlHttpClient.php
+4Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,10 @@ public function request(string $method, string $url, array $options = []): Respo
284284
$curlopts[file_exists($options['bindto']) ? CURLOPT_UNIX_SOCKET_PATH : CURLOPT_INTERFACE] = $options['bindto'];
285285
}
286286

287+
if (0 < $options['max_duration']) {
288+
$curlopts[CURLOPT_TIMEOUT_MS] = 1000 * $options['max_duration'];
289+
}
290+
287291
$ch = curl_init();
288292

289293
foreach ($curlopts as $opt => $value) {

‎src/Symfony/Component/HttpClient/HttpClientTrait.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/HttpClient/HttpClientTrait.php
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ private static function prepareRequest(?string $method, ?string $url, array $opt
113113
// Finalize normalization of options
114114
$options['http_version'] = (string) ($options['http_version'] ?? '') ?: null;
115115
$options['timeout'] = (float) ($options['timeout'] ?? ini_get('default_socket_timeout'));
116+
$options['max_duration'] = isset($options['max_duration']) ? (float) $options['max_duration'] : 0;
116117

117118
return [$url, $options];
118119
}

‎src/Symfony/Component/HttpClient/NativeHttpClient.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/HttpClient/NativeHttpClient.php
+17-1Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,12 @@ public function request(string $method, string $url, array $options = []): Respo
113113
if ($onProgress = $options['on_progress']) {
114114
// Memoize the last progress to ease calling the callback periodically when no network transfer happens
115115
$lastProgress = [0, 0];
116-
$onProgress = static function (...$progress) use ($onProgress, &$lastProgress, &$info) {
116+
$maxDuration = 0 < $options['max_duration'] ? $options['max_duration'] : INF;
117+
$onProgress = static function (...$progress) use ($onProgress, &$lastProgress, &$info, $maxDuration) {
118+
if ($info['total_time'] >= $maxDuration) {
119+
throw new TransportException(sprintf('Max duration was reached for "%s".', implode('', $info['url'])));
120+
}
121+
117122
$progressInfo = $info;
118123
$progressInfo['url'] = implode('', $info['url']);
119124
unset($progressInfo['size_body']);
@@ -127,6 +132,13 @@ public function request(string $method, string $url, array $options = []): Respo
127132

128133
$onProgress($lastProgress[0], $lastProgress[1], $progressInfo);
129134
};
135+
} elseif (0 < $options['max_duration']) {
136+
$maxDuration = $options['max_duration'];
137+
$onProgress = static function () use (&$info, $maxDuration): void {
138+
if ($info['total_time'] >= $maxDuration) {
139+
throw new TransportException(sprintf('Max duration was reached for "%s".', implode('', $info['url'])));
140+
}
141+
};
130142
}
131143

132144
// Always register a notification callback to compute live stats about the response
@@ -166,6 +178,10 @@ public function request(string $method, string $url, array $options = []): Respo
166178
$options['headers'][] = 'User-Agent: Symfony HttpClient/Native';
167179
}
168180

181+
if (0 < $options['max_duration']) {
182+
$options['timeout'] = min($options['max_duration'], $options['timeout']);
183+
}
184+
169185
$context = [
170186
'http' => [
171187
'protocol_version' => $options['http_version'] ?: '1.1',

‎src/Symfony/Component/HttpClient/Tests/MockHttpClientTest.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/HttpClient/Tests/MockHttpClientTest.php
+13Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,19 @@ protected function getHttpClient(string $testCase): HttpClientInterface
123123
$body = ['<1>', '', '<2>'];
124124
$responses[] = new MockResponse($body, ['response_headers' => $headers]);
125125
break;
126+
127+
case 'testMaxDuration':
128+
$mock = $this->getMockBuilder(ResponseInterface::class)->getMock();
129+
$mock->expects($this->any())
130+
->method('getContent')
131+
->willReturnCallback(static function (): void {
132+
usleep(100000);
133+
134+
throw new TransportException('Max duration was reached.');
135+
});
136+
137+
$responses[] = $mock;
138+
break;
126139
}
127140

128141
return new MockHttpClient($responses);

‎src/Symfony/Component/HttpClient/composer.json

Copy file name to clipboardExpand all lines: src/Symfony/Component/HttpClient/composer.json
+1-1Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
"require": {
2323
"php": "^7.1.3",
2424
"psr/log": "^1.0",
25-
"symfony/http-client-contracts": "^1.1.4",
25+
"symfony/http-client-contracts": "^1.1.6",
2626
"symfony/polyfill-php73": "^1.11"
2727
},
2828
"require-dev": {

‎src/Symfony/Contracts/HttpClient/HttpClientInterface.php

Copy file name to clipboardExpand all lines: src/Symfony/Contracts/HttpClient/HttpClientInterface.php
+2Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ interface HttpClientInterface
5353
'proxy' => null, // string - by default, the proxy-related env vars handled by curl SHOULD be honored
5454
'no_proxy' => null, // string - a comma separated list of hosts that do not require a proxy to be reached
5555
'timeout' => null, // float - the idle timeout - defaults to ini_get('default_socket_timeout')
56+
'max_duration' => 0, // float - the maximum execution time for the request+response as a whole;
57+
// a value lower than or equal to 0 means it is unlimited
5658
'bindto' => '0', // string - the interface or the local socket to bind to
5759
'verify_peer' => true, // see https://php.net/context.ssl for the following options
5860
'verify_host' => true,

‎src/Symfony/Contracts/HttpClient/Test/Fixtures/web/index.php

Copy file name to clipboardExpand all lines: src/Symfony/Contracts/HttpClient/Test/Fixtures/web/index.php
+10Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,16 @@
132132
header('Content-Encoding: gzip');
133133
echo str_repeat('-', 1000);
134134
exit;
135+
136+
case '/max-duration':
137+
ignore_user_abort(false);
138+
while (true) {
139+
echo '<1>';
140+
@ob_flush();
141+
flush();
142+
usleep(500);
143+
}
144+
exit;
135145
}
136146

137147
header('Content-Type: application/json', true);

‎src/Symfony/Contracts/HttpClient/Test/HttpClientTestCase.php

Copy file name to clipboardExpand all lines: src/Symfony/Contracts/HttpClient/Test/HttpClientTestCase.php
+21Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -778,4 +778,25 @@ public function testGzipBroken()
778778
$this->expectException(TransportExceptionInterface::class);
779779
$response->getContent();
780780
}
781+
782+
public function testMaxDuration()
783+
{
784+
$client = $this->getHttpClient(__FUNCTION__);
785+
$response = $client->request('GET', 'http://localhost:8057/max-duration', [
786+
'max_duration' => 0.1,
787+
]);
788+
789+
$start = microtime(true);
790+
791+
try {
792+
$response->getContent();
793+
} catch (TransportExceptionInterface $e) {
794+
$this->addToAssertionCount(1);
795+
}
796+
797+
$duration = microtime(true) - $start;
798+
799+
$this->assertGreaterThanOrEqual(0.1, $duration);
800+
$this->assertLessThan(0.2, $duration);
801+
}
781802
}

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.