Description
I did this
I have a curl easy handle that is used with curl_easy_perform
to periodically POST data. The easy handle is reused between requests to take advantage of the cached connection. Data is attached to the upload via CURLOPT_READFUNCTION
. However, a CURLOPT_SEEKFUNCTION
is not configured.
To avoid stalled connections, CURLOPT_LOW_SPEED_LIMIT
is set:
curl_easy_setopt(easy, CURLOPT_LOW_SPEED_LIMIT, 1L);
curl_easy_setopt(easy, CURLOPT_LOW_SPEED_TIME, 60L);
Occasionally, I'll see the following behavior:
- (OK) A request succeeds and the connection is cached
- (OK) A request fails, as the cached connection is actually closed and the request can't be rewound
- (NOT OK) All subsequent requests successfully upload all data, but hit
CURLOPT_LOW_SPEED_LIMIT
before receiving the response
See annotated_log.txt for annotated logs from CURLOPT_DEBUGFUNCTION
.
Suspicion:
I think the behavior in part 3 above occurs because the rewind
bit is still set on the request from the previous (failed) request. When Curl_init_do
is called at the start of the request in part 3, it calls Curl_req_start
-> Curl_req_soft_reset
-> Curl_client_start
which will attempt to perform a rewind. This rewind is guaranteed to fail, because I'm using a read function and don't have a seek function.
Popping back to Curl_init_do
, if Curl_req_start
fails Curl_speedinit
will not be called:
Lines 3853 to 3858 in 657aae7
None of the call sites of Curl_init_do
check the result of the function:
Lines 3555 to 3558 in 657aae7
Lines 3706 to 3714 in 657aae7
This is ultimately what triggers the CURLOPT_LOW_SPEED_LIMIT
failure on an otherwise successful request. The speed data on request 3 is still the speed data from request 2. Thus, it looks like request 3 has stalled.
I expected the following
I'd expect curl_easy_perform
to fail if a required seek fails. IE: The result of Curl_init_do
should be checked.
I'd expect the rewind
bit to be reset after the request, but I'm not sure this is a reasonable expectation. For my use, each call to curl_easy_perform
is a request and rewinding before sending any data has no meaning. I'm not sure if this holds for all use cases.
curl/libcurl version
libcurl 8.12.1 w/ boringssl
operating system
macOS 15.4.1 (24E263)
Darwin <REDACTED> 24.4.0 Darwin Kernel Version 24.4.0: Fri Apr 11 18:33:47 PDT 2025; root:xnu-11417.101.15~117/RELEASE_ARM64_T6000 arm64
Activity
icing commentedon Apr 23, 2025
@Coderlane thanks for your detailed report. I believe your analysis is correct and the failed rewind should lead to an early error, not a transfer failed unreliably later. Will work on a fix...
init_do(): check result
icing commentedon Apr 23, 2025
I made the fix in #17150 (and found a bug in a test case that way). Could you test that in your setup? Thanks!
Coderlane commentedon Apr 23, 2025
Thank you! I'll give it a try and get back to you.
Coderlane commentedon Apr 23, 2025
177f7b3 seems correct to me. The request now fails with
CURLE_SEND_FAIL_REWIND
when I don't have aCURLOPT_SEEKFUNCTION
registered. Thank you!Here are the logs:
multi: init_do(): check result
multi: init_do(): check result