Newer
Older
struct Curl_dns_entry **addr,
bool *async)
{
CURLcode result=CURLE_OK;
long shortest = 0; /* default to no timeout */
/*************************************************************
* Set timeout if that is being used
*************************************************************/
if(data->set.timeout || data->set.connecttimeout) {
/* We set the timeout on the name resolving phase first, separately from
* the download/upload part to allow a maximum time on everything. This is
* a signal-based timeout, why it won't work and shouldn't be used in
* multi-threaded environments. */
shortest = data->set.timeout; /* default to this timeout value */
if(shortest && data->set.connecttimeout &&
(data->set.connecttimeout < shortest))
/* if both are set, pick the shortest */
shortest = data->set.connecttimeout;
else if(!shortest)
/* if timeout is not set, use the connect timeout */
shortest = data->set.connecttimeout;
/* We can expect the conn->created time to be "now", as that was just
recently set in the beginning of this function and nothing slow
has been done since then until now. */
4028
4029
4030
4031
4032
4033
4034
4035
4036
4037
4038
4039
4040
4041
4042
4043
4044
4045
4046
4047
4048
4049
4050
4051
4052
4053
4054
}
/*************************************************************
* Resolve the name of the server or proxy
*************************************************************/
if(conn->bits.reuse) {
/* re-used connection, no resolving is necessary */
*addr = NULL;
/* we'll need to clear conn->dns_entry later in Curl_disconnect() */
if(conn->bits.proxy)
fix_hostname(data, conn, &conn->host);
}
else {
/* this is a fresh connect */
int rc;
struct Curl_dns_entry *hostaddr;
/* set a pointer to the hostname we display */
fix_hostname(data, conn, &conn->host);
if(!conn->proxy.name || !*conn->proxy.name) {
/* 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 on */
rc = Curl_resolv_timeout(conn, conn->host.name, (int)conn->port,
&hostaddr, shortest);
if(rc == CURLRESOLV_PENDING)
*async = TRUE;
else if (rc == CURLRESOLV_TIMEDOUT)
result = CURLE_OPERATION_TIMEDOUT;
else if(!hostaddr) {
failf(data, "Couldn't resolve host '%s'", conn->host.dispname);
result = CURLE_COULDNT_RESOLVE_HOST;
/* don't return yet, we need to clean up the timeout first */
}
}
else {
/* This is a proxy that hasn't been resolved yet. */
/* IDN-fix the proxy name */
fix_hostname(data, conn, &conn->proxy);
/* resolve proxy */
rc = Curl_resolv_timeout(conn, conn->proxy.name, (int)conn->port,
&hostaddr, shortest);
if(rc == CURLRESOLV_PENDING)
*async = TRUE;
else if (rc == CURLRESOLV_TIMEDOUT)
result = CURLE_OPERATION_TIMEDOUT;
4085
4086
4087
4088
4089
4090
4091
4092
4093
4094
4095
4096
4097
4098
4099
4100
4101
4102
4103
4104
4105
4106
4107
4108
4109
4110
4111
4112
4113
4114
4115
4116
4117
4118
4119
4120
4121
4122
4123
4124
4125
4126
4127
4128
4129
4130
4131
4132
4133
4134
4135
4136
4137
4138
4139
4140
4141
4142
4143
4144
4145
4146
4147
4148
4149
4150
4151
4152
4153
4154
4155
4156
4157
4158
4159
4160
4161
4162
4163
4164
4165
4166
4167
4168
4169
4170
4171
4172
4173
4174
4175
4176
4177
4178
4179
4180
4181
4182
4183
4184
4185
4186
4187
4188
4189
4190
4191
4192
4193
4194
4195
4196
4197
4198
4199
4200
4201
4202
4203
4204
4205
4206
4207
4208
4209
4210
4211
4212
4213
4214
4215
4216
4217
4218
4219
4220
else if(!hostaddr) {
failf(data, "Couldn't resolve proxy '%s'", conn->proxy.dispname);
result = CURLE_COULDNT_RESOLVE_PROXY;
/* don't return yet, we need to clean up the timeout first */
}
}
*addr = hostaddr;
}
return result;
}
/*
* Cleanup the connection just allocated before we can move along and use the
* previously existing one. All relevant data is copied over and old_conn is
* ready for freeing once this function returns.
*/
static void reuse_conn(struct connectdata *old_conn,
struct connectdata *conn)
{
if(old_conn->proxy.rawalloc)
free(old_conn->proxy.rawalloc);
/* free the SSL config struct from this connection struct as this was
allocated in vain and is targeted for destruction */
Curl_free_ssl_config(&old_conn->ssl_config);
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;
if(conn->bits.user_passwd) {
/* use the new user name and password though */
Curl_safefree(conn->user);
Curl_safefree(conn->passwd);
conn->user = old_conn->user;
conn->passwd = old_conn->passwd;
old_conn->user = NULL;
old_conn->passwd = NULL;
}
conn->bits.proxy_user_passwd = old_conn->bits.proxy_user_passwd;
if(conn->bits.proxy_user_passwd) {
/* use the new proxy user name and proxy password though */
Curl_safefree(conn->proxyuser);
Curl_safefree(conn->proxypasswd);
conn->proxyuser = old_conn->proxyuser;
conn->proxypasswd = old_conn->proxypasswd;
old_conn->proxyuser = NULL;
old_conn->proxypasswd = NULL;
}
/* host can change, when doing keepalive with a proxy ! */
if(conn->bits.proxy) {
free(conn->host.rawalloc);
conn->host=old_conn->host;
}
else
free(old_conn->host.rawalloc); /* free the newly allocated name buffer */
/* re-use init */
conn->bits.reuse = TRUE; /* yes, we're re-using here */
Curl_safefree(old_conn->user);
Curl_safefree(old_conn->passwd);
Curl_safefree(old_conn->proxyuser);
Curl_safefree(old_conn->proxypasswd);
Curl_llist_destroy(old_conn->send_pipe, NULL);
Curl_llist_destroy(old_conn->recv_pipe, NULL);
Curl_llist_destroy(old_conn->pend_pipe, NULL);
Curl_safefree(old_conn->master_buffer);
}
/**
* create_conn() 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 setup_conn()
*
* *NOTE* this function assigns the conn->data pointer!
*/
static CURLcode create_conn(struct SessionHandle *data,
struct connectdata **in_connect,
struct Curl_dns_entry **addr,
bool *async)
{
CURLcode result=CURLE_OK;
struct connectdata *conn;
struct connectdata *conn_temp = NULL;
size_t urllen;
char user[MAX_CURL_USER_LENGTH];
char passwd[MAX_CURL_PASSWORD_LENGTH];
bool reuse;
char *proxy = NULL;
*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 = allocate_conn();
/* 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;
if(!conn)
return CURLE_OUT_OF_MEMORY;
conn->data = data; /* Setup the association between this connection
and the SessionHandle */
conn->proxytype = data->set.proxytype; /* type */
Daniel Stenberg
committed
#ifdef CURL_DISABLE_PROXY
conn->bits.proxy = FALSE;
conn->bits.httpproxy = FALSE;
conn->bits.proxy_user_passwd = FALSE;
conn->bits.tunnel_proxy = FALSE;
#else /* CURL_DISABLE_PROXY */
conn->bits.proxy = (bool)(data->set.str[STRING_PROXY] &&
*data->set.str[STRING_PROXY]);
conn->bits.httpproxy = (bool)(conn->bits.proxy &&
(conn->proxytype == CURLPROXY_HTTP ||
conn->proxytype == CURLPROXY_HTTP_1_0));
conn->bits.proxy_user_passwd =
(bool)(NULL != data->set.str[STRING_PROXYUSERNAME]);
Daniel Stenberg
committed
conn->bits.tunnel_proxy = data->set.tunnel_thru_httpproxy;
Daniel Stenberg
committed
#endif /* CURL_DISABLE_PROXY */
conn->bits.user_passwd = (bool)(NULL != data->set.str[STRING_USERNAME]);
4243
4244
4245
4246
4247
4248
4249
4250
4251
4252
4253
4254
4255
4256
4257
4258
4259
4260
4261
4262
4263
4264
4265
4266
4267
4268
4269
4270
4271
4272
4273
4274
conn->bits.ftp_use_epsv = data->set.ftp_use_epsv;
conn->bits.ftp_use_eprt = data->set.ftp_use_eprt;
if(data->multi && Curl_multi_canPipeline(data->multi) &&
!conn->master_buffer) {
/* Allocate master_buffer to be used for pipelining */
conn->master_buffer = calloc(BUFSIZE, sizeof (char));
if(!conn->master_buffer)
return CURLE_OUT_OF_MEMORY;
}
/* 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);
conn->pend_pipe = Curl_llist_alloc((curl_llist_dtor) llist_dtor);
if(!conn->send_pipe || !conn->recv_pipe || !conn->pend_pipe)
return CURLE_OUT_OF_MEMORY;
/* 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)
*/
Curl_safefree(data->state.pathbuffer);
data->state.pathbuffer = malloc(urllen+2);
Daniel Stenberg
committed
if(NULL == data->state.pathbuffer)
return CURLE_OUT_OF_MEMORY; /* really bad error */
Daniel Stenberg
committed
data->state.path = data->state.pathbuffer;
conn->host.rawalloc = 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);
Daniel Stenberg
committed
if(result != CURLE_OK) {
return result;
}
Daniel Stenberg
committed
#ifndef CURL_DISABLE_PROXY
/*************************************************************
* Extract the user and password from the authentication string
*************************************************************/
if(conn->bits.proxy_user_passwd) {
result = parse_proxy_auth(data, conn);
Daniel Stenberg
committed
if(result != CURLE_OK)
return result;
}
/*************************************************************
* Detect what (if any) proxy to use
*************************************************************/
Daniel Stenberg
committed
if(data->set.str[STRING_PROXY]) {
proxy = strdup(data->set.str[STRING_PROXY]);
/* if global proxy is set, this is it */
if(NULL == proxy) {
failf(data, "memory shortage");
return CURLE_OUT_OF_MEMORY;
}
}
Daniel Stenberg
committed
if(!proxy)
proxy = detect_proxy(conn);
Daniel Stenberg
committed
else if(data->set.str[STRING_NOPROXY]) {
if(check_noproxy(conn->host.name, data->set.str[STRING_NOPROXY])) {
free(proxy); /* proxy is in exception list */
proxy = NULL;
}
}
Daniel Stenberg
committed
if(proxy && !*proxy) {
free(proxy); /* Don't bother with an empty proxy string */
proxy = NULL;
}
/* proxy must be freed later unless NULL */
Daniel Stenberg
committed
if(proxy && *proxy) {
long bits = conn->protocol & (PROT_HTTPS|PROT_SSL|PROT_MISSING);
if((conn->proxytype == CURLPROXY_HTTP) ||
(conn->proxytype == CURLPROXY_HTTP_1_0)) {
Daniel Stenberg
committed
/* force this connection's protocol to become HTTP */
conn->protocol = PROT_HTTP | bits;
conn->bits.httpproxy = TRUE;
}
conn->bits.proxy = TRUE;
}
else {
/* we aren't using the proxy after all... */
conn->bits.proxy = FALSE;
conn->bits.httpproxy = FALSE;
conn->bits.proxy_user_passwd = FALSE;
conn->bits.tunnel_proxy = FALSE;
}
Daniel Stenberg
committed
#endif /* CURL_DISABLE_PROXY */
/*************************************************************
* No protocol part in URL was used, add it!
*************************************************************/
if(conn->protocol&PROT_MISSING) {
/* We're guessing prefixes here and if we're told to use a proxy or if
we're gonna follow a Location: later or... then we need the protocol
part added so that we have a valid URL. */
char *reurl;
reurl = aprintf("%s://%s", conn->protostr, data->change.url);
if(!reurl) {
Curl_safefree(proxy);
}
data->change.url = reurl;
data->change.url_alloc = TRUE; /* free this later */
conn->protocol &= ~PROT_MISSING; /* switch that one off again */
/*************************************************************
* Setup internals depending on protocol
*************************************************************/
result = setup_connection_internals(data, conn);
Daniel Stenberg
committed
if(result != CURLE_OK) {
Curl_safefree(proxy);
return result;
}
/***********************************************************************
* If this is supposed to use a proxy, we need to figure out the proxy
* host name, so that we can re-use an existing connection
* that may exist registered to the same proxy host.
***********************************************************************/
Daniel Stenberg
committed
if(proxy) {
result = parse_proxy(data, conn, proxy);
/* parse_proxy has freed the proxy string, so don't try to use it again */
proxy = NULL;
Daniel Stenberg
committed
if(result != CURLE_OK)
return result;
}
#endif /* CURL_DISABLE_PROXY */
/***********************************************************************
* file: is a special case in that it doesn't need a network connection
***********************************************************************/
if(conn->protocol & PROT_FILE) {
bool done;
/* this is supposed to be the connect function so we better at least check
that the file is present here! */
DEBUGASSERT(conn->handler->connect_it);
result = conn->handler->connect_it(conn, &done);
/* Setup a "faked" transfer that'll do nothing */
if(CURLE_OK == result) {
conn->data = data;
conn->bits.tcpconnect = TRUE; /* we are "connected */
Daniel Stenberg
committed
ConnectionStore(data, conn);
Daniel Stenberg
committed
/*
* Setup whatever necessary for a resumed transfer
*/
result = setup_range(data);
if(result) {
DEBUGASSERT(conn->handler->done);
/* we ignore the return code for the protocol-specific DONE */
(void)conn->handler->done(conn, result, FALSE);
return result;
Daniel Stenberg
committed
}
result = Curl_setup_transfer(conn, -1, -1, FALSE,
NULL, /* no download */
-1, NULL); /* no upload */
Daniel Stenberg
committed
}
return result;
#endif
/*************************************************************
* If the protocol is using SSL and HTTP proxy is used, we set
* the tunnel_proxy bit.
*************************************************************/
if((conn->protocol&PROT_SSL) && conn->bits.httpproxy)
conn->bits.tunnel_proxy = TRUE;
Daniel Stenberg
committed
/*************************************************************
* Parse a user name and password in the URL and strip it out
* of the host name
*************************************************************/
result = parse_url_userpass(data, conn, user, passwd);
if(result != CURLE_OK)
return result;
/*************************************************************
* Figure out the remote port number and fix it in the URL
*************************************************************/
result = parse_remote_port(data, conn);
if(result != CURLE_OK)
return result;
/*************************************************************
* Check for an overridden user name and password, then set it
* for use
*************************************************************/
override_userpass(data, conn, user, passwd);
result = set_userpass(conn, user, passwd);
if(result != CURLE_OK)
return result;
/*************************************************************
* Check the current list of connections to see if we can
* re-use an already existing one or if we have to create a
* new one.
*************************************************************/
Daniel Stenberg
committed
/* Get a cloned copy of the SSL config situation stored in the
connection struct. But to get this going nicely, we must first make
sure that the strings in the master copy are pointing to the correct
strings in the session handle strings array!
Keep in mind that the pointers in the master copy are pointing to strings
that will be freed as part of the SessionHandle struct, but all cloned
copies will be separately allocated.
*/
data->set.ssl.CApath = data->set.str[STRING_SSL_CAPATH];
data->set.ssl.CAfile = data->set.str[STRING_SSL_CAFILE];
Daniel Stenberg
committed
data->set.ssl.CRLfile = data->set.str[STRING_SSL_CRLFILE];
Daniel Stenberg
committed
data->set.ssl.issuercert = data->set.str[STRING_SSL_ISSUERCERT];
Daniel Stenberg
committed
data->set.ssl.random_file = data->set.str[STRING_SSL_RANDOM_FILE];
data->set.ssl.egdsocket = data->set.str[STRING_SSL_EGDSOCKET];
data->set.ssl.cipher_list = data->set.str[STRING_SSL_CIPHER_LIST];
Daniel Stenberg
committed
if(!Curl_clone_ssl_config(&data->set.ssl, &conn->ssl_config))
return CURLE_OUT_OF_MEMORY;
Daniel Stenberg
committed
/* reuse_fresh is TRUE if we are told to use a new connection by force, but
we only acknowledge this option if this is not a re-used connection
already (which happens due to follow-location or during a HTTP
authentication phase). */
if(data->set.reuse_fresh && !data->state.this_is_a_follow)
Daniel Stenberg
committed
reuse = FALSE;
Daniel Stenberg
committed
reuse = ConnectionExists(data, conn, &conn_temp);
if(reuse) {
/*
* We already have a connection for this, we got the former connection
* in the conn_temp variable and thus we need to cleanup the one we
* just allocated before we can move along and use the previously
* existing one.
*/
reuse_conn(conn, conn_temp);
free(conn); /* we don't need this anymore */
conn = conn_temp;
*in_connect = conn;
infof(data, "Re-using existing connection! (#%ld) with host %s\n",
conn->connectindex,
conn->proxy.name?conn->proxy.dispname:conn->host.dispname);
Daniel Stenberg
committed
/* copy this IP address to the common buffer for the easy handle so that
the address can actually survice the removal of this connection. strcpy
is safe since the target buffer is big enough to hold the largest
possible IP address */
strcpy(data->info.ip, conn->ip_addr_str);
}
else {
/*
* This is a brand new connection, so let's store it in the connection
* cache of ours!
*/
ConnectionStore(data, conn);
}
/*
* Setup whatever necessary for a resumed transfer
*/
Daniel Stenberg
committed
result = setup_range(data);
if(result)
return result;
/* Continue connectdata initialization here. */
/*
* Inherit the proper values from the urldata struct AFTER we have arranged
* the persistent connection stuff
*/
conn->fread_func = data->set.fread_func;
conn->fread_in = data->set.in;
Daniel Stenberg
committed
conn->seek_func = data->set.seek_func;
conn->seek_client = data->set.seek_client;
/*************************************************************
* Resolve the address of the server or proxy
*************************************************************/
result = resolve_server(data, conn, addr, async);
Daniel Stenberg
committed
return result;
}
Daniel Stenberg
committed
/* setup_conn() is called after the name resolve initiated in
* create_conn() is all done.
*
* NOTE: the argument 'hostaddr' is NULL when this function is called for a
* re-used connection.
*
Daniel Stenberg
committed
* conn->data MUST already have been setup fine (in create_conn)
Daniel Stenberg
committed
*/
Daniel Stenberg
committed
static CURLcode setup_conn(struct connectdata *conn,
Daniel Stenberg
committed
struct Curl_dns_entry *hostaddr,
bool *protocol_done)
Daniel Stenberg
committed
{
CURLcode result=CURLE_OK;
struct SessionHandle *data = conn->data;
Daniel Stenberg
committed
Curl_pgrsTime(data, TIMER_NAMELOOKUP);
if(conn->protocol & PROT_FILE) {
Daniel Stenberg
committed
/* There's nothing in this function to setup if we're only doing
a file:// transfer */
*protocol_done = TRUE;
Daniel Stenberg
committed
return result;
}
*protocol_done = FALSE; /* default to not done */
Daniel Stenberg
committed
/* set proxy_connect_closed to false unconditionally already here since it
is used strictly to provide extra information to a parent function in the
case of proxy CONNECT failures and we must make sure we don't have it
lingering set from a previous invoke */
conn->bits.proxy_connect_closed = FALSE;
Daniel Stenberg
committed
/*
* Set user-agent. Used for HTTP, but since we can attempt to tunnel
* basically anything through a http proxy we can't limit this based on
* protocol.
*/
if(data->set.str[STRING_USERAGENT]) {
Daniel Stenberg
committed
Curl_safefree(conn->allocptr.uagent);
conn->allocptr.uagent =
Daniel Stenberg
committed
aprintf("User-Agent: %s\r\n", data->set.str[STRING_USERAGENT]);
Daniel Stenberg
committed
if(!conn->allocptr.uagent)
return CURLE_OUT_OF_MEMORY;
Daniel Stenberg
committed
data->req.headerbytecount = 0;
Daniel Stenberg
committed
#ifdef CURL_DO_LINEEND_CONV
data->state.crlf_conversions = 0; /* reset CRLF conversion counter */
#endif /* CURL_DO_LINEEND_CONV */
Daniel Stenberg
committed
for(;;) {
/* loop for CURL_SERVER_CLOSED_CONNECTION */
Daniel Stenberg
committed
if(CURL_SOCKET_BAD == conn->sock[FIRSTSOCKET]) {
bool connected = FALSE;
Daniel Stenberg
committed
/* Connect only if not already connected!
*
* NOTE: hostaddr can be NULL when passed to this function, but that is
* only for the case where we re-use an existing connection and thus
* this code section will not be reached with hostaddr == NULL.
*/
Daniel Stenberg
committed
result = ConnectPlease(data, conn, hostaddr, &connected);
Daniel Stenberg
committed
if(connected) {
result = Curl_protocol_connect(conn, protocol_done);
if(CURLE_OK == result)
conn->bits.tcpconnect = TRUE;
}
else
conn->bits.tcpconnect = FALSE;
Daniel Stenberg
committed
/* if the connection was closed by the server while exchanging
authentication informations, retry with the new set
authentication information */
Daniel Stenberg
committed
if(conn->bits.proxy_connect_closed) {
/* reset the error buffer */
Daniel Stenberg
committed
if(data->set.errorbuffer)
Daniel Stenberg
committed
data->set.errorbuffer[0] = '\0';
data->state.errorbuf = FALSE;
Daniel Stenberg
committed
continue;
Daniel Stenberg
committed
}
Daniel Stenberg
committed
if(CURLE_OK != result)
return result;
}
else {
Curl_pgrsTime(data, TIMER_CONNECT); /* we're connected already */
Curl_pgrsTime(data, TIMER_APPCONNECT); /* we're connected already */
Daniel Stenberg
committed
conn->bits.tcpconnect = TRUE;
*protocol_done = TRUE;
if(data->set.verbose)
verboseconnect(conn);
}
/* Stop the loop now */
break;
Daniel Stenberg
committed
conn->now = Curl_tvnow(); /* time this *after* the connect is done, we
set this here perhaps a second time */
/*
* This check is quite a hack. We're calling _fsetmode to fix the problem
* with fwrite converting newline characters (you get mangled text files,
* and corrupted binary files when you download to stdout and redirect it to
* a file).
*/
Daniel Stenberg
committed
if((data->set.out)->_handle == NULL) {
_fsetmode(stdout, "b");
}
#endif
return result;
Daniel Stenberg
committed
CURLcode Curl_connect(struct SessionHandle *data,
Daniel Stenberg
committed
struct connectdata **in_connect,
bool *asyncp,
bool *protocol_done)
Daniel Stenberg
committed
struct Curl_dns_entry *dns;
Daniel Stenberg
committed
*asyncp = FALSE; /* assume synchronous resolves by default */
/* call the stuff that needs to be called */
Daniel Stenberg
committed
code = create_conn(data, in_connect, &dns, asyncp);
Daniel Stenberg
committed
if(CURLE_OK == code) {
/* no error */
if((*in_connect)->send_pipe->size || (*in_connect)->recv_pipe->size)
Daniel Stenberg
committed
/* pipelining */
*protocol_done = TRUE;
Daniel Stenberg
committed
else {
Daniel Stenberg
committed
if(dns || !*asyncp)
/* If an address is available it means that we already have the name
resolved, OR it isn't async. if this is a re-used connection 'dns'
will be NULL here. Continue connecting from here */
Daniel Stenberg
committed
code = setup_conn(*in_connect, dns, protocol_done);
if(dns && code) {
/* We have the dns entry info already but failed to connect to the
* host and thus we must make sure to unlock the dns entry again
* before returning failure from here.
*/
Curl_resolv_unlock(data, dns);
}
Daniel Stenberg
committed
}
Daniel Stenberg
committed
}
/* We're not allowed to return failure with memory left allocated
in the connectdata struct, free those here */
Daniel Stenberg
committed
Curl_disconnect(*in_connect); /* close the connection */
*in_connect = NULL; /* return a NULL */
Daniel Stenberg
committed
Daniel Stenberg
committed
/* Call this function after Curl_connect() has returned async=TRUE and
then a successful name resolve has been received.
Note: this function disconnects and frees the conn data in case of
resolve failure */
CURLcode Curl_async_resolved(struct connectdata *conn,
bool *protocol_done)
Daniel Stenberg
committed
{
#if defined(USE_ARES) || defined(USE_THREADING_GETHOSTBYNAME) || \
defined(USE_THREADING_GETADDRINFO)
Daniel Stenberg
committed
CURLcode code = setup_conn(conn, conn->async.dns, protocol_done);
Daniel Stenberg
committed
if(code)
/* We're not allowed to return failure with memory left allocated
in the connectdata struct, free those here */
Curl_disconnect(conn); /* close the connection */
return code;
#else
(void)conn;
Daniel Stenberg
committed
return CURLE_OK;
#endif
}
Daniel Stenberg
committed
CURLcode Curl_done(struct connectdata **connp,
Daniel Stenberg
committed
CURLcode status, /* an error if this is called after an
error was detected */
bool premature)
struct connectdata *conn;
struct SessionHandle *data;
DEBUGASSERT(*connp);
conn = *connp;
data = conn->data;
Daniel Stenberg
committed
Curl_expire(data, 0); /* stop timer */
Curl_getoff_all_pipelines(data, conn);
Daniel Stenberg
committed
if(conn->bits.done ||
(conn->send_pipe->size + conn->recv_pipe->size != 0 &&
!data->set.reuse_forbid &&
!conn->bits.close))
/* Stop if Curl_done() has already been called or pipeline
is not empty and we do not have to close connection. */
return CURLE_OK;
conn->bits.done = TRUE; /* called just now! */
/* Cleanup possible redirect junk */
Daniel Stenberg
committed
if(data->req.newurl) {
free(data->req.newurl);
data->req.newurl = NULL;
}
Daniel Stenberg
committed
if(data->req.location) {
free(data->req.location);
data->req.location = NULL;
}
if(conn->dns_entry) {
Curl_resolv_unlock(data, conn->dns_entry); /* done with this */
conn->dns_entry = NULL;
}
/* this calls the protocol-specific function pointer previously set */
Patrick Monnerat
committed
if(conn->handler->done)
result = conn->handler->done(conn, status, premature);
Curl_pgrsDone(conn); /* done with the operation */
Daniel Stenberg
committed
/* if the transfer was completed in a paused state there can be buffered
data left to write and then kill */
if(data->state.tempwrite) {
free(data->state.tempwrite);
data->state.tempwrite = NULL;
}
/* for ares-using, make sure all possible outstanding requests are properly
cancelled before we proceed */
ares_cancel(data->state.areschannel);
Daniel Stenberg
committed
/* if data->set.reuse_forbid is TRUE, it means the libcurl client has
forced us to close this no matter what we think.
if conn->bits.close is TRUE, it means that the connection should be
closed in spite of all our efforts to be nice, due to protocol
Daniel Stenberg
committed
restrictions in our or the server's end
if premature is TRUE, it means this connection was said to be DONE before
the entire request operation is complete and thus we can't know in what
state it is for re-using, so we're forced to close it. In a perfect world
we can add code that keep track of if we really must close it here or not,
but currently we have no such detail knowledge.
connectindex == -1 here means that the connection has no spot in the
connection cache and thus we must disconnect it here.
Daniel Stenberg
committed
*/
if(data->set.reuse_forbid || conn->bits.close || premature ||
(-1 == conn->connectindex)) {
Daniel Stenberg
committed
CURLcode res2 = Curl_disconnect(conn); /* close the connection */
Daniel Stenberg
committed
/* If we had an error already, make sure we return that one. But
if we got a new error, return that. */
if(!result && res2)
result = res2;
}
Daniel Stenberg
committed
else {
Daniel Stenberg
committed
ConnectionDone(conn); /* the connection is no longer in use */
Daniel Stenberg
committed
/* remember the most recently used connection */
data->state.lastconnect = conn->connectindex;
infof(data, "Connection #%ld to host %s left intact\n",
conn->connectindex,
conn->bits.httpproxy?conn->proxy.dispname:conn->host.dispname);
Daniel Stenberg
committed
}
*connp = NULL; /* to make the caller of this function better detect that
this was either closed or handed over to the connection
cache here, and therefore cannot be used from this point on
*/
Daniel Stenberg
committed
/*
* do_init() inits the readwrite session. This is inited each time (in the DO
* function before the protocol-specific DO functions are invoked) for a
* transfer, sometimes multiple times on the same SessionHandle. Make sure
* nothing in here depends on stuff that are setup dynamically for the
* transfer.
Daniel Stenberg
committed
*/
static CURLcode do_init(struct connectdata *conn)
{
struct SessionHandle *data = conn->data;
Daniel Stenberg
committed
struct SingleRequest *k = &data->req;
Daniel Stenberg
committed
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 */
data->state.expect100header = FALSE;
Daniel Stenberg
committed
Daniel Stenberg
committed
if(data->set.opt_no_body)
/* in HTTP lingo, no body means using the HEAD request... */
data->set.httpreq = HTTPREQ_HEAD;
else if(HTTPREQ_HEAD == data->set.httpreq)
/* ... but if unset there really is no perfect method that is the
"opposite" of HEAD but in reality most people probably think GET
then. The important thing is that we can't let it remain HEAD if the
opt_no_body is set FALSE since then we'll behave wrong when getting
HTTP. */
data->set.httpreq = HTTPREQ_GET;
Daniel Stenberg
committed
/* NB: the content encoding software depends on this initialization */
Curl_easy_initHandleData(data);
Daniel Stenberg
committed
4884
4885
4886
4887
4888
4889
4890
4891
4892
4893
4894
4895
4896
4897
4898
4899
4900
4901
4902
4903
4904
4905
4906
4907
4908
4909
4910
4911
4912
k->start = Curl_tvnow(); /* start time */
k->now = k->start; /* current time is now */
k->header = TRUE; /* assume header */
k->bytecount = 0;
k->buf = data->state.buffer;
k->uploadbuf = data->state.uploadbuffer;
k->hbufp = data->state.headerbuff;
k->ignorebody=FALSE;
Curl_pgrsTime(data, TIMER_PRETRANSFER);
Curl_speedinit(data);
Curl_pgrsSetUploadCounter(data, 0);
Curl_pgrsSetDownloadCounter(data, 0);
return CURLE_OK;
}
/*
* do_complete is called when the DO actions are complete.
*
* We init chunking and trailer bits to their default values here immediately
* before receiving any header data for the current request in the pipeline.
*/
static void do_complete(struct connectdata *conn)
{
Daniel Stenberg
committed
conn->data->req.chunk=FALSE;
conn->data->req.trailerhdrpresent=FALSE;
Daniel Stenberg
committed
Daniel Stenberg
committed
conn->data->req.maxfd = (conn->sockfd>conn->writesockfd?
conn->sockfd:conn->writesockfd)+1;
Daniel Stenberg
committed
}
CURLcode Curl_do(struct connectdata **connp, bool *done)
CURLcode result=CURLE_OK;
struct connectdata *conn = *connp;
struct SessionHandle *data = conn->data;
Daniel Stenberg
committed
/* setup and init stuff before DO starts, in preparing for the transfer */
do_init(conn);
Patrick Monnerat
committed
if(conn->handler->do_it) {
/* generic protocol-specific function pointer set in curl_connect() */
Patrick Monnerat
committed
result = conn->handler->do_it(conn, done);
/* This was formerly done in transfer.c, but we better do it here */
if((CURLE_SEND_ERROR == result) && conn->bits.reuse) {
/* This was a re-use of a connection and we got a write error in the
* DO-phase. Then we DISCONNECT this connection and have another attempt
* to CONNECT and then DO again! The retry cannot possibly find another
* connection to re-use, since we only keep one possible connection for
* each. */
infof(data, "Re-used connection seems dead, get a new one\n");
conn->bits.close = TRUE; /* enforce close of this connection */
Daniel Stenberg
committed
result = Curl_done(&conn, result, FALSE); /* we are so done with this */
Daniel Stenberg
committed
/* conn may no longer be a good pointer */
Daniel Stenberg
committed
/*
* According to bug report #1330310. We need to check for
* CURLE_SEND_ERROR here as well. I figure this could happen when the
* request failed on a FTP connection and thus Curl_done() itself tried
* to use the connection (again). Slight Lack of feedback in the report,
* but I don't think this extra check can do much harm.
*/
if((CURLE_OK == result) || (CURLE_SEND_ERROR == result)) {
Daniel Stenberg
committed
bool async;
bool protocol_done = TRUE;
/* Now, redo the connect and get a new connection */
result = Curl_connect(data, connp, &async, &protocol_done);
Daniel Stenberg
committed
if(CURLE_OK == result) {
/* We have connected or sent away a name resolve query fine */
conn = *connp; /* setup conn to again point to something nice */
Daniel Stenberg
committed
if(async) {
/* Now, if async is TRUE here, we need to wait for the name
to resolve */
result = Curl_wait_for_resolv(conn, NULL);
if(result)
return result;
Daniel Stenberg
committed
/* Resolved, continue with the connection */
result = Curl_async_resolved(conn, &protocol_done);
Daniel Stenberg
committed
if(result)
return result;
}
/* ... finally back to actually retry the DO phase */
Patrick Monnerat
committed
result = conn->handler->do_it(conn, done);
Daniel Stenberg
committed
}
Daniel Stenberg
committed
if((result == CURLE_OK) && *done)
/* do_complete must be called after the protocol-specific DO function */
Daniel Stenberg
committed
do_complete(conn);
CURLcode Curl_do_more(struct connectdata *conn)
{
CURLcode result=CURLE_OK;
Patrick Monnerat
committed
if(conn->handler->do_more)
result = conn->handler->do_more(conn);
if(result == CURLE_OK)
/* do_complete must be called after the protocol-specific DO function */
do_complete(conn);