Newer
Older
/* 304 means we did a conditional request and it was "Not modified".
* We shouldn't get any Location: header in this response!
*/
break;
case 305: /* Use Proxy */
/* (quote from RFC2616, section 10.3.6):
* "The requested resource MUST be accessed through the proxy given
* by the Location field. The Location field gives the URI of the
* proxy. The recipient is expected to repeat this single request
* via the proxy. 305 responses MUST only be generated by origin
* servers."
*/
break;
}
Curl_pgrsTime(data, TIMER_REDIRECT);
Curl_pgrsResetTimes(data);
return CURLE_OK;
}
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
2052
static CURLcode
Curl_connect_host(struct SessionHandle *data,
struct connectdata **conn)
{
CURLcode res = CURLE_OK;
int urlchanged = FALSE;
do {
bool async;
Curl_pgrsTime(data, TIMER_STARTSINGLE);
data->change.url_changed = FALSE;
res = Curl_connect(data, conn, &async);
if((CURLE_OK == res) && async) {
/* Now, if async is TRUE here, we need to wait for the name
to resolve */
res = Curl_wait_for_resolv(*conn, NULL);
if(CURLE_OK == res)
/* Resolved, continue with the connection */
res = Curl_async_resolved(*conn);
}
if(res)
break;
/* If a callback (or something) has altered the URL we should use within
the Curl_connect(), we detect it here and act as if we are redirected
to the new URL */
urlchanged = data->change.url_changed;
if ((CURLE_OK == res) && urlchanged) {
res = Curl_done(conn, res);
if(CURLE_OK == res) {
char *gotourl = strdup(data->change.url);
Daniel Stenberg
committed
res = Curl_follow(data, gotourl, FALSE);
if(res)
free(gotourl);
}
}
} while (urlchanged && res == CURLE_OK);
return res;
}
/*
* Curl_perform() is the internal high-level function that gets called by the
* external curl_easy_perform() function. It inits, performs and cleans up a
* single file transfer.
*/
CURLcode Curl_perform(struct SessionHandle *data)
{
CURLcode res;
CURLcode res2;
struct connectdata *conn=NULL;
char *newurl = NULL; /* possibly a new URL to follow to! */
Daniel Stenberg
committed
bool retry = FALSE;
data->state.used_interface = Curl_if_easy;
res = Curl_pretransfer(data);
if(res)
return res;
/*
* It is important that there is NO 'return' from this function at any other
* place than falling down to the end of the function! This is because we
* have cleanup stuff that must be done before we get back, and that is only
* performed after this do-while loop.
*/
res = Curl_connect_host(data, &conn); /* primary connection */
if(res == CURLE_OK) {
if (data->set.source_url) /* 3rd party transfer */
res = Curl_pretransfersec(conn);
else
conn->sec_conn = NULL;
}
if(res == CURLE_OK) {
res = Curl_do(&conn);
/* for non 3rd party transfer only */
if(res == CURLE_OK && !data->set.source_url) {
res = Transfer(conn); /* now fetch that URL please */
if(res == CURLE_OK) {
Daniel Stenberg
committed
retry = FALSE;
Daniel Stenberg
committed
if((conn->keep.bytecount+conn->headerbytecount == 0) &&
conn->bits.reuse) {
Daniel Stenberg
committed
/* We got no data and we attempted to re-use a connection. This
might happen if the connection was left alive when we were done
using it before, but that was closed when we wanted to read
from it again. Bad luck. Retry the same request on a fresh
connect! */
infof(data, "Connection died, retrying a fresh connect\n");
newurl = strdup(conn->data->change.url);
conn->bits.close = TRUE; /* close this connection */
conn->bits.retry = TRUE; /* mark this as a connection we're about
to retry. Marking it this way should
prevent i.e HTTP transfers to return
error just because nothing has been
transfered! */
Daniel Stenberg
committed
retry = TRUE;
}
else
/*
* We must duplicate the new URL here as the connection data
* may be free()ed in the Curl_done() function.
*/
newurl = conn->newurl?strdup(conn->newurl):NULL;
}
Daniel Stenberg
committed
else {
/* The transfer phase returned error, we mark the connection to get
* closed to prevent being re-used. This is becasue we can't
* possibly know if the connection is in a good shape or not now. */
conn->bits.close = TRUE;
Daniel Stenberg
committed
Daniel Stenberg
committed
if(CURL_SOCKET_BAD != conn->sock[SECONDARYSOCKET]) {
Daniel Stenberg
committed
/* if we failed anywhere, we must clean up the secondary socket if
it was used */
Daniel Stenberg
committed
sclose(conn->sock[SECONDARYSOCKET]);
Daniel Stenberg
committed
conn->sock[SECONDARYSOCKET] = CURL_SOCKET_BAD;
Daniel Stenberg
committed
}
}
Daniel Stenberg
committed
/* Always run Curl_done(), even if some of the previous calls
failed, but return the previous (original) error code */
Daniel Stenberg
committed
res2 = Curl_done(&conn, res);
Daniel Stenberg
committed
if(CURLE_OK == res)
res = res2;
Daniel Stenberg
committed
else
/* Curl_do() failed, clean up left-overs in the done-call */
Daniel Stenberg
committed
res2 = Curl_done(&conn, res);
Daniel Stenberg
committed
/*
* Important: 'conn' cannot be used here, since it may have been closed
* in 'Curl_done' or other functions.
*/
if((res == CURLE_OK) && newurl) {
Daniel Stenberg
committed
res = Curl_follow(data, newurl, retry);
if(CURLE_OK == res) {
newurl = NULL;
continue;
}
}
break; /* it only reaches here when this shouldn't loop */
} while(1); /* loop if Location: */
Daniel Stenberg
committed
if(newurl)
free(newurl);
/* run post-transfer uncondionally, but don't clobber the return code if
we already have an error code recorder */
res2 = Curl_posttransfer(data);
if(!res && res2)
res = res2;
return res;
}
/*
* Curl_Transfer() is called to setup some basic properties for the upcoming
* transfer.
*/
Curl_Transfer(struct connectdata *c_conn, /* connection data */
int sockindex, /* socket index to read from or -1 */
curl_off_t size, /* -1 if unknown at this point */
bool getheader, /* TRUE if header parsing is wanted */
curl_off_t *bytecountp, /* return number of bytes read or NULL */
Daniel Stenberg
committed
int writesockindex, /* socket index to write to, it may very
well be the same we read from. -1
disables */
curl_off_t *writecountp /* return number of bytes written or
NULL */
)
{
struct connectdata *conn = (struct connectdata *)c_conn;
if(!conn)
return CURLE_BAD_FUNCTION_ARGUMENT;
curlassert((sockindex <= 1) && (sockindex >= -1));
/* now copy all input parameters */
conn->sockfd = sockindex==-1?
CURL_SOCKET_BAD:conn->sock[sockindex];
conn->bits.getheader = getheader;
conn->bytecountp = bytecountp;
conn->writesockfd = writesockindex==-1?
CURL_SOCKET_BAD:conn->sock[writesockindex];
conn->writebytecountp = writecountp;
return CURLE_OK;
}
/*
* Curl_pretransfersec() prepares the secondary connection (used for 3rd party
* FTP transfers).
*/
CURLcode Curl_pretransfersec(struct connectdata *conn)
{
CURLcode status = CURLE_OK;
struct SessionHandle *data = conn->data;
struct connectdata *sec_conn = NULL; /* secondary connection */
bool backup_reuse_fresh = data->set.reuse_fresh;
char *backup_userpwd = data->set.userpwd;
if(data->change.url_alloc)
free(data->change.url);
data->change.url_alloc = FALSE;
data->change.url = data->set.source_url;
/* We must never actually alter 'data->set' properties, so we restore the
backed up values afterwards! */
Daniel Stenberg
committed
/* if both remote hosts are the same host - create new connection */
if (strequal(conn->host.dispname, data->set.source_host))
Daniel Stenberg
committed
data->set.reuse_fresh = TRUE;
data->set.userpwd = data->set.source_userpwd;
/* secondary connection */
status = Curl_connect_host(data, &sec_conn);
if(CURLE_OK == status) {
Daniel Stenberg
committed
sec_conn->sec_conn = NULL; /* important if re-using existing connection
to prevent loop */
sec_conn->data = data;
conn->sec_conn = sec_conn;
}
data->set.reuse_fresh = backup_reuse_fresh;
data->set.userpwd = backup_userpwd;
Daniel Stenberg
committed
return status;
}