Description
Symfony version(s) affected
7.2.2+
Description
Since this change in 7.2.2 that allows for streaming the HTTP request body with CURLOPT_READFUNCTION
instead of CURLOPT_POSTFIELDS
: bbdf0e0 (introduced by @nicolas-grekas)
We are receiving: CURLE_SEND_FAIL_REWIND (65)
When doing a send operation curl had to rewind the data to retransmit, but the rewinding operation failed.
Unable to read stream contents: Necessary data rewind wasn't possible for "http://xxxx"
Exception trace:
at /app/vendor/symfony/http-client/Response/CurlResponse.php:319
Symfony\Component\HttpClient\Response\CurlResponse::perform() at /app/vendor/symfony/http-client/Response/TransportResponseTrait.php:167
Symfony\Component\HttpClient\Response\CurlResponse::stream() at /app/vendor/symfony/http-client/Response/StreamWrapper.php:123
Symfony\Component\HttpClient\Response\StreamWrapper->stream_read() at n/a:n/a
stream_get_contents() at /app/vendor/nyholm/psr7/src/Stream.php:270
Nyholm\Psr7\Stream->getContents() at /app/vendor/nyholm/psr7/src/StreamTrait.php:23
Nyholm\Psr7\Stream->__toString() at /app/vendor/php-soap/psr18-transport/src/HttpBinding/Psr7Converter.php:41
This happens on the second request. The first request gets handled as expected.
The server is private and cannot be shared.
Some details:
- It's a SOAP 1.1 server
- It has NTLM authentication (meaning curl will perform additional NTLM authentication requests / responses internally)
This happens on:
- PHP 8.3
- curl version 8.11.0
Our best guess is that using CURLOPT_READFUNCTION
in combination with NTLM is resulting in this specific error inside curl
.
When we use this code-change on other endpoints, it works as expected.
We found following issues on the web which might or might not be related:
- https://bugs.php.net/bug.php?id=80518
- https://discussions.unity.com/t/curl-error-65-neccessary-data-rewind-wasnt-possible/849943/5
How to reproduce
As mentioned, the server is private and cannot be shared.
The configuration of the client is straight forward and looks like this:
use Symfony\Component\HttpClient\CurlHttpClient;
use Symfony\Component\HttpClient\HttplugClient;
$client = new HttplugClient(
new CurlHttpClient([
'auth_ntlm' => 'user:password',
])
)
ℹ️ The first request that is sent over the shared connection will succeed as usual. The second request will fail with the error Unable to read stream contents: Necessary data rewind wasn't possible
The request is a SOAP request that looks like this:
$headers = [
0 => "Host: host:port"
1 => "SOAPAction: "..."
2 => "Content-Type: text/xml; charset="utf-8""
3 => "Content-Length: 376"
4 => "Accept: */*"
5 => "User-Agent: Symfony HttpClient (Curl)"
6 => "Accept-Encoding: gzip"
7 => "expect:"
8 => "Transfer-Encoding:"
];
$body = <<<EOXML
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Body xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<tns:AppInitialise xmlns:tns="uri:foo">
<tns:useragent>foo</tns:useragent>
<tns:languagecode>NLB</tns:languagecode>
</tns:AppInitialise>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
EOXML;
which results in following curl options:
^ array:31 [
10002 => "http://..."
121 => true
181 => 3
182 => 3
52 => true
68 => 20
10031 => ""
13 => 0
10004 => null
10177 => ""
64 => true
81 => 2
10065 => null
10097 => null
10083 => null
10025 => null
10087 => null
10026 => null
172 => false
32 => 6
107 => 8
84 => 2
10005 => "user:password"
91 => false
229 => 1
10036 => "POST"
47 => true
10023 => array:9 [
0 => "Host: horst:port"
1 => "SOAPAction: "action""
2 => "Content-Type: text/xml; charset="utf-8""
3 => "Content-Length: 376"
4 => "Accept: */*"
5 => "User-Agent: Symfony HttpClient (Curl)"
6 => "Accept-Encoding: gzip"
7 => "expect:"
8 => "Transfer-Encoding:"
]
20012 => Closure($ch, $fd, $length)^ {#39732
class: "Symfony\Component\HttpClient\CurlHttpClient"
use: {
$body: Closure(int $size)^ {#39649 …}
$eof: false
$buffer: ""
}
}
14 => 502
10100 => CurlShareHandle {#39677}
]
Let me know if I can add anything to this issue report to make it easier for you to reproduce or figure out what is going wrong.
Possible Solution
We are not sure what is causing this specific error. It might be an issue in curl
instead of symfony/http-client.
Following configuration got us around the actual issue, but seems like a bad idea performance-wise.
CURLOPT_FORBID_REUSE - make connection get closed at once after use
$client = new HttplugClient(
new CurlHttpClient([
'auth_ntlm' => 'user:password',
'extra' => [
'curl' => [
\CURLOPT_FORBID_REUSE => true,
]
]
])
);
Another option around the problem would be to introduce a new CurlHttpClient
option.
Something like $options['stream_request']
which has a default of true to use the CURLOPT_READFUNCTION
implementation, but can be set to false for using the CURLOPT_POSTFIELDS
logic.
Additional Context
No response