Newer
Older
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();
if(CURL_SOCKET_BAD == conn->sock[FIRSTSOCKET]) {
conn->bits.tcpconnect[FIRSTSOCKET] = FALSE;
result = Curl_connecthost(conn, conn->dns_entry);
Curl_pgrsTime(data, TIMER_CONNECT); /* we're connected already */
Curl_pgrsTime(data, TIMER_APPCONNECT); /* we're connected already */
conn->bits.tcpconnect[FIRSTSOCKET] = TRUE;
*protocol_done = TRUE;
Curl_updateconninfo(conn, conn->sock[FIRSTSOCKET]);
Curl_verboseconnect(conn);
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 */
result = create_conn(data, in_connect, asyncp);
Daniel Stenberg
committed
Daniel Stenberg
committed
/* 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) */
result = Curl_setup_conn(*in_connect, protocol_done);
Daniel Stenberg
committed
}
Daniel Stenberg
committed
}
if(result == CURLE_NO_CONNECTION_AVAILABLE) {
*in_connect = NULL;
if(result && *in_connect) {
/* 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;
DEBUGF(infof(data, "Curl_done\n"));
if(data->state.done)
Daniel Stenberg
committed
/* Stop if Curl_done() has already been called */
return CURLE_OK;
Curl_getoff_all_pipelines(data, conn);
/* Cleanup possible redirect junk */
Markus Elfring
committed
free(data->req.newurl);
data->req.newurl = NULL;
free(data->req.location);
data->req.location = NULL;
switch(status) {
case CURLE_ABORTED_BY_CALLBACK:
case CURLE_READ_ERROR:
case CURLE_WRITE_ERROR:
/* 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;
default:
break;
}
/* this calls the protocol-specific function pointer previously set */
Patrick Monnerat
committed
if(conn->handler->done)
result = conn->handler->done(conn, status, premature);
if(CURLE_ABORTED_BY_CALLBACK != result) {
/* avoid this if we already aborted by callback to avoid this calling
another callback */
CURLcode rc = Curl_pgrsDone(conn);
if(!result && rc)
result = CURLE_ABORTED_BY_CALLBACK;
}
if((conn->send_pipe->size + conn->recv_pipe->size != 0 &&
!data->set.reuse_forbid &&
!conn->bits.close)) {
/* Stop if pipeline is not empty and we do not have to close
connection. */
DEBUGF(infof(data, "Connection still in use, no more Curl_done now!\n"));
return CURLE_OK;
}
data->state.done = TRUE; /* called just now! */
Curl_resolver_cancel(conn);
if(conn->dns_entry) {
Curl_resolv_unlock(data, conn->dns_entry); /* done with this */
conn->dns_entry = NULL;
}
Daniel Stenberg
committed
/* if the transfer was completed in a paused state there can be buffered
data left to write and then kill */
Markus Elfring
committed
free(data->state.tempwrite);
data->state.tempwrite = NULL;
Daniel Stenberg
committed
Daniel Stenberg
committed
/* if data->set.reuse_forbid is TRUE, it means the libcurl client has
forced us to close this connection. This is ignored for requests taking
place in a NTLM authentication handshake
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
#if defined(USE_NTLM)
&& !(conn->ntlm.state == NTLMSTATE_TYPE2 ||
conn->proxyntlm.state == NTLMSTATE_TYPE2)
#endif
) || 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
*/
Curl_free_request_state(data);
Daniel Stenberg
committed
/*
* Curl_init_do() 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.
*
* Allow this function to get called with 'conn' set to NULL.
Daniel Stenberg
committed
*/
CURLcode Curl_init_do(struct SessionHandle *data, struct connectdata *conn)
Daniel Stenberg
committed
{
Daniel Stenberg
committed
struct SingleRequest *k = &data->req;
Daniel Stenberg
committed
if(conn)
conn->bits.do_more = FALSE; /* by default there's no curl_do_more() to
* use */
data->state.done = FALSE; /* Curl_done() is not called yet */
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
6265
6266
6267
6268
6269
6270
6271
6272
6273
6274
6275
6276
6277
6278
6279
6280
6281
6282
6283
6284
6285
6286
6287
6288
6289
6290
6291
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);
/* ... 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
/* 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.
*
* 'complete' can return 0 for incomplete, 1 for done and -1 for go back to
* DOING state there's more work to do!
CURLcode Curl_do_more(struct connectdata *conn, int *complete)
{
CURLcode result=CURLE_OK;
*complete = 0;
Patrick Monnerat
committed
if(conn->handler->do_more)
result = conn->handler->do_more(conn, complete);
if(!result && (*complete == 1))
/* do_complete must be called after the protocol-specific DO function */
do_complete(conn);
return result;
}