- 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
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)
- 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.

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 fixed an issue with CCC not working on some servers
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:
@ -52,6 +54,6 @@ advice from friends like these:
Yang Tse, Manfred Schwarb, Michael Wallner, Jeff Pohlmeyer, Shmulik Regev,
Rob Crittenden, Robert A. Monat, Dan Fandrich, Duncan Mac-Vicar Prett,
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)

View File

@ -1115,33 +1115,32 @@ CURLcode Curl_proxyCONNECT(struct connectdata *conn,
struct Curl_transfer_keeper *k = &data->reqdata.keep;
CURLcode result;
int res;
size_t nread; /* total size read */
int perline; /* count bytes per line */
int keepon=TRUE;
ssize_t gotbytes;
char *ptr;
long timeout =
data->set.timeout?data->set.timeout:3600000; /* in milliseconds */
char *line_start;
char *host_port;
curl_socket_t tunnelsocket = conn->sock[sockindex];
send_buffer *req_buffer;
curl_off_t cl=0;
bool closeConnection = FALSE;
long check;
#define SELECT_OK 0
#define SELECT_ERROR 1
#define SELECT_TIMEOUT 2
int error = SELECT_OK;
infof(data, "Establish HTTP proxy tunnel to %s:%d\n", hostname, remote_port);
conn->bits.proxy_connect_closed = FALSE;
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) {
/* This only happens if we've looped here due to authentication reasons,
and we don't really use the newly cloned URL here then. Just free()
it. */
/* This only happens if we've looped here due to authentication
reasons, and we don't really use the newly cloned URL here
then. Just free() it. */
free(data->reqdata.newurl);
data->reqdata.newurl = NULL;
}
@ -1214,6 +1213,53 @@ CURLcode Curl_proxyCONNECT(struct connectdata *conn,
if(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;
line_start = ptr;
@ -1224,7 +1270,7 @@ CURLcode Curl_proxyCONNECT(struct connectdata *conn,
while((nread<BUFSIZE) && (keepon && !error)) {
/* 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 */
if(check <= 0) {
failf(data, "Proxy CONNECT aborted due to timeout");
@ -1255,8 +1301,9 @@ CURLcode Curl_proxyCONNECT(struct connectdata *conn,
}
else {
/*
* We got a whole chunk of data, which can be anything from one byte
* to a set of lines and possibly just a piece of the last line.
* We got a whole chunk of data, which can be anything from one
* byte to a set of lines and possibly just a piece of the last
* line.
*/
int i;
@ -1264,8 +1311,8 @@ CURLcode Curl_proxyCONNECT(struct connectdata *conn,
if(keepon > TRUE) {
/* This means we are currently ignoring a response-body, so we
simply count down our counter and make sure to break out of the
loop when we're done! */
simply count down our counter and make sure to break out of
the loop when we're done! */
cl -= gotbytes;
if(cl<=0) {
keepon = FALSE;
@ -1300,15 +1347,16 @@ CURLcode Curl_proxyCONNECT(struct connectdata *conn,
if(('\r' == line_start[0]) ||
('\n' == line_start[0])) {
/* end of response-headers from the proxy */
if(cl && (407 == k->httpcode) && !data->state.authproblem) {
/* If we get a 407 response code with content length when we
* have no auth problem, we must ignore the whole
* response-body */
if(cl && (407 == k->httpcode) &&
!data->state.authproblem) {
/* If we get a 407 response code with content length
* when we have no auth problem, we must ignore the
* whole response-body */
keepon = 2;
infof(data, "Ignore %" FORMAT_OFF_T
" bytes of response-body\n", cl);
cl -= (gotbytes - i);/* remove the remaining chunk of what
we already read */
cl -= (gotbytes - i);/* remove the remaining chunk of
what we already read */
if(cl<=0)
/* if the whole thing was already read, we are done! */
keepon=FALSE;
@ -1325,7 +1373,8 @@ CURLcode Curl_proxyCONNECT(struct connectdata *conn,
(401 == k->httpcode)) ||
(checkprefix("Proxy-authenticate:", line_start) &&
(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)
return result;
}
@ -1368,6 +1417,7 @@ CURLcode Curl_proxyCONNECT(struct connectdata *conn,
conn->sock[sockindex] = CURL_SOCKET_BAD;
break;
}
} /* END NEGOTIATION PHASE */
} while(data->reqdata.newurl);
if(200 != k->httpcode) {
@ -1423,6 +1473,11 @@ CURLcode Curl_http_connect(struct connectdata *conn, bool *done)
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) {
/* this is not a followed location, get the original host name */
if (data->state.first_host)

View File

@ -47,6 +47,7 @@
#include "multiif.h"
#include "sendf.h"
#include "timeval.h"
#include "http.h"
/* The last #include file should be: */
#include "memdebug.h"
@ -62,6 +63,7 @@ typedef enum {
CURLM_STATE_CONNECT, /* resolve/connect has been sent off */
CURLM_STATE_WAITRESOLVE, /* awaiting the resolve 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
phase */
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);
result = CURLM_CALL_MULTI_PERFORM;
easy->result = CURLE_OK;
} else {
}
else {
easy->result = CURLE_COULDNT_CONNECT;
multistate(easy, CURLM_STATE_COMPLETED);
}
@ -871,9 +874,12 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
WAITDO! */
result = CURLM_CALL_MULTI_PERFORM;
if(protocol_connect) {
if(protocol_connect)
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);
}
}
@ -903,10 +909,14 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
result = CURLM_CALL_MULTI_PERFORM;
if(protocol_connect)
multistate(easy, CURLM_STATE_DO);
else {
if (easy->easy_conn->bits.tunnel_connecting)
multistate(easy, CURLM_STATE_WAITPROXYCONNECT);
else
multistate(easy, CURLM_STATE_WAITCONNECT);
}
}
}
if(CURLE_OK != easy->result) {
/* failure detected */
@ -917,6 +927,16 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
}
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:
/* awaiting a completion of an asynch connect */
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
proxies, but can also be enabled explicitly by
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
that we are creating a request with an auth header,
but it is not the final request in the auth