Newer
Older
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
}
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 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;
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;
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);
}
else {
/*
* This is a brand new connection, so let's store it in the connection
* cache of ours!
*/
ConnectionStore(data, conn);
}
/* 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 */
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
}
/* 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;
}
/* 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.
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)) {
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 {
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
5466
5467
5468
5469
5470
5471
5472
5473
5474
5475
5476
5477
5478
5479
5480
5481
5482
5483
5484
5485
5486
5487
5488
5489
5490
5491
5492
5493
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;
Daniel Stenberg
committed
if(data->state.proto.generic && data->state.current_conn != conn) {
free(data->state.proto.generic);
data->state.proto.generic = NULL;
Daniel Stenberg
committed
data->state.current_conn = conn;