8000 bug #59062 [HttpClient] Always set CURLOPT_CUSTOMREQUEST to the corre… · symfony/symfony@ffc4dc6 · GitHub
[go: up one dir, main page]

Skip to content

Commit ffc4dc6

Browse files
bug #59062 [HttpClient] Always set CURLOPT_CUSTOMREQUEST to the correct HTTP method in CurlHttpClient (KurtThiemann)
This PR was submitted for the 7.2 branch but it was squashed and merged into the 6.4 branch instead. Discussion ---------- [HttpClient] Always set CURLOPT_CUSTOMREQUEST to the correct HTTP method in CurlHttpClient | Q | A | ------------- | --- | Branch? | 7.2 | Bug fix? | yes | New feature? | no | Deprecations? | no | Issues | Fix #59043 | License | MIT Currently, when sending a `HEAD` request and using a closure as the request body using `CurlHttpClient`, a `PUT` request instead of a HEAD request will be sent. This is because `CURLOPT_NOBODY` is set to make a `HEAD` request, but is overwritten by `CURLOPT_UPLOAD`, which is necessary to make curl call `CURLOPT_READFUNCTION`, but also sets the HTTP method to `PUT`. The solution I am suggesting is to just always set `CURLOPT_CUSTOMREQUEST`, so the correct HTTP method will be used, no matter what other options are added. This is mainly an issue in the PSR-18 wrapper, since PSR requests always have a request body stream, even if the body is empty. Since #58856, `Psr18Client` always passes a closure as the request body to the underlying HTTP client instead of just reading the entire request body stream into a string, as that made it impossible to upload large files. While it would be possible to add an exception to `Psr18Client` to only add the request body if the method used commonly has a body, I think the better solution is to ensure that `CurlHttpClient` always actually sends the type of request that it is asked to send. Commits ------- bfdf5d9 [HttpClient] Always set CURLOPT_CUSTOMREQUEST to the correct HTTP method in CurlHttpClient
2 parents 8c8bab2 + bfdf5d9 commit ffc4dc6

File tree

4 files changed

+25
-3
lines changed

4 files changed

+25
-3
lines changed

src/Symfony/Component/HttpClient/CurlHttpClient.php

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -197,13 +197,12 @@ public function request(string $method, string $url, array $options = []): Respo
197197
$curlopts[\CURLOPT_RESOLVE] = $resolve;
198198
}
199199

200+
$curlopts[\CURLOPT_CUSTOMREQUEST] = $method;
200201
if ('POST' === $method) {
201202
// Use CURLOPT_POST to have browser-like POST-to-GET redirects for 301, 302 and 303
202203
$curlopts[\CURLOPT_POST] = true;
203204
} elseif ('HEAD' === $method) {
204205
$curlopts[\CURLOPT_NOBODY] = true;
205-
} else {
206-
$curlopts[\CURLOPT_CUSTOMREQUEST] = $method;
207206
}
208207

209208
if ('\\' !== \DIRECTORY_SEPARATOR && $options['timeout'] < 1) {

src/Symfony/Component/HttpClient/Tests/HttpClientTestCase.php

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -651,4 +651,26 @@ public function testDefaultContentType()
651651

652652
$this->assertSame(['abc' => 'def', 'content-type' => 'application/json', 'REQUEST_METHOD' => 'POST'], $response->toArray());
653653
}
654+
655+
public function testHeadRequestWithClosureBody()
656+
{
657+
$p = TestHttpServer::start(8067);
658+
659+
try {
660+
$client = $this->getHttpClient(__FUNCTION__);
661+
662+
$response = $client->request('HEAD', 'http://localhost:8057/head', [
663+
'body' => fn () => '',
664+
]);
665+
$headers = $response->getHeaders();
666+
} finally {
667+
$p->stop();
668+
}
669+
670+
$this->assertArrayHasKey('x-request-vars', $headers);
671+
672+
$vars = json_decode($headers['x-request-vars'][0], true);
673+
$this->assertIsArray($vars);
674+
$this->assertSame('HEAD', $vars['REQUEST_METHOD']);
675+
}
654676
}

src/Symfony/Component/HttpClient/composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
"php": ">=8.1",
2626
"psr/log": "^1|^2|^3",
2727
"symfony/deprecation-contracts": "^2.5|^3",
28-
"symfony/http-client-contracts": "~3.4.3|^3.5.1",
28+
"symfony/http-client-contracts": "~3.4.4|^3.5.2",
2929
"symfony/service-contracts": "^2.5|^3"
3030
},
3131
"require-dev": {

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
exit;
4343

4444
case '/head':
45+
header('X-Request-Vars: '.json_encode($vars, \JSON_UNESCAPED_SLASHES | \JSON_UNESCAPED_UNICODE));
4546
header('Content-Length: '.strlen($json), true);
4647
break;
4748

0 commit comments

Comments
 (0)
0