diff options
Diffstat (limited to 'Utilities/cmcurl/lib/multi.c')
-rw-r--r-- | Utilities/cmcurl/lib/multi.c | 241 |
1 files changed, 127 insertions, 114 deletions
diff --git a/Utilities/cmcurl/lib/multi.c b/Utilities/cmcurl/lib/multi.c index 51acba73ac..b96ee7c7ea 100644 --- a/Utilities/cmcurl/lib/multi.c +++ b/Utilities/cmcurl/lib/multi.c @@ -29,6 +29,7 @@ #include "urldata.h" #include "transfer.h" #include "url.h" +#include "cfilters.h" #include "connect.h" #include "progress.h" #include "easyif.h" @@ -160,7 +161,7 @@ static void mstate(struct Curl_easy *data, CURLMstate state NULL, /* TUNNELING */ NULL, /* PROTOCONNECT */ NULL, /* PROTOCONNECTING */ - Curl_connect_free, /* DO */ + NULL, /* DO */ NULL, /* DOING */ NULL, /* DOING_MORE */ before_perform, /* DID */ @@ -709,6 +710,11 @@ static CURLcode multi_done(struct Curl_easy *data, #endif ) || conn->bits.close || (premature && !(conn->handler->flags & PROTOPT_STREAM))) { + DEBUGF(infof(data, "multi_done, not re-using connection=%ld, forbid=%d" + ", close=%d, premature=%d, stream=%d", + conn->connection_id, + data->set.reuse_forbid, conn->bits.close, premature, + (conn->handler->flags & PROTOPT_STREAM))); connclose(conn, "disconnecting"); Curl_conncache_remove_conn(data, conn, FALSE); CONNCACHE_UNLOCK(data); @@ -948,9 +954,8 @@ void Curl_detach_connection(struct Curl_easy *data) { struct connectdata *conn = data->conn; if(conn) { - Curl_connect_done(data); /* if mid-CONNECT, shut it down */ + Curl_conn_detach_data(conn, data); Curl_llist_remove(&conn->easyq, &data->conn_queue, NULL); - Curl_ssl_detach_conn(data, conn); } data->conn = NULL; } @@ -968,53 +973,9 @@ void Curl_attach_connection(struct Curl_easy *data, data->conn = conn; Curl_llist_insert_next(&conn->easyq, conn->easyq.tail, data, &data->conn_queue); + Curl_conn_attach_data(conn, data); if(conn->handler->attach) conn->handler->attach(data, conn); - Curl_ssl_associate_conn(data, conn); -} - -static int waitconnect_getsock(struct connectdata *conn, - curl_socket_t *sock) -{ - int i; - int s = 0; - int rc = 0; - -#ifdef USE_SSL -#ifndef CURL_DISABLE_PROXY - if(CONNECT_FIRSTSOCKET_PROXY_SSL()) - return Curl_ssl->getsock(conn, sock); -#endif -#endif - - if(SOCKS_STATE(conn->cnnct.state)) - return Curl_SOCKS_getsock(conn, sock, FIRSTSOCKET); - - for(i = 0; i<2; i++) { - if(conn->tempsock[i] != CURL_SOCKET_BAD) { - sock[s] = conn->tempsock[i]; - rc |= GETSOCK_WRITESOCK(s); -#ifdef ENABLE_QUIC - if(conn->transport == TRNSPRT_QUIC) - /* when connecting QUIC, we want to read the socket too */ - rc |= GETSOCK_READSOCK(s); -#endif - s++; - } - } - - return rc; -} - -static int waitproxyconnect_getsock(struct connectdata *conn, - curl_socket_t *sock) -{ - sock[0] = conn->sock[FIRSTSOCKET]; - - if(conn->connect_state) - return Curl_connect_getsock(conn); - - return GETSOCK_WRITESOCK(0); } static int domore_getsock(struct Curl_easy *data, @@ -1076,10 +1037,8 @@ static int multi_getsock(struct Curl_easy *data, return doing_getsock(data, conn, socks); case MSTATE_TUNNELING: - return waitproxyconnect_getsock(conn, socks); - case MSTATE_CONNECTING: - return waitconnect_getsock(conn, socks); + return Curl_conn_get_select_socks(data, FIRSTSOCKET, socks); case MSTATE_DOING_MORE: return domore_getsock(data, conn, socks); @@ -1737,7 +1696,8 @@ static CURLcode protocol_connect(struct Curl_easy *data, *protocol_done = FALSE; - if(conn->bits.tcpconnect[FIRSTSOCKET] && conn->bits.protoconnstart) { + if(Curl_conn_is_connected(conn, FIRSTSOCKET) + && conn->bits.protoconnstart) { /* We already are connected, get back. This may happen when the connect worked fine in the first call, like when we connect to a local server or proxy. Note that we don't know if the protocol is actually done. @@ -1751,21 +1711,6 @@ static CURLcode protocol_connect(struct Curl_easy *data, } if(!conn->bits.protoconnstart) { -#ifndef CURL_DISABLE_PROXY - result = Curl_proxy_connect(data, FIRSTSOCKET); - if(result) - return result; - - if(CONNECT_FIRSTSOCKET_PROXY_SSL()) - /* wait for HTTPS proxy SSL initialization to complete */ - return CURLE_OK; - - if(conn->bits.tunnel_proxy && conn->bits.httpproxy && - Curl_connect_ongoing(conn)) - /* when using an HTTP tunnel proxy, await complete tunnel establishment - before proceeding further. Return CURLE_OK so we'll be called again */ - return CURLE_OK; -#endif if(conn->handler->connect_it) { /* is there a protocol-specific connect() procedure? */ @@ -1785,6 +1730,90 @@ static CURLcode protocol_connect(struct Curl_easy *data, } /* + * readrewind() rewinds the read stream. This is typically used for HTTP + * POST/PUT with multi-pass authentication when a sending was denied and a + * resend is necessary. + */ +static CURLcode readrewind(struct Curl_easy *data) +{ + struct connectdata *conn = data->conn; + curl_mimepart *mimepart = &data->set.mimepost; + DEBUGASSERT(conn); + + data->state.rewindbeforesend = FALSE; /* we rewind now */ + + /* explicitly switch off sending data on this connection now since we are + about to restart a new transfer and thus we want to avoid inadvertently + sending more data on the existing connection until the next transfer + starts */ + data->req.keepon &= ~KEEP_SEND; + + /* We have sent away data. If not using CURLOPT_POSTFIELDS or + CURLOPT_HTTPPOST, call app to rewind + */ + if(conn->handler->protocol & PROTO_FAMILY_HTTP) { + struct HTTP *http = data->req.p.http; + + if(http->sendit) + mimepart = http->sendit; + } + if(data->set.postfields || + (data->state.httpreq == HTTPREQ_GET) || + (data->state.httpreq == HTTPREQ_HEAD)) + ; /* no need to rewind */ + else if(data->state.httpreq == HTTPREQ_POST_MIME || + data->state.httpreq == HTTPREQ_POST_FORM) { + CURLcode result = Curl_mime_rewind(mimepart); + if(result) { + failf(data, "Cannot rewind mime/post data"); + return result; + } + } + else { + if(data->set.seek_func) { + int err; + + Curl_set_in_callback(data, true); + err = (data->set.seek_func)(data->set.seek_client, 0, SEEK_SET); + Curl_set_in_callback(data, false); + if(err) { + failf(data, "seek callback returned error %d", (int)err); + return CURLE_SEND_FAIL_REWIND; + } + } + else if(data->set.ioctl_func) { + curlioerr err; + + Curl_set_in_callback(data, true); + err = (data->set.ioctl_func)(data, CURLIOCMD_RESTARTREAD, + data->set.ioctl_client); + Curl_set_in_callback(data, false); + infof(data, "the ioctl callback returned %d", (int)err); + + if(err) { + failf(data, "ioctl callback returned error %d", (int)err); + return CURLE_SEND_FAIL_REWIND; + } + } + else { + /* If no CURLOPT_READFUNCTION is used, we know that we operate on a + given FILE * stream and we can actually attempt to rewind that + ourselves with fseek() */ + if(data->state.fread_func == (curl_read_callback)fread) { + if(-1 != fseek(data->state.in, 0, SEEK_SET)) + /* successful rewind */ + return CURLE_OK; + } + + /* no callback set or failure above, makes us fail at once */ + failf(data, "necessary data rewind wasn't possible"); + return CURLE_SEND_FAIL_REWIND; + } + } + return CURLE_OK; +} + +/* * Curl_preconnect() is called immediately before a connect starts. When a * redirect is followed, this is then called multiple times during a single * transfer. @@ -1796,6 +1825,7 @@ CURLcode Curl_preconnect(struct Curl_easy *data) if(!data->state.buffer) return CURLE_OUT_OF_MEMORY; } + return CURLE_OK; } @@ -1901,7 +1931,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, if(data->set.connecttimeout) Curl_expire(data, data->set.connecttimeout, EXPIRE_CONNECTTIMEOUT); - result = Curl_connect(data, &async, &protocol_connected); + result = Curl_connect(data, &async, &connected); if(CURLE_NO_CONNECTION_AVAILABLE == result) { /* There was no connection available. We will go to the pending state and wait for an available connection. */ @@ -1929,15 +1959,10 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, WAITDO or DO! */ rc = CURLM_CALL_MULTI_PERFORM; - if(protocol_connected) - multistate(data, MSTATE_DO); + if(connected) + multistate(data, MSTATE_PROTOCONNECT); else { -#ifndef CURL_DISABLE_HTTP - if(Curl_connect_ongoing(data->conn)) - multistate(data, MSTATE_TUNNELING); - else -#endif - multistate(data, MSTATE_CONNECTING); + multistate(data, MSTATE_CONNECTING); } } } @@ -1989,7 +2014,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, if(dns) { /* Perform the next step in the connection phase, and then move on to the WAITCONNECT state */ - result = Curl_once_resolved(data, &protocol_connected); + result = Curl_once_resolved(data, &connected); if(result) /* if Curl_once_resolved() returns failure, the connection struct @@ -1998,15 +2023,10 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, else { /* call again please so that we get the next socket setup */ rc = CURLM_CALL_MULTI_PERFORM; - if(protocol_connected) - multistate(data, MSTATE_DO); + if(connected) + multistate(data, MSTATE_PROTOCONNECT); else { -#ifndef CURL_DISABLE_HTTP - if(Curl_connect_ongoing(data->conn)) - multistate(data, MSTATE_TUNNELING); - else -#endif - multistate(data, MSTATE_CONNECTING); + multistate(data, MSTATE_CONNECTING); } } } @@ -2035,16 +2055,9 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, else #endif if(!result) { - if( -#ifndef CURL_DISABLE_PROXY - (data->conn->http_proxy.proxytype != CURLPROXY_HTTPS || - data->conn->bits.proxy_ssl_connected[FIRSTSOCKET]) && -#endif - Curl_connect_complete(data->conn)) { - rc = CURLM_CALL_MULTI_PERFORM; - /* initiate protocol connect phase */ - multistate(data, MSTATE_PROTOCONNECT); - } + rc = CURLM_CALL_MULTI_PERFORM; + /* initiate protocol connect phase */ + multistate(data, MSTATE_PROTOCONNECT); } else stream_error = TRUE; @@ -2054,27 +2067,10 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, case MSTATE_CONNECTING: /* awaiting a completion of an asynch TCP connect */ DEBUGASSERT(data->conn); - result = Curl_is_connected(data, data->conn, FIRSTSOCKET, &connected); + result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &connected); if(connected && !result) { -#ifndef CURL_DISABLE_HTTP - if( -#ifndef CURL_DISABLE_PROXY - (data->conn->http_proxy.proxytype == CURLPROXY_HTTPS && - !data->conn->bits.proxy_ssl_connected[FIRSTSOCKET]) || -#endif - Curl_connect_ongoing(data->conn)) { - multistate(data, MSTATE_TUNNELING); - break; - } -#endif rc = CURLM_CALL_MULTI_PERFORM; -#ifndef CURL_DISABLE_PROXY - multistate(data, - data->conn->bits.tunnel_proxy? - MSTATE_TUNNELING : MSTATE_PROTOCONNECT); -#else multistate(data, MSTATE_PROTOCONNECT); -#endif } else if(result) { /* failure detected */ @@ -2086,7 +2082,19 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, break; case MSTATE_PROTOCONNECT: - result = protocol_connect(data, &protocol_connected); + if(data->state.rewindbeforesend) + result = readrewind(data); + + if(!result && data->conn->bits.reuse) { + /* ftp seems to hang when protoconnect on reused connection + * since we handle PROTOCONNECT in general inside the filers, it + * seems wrong to restart this on a reused connection. */ + multistate(data, MSTATE_DO); + rc = CURLM_CALL_MULTI_PERFORM; + break; + } + if(!result) + result = protocol_connect(data, &protocol_connected); if(!result && !protocol_connected) /* switch to waiting state */ multistate(data, MSTATE_PROTOCONNECTING); @@ -2770,6 +2778,11 @@ CURLMcode curl_multi_cleanup(struct Curl_multi *multi) wakeup_close(multi->wakeup_pair[1]); #endif #endif + +#ifdef USE_SSL + Curl_free_multi_ssl_backend_data(multi->ssl_backend_data); +#endif + free(multi); return CURLM_OK; |