aboutsummaryrefslogtreecommitdiff
path: root/lib/transfer.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/transfer.c')
-rw-r--r--lib/transfer.c140
1 files changed, 88 insertions, 52 deletions
diff --git a/lib/transfer.c b/lib/transfer.c
index 0856d2a55..0ebe19652 100644
--- a/lib/transfer.c
+++ b/lib/transfer.c
@@ -250,7 +250,7 @@ CURLcode Curl_readrewind(struct connectdata *conn)
err = (data->set.seek_func)(data->set.seek_client, 0, SEEK_SET);
if(err) {
- failf(data, "seek callback returned error %d", (int)err);
+ failf(data, "seek callback returned error %d", (int)err);
return CURLE_SEND_FAIL_REWIND;
}
}
@@ -1113,34 +1113,37 @@ CURLcode Curl_readwrite(struct connectdata *conn,
}
else if((k->httpcode >= 300 && k->httpcode < 400) &&
checkprefix("Location:", k->p)) {
- if(data->set.http_follow_location) {
- /* this is the URL that the server advices us to get instead */
- char *ptr;
- char *start=k->p;
- char backup;
-
- start += 9; /* pass "Location:" */
-
- /* Skip spaces and tabs. We do this to support multiple
- white spaces after the "Location:" keyword. */
- while(*start && ISSPACE(*start ))
- start++;
-
- /* Scan through the string from the end to find the last
- non-space. k->end_ptr points to the actual terminating zero
- letter, move pointer one letter back and start from
- there. This logic strips off trailing whitespace, but keeps
- any embedded whitespace. */
- ptr = k->end_ptr-1;
- while((ptr>=start) && ISSPACE(*ptr))
- ptr--;
- ptr++;
-
- backup = *ptr; /* store the ending letter */
- if(ptr != start) {
- *ptr = '\0'; /* zero terminate */
- data->req.newurl = strdup(start); /* clone string */
- *ptr = backup; /* restore ending letter */
+ /* this is the URL that the server advices us to use instead */
+ char *ptr;
+ char *start=k->p;
+ char backup;
+
+ start += 9; /* pass "Location:" */
+
+ /* Skip spaces and tabs. We do this to support multiple
+ white spaces after the "Location:" keyword. */
+ while(*start && ISSPACE(*start ))
+ start++;
+
+ /* Scan through the string from the end to find the last
+ non-space. k->end_ptr points to the actual terminating zero
+ letter, move pointer one letter back and start from
+ there. This logic strips off trailing whitespace, but keeps
+ any embedded whitespace. */
+ ptr = k->end_ptr-1;
+ while((ptr>=start) && ISSPACE(*ptr))
+ ptr--;
+ ptr++;
+
+ backup = *ptr; /* store the ending letter */
+ if(ptr != start) {
+ *ptr = '\0'; /* zero terminate */
+ data->req.location = strdup(start); /* clone string */
+ *ptr = backup; /* restore ending letter */
+ if(!data->req.location)
+ return CURLE_OUT_OF_MEMORY;
+ if(data->set.http_follow_location) {
+ data->req.newurl = strdup(data->req.location); /* clone */
if(!data->req.newurl)
return CURLE_OUT_OF_MEMORY;
}
@@ -1969,16 +1972,16 @@ CURLcode Curl_follow(struct SessionHandle *data,
char *newurl, /* this 'newurl' is the Location: string,
and it must be malloc()ed before passed
here */
- bool retry) /* set TRUE if this is a request retry as
- opposed to a real redirect following */
+ followtype type) /* see transfer.h */
{
/* Location: redirect */
char prot[16]; /* URL protocol string storage */
char letter; /* used for a silly sscanf */
size_t newlen;
char *newest;
+ bool disallowport = FALSE;
- if(!retry) {
+ if(type == FOLLOW_REDIR) {
if((data->set.maxredirs != -1) &&
(data->set.followlocation >= data->set.maxredirs)) {
failf(data,"Maximum (%d) redirects followed", data->set.maxredirs);
@@ -1989,19 +1992,19 @@ CURLcode Curl_follow(struct SessionHandle *data,
data->state.this_is_a_follow = TRUE;
data->set.followlocation++; /* count location-followers */
- }
- if(data->set.http_auto_referer) {
- /* We are asked to automatically set the previous URL as the
- referer when we get the next URL. We pick the ->url field,
- which may or may not be 100% correct */
+ if(data->set.http_auto_referer) {
+ /* We are asked to automatically set the previous URL as the referer
+ when we get the next URL. We pick the ->url field, which may or may
+ not be 100% correct */
- if(data->change.referer_alloc)
- /* If we already have an allocated referer, free this first */
- free(data->change.referer);
+ if(data->change.referer_alloc)
+ /* If we already have an allocated referer, free this first */
+ free(data->change.referer);
- data->change.referer = strdup(data->change.url);
- data->change.referer_alloc = TRUE; /* yes, free this later */
+ data->change.referer = strdup(data->change.url);
+ data->change.referer_alloc = TRUE; /* yes, free this later */
+ }
}
if(2 != sscanf(newurl, "%15[^?&/:]://%c", prot, &letter)) {
@@ -2141,7 +2144,7 @@ CURLcode Curl_follow(struct SessionHandle *data,
}
else {
/* This is an absolute URL, don't allow the custom port number */
- data->state.allow_port = FALSE;
+ disallowport = TRUE;
if(strchr(newurl, ' ')) {
/* This new URL contains at least one space, this is a mighty stupid
@@ -2159,6 +2162,16 @@ CURLcode Curl_follow(struct SessionHandle *data,
}
+ if(type == FOLLOW_FAKE) {
+ /* we're only figuring out the new url if we would've followed locations
+ but now we're done so we can get out! */
+ data->info.wouldredirect = newurl;
+ return CURLE_OK;
+ }
+
+ if(disallowport)
+ data->state.allow_port = FALSE;
+
if(data->change.url_alloc)
free(data->change.url);
else
@@ -2289,7 +2302,9 @@ connect_host(struct SessionHandle *data,
return res;
}
-/* Returns TRUE and sets '*url' if a request retry is wanted */
+/* Returns TRUE and sets '*url' if a request retry is wanted.
+
+ NOTE: that the *url is malloc()ed. */
bool Curl_retry_request(struct connectdata *conn,
char **url)
{
@@ -2335,7 +2350,7 @@ CURLcode Curl_perform(struct SessionHandle *data)
CURLcode res2;
struct connectdata *conn=NULL;
char *newurl = NULL; /* possibly a new URL to follow to! */
- bool retry = FALSE;
+ int follow = FOLLOW_NONE;
data->state.used_interface = Curl_if_easy;
@@ -2366,14 +2381,29 @@ CURLcode Curl_perform(struct SessionHandle *data)
if(res == CURLE_OK) {
res = Transfer(conn); /* now fetch that URL please */
if(res == CURLE_OK) {
- retry = Curl_retry_request(conn, &newurl);
+ bool retry = Curl_retry_request(conn, &newurl);
- if(!retry)
+ if(retry)
+ follow = FOLLOW_RETRY;
+ else {
/*
* We must duplicate the new URL here as the connection data may
- * be free()ed in the Curl_done() function.
+ * be free()ed in the Curl_done() function. We prefer the newurl
+ * one since that's used for redirects or just further requests
+ * for retries or multi-stage HTTP auth methods etc.
*/
- newurl = data->req.newurl?strdup(data->req.newurl):NULL;
+ if(data->req.newurl) {
+ follow = FOLLOW_REDIR;
+ newurl = strdup(data->req.newurl);
+ }
+ else if(data->req.location) {
+ follow = FOLLOW_FAKE;
+ newurl = strdup(data->req.location);
+ }
+ }
+
+ /* in the above cases where 'newurl' gets assigned, we have a fresh
+ * allocated memory pointed to */
}
else {
/* The transfer phase returned error, we mark the connection to get
@@ -2409,11 +2439,17 @@ CURLcode Curl_perform(struct SessionHandle *data)
* in 'Curl_done' or other functions.
*/
- if((res == CURLE_OK) && newurl) {
- res = Curl_follow(data, newurl, retry);
+ if((res == CURLE_OK) && follow) {
+ res = Curl_follow(data, newurl, follow);
if(CURLE_OK == res) {
+ /* if things went fine, Curl_follow() freed or otherwise took
+ responsibility for the newurl pointer */
newurl = NULL;
- continue;
+ if(follow >= FOLLOW_RETRY) {
+ follow = FOLLOW_NONE;
+ continue;
+ }
+ /* else we break out of the loop below */
}
}
}