Skip to content

Commit 4e7fe5e

Browse files
[HttpClient] fix 303 after PUT and sending chunked requests
1 parent b83d683 commit 4e7fe5e

File tree

4 files changed

+31
-3
lines changed

4 files changed

+31
-3
lines changed

HttpClientTrait.php

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
namespace Symfony\Component\HttpClient;
1313

1414
use Symfony\Component\HttpClient\Exception\InvalidArgumentException;
15+
use Symfony\Component\HttpClient\Exception\TransportException;
1516

1617
/**
1718
* Provides the common logic from writing HttpClientInterface implementations.
@@ -89,8 +90,13 @@ private static function prepareRequest(?string $method, ?string $url, array $opt
8990

9091
if (\is_string($options['body'])
9192
&& (string) \strlen($options['body']) !== substr($h = $options['normalized_headers']['content-length'][0] ?? '', 16)
92-
&& ('' !== $h || ('' !== $options['body'] && !isset($options['normalized_headers']['transfer-encoding'])))
93+
&& ('' !== $h || '' !== $options['body'])
9394
) {
95+
if (isset($options['normalized_headers']['transfer-encoding'])) {
96+
unset($options['normalized_headers']['transfer-encoding']);
97+
$options['body'] = self::dechunk($options['body']);
98+
}
99+
94100
$options['normalized_headers']['content-length'] = [substr_replace($h ?: 'Content-Length: ', \strlen($options['body']), 16)];
95101
}
96102
}
@@ -329,6 +335,22 @@ private static function normalizeBody($body)
329335
return $body;
330336
}
331337

338+
private static function dechunk(string $body): string
339+
{
340+
$h = fopen('php://temp', 'w+');
341+
stream_filter_append($h, 'dechunk', \STREAM_FILTER_WRITE);
342+
fwrite($h, $body);
343+
$body = stream_get_contents($h, -1, 0);
344+
rewind($h);
345+
ftruncate($h, 0);
346+
347+
if (fwrite($h, '-') && '' !== stream_get_contents($h, -1, 0)) {
348+
throw new TransportException('Request body has broken chunked encoding.');
349+
}
350+
351+
return $body;
352+
}
353+
332354
/**
333355
* @param string|string[] $fingerprint
334356
*

NativeHttpClient.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,11 @@ public function request(string $method, string $url, array $options = []): Respo
8585

8686
$options['body'] = self::getBodyAsString($options['body']);
8787

88+
if (isset($options['normalized_headers']['transfer-encoding'])) {
89+
unset($options['normalized_headers']['transfer-encoding']);
90+
$options['headers'] = array_merge(...array_values($options['normalized_headers']));
91+
$options['body'] = self::dechunk($options['body']);
92+
}
8893
if ('' === $options['body'] && $hasBody && !$hasContentLength) {
8994
$options['headers'][] = 'Content-Length: 0';
9095
}

Response/CurlResponse.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -363,6 +363,7 @@ private static function parseHeaderLine($ch, string $data, array &$info, array &
363363
} elseif (303 === $info['http_code'] || ('POST' === $info['http_method'] && \in_array($info['http_code'], [301, 302], true))) {
364364
$info['http_method'] = 'HEAD' === $info['http_method'] ? 'HEAD' : 'GET';
365365
curl_setopt($ch, \CURLOPT_POSTFIELDS, '');
366+
curl_setopt($ch, \CURLOPT_CUSTOMREQUEST, $info['http_method']);
366367
}
367368
}
368369

Tests/MockHttpClientTest.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -96,12 +96,12 @@ public function testFixContentLength()
9696
$this->assertSame(['Content-Length: 7'], $requestOptions['normalized_headers']['content-length']);
9797

9898
$response = $client->request('POST', 'http://localhost:8057/post', [
99-
'body' => 'abc=def',
99+
'body' => "8\r\nSymfony \r\n5\r\nis aw\r\n6\r\nesome!\r\n0\r\n\r\n",
100100
'headers' => ['Transfer-Encoding: chunked'],
101101
]);
102102

103103
$requestOptions = $response->getRequestOptions();
104-
$this->assertFalse(isset($requestOptions['normalized_headers']['content-length']));
104+
$this->assertSame(['Content-Length: 19'], $requestOptions['normalized_headers']['content-length']);
105105

106106
$response = $client->request('POST', 'http://localhost:8057/post', [
107107
'body' => '',

0 commit comments

Comments
 (0)