diff --git a/lib/conncache.c b/lib/conncache.c index 290638ea0..5bbcf3c86 100644 --- a/lib/conncache.c +++ b/lib/conncache.c @@ -202,6 +202,7 @@ void Curl_conncache_foreach(struct conncache *connc, struct connectdata *conn; bundle = he->ptr; + he = Curl_hash_next_element(&iter); curr = bundle->conn_list->head; while(curr) { @@ -213,8 +214,6 @@ void Curl_conncache_foreach(struct conncache *connc, if(1 == func(conn, param)) return; } - - he = Curl_hash_next_element(&iter); } } diff --git a/lib/conncache.h b/lib/conncache.h index 866554b63..d793f2482 100644 --- a/lib/conncache.h +++ b/lib/conncache.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2012, 2013, Linus Nielsen Feltzing, + * Copyright (C) 2012 - 2014, Linus Nielsen Feltzing, * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -26,6 +26,7 @@ struct conncache { struct curl_hash *hash; size_t num_connections; long next_connection_id; + struct timeval last_cleanup; }; struct conncache *Curl_conncache_init(int size); diff --git a/lib/url.c b/lib/url.c index 57d243822..cf9325308 100644 --- a/lib/url.c +++ b/lib/url.c @@ -2912,6 +2912,69 @@ find_oldest_idle_connection_in_bundle(struct SessionHandle *data, return conn_candidate; } +/* + * This function checks if given connection is dead and disconnects if so. + * (That also removes it from the connection cache.) + * + * Returns TRUE if the connection actually was dead and disconnected. + */ +static bool disconnect_if_dead(struct connectdata *conn, + struct SessionHandle *data) +{ + size_t pipeLen = conn->send_pipe->size + conn->recv_pipe->size; + if(!pipeLen && !conn->inuse) { + /* The check for a dead socket makes sense only if there are no + handles in pipeline and the connection isn't already marked in + use */ + bool dead; + if(conn->handler->protocol & CURLPROTO_RTSP) + /* RTSP is a special case due to RTP interleaving */ + dead = Curl_rtsp_connisdead(conn); + else + dead = SocketIsDead(conn->sock[FIRSTSOCKET]); + + if(dead) { + conn->data = data; + infof(data, "Connection %ld seems to be dead!\n", conn->connection_id); + + /* disconnect resources */ + Curl_disconnect(conn, /* dead_connection */TRUE); + return TRUE; + } + } + return FALSE; +} + +/* + * Wrapper to use disconnect_if_dead() function in Curl_conncache_foreach() + * + * Returns always 0. + */ +static int call_disconnect_if_dead(struct connectdata *conn, + void *param) +{ + struct SessionHandle* data = (struct SessionHandle*)param; + disconnect_if_dead(conn, data); + return 0; /* continue iteration */ +} + +/* + * This function scans the connection cache for half-open/dead connections, + * closes and removes them. + * The cleanup is done at most once per second. + */ +static void prune_dead_connections(struct SessionHandle *data) +{ + struct timeval now = Curl_tvnow(); + long elapsed = Curl_tvdiff(now, data->state.conn_cache->last_cleanup); + + if(elapsed >= 1000L) { + Curl_conncache_foreach(data->state.conn_cache, data, + call_disconnect_if_dead); + data->state.conn_cache->last_cleanup = now; + } +} + /* * Given one filled in connection struct (named needle), this function should * detect if there already is one that has all the significant details @@ -2976,30 +3039,11 @@ ConnectionExists(struct SessionHandle *data, check = curr->ptr; curr = curr->next; + if(disconnect_if_dead(check, data)) + continue; + pipeLen = check->send_pipe->size + check->recv_pipe->size; - if(!pipeLen && !check->inuse) { - /* The check for a dead socket makes sense only if there are no - handles in pipeline and the connection isn't already marked in - use */ - bool dead; - if(check->handler->protocol & CURLPROTO_RTSP) - /* RTSP is a special case due to RTP interleaving */ - dead = Curl_rtsp_connisdead(check); - else - dead = SocketIsDead(check->sock[FIRSTSOCKET]); - - if(dead) { - check->data = data; - infof(data, "Connection %ld seems to be dead!\n", - check->connection_id); - - /* disconnect resources */ - Curl_disconnect(check, /* dead_connection */ TRUE); - continue; - } - } - if(canPipeline) { /* Make sure the pipe has only GET requests */ struct SessionHandle* sh = gethandleathead(check->send_pipe); @@ -5460,6 +5504,8 @@ static CURLcode create_conn(struct SessionHandle *data, goto out; } + prune_dead_connections(data); + /************************************************************* * Check the current list of connections to see if we can * re-use an already existing one or if we have to create a