multi: convert Curl_follow to static multi_follow
Moved over from transfer.c because it is only used in multi.c Closes #15260
This commit is contained in:
parent
be39ed19a5
commit
d0377f5a86
299
lib/multi.c
299
lib/multi.c
@ -50,6 +50,7 @@
|
||||
#include "http2.h"
|
||||
#include "socketpair.h"
|
||||
#include "socks.h"
|
||||
#include "urlapi-int.h"
|
||||
/* The last 3 #include files should be in this order */
|
||||
#include "curl_printf.h"
|
||||
#include "curl_memory.h"
|
||||
@ -1817,6 +1818,298 @@ static void multi_posttransfer(struct Curl_easy *data)
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* multi_follow() handles the URL redirect magic. Pass in the 'newurl' string
|
||||
* as given by the remote server and set up the new URL to request.
|
||||
*
|
||||
* This function DOES NOT FREE the given url.
|
||||
*/
|
||||
static CURLcode multi_follow(struct Curl_easy *data,
|
||||
char *newurl, /* the Location: string */
|
||||
followtype type) /* see transfer.h */
|
||||
{
|
||||
#ifdef CURL_DISABLE_HTTP
|
||||
(void)data;
|
||||
(void)newurl;
|
||||
(void)type;
|
||||
/* Location: following will not happen when HTTP is disabled */
|
||||
return CURLE_TOO_MANY_REDIRECTS;
|
||||
#else
|
||||
|
||||
/* Location: redirect */
|
||||
bool disallowport = FALSE;
|
||||
bool reachedmax = FALSE;
|
||||
CURLUcode uc;
|
||||
|
||||
DEBUGASSERT(type != FOLLOW_NONE);
|
||||
|
||||
if(type != FOLLOW_FAKE)
|
||||
data->state.requests++; /* count all real follows */
|
||||
if(type == FOLLOW_REDIR) {
|
||||
if((data->set.maxredirs != -1) &&
|
||||
(data->state.followlocation >= data->set.maxredirs)) {
|
||||
reachedmax = TRUE;
|
||||
type = FOLLOW_FAKE; /* switch to fake to store the would-be-redirected
|
||||
to URL */
|
||||
}
|
||||
else {
|
||||
data->state.followlocation++; /* count redirect-followings, including
|
||||
auth reloads */
|
||||
|
||||
if(data->set.http_auto_referer) {
|
||||
CURLU *u;
|
||||
char *referer = NULL;
|
||||
|
||||
/* 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->state.referer_alloc) {
|
||||
Curl_safefree(data->state.referer);
|
||||
data->state.referer_alloc = FALSE;
|
||||
}
|
||||
|
||||
/* Make a copy of the URL without credentials and fragment */
|
||||
u = curl_url();
|
||||
if(!u)
|
||||
return CURLE_OUT_OF_MEMORY;
|
||||
|
||||
uc = curl_url_set(u, CURLUPART_URL, data->state.url, 0);
|
||||
if(!uc)
|
||||
uc = curl_url_set(u, CURLUPART_FRAGMENT, NULL, 0);
|
||||
if(!uc)
|
||||
uc = curl_url_set(u, CURLUPART_USER, NULL, 0);
|
||||
if(!uc)
|
||||
uc = curl_url_set(u, CURLUPART_PASSWORD, NULL, 0);
|
||||
if(!uc)
|
||||
uc = curl_url_get(u, CURLUPART_URL, &referer, 0);
|
||||
|
||||
curl_url_cleanup(u);
|
||||
|
||||
if(uc || !referer)
|
||||
return CURLE_OUT_OF_MEMORY;
|
||||
|
||||
data->state.referer = referer;
|
||||
data->state.referer_alloc = TRUE; /* yes, free this later */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if((type != FOLLOW_RETRY) &&
|
||||
(data->req.httpcode != 401) && (data->req.httpcode != 407) &&
|
||||
Curl_is_absolute_url(newurl, NULL, 0, FALSE)) {
|
||||
/* If this is not redirect due to a 401 or 407 response and an absolute
|
||||
URL: do not allow a custom port number */
|
||||
disallowport = TRUE;
|
||||
}
|
||||
|
||||
DEBUGASSERT(data->state.uh);
|
||||
uc = curl_url_set(data->state.uh, CURLUPART_URL, newurl, (unsigned int)
|
||||
((type == FOLLOW_FAKE) ? CURLU_NON_SUPPORT_SCHEME :
|
||||
((type == FOLLOW_REDIR) ? CURLU_URLENCODE : 0) |
|
||||
CURLU_ALLOW_SPACE |
|
||||
(data->set.path_as_is ? CURLU_PATH_AS_IS : 0)));
|
||||
if(uc) {
|
||||
if(type != FOLLOW_FAKE) {
|
||||
failf(data, "The redirect target URL could not be parsed: %s",
|
||||
curl_url_strerror(uc));
|
||||
return Curl_uc_to_curlcode(uc);
|
||||
}
|
||||
|
||||
/* the URL could not be parsed for some reason, but since this is FAKE
|
||||
mode, just duplicate the field as-is */
|
||||
newurl = strdup(newurl);
|
||||
if(!newurl)
|
||||
return CURLE_OUT_OF_MEMORY;
|
||||
}
|
||||
else {
|
||||
uc = curl_url_get(data->state.uh, CURLUPART_URL, &newurl, 0);
|
||||
if(uc)
|
||||
return Curl_uc_to_curlcode(uc);
|
||||
|
||||
/* Clear auth if this redirects to a different port number or protocol,
|
||||
unless permitted */
|
||||
if(!data->set.allow_auth_to_other_hosts && (type != FOLLOW_FAKE)) {
|
||||
char *portnum;
|
||||
int port;
|
||||
bool clear = FALSE;
|
||||
|
||||
if(data->set.use_port && data->state.allow_port)
|
||||
/* a custom port is used */
|
||||
port = (int)data->set.use_port;
|
||||
else {
|
||||
uc = curl_url_get(data->state.uh, CURLUPART_PORT, &portnum,
|
||||
CURLU_DEFAULT_PORT);
|
||||
if(uc) {
|
||||
free(newurl);
|
||||
return Curl_uc_to_curlcode(uc);
|
||||
}
|
||||
port = atoi(portnum);
|
||||
free(portnum);
|
||||
}
|
||||
if(port != data->info.conn_remote_port) {
|
||||
infof(data, "Clear auth, redirects to port from %u to %u",
|
||||
data->info.conn_remote_port, port);
|
||||
clear = TRUE;
|
||||
}
|
||||
else {
|
||||
char *scheme;
|
||||
const struct Curl_handler *p;
|
||||
uc = curl_url_get(data->state.uh, CURLUPART_SCHEME, &scheme, 0);
|
||||
if(uc) {
|
||||
free(newurl);
|
||||
return Curl_uc_to_curlcode(uc);
|
||||
}
|
||||
|
||||
p = Curl_get_scheme_handler(scheme);
|
||||
if(p && (p->protocol != data->info.conn_protocol)) {
|
||||
infof(data, "Clear auth, redirects scheme from %s to %s",
|
||||
data->info.conn_scheme, scheme);
|
||||
clear = TRUE;
|
||||
}
|
||||
free(scheme);
|
||||
}
|
||||
if(clear) {
|
||||
Curl_safefree(data->state.aptr.user);
|
||||
Curl_safefree(data->state.aptr.passwd);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(type == FOLLOW_FAKE) {
|
||||
/* we are only figuring out the new URL if we would have followed locations
|
||||
but now we are done so we can get out! */
|
||||
data->info.wouldredirect = newurl;
|
||||
|
||||
if(reachedmax) {
|
||||
failf(data, "Maximum (%ld) redirects followed", data->set.maxredirs);
|
||||
return CURLE_TOO_MANY_REDIRECTS;
|
||||
}
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
if(disallowport)
|
||||
data->state.allow_port = FALSE;
|
||||
|
||||
if(data->state.url_alloc)
|
||||
Curl_safefree(data->state.url);
|
||||
|
||||
data->state.url = newurl;
|
||||
data->state.url_alloc = TRUE;
|
||||
Curl_req_soft_reset(&data->req, data);
|
||||
infof(data, "Issue another request to this URL: '%s'", data->state.url);
|
||||
|
||||
/*
|
||||
* We get here when the HTTP code is 300-399 (and 401). We need to perform
|
||||
* differently based on exactly what return code there was.
|
||||
*
|
||||
* News from 7.10.6: we can also get here on a 401 or 407, in case we act on
|
||||
* an HTTP (proxy-) authentication scheme other than Basic.
|
||||
*/
|
||||
switch(data->info.httpcode) {
|
||||
/* 401 - Act on a WWW-Authenticate, we keep on moving and do the
|
||||
Authorization: XXXX header in the HTTP request code snippet */
|
||||
/* 407 - Act on a Proxy-Authenticate, we keep on moving and do the
|
||||
Proxy-Authorization: XXXX header in the HTTP request code snippet */
|
||||
/* 300 - Multiple Choices */
|
||||
/* 306 - Not used */
|
||||
/* 307 - Temporary Redirect */
|
||||
default: /* for all above (and the unknown ones) */
|
||||
/* Some codes are explicitly mentioned since I have checked RFC2616 and
|
||||
* they seem to be OK to POST to.
|
||||
*/
|
||||
break;
|
||||
case 301: /* Moved Permanently */
|
||||
/* (quote from RFC7231, section 6.4.2)
|
||||
*
|
||||
* Note: For historical reasons, a user agent MAY change the request
|
||||
* method from POST to GET for the subsequent request. If this
|
||||
* behavior is undesired, the 307 (Temporary Redirect) status code
|
||||
* can be used instead.
|
||||
*
|
||||
* ----
|
||||
*
|
||||
* Many webservers expect this, so these servers often answers to a POST
|
||||
* request with an error page. To be sure that libcurl gets the page that
|
||||
* most user agents would get, libcurl has to force GET.
|
||||
*
|
||||
* This behavior is forbidden by RFC1945 and the obsolete RFC2616, and
|
||||
* can be overridden with CURLOPT_POSTREDIR.
|
||||
*/
|
||||
if((data->state.httpreq == HTTPREQ_POST
|
||||
|| data->state.httpreq == HTTPREQ_POST_FORM
|
||||
|| data->state.httpreq == HTTPREQ_POST_MIME)
|
||||
&& !(data->set.keep_post & CURL_REDIR_POST_301)) {
|
||||
infof(data, "Switch from POST to GET");
|
||||
data->state.httpreq = HTTPREQ_GET;
|
||||
Curl_creader_set_rewind(data, FALSE);
|
||||
}
|
||||
break;
|
||||
case 302: /* Found */
|
||||
/* (quote from RFC7231, section 6.4.3)
|
||||
*
|
||||
* Note: For historical reasons, a user agent MAY change the request
|
||||
* method from POST to GET for the subsequent request. If this
|
||||
* behavior is undesired, the 307 (Temporary Redirect) status code
|
||||
* can be used instead.
|
||||
*
|
||||
* ----
|
||||
*
|
||||
* Many webservers expect this, so these servers often answers to a POST
|
||||
* request with an error page. To be sure that libcurl gets the page that
|
||||
* most user agents would get, libcurl has to force GET.
|
||||
*
|
||||
* This behavior is forbidden by RFC1945 and the obsolete RFC2616, and
|
||||
* can be overridden with CURLOPT_POSTREDIR.
|
||||
*/
|
||||
if((data->state.httpreq == HTTPREQ_POST
|
||||
|| data->state.httpreq == HTTPREQ_POST_FORM
|
||||
|| data->state.httpreq == HTTPREQ_POST_MIME)
|
||||
&& !(data->set.keep_post & CURL_REDIR_POST_302)) {
|
||||
infof(data, "Switch from POST to GET");
|
||||
data->state.httpreq = HTTPREQ_GET;
|
||||
Curl_creader_set_rewind(data, FALSE);
|
||||
}
|
||||
break;
|
||||
|
||||
case 303: /* See Other */
|
||||
/* 'See Other' location is not the resource but a substitute for the
|
||||
* resource. In this case we switch the method to GET/HEAD, unless the
|
||||
* method is POST and the user specified to keep it as POST.
|
||||
* https://github.com/curl/curl/issues/5237#issuecomment-614641049
|
||||
*/
|
||||
if(data->state.httpreq != HTTPREQ_GET &&
|
||||
((data->state.httpreq != HTTPREQ_POST &&
|
||||
data->state.httpreq != HTTPREQ_POST_FORM &&
|
||||
data->state.httpreq != HTTPREQ_POST_MIME) ||
|
||||
!(data->set.keep_post & CURL_REDIR_POST_303))) {
|
||||
data->state.httpreq = HTTPREQ_GET;
|
||||
infof(data, "Switch to %s",
|
||||
data->req.no_body ? "HEAD" : "GET");
|
||||
}
|
||||
break;
|
||||
case 304: /* Not Modified */
|
||||
/* 304 means we did a conditional request and it was "Not modified".
|
||||
* We should not 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_pgrsResetTransferSizes(data);
|
||||
|
||||
return CURLE_OK;
|
||||
#endif /* CURL_DISABLE_HTTP */
|
||||
}
|
||||
|
||||
static CURLMcode multi_runsingle(struct Curl_multi *multi,
|
||||
struct curltime *nowp,
|
||||
struct Curl_easy *data)
|
||||
@ -2197,7 +2490,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
|
||||
if(newurl) {
|
||||
if(!drc || (drc == CURLE_SEND_ERROR)) {
|
||||
follow = FOLLOW_RETRY;
|
||||
drc = Curl_follow(data, newurl, follow);
|
||||
drc = multi_follow(data, newurl, follow);
|
||||
if(!drc) {
|
||||
multistate(data, MSTATE_SETUP);
|
||||
rc = CURLM_CALL_MULTI_PERFORM;
|
||||
@ -2451,7 +2744,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
|
||||
follow = FOLLOW_RETRY;
|
||||
(void)multi_done(data, CURLE_OK, FALSE);
|
||||
/* multi_done() might return CURLE_GOT_NOTHING */
|
||||
result = Curl_follow(data, newurl, follow);
|
||||
result = multi_follow(data, newurl, follow);
|
||||
if(!result) {
|
||||
multistate(data, MSTATE_SETUP);
|
||||
rc = CURLM_CALL_MULTI_PERFORM;
|
||||
@ -2466,7 +2759,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
|
||||
free(newurl);
|
||||
newurl = data->req.location;
|
||||
data->req.location = NULL;
|
||||
result = Curl_follow(data, newurl, FOLLOW_FAKE);
|
||||
result = multi_follow(data, newurl, FOLLOW_FAKE);
|
||||
if(result) {
|
||||
stream_error = TRUE;
|
||||
result = multi_done(data, result, TRUE);
|
||||
|
||||
293
lib/transfer.c
293
lib/transfer.c
@ -79,7 +79,6 @@
|
||||
#include "http2.h"
|
||||
#include "mime.h"
|
||||
#include "strcase.h"
|
||||
#include "urlapi-int.h"
|
||||
#include "hsts.h"
|
||||
#include "setopt.h"
|
||||
#include "headers.h"
|
||||
@ -700,298 +699,6 @@ CURLcode Curl_pretransfer(struct Curl_easy *data)
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Curl_follow() handles the URL redirect magic. Pass in the 'newurl' string
|
||||
* as given by the remote server and set up the new URL to request.
|
||||
*
|
||||
* This function DOES NOT FREE the given url.
|
||||
*/
|
||||
CURLcode Curl_follow(struct Curl_easy *data,
|
||||
char *newurl, /* the Location: string */
|
||||
followtype type) /* see transfer.h */
|
||||
{
|
||||
#ifdef CURL_DISABLE_HTTP
|
||||
(void)data;
|
||||
(void)newurl;
|
||||
(void)type;
|
||||
/* Location: following will not happen when HTTP is disabled */
|
||||
return CURLE_TOO_MANY_REDIRECTS;
|
||||
#else
|
||||
|
||||
/* Location: redirect */
|
||||
bool disallowport = FALSE;
|
||||
bool reachedmax = FALSE;
|
||||
CURLUcode uc;
|
||||
|
||||
DEBUGASSERT(type != FOLLOW_NONE);
|
||||
|
||||
if(type != FOLLOW_FAKE)
|
||||
data->state.requests++; /* count all real follows */
|
||||
if(type == FOLLOW_REDIR) {
|
||||
if((data->set.maxredirs != -1) &&
|
||||
(data->state.followlocation >= data->set.maxredirs)) {
|
||||
reachedmax = TRUE;
|
||||
type = FOLLOW_FAKE; /* switch to fake to store the would-be-redirected
|
||||
to URL */
|
||||
}
|
||||
else {
|
||||
data->state.followlocation++; /* count redirect-followings, including
|
||||
auth reloads */
|
||||
|
||||
if(data->set.http_auto_referer) {
|
||||
CURLU *u;
|
||||
char *referer = NULL;
|
||||
|
||||
/* 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->state.referer_alloc) {
|
||||
Curl_safefree(data->state.referer);
|
||||
data->state.referer_alloc = FALSE;
|
||||
}
|
||||
|
||||
/* Make a copy of the URL without credentials and fragment */
|
||||
u = curl_url();
|
||||
if(!u)
|
||||
return CURLE_OUT_OF_MEMORY;
|
||||
|
||||
uc = curl_url_set(u, CURLUPART_URL, data->state.url, 0);
|
||||
if(!uc)
|
||||
uc = curl_url_set(u, CURLUPART_FRAGMENT, NULL, 0);
|
||||
if(!uc)
|
||||
uc = curl_url_set(u, CURLUPART_USER, NULL, 0);
|
||||
if(!uc)
|
||||
uc = curl_url_set(u, CURLUPART_PASSWORD, NULL, 0);
|
||||
if(!uc)
|
||||
uc = curl_url_get(u, CURLUPART_URL, &referer, 0);
|
||||
|
||||
curl_url_cleanup(u);
|
||||
|
||||
if(uc || !referer)
|
||||
return CURLE_OUT_OF_MEMORY;
|
||||
|
||||
data->state.referer = referer;
|
||||
data->state.referer_alloc = TRUE; /* yes, free this later */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if((type != FOLLOW_RETRY) &&
|
||||
(data->req.httpcode != 401) && (data->req.httpcode != 407) &&
|
||||
Curl_is_absolute_url(newurl, NULL, 0, FALSE)) {
|
||||
/* If this is not redirect due to a 401 or 407 response and an absolute
|
||||
URL: do not allow a custom port number */
|
||||
disallowport = TRUE;
|
||||
}
|
||||
|
||||
DEBUGASSERT(data->state.uh);
|
||||
uc = curl_url_set(data->state.uh, CURLUPART_URL, newurl, (unsigned int)
|
||||
((type == FOLLOW_FAKE) ? CURLU_NON_SUPPORT_SCHEME :
|
||||
((type == FOLLOW_REDIR) ? CURLU_URLENCODE : 0) |
|
||||
CURLU_ALLOW_SPACE |
|
||||
(data->set.path_as_is ? CURLU_PATH_AS_IS : 0)));
|
||||
if(uc) {
|
||||
if(type != FOLLOW_FAKE) {
|
||||
failf(data, "The redirect target URL could not be parsed: %s",
|
||||
curl_url_strerror(uc));
|
||||
return Curl_uc_to_curlcode(uc);
|
||||
}
|
||||
|
||||
/* the URL could not be parsed for some reason, but since this is FAKE
|
||||
mode, just duplicate the field as-is */
|
||||
newurl = strdup(newurl);
|
||||
if(!newurl)
|
||||
return CURLE_OUT_OF_MEMORY;
|
||||
}
|
||||
else {
|
||||
uc = curl_url_get(data->state.uh, CURLUPART_URL, &newurl, 0);
|
||||
if(uc)
|
||||
return Curl_uc_to_curlcode(uc);
|
||||
|
||||
/* Clear auth if this redirects to a different port number or protocol,
|
||||
unless permitted */
|
||||
if(!data->set.allow_auth_to_other_hosts && (type != FOLLOW_FAKE)) {
|
||||
char *portnum;
|
||||
int port;
|
||||
bool clear = FALSE;
|
||||
|
||||
if(data->set.use_port && data->state.allow_port)
|
||||
/* a custom port is used */
|
||||
port = (int)data->set.use_port;
|
||||
else {
|
||||
uc = curl_url_get(data->state.uh, CURLUPART_PORT, &portnum,
|
||||
CURLU_DEFAULT_PORT);
|
||||
if(uc) {
|
||||
free(newurl);
|
||||
return Curl_uc_to_curlcode(uc);
|
||||
}
|
||||
port = atoi(portnum);
|
||||
free(portnum);
|
||||
}
|
||||
if(port != data->info.conn_remote_port) {
|
||||
infof(data, "Clear auth, redirects to port from %u to %u",
|
||||
data->info.conn_remote_port, port);
|
||||
clear = TRUE;
|
||||
}
|
||||
else {
|
||||
char *scheme;
|
||||
const struct Curl_handler *p;
|
||||
uc = curl_url_get(data->state.uh, CURLUPART_SCHEME, &scheme, 0);
|
||||
if(uc) {
|
||||
free(newurl);
|
||||
return Curl_uc_to_curlcode(uc);
|
||||
}
|
||||
|
||||
p = Curl_get_scheme_handler(scheme);
|
||||
if(p && (p->protocol != data->info.conn_protocol)) {
|
||||
infof(data, "Clear auth, redirects scheme from %s to %s",
|
||||
data->info.conn_scheme, scheme);
|
||||
clear = TRUE;
|
||||
}
|
||||
free(scheme);
|
||||
}
|
||||
if(clear) {
|
||||
Curl_safefree(data->state.aptr.user);
|
||||
Curl_safefree(data->state.aptr.passwd);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(type == FOLLOW_FAKE) {
|
||||
/* we are only figuring out the new URL if we would have followed locations
|
||||
but now we are done so we can get out! */
|
||||
data->info.wouldredirect = newurl;
|
||||
|
||||
if(reachedmax) {
|
||||
failf(data, "Maximum (%ld) redirects followed", data->set.maxredirs);
|
||||
return CURLE_TOO_MANY_REDIRECTS;
|
||||
}
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
if(disallowport)
|
||||
data->state.allow_port = FALSE;
|
||||
|
||||
if(data->state.url_alloc)
|
||||
Curl_safefree(data->state.url);
|
||||
|
||||
data->state.url = newurl;
|
||||
data->state.url_alloc = TRUE;
|
||||
Curl_req_soft_reset(&data->req, data);
|
||||
infof(data, "Issue another request to this URL: '%s'", data->state.url);
|
||||
|
||||
/*
|
||||
* We get here when the HTTP code is 300-399 (and 401). We need to perform
|
||||
* differently based on exactly what return code there was.
|
||||
*
|
||||
* News from 7.10.6: we can also get here on a 401 or 407, in case we act on
|
||||
* an HTTP (proxy-) authentication scheme other than Basic.
|
||||
*/
|
||||
switch(data->info.httpcode) {
|
||||
/* 401 - Act on a WWW-Authenticate, we keep on moving and do the
|
||||
Authorization: XXXX header in the HTTP request code snippet */
|
||||
/* 407 - Act on a Proxy-Authenticate, we keep on moving and do the
|
||||
Proxy-Authorization: XXXX header in the HTTP request code snippet */
|
||||
/* 300 - Multiple Choices */
|
||||
/* 306 - Not used */
|
||||
/* 307 - Temporary Redirect */
|
||||
default: /* for all above (and the unknown ones) */
|
||||
/* Some codes are explicitly mentioned since I have checked RFC2616 and
|
||||
* they seem to be OK to POST to.
|
||||
*/
|
||||
break;
|
||||
case 301: /* Moved Permanently */
|
||||
/* (quote from RFC7231, section 6.4.2)
|
||||
*
|
||||
* Note: For historical reasons, a user agent MAY change the request
|
||||
* method from POST to GET for the subsequent request. If this
|
||||
* behavior is undesired, the 307 (Temporary Redirect) status code
|
||||
* can be used instead.
|
||||
*
|
||||
* ----
|
||||
*
|
||||
* Many webservers expect this, so these servers often answers to a POST
|
||||
* request with an error page. To be sure that libcurl gets the page that
|
||||
* most user agents would get, libcurl has to force GET.
|
||||
*
|
||||
* This behavior is forbidden by RFC1945 and the obsolete RFC2616, and
|
||||
* can be overridden with CURLOPT_POSTREDIR.
|
||||
*/
|
||||
if((data->state.httpreq == HTTPREQ_POST
|
||||
|| data->state.httpreq == HTTPREQ_POST_FORM
|
||||
|| data->state.httpreq == HTTPREQ_POST_MIME)
|
||||
&& !(data->set.keep_post & CURL_REDIR_POST_301)) {
|
||||
infof(data, "Switch from POST to GET");
|
||||
data->state.httpreq = HTTPREQ_GET;
|
||||
Curl_creader_set_rewind(data, FALSE);
|
||||
}
|
||||
break;
|
||||
case 302: /* Found */
|
||||
/* (quote from RFC7231, section 6.4.3)
|
||||
*
|
||||
* Note: For historical reasons, a user agent MAY change the request
|
||||
* method from POST to GET for the subsequent request. If this
|
||||
* behavior is undesired, the 307 (Temporary Redirect) status code
|
||||
* can be used instead.
|
||||
*
|
||||
* ----
|
||||
*
|
||||
* Many webservers expect this, so these servers often answers to a POST
|
||||
* request with an error page. To be sure that libcurl gets the page that
|
||||
* most user agents would get, libcurl has to force GET.
|
||||
*
|
||||
* This behavior is forbidden by RFC1945 and the obsolete RFC2616, and
|
||||
* can be overridden with CURLOPT_POSTREDIR.
|
||||
*/
|
||||
if((data->state.httpreq == HTTPREQ_POST
|
||||
|| data->state.httpreq == HTTPREQ_POST_FORM
|
||||
|| data->state.httpreq == HTTPREQ_POST_MIME)
|
||||
&& !(data->set.keep_post & CURL_REDIR_POST_302)) {
|
||||
infof(data, "Switch from POST to GET");
|
||||
data->state.httpreq = HTTPREQ_GET;
|
||||
Curl_creader_set_rewind(data, FALSE);
|
||||
}
|
||||
break;
|
||||
|
||||
case 303: /* See Other */
|
||||
/* 'See Other' location is not the resource but a substitute for the
|
||||
* resource. In this case we switch the method to GET/HEAD, unless the
|
||||
* method is POST and the user specified to keep it as POST.
|
||||
* https://github.com/curl/curl/issues/5237#issuecomment-614641049
|
||||
*/
|
||||
if(data->state.httpreq != HTTPREQ_GET &&
|
||||
((data->state.httpreq != HTTPREQ_POST &&
|
||||
data->state.httpreq != HTTPREQ_POST_FORM &&
|
||||
data->state.httpreq != HTTPREQ_POST_MIME) ||
|
||||
!(data->set.keep_post & CURL_REDIR_POST_303))) {
|
||||
data->state.httpreq = HTTPREQ_GET;
|
||||
infof(data, "Switch to %s",
|
||||
data->req.no_body ? "HEAD" : "GET");
|
||||
}
|
||||
break;
|
||||
case 304: /* Not Modified */
|
||||
/* 304 means we did a conditional request and it was "Not modified".
|
||||
* We should not 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_pgrsResetTransferSizes(data);
|
||||
|
||||
return CURLE_OK;
|
||||
#endif /* CURL_DISABLE_HTTP */
|
||||
}
|
||||
|
||||
/* Returns CURLE_OK *and* sets '*url' if a request retry is wanted.
|
||||
|
||||
NOTE: that the *url is malloc()ed. */
|
||||
|
||||
@ -42,8 +42,6 @@ typedef enum {
|
||||
FOLLOW_REDIR /* a full true redirect */
|
||||
} followtype;
|
||||
|
||||
CURLcode Curl_follow(struct Curl_easy *data, char *newurl,
|
||||
followtype type);
|
||||
CURLcode Curl_sendrecv(struct Curl_easy *data, struct curltime *nowp);
|
||||
int Curl_single_getsock(struct Curl_easy *data,
|
||||
struct connectdata *conn, curl_socket_t *socks);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user