cURL / Mailing Lists / curl-library / Single Mail

curl-library

Re: Multi cURL connect bug

From: Evgeny Turnaev <turnaev.e_at_gmail.com>
Date: Fri, 5 Jul 2013 13:26:47 +0400

Hi Keyur,
   First of i am not really expert in curl. But reading through
documentation i can't find evidence that curl_multi_perform should
return CURLM_CALL_MULTI_PERFORM untill connection succeeds. Connection
can take very long time. Why don't you change your application code so
that it selects (poll or whatever) on curl sockets and when there is
action - call curl_multi_perform again. I suppose this is a typical
usage. Someone please correct me if i am wrong.

2013/7/5 Keyur Govande <keyurgovande_at_gmail.com>:
> Hello,
>
> We're using cURL to do some asynchronous programming in PHP.
>
> The code fires off a multi-curl request to get some data from a remote
> server. Once the request is sent i.e. curl_multi_exec() (PHP's wrapper
> around curl_multi_perform() ) returns CURLM_OK, the code continues its
> processing and once it is all done and ready, it checks the multi-curl
> request for a response.
>
> We're seeing some unusual behavior that might be a bug. We're
> currently on 7.19.7-26.el6_1.2, but I checked the code and the same
> behavior is in the latest version as well.
>
> If the curl request tries to connect to a host that is slow to respond
> to the initial connect, then curl_multi_perform() returns CURLM_OK,
> even though the connection is not completed and the request not sent.
>
> Here's a couple of sample straces showing both conditions. The PHP code is:
>
> // Set up the multi-curl
> do {
> $cme = curl_multi_exec($this->mc_handle, $this->mc_running);
> } while ($cme === CURLM_CALL_MULTI_PERFORM);
> // For purposes of the test, sleep. This is where other code would execute
> sleep(2);
>
> Connection to localhost where connect() is super fast:
> 1372871818.596930 socket(PF_INET, SOCK_STREAM, IPPROTO_TCP) = 61 <0.000024>
> 1372871818.596990 fcntl(61, F_GETFL) = 0x2 (flags O_RDWR) <0.000023>
> 1372871818.597044 fcntl(61, F_SETFL, O_RDWR|O_NONBLOCK) = 0 <0.000015>
> 1372871818.597086 connect(61, {sa_family=AF_INET, sin_port=htons(80),
> sin_addr=inet_addr("127.0.0.1")}, 16) = -1 EINPROGRESS (Operation now
> in progress) <0.000186>
> 1372871818.597315 poll([{fd=61, events=POLLOUT|POLLWRNORM}], 1, 0) = 1
> ([{fd=61, revents=POLLOUT|POLLWRNORM}]) <0.000017>
> 1372871818.597370 getsockopt(61, SOL_SOCKET, SO_ERROR, [0], [4]) = 0 <0.000016>
> 1372871818.597902 socket(PF_INET, SOCK_STREAM, IPPROTO_TCP) = 62 <0.000020>
> 1372871818.597960 fcntl(62, F_GETFL) = 0x2 (flags O_RDWR) <0.000016>
> 1372871818.598005 fcntl(62, F_SETFL, O_RDWR|O_NONBLOCK) = 0 <0.000018>
> 1372871818.598060 connect(62, {sa_family=AF_INET, sin_port=htons(80),
> sin_addr=inet_addr("127.0.0.1")}, 16) = -1 EINPROGRESS (Operation now
> in progress) <0.000057>
> 1372871818.598162 poll([{fd=62, events=POLLOUT|POLLWRNORM}], 1, 0) = 1
> ([{fd=62, revents=POLLOUT|POLLWRNORM}]) <0.000016>
> 1372871818.598212 getsockopt(62, SOL_SOCKET, SO_ERROR, [0], [4]) = 0 <0.000015>
> 1372871818.598665 sendto(61, "GET /rpc_test.php?sleep=1
> HTTP/1.1\r\nAccept: */*\r\nHost: www.example.com\r\n\r\n", 75,
> MSG_NOSIGNAL, NULL, 0) = 75 <0.000159>
> 1372871818.599054 sendto(62, "GET /rpc_test.php?sleep=1
> HTTP/1.1\r\nAccept: */*\r\nHost: www.example.com\r\n\r\n", 75,
> MSG_NOSIGNAL, NULL, 0) = 75 <0.000072>
> 1372871818.601071 nanosleep({2, 0}, 0x7fff7ce2f4c0) = 0 <2.000130>
>
> Connecting to a slow remote host:
> 1372871825.806179 connect(61, {sa_family=AF_INET, sin_port=htons(80),
> sin_addr=inet_addr("10.255.58.37")}, 16) = -1 EINPROGRESS (Operation
> now in progress) <0.000195>
> 1372871825.806417 poll([{fd=61, events=POLLOUT|POLLWRNORM}], 1, 0) = 0
> (Timeout) <0.000017>
> 1372871825.806919 socket(PF_INET, SOCK_STREAM, IPPROTO_TCP) = 62 <0.000024>
> 1372871825.806976 fcntl(62, F_GETFL) = 0x2 (flags O_RDWR) <0.000016>
> 1372871825.807020 fcntl(62, F_SETFL, O_RDWR|O_NONBLOCK) = 0 <0.000016>
> 1372871825.807064 connect(62, {sa_family=AF_INET, sin_port=htons(80),
> sin_addr=inet_addr("10.255.58.37")}, 16) = -1 EINPROGRESS (Operation
> now in progress) <0.000173>
> 1372871825.807278 poll([{fd=62, events=POLLOUT|POLLWRNORM}], 1, 0) = 0
> (Timeout) <0.000021>
> 1372871825.807688 poll([{fd=61, events=POLLOUT|POLLWRNORM}], 1, 0) = 0
> (Timeout) <0.000016>
> 1372871825.807824 poll([{fd=62, events=POLLOUT|POLLWRNORM}], 1, 0) = 0
> (Timeout) <0.000016>
> 1372871825.809776 nanosleep({2, 0}, 0x7fff04b17530) = 0 <2.000174>
>
> For the slow remote host, the sleep is getting triggered even though
> the GET request was not yet sent.
>
> Reading through the code in lib/multi.c, in the
> CURLM_STATE_WAITCONNECT block, if connected is false, then the result
> should be CURLM_CALL_MULTI_PERFORM in order to keep calling poll until
> the connections are established and the requests flushed out.
>
> Here's the diff on the 7.19.7 branch that I think will fix the issue.
> The patch for 7.31.0 would be exactly the same:
> diff --git a/lib/multi.c b/lib/multi.c
> index 48df928..dc5ec48 100644
> --- a/lib/multi.c
> +++ b/lib/multi.c
> @@ -1077,6 +1077,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
> break;
> }
>
> + result = CURLM_CALL_MULTI_PERFORM;
> if(connected) {
> if(!protocol_connect) {
> /* We have a TCP connection, but 'protocol_connect' may be false
> @@ -1095,8 +1096,6 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
> /* after the connect has completed, go WAITDO or DO */
> multistate(easy, multi->pipelining_enabled?
> CURLM_STATE_WAITDO:CURLM_STATE_DO);
> -
> - result = CURLM_CALL_MULTI_PERFORM;
> }
> }
> break;
>
> Is there anything I'm missing, or another way to accomplish this?
>
> Thanks,
> Keyur.
> -------------------------------------------------------------------
> List admin: http://cool.haxx.se/list/listinfo/curl-library
> Etiquette: http://curl.haxx.se/mail/etiquette.html
-------------------------------------------------------------------
List admin: http://cool.haxx.se/list/listinfo/curl-library
Etiquette: http://curl.haxx.se/mail/etiquette.html
Received on 2013-07-05