diff options
author | Daniel Stenberg <daniel@haxx.se> | 2012-11-30 14:52:38 +0100 |
---|---|---|
committer | Daniel Stenberg <daniel@haxx.se> | 2012-12-04 22:14:23 +0100 |
commit | 8b02afd9a938d6db150fa1ba34cca64de873f5d8 (patch) | |
tree | b7567f4eb85251a0ef63fd9c84580ac50e40f337 /lib | |
parent | 3202cc61627c07ef3311fc4b3d9880ad7bcd77d2 (diff) |
multi: fix re-sending request on early connection close
This handling already works with the easy-interface code. When a request
is sent on a re-used connection that gets closed by the server at the
same time as the request is sent, the situation may occur so that we can
send the request and we discover the broken connection as a RECV_ERROR
in the PERFORM state and then the request needs to be retried on a fresh
connection. Test 64 broke with 'multi-always-internally'.
Diffstat (limited to 'lib')
-rw-r--r-- | lib/multi.c | 45 |
1 files changed, 31 insertions, 14 deletions
diff --git a/lib/multi.c b/lib/multi.c index f7ad9cbef..d2d154fa7 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -1539,6 +1539,10 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, break; case CURLM_STATE_PERFORM: + { + char *newurl = NULL; + bool retry = FALSE; + /* check if over send speed */ if((data->set.max_send_speed > 0) && (data->progress.ulspeed > data->set.max_send_speed)) { @@ -1586,13 +1590,29 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, easy->easy_conn->writechannel_inuse = FALSE; } + if(done || (easy->result == CURLE_RECV_ERROR)) { + /* If CURLE_RECV_ERROR happens early enough, we assume it was a race + * condition and the server closed the re-used connection exactly when + * we wanted to use it, so figure out if that is indeed the case. + */ + CURLcode ret = Curl_retry_request(easy->easy_conn, &newurl); + if(!ret) + retry = (newurl)?TRUE:FALSE; + + if(retry) + /* if we are to retry, set the result to OK */ + easy->result = CURLE_OK; + } + if(easy->result) { - /* The transfer phase returned error, we mark the connection to get + /* + * The transfer phase returned error, we mark the connection to get * closed to prevent being re-used. This is because we can't possibly * know if the connection is in a good shape or not now. Unless it is * a protocol which uses two "channels" like FTP, as then the error * happened in the data connection. */ + if(!(easy->easy_conn->handler->flags & PROTOPT_DUAL)) easy->easy_conn->bits.close = TRUE; @@ -1600,14 +1620,8 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, Curl_done(&easy->easy_conn, easy->result, FALSE); } else if(done) { - char *newurl = NULL; - bool retry = FALSE; followtype follow=FOLLOW_NONE; - easy->result = Curl_retry_request(easy->easy_conn, &newurl); - if(!easy->result) - retry = (newurl)?TRUE:FALSE; - /* call this even if the readwrite function returned error */ Curl_posttransfer(data); @@ -1640,11 +1654,10 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, if(CURLE_OK == easy->result) { multistate(easy, CURLM_STATE_CONNECT); result = CURLM_CALL_MULTI_PERFORM; + newurl = NULL; /* handed over the memory ownership to + Curl_follow(), make sure we don't free() it + here */ } - else if(newurl) - /* Since we "took it", we are in charge of freeing this on - failure */ - free(newurl); } else { /* after the transfer is done, go DONE */ @@ -1652,13 +1665,14 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, /* but first check to see if we got a location info even though we're not following redirects */ if(data->req.location) { + if(newurl) + free(newurl); newurl = data->req.location; data->req.location = NULL; easy->result = Curl_follow(data, newurl, FOLLOW_FAKE); - if(easy->result) { + newurl = NULL; /* allocation was handed over */ + if(easy->result) disconnect_conn = TRUE; - free(newurl); - } } multistate(easy, CURLM_STATE_DONE); @@ -1666,7 +1680,10 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, } } + if(newurl) + free(newurl); break; + } case CURLM_STATE_DONE: |