Newer
Older
* 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
Daniel Stenberg
committed
* @param async is set TRUE when an async DNS resolution is pending
*
* *NOTE* this function assigns the conn->data pointer!
*/
static CURLcode create_conn(struct SessionHandle *data,
struct connectdata **in_connect,
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];
char options[MAX_CURL_OPTIONS_LENGTH];
bool reuse;
char *proxy = NULL;
bool prot_missing = FALSE;
bool no_connections_available = FALSE;
bool force_reuse;
size_t max_host_connections = Curl_multi_max_host_connections(data->multi);
size_t max_total_connections = Curl_multi_max_total_connections(data->multi);
*async = FALSE;
Daniel Stenberg
committed
/*************************************************************
* 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. */
if(!conn)
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;
/* 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 2 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) {
Curl_safefree(data->state.pathbuffer);
data->state.path = NULL;
return CURLE_OUT_OF_MEMORY;
conn->host.name = conn->host.rawalloc;
conn->host.name[0] = 0;
result = parseurlandfillconn(data, conn, &prot_missing, user, passwd,
options);
if(result != CURLE_OK)
return result;
Daniel Stenberg
committed
/*************************************************************
* No protocol part in URL was used, add it!
*************************************************************/
if(prot_missing) {
Daniel Stenberg
committed
/* 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;
Daniel Stenberg
committed
reurl = aprintf("%s://%s", conn->handler->scheme, data->change.url);
Daniel Stenberg
committed
if(!reurl) {
Curl_safefree(proxy);
return CURLE_OUT_OF_MEMORY;
}
if(data->change.url_alloc) {
Curl_safefree(data->change.url);
data->change.url_alloc = FALSE;
}
Daniel Stenberg
committed
data->change.url = reurl;
data->change.url_alloc = TRUE; /* free this later */
}
/*************************************************************
* If the protocol can't handle url query strings, then cut
* of the unhandable part
*************************************************************/
if((conn->given->flags&PROTOPT_NOURLQUERY)) {
char *path_q_sep = strchr(conn->data->state.path, '?');
if(path_q_sep) {
/* according to rfc3986, allow the query (?foo=bar)
also on protocols that can't handle it.
cut the string-part after '?'
*/
/* terminate the string */
path_q_sep[0] = 0;
}
}
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(data->set.str[STRING_NOPROXY] &&
check_noproxy(conn->host.name, data->set.str[STRING_NOPROXY])) {
if(proxy) {
Daniel Stenberg
committed
free(proxy); /* proxy is in exception list */
proxy = NULL;
}
}
Daniel Stenberg
committed
else if(!proxy)
proxy = detect_proxy(conn);
if(proxy && (!*proxy || (conn->handler->flags & PROTOPT_NONETWORK))) {
free(proxy); /* Don't bother with an empty proxy string or if the
protocol doesn't work with network */
proxy = NULL;
}
/***********************************************************************
* If this is supposed to use a proxy, we need to figure out the proxy host
* name, proxy type and port number, so that we can re-use an existing
* connection that may exist registered to the same proxy host.
***********************************************************************/
if(proxy) {
result = parse_proxy(data, conn, proxy);
free(proxy); /* parse_proxy copies the proxy string */
if(result)
if((conn->proxytype == CURLPROXY_HTTP) ||
(conn->proxytype == CURLPROXY_HTTP_1_0)) {
#ifdef CURL_DISABLE_HTTP
/* asking for a HTTP proxy is a bit funny when HTTP is disabled... */
return CURLE_UNSUPPORTED_PROTOCOL;
#else
/* force this connection's protocol to become HTTP if not already
compatible - if it isn't tunneling through */
if(!(conn->handler->protocol & CURLPROTO_HTTP) &&
!conn->bits.tunnel_proxy)
conn->handler = &Curl_handler_http;
Daniel Stenberg
committed
conn->bits.httpproxy = TRUE;
#endif
Daniel Stenberg
committed
}
else
conn->bits.httpproxy = FALSE; /* not a HTTP proxy */
Daniel Stenberg
committed
conn->bits.proxy = TRUE;
}
else {
Daniel Stenberg
committed
/* 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 */
Daniel Stenberg
committed
/*************************************************************
* Setup internals depending on protocol. Needs to be done after
* we figured out what/if proxy to use.
*************************************************************/
Daniel Stenberg
committed
result = setup_connection_internals(conn);
Daniel Stenberg
committed
if(result != CURLE_OK) {
Curl_safefree(proxy);
return result;
}
conn->recv[FIRSTSOCKET] = Curl_recv_plain;
conn->send[FIRSTSOCKET] = Curl_send_plain;
conn->recv[SECONDARYSOCKET] = Curl_recv_plain;
conn->send[SECONDARYSOCKET] = Curl_send_plain;
/***********************************************************************
* file: is a special case in that it doesn't need a network connection
***********************************************************************/
if(conn->handler->flags & PROTOPT_NONETWORK) {
/* 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[FIRSTSOCKET] = 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
}
Curl_setup_transfer(conn, -1, -1, FALSE, NULL, /* no download */
-1, NULL); /* no upload */
Daniel Stenberg
committed
}
/* since we skip do_init() */
Curl_speedinit(data);
return result;
#endif
/*************************************************************
* If the protocol is using SSL and HTTP proxy is used, we set
* the tunnel_proxy bit.
*************************************************************/
if((conn->given->flags&PROTOPT_SSL) && conn->bits.httpproxy)
conn->bits.tunnel_proxy = TRUE;
Daniel Stenberg
committed
/*************************************************************
* 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 overridden login details and set them accordingly */
override_login(data, conn, user, passwd, options);
result = set_login(conn, user, passwd, options);
if(result != CURLE_OK)
return result;
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];
#ifdef USE_TLS_SRP
data->set.ssl.username = data->set.str[STRING_TLSAUTH_USERNAME];
data->set.ssl.password = data->set.str[STRING_TLSAUTH_PASSWORD];
#endif
Daniel Stenberg
committed
Daniel Stenberg
committed
if(!Curl_clone_ssl_config(&data->set.ssl, &conn->ssl_config))
return CURLE_OUT_OF_MEMORY;
Daniel Stenberg
committed
/*************************************************************
* 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
/* 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;
reuse = ConnectionExists(data, conn, &conn_temp, &force_reuse);
/* If we found a reusable connection, we may still want to
open a new connection if we are pipelining. */
if(reuse && !force_reuse && IsPipeliningPossible(data, conn_temp)) {
size_t pipelen = conn_temp->send_pipe->size + conn_temp->recv_pipe->size;
if(pipelen > 0) {
infof(data, "Found connection %ld, with requests in the pipe (%zu)\n",
conn_temp->connection_id, pipelen);
if(conn_temp->bundle->num_connections < max_host_connections &&
data->state.conn_cache->num_connections < max_total_connections) {
/* We want a new connection anyway */
reuse = FALSE;
infof(data, "We can reuse, but we want a new connection anyway\n");
}
}
}
Daniel Stenberg
committed
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.
*/
conn_temp->inuse = TRUE; /* mark this as being in use so that no other
handle in a multi stack may nick it */
reuse_conn(conn, conn_temp);
free(conn); /* we don't need this anymore */
conn = conn_temp;
*in_connect = conn;
/* set a pointer to the hostname we display */
fix_hostname(data, conn, &conn->host);
infof(data, "Re-using existing connection! (#%ld) with host %s\n",
conn->connection_id,
conn->proxy.name?conn->proxy.dispname:conn->host.dispname);
5374
5375
5376
5377
5378
5379
5380
5381
5382
5383
5384
5385
5386
5387
5388
5389
5390
5391
5392
5393
5394
5395
5396
5397
5398
5399
5400
5401
5402
5403
5404
5405
5406
5407
5408
5409
5410
5411
5412
5413
5414
5415
5416
5417
5418
5419
5420
5421
5422
5423
5424
5425
5426
5427
5428
/* We have decided that we want a new connection. However, we may not
be able to do that if we have reached the limit of how many
connections we are allowed to open. */
struct connectbundle *bundle;
bundle = Curl_conncache_find_bundle(data->state.conn_cache,
conn->host.name);
if(max_host_connections > 0 && bundle &&
(bundle->num_connections >= max_host_connections)) {
struct connectdata *conn_candidate;
/* The bundle is full. Let's see if we can kill a connection. */
conn_candidate = find_oldest_idle_connection_in_bundle(data, bundle);
if(conn_candidate) {
/* Set the connection's owner correctly, then kill it */
conn_candidate->data = data;
(void)Curl_disconnect(conn_candidate, /* dead_connection */ FALSE);
}
else
no_connections_available = TRUE;
}
if(max_total_connections > 0 &&
(data->state.conn_cache->num_connections >= max_total_connections)) {
struct connectdata *conn_candidate;
/* The cache is full. Let's see if we can kill a connection. */
conn_candidate = find_oldest_idle_connection(data);
if(conn_candidate) {
/* Set the connection's owner correctly, then kill it */
conn_candidate->data = data;
(void)Curl_disconnect(conn_candidate, /* dead_connection */ FALSE);
}
else
no_connections_available = TRUE;
}
if(no_connections_available) {
infof(data, "No connections available.\n");
conn_free(conn);
*in_connect = NULL;
return CURLE_NO_CONNECTION_AVAILABLE;
}
else {
/*
* This is a brand new connection, so let's store it in the connection
* cache of ours!
*/
ConnectionStore(data, conn);
}
/* Mark the connection as used */
conn->inuse = TRUE;
/* Setup and init stuff before DO starts, in preparing for the transfer. */
do_init(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
*************************************************************/
Daniel Stenberg
committed
result = resolve_server(data, conn, async);
Daniel Stenberg
committed
return result;
}
/* Curl_setup_conn() is called after the name resolve initiated in
Daniel Stenberg
committed
* create_conn() is all done.
* Curl_setup_conn() also handles reused connections
Daniel Stenberg
committed
*
Daniel Stenberg
committed
* conn->data MUST already have been setup fine (in create_conn)
Daniel Stenberg
committed
*/
CURLcode Curl_setup_conn(struct connectdata *conn,
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->handler->flags & PROTOPT_NONETWORK) {
/* nothing to setup when not using a network */
*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 */
/* set start time here for timeout purposes in the connect procedure, it
is later set again for the progress meter purpose */
conn->now = Curl_tvnow();
Daniel Stenberg
committed
for(;;) {
/* loop for CURL_SERVER_CLOSED_CONNECTION */
Daniel Stenberg
committed
if(CURL_SOCKET_BAD == conn->sock[FIRSTSOCKET]) {
Daniel Stenberg
committed
/* Try to connect only if not already connected */
Daniel Stenberg
committed
bool connected = FALSE;
Daniel Stenberg
committed
result = ConnectPlease(data, conn, &connected);
if(result && !conn->ip_addr) {
/* transport connection failure not related with authentication */
conn->bits.tcpconnect[FIRSTSOCKET] = FALSE;
return result;
}
Daniel Stenberg
committed
if(connected) {
result = Curl_protocol_connect(conn, protocol_done);
if(CURLE_OK == result)
conn->bits.tcpconnect[FIRSTSOCKET] = TRUE;
Daniel Stenberg
committed
}
else
conn->bits.tcpconnect[FIRSTSOCKET] = 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 */
conn->bits.tcpconnect[FIRSTSOCKET] = TRUE;
Daniel Stenberg
committed
*protocol_done = TRUE;
Curl_verboseconnect(conn);
Curl_updateconninfo(conn, conn->sock[FIRSTSOCKET]);
Daniel Stenberg
committed
}
/* 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
*asyncp = FALSE; /* assume synchronous resolves by default */
/* call the stuff that needs to be called */
Daniel Stenberg
committed
code = create_conn(data, in_connect, 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;
else if(!*asyncp) {
Daniel Stenberg
committed
/* DNS resolution is done: that's either because this is a reused
connection, in which case DNS was unnecessary, or because DNS
really did finish already (synch resolver/fast async resolve) */
code = Curl_setup_conn(*in_connect, protocol_done);
Daniel Stenberg
committed
}
Daniel Stenberg
committed
}
if(code == CURLE_NO_CONNECTION_AVAILABLE) {
*in_connect = NULL;
return code;
}
/* We're not allowed to return failure with memory left allocated
in the connectdata struct, free those here */
Curl_disconnect(*in_connect, FALSE); /* close the connection */
Daniel Stenberg
committed
*in_connect = NULL; /* return a NULL */
Daniel Stenberg
committed
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
if(conn->bits.done)
/* Stop if Curl_done() has already been called */
return CURLE_OK;
Curl_getoff_all_pipelines(data, conn);
if((conn->send_pipe->size + conn->recv_pipe->size != 0 &&
Daniel Stenberg
committed
!data->set.reuse_forbid &&
!conn->bits.close))
Daniel Stenberg
committed
/* Stop if pipeline is not empty and we do not have to close
connection. */
Daniel Stenberg
committed
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;
}
if(status == CURLE_ABORTED_BY_CALLBACK)
/* When we're aborted due to a callback return code it basically have to
be counted as premature as there is trouble ahead if we don't. We have
many callbacks and protocols work differently, we could potentially do
this more fine-grained in the future. */
premature = TRUE;
/* this calls the protocol-specific function pointer previously set */
Patrick Monnerat
committed
if(conn->handler->done)
result = conn->handler->done(conn, status, premature);
if(Curl_pgrsDone(conn) && !result)
result = CURLE_ABORTED_BY_CALLBACK;
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;
}
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.
*/
if(data->set.reuse_forbid || conn->bits.close || premature) {
CURLcode res2 = Curl_disconnect(conn, premature); /* close 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 {
/* the connection is no longer in use */
if(ConnectionDone(data, conn)) {
/* remember the most recently used connection */
data->state.lastconnect = conn;
infof(data, "Connection #%ld to host %s left intact\n",
conn->connection_id,
conn->bits.httpproxy?conn->proxy.dispname:conn->host.dispname);
}
else
data->state.lastconnect = NULL;
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
5765
5766
5767
5768
5769
5770
5771
5772
5773
5774
5775
5776
5777
5778
5779
5780
5781
5782
5783
5784
5785
5786
5787
5788
5789
5790
5791
5792
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_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;
Daniel Stenberg
committed
conn->data->req.maxfd = (conn->sockfd>conn->writesockfd?
conn->sockfd:conn->writesockfd)+1;
Curl_pgrsTime(conn->data, TIMER_PRETRANSFER);
Daniel Stenberg
committed
}
CURLcode Curl_do(struct connectdata **connp, bool *done)
CURLcode result=CURLE_OK;
struct connectdata *conn = *connp;
struct SessionHandle *data = conn->data;
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) {
/*
* If the connection is using an easy handle, call reconnect
* to re-establish the connection. Otherwise, let the multi logic
* figure out how to re-establish the connection.
*/
if(!data->multi) {
result = Curl_reconnect_request(connp);
if(result == CURLE_OK) {
/* ... finally back to actually retry the DO phase */
conn = *connp; /* re-assign conn since Curl_reconnect_request
creates a new connection */
result = conn->handler->do_it(conn, done);
Daniel Stenberg
committed
}
}
else
return result;
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);
/*
* Curl_do_more() is called during the DO_MORE multi state. It is basically a
* second stage DO state which (wrongly) was introduced to support FTP's
* second connection.
*
* TODO: A future libcurl should be able to work away this state.
*
*/
CURLcode Curl_do_more(struct connectdata *conn, bool *completed)
{
CURLcode result=CURLE_OK;
*completed = FALSE;
Patrick Monnerat
committed
if(conn->handler->do_more)
result = conn->handler->do_more(conn, completed);
/* do_complete must be called after the protocol-specific DO function */
do_complete(conn);
return result;
}
/* Called on connect, and if there's already a protocol-specific struct
allocated for a different connection, this frees it that it can be setup
properly later on. */
void Curl_reset_reqproto(struct connectdata *conn)
{
struct SessionHandle *data = conn->data;
Curl_safefree(data->state.proto.generic);
data->state.proto.generic = NULL;