Skip to content

Commit 1c5d9b6

Browse files
Merge branch '4.4' into 5.4
* 4.4: [Cache] Declaratively declare/hide DoctrineProvider to avoid breaking static analysis [HttpClient] Let curl handle Content-Length headers Improve testsuite [HttpClient] Move Content-Type after Content-Length [HttpClient] minor cs fix
2 parents 7dd6293 + 62e681f commit 1c5d9b6

File tree

4 files changed

+32
-9
lines changed

4 files changed

+32
-9
lines changed

CurlHttpClient.php

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,12 @@ public function request(string $method, string $url, array $options = []): Respo
205205
$options['headers'][] = 'Accept-Encoding: gzip'; // Expose only one encoding, some servers mess up when more are provided
206206
}
207207

208+
$hasContentLength = isset($options['normalized_headers']['content-length'][0]);
209+
208210
foreach ($options['headers'] as $header) {
211+
if ($hasContentLength && 0 === stripos($header, 'Content-Length:')) {
212+
continue; // Let curl handle Content-Length headers
213+
}
209214
if (':' === $header[-2] && \strlen($header) - 2 === strpos($header, ': ')) {
210215
// curl requires a special syntax to send empty headers
211216
$curlopts[\CURLOPT_HTTPHEADER][] = substr_replace($header, ';', -2);
@@ -232,7 +237,7 @@ public function request(string $method, string $url, array $options = []): Respo
232237
};
233238
}
234239

235-
if (isset($options['normalized_headers']['content-length'][0])) {
240+
if ($hasContentLength) {
236241
$curlopts[\CURLOPT_INFILESIZE] = substr($options['normalized_headers']['content-length'][0], \strlen('Content-Length: '));
237242
} elseif (!isset($options['normalized_headers']['transfer-encoding'])) {
238243
$curlopts[\CURLOPT_HTTPHEADER][] = 'Transfer-Encoding: chunked'; // Enable chunked request bodies

HttpClientTrait.php

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -88,12 +88,12 @@ private static function prepareRequest(?string $method, ?string $url, array $opt
8888
unset($options['json']);
8989

9090
if (!isset($options['normalized_headers']['content-type'])) {
91-
$options['normalized_headers']['content-type'] = [$options['headers'][] = 'Content-Type: application/json'];
91+
$options['normalized_headers']['content-type'] = ['Content-Type: application/json'];
9292
}
9393
}
9494

9595
if (!isset($options['normalized_headers']['accept'])) {
96-
$options['normalized_headers']['accept'] = [$options['headers'][] = 'Accept: */*'];
96+
$options['normalized_headers']['accept'] = ['Accept: */*'];
9797
}
9898

9999
if (isset($options['body'])) {
@@ -104,7 +104,6 @@ private static function prepareRequest(?string $method, ?string $url, array $opt
104104
&& ('' !== $h || ('' !== $options['body'] && !isset($options['normalized_headers']['transfer-encoding'])))
105105
) {
106106
$options['normalized_headers']['content-length'] = [substr_replace($h ?: 'Content-Length: ', \strlen($options['body']), 16)];
107-
$options['headers'] = array_merge(...array_values($options['normalized_headers']));
108107
}
109108
}
110109

@@ -146,11 +145,11 @@ private static function prepareRequest(?string $method, ?string $url, array $opt
146145
if (null !== $url) {
147146
// Merge auth with headers
148147
if (($options['auth_basic'] ?? false) && !($options['normalized_headers']['authorization'] ?? false)) {
149-
$options['normalized_headers']['authorization'] = [$options['headers'][] = 'Authorization: Basic '.base64_encode($options['auth_basic'])];
148+
$options['normalized_headers']['authorization'] = ['Authorization: Basic '.base64_encode($options['auth_basic'])];
150149
}
151150
// Merge bearer with headers
152151
if (($options['auth_bearer'] ?? false) && !($options['normalized_headers']['authorization'] ?? false)) {
153-
$options['normalized_headers']['authorization'] = [$options['headers'][] = 'Authorization: Bearer '.$options['auth_bearer']];
152+
$options['normalized_headers']['authorization'] = ['Authorization: Bearer '.$options['auth_bearer']];
154153
}
155154

156155
unset($options['auth_basic'], $options['auth_bearer']);
@@ -173,6 +172,14 @@ private static function prepareRequest(?string $method, ?string $url, array $opt
173172

174173
$options['max_duration'] = isset($options['max_duration']) ? (float) $options['max_duration'] : 0;
175174

175+
if (isset($options['normalized_headers']['content-length']) && $contentType = $options['normalized_headers']['content-type'] ?? null) {
176+
// Move Content-Type after Content-Length, see https://bugs.php.net/44603
177+
unset($options['normalized_headers']['content-type']);
178+
$options['normalized_headers']['content-type'] = $contentType;
179+
}
180+
181+
$options['headers'] = array_merge(...array_values($options['normalized_headers']));
182+
176183
return [$url, $options];
177184
}
178185

Tests/HttpClientTestCase.php

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -397,11 +397,22 @@ public function testNegativeTimeout()
397397
])->getStatusCode());
398398
}
399399

400+
public function testRedirectAfterPost()
401+
{
402+
$client = $this->getHttpClient(__FUNCTION__);
403+
404+
$response = $client->request('POST', 'http://localhost:8057/302/relative', [
405+
'body' => 'abc',
406+
]);
407+
408+
$this->assertSame(200, $response->getStatusCode());
409+
}
410+
400411
public function testNullBody()
401412
{
402-
$httpClient = $this->getHttpClient(__FUNCTION__);
413+
$client = $this->getHttpClient(__FUNCTION__);
403414

404-
$httpClient->request('POST', 'http://localhost:8057/post', [
415+
$client->request('POST', 'http://localhost:8057/post', [
405416
'body' => null,
406417
]);
407418

Tests/ScopingHttpClientTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ public function testMatchingUrlsAndOptions()
7373

7474
$response = $client->request('GET', 'http://example.com/foo-bar', ['json' => ['url' => 'http://example.com']]);
7575
$requestOptions = $response->getRequestOptions();
76-
$this->assertSame('Content-Type: application/json', $requestOptions['headers'][1]);
76+
$this->assertSame('Content-Type: application/json', $requestOptions['headers'][3]);
7777
$requestJson = json_decode($requestOptions['body'], true);
7878
$this->assertSame('http://example.com', $requestJson['url']);
7979
$this->assertSame('X-FooBar: '.$defaultOptions['.*/foo-bar']['headers']['X-FooBar'], $requestOptions['headers'][0]);

0 commit comments

Comments
 (0)