bits.close: introduce connection close tracking

Make all code use connclose() and connkeep() when changing the "close
state" for a connection. These two macros take a string argument with an
explanation, and debug builds of curl will include that in the debug
output. Helps tracking connection re-use/close issues.
This commit is contained in:
Daniel Stenberg 2014-05-20 10:32:23 +02:00
parent 491767418b
commit df13f8e8c2
18 changed files with 85 additions and 48 deletions

View File

@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al.
* Copyright (C) 1998 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@ -421,7 +421,7 @@ CURLcode Curl_resolver_wait_resolv(struct connectdata *conn,
cleaning up this connection properly.
TODO: remove this action from here, it is not a name resolver decision.
*/
conn->bits.close = TRUE;
connclose(conn, "c-ares resolve failed");
return rc;
}

View File

@ -446,7 +446,7 @@ CURLcode Curl_resolver_wait_resolv(struct connectdata *conn,
destroy_async_data(&conn->async);
if(!conn->async.dns)
conn->bits.close = TRUE;
connclose(conn, "asynch resolve failed");
return (rc);
}

View File

@ -1321,3 +1321,20 @@ CURLcode Curl_socket(struct connectdata *conn,
return CURLE_OK;
}
#ifdef CURLDEBUG
/*
* Curl_conncontrol() is used to set the conn->bits.close bit on or off. It
* MUST be called with the connclose() or connclose() macros with a stated
* reason. The reason is only shown in debug builds but helps to figure out
* decision paths when connections are or aren't re-used as expected.
*/
void Curl_conncontrol(struct connectdata *conn, bool closeit,
const char *reason)
{
infof(conn->data, "Marked for [%s]: %s\n", closeit?"closure":"keep alive",
reason);
conn->bits.close = closeit; /* the only place in the source code that should
assign this bit */
}
#endif

View File

@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al.
* Copyright (C) 1998 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@ -102,4 +102,21 @@ CURLcode Curl_socket(struct connectdata *conn,
struct Curl_sockaddr_ex *addr,
curl_socket_t *sockfd);
#ifdef CURLDEBUG
/*
* Curl_connclose() sets the bit.close bit to TRUE with an explanation.
* Nothing else.
*/
void Curl_conncontrol(struct connectdata *conn,
bool closeit,
const char *reason);
#define connclose(x,y) Curl_conncontrol(x,TRUE, y)
#define connkeep(x,y) Curl_conncontrol(x, FALSE, y)
#else /* if !CURLDEBUG */
#define connclose(x,y) (x)->bits.close = TRUE
#define connkeep(x,y) (x)->bits.close = FALSE
#endif
#endif /* HEADER_CURL_CONNECT_H */

View File

@ -3164,7 +3164,7 @@ static CURLcode ftp_connect(struct connectdata *conn,
*done = FALSE; /* default to not done yet */
/* We always support persistent connections on ftp */
conn->bits.close = FALSE;
connkeep(conn, "FTP default");
pp->response_time = RESP_TIMEOUT; /* set default response time-out */
pp->statemach_act = ftp_statemach_act;
@ -3248,7 +3248,7 @@ static CURLcode ftp_done(struct connectdata *conn, CURLcode status,
ftpc->ctl_valid = FALSE;
ftpc->cwdfail = TRUE; /* set this TRUE to prevent us to remember the
current path, as this connection is going */
conn->bits.close = TRUE; /* marked for closure */
connclose(conn, "FTP ended with bad error code");
result = status; /* use the already set error code */
break;
}
@ -3272,7 +3272,7 @@ static CURLcode ftp_done(struct connectdata *conn, CURLcode status,
if(!result)
result = CURLE_OUT_OF_MEMORY;
ftpc->ctl_valid = FALSE; /* mark control connection as bad */
conn->bits.close = TRUE; /* mark for connection closure */
connclose(conn, "FTP: out of memory!"); /* mark for connection closure */
ftpc->prevpath = NULL; /* no path remembering */
}
else {
@ -3315,7 +3315,7 @@ static CURLcode ftp_done(struct connectdata *conn, CURLcode status,
failf(data, "Failure sending ABOR command: %s",
curl_easy_strerror(result));
ftpc->ctl_valid = FALSE; /* mark control connection as bad */
conn->bits.close = TRUE; /* mark for connection closure */
connclose(conn, "ABOR command failed"); /* connection closure */
}
}
@ -3354,7 +3354,7 @@ static CURLcode ftp_done(struct connectdata *conn, CURLcode status,
if(!nread && (CURLE_OPERATION_TIMEDOUT == result)) {
failf(data, "control connection looks dead");
ftpc->ctl_valid = FALSE; /* mark control connection as bad */
conn->bits.close = TRUE; /* mark for closure */
connclose(conn, "Timeout or similar in FTP DONE operation"); /* close */
}
if(result)
@ -3364,7 +3364,7 @@ static CURLcode ftp_done(struct connectdata *conn, CURLcode status,
/* we have just sent ABOR and there is no reliable way to check if it was
* successful or not; we have to close the connection now */
infof(data, "partial download completed, closing connection\n");
conn->bits.close = TRUE; /* mark for closure */
connclose(conn, "Partial download with no ability to check");
return result;
}
@ -4133,7 +4133,7 @@ static CURLcode ftp_quit(struct connectdata *conn)
failf(conn->data, "Failure sending QUIT command: %s",
curl_easy_strerror(result));
conn->proto.ftpc.ctl_valid = FALSE; /* mark control connection as bad */
conn->bits.close = TRUE; /* mark for connection closure */
connclose(conn, "QUIT command failed"); /* mark for connection closure */
state(conn, FTP_STOP);
return result;
}

View File

@ -76,6 +76,7 @@
#include "bundles.h"
#include "pipeline.h"
#include "http2.h"
#include "connect.h"
#define _MPRINTF_REPLACE /* use our functions only */
#include <curl/mprintf.h>
@ -449,7 +450,7 @@ static CURLcode http_perhapsrewind(struct connectdata *conn)
/* This is not NTLM or many bytes left to send: close
*/
conn->bits.close = TRUE;
connclose(conn, "Mid-auth HTTP and much data left to send");
data->req.size = 0; /* don't download any more than 0 bytes */
/* There still is data left to send, but this connection is marked for
@ -1337,7 +1338,7 @@ CURLcode Curl_http_connect(struct connectdata *conn, bool *done)
/* We default to persistent connections. We set this already in this connect
function to make the re-use checks properly be able to check this bit. */
conn->bits.close = FALSE;
connkeep(conn, "HTTP default");
/* the CONNECT procedure might not have been completed */
result = Curl_proxy_connect(conn);
@ -1382,8 +1383,8 @@ static CURLcode https_connecting(struct connectdata *conn, bool *done)
/* perform SSL initialization for this socket */
result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, done);
if(result)
conn->bits.close = TRUE; /* a failed connection is marked for closure
to prevent (bad) re-use or similar */
connclose(conn, "Failed HTTPS connection");
return result;
}
#endif
@ -3016,7 +3017,7 @@ CURLcode Curl_http_readwrite_headers(struct SessionHandle *data,
signal the end of the document. */
infof(data, "no chunk, no close, no size. Assume close to "
"signal end\n");
conn->bits.close = TRUE;
connclose(conn, "HTTP: No end-of-message indicator");
}
}
@ -3085,7 +3086,7 @@ CURLcode Curl_http_readwrite_headers(struct SessionHandle *data,
*/
if(!k->upload_done) {
infof(data, "HTTP error before end of send, stop sending\n");
conn->bits.close = TRUE; /* close after this */
connclose(conn, "Stop sending data before everything sent");
k->upload_done = TRUE;
k->keepon &= ~KEEP_SEND; /* don't send */
if(data->state.expect100header)
@ -3280,7 +3281,7 @@ CURLcode Curl_http_readwrite_headers(struct SessionHandle *data,
we get one of those fancy headers that tell us the
server keeps it open for us! */
infof(data, "HTTP 1.0, assume close after body\n");
conn->bits.close = TRUE;
connclose(conn, "HTTP/1.0 close after body");
}
else if(conn->httpversion >= 11 &&
!conn->bits.close) {
@ -3355,7 +3356,7 @@ CURLcode Curl_http_readwrite_headers(struct SessionHandle *data,
/* Negative Content-Length is really odd, and we know it
happens for example when older Apache servers send large
files */
conn->bits.close = TRUE;
connclose(conn, "negative content-length");
infof(data, "Negative content-length: %" CURL_FORMAT_CURL_OFF_T
", closing after transfer\n", contentlength);
}
@ -3393,7 +3394,7 @@ CURLcode Curl_http_readwrite_headers(struct SessionHandle *data,
* connection will be kept alive for our pleasure.
* Default action for 1.0 is to close.
*/
conn->bits.close = FALSE; /* don't close when done */
connkeep(conn, "Proxy-Connection keep-alive"); /* don't close */
infof(data, "HTTP/1.0 proxy connection set to keep alive!\n");
}
else if((conn->httpversion == 11) &&
@ -3404,7 +3405,7 @@ CURLcode Curl_http_readwrite_headers(struct SessionHandle *data,
* We get a HTTP/1.1 response from a proxy and it says it'll
* close down after this transfer.
*/
conn->bits.close = TRUE; /* close when done */
connclose(conn, "Proxy-Connection: asked to close after done");
infof(data, "HTTP/1.1 proxy connection set close!\n");
}
else if((conn->httpversion == 10) &&
@ -3415,7 +3416,7 @@ CURLcode Curl_http_readwrite_headers(struct SessionHandle *data,
* pleasure. Default action for 1.0 is to close.
*
* [RFC2068, section 19.7.1] */
conn->bits.close = FALSE; /* don't close when done */
connkeep(conn, "Connection keep-alive");
infof(data, "HTTP/1.0 connection set to keep alive!\n");
}
else if(Curl_compareheader(k->p, "Connection:", "close")) {
@ -3425,7 +3426,7 @@ CURLcode Curl_http_readwrite_headers(struct SessionHandle *data,
* the connection will close when this request has been
* served.
*/
conn->bits.close = TRUE; /* close when done */
connclose(conn, "Connection: close used");
}
else if(checkprefix("Transfer-Encoding:", k->p)) {
/* One or more encodings. We check for chunked and/or a compression

View File

@ -69,7 +69,7 @@ CURLcode Curl_proxy_connect(struct connectdata *conn)
prot_save = conn->data->req.protop;
memset(&http_proxy, 0, sizeof(http_proxy));
conn->data->req.protop = &http_proxy;
conn->bits.close = FALSE;
connkeep(conn, "HTTP proxy CONNECT");
result = Curl_proxyCONNECT(conn, FIRSTSOCKET,
conn->host.name, conn->remote_port);
conn->data->req.protop = prot_save;

View File

@ -1881,7 +1881,7 @@ static CURLcode imap_connect(struct connectdata *conn, bool *done)
*done = FALSE; /* default to not done yet */
/* We always support persistent connections in IMAP */
conn->bits.close = FALSE;
connkeep(conn, "IMAP default");
/* Set the default response time-out */
pp->response_time = RESP_TIMEOUT;
@ -1938,7 +1938,7 @@ static CURLcode imap_done(struct connectdata *conn, CURLcode status,
return CURLE_OK;
if(status) {
conn->bits.close = TRUE; /* marked for closure */
connclose(conn, "IMAP done with bad status"); /* marked for closure */
result = status; /* use the already set error code */
}
else if(!data->set.connect_only && !imap->custom &&

View File

@ -466,7 +466,7 @@ quit:
/* no data to transfer */
Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
conn->bits.close = TRUE;
connclose(conn, "LDAP connection always disable re-use");
return status;
}

View File

@ -515,7 +515,7 @@ CURLMcode curl_multi_remove_handle(CURLM *multi_handle,
/* If the handle is in a pipeline and has started sending off its
request but not received its response yet, we need to close
connection. */
data->easy_conn->bits.close = TRUE;
connclose(data->easy_conn, "Removed with partial response");
/* Set connection owner so that Curl_done() closes it.
We can sefely do this here since connection is killed. */
data->easy_conn->data = easy;
@ -1011,7 +1011,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
/* Force the connection closed because the server could continue to
send us stuff at any time. (The disconnect_conn logic used below
doesn't work at this point). */
data->easy_conn->bits.close = TRUE;
connclose(data->easy_conn, "Disconnected with pending data");
data->result = CURLE_OPERATION_TIMEDOUT;
multistate(data, CURLM_STATE_COMPLETED);
break;
@ -1239,7 +1239,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
case CURLM_STATE_DO:
if(data->set.connect_only) {
/* keep connection open for application to use the socket */
data->easy_conn->bits.close = FALSE;
connkeep(data->easy_conn, "CONNECT_ONLY");
multistate(data, CURLM_STATE_DONE);
data->result = CURLE_OK;
result = CURLM_CALL_MULTI_PERFORM;
@ -1525,7 +1525,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
*/
if(!(data->easy_conn->handler->flags & PROTOPT_DUAL))
data->easy_conn->bits.close = TRUE;
connclose(data->easy_conn, "Transfer returned error");
Curl_posttransfer(data);
Curl_done(&data->easy_conn, data->result, FALSE);
@ -1705,7 +1705,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
/* aborted due to progress callback return code must close the
connection */
data->result = CURLE_ABORTED_BY_CALLBACK;
data->easy_conn->bits.close = TRUE;
connclose(data->easy_conn, "Aborted by callback");
/* if not yet in DONE state, go there, otherwise COMPLETED */
multistate(data, (data->mstate < CURLM_STATE_DONE)?

View File

@ -6,7 +6,7 @@
* \___|\___/|_| \_\_____|
*
* Copyright (C) 2010, 2013, Howard Chu, <hyc@openldap.org>
* Copyright (C) 2011 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al.
* Copyright (C) 2011 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@ -46,6 +46,7 @@
#include "curl_ldap.h"
#include "curl_memory.h"
#include "curl_base64.h"
#include "connect.h"
#define _MPRINTF_REPLACE /* use our functions only */
#include <curl/mprintf.h>
@ -175,7 +176,7 @@ static CURLcode ldap_setup(struct connectdata *conn)
return CURLE_OUT_OF_MEMORY;
li->proto = proto;
conn->proto.generic = li;
conn->bits.close = FALSE;
connkeep(conn, "OpenLDAP default");
/* TODO:
* - provide option to choose SASL Binds instead of Simple
*/
@ -349,7 +350,7 @@ static CURLcode ldap_do(struct connectdata *conn, bool *done)
int msgid;
struct SessionHandle *data=conn->data;
conn->bits.close = FALSE;
connkeep(conn, "OpenLDAP do");
infof(data, "LDAP local: %s\n", data->change.url);

View File

@ -1547,7 +1547,7 @@ static CURLcode pop3_connect(struct connectdata *conn, bool *done)
*done = FALSE; /* default to not done yet */
/* We always support persistent connections in POP3 */
conn->bits.close = FALSE;
connkeep(conn, "POP3 default");
/* Set the default response time-out */
pp->response_time = RESP_TIMEOUT;
@ -1601,7 +1601,7 @@ static CURLcode pop3_done(struct connectdata *conn, CURLcode status,
return CURLE_OK;
if(status) {
conn->bits.close = TRUE; /* marked for closure */
connclose(conn, "POP3 done with bad status");
result = status; /* use the already set error code */
}

View File

@ -1588,7 +1588,7 @@ static CURLcode smtp_connect(struct connectdata *conn, bool *done)
*done = FALSE; /* default to not done yet */
/* We always support persistent connections in SMTP */
conn->bits.close = FALSE;
connkeep(conn, "SMTP default");
/* Set the default response time-out */
pp->response_time = RESP_TIMEOUT;
@ -1650,7 +1650,7 @@ static CURLcode smtp_done(struct connectdata *conn, CURLcode status,
return CURLE_OK;
if(status) {
conn->bits.close = TRUE; /* marked for closure */
connclose(conn, "SMTP done with bad status"); /* marked for closure */
result = status; /* use the already set error code */
}
else if(!data->set.connect_only && data->set.upload && data->set.mail_rcpt) {

View File

@ -2544,7 +2544,7 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block)
memset(sshc, 0, sizeof(struct ssh_conn));
conn->bits.close = TRUE;
connclose(conn, "SSH session free");
sshc->state = SSH_SESSION_FREE; /* current */
sshc->nextstate = SSH_NO_STATE;
state(conn, SSH_STOP);
@ -2747,7 +2747,7 @@ static CURLcode ssh_connect(struct connectdata *conn, bool *done)
/* We default to persistent connections. We set this already in this connect
function to make the re-use checks properly be able to check this bit. */
conn->bits.close = FALSE;
connkeep(conn, "SSH default");
if(conn->handler->protocol & CURLPROTO_SCP) {
conn->recv[FIRSTSOCKET] = scp_recv;

View File

@ -974,9 +974,9 @@ static CURLcode tftp_connect(struct connectdata *conn, bool *done)
return CURLE_OUT_OF_MEMORY;
}
conn->bits.close = TRUE; /* we don't keep TFTP connections up bascially
because there's none or very little gain for UDP
*/
/* we don't keep TFTP connections up bascially because there's none or very
* little gain for UDP */
connclose(conn, "TFTP");
state->conn = conn;
state->sockfd = state->conn->sock[FIRSTSOCKET];

View File

@ -569,7 +569,7 @@ static CURLcode readwrite_data(struct SessionHandle *data,
infof(data, "Simulate a HTTP 304 response!\n");
/* we abort the transfer before it is completed == we ruin the
re-use ability. Close the connection */
conn->bits.close = TRUE;
connclose(conn, "Simulated 304 handling");
return CURLE_OK;
}
} /* we have a time condition */
@ -1818,7 +1818,7 @@ Curl_reconnect_request(struct connectdata **connp)
infof(data, "Re-used connection seems dead, get a new one\n");
conn->bits.close = TRUE; /* enforce close of this connection */
connclose(conn, "Reconnect dead connection"); /* enforce close */
result = Curl_done(&conn, result, FALSE); /* we are so done with this */
/* conn may no longer be a good pointer, clear it to avoid mistakes by
@ -1891,7 +1891,7 @@ CURLcode Curl_retry_request(struct connectdata *conn,
if(!*url)
return CURLE_OUT_OF_MEMORY;
conn->bits.close = TRUE; /* close this connection */
connclose(conn, "retry"); /* close this connection */
conn->bits.retry = TRUE; /* mark this as a connection we're about
to retry. Marking it this way should
prevent i.e HTTP transfers to return

View File

@ -3590,7 +3590,7 @@ static struct connectdata *allocate_conn(struct SessionHandle *data)
/* Default protocol-independent behavior doesn't support persistent
connections, so we set this to force-close. Protocols that support
this need to set this to FALSE in their "curl_do" functions. */
conn->bits.close = TRUE;
connclose(conn, "Default to force-close");
/* Store creation time to help future close decision making */
conn->created = Curl_tvnow();

View File

@ -475,6 +475,7 @@ struct negotiatedata {
* Boolean values that concerns this connection.
*/
struct ConnectBits {
/* always modify bits.close with the connclose() and connkeep() macros! */
bool close; /* if set, we close the connection after this request */
bool reuse; /* if set, this is a re-used connection */
bool proxy; /* if set, this transfer is done through a proxy - any type */