Our service recently upgraded PHP to use Libcurl, expecting the new version of Libcurl to support the millisecond timeout, allowing finer control over the backend interface timeouts, thus improving overall response time.
However, we found that, on our CentOS server, when you set a timeout of less than 1000ms, curl does not initiate any requests and returns a time-out error (timeout reached 28).
So there's a hole in it, curl. By default, on Linux systems, if you use System-standard DNS resolution, Sigalarm is used to provide control over the domain name resolution timeout, but Sigalarm does not support timeouts less than 1s, so libcurl In the 7.28.1 code (note the Chinese comment line):
Int curl_resolv_timeout (struct connectdata *conn, const char *hostname, int port, struct curl_dns_entry **entry, &NBSP;&NBSP;&NBSP;&NBSP;&NBSP;&NBSP;&NBSP;&NBSP;&NBSP;&NBSP;&NBSP;&NBSP;&NBSP;LONG&NBSP;TIMEOUTMS) { ... #ifdef use_alarm_timeout if (data->set.no_signal) /*----- Ignore the timeout when signals are disabled */ timeout = 0; eLse timeout = timeoutms; if (!timeout) /* USE_ALARM_TIMEOUT defined, but no timeout actually requested */ return curl_resolv (conn, hostname, port, entry); if (timeout < 1000) //if less than 1000, direct timeout return /* The Alarm () function only provides integer second resolution, so if we want to wait less than one second we must bail out already now. */ return curlresolv_timedout; ....
As you can see, when your timeout is less than 1000ms, name parsing will return directly to Curlresolv_timeout, which will eventually lead to Curle_operation_timedout, then error, timeout reached ...
This .... Too much, Daddy? Can we not use milliseconds to timeout? What do you offer this function for?
Look at the code, or the code just now, note this (Chinese comment line):
#ifdef use_alarm_timeout if (data->set.no_signal) //pay attention to this line / * ignore the timeout when signals are disabled */ timeout = 0; else timeout = timeoutms; if (!timeout) /* USE_ALARM_TIMEOUT defined, but no Timeout actually requested */ return curl_resolv (conn, Hostname, port, entry); if (timeout < 1000) /* the alarm () function only provides integer second resolution, so if we want to wait less than one second we must bail out already now. */ return CURLRESOLV_TIMEDOUT;
It seems that as long as set.no_signal this thing is 1, it can be bypassed ... So what's this thing?
This is simple, grep the code, found:
Case curlopt_nosignal:/* * The application asks not to set any signal () or alarm () handlers, * even when using A timeout. */data->set.no_signal = (0! = va_arg (param, long))? True:false; Break
Haha, the original is this goods:
<?php curl_setopt ($ch, curlopt_nosignal, 1);? >
Plus this opt, everything is finally normal!
Postscript:
In this way, there will be a hidden danger, that is, DNS resolution will not be limited by the timeout, which is within the company, generally no problem, but in case the DNS server hang, it may cause the application timeout.
So is there any other way?
Yes, that's Mike's warning, we can have Libcurl use C-ares (C library for asynchronous DNS requests) to do name resolution. The specific can be in the Config curl:
A "Bug" in Curl's millisecond timeout