- Adam D. Moss made the HTTP CONNECT procedure less blocking when used from

the multi interface. Note that it still does a part of the connection in a
  blocking manner.
This commit is contained in:
Daniel Stenberg
2007-02-25 11:38:13 +00:00
parent d2cfb7fd13
commit b819c72700
5 changed files with 313 additions and 229 deletions

View File

@@ -6,6 +6,11 @@
Changelog Changelog
Daniel (25 February 2007)
- Adam D. Moss made the HTTP CONNECT procedure less blocking when used from
the multi interface. Note that it still does a part of the connection in a
blocking manner.
Daniel (23 February 2007) Daniel (23 February 2007)
- Added warning outputs if the command line uses more than one of the options - Added warning outputs if the command line uses more than one of the options
-v, --trace and --trace-ascii, since it could really confuse the user. -v, --trace and --trace-ascii, since it could really confuse the user.

View File

@@ -33,6 +33,8 @@ This release includes the following bugfixes:
o curl-config --libs and libcurl.pc no longer list unnecessary dependencies o curl-config --libs and libcurl.pc no longer list unnecessary dependencies
o fixed an issue with CCC not working on some servers o fixed an issue with CCC not working on some servers
o several HTTP pipelining problems o several HTTP pipelining problems
o HTTP CONNECT thru a proxy is now less blocking when the multi interface is
used
This release includes the following known bugs: This release includes the following known bugs:
@@ -52,6 +54,6 @@ advice from friends like these:
Yang Tse, Manfred Schwarb, Michael Wallner, Jeff Pohlmeyer, Shmulik Regev, Yang Tse, Manfred Schwarb, Michael Wallner, Jeff Pohlmeyer, Shmulik Regev,
Rob Crittenden, Robert A. Monat, Dan Fandrich, Duncan Mac-Vicar Prett, Rob Crittenden, Robert A. Monat, Dan Fandrich, Duncan Mac-Vicar Prett,
Michal Marek, Robson Braga Araujo, Ian Turner, Linus Nielsen Feltzing, Michal Marek, Robson Braga Araujo, Ian Turner, Linus Nielsen Feltzing,
Ravi Pratap Ravi Pratap, Adam D. Moss
Thanks! (and sorry if I forgot to mention someone) Thanks! (and sorry if I forgot to mention someone)

View File

@@ -1115,33 +1115,32 @@ CURLcode Curl_proxyCONNECT(struct connectdata *conn,
struct Curl_transfer_keeper *k = &data->reqdata.keep; struct Curl_transfer_keeper *k = &data->reqdata.keep;
CURLcode result; CURLcode result;
int res; int res;
size_t nread; /* total size read */
int perline; /* count bytes per line */
int keepon=TRUE;
ssize_t gotbytes;
char *ptr;
long timeout = long timeout =
data->set.timeout?data->set.timeout:3600000; /* in milliseconds */ data->set.timeout?data->set.timeout:3600000; /* in milliseconds */
char *line_start;
char *host_port;
curl_socket_t tunnelsocket = conn->sock[sockindex]; curl_socket_t tunnelsocket = conn->sock[sockindex];
send_buffer *req_buffer;
curl_off_t cl=0; curl_off_t cl=0;
bool closeConnection = FALSE; bool closeConnection = FALSE;
long check;
#define SELECT_OK 0 #define SELECT_OK 0
#define SELECT_ERROR 1 #define SELECT_ERROR 1
#define SELECT_TIMEOUT 2 #define SELECT_TIMEOUT 2
int error = SELECT_OK; int error = SELECT_OK;
infof(data, "Establish HTTP proxy tunnel to %s:%d\n", hostname, remote_port);
conn->bits.proxy_connect_closed = FALSE; conn->bits.proxy_connect_closed = FALSE;
do { do {
if (!conn->bits.tunnel_connecting) { /* BEGIN CONNECT PHASE */
char *host_port;
send_buffer *req_buffer;
infof(data, "Establish HTTP proxy tunnel to %s:%d\n",
hostname, remote_port);
if(data->reqdata.newurl) { if(data->reqdata.newurl) {
/* This only happens if we've looped here due to authentication reasons, /* This only happens if we've looped here due to authentication
and we don't really use the newly cloned URL here then. Just free() reasons, and we don't really use the newly cloned URL here
it. */ then. Just free() it. */
free(data->reqdata.newurl); free(data->reqdata.newurl);
data->reqdata.newurl = NULL; data->reqdata.newurl = NULL;
} }
@@ -1214,6 +1213,53 @@ CURLcode Curl_proxyCONNECT(struct connectdata *conn,
if(result) if(result)
return result; return result;
conn->bits.tunnel_connecting = TRUE;
} /* END CONNECT PHASE */
/* now we've issued the CONNECT and we're waiting to hear back -
we try not to block here in multi-mode because that might be a LONG
wait if the proxy cannot connect-through to the remote host. */
/* if timeout is requested, find out how much remaining time we have */
check = timeout - /* timeout time */
Curl_tvdiff(Curl_tvnow(), conn->now); /* spent time */
if(check <=0 ) {
failf(data, "Proxy CONNECT aborted due to timeout");
error = SELECT_TIMEOUT; /* already too little time */
break;
}
/* if we're in multi-mode and we would block, return instead for a retry */
if (Curl_if_multi == data->state.used_interface) {
if (0 == Curl_select(tunnelsocket, CURL_SOCKET_BAD, 0))
/* return so we'll be called again polling-style */
return CURLE_OK;
else {
DEBUGF(infof(data,
"Multi mode finished polling for response from "
"proxy CONNECT."));
}
}
else {
DEBUGF(infof(data, "Easy mode waiting for response from proxy CONNECT."));
}
/* at this point, either:
1) we're in easy-mode and so it's okay to block waiting for a CONNECT
response
2) we're in multi-mode and we didn't block - it's either an error or we
now have some data waiting.
In any case, the tunnel_connecting phase is over. */
conn->bits.tunnel_connecting = FALSE;
{ /* BEGIN NEGOTIATION PHASE */
size_t nread; /* total size read */
int perline; /* count bytes per line */
int keepon=TRUE;
ssize_t gotbytes;
char *ptr;
char *line_start;
ptr=data->state.buffer; ptr=data->state.buffer;
line_start = ptr; line_start = ptr;
@@ -1224,7 +1270,7 @@ CURLcode Curl_proxyCONNECT(struct connectdata *conn,
while((nread<BUFSIZE) && (keepon && !error)) { while((nread<BUFSIZE) && (keepon && !error)) {
/* if timeout is requested, find out how much remaining time we have */ /* if timeout is requested, find out how much remaining time we have */
long check = timeout - /* timeout time */ check = timeout - /* timeout time */
Curl_tvdiff(Curl_tvnow(), conn->now); /* spent time */ Curl_tvdiff(Curl_tvnow(), conn->now); /* spent time */
if(check <= 0) { if(check <= 0) {
failf(data, "Proxy CONNECT aborted due to timeout"); failf(data, "Proxy CONNECT aborted due to timeout");
@@ -1255,8 +1301,9 @@ CURLcode Curl_proxyCONNECT(struct connectdata *conn,
} }
else { else {
/* /*
* We got a whole chunk of data, which can be anything from one byte * We got a whole chunk of data, which can be anything from one
* to a set of lines and possibly just a piece of the last line. * byte to a set of lines and possibly just a piece of the last
* line.
*/ */
int i; int i;
@@ -1264,8 +1311,8 @@ CURLcode Curl_proxyCONNECT(struct connectdata *conn,
if(keepon > TRUE) { if(keepon > TRUE) {
/* This means we are currently ignoring a response-body, so we /* This means we are currently ignoring a response-body, so we
simply count down our counter and make sure to break out of the simply count down our counter and make sure to break out of
loop when we're done! */ the loop when we're done! */
cl -= gotbytes; cl -= gotbytes;
if(cl<=0) { if(cl<=0) {
keepon = FALSE; keepon = FALSE;
@@ -1300,15 +1347,16 @@ CURLcode Curl_proxyCONNECT(struct connectdata *conn,
if(('\r' == line_start[0]) || if(('\r' == line_start[0]) ||
('\n' == line_start[0])) { ('\n' == line_start[0])) {
/* end of response-headers from the proxy */ /* end of response-headers from the proxy */
if(cl && (407 == k->httpcode) && !data->state.authproblem) { if(cl && (407 == k->httpcode) &&
/* If we get a 407 response code with content length when we !data->state.authproblem) {
* have no auth problem, we must ignore the whole /* If we get a 407 response code with content length
* response-body */ * when we have no auth problem, we must ignore the
* whole response-body */
keepon = 2; keepon = 2;
infof(data, "Ignore %" FORMAT_OFF_T infof(data, "Ignore %" FORMAT_OFF_T
" bytes of response-body\n", cl); " bytes of response-body\n", cl);
cl -= (gotbytes - i);/* remove the remaining chunk of what cl -= (gotbytes - i);/* remove the remaining chunk of
we already read */ what we already read */
if(cl<=0) if(cl<=0)
/* if the whole thing was already read, we are done! */ /* if the whole thing was already read, we are done! */
keepon=FALSE; keepon=FALSE;
@@ -1325,7 +1373,8 @@ CURLcode Curl_proxyCONNECT(struct connectdata *conn,
(401 == k->httpcode)) || (401 == k->httpcode)) ||
(checkprefix("Proxy-authenticate:", line_start) && (checkprefix("Proxy-authenticate:", line_start) &&
(407 == k->httpcode))) { (407 == k->httpcode))) {
result = Curl_http_input_auth(conn, k->httpcode, line_start); result = Curl_http_input_auth(conn, k->httpcode,
line_start);
if(result) if(result)
return result; return result;
} }
@@ -1368,6 +1417,7 @@ CURLcode Curl_proxyCONNECT(struct connectdata *conn,
conn->sock[sockindex] = CURL_SOCKET_BAD; conn->sock[sockindex] = CURL_SOCKET_BAD;
break; break;
} }
} /* END NEGOTIATION PHASE */
} while(data->reqdata.newurl); } while(data->reqdata.newurl);
if(200 != k->httpcode) { if(200 != k->httpcode) {
@@ -1423,6 +1473,11 @@ CURLcode Curl_http_connect(struct connectdata *conn, bool *done)
return result; return result;
} }
if (conn->bits.tunnel_connecting) {
/* nothing else to do except wait right now - we're not done here. */
return CURLE_OK;
}
if(!data->state.this_is_a_follow) { if(!data->state.this_is_a_follow) {
/* this is not a followed location, get the original host name */ /* this is not a followed location, get the original host name */
if (data->state.first_host) if (data->state.first_host)

View File

@@ -47,6 +47,7 @@
#include "multiif.h" #include "multiif.h"
#include "sendf.h" #include "sendf.h"
#include "timeval.h" #include "timeval.h"
#include "http.h"
/* The last #include file should be: */ /* The last #include file should be: */
#include "memdebug.h" #include "memdebug.h"
@@ -62,6 +63,7 @@ typedef enum {
CURLM_STATE_CONNECT, /* resolve/connect has been sent off */ CURLM_STATE_CONNECT, /* resolve/connect has been sent off */
CURLM_STATE_WAITRESOLVE, /* awaiting the resolve to finalize */ CURLM_STATE_WAITRESOLVE, /* awaiting the resolve to finalize */
CURLM_STATE_WAITCONNECT, /* awaiting the connect to finalize */ CURLM_STATE_WAITCONNECT, /* awaiting the connect to finalize */
CURLM_STATE_WAITPROXYCONNECT, /* awaiting proxy CONNECT to finalize */
CURLM_STATE_PROTOCONNECT, /* completing the protocol-specific connect CURLM_STATE_PROTOCONNECT, /* completing the protocol-specific connect
phase */ phase */
CURLM_STATE_WAITDO, /* wait for our turn to send the request */ CURLM_STATE_WAITDO, /* wait for our turn to send the request */
@@ -791,7 +793,8 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
multistate(easy, CURLM_STATE_CONNECT); multistate(easy, CURLM_STATE_CONNECT);
result = CURLM_CALL_MULTI_PERFORM; result = CURLM_CALL_MULTI_PERFORM;
easy->result = CURLE_OK; easy->result = CURLE_OK;
} else { }
else {
easy->result = CURLE_COULDNT_CONNECT; easy->result = CURLE_COULDNT_CONNECT;
multistate(easy, CURLM_STATE_COMPLETED); multistate(easy, CURLM_STATE_COMPLETED);
} }
@@ -871,9 +874,12 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
WAITDO! */ WAITDO! */
result = CURLM_CALL_MULTI_PERFORM; result = CURLM_CALL_MULTI_PERFORM;
if(protocol_connect) { if(protocol_connect)
multistate(easy, CURLM_STATE_WAITDO); multistate(easy, CURLM_STATE_WAITDO);
} else { else {
if (easy->easy_conn->bits.tunnel_connecting)
multistate(easy, CURLM_STATE_WAITPROXYCONNECT);
else
multistate(easy, CURLM_STATE_WAITCONNECT); multistate(easy, CURLM_STATE_WAITCONNECT);
} }
} }
@@ -903,10 +909,14 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
result = CURLM_CALL_MULTI_PERFORM; result = CURLM_CALL_MULTI_PERFORM;
if(protocol_connect) if(protocol_connect)
multistate(easy, CURLM_STATE_DO); multistate(easy, CURLM_STATE_DO);
else {
if (easy->easy_conn->bits.tunnel_connecting)
multistate(easy, CURLM_STATE_WAITPROXYCONNECT);
else else
multistate(easy, CURLM_STATE_WAITCONNECT); multistate(easy, CURLM_STATE_WAITCONNECT);
} }
} }
}
if(CURLE_OK != easy->result) { if(CURLE_OK != easy->result) {
/* failure detected */ /* failure detected */
@@ -917,6 +927,16 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
} }
break; break;
case CURLM_STATE_WAITPROXYCONNECT:
/* this is HTTP-specific, but sending CONNECT to a proxy is HTTP... */
easy->result = Curl_http_connect(easy->easy_conn, &protocol_connect);
if(CURLE_OK == easy->result) {
if (!easy->easy_conn->bits.tunnel_connecting)
multistate(easy, CURLM_STATE_WAITCONNECT);
}
break;
case CURLM_STATE_WAITCONNECT: case CURLM_STATE_WAITCONNECT:
/* awaiting a completion of an asynch connect */ /* awaiting a completion of an asynch connect */
easy->result = Curl_is_connected(easy->easy_conn, easy->result = Curl_is_connected(easy->easy_conn,

View File

@@ -470,6 +470,8 @@ struct ConnectBits {
This is implicit when SSL-protocols are used through This is implicit when SSL-protocols are used through
proxies, but can also be enabled explicitly by proxies, but can also be enabled explicitly by
apps */ apps */
bool tunnel_connecting; /* TRUE while we're still waiting for a proxy CONNECT
*/
bool authneg; /* TRUE when the auth phase has started, which means bool authneg; /* TRUE when the auth phase has started, which means
that we are creating a request with an auth header, that we are creating a request with an auth header,
but it is not the final request in the auth but it is not the final request in the auth