From c19349951df1fde96c31d68e054e3a36a3b03860 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Wed, 11 Feb 2015 23:18:32 +0100 Subject: multi: fix *getsock() with CONNECT The code used some happy eyeballs logic even _after_ CONNECT has been sent to a proxy, while the happy eyeball phase is already (should be) over by then. This is solved by splitting the multi state into two separate states introducing the new SENDPROTOCONNECT state. Bug: http://curl.haxx.se/mail/lib-2015-01/0170.html Reported-by: Peter Laser --- lib/multi.c | 90 ++++++++++++++++++++++++++++++------------------------------- 1 file changed, 44 insertions(+), 46 deletions(-) (limited to 'lib/multi.c') diff --git a/lib/multi.c b/lib/multi.c index caf51de77..6d21b138d 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -86,6 +86,7 @@ static const char * const statename[]={ "WAITRESOLVE", "WAITCONNECT", "WAITPROXYCONNECT", + "SENDPROTOCONNECT", "PROTOCONNECT", "WAITDO", "DO", @@ -646,14 +647,24 @@ static int waitconnect_getsock(struct connectdata *conn, } } + return rc; +} + +static int waitproxyconnect_getsock(struct connectdata *conn, + curl_socket_t *sock, + int numsocks) +{ + if(!numsocks) + return GETSOCK_BLANK; + + sock[0] = conn->sock[FIRSTSOCKET]; + /* when we've sent a CONNECT to a proxy, we should rather wait for the socket to become readable to be able to get the response headers */ - if(conn->tunnel_state[FIRSTSOCKET] == TUNNEL_CONNECT) { - sock[0] = conn->sock[FIRSTSOCKET]; - rc = GETSOCK_READSOCK(0); - } + if(conn->tunnel_state[FIRSTSOCKET] == TUNNEL_CONNECT) + return GETSOCK_READSOCK(0); - return rc; + return GETSOCK_WRITESOCK(0); } static int domore_getsock(struct connectdata *conn, @@ -706,6 +717,7 @@ static int multi_getsock(struct SessionHandle *data, return Curl_resolver_getsock(data->easy_conn, socks, numsocks); case CURLM_STATE_PROTOCONNECT: + case CURLM_STATE_SENDPROTOCONNECT: return Curl_protocol_getsock(data->easy_conn, socks, numsocks); case CURLM_STATE_DO: @@ -713,6 +725,8 @@ static int multi_getsock(struct SessionHandle *data, return Curl_doing_getsock(data->easy_conn, socks, numsocks); case CURLM_STATE_WAITPROXYCONNECT: + return waitproxyconnect_getsock(data->easy_conn, socks, numsocks); + case CURLM_STATE_WAITCONNECT: return waitconnect_getsock(data->easy_conn, socks, numsocks); @@ -1164,40 +1178,28 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, /* this is HTTP-specific, but sending CONNECT to a proxy is HTTP... */ result = Curl_http_connect(data->easy_conn, &protocol_connect); + rc = CURLM_CALL_MULTI_PERFORM; if(data->easy_conn->bits.proxy_connect_closed) { /* connect back to proxy again */ result = CURLE_OK; - rc = CURLM_CALL_MULTI_PERFORM; multistate(data, CURLM_STATE_CONNECT); } else if(!result) { if(data->easy_conn->tunnel_state[FIRSTSOCKET] == TUNNEL_COMPLETE) - multistate(data, CURLM_STATE_WAITCONNECT); + /* initiate protocol connect phase */ + multistate(data, CURLM_STATE_SENDPROTOCONNECT); } break; #endif case CURLM_STATE_WAITCONNECT: - /* awaiting a completion of an asynch connect */ - result = Curl_is_connected(data->easy_conn, - FIRSTSOCKET, - &connected); - if(connected) { - - if(!result) - /* if everything is still fine we do the protocol-specific connect - setup */ - result = Curl_protocol_connect(data->easy_conn, - &protocol_connect); - } - - if(data->easy_conn->bits.proxy_connect_closed) { - /* connect back to proxy again since it was closed in a proxy CONNECT - setup */ - result = CURLE_OK; + /* awaiting a completion of an asynch TCP connect */ + result = Curl_is_connected(data->easy_conn, FIRSTSOCKET, &connected); + if(connected && !result) { rc = CURLM_CALL_MULTI_PERFORM; - multistate(data, CURLM_STATE_CONNECT); - break; + multistate(data, data->easy_conn->bits.tunnel_proxy? + CURLM_STATE_WAITPROXYCONNECT: + CURLM_STATE_SENDPROTOCONNECT); } else if(result) { /* failure detected */ @@ -1205,29 +1207,25 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, disconnect_conn = TRUE; break; } + break; - if(connected) { - if(!protocol_connect) { - /* We have a TCP connection, but 'protocol_connect' may be false - and then we continue to 'STATE_PROTOCONNECT'. If protocol - connect is TRUE, we move on to STATE_DO. - BUT if we are using a proxy we must change to WAITPROXYCONNECT - */ -#ifndef CURL_DISABLE_HTTP - if(data->easy_conn->tunnel_state[FIRSTSOCKET] == TUNNEL_CONNECT) - multistate(data, CURLM_STATE_WAITPROXYCONNECT); - else -#endif - multistate(data, CURLM_STATE_PROTOCONNECT); - - } - else - /* after the connect has completed, go WAITDO or DO */ - multistate(data, multi->pipelining_enabled? - CURLM_STATE_WAITDO:CURLM_STATE_DO); - + case CURLM_STATE_SENDPROTOCONNECT: + result = Curl_protocol_connect(data->easy_conn, &protocol_connect); + if(!protocol_connect) + /* switch to waiting state */ + multistate(data, CURLM_STATE_PROTOCONNECT); + else if(!result) { + /* protocol connect has completed, go WAITDO or DO */ + multistate(data, multi->pipelining_enabled? + CURLM_STATE_WAITDO:CURLM_STATE_DO); rc = CURLM_CALL_MULTI_PERFORM; } + else if(result) { + /* failure detected */ + Curl_posttransfer(data); + Curl_done(&data->easy_conn, result, TRUE); + disconnect_conn = TRUE; + } break; case CURLM_STATE_PROTOCONNECT: -- cgit v1.2.3