Skip to content

Connect timeout never set in cURL, hanging longer than timeout/max_duration on unavailable hosts #47246

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
beberlei opened this issue Aug 10, 2022 · 10 comments

Comments

@beberlei
Copy link
Contributor

Symfony version(s) affected

4.4.x and higher

Description

In #33022 the CURL_CONNECTTIMEOUT_MS was removed but it was not added to the allow list of curl.extra again. As such its not possible to set this setting, causing the default of 5 seconds to be used when connecting to a host that is not available.

How to reproduce

<?php

use Symfony\Component\HttpClient\HttpClient;
use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;

require_once __DIR__ . '/../vendor/autoload.php';

$client = HttpClient::create();
var_dump(get_class($client));
$ts = hrtime(true);
try {
    $response = $client->request(
        'GET',
        'http://unknown.local:1080/messages',
        options: ['timeout' => 1, 'max_duration' => 1]
    );

    $messages = $response->toArray();
} catch (TransportExceptionInterface $e) {
    var_dump($e->getMessage());
}
echo "Duration: " . ((hrtime(true) - $ts) / 1e+6) . "\n";

/**
 * string(43) "Symfony\Component\HttpClient\CurlHttpClient"
string(85) "Resolving timed out after 1000 milliseconds for "http://unknown.local:1080/messages"."
Duration: 5024.504761
 */

Possible Solution

Allow at least to set CURLOPT_CONNECTTIMEOUT_MS in curl.extra, but in general at least for max_duration, it should also set CURLOPT_CONNECTTIMEOUT_MS in addition to CURLOPT_TIMEOUT_MS.

Additional Context

@beberlei beberlei added the Bug label Aug 10, 2022
@beberlei beberlei changed the title Connect timeout never set in cURL, hanging 15 seconds on unavailable hosts Connect timeout never set in cURL, hanging 5 seconds on unavailable hosts Aug 10, 2022
@beberlei beberlei changed the title Connect timeout never set in cURL, hanging 5 seconds on unavailable hosts Connect timeout never set in cURL, hanging longer than timeout/max_duration on unavailable hosts Aug 10, 2022
@nicolas-grekas
Copy link
Member

nicolas-grekas commented Oct 7, 2022

As far as I tested correctly, this is unrelated to not being able to set the option.

Even this simple script runs in 5s no matter what:

$h = curl_init('http://unknown.local:1080/messages');
curl_setopt_array($h, [
    CURLOPT_CONNECTTIMEOUT => 2,
    CURLOPT_TIMEOUT => 2,
]);

curl_exec($h);

strace-ing the scripts, it shows many pool and then a long call like:
futex(0x7fe0741ff910, FUTEX_WAIT_BITSET|FUTEX_CLOCK_REALTIME, 149948, NULL, FUTEX_BITSET_MATCH_ANY

This looks like a curl issue to me.

Note that not being able to set the option is on purpose: the API of HttpClient already provides precise control over timeouts, via the timeout+max_duration options for general config, and via $client->stream($responses, $timeout) for precise timeout control, including connection timeout.

BUT, this is hidden by this futex, and I don't know how to work around it...

@tomasliubinas
Copy link
Contributor

I could reproduce a 5 second wait time on MacOS, regardless of the set timeout value. This case could be reproduced in php call or using command line curl connecting to unknown.local host. The issue might not be entirely related to curl. The waiting could be occurring due to the DNS lookup timeout value, which should be addressed at the OS network configuration level.

@nicolas-grekas
Copy link
Member

Closing as this is not related to HttpClient.
See curl/curl#9272 for pointers.

@deeky666
Copy link
Contributor

deeky666 commented Dec 6, 2022

@nicolas-grekas this still is an issue if DNS resolves but the target port is not open:

$h = curl_init('http://172.67.72.38:1080');
curl_setopt_array($h, [
    CURLOPT_CONNECTTIMEOUT => 2,
    CURLOPT_TIMEOUT => 10,
    CURLOPT_VERBOSE => true,
]);

curl_exec($h);

gives:

*   Trying 172.67.72.38:1080...
* After 2000ms connect time, move on!
* connect to 172.67.72.38 port 1080 failed: Connection timed out
* Connection timeout after 2001 ms
* Closing connection 0

So the curl connection timeout still has an effect in certain situations. We encountered this problem lately in a production environment, where a firewall misconfiguration caused the target port to be blocked. I now figured out, it is not possible to configure connection phase timeouts with Symfony HTTP client using curl. Being able to configure the timeout for the whole request time is not enough, as we want to fail fast on connection errors. At the moment we have to set the request timeout to a relatively high value which does not play well with the retry option. This leads to long waiting times on the user's side before giving up.

Would it be possible to reevaluate and eventually allow setting the curl connection timeout again?

Thank you very much!

@stof
Copy link
Member

stof commented Dec 6, 2022

@deeky666 I suggest you to create a new issue instead of commenting on a closed one (especially when your case is not exactly the same). Opened issues have much more visibility for the community than closed ones.

@beberlei
Copy link
Contributor Author

beberlei commented Dec 6, 2022

@nicolas-grekas agree with @deeky666 here. Even if its a curl error. There is no way to workaround zhis in Symfony http-client, essentially you cannot use it with domains not under your control, which then should be documented as a risk

@beberlei
Copy link
Contributor Author

beberlei commented Dec 6, 2022

@stof i think its the same issue as mine

@nicolas-grekas
Copy link
Member

Could be fixed by php/php-src#10454

@deeky666
Copy link
Contributor

@nicolas-grekas that could fix DNS resolution errors but it won't fix situations where DNS resolves but the client is not able to connect to the host (for example because a firewall rule does not allow to connect to that host/port).

@nicolas-grekas
Copy link
Member

There is nothing we can do about it so 🤷
Let me know if you find a way around.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

7 participants