CONNECT: fix multi interface regression

The refactoring of HTTP CONNECT handling in commit 41b0237834 that
made it protocol independent broke it for the multi interface. This fix
now introduce a better state handling and moved some logic to the
http_proxy.c source file.

Reported by: Yang Tse
Bug: http://curl.haxx.se/mail/lib-2012-03/0162.html
This commit is contained in:
Daniel Stenberg 2012-03-21 23:22:39 +01:00
Родитель 805788e043
Коммит c83de6d076
6 изменённых файлов: 72 добавлений и 43 удалений

Просмотреть файл

@ -1309,10 +1309,17 @@ CURLcode Curl_http_connect(struct connectdata *conn, bool *done)
function to make the re-use checks properly be able to check this bit. */ function to make the re-use checks properly be able to check this bit. */
conn->bits.close = FALSE; conn->bits.close = FALSE;
if(conn->bits.tunnel_connecting) { if(data->state.used_interface == Curl_if_multi) {
/* when the multi interface is used, the CONNECT procedure might not have
been completed */
result = Curl_proxy_connect(conn);
if(result)
return result;
}
if(conn->tunnel_state[FIRSTSOCKET] == TUNNEL_CONNECT)
/* nothing else to do except wait right now - we're not done here. */ /* nothing else to do except wait right now - we're not done here. */
return CURLE_OK; return CURLE_OK;
}
if(conn->given->flags & PROTOPT_SSL) { if(conn->given->flags & PROTOPT_SSL) {
/* perform SSL initialization */ /* perform SSL initialization */

Просмотреть файл

@ -48,6 +48,40 @@
/* The last #include file should be: */ /* The last #include file should be: */
#include "memdebug.h" #include "memdebug.h"
CURLcode Curl_proxy_connect(struct connectdata *conn)
{
if(conn->bits.tunnel_proxy && conn->bits.httpproxy) {
#ifndef CURL_DISABLE_PROXY
/* for [protocol] tunneled through HTTP proxy */
struct HTTP http_proxy;
void *prot_save;
CURLcode result;
/* BLOCKING */
/* We want "seamless" operations through HTTP proxy tunnel */
/* Curl_proxyCONNECT is based on a pointer to a struct HTTP at the
* member conn->proto.http; we want [protocol] through HTTP and we have
* to change the member temporarily for connecting to the HTTP
* proxy. After Curl_proxyCONNECT we have to set back the member to the
* original pointer
*/
prot_save = conn->data->state.proto.generic;
memset(&http_proxy, 0, sizeof(http_proxy));
conn->data->state.proto.http = &http_proxy;
result = Curl_proxyCONNECT(conn, FIRSTSOCKET,
conn->host.name, conn->remote_port);
conn->data->state.proto.generic = prot_save;
if(CURLE_OK != result)
return result;
#else
return CURLE_NOT_BUILT_IN;
#endif
}
/* no HTTP tunnel proxy, just return */
return CURLE_OK;
}
/* /*
* Curl_proxyCONNECT() requires that we're connected to a HTTP proxy. This * Curl_proxyCONNECT() requires that we're connected to a HTTP proxy. This
* function will issue the necessary commands to get a seamless tunnel through * function will issue the necessary commands to get a seamless tunnel through
@ -83,10 +117,14 @@ CURLcode Curl_proxyCONNECT(struct connectdata *conn,
#define SELECT_TIMEOUT 2 #define SELECT_TIMEOUT 2
int error = SELECT_OK; int error = SELECT_OK;
if(conn->tunnel_state[sockindex] == TUNNEL_COMPLETE)
return CURLE_OK; /* CONNECT is already completed */
conn->bits.proxy_connect_closed = FALSE; conn->bits.proxy_connect_closed = FALSE;
do { do {
if(!conn->bits.tunnel_connecting) { /* BEGIN CONNECT PHASE */ if(TUNNEL_INIT == conn->tunnel_state[sockindex]) {
/* BEGIN CONNECT PHASE */
char *host_port; char *host_port;
Curl_send_buffer *req_buffer; Curl_send_buffer *req_buffer;
@ -190,7 +228,7 @@ CURLcode Curl_proxyCONNECT(struct connectdata *conn,
if(result) if(result)
return result; return result;
conn->bits.tunnel_connecting = TRUE; conn->tunnel_state[sockindex] = TUNNEL_CONNECT;
} /* END CONNECT PHASE */ } /* END CONNECT PHASE */
/* now we've issued the CONNECT and we're waiting to hear back - /* now we've issued the CONNECT and we're waiting to hear back -
@ -226,7 +264,6 @@ CURLcode Curl_proxyCONNECT(struct connectdata *conn,
2) we're in multi-mode and we didn't block - it's either an error or we 2) we're in multi-mode and we didn't block - it's either an error or we
now have some data waiting. now have some data waiting.
In any case, the tunnel_connecting phase is over. */ In any case, the tunnel_connecting phase is over. */
conn->bits.tunnel_connecting = FALSE;
{ /* BEGIN NEGOTIATION PHASE */ { /* BEGIN NEGOTIATION PHASE */
size_t nread; /* total size read */ size_t nread; /* total size read */
@ -516,9 +553,14 @@ CURLcode Curl_proxyCONNECT(struct connectdata *conn,
if(closeConnection && data->req.newurl) if(closeConnection && data->req.newurl)
conn->bits.proxy_connect_closed = TRUE; conn->bits.proxy_connect_closed = TRUE;
/* to back to init state */
conn->tunnel_state[sockindex] = TUNNEL_INIT;
return CURLE_RECV_ERROR; return CURLE_RECV_ERROR;
} }
conn->tunnel_state[sockindex] = TUNNEL_COMPLETE;
/* If a proxy-authorization header was used for the proxy, then we should /* If a proxy-authorization header was used for the proxy, then we should
make sure that it isn't accidentally used for the document request make sure that it isn't accidentally used for the document request
after we've connected. So let's free and clear it here. */ after we've connected. So let's free and clear it here. */

Просмотреть файл

@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___ * | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____| * \___|\___/|_| \_\_____|
* *
* Copyright (C) 1998 - 2011, Daniel Stenberg, <daniel@haxx.se>, et al. * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al.
* *
* This software is licensed as described in the file COPYING, which * This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms * you should have received as part of this distribution. The terms
@ -21,6 +21,9 @@
* KIND, either express or implied. * KIND, either express or implied.
* *
***************************************************************************/ ***************************************************************************/
CURLcode Curl_proxy_connect(struct connectdata *conn);
#if !defined(CURL_DISABLE_PROXY) && !defined(CURL_DISABLE_HTTP) #if !defined(CURL_DISABLE_PROXY) && !defined(CURL_DISABLE_HTTP)
/* ftp can use this as well */ /* ftp can use this as well */
CURLcode Curl_proxyCONNECT(struct connectdata *conn, CURLcode Curl_proxyCONNECT(struct connectdata *conn,

Просмотреть файл

@ -812,7 +812,7 @@ static int waitconnect_getsock(struct connectdata *conn,
/* when we've sent a CONNECT to a proxy, we should rather wait for the /* 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 */ socket to become readable to be able to get the response headers */
if(conn->bits.tunnel_connecting) if(conn->tunnel_state[FIRSTSOCKET] == TUNNEL_CONNECT)
return GETSOCK_READSOCK(0); return GETSOCK_READSOCK(0);
return GETSOCK_WRITESOCK(0); return GETSOCK_WRITESOCK(0);
@ -1066,7 +1066,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
CURLM_STATE_WAITDO:CURLM_STATE_DO); CURLM_STATE_WAITDO:CURLM_STATE_DO);
else { else {
#ifndef CURL_DISABLE_HTTP #ifndef CURL_DISABLE_HTTP
if(easy->easy_conn->bits.tunnel_connecting) if(easy->easy_conn->tunnel_state[FIRSTSOCKET] == TUNNEL_CONNECT)
multistate(easy, CURLM_STATE_WAITPROXYCONNECT); multistate(easy, CURLM_STATE_WAITPROXYCONNECT);
else else
#endif #endif
@ -1111,7 +1111,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
CURLM_STATE_WAITDO:CURLM_STATE_DO); CURLM_STATE_WAITDO:CURLM_STATE_DO);
else { else {
#ifndef CURL_DISABLE_HTTP #ifndef CURL_DISABLE_HTTP
if(easy->easy_conn->bits.tunnel_connecting) if(easy->easy_conn->tunnel_state[FIRSTSOCKET] == TUNNEL_CONNECT)
multistate(easy, CURLM_STATE_WAITPROXYCONNECT); multistate(easy, CURLM_STATE_WAITPROXYCONNECT);
else else
#endif #endif
@ -1144,7 +1144,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
multistate(easy, CURLM_STATE_CONNECT); multistate(easy, CURLM_STATE_CONNECT);
} }
else if(CURLE_OK == easy->result) { else if(CURLE_OK == easy->result) {
if(!easy->easy_conn->bits.tunnel_connecting) if(easy->easy_conn->tunnel_state[FIRSTSOCKET] == TUNNEL_COMPLETE)
multistate(easy, CURLM_STATE_WAITCONNECT); multistate(easy, CURLM_STATE_WAITCONNECT);
} }
break; break;
@ -1179,7 +1179,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
BUT if we are using a proxy we must change to WAITPROXYCONNECT BUT if we are using a proxy we must change to WAITPROXYCONNECT
*/ */
#ifndef CURL_DISABLE_HTTP #ifndef CURL_DISABLE_HTTP
if(easy->easy_conn->bits.tunnel_connecting) if(easy->easy_conn->tunnel_state[FIRSTSOCKET] == TUNNEL_CONNECT)
multistate(easy, CURLM_STATE_WAITPROXYCONNECT); multistate(easy, CURLM_STATE_WAITPROXYCONNECT);
else else
#endif #endif

Просмотреть файл

@ -3391,36 +3391,9 @@ CURLcode Curl_protocol_connect(struct connectdata *conn,
is later set again for the progress meter purpose */ is later set again for the progress meter purpose */
conn->now = Curl_tvnow(); conn->now = Curl_tvnow();
if(conn->bits.tunnel_proxy && conn->bits.httpproxy) { result = Curl_proxy_connect(conn);
#ifndef CURL_DISABLE_PROXY if(result)
/* for [protocol] tunneled through HTTP proxy */
struct HTTP http_proxy;
void *prot_save;
/* BLOCKING */
/* We want "seamless" operations through HTTP proxy tunnel */
/* Curl_proxyCONNECT is based on a pointer to a struct HTTP at the
* member conn->proto.http; we want [protocol] through HTTP and we have
* to change the member temporarily for connecting to the HTTP
* proxy. After Curl_proxyCONNECT we have to set back the member to the
* original pointer
*/
prot_save = data->state.proto.generic;
memset(&http_proxy, 0, sizeof(http_proxy));
data->state.proto.http = &http_proxy;
result = Curl_proxyCONNECT(conn, FIRSTSOCKET,
conn->host.name, conn->remote_port);
data->state.proto.generic = prot_save;
if(CURLE_OK != result)
return result; return result;
#else
return CURLE_NOT_BUILT_IN;
#endif
}
if(conn->handler->connect_it) { if(conn->handler->connect_it) {
/* is there a protocol-specific connect() procedure? */ /* is there a protocol-specific connect() procedure? */

Просмотреть файл

@ -422,8 +422,6 @@ 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
@ -964,6 +962,12 @@ struct connectdata {
unsigned short localport; unsigned short localport;
int localportrange; int localportrange;
/* tunnel as in tunnel through a HTTP proxy with CONNECT */
enum {
TUNNEL_INIT, /* init/default/no tunnel state */
TUNNEL_CONNECT, /* CONNECT has been sent off */
TUNNEL_COMPLETE /* CONNECT response received completely */
} tunnel_state[2]; /* two separate ones to allow FTP */
}; };
/* The end of connectdata. */ /* The end of connectdata. */