diff options
Diffstat (limited to 'lib/url.c')
-rw-r--r-- | lib/url.c | 936 |
1 files changed, 592 insertions, 344 deletions
@@ -118,6 +118,7 @@ void idn_free (void *ptr); /* prototype from idn-free.h, not provided by #include "http_negotiate.h" #include "select.h" #include "multiif.h" +#include "easyif.h" /* And now for the protocols */ #include "ftp.h" @@ -155,6 +156,10 @@ static bool ConnectionExists(struct SessionHandle *data, struct connectdata **usethis); static long ConnectionStore(struct SessionHandle *data, struct connectdata *conn); +static bool IsPipeliningPossible(struct SessionHandle *handle); +static void conn_free(struct connectdata *conn); + +#define MAX_PIPELINE_LENGTH 5 #ifndef USE_ARES /* not for Win32, unless it is cygwin @@ -186,6 +191,13 @@ void Curl_safefree(void *ptr) free(ptr); } +static void close_connections(struct SessionHandle *data) +{ + /* Loop through all open connections and kill them one by one */ + while(-1 != ConnectionKillOne(data)) + ; /* empty loop */ +} + /* * This is the internal function curl_easy_cleanup() calls. This should * cleanup and free all resources associated with this sessionhandle. @@ -197,13 +209,21 @@ void Curl_safefree(void *ptr) CURLcode Curl_close(struct SessionHandle *data) { - if(data->multi) { - /* this handle is still part of a multi handle, take care of this first */ + struct Curl_multi *m = data->multi; + + if(m) + /* This handle is still part of a multi handle, take care of this first + and detach this handle from there. */ Curl_multi_rmeasy(data->multi, data); + + if(data->state.connc && (data->state.connc->type == CONNCACHE_PRIVATE)) { + /* close all connections still alive that are in the private connection + cache, as we no longer have the pointer left to the shared one. */ + close_connections(data); + + /* free the connection cache if allocated privately */ + Curl_rm_connc(data->state.connc); } - /* Loop through all open connections and kill them one by one */ - while(-1 != ConnectionKillOne(data)) - ; /* empty loop */ if ( ! (data->share && data->share->hostcache) ) { if ( !Curl_global_host_cache_use(data)) { @@ -211,6 +231,18 @@ CURLcode Curl_close(struct SessionHandle *data) } } + if(data->state.shared_conn) { + /* this handle is still being used by a shared connection cache and thus + we leave it around for now */ + Curl_multi_add_closure(data->state.shared_conn, data); + + return CURLE_OK; + } + + /* Free the pathbuffer */ + Curl_safefree(data->reqdata.pathbuffer); + Curl_safefree(data->reqdata.proto.generic); + /* Close down all open SSL info and sessions */ Curl_ssl_close_all(data); Curl_safefree(data->state.first_host); @@ -254,19 +286,12 @@ CURLcode Curl_close(struct SessionHandle *data) Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE); #endif -#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_CRYPTO_AUTH) Curl_digest_cleanup(data); -#endif - - /* free the connection cache */ - free(data->state.connects); Curl_safefree(data->info.contenttype); -#ifdef USE_ARES /* this destroys the channel and we cannot use it anymore after this */ ares_destroy(data->state.areschannel); -#endif #if defined(CURL_DOES_CONVERSIONS) && defined(HAVE_ICONV) /* close iconv conversion descriptors */ @@ -289,6 +314,44 @@ CURLcode Curl_close(struct SessionHandle *data) return CURLE_OK; } +/* create a connection cache of a private or multi type */ +struct conncache *Curl_mk_connc(int type) +{ + /* It is subject for debate how many default connections to have for a multi + connection cache... */ + int default_amount = (type == CONNCACHE_PRIVATE)?5:10; + struct conncache *c; + + c= calloc(sizeof(struct conncache), 1); + if(!c) + return NULL; + + c->connects = calloc(sizeof(struct connectdata *), default_amount); + if(!c->connects) { + free(c); + return NULL; + } + + c->num = default_amount; + + return c; +} + +/* Free a connection cache. This is called from Curl_close() and + curl_multi_cleanup(). */ +void Curl_rm_connc(struct conncache *c) +{ + if(c->connects) { + int i; + for(i = 0; i < c->num; ++i) + conn_free(c->connects[i]); + + free(c->connects); + } + + free(c); +} + /** * Curl_open() * @@ -301,6 +364,7 @@ CURLcode Curl_open(struct SessionHandle **curl) { CURLcode res = CURLE_OK; struct SessionHandle *data; + /* Very simple start-up: alloc the struct, init it with zeroes and return */ data = (struct SessionHandle *)calloc(1, sizeof(struct SessionHandle)); if(!data) @@ -369,17 +433,15 @@ CURLcode Curl_open(struct SessionHandle **curl) data->set.httpauth = CURLAUTH_BASIC; /* defaults to basic */ data->set.proxyauth = CURLAUTH_BASIC; /* defaults to basic */ - /* create an array with connection data struct pointers */ - data->state.numconnects = 5; /* hard-coded right now */ - data->state.connects = (struct connectdata **) - calloc(sizeof(struct connectdata *) * data->state.numconnects, 1); - - if(!data->state.connects) - res = CURLE_OUT_OF_MEMORY; + /* This no longer creates a connection cache here. It is instead made on + the first call to curl_easy_perform() or when the handle is added to a + multi stack. */ /* most recent connection is not yet defined */ data->state.lastconnect = -1; + Curl_easy_initHandleData(data); + /* * libcurl 7.10 introduced SSL verification *by default*! This needs to be * switched off unless wanted. @@ -393,9 +455,7 @@ CURLcode Curl_open(struct SessionHandle **curl) } if(res) { -#ifdef USE_ARES ares_destroy(data->state.areschannel); -#endif if(data->state.headerbuff) free(data->state.headerbuff); free(data); @@ -454,41 +514,39 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option, struct connectdata **newptr; long i; - if(newconnects < data->state.numconnects) { + if(newconnects < data->state.connc->num) { /* Since this number is *decreased* from the existing number, we must close the possibly open connections that live on the indexes that - are being removed! */ - for(i=newconnects; i< data->state.numconnects; i++) - Curl_disconnect(data->state.connects[i]); + are being removed! + + NOTE: for conncache_multi cases we must make sure that we only + close handles not in use. + */ + for(i=newconnects; i< data->state.connc->num; i++) + Curl_disconnect(data->state.connc->connects[i]); - /* If the most recent connection is no longer valid, mark it invalid. */ + /* If the most recent connection is no longer valid, mark it + invalid. */ if(data->state.lastconnect <= newconnects) data->state.lastconnect = -1; } - if(newconnects) { + if(newconnects > 0) { newptr= (struct connectdata **) - realloc(data->state.connects, + realloc(data->state.connc->connects, sizeof(struct connectdata *) * newconnects); if(!newptr) /* we closed a few connections in vain, but so what? */ return CURLE_OUT_OF_MEMORY; /* nullify the newly added pointers */ - for(i=data->state.numconnects; i<newconnects; i++) { + for(i=data->state.connc->num; i<newconnects; i++) newptr[i] = NULL; - } - data->state.connects = newptr; - data->state.numconnects = newconnects; - } - else { - /* zero makes NO cache at all */ - if(data->state.connects) - free(data->state.connects); - data->state.connects = NULL; - data->state.numconnects = 0; - data->state.lastconnect = -1; + data->state.connc->connects = newptr; + data->state.connc->num = newconnects; } + /* we no longer support less than 1 as size for the connection cache, + and I'm not sure it ever worked to set it to zero */ } break; case CURLOPT_FORBID_REUSE: @@ -1576,14 +1634,57 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option, return result; } +static void conn_free(struct connectdata *conn) +{ + if (!conn) + return; + + /* close possibly still open sockets */ + if(CURL_SOCKET_BAD != conn->sock[SECONDARYSOCKET]) + sclose(conn->sock[SECONDARYSOCKET]); + if(CURL_SOCKET_BAD != conn->sock[FIRSTSOCKET]) + sclose(conn->sock[FIRSTSOCKET]); + + Curl_safefree(conn->user); + Curl_safefree(conn->passwd); + Curl_safefree(conn->proxyuser); + Curl_safefree(conn->proxypasswd); + Curl_safefree(conn->allocptr.proxyuserpwd); + Curl_safefree(conn->allocptr.uagent); + Curl_safefree(conn->allocptr.userpwd); + Curl_safefree(conn->allocptr.accept_encoding); + Curl_safefree(conn->allocptr.rangeline); + Curl_safefree(conn->allocptr.ref); + Curl_safefree(conn->allocptr.host); + Curl_safefree(conn->allocptr.cookiehost); + Curl_safefree(conn->ip_addr_str); + Curl_safefree(conn->trailer); + Curl_safefree(conn->sec_pathbuffer); + Curl_safefree(conn->host.rawalloc); /* host name buffer */ + Curl_safefree(conn->proxy.rawalloc); /* proxy name buffer */ + + Curl_llist_destroy(conn->send_pipe, NULL); + Curl_llist_destroy(conn->recv_pipe, NULL); + + /* possible left-overs from the async name resolvers */ +#if defined(USE_ARES) + Curl_safefree(conn->async.hostname); + Curl_safefree(conn->async.os_specific); +#elif defined(CURLRES_THREADED) + Curl_destroy_thread_data(&conn->async); +#endif + + Curl_free_ssl_config(&conn->ssl_config); + + free(conn); /* free all the connection oriented data */ +} + CURLcode Curl_disconnect(struct connectdata *conn) { - struct SessionHandle *data; + struct SessionHandle *data = conn->data; if(!conn) return CURLE_OK; /* this is closed and fine already */ - data = conn->data; - #if defined(CURLDEBUG) && defined(AGGRESIVE_TEST) /* scan for DNS cache entries still marked as in use */ Curl_hash_apply(data->hostcache, @@ -1598,9 +1699,9 @@ CURLcode Curl_disconnect(struct connectdata *conn) * get here *instead* if we fail prematurely. Thus we need to be able * to free this resource here as well. */ - if(conn->bits.rangestringalloc) { - free(conn->range); - conn->bits.rangestringalloc = FALSE; + if(data->reqdata.rangestringalloc) { + free(data->reqdata.range); + data->reqdata.rangestringalloc = FALSE; } if((conn->ntlm.state != NTLMSTATE_NONE) || @@ -1628,15 +1729,9 @@ CURLcode Curl_disconnect(struct connectdata *conn) if(-1 != conn->connectindex) { /* unlink ourselves! */ infof(data, "Closing connection #%ld\n", conn->connectindex); - data->state.connects[conn->connectindex] = NULL; + data->state.connc->connects[conn->connectindex] = NULL; } - Curl_safefree(conn->proto.generic); - Curl_safefree(conn->newurl); - Curl_safefree(conn->pathbuffer); /* the URL path buffer */ - - Curl_safefree(conn->host.rawalloc); /* host name buffer */ - Curl_safefree(conn->proxy.rawalloc); /* proxy name buffer */ #ifdef USE_LIBIDN if(conn->host.encalloc) idn_free(conn->host.encalloc); /* encoded host name buffer, must be freed @@ -1647,40 +1742,14 @@ CURLcode Curl_disconnect(struct connectdata *conn) freed with idn_free() since this was allocated by libidn */ #endif - Curl_ssl_close(conn); - - /* close possibly still open sockets */ - if(CURL_SOCKET_BAD != conn->sock[SECONDARYSOCKET]) - sclose(conn->sock[SECONDARYSOCKET]); - if(CURL_SOCKET_BAD != conn->sock[FIRSTSOCKET]) - sclose(conn->sock[FIRSTSOCKET]); - Curl_safefree(conn->user); - Curl_safefree(conn->passwd); - Curl_safefree(conn->proxyuser); - Curl_safefree(conn->proxypasswd); - Curl_safefree(conn->allocptr.proxyuserpwd); - Curl_safefree(conn->allocptr.uagent); - Curl_safefree(conn->allocptr.userpwd); - Curl_safefree(conn->allocptr.accept_encoding); - Curl_safefree(conn->allocptr.rangeline); - Curl_safefree(conn->allocptr.ref); - Curl_safefree(conn->allocptr.host); - Curl_safefree(conn->allocptr.cookiehost); - Curl_safefree(conn->ip_addr_str); - Curl_safefree(conn->trailer); - - /* possible left-overs from the async name resolvers */ -#if defined(USE_ARES) - Curl_safefree(conn->async.hostname); - Curl_safefree(conn->async.os_specific); -#elif defined(CURLRES_THREADED) - Curl_destroy_thread_data(&conn->async); -#endif + Curl_ssl_close(conn); - Curl_free_ssl_config(&conn->ssl_config); + /* Indicate to all handles on the pipe that we're dead */ + Curl_signalPipeClose(conn->send_pipe); + Curl_signalPipeClose(conn->recv_pipe); - free(conn); /* free all the connection oriented data */ + conn_free(conn); return CURLE_OK; } @@ -1703,10 +1772,92 @@ static bool SocketIsDead(curl_socket_t sock) return ret_val; } +static bool IsPipeliningPossible(struct SessionHandle *handle) +{ + if (handle->multi && Curl_multi_canPipeline(handle->multi) && + (handle->set.httpreq == HTTPREQ_GET || + handle->set.httpreq == HTTPREQ_HEAD) && + handle->set.httpversion != CURL_HTTP_VERSION_1_0) + return TRUE; + + return FALSE; +} + +void Curl_addHandleToPipeline(struct SessionHandle *handle, + struct curl_llist *pipe) +{ + Curl_llist_insert_next(pipe, + pipe->tail, + handle); +} + + +void Curl_removeHandleFromPipeline(struct SessionHandle *handle, + struct curl_llist *pipe) +{ + struct curl_llist_element *curr; + + curr = pipe->head; + while (curr) { + if (curr->ptr == handle) { + Curl_llist_remove(pipe, curr, NULL); + break; + } + curr = curr->next; + } +} + +#if 0 +static void Curl_printPipeline(struct curl_llist *pipe) +{ + struct curl_llist_element *curr; + + curr = pipe->head; + while (curr) { + struct SessionHandle *data = (struct SessionHandle *) curr->ptr; + infof(data, "Handle in pipeline: %s\n", + data->reqdata.path); + curr = curr->next; + } +} +#endif + +bool Curl_isHandleAtHead(struct SessionHandle *handle, + struct curl_llist *pipe) +{ + struct curl_llist_element *curr = pipe->head; + if (curr) { + return curr->ptr == handle ? TRUE : FALSE; + } + + return FALSE; +} + +void Curl_signalPipeClose(struct curl_llist *pipe) +{ + struct curl_llist_element *curr; + + curr = pipe->head; + while (curr) { + struct curl_llist_element *next = curr->next; + struct SessionHandle *data = (struct SessionHandle *) curr->ptr; + + data->state.pipe_broke = TRUE; + + Curl_llist_remove(pipe, curr, NULL); + curr = next; + } +} + + /* * Given one filled in connection struct (named needle), this function should - * detect if there already is one that have all the significant details + * detect if there already is one that has all the significant details * exactly the same and thus should be used instead. + * + * If there is a match, this function returns TRUE - and has marked the + * connection as 'in-use'. It must later be called with ConnectionDone() to + * return back to 'idle' (unused) state. */ static bool ConnectionExists(struct SessionHandle *data, @@ -1715,18 +1866,28 @@ ConnectionExists(struct SessionHandle *data, { long i; struct connectdata *check; + bool canPipeline = IsPipeliningPossible(data); - for(i=0; i< data->state.numconnects; i++) { + for(i=0; i< data->state.connc->num; i++) { bool match = FALSE; /* * Note that if we use a HTTP proxy, we check connections to that * proxy and not to the actual remote server. */ - check = data->state.connects[i]; + check = data->state.connc->connects[i]; if(!check) /* NULL pointer means not filled-in entry */ continue; + if(check->inuse && !canPipeline) + /* can only happen within multi handles, and means that another easy + handle is using this connection */ + continue; + + if (check->send_pipe->size >= MAX_PIPELINE_LENGTH || + check->recv_pipe->size >= MAX_PIPELINE_LENGTH) + continue; + if((needle->protocol&PROT_SSL) != (check->protocol&PROT_SSL)) /* don't do mixed SSL and non-SSL connections */ continue; @@ -1753,7 +1914,7 @@ ConnectionExists(struct SessionHandle *data, } if((needle->protocol & PROT_FTP) || ((needle->protocol & PROT_HTTP) && - (needle->data->state.authhost.want==CURLAUTH_NTLM))) { + (data->state.authhost.want==CURLAUTH_NTLM))) { /* This is FTP or HTTP+NTLM, verify that we're using the same name and password as well */ if(!strequal(needle->user, check->user) || @@ -1780,15 +1941,24 @@ ConnectionExists(struct SessionHandle *data, if(dead) { /* */ + check->data = data; infof(data, "Connection %d seems to be dead!\n", i); Curl_disconnect(check); /* disconnect resources */ - data->state.connects[i]=NULL; /* nothing here */ + data->state.connc->connects[i]=NULL; /* nothing here */ /* There's no need to continue searching, because we only store one connection for each unique set of identifiers */ return FALSE; } + check->inuse = TRUE; /* mark this as being in use so that no other + handle in a multi stack may nick it */ + + if (canPipeline) { + /* Mark the connection as being in a pipeline */ + check->is_in_pipeline = TRUE; + } + *usethis = check; return TRUE; /* yes, we found one to use! */ } @@ -1796,6 +1966,7 @@ ConnectionExists(struct SessionHandle *data, return FALSE; /* no matching connecting exists */ } + /* * This function frees/closes a connection in the connection cache. This * should take the previously set policy into account when deciding which @@ -1813,8 +1984,8 @@ ConnectionKillOne(struct SessionHandle *data) now = Curl_tvnow(); - for(i=0; i< data->state.numconnects; i++) { - conn = data->state.connects[i]; + for(i=0; data->state.connc && (i< data->state.connc->num); i++) { + conn = data->state.connc->connects[i]; if(!conn) continue; @@ -1846,17 +2017,32 @@ ConnectionKillOne(struct SessionHandle *data) } } if(connindex >= 0) { + /* Set the connection's owner correctly */ + conn = data->state.connc->connects[connindex]; + conn->data = data; /* the winner gets the honour of being disconnected */ - (void) Curl_disconnect(data->state.connects[connindex]); + (void)Curl_disconnect(conn); /* clean the array entry */ - data->state.connects[connindex] = NULL; + data->state.connc->connects[connindex] = NULL; } return connindex; /* return the available index or -1 */ } +/* this connection can now be marked 'idle' */ +static void +ConnectionDone(struct connectdata *conn) +{ + conn->inuse = FALSE; + conn->data = NULL; + + if (conn->send_pipe == 0 && + conn->recv_pipe == 0) + conn->is_in_pipeline = FALSE; +} + /* * The given input connection struct pointer is to be stored. If the "cache" * is already full, we must clean out the most suitable using the previously @@ -1870,24 +2056,34 @@ ConnectionStore(struct SessionHandle *data, struct connectdata *conn) { long i; - for(i=0; i< data->state.numconnects; i++) { - if(!data->state.connects[i]) + for(i=0; i< data->state.connc->num; i++) { + if(!data->state.connc->connects[i]) break; } - if(i == data->state.numconnects) { + if(i == data->state.connc->num) { /* there was no room available, kill one */ i = ConnectionKillOne(data); infof(data, "Connection (#%d) was killed to make room\n", i); } + conn->connectindex = i; /* Make the child know where the pointer to this + particular data is stored. But note that this -1 + if this is not within the cache and this is + probably not checked for everywhere (yet). */ + conn->inuse = TRUE; if(-1 != i) { - /* only do this if a true index was returned, if -1 was returned there + /* Only do this if a true index was returned, if -1 was returned there is no room in the cache for an unknown reason and we cannot store - this there. */ - data->state.connects[i] = conn; /* fill in this */ - conn->connectindex = i; /* make the child know where the pointer to this - particular data is stored */ + this there. + + TODO: make sure we really can work with more handles than positions in + the cache, or possibly we should (allow to automatically) resize the + connection cache when we add more easy handles to a multi handle! + */ + data->state.connc->connects[i] = conn; /* fill in this */ + conn->data = data; } + return i; } @@ -1902,13 +2098,14 @@ ConnectionStore(struct SessionHandle *data, * Nonsupport "SOCKS 4A (Simple Extension to SOCKS 4 Protocol)" * Nonsupport "Identification Protocol (RFC1413)" */ -static int handleSock4Proxy(const char *proxy_name, struct connectdata *conn) +static int handleSock4Proxy(const char *proxy_name, + struct SessionHandle *data, + struct connectdata *conn) { unsigned char socksreq[262]; /* room for SOCKS4 request incl. user id */ int result; CURLcode code; curl_socket_t sock = conn->sock[FIRSTSOCKET]; - struct SessionHandle *data = conn->data; Curl_nonblock(sock, FALSE); @@ -1964,11 +2161,11 @@ static int handleSock4Proxy(const char *proxy_name, struct connectdata *conn) else hp = NULL; /* fail! */ - Curl_resolv_unlock(conn->data, dns); /* not used anymore from now on */ + Curl_resolv_unlock(data, dns); /* not used anymore from now on */ } if(!hp) { - failf(conn->data, "Failed to resolve \"%s\" for SOCKS4 connect.", + failf(data, "Failed to resolve \"%s\" for SOCKS4 connect.", conn->host.name); return 1; } @@ -1993,7 +2190,7 @@ static int handleSock4Proxy(const char *proxy_name, struct connectdata *conn) /* Send request */ code = Curl_write(conn, sock, (char *)socksreq, packetsize, &written); if ((code != CURLE_OK) || (written != packetsize)) { - failf(conn->data, "Failed to send SOCKS4 connect request."); + failf(data, "Failed to send SOCKS4 connect request."); return 1; } @@ -2002,7 +2199,7 @@ static int handleSock4Proxy(const char *proxy_name, struct connectdata *conn) /* Receive response */ result = Curl_read(conn, sock, (char *)socksreq, packetsize, &actualread); if ((result != CURLE_OK) || (actualread != packetsize)) { - failf(conn->data, "Failed to receive SOCKS4 connect request ack."); + failf(data, "Failed to receive SOCKS4 connect request ack."); return 1; } @@ -2027,7 +2224,7 @@ static int handleSock4Proxy(const char *proxy_name, struct connectdata *conn) /* wrong version ? */ if (socksreq[0] != 0) { - failf(conn->data, + failf(data, "SOCKS4 reply has wrong version, version should be 4."); return 1; } @@ -2039,7 +2236,7 @@ static int handleSock4Proxy(const char *proxy_name, struct connectdata *conn) infof(data, "SOCKS4 request granted.\n"); break; case 91: - failf(conn->data, + failf(data, "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)" ", request rejected or failed.", (unsigned char)socksreq[4], (unsigned char)socksreq[5], @@ -2048,7 +2245,7 @@ static int handleSock4Proxy(const char *proxy_name, struct connectdata *conn) socksreq[1]); return 1; case 92: - failf(conn->data, + failf(data, "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)" ", request rejected because SOCKS server cannot connect to " "identd on the client.", @@ -2058,7 +2255,7 @@ static int handleSock4Proxy(const char *proxy_name, struct connectdata *conn) socksreq[1]); return 1; case 93: - failf(conn->data, + failf(data, "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)" ", request rejected because the client program and identd " "report different user-ids.", @@ -2068,7 +2265,7 @@ static int handleSock4Proxy(const char *proxy_name, struct connectdata *conn) socksreq[1]); return 1; default : - failf(conn->data, + failf(data, "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)" ", Unknown.", (unsigned char)socksreq[4], (unsigned char)socksreq[5], @@ -2161,7 +2358,7 @@ static int handleSock5Proxy(const char *proxy_name, code = Curl_write(conn, sock, (char *)socksreq, (2 + (int)socksreq[1]), &written); if ((code != CURLE_OK) || (written != (2 + (int)socksreq[1]))) { - failf(conn->data, "Unable to send initial SOCKS5 request."); + failf(data, "Unable to send initial SOCKS5 request."); return 1; } @@ -2187,12 +2384,12 @@ static int handleSock5Proxy(const char *proxy_name, result=Curl_read(conn, sock, (char *)socksreq, 2, &actualread); if ((result != CURLE_OK) || (actualread != 2)) { - failf(conn->data, "Unable to receive initial SOCKS5 response."); + failf(data, "Unable to receive initial SOCKS5 response."); return 1; } if (socksreq[0] != 5) { - failf(conn->data, "Received invalid version in initial SOCKS5 response."); + failf(data, "Received invalid version in initial SOCKS5 response."); return 1; } if (socksreq[1] == 0) { @@ -2230,19 +2427,19 @@ static int handleSock5Proxy(const char *proxy_name, code = Curl_write(conn, sock, (char *)socksreq, len, &written); if ((code != CURLE_OK) || (len != written)) { - failf(conn->data, "Failed to send SOCKS5 sub-negotiation request."); + failf(data, "Failed to send SOCKS5 sub-negotiation request."); return 1; } result=Curl_read(conn, sock, (char *)socksreq, 2, &actualread); if ((result != CURLE_OK) || (actualread != 2)) { - failf(conn->data, "Unable to receive SOCKS5 sub-negotiation response."); + failf(data, "Unable to receive SOCKS5 sub-negotiation response."); return 1; } /* ignore the first (VER) byte */ if (socksreq[1] != 0) { /* status */ - failf(conn->data, "User was rejected by the SOCKS5 server (%d %d).", + failf(data, "User was rejected by the SOCKS5 server (%d %d).", socksreq[0], socksreq[1]); return 1; } @@ -2252,24 +2449,24 @@ static int handleSock5Proxy(const char *proxy_name, else { /* error */ if (socksreq[1] == 1) { - failf(conn->data, + failf(data, "SOCKS5 GSSAPI per-message authentication is not supported."); return 1; } else if (socksreq[1] == 255) { if (!proxy_name || !*proxy_name) { - failf(conn->data, + failf(data, "No authentication method was acceptable. (It is quite likely" " that the SOCKS5 server wanted a username/password, since none" " was supplied to the server on this connection.)"); } else { - failf(conn->data, "No authentication method was acceptable."); + failf(data, "No authentication method was acceptable."); } return 1; } else { - failf(conn->data, + failf(data, "Undocumented SOCKS5 mode attempted to be used by server."); return 1; } @@ -2314,10 +2511,10 @@ static int handleSock5Proxy(const char *proxy_name, else hp = NULL; /* fail! */ - Curl_resolv_unlock(conn->data, dns); /* not used anymore from now on */ + Curl_resolv_unlock(data, dns); /* not used anymore from now on */ } if(!hp) { - failf(conn->data, "Failed to resolve \"%s\" for SOCKS5 connect.", + failf(data, "Failed to resolve \"%s\" for SOCKS5 connect.", conn->host.name); return 1; } @@ -2330,23 +2527,23 @@ static int handleSock5Proxy(const char *proxy_name, code = Curl_write(conn, sock, (char *)socksreq, packetsize, &written); if ((code != CURLE_OK) || (written != packetsize)) { - failf(conn->data, "Failed to send SOCKS5 connect request."); + failf(data, "Failed to send SOCKS5 connect request."); return 1; } result = Curl_read(conn, sock, (char *)socksreq, packetsize, &actualread); if ((result != CURLE_OK) || (actualread != packetsize)) { - failf(conn->data, "Failed to receive SOCKS5 connect request ack."); + failf(data, "Failed to receive SOCKS5 connect request ack."); return 1; } if (socksreq[0] != 5) { /* version */ - failf(conn->data, + failf(data, "SOCKS5 reply has wrong version, version should be 5."); return 1; } if (socksreq[1] != 0) { /* Anything besides 0 is an error */ - failf(conn->data, + failf(data, "Can't complete SOCKS5 connection to %d.%d.%d.%d:%d. (%d)", (unsigned char)socksreq[4], (unsigned char)socksreq[5], (unsigned char)socksreq[6], (unsigned char)socksreq[7], @@ -2360,13 +2557,13 @@ static int handleSock5Proxy(const char *proxy_name, return 0; /* Proxy was successful! */ } -static CURLcode ConnectPlease(struct connectdata *conn, +static CURLcode ConnectPlease(struct SessionHandle *data, + struct connectdata *conn, struct Curl_dns_entry *hostaddr, bool *connected) { CURLcode result; Curl_addrinfo *addr; - struct SessionHandle *data = conn->data; char *hostname = data->change.proxy?conn->proxy.name:conn->host.name; infof(data, "About to connect() to %s%s port %d\n", @@ -2388,7 +2585,7 @@ static CURLcode ConnectPlease(struct connectdata *conn, Curl_store_ip_addr(conn); - switch(conn->data->set.proxytype) { + switch(data->set.proxytype) { case CURLPROXY_SOCKS5: return handleSock5Proxy(conn->proxyuser, conn->proxypasswd, @@ -2398,10 +2595,10 @@ static CURLcode ConnectPlease(struct connectdata *conn, /* do nothing here. handled later. */ break; case CURLPROXY_SOCKS4: - return handleSock4Proxy(conn->proxyuser, conn) ? + return handleSock4Proxy(conn->proxyuser, data, conn) ? CURLE_COULDNT_CONNECT : CURLE_OK; default: - failf(conn->data, "unknown proxytype option given"); + failf(data, "unknown proxytype option given"); return CURLE_COULDNT_CONNECT; } } @@ -2443,7 +2640,8 @@ int Curl_doing_getsock(struct connectdata *conn, * protocol layer. */ -CURLcode Curl_protocol_connecting(struct connectdata *conn, bool *done) +CURLcode Curl_protocol_connecting(struct connectdata *conn, + bool *done) { CURLcode result=CURLE_OK; @@ -2481,10 +2679,11 @@ CURLcode Curl_protocol_doing(struct connectdata *conn, bool *done) * proceed with some action. * */ -CURLcode Curl_protocol_connect(struct connectdata *conn, bool *protocol_done) +CURLcode Curl_protocol_connect(struct connectdata *conn, + bool *protocol_done) { - struct SessionHandle *data = conn->data; CURLcode result=CURLE_OK; + struct SessionHandle *data = conn->data; *protocol_done = FALSE; @@ -2586,7 +2785,8 @@ static bool tld_check_name(struct SessionHandle *data, } #endif -static void fix_hostname(struct connectdata *conn, struct hostname *host) +static void fix_hostname(struct SessionHandle *data, + struct connectdata *conn, struct hostname *host) { /* set the name we use to display the host name */ host->dispname = host->name; @@ -2598,7 +2798,6 @@ static void fix_hostname(struct connectdata *conn, struct hostname *host) if (!is_ASCII_name(host->name) && stringprep_check_version(LIBIDN_REQUIRED_VERSION)) { char *ace_hostname = NULL; - struct SessionHandle *data = conn->data; int rc = idna_to_ascii_lz(host->name, &ace_hostname, 0); infof (data, "Input domain encoded as `%s'\n", stringprep_locale_charset ()); @@ -2616,147 +2815,21 @@ static void fix_hostname(struct connectdata *conn, struct hostname *host) } } #else + (void)data; /* never used */ (void)conn; /* never used */ #endif } - -/** - * CreateConnection() sets up a new connectdata struct, or re-uses an already - * existing one, and resolves host name. - * - * if this function returns CURLE_OK and *async is set to TRUE, the resolve - * response will be coming asynchronously. If *async is FALSE, the name is - * already resolved. - * - * @param data The sessionhandle pointer - * @param in_connect is set to the next connection data pointer - * @param addr is set to the new dns entry for this connection. If this - * connection is re-used it will be NULL. - * @param async is set TRUE/FALSE depending on the nature of this lookup - * @return CURLcode - * @see SetupConnection() +/* + * Parse URL and fill in the relevant members of the connection struct. */ - -static CURLcode CreateConnection(struct SessionHandle *data, - struct connectdata **in_connect, - struct Curl_dns_entry **addr, - bool *async) +static CURLcode ParseURLAndFillConnection(struct SessionHandle *data, + struct connectdata *conn) { - char *tmp; char *at; - CURLcode result=CURLE_OK; - struct connectdata *conn; - struct connectdata *conn_temp = NULL; - size_t urllen; - struct Curl_dns_entry *hostaddr; -#if defined(HAVE_ALARM) && !defined(USE_ARES) - unsigned int prev_alarm=0; -#endif - char endbracket; - char user[MAX_CURL_USER_LENGTH]; - char passwd[MAX_CURL_PASSWORD_LENGTH]; - int rc; - bool reuse; - -#ifndef USE_ARES -#ifdef SIGALRM -#ifdef HAVE_SIGACTION - struct sigaction keep_sigact; /* store the old struct here */ - bool keep_copysig=FALSE; /* did copy it? */ -#else -#ifdef HAVE_SIGNAL - void *keep_sigact; /* store the old handler here */ -#endif /* HAVE_SIGNAL */ -#endif /* HAVE_SIGACTION */ -#endif /* SIGALRM */ -#endif /* USE_ARES */ - - *addr = NULL; /* nothing yet */ - *async = FALSE; - - /************************************************************* - * Check input data - *************************************************************/ - - if(!data->change.url) - return CURLE_URL_MALFORMAT; - - /* First, split up the current URL in parts so that we can use the - parts for checking against the already present connections. In order - to not have to modify everything at once, we allocate a temporary - connection data struct and fill in for comparison purposes. */ - - conn = (struct connectdata *)calloc(sizeof(struct connectdata), 1); - if(!conn) { - *in_connect = NULL; /* clear the pointer */ - return CURLE_OUT_OF_MEMORY; - } - /* We must set the return variable as soon as possible, so that our - parent can cleanup any possible allocs we may have done before - any failure */ - *in_connect = conn; - - /* and we setup a few fields in case we end up actually using this struct */ - conn->data = data; /* remember our daddy */ - conn->sock[FIRSTSOCKET] = CURL_SOCKET_BAD; /* no file descriptor */ - conn->sock[SECONDARYSOCKET] = CURL_SOCKET_BAD; /* no file descriptor */ - conn->connectindex = -1; /* no index */ - conn->bits.httpproxy = (data->change.proxy && *data->change.proxy && - (data->set.proxytype == CURLPROXY_HTTP))? - TRUE:FALSE; /* http proxy or not */ - - /* 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; - - /* maxdownload must be -1 on init, as 0 is a valid value! */ - conn->maxdownload = -1; /* might have been used previously! */ - - /* Store creation time to help future close decision making */ - conn->created = Curl_tvnow(); - - conn->bits.use_range = data->set.set_range?TRUE:FALSE; /* range status */ - conn->range = data->set.set_range; /* clone the range setting */ - conn->resume_from = data->set.set_resume_from; /* inherit resume_from */ - - conn->bits.user_passwd = data->set.userpwd?1:0; - conn->bits.proxy_user_passwd = data->set.proxyuserpwd?1:0; - conn->bits.no_body = data->set.opt_no_body; - conn->bits.tunnel_proxy = data->set.tunnel_thru_httpproxy; - conn->bits.ftp_use_epsv = data->set.ftp_use_epsv; - conn->bits.ftp_use_eprt = data->set.ftp_use_eprt; - - /* This initing continues below, see the comment "Continue connectdata - * initialization here" */ - - /*********************************************************** - * We need to allocate memory to store the path in. We get the size of the - * full URL to be sure, and we need to make it at least 256 bytes since - * other parts of the code will rely on this fact - ***********************************************************/ -#define LEAST_PATH_ALLOC 256 - urllen=strlen(data->change.url); - if(urllen < LEAST_PATH_ALLOC) - urllen=LEAST_PATH_ALLOC; - - /* - * We malloc() the buffers below urllen+2 to make room for to possibilities: - * 1 - an extra terminating zero - * 2 - an extra slash (in case a syntax like "www.host.com?moo" is used) - */ + char *tmp; - conn->pathbuffer=(char *)malloc(urllen+2); - if(NULL == conn->pathbuffer) - return CURLE_OUT_OF_MEMORY; /* really bad error */ - conn->path = conn->pathbuffer; - - conn->host.rawalloc=(char *)malloc(urllen+2); - if(NULL == conn->host.rawalloc) - return CURLE_OUT_OF_MEMORY; - conn->host.name = conn->host.rawalloc; - conn->host.name[0] = 0; + char *path = data->reqdata.path; /************************************************************* * Parse the URL. @@ -2768,8 +2841,8 @@ static CURLcode CreateConnection(struct SessionHandle *data, ************************************************************/ if((2 == sscanf(data->change.url, "%15[^:]:%[^\n]", conn->protostr, - conn->path)) && strequal(conn->protostr, "file")) { - if(conn->path[0] == '/' && conn->path[1] == '/') { + path)) && strequal(conn->protostr, "file")) { + if(path[0] == '/' && path[1] == '/') { /* Allow omitted hostname (e.g. file:/<path>). This is not strictly * speaking a valid file: URL by RFC 1738, but treating file:/<path> as * file://localhost/<path> is similar to how other schemes treat missing @@ -2777,17 +2850,17 @@ static CURLcode CreateConnection(struct SessionHandle *data, /* This cannot be done with strcpy() in a portable manner, since the memory areas overlap! */ - memmove(conn->path, conn->path + 2, strlen(conn->path + 2)+1); + memmove(path, path + 2, strlen(path + 2)+1); } /* * we deal with file://<host>/<path> differently since it supports no * hostname other than "localhost" and "127.0.0.1", which is unique among * the URL protocols specified in RFC 1738 */ - if(conn->path[0] != '/') { + if(path[0] != '/') { /* the URL included a host name, we ignore host names in file:// URLs as the standards don't define what to do with them */ - char *ptr=strchr(conn->path, '/'); + char *ptr=strchr(path, '/'); if(ptr) { /* there was a slash present @@ -2810,7 +2883,7 @@ static CURLcode CreateConnection(struct SessionHandle *data, ptr++; /* This cannot be made with strcpy, as the memory chunks overlap! */ - memmove(conn->path, ptr, strlen(ptr)+1); + memmove(path, ptr, strlen(ptr)+1); } } @@ -2818,19 +2891,19 @@ static CURLcode CreateConnection(struct SessionHandle *data, } else { /* clear path */ - conn->path[0]=0; + path[0]=0; if (2 > sscanf(data->change.url, "%15[^\n:]://%[^\n/]%[^\n]", conn->protostr, - conn->host.name, conn->path)) { + conn->host.name, path)) { /* * The URL was badly formatted, let's try the browser-style _without_ * protocol specified like 'http://'. */ if((1 > sscanf(data->change.url, "%[^\n/]%[^\n]", - conn->host.name, conn->path)) ) { + conn->host.name, path)) ) { /* * We couldn't even get this format. */ @@ -2886,43 +2959,213 @@ static CURLcode CreateConnection(struct SessionHandle *data, */ size_t hostlen = strlen(tmp); - size_t pathlen = strlen(conn->path); + size_t pathlen = strlen(path); /* move the existing path plus the zero byte forward, to make room for the host-name part */ - memmove(conn->path+hostlen+1, conn->path, pathlen+1); + memmove(path+hostlen+1, path, pathlen+1); /* now copy the trailing host part in front of the existing path */ - memcpy(conn->path+1, tmp, hostlen); + memcpy(path+1, tmp, hostlen); - conn->path[0]='/'; /* prepend the missing slash */ + path[0]='/'; /* prepend the missing slash */ *tmp=0; /* now cut off the hostname at the ? */ } - else if(!conn->path[0]) { + else if(!path[0]) { /* if there's no path set, use a single slash */ - strcpy(conn->path, "/"); + strcpy(path, "/"); } /* If the URL is malformatted (missing a '/' after hostname before path) we * insert a slash here. The only letter except '/' we accept to start a path * is '?'. */ - if(conn->path[0] == '?') { + if(path[0] == '?') { /* We need this function to deal with overlapping memory areas. We know that the memory area 'path' points to is 'urllen' bytes big and that is bigger than the path. Use +1 to move the zero byte too. */ - memmove(&conn->path[1], conn->path, strlen(conn->path)+1); - conn->path[0] = '/'; + memmove(&path[1], path, strlen(path)+1); + path[0] = '/'; } /* * So if the URL was A://B/C, * conn->protostr is A * conn->host.name is B - * conn->path is /C + * data->reqdata.path is /C + */ + + return CURLE_OK; +} + +static void llist_dtor(void *user, void *element) +{ + (void)user; + (void)element; + /* Do nothing */ +} + + +/** + * CreateConnection() sets up a new connectdata struct, or re-uses an already + * existing one, and resolves host name. + * + * if this function returns CURLE_OK and *async is set to TRUE, the resolve + * response will be coming asynchronously. If *async is FALSE, the name is + * already resolved. + * + * @param data The sessionhandle pointer + * @param in_connect is set to the next connection data pointer + * @param addr is set to the new dns entry for this connection. If this + * connection is re-used it will be NULL. + * @param async is set TRUE/FALSE depending on the nature of this lookup + * @return CURLcode + * @see SetupConnection() + * + * *NOTE* this function assigns the conn->data pointer! + */ + +static CURLcode CreateConnection(struct SessionHandle *data, + struct connectdata **in_connect, + struct Curl_dns_entry **addr, + bool *async) +{ + + char *tmp; + CURLcode result=CURLE_OK; + struct connectdata *conn; + struct connectdata *conn_temp = NULL; + size_t urllen; + struct Curl_dns_entry *hostaddr; +#if defined(HAVE_ALARM) && !defined(USE_ARES) + unsigned int prev_alarm=0; +#endif + char endbracket; + char user[MAX_CURL_USER_LENGTH]; + char passwd[MAX_CURL_PASSWORD_LENGTH]; + int rc; + bool reuse; + +#ifndef USE_ARES +#ifdef SIGALRM +#ifdef HAVE_SIGACTION + struct sigaction keep_sigact; /* store the old struct here */ + bool keep_copysig=FALSE; /* did copy it? */ +#else +#ifdef HAVE_SIGNAL + void *keep_sigact; /* store the old handler here */ +#endif /* HAVE_SIGNAL */ +#endif /* HAVE_SIGACTION */ +#endif /* SIGALRM */ +#endif /* USE_ARES */ + + *addr = NULL; /* nothing yet */ + *async = FALSE; + + /************************************************************* + * Check input data + *************************************************************/ + + if(!data->change.url) + return CURLE_URL_MALFORMAT; + + /* First, split up the current URL in parts so that we can use the + parts for checking against the already present connections. In order + to not have to modify everything at once, we allocate a temporary + connection data struct and fill in for comparison purposes. */ + + conn = (struct connectdata *)calloc(sizeof(struct connectdata), 1); + if(!conn) { + *in_connect = NULL; /* clear the pointer */ + return CURLE_OUT_OF_MEMORY; + } + /* We must set the return variable as soon as possible, so that our + parent can cleanup any possible allocs we may have done before + any failure */ + *in_connect = conn; + + /* and we setup a few fields in case we end up actually using this struct */ + + conn->data = data; /* Setup the association between this connection + and the SessionHandle */ + + conn->sock[FIRSTSOCKET] = CURL_SOCKET_BAD; /* no file descriptor */ + conn->sock[SECONDARYSOCKET] = CURL_SOCKET_BAD; /* no file descriptor */ + conn->connectindex = -1; /* no index */ + conn->bits.httpproxy = (data->change.proxy && *data->change.proxy && + (data->set.proxytype == CURLPROXY_HTTP))? + TRUE:FALSE; /* http proxy or not */ + + /* 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; + + conn->readchannel_inuse = FALSE; + conn->writechannel_inuse = FALSE; + + /* Initialize the pipeline lists */ + conn->send_pipe = Curl_llist_alloc((curl_llist_dtor) llist_dtor); + conn->recv_pipe = Curl_llist_alloc((curl_llist_dtor) llist_dtor); + + /* Store creation time to help future close decision making */ + conn->created = Curl_tvnow(); + + data->reqdata.use_range = data->set.set_range?TRUE:FALSE; /* range status */ + + data->reqdata.range = data->set.set_range; /* clone the range setting */ + data->reqdata.resume_from = data->set.set_resume_from; + + conn->bits.user_passwd = data->set.userpwd?1:0; + conn->bits.proxy_user_passwd = data->set.proxyuserpwd?1:0; + conn->bits.no_body = data->set.opt_no_body; + conn->bits.tunnel_proxy = data->set.tunnel_thru_httpproxy; + conn->bits.ftp_use_epsv = data->set.ftp_use_epsv; + conn->bits.ftp_use_eprt = data->set.ftp_use_eprt; + + /* This initing continues below, see the comment "Continue connectdata + * initialization here" */ + + /*********************************************************** + * We need to allocate memory to store the path in. We get the size of the + * full URL to be sure, and we need to make it at least 256 bytes since + * other parts of the code will rely on this fact + ***********************************************************/ +#define LEAST_PATH_ALLOC 256 + urllen=strlen(data->change.url); + if(urllen < LEAST_PATH_ALLOC) + urllen=LEAST_PATH_ALLOC; + + if (!data->set.source_url /* 3rd party FTP */ + && data->reqdata.pathbuffer) { + /* Free the old buffer */ + free(data->reqdata.pathbuffer); + } + + /* + * We malloc() the buffers below urllen+2 to make room for to possibilities: + * 1 - an extra terminating zero + * 2 - an extra slash (in case a syntax like "www.host.com?moo" is used) */ + data->reqdata.pathbuffer=(char *)malloc(urllen+2); + if(NULL == data->reqdata.pathbuffer) + return CURLE_OUT_OF_MEMORY; /* really bad error */ + data->reqdata.path = data->reqdata.pathbuffer; + + conn->host.rawalloc=(char *)malloc(urllen+2); + if(NULL == conn->host.rawalloc) + return CURLE_OUT_OF_MEMORY; + + conn->host.name = conn->host.rawalloc; + conn->host.name[0] = 0; + + result = ParseURLAndFillConnection(data, conn); + if (result != CURLE_OK) { + return result; + } + /************************************************************* * Take care of proxy authentication stuff *************************************************************/ @@ -3087,14 +3330,14 @@ static CURLcode CreateConnection(struct SessionHandle *data, * server, we just fail since we can't rewind the file writing from within * this function. ***********************************************************/ - if(conn->resume_from) { - if(!conn->bits.use_range) { + if(data->reqdata.resume_from) { + if(!data->reqdata.use_range) { /* if it already was in use, we just skip this */ - conn->range = aprintf("%" FORMAT_OFF_T "-", conn->resume_from); - if(!conn->range) + data->reqdata.range = aprintf("%" FORMAT_OFF_T "-", data->reqdata.resume_from); + if(!data->reqdata.range) return CURLE_OUT_OF_MEMORY; - conn->bits.rangestringalloc = TRUE; /* mark as allocated */ - conn->bits.use_range = 1; /* switch on range usage */ + data->reqdata.rangestringalloc = TRUE; /* mark as allocated */ + data->reqdata.use_range = 1; /* switch on range usage */ } } #endif @@ -3160,7 +3403,7 @@ static CURLcode CreateConnection(struct SessionHandle *data, conn->port = port; conn->remote_port = (unsigned short)port; - conn->protocol |= PROT_FTP; + conn->protocol |= PROT_FTP|PROT_CLOSEACTION; if(data->change.proxy && *data->change.proxy && @@ -3188,11 +3431,11 @@ static CURLcode CreateConnection(struct SessionHandle *data, conn->curl_disconnect = Curl_ftp_disconnect; } - conn->path++; /* don't include the initial slash */ + data->reqdata.path++; /* don't include the initial slash */ /* FTP URLs support an extension like ";type=<typecode>" that * we'll try to get now! */ - type=strstr(conn->path, ";type="); + type=strstr(data->reqdata.path, ";type="); if(!type) { type=strstr(conn->host.rawalloc, ";type="); } @@ -3272,8 +3515,8 @@ static CURLcode CreateConnection(struct SessionHandle *data, /* Setup a "faked" transfer that'll do nothing */ if(CURLE_OK == result) { conn->bits.tcpconnect = TRUE; /* we are "connected */ - result = Curl_Transfer(conn, -1, -1, FALSE, NULL, /* no download */ - -1, NULL); /* no upload */ + result = Curl_setup_transfer(conn, -1, -1, FALSE, NULL, /* no download */ + -1, NULL); /* no upload */ } return result; @@ -3294,7 +3537,7 @@ static CURLcode CreateConnection(struct SessionHandle *data, conn->curl_done = Curl_tftp_done; /* TFTP URLs support an extension like ";mode=<typecode>" that * we'll try to get now! */ - type=strstr(conn->path, ";mode="); + type=strstr(data->reqdata.path, ";mode="); if(!type) { type=strstr(conn->host.rawalloc, ";mode="); } @@ -3580,7 +3823,7 @@ static CURLcode CreateConnection(struct SessionHandle *data, char *url; url = aprintf("http://%s:%d%s", conn->host.name, conn->remote_port, - conn->path); + data->reqdata.path); if(!url) return CURLE_OUT_OF_MEMORY; @@ -3704,6 +3947,8 @@ static CURLcode CreateConnection(struct SessionHandle *data, conn = conn_temp; /* use this connection from now on */ + conn->data = old_conn->data; + /* get the user+password information from the old_conn struct since it may * be new for this request even when we re-use an existing connection */ conn->bits.user_passwd = old_conn->bits.user_passwd; @@ -3740,23 +3985,17 @@ static CURLcode CreateConnection(struct SessionHandle *data, if (!conn->bits.httpproxy) free(old_conn->host.rawalloc); /* free the newly allocated name buffer */ - free(conn->pathbuffer); /* free the newly allocated path pointer */ - conn->pathbuffer = old_conn->pathbuffer; /* use the old one */ - conn->path = old_conn->path; - /* re-use init */ conn->bits.reuse = TRUE; /* yes, we're re-using here */ conn->bits.chunk = FALSE; /* always assume not chunked unless told otherwise */ - conn->maxdownload = -1; /* might have been used previously! */ Curl_safefree(old_conn->user); Curl_safefree(old_conn->passwd); Curl_safefree(old_conn->proxyuser); Curl_safefree(old_conn->proxypasswd); - - if(old_conn->bits.rangestringalloc) - free(old_conn->range); + Curl_llist_destroy(old_conn->send_pipe, NULL); + Curl_llist_destroy(old_conn->recv_pipe, NULL); free(old_conn); /* we don't need this anymore */ @@ -3764,28 +4003,29 @@ static CURLcode CreateConnection(struct SessionHandle *data, * If we're doing a resumed transfer, we need to setup our stuff * properly. */ - conn->resume_from = data->set.set_resume_from; - if (conn->resume_from) { - if (conn->bits.rangestringalloc == TRUE) - free(conn->range); - conn->range = aprintf("%" FORMAT_OFF_T "-", conn->resume_from); - if(!conn->range) + data->reqdata.resume_from = data->set.set_resume_from; + if (data->reqdata.resume_from) { + if (data->reqdata.rangestringalloc == TRUE) + free(data->reqdata.range); + data->reqdata.range = aprintf("%" FORMAT_OFF_T "-", + data->reqdata.resume_from); + if(!data->reqdata.range) return CURLE_OUT_OF_MEMORY; /* tell ourselves to fetch this range */ - conn->bits.use_range = TRUE; /* enable range download */ - conn->bits.rangestringalloc = TRUE; /* mark range string allocated */ + data->reqdata.use_range = TRUE; /* enable range download */ + data->reqdata.rangestringalloc = TRUE; /* mark range string allocated */ } else if (data->set.set_range) { /* There is a range, but is not a resume, useful for random ftp access */ - conn->range = strdup(data->set.set_range); - if(!conn->range) + data->reqdata.range = strdup(data->set.set_range); + if(!data->reqdata.range) return CURLE_OUT_OF_MEMORY; - conn->bits.rangestringalloc = TRUE; /* mark range string allocated */ - conn->bits.use_range = TRUE; /* enable range download */ + data->reqdata.rangestringalloc = TRUE; /* mark range string allocated */ + data->reqdata.use_range = TRUE; /* enable range download */ } else - conn->bits.use_range = FALSE; /* disable range download */ + data->reqdata.use_range = FALSE; /* disable range download */ *in_connect = conn; /* return this instead! */ @@ -3801,7 +4041,9 @@ static CURLcode CreateConnection(struct SessionHandle *data, ConnectionStore(data, conn); } - /* Continue connectdata initialization here. + /* Continue connectdata initialization here. */ + + /* * * Inherit the proper values from the urldata struct AFTER we have arranged * the persistent connection stuff */ @@ -3877,20 +4119,20 @@ static CURLcode CreateConnection(struct SessionHandle *data, /* we'll need to clear conn->dns_entry later in Curl_disconnect() */ if (conn->bits.httpproxy) - fix_hostname(conn, &conn->host); + fix_hostname(data, conn, &conn->host); } else { /* this is a fresh connect */ /* set a pointer to the hostname we display */ - fix_hostname(conn, &conn->host); + fix_hostname(data, conn, &conn->host); if(!data->change.proxy || !*data->change.proxy) { /* If not connecting via a proxy, extract the port from the URL, if it is * there, thus overriding any defaults that might have been set above. */ conn->port = conn->remote_port; /* it is the same port */ - /* Resolve target host right now */ + /* Resolve target host right on */ rc = Curl_resolv(conn, conn->host.name, (int)conn->port, &hostaddr); if(rc == CURLRESOLV_PENDING) *async = TRUE; @@ -3905,7 +4147,7 @@ static CURLcode CreateConnection(struct SessionHandle *data, /* This is a proxy that hasn't been resolved yet. */ /* IDN-fix the proxy name */ - fix_hostname(conn, &conn->proxy); + fix_hostname(data, conn, &conn->proxy); /* resolve proxy */ rc = Curl_resolv(conn, conn->proxy.name, (int)conn->port, &hostaddr); @@ -3973,14 +4215,16 @@ static CURLcode CreateConnection(struct SessionHandle *data, * * NOTE: the argument 'hostaddr' is NULL when this function is called for a * re-used connection. + * + * conn->data MUST already have been setup fine (in CreateConnection) */ static CURLcode SetupConnection(struct connectdata *conn, struct Curl_dns_entry *hostaddr, bool *protocol_done) { - struct SessionHandle *data = conn->data; CURLcode result=CURLE_OK; + struct SessionHandle *data = conn->data; Curl_pgrsTime(data, TIMER_NAMELOOKUP); @@ -4007,8 +4251,6 @@ static CURLcode SetupConnection(struct connectdata *conn, } } - conn->bytecount = 0; - conn->headerbytecount = 0; #ifdef CURL_DO_LINEEND_CONV data->state.crlf_conversions = 0; /* reset CRLF conversion counter */ #endif /* CURL_DO_LINEEND_CONV */ @@ -4017,7 +4259,7 @@ static CURLcode SetupConnection(struct connectdata *conn, bool connected = FALSE; /* Connect only if not already connected! */ - result = ConnectPlease(conn, hostaddr, &connected); + result = ConnectPlease(data, conn, hostaddr, &connected); if(connected) { result = Curl_protocol_connect(conn, protocol_done); @@ -4088,6 +4330,9 @@ CURLcode Curl_connect(struct SessionHandle *data, Curl_disconnect(*in_connect); /* close the connection */ *in_connect = NULL; /* return a NULL */ } + } else { + if ((*in_connect)->is_in_pipeline) + data->state.is_in_pipeline = TRUE; } return code; @@ -4125,7 +4370,7 @@ CURLcode Curl_done(struct connectdata **connp, { CURLcode result; struct connectdata *conn = *connp; - struct SessionHandle *data=conn->data; + struct SessionHandle *data = conn->data; Curl_expire(data, 0); /* stop timer */ @@ -4135,9 +4380,16 @@ CURLcode Curl_done(struct connectdata **connp, conn->bits.done = TRUE; /* called just now! */ /* cleanups done even if the connection is re-used */ - if(conn->bits.rangestringalloc) { - free(conn->range); - conn->bits.rangestringalloc = FALSE; + + if(data->reqdata.rangestringalloc) { + free(data->reqdata.range); + data->reqdata.rangestringalloc = FALSE; + } + + /* Cleanup possible redirect junk */ + if(data->reqdata.newurl) { + free(data->reqdata.newurl); + data->reqdata.newurl = NULL; } if(conn->dns_entry) { @@ -4145,12 +4397,6 @@ CURLcode Curl_done(struct connectdata **connp, conn->dns_entry = NULL; } - /* Cleanup possible redirect junk */ - if(conn->newurl) { - free(conn->newurl); - conn->newurl = NULL; - } - /* this calls the protocol-specific function pointer previously set */ if(conn->curl_done) result = conn->curl_done(conn, status); @@ -4188,6 +4434,8 @@ CURLcode Curl_done(struct connectdata **connp, infof(data, "Connection #%ld to host %s left intact\n", conn->connectindex, conn->bits.httpproxy?conn->proxy.dispname:conn->host.dispname); + + ConnectionDone(conn); /* the connection is no longer in use */ } return result; @@ -4197,7 +4445,7 @@ CURLcode Curl_do(struct connectdata **connp, bool *done) { CURLcode result=CURLE_OK; struct connectdata *conn = *connp; - struct SessionHandle *data=conn->data; + struct SessionHandle *data = conn->data; conn->bits.done = FALSE; /* Curl_done() is not called yet */ conn->bits.do_more = FALSE; /* by default there's no curl_do_more() to use */ |