mirror of
https://github.com/curl/curl.git
synced 2025-09-10 22:22:43 +03:00
lib: redirect handling by protocol handler
Adds a `follow()` callback to protocol handlers, so they may decide how to act on a `newurl` after a request has been done. This is optional. This moves the HTTP code for handling redirects from multi.c to http.c where it should be. If we ever add a protocol with its own logic, it would install its own follow function. Closes #16075
This commit is contained in:
parent
e83818cae1
commit
1213c31272
|
@ -85,6 +85,7 @@ const struct Curl_handler Curl_handler_rtmp = {
|
||||||
ZERO_NULL, /* write_resp_hd */
|
ZERO_NULL, /* write_resp_hd */
|
||||||
ZERO_NULL, /* connection_check */
|
ZERO_NULL, /* connection_check */
|
||||||
ZERO_NULL, /* attach connection */
|
ZERO_NULL, /* attach connection */
|
||||||
|
ZERO_NULL, /* follow */
|
||||||
PORT_RTMP, /* defport */
|
PORT_RTMP, /* defport */
|
||||||
CURLPROTO_RTMP, /* protocol */
|
CURLPROTO_RTMP, /* protocol */
|
||||||
CURLPROTO_RTMP, /* family */
|
CURLPROTO_RTMP, /* family */
|
||||||
|
@ -109,6 +110,7 @@ const struct Curl_handler Curl_handler_rtmpt = {
|
||||||
ZERO_NULL, /* write_resp_hd */
|
ZERO_NULL, /* write_resp_hd */
|
||||||
ZERO_NULL, /* connection_check */
|
ZERO_NULL, /* connection_check */
|
||||||
ZERO_NULL, /* attach connection */
|
ZERO_NULL, /* attach connection */
|
||||||
|
ZERO_NULL, /* follow */
|
||||||
PORT_RTMPT, /* defport */
|
PORT_RTMPT, /* defport */
|
||||||
CURLPROTO_RTMPT, /* protocol */
|
CURLPROTO_RTMPT, /* protocol */
|
||||||
CURLPROTO_RTMPT, /* family */
|
CURLPROTO_RTMPT, /* family */
|
||||||
|
@ -133,6 +135,7 @@ const struct Curl_handler Curl_handler_rtmpe = {
|
||||||
ZERO_NULL, /* write_resp_hd */
|
ZERO_NULL, /* write_resp_hd */
|
||||||
ZERO_NULL, /* connection_check */
|
ZERO_NULL, /* connection_check */
|
||||||
ZERO_NULL, /* attach connection */
|
ZERO_NULL, /* attach connection */
|
||||||
|
ZERO_NULL, /* follow */
|
||||||
PORT_RTMP, /* defport */
|
PORT_RTMP, /* defport */
|
||||||
CURLPROTO_RTMPE, /* protocol */
|
CURLPROTO_RTMPE, /* protocol */
|
||||||
CURLPROTO_RTMPE, /* family */
|
CURLPROTO_RTMPE, /* family */
|
||||||
|
@ -157,6 +160,7 @@ const struct Curl_handler Curl_handler_rtmpte = {
|
||||||
ZERO_NULL, /* write_resp_hd */
|
ZERO_NULL, /* write_resp_hd */
|
||||||
ZERO_NULL, /* connection_check */
|
ZERO_NULL, /* connection_check */
|
||||||
ZERO_NULL, /* attach connection */
|
ZERO_NULL, /* attach connection */
|
||||||
|
ZERO_NULL, /* follow */
|
||||||
PORT_RTMPT, /* defport */
|
PORT_RTMPT, /* defport */
|
||||||
CURLPROTO_RTMPTE, /* protocol */
|
CURLPROTO_RTMPTE, /* protocol */
|
||||||
CURLPROTO_RTMPTE, /* family */
|
CURLPROTO_RTMPTE, /* family */
|
||||||
|
@ -181,6 +185,7 @@ const struct Curl_handler Curl_handler_rtmps = {
|
||||||
ZERO_NULL, /* write_resp_hd */
|
ZERO_NULL, /* write_resp_hd */
|
||||||
ZERO_NULL, /* connection_check */
|
ZERO_NULL, /* connection_check */
|
||||||
ZERO_NULL, /* attach connection */
|
ZERO_NULL, /* attach connection */
|
||||||
|
ZERO_NULL, /* follow */
|
||||||
PORT_RTMPS, /* defport */
|
PORT_RTMPS, /* defport */
|
||||||
CURLPROTO_RTMPS, /* protocol */
|
CURLPROTO_RTMPS, /* protocol */
|
||||||
CURLPROTO_RTMP, /* family */
|
CURLPROTO_RTMP, /* family */
|
||||||
|
@ -205,6 +210,7 @@ const struct Curl_handler Curl_handler_rtmpts = {
|
||||||
ZERO_NULL, /* write_resp_hd */
|
ZERO_NULL, /* write_resp_hd */
|
||||||
ZERO_NULL, /* connection_check */
|
ZERO_NULL, /* connection_check */
|
||||||
ZERO_NULL, /* attach connection */
|
ZERO_NULL, /* attach connection */
|
||||||
|
ZERO_NULL, /* follow */
|
||||||
PORT_RTMPS, /* defport */
|
PORT_RTMPS, /* defport */
|
||||||
CURLPROTO_RTMPTS, /* protocol */
|
CURLPROTO_RTMPTS, /* protocol */
|
||||||
CURLPROTO_RTMPT, /* family */
|
CURLPROTO_RTMPT, /* family */
|
||||||
|
|
|
@ -93,6 +93,7 @@ const struct Curl_handler Curl_handler_dict = {
|
||||||
ZERO_NULL, /* write_resp_hd */
|
ZERO_NULL, /* write_resp_hd */
|
||||||
ZERO_NULL, /* connection_check */
|
ZERO_NULL, /* connection_check */
|
||||||
ZERO_NULL, /* attach connection */
|
ZERO_NULL, /* attach connection */
|
||||||
|
ZERO_NULL, /* follow */
|
||||||
PORT_DICT, /* defport */
|
PORT_DICT, /* defport */
|
||||||
CURLPROTO_DICT, /* protocol */
|
CURLPROTO_DICT, /* protocol */
|
||||||
CURLPROTO_DICT, /* family */
|
CURLPROTO_DICT, /* family */
|
||||||
|
|
|
@ -120,6 +120,7 @@ const struct Curl_handler Curl_handler_file = {
|
||||||
ZERO_NULL, /* write_resp_hd */
|
ZERO_NULL, /* write_resp_hd */
|
||||||
ZERO_NULL, /* connection_check */
|
ZERO_NULL, /* connection_check */
|
||||||
ZERO_NULL, /* attach connection */
|
ZERO_NULL, /* attach connection */
|
||||||
|
ZERO_NULL, /* follow */
|
||||||
0, /* defport */
|
0, /* defport */
|
||||||
CURLPROTO_FILE, /* protocol */
|
CURLPROTO_FILE, /* protocol */
|
||||||
CURLPROTO_FILE, /* family */
|
CURLPROTO_FILE, /* family */
|
||||||
|
|
|
@ -250,6 +250,7 @@ const struct Curl_handler Curl_handler_ftp = {
|
||||||
ZERO_NULL, /* write_resp_hd */
|
ZERO_NULL, /* write_resp_hd */
|
||||||
ZERO_NULL, /* connection_check */
|
ZERO_NULL, /* connection_check */
|
||||||
ZERO_NULL, /* attach connection */
|
ZERO_NULL, /* attach connection */
|
||||||
|
ZERO_NULL, /* follow */
|
||||||
PORT_FTP, /* defport */
|
PORT_FTP, /* defport */
|
||||||
CURLPROTO_FTP, /* protocol */
|
CURLPROTO_FTP, /* protocol */
|
||||||
CURLPROTO_FTP, /* family */
|
CURLPROTO_FTP, /* family */
|
||||||
|
@ -282,6 +283,7 @@ const struct Curl_handler Curl_handler_ftps = {
|
||||||
ZERO_NULL, /* write_resp_hd */
|
ZERO_NULL, /* write_resp_hd */
|
||||||
ZERO_NULL, /* connection_check */
|
ZERO_NULL, /* connection_check */
|
||||||
ZERO_NULL, /* attach connection */
|
ZERO_NULL, /* attach connection */
|
||||||
|
ZERO_NULL, /* follow */
|
||||||
PORT_FTPS, /* defport */
|
PORT_FTPS, /* defport */
|
||||||
CURLPROTO_FTPS, /* protocol */
|
CURLPROTO_FTPS, /* protocol */
|
||||||
CURLPROTO_FTP, /* family */
|
CURLPROTO_FTP, /* family */
|
||||||
|
|
|
@ -79,6 +79,7 @@ const struct Curl_handler Curl_handler_gopher = {
|
||||||
ZERO_NULL, /* write_resp_hd */
|
ZERO_NULL, /* write_resp_hd */
|
||||||
ZERO_NULL, /* connection_check */
|
ZERO_NULL, /* connection_check */
|
||||||
ZERO_NULL, /* attach connection */
|
ZERO_NULL, /* attach connection */
|
||||||
|
ZERO_NULL, /* follow */
|
||||||
PORT_GOPHER, /* defport */
|
PORT_GOPHER, /* defport */
|
||||||
CURLPROTO_GOPHER, /* protocol */
|
CURLPROTO_GOPHER, /* protocol */
|
||||||
CURLPROTO_GOPHER, /* family */
|
CURLPROTO_GOPHER, /* family */
|
||||||
|
@ -104,6 +105,7 @@ const struct Curl_handler Curl_handler_gophers = {
|
||||||
ZERO_NULL, /* write_resp_hd */
|
ZERO_NULL, /* write_resp_hd */
|
||||||
ZERO_NULL, /* connection_check */
|
ZERO_NULL, /* connection_check */
|
||||||
ZERO_NULL, /* attach connection */
|
ZERO_NULL, /* attach connection */
|
||||||
|
ZERO_NULL, /* follow */
|
||||||
PORT_GOPHER, /* defport */
|
PORT_GOPHER, /* defport */
|
||||||
CURLPROTO_GOPHERS, /* protocol */
|
CURLPROTO_GOPHERS, /* protocol */
|
||||||
CURLPROTO_GOPHER, /* family */
|
CURLPROTO_GOPHER, /* family */
|
||||||
|
|
280
lib/http.c
280
lib/http.c
|
@ -64,6 +64,7 @@
|
||||||
#include "http_negotiate.h"
|
#include "http_negotiate.h"
|
||||||
#include "http_aws_sigv4.h"
|
#include "http_aws_sigv4.h"
|
||||||
#include "url.h"
|
#include "url.h"
|
||||||
|
#include "urlapi-int.h"
|
||||||
#include "share.h"
|
#include "share.h"
|
||||||
#include "hostip.h"
|
#include "hostip.h"
|
||||||
#include "dynhds.h"
|
#include "dynhds.h"
|
||||||
|
@ -145,6 +146,7 @@ const struct Curl_handler Curl_handler_http = {
|
||||||
Curl_http_write_resp_hd, /* write_resp_hd */
|
Curl_http_write_resp_hd, /* write_resp_hd */
|
||||||
ZERO_NULL, /* connection_check */
|
ZERO_NULL, /* connection_check */
|
||||||
ZERO_NULL, /* attach connection */
|
ZERO_NULL, /* attach connection */
|
||||||
|
Curl_http_follow, /* follow */
|
||||||
PORT_HTTP, /* defport */
|
PORT_HTTP, /* defport */
|
||||||
CURLPROTO_HTTP, /* protocol */
|
CURLPROTO_HTTP, /* protocol */
|
||||||
CURLPROTO_HTTP, /* family */
|
CURLPROTO_HTTP, /* family */
|
||||||
|
@ -174,6 +176,7 @@ const struct Curl_handler Curl_handler_https = {
|
||||||
Curl_http_write_resp_hd, /* write_resp_hd */
|
Curl_http_write_resp_hd, /* write_resp_hd */
|
||||||
ZERO_NULL, /* connection_check */
|
ZERO_NULL, /* connection_check */
|
||||||
ZERO_NULL, /* attach connection */
|
ZERO_NULL, /* attach connection */
|
||||||
|
Curl_http_follow, /* follow */
|
||||||
PORT_HTTPS, /* defport */
|
PORT_HTTPS, /* defport */
|
||||||
CURLPROTO_HTTPS, /* protocol */
|
CURLPROTO_HTTPS, /* protocol */
|
||||||
CURLPROTO_HTTP, /* family */
|
CURLPROTO_HTTP, /* family */
|
||||||
|
@ -1091,6 +1094,283 @@ static bool http_should_fail(struct Curl_easy *data, int httpcode)
|
||||||
return data->state.authproblem;
|
return data->state.authproblem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CURLcode Curl_http_follow(struct Curl_easy *data, const char *newurl,
|
||||||
|
followtype type)
|
||||||
|
{
|
||||||
|
bool disallowport = FALSE;
|
||||||
|
bool reachedmax = FALSE;
|
||||||
|
char *follow_url = NULL;
|
||||||
|
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 */
|
||||||
|
follow_url = strdup(newurl);
|
||||||
|
if(!follow_url)
|
||||||
|
return CURLE_OUT_OF_MEMORY;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
uc = curl_url_get(data->state.uh, CURLUPART_URL, &follow_url, 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(follow_url);
|
||||||
|
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(follow_url);
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DEBUGASSERT(follow_url);
|
||||||
|
|
||||||
|
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 = follow_url;
|
||||||
|
|
||||||
|
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 = follow_url;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Curl_compareheader()
|
* Curl_compareheader()
|
||||||
*
|
*
|
||||||
|
|
16
lib/http.h
16
lib/http.h
|
@ -42,6 +42,18 @@ typedef enum {
|
||||||
HTTPREQ_HEAD
|
HTTPREQ_HEAD
|
||||||
} Curl_HttpReq;
|
} Curl_HttpReq;
|
||||||
|
|
||||||
|
|
||||||
|
/* When redirecting transfers. */
|
||||||
|
typedef enum {
|
||||||
|
FOLLOW_NONE, /* not used within the function, just a placeholder to
|
||||||
|
allow initing to this */
|
||||||
|
FOLLOW_FAKE, /* only records stuff, not actually following */
|
||||||
|
FOLLOW_RETRY, /* set if this is a request retry as opposed to a real
|
||||||
|
redirect following */
|
||||||
|
FOLLOW_REDIR /* a full true redirect */
|
||||||
|
} followtype;
|
||||||
|
|
||||||
|
|
||||||
#ifndef CURL_DISABLE_HTTP
|
#ifndef CURL_DISABLE_HTTP
|
||||||
|
|
||||||
#if defined(USE_HTTP3)
|
#if defined(USE_HTTP3)
|
||||||
|
@ -103,6 +115,10 @@ CURLcode Curl_http_input_auth(struct Curl_easy *data, bool proxy,
|
||||||
const char *auth);
|
const char *auth);
|
||||||
CURLcode Curl_http_auth_act(struct Curl_easy *data);
|
CURLcode Curl_http_auth_act(struct Curl_easy *data);
|
||||||
|
|
||||||
|
/* follow a redirect or not */
|
||||||
|
CURLcode Curl_http_follow(struct Curl_easy *data, const char *newurl,
|
||||||
|
followtype type);
|
||||||
|
|
||||||
/* If only the PICKNONE bit is set, there has been a round-trip and we
|
/* If only the PICKNONE bit is set, there has been a round-trip and we
|
||||||
selected to use no auth at all. Ie, we actively select no auth, as opposed
|
selected to use no auth at all. Ie, we actively select no auth, as opposed
|
||||||
to not having one selected. The other CURLAUTH_* defines are present in the
|
to not having one selected. The other CURLAUTH_* defines are present in the
|
||||||
|
|
|
@ -134,6 +134,7 @@ const struct Curl_handler Curl_handler_imap = {
|
||||||
ZERO_NULL, /* write_resp_hd */
|
ZERO_NULL, /* write_resp_hd */
|
||||||
ZERO_NULL, /* connection_check */
|
ZERO_NULL, /* connection_check */
|
||||||
ZERO_NULL, /* attach connection */
|
ZERO_NULL, /* attach connection */
|
||||||
|
ZERO_NULL, /* follow */
|
||||||
PORT_IMAP, /* defport */
|
PORT_IMAP, /* defport */
|
||||||
CURLPROTO_IMAP, /* protocol */
|
CURLPROTO_IMAP, /* protocol */
|
||||||
CURLPROTO_IMAP, /* family */
|
CURLPROTO_IMAP, /* family */
|
||||||
|
@ -164,6 +165,7 @@ const struct Curl_handler Curl_handler_imaps = {
|
||||||
ZERO_NULL, /* write_resp_hd */
|
ZERO_NULL, /* write_resp_hd */
|
||||||
ZERO_NULL, /* connection_check */
|
ZERO_NULL, /* connection_check */
|
||||||
ZERO_NULL, /* attach connection */
|
ZERO_NULL, /* attach connection */
|
||||||
|
ZERO_NULL, /* follow */
|
||||||
PORT_IMAPS, /* defport */
|
PORT_IMAPS, /* defport */
|
||||||
CURLPROTO_IMAPS, /* protocol */
|
CURLPROTO_IMAPS, /* protocol */
|
||||||
CURLPROTO_IMAP, /* family */
|
CURLPROTO_IMAP, /* family */
|
||||||
|
|
|
@ -187,6 +187,7 @@ const struct Curl_handler Curl_handler_ldap = {
|
||||||
ZERO_NULL, /* write_resp_hd */
|
ZERO_NULL, /* write_resp_hd */
|
||||||
ZERO_NULL, /* connection_check */
|
ZERO_NULL, /* connection_check */
|
||||||
ZERO_NULL, /* attach connection */
|
ZERO_NULL, /* attach connection */
|
||||||
|
ZERO_NULL, /* follow */
|
||||||
PORT_LDAP, /* defport */
|
PORT_LDAP, /* defport */
|
||||||
CURLPROTO_LDAP, /* protocol */
|
CURLPROTO_LDAP, /* protocol */
|
||||||
CURLPROTO_LDAP, /* family */
|
CURLPROTO_LDAP, /* family */
|
||||||
|
@ -216,6 +217,7 @@ const struct Curl_handler Curl_handler_ldaps = {
|
||||||
ZERO_NULL, /* write_resp_hd */
|
ZERO_NULL, /* write_resp_hd */
|
||||||
ZERO_NULL, /* connection_check */
|
ZERO_NULL, /* connection_check */
|
||||||
ZERO_NULL, /* attach connection */
|
ZERO_NULL, /* attach connection */
|
||||||
|
ZERO_NULL, /* follow */
|
||||||
PORT_LDAPS, /* defport */
|
PORT_LDAPS, /* defport */
|
||||||
CURLPROTO_LDAPS, /* protocol */
|
CURLPROTO_LDAPS, /* protocol */
|
||||||
CURLPROTO_LDAP, /* family */
|
CURLPROTO_LDAP, /* family */
|
||||||
|
|
|
@ -92,6 +92,7 @@ const struct Curl_handler Curl_handler_mqtt = {
|
||||||
ZERO_NULL, /* write_resp_hd */
|
ZERO_NULL, /* write_resp_hd */
|
||||||
ZERO_NULL, /* connection_check */
|
ZERO_NULL, /* connection_check */
|
||||||
ZERO_NULL, /* attach connection */
|
ZERO_NULL, /* attach connection */
|
||||||
|
ZERO_NULL, /* follow */
|
||||||
PORT_MQTT, /* defport */
|
PORT_MQTT, /* defport */
|
||||||
CURLPROTO_MQTT, /* protocol */
|
CURLPROTO_MQTT, /* protocol */
|
||||||
CURLPROTO_MQTT, /* family */
|
CURLPROTO_MQTT, /* family */
|
||||||
|
|
292
lib/multi.c
292
lib/multi.c
|
@ -1855,289 +1855,13 @@ static void multi_posttransfer(struct Curl_easy *data)
|
||||||
* This function DOES NOT FREE the given url.
|
* This function DOES NOT FREE the given url.
|
||||||
*/
|
*/
|
||||||
static CURLcode multi_follow(struct Curl_easy *data,
|
static CURLcode multi_follow(struct Curl_easy *data,
|
||||||
char *newurl, /* the Location: string */
|
const struct Curl_handler *handler,
|
||||||
|
const char *newurl, /* the Location: string */
|
||||||
followtype type) /* see transfer.h */
|
followtype type) /* see transfer.h */
|
||||||
{
|
{
|
||||||
#ifdef CURL_DISABLE_HTTP
|
if(handler && handler->follow)
|
||||||
(void)data;
|
return handler->follow(data, newurl, type);
|
||||||
(void)newurl;
|
|
||||||
(void)type;
|
|
||||||
/* Location: following will not happen when HTTP is disabled */
|
|
||||||
return CURLE_TOO_MANY_REDIRECTS;
|
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 state_performing(struct Curl_easy *data,
|
static CURLMcode state_performing(struct Curl_easy *data,
|
||||||
|
@ -2236,6 +1960,7 @@ static CURLMcode state_performing(struct Curl_easy *data,
|
||||||
multi_done(data, result, TRUE);
|
multi_done(data, result, TRUE);
|
||||||
}
|
}
|
||||||
else if(data->req.done && !Curl_cwriter_is_paused(data)) {
|
else if(data->req.done && !Curl_cwriter_is_paused(data)) {
|
||||||
|
const struct Curl_handler *handler = data->conn->handler;
|
||||||
|
|
||||||
/* call this even if the readwrite function returned error */
|
/* call this even if the readwrite function returned error */
|
||||||
multi_posttransfer(data);
|
multi_posttransfer(data);
|
||||||
|
@ -2256,7 +1981,7 @@ static CURLMcode state_performing(struct Curl_easy *data,
|
||||||
follow = FOLLOW_RETRY;
|
follow = FOLLOW_RETRY;
|
||||||
(void)multi_done(data, CURLE_OK, FALSE);
|
(void)multi_done(data, CURLE_OK, FALSE);
|
||||||
/* multi_done() might return CURLE_GOT_NOTHING */
|
/* multi_done() might return CURLE_GOT_NOTHING */
|
||||||
result = multi_follow(data, newurl, follow);
|
result = multi_follow(data, handler, newurl, follow);
|
||||||
if(!result) {
|
if(!result) {
|
||||||
multistate(data, MSTATE_SETUP);
|
multistate(data, MSTATE_SETUP);
|
||||||
rc = CURLM_CALL_MULTI_PERFORM;
|
rc = CURLM_CALL_MULTI_PERFORM;
|
||||||
|
@ -2271,7 +1996,7 @@ static CURLMcode state_performing(struct Curl_easy *data,
|
||||||
free(newurl);
|
free(newurl);
|
||||||
newurl = data->req.location;
|
newurl = data->req.location;
|
||||||
data->req.location = NULL;
|
data->req.location = NULL;
|
||||||
result = multi_follow(data, newurl, FOLLOW_FAKE);
|
result = multi_follow(data, handler, newurl, FOLLOW_FAKE);
|
||||||
if(result) {
|
if(result) {
|
||||||
*stream_errorp = TRUE;
|
*stream_errorp = TRUE;
|
||||||
result = multi_done(data, result, TRUE);
|
result = multi_done(data, result, TRUE);
|
||||||
|
@ -2380,6 +2105,7 @@ static CURLMcode state_do(struct Curl_easy *data,
|
||||||
* unexpectedly died. If possible, send the connection back to the
|
* unexpectedly died. If possible, send the connection back to the
|
||||||
* CONNECT phase so we can try again.
|
* CONNECT phase so we can try again.
|
||||||
*/
|
*/
|
||||||
|
const struct Curl_handler *handler = data->conn->handler;
|
||||||
char *newurl = NULL;
|
char *newurl = NULL;
|
||||||
followtype follow = FOLLOW_NONE;
|
followtype follow = FOLLOW_NONE;
|
||||||
CURLcode drc;
|
CURLcode drc;
|
||||||
|
@ -2399,7 +2125,7 @@ static CURLMcode state_do(struct Curl_easy *data,
|
||||||
if(newurl) {
|
if(newurl) {
|
||||||
if(!drc || (drc == CURLE_SEND_ERROR)) {
|
if(!drc || (drc == CURLE_SEND_ERROR)) {
|
||||||
follow = FOLLOW_RETRY;
|
follow = FOLLOW_RETRY;
|
||||||
drc = multi_follow(data, newurl, follow);
|
drc = multi_follow(data, handler, newurl, follow);
|
||||||
if(!drc) {
|
if(!drc) {
|
||||||
multistate(data, MSTATE_SETUP);
|
multistate(data, MSTATE_SETUP);
|
||||||
rc = CURLM_CALL_MULTI_PERFORM;
|
rc = CURLM_CALL_MULTI_PERFORM;
|
||||||
|
|
|
@ -134,6 +134,7 @@ const struct Curl_handler Curl_handler_ldap = {
|
||||||
ZERO_NULL, /* write_resp_hd */
|
ZERO_NULL, /* write_resp_hd */
|
||||||
ZERO_NULL, /* connection_check */
|
ZERO_NULL, /* connection_check */
|
||||||
ZERO_NULL, /* attach connection */
|
ZERO_NULL, /* attach connection */
|
||||||
|
ZERO_NULL, /* follow */
|
||||||
PORT_LDAP, /* defport */
|
PORT_LDAP, /* defport */
|
||||||
CURLPROTO_LDAP, /* protocol */
|
CURLPROTO_LDAP, /* protocol */
|
||||||
CURLPROTO_LDAP, /* family */
|
CURLPROTO_LDAP, /* family */
|
||||||
|
@ -163,6 +164,7 @@ const struct Curl_handler Curl_handler_ldaps = {
|
||||||
ZERO_NULL, /* write_resp_hd */
|
ZERO_NULL, /* write_resp_hd */
|
||||||
ZERO_NULL, /* connection_check */
|
ZERO_NULL, /* connection_check */
|
||||||
ZERO_NULL, /* attach connection */
|
ZERO_NULL, /* attach connection */
|
||||||
|
ZERO_NULL, /* follow */
|
||||||
PORT_LDAPS, /* defport */
|
PORT_LDAPS, /* defport */
|
||||||
CURLPROTO_LDAPS, /* protocol */
|
CURLPROTO_LDAPS, /* protocol */
|
||||||
CURLPROTO_LDAP, /* family */
|
CURLPROTO_LDAP, /* family */
|
||||||
|
|
|
@ -138,6 +138,7 @@ const struct Curl_handler Curl_handler_pop3 = {
|
||||||
ZERO_NULL, /* write_resp_hd */
|
ZERO_NULL, /* write_resp_hd */
|
||||||
ZERO_NULL, /* connection_check */
|
ZERO_NULL, /* connection_check */
|
||||||
ZERO_NULL, /* attach connection */
|
ZERO_NULL, /* attach connection */
|
||||||
|
ZERO_NULL, /* follow */
|
||||||
PORT_POP3, /* defport */
|
PORT_POP3, /* defport */
|
||||||
CURLPROTO_POP3, /* protocol */
|
CURLPROTO_POP3, /* protocol */
|
||||||
CURLPROTO_POP3, /* family */
|
CURLPROTO_POP3, /* family */
|
||||||
|
@ -168,6 +169,7 @@ const struct Curl_handler Curl_handler_pop3s = {
|
||||||
ZERO_NULL, /* write_resp_hd */
|
ZERO_NULL, /* write_resp_hd */
|
||||||
ZERO_NULL, /* connection_check */
|
ZERO_NULL, /* connection_check */
|
||||||
ZERO_NULL, /* attach connection */
|
ZERO_NULL, /* attach connection */
|
||||||
|
ZERO_NULL, /* follow */
|
||||||
PORT_POP3S, /* defport */
|
PORT_POP3S, /* defport */
|
||||||
CURLPROTO_POP3S, /* protocol */
|
CURLPROTO_POP3S, /* protocol */
|
||||||
CURLPROTO_POP3, /* family */
|
CURLPROTO_POP3, /* family */
|
||||||
|
|
|
@ -117,6 +117,7 @@ const struct Curl_handler Curl_handler_rtsp = {
|
||||||
ZERO_NULL, /* write_resp_hd */
|
ZERO_NULL, /* write_resp_hd */
|
||||||
rtsp_conncheck, /* connection_check */
|
rtsp_conncheck, /* connection_check */
|
||||||
ZERO_NULL, /* attach connection */
|
ZERO_NULL, /* attach connection */
|
||||||
|
Curl_http_follow, /* follow */
|
||||||
PORT_RTSP, /* defport */
|
PORT_RTSP, /* defport */
|
||||||
CURLPROTO_RTSP, /* protocol */
|
CURLPROTO_RTSP, /* protocol */
|
||||||
CURLPROTO_RTSP, /* family */
|
CURLPROTO_RTSP, /* family */
|
||||||
|
|
|
@ -273,6 +273,7 @@ const struct Curl_handler Curl_handler_smb = {
|
||||||
ZERO_NULL, /* write_resp_hd */
|
ZERO_NULL, /* write_resp_hd */
|
||||||
ZERO_NULL, /* connection_check */
|
ZERO_NULL, /* connection_check */
|
||||||
ZERO_NULL, /* attach connection */
|
ZERO_NULL, /* attach connection */
|
||||||
|
ZERO_NULL, /* follow */
|
||||||
PORT_SMB, /* defport */
|
PORT_SMB, /* defport */
|
||||||
CURLPROTO_SMB, /* protocol */
|
CURLPROTO_SMB, /* protocol */
|
||||||
CURLPROTO_SMB, /* family */
|
CURLPROTO_SMB, /* family */
|
||||||
|
@ -301,6 +302,7 @@ const struct Curl_handler Curl_handler_smbs = {
|
||||||
ZERO_NULL, /* write_resp_hd */
|
ZERO_NULL, /* write_resp_hd */
|
||||||
ZERO_NULL, /* connection_check */
|
ZERO_NULL, /* connection_check */
|
||||||
ZERO_NULL, /* attach connection */
|
ZERO_NULL, /* attach connection */
|
||||||
|
ZERO_NULL, /* follow */
|
||||||
PORT_SMBS, /* defport */
|
PORT_SMBS, /* defport */
|
||||||
CURLPROTO_SMBS, /* protocol */
|
CURLPROTO_SMBS, /* protocol */
|
||||||
CURLPROTO_SMB, /* family */
|
CURLPROTO_SMB, /* family */
|
||||||
|
|
|
@ -135,6 +135,7 @@ const struct Curl_handler Curl_handler_smtp = {
|
||||||
ZERO_NULL, /* write_resp_hd */
|
ZERO_NULL, /* write_resp_hd */
|
||||||
ZERO_NULL, /* connection_check */
|
ZERO_NULL, /* connection_check */
|
||||||
ZERO_NULL, /* attach connection */
|
ZERO_NULL, /* attach connection */
|
||||||
|
ZERO_NULL, /* follow */
|
||||||
PORT_SMTP, /* defport */
|
PORT_SMTP, /* defport */
|
||||||
CURLPROTO_SMTP, /* protocol */
|
CURLPROTO_SMTP, /* protocol */
|
||||||
CURLPROTO_SMTP, /* family */
|
CURLPROTO_SMTP, /* family */
|
||||||
|
@ -165,6 +166,7 @@ const struct Curl_handler Curl_handler_smtps = {
|
||||||
ZERO_NULL, /* write_resp_hd */
|
ZERO_NULL, /* write_resp_hd */
|
||||||
ZERO_NULL, /* connection_check */
|
ZERO_NULL, /* connection_check */
|
||||||
ZERO_NULL, /* attach connection */
|
ZERO_NULL, /* attach connection */
|
||||||
|
ZERO_NULL, /* follow */
|
||||||
PORT_SMTPS, /* defport */
|
PORT_SMTPS, /* defport */
|
||||||
CURLPROTO_SMTPS, /* protocol */
|
CURLPROTO_SMTPS, /* protocol */
|
||||||
CURLPROTO_SMTP, /* family */
|
CURLPROTO_SMTP, /* family */
|
||||||
|
|
|
@ -190,6 +190,7 @@ const struct Curl_handler Curl_handler_telnet = {
|
||||||
ZERO_NULL, /* write_resp_hd */
|
ZERO_NULL, /* write_resp_hd */
|
||||||
ZERO_NULL, /* connection_check */
|
ZERO_NULL, /* connection_check */
|
||||||
ZERO_NULL, /* attach connection */
|
ZERO_NULL, /* attach connection */
|
||||||
|
ZERO_NULL, /* follow */
|
||||||
PORT_TELNET, /* defport */
|
PORT_TELNET, /* defport */
|
||||||
CURLPROTO_TELNET, /* protocol */
|
CURLPROTO_TELNET, /* protocol */
|
||||||
CURLPROTO_TELNET, /* family */
|
CURLPROTO_TELNET, /* family */
|
||||||
|
|
|
@ -185,6 +185,7 @@ const struct Curl_handler Curl_handler_tftp = {
|
||||||
ZERO_NULL, /* write_resp_hd */
|
ZERO_NULL, /* write_resp_hd */
|
||||||
ZERO_NULL, /* connection_check */
|
ZERO_NULL, /* connection_check */
|
||||||
ZERO_NULL, /* attach connection */
|
ZERO_NULL, /* attach connection */
|
||||||
|
ZERO_NULL, /* follow */
|
||||||
PORT_TFTP, /* defport */
|
PORT_TFTP, /* defport */
|
||||||
CURLPROTO_TFTP, /* protocol */
|
CURLPROTO_TFTP, /* protocol */
|
||||||
CURLPROTO_TFTP, /* family */
|
CURLPROTO_TFTP, /* family */
|
||||||
|
|
|
@ -33,15 +33,6 @@ void Curl_init_CONNECT(struct Curl_easy *data);
|
||||||
|
|
||||||
CURLcode Curl_pretransfer(struct Curl_easy *data);
|
CURLcode Curl_pretransfer(struct Curl_easy *data);
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
FOLLOW_NONE, /* not used within the function, just a placeholder to
|
|
||||||
allow initing to this */
|
|
||||||
FOLLOW_FAKE, /* only records stuff, not actually following */
|
|
||||||
FOLLOW_RETRY, /* set if this is a request retry as opposed to a real
|
|
||||||
redirect following */
|
|
||||||
FOLLOW_REDIR /* a full true redirect */
|
|
||||||
} followtype;
|
|
||||||
|
|
||||||
CURLcode Curl_sendrecv(struct Curl_easy *data, struct curltime *nowp);
|
CURLcode Curl_sendrecv(struct Curl_easy *data, struct curltime *nowp);
|
||||||
int Curl_single_getsock(struct Curl_easy *data,
|
int Curl_single_getsock(struct Curl_easy *data,
|
||||||
struct connectdata *conn, curl_socket_t *socks);
|
struct connectdata *conn, curl_socket_t *socks);
|
||||||
|
|
|
@ -676,6 +676,12 @@ struct Curl_handler {
|
||||||
/* attach() attaches this transfer to this connection */
|
/* attach() attaches this transfer to this connection */
|
||||||
void (*attach)(struct Curl_easy *data, struct connectdata *conn);
|
void (*attach)(struct Curl_easy *data, struct connectdata *conn);
|
||||||
|
|
||||||
|
/* return CURLE_OK if a redirect to `newurl` should be followed,
|
||||||
|
CURLE_TOO_MANY_REDIRECTS otherwise. May alter `data` to change
|
||||||
|
the way the follow request is performed. */
|
||||||
|
CURLcode (*follow)(struct Curl_easy *data, const char *newurl,
|
||||||
|
followtype type);
|
||||||
|
|
||||||
int defport; /* Default port. */
|
int defport; /* Default port. */
|
||||||
curl_prot_t protocol; /* See CURLPROTO_* - this needs to be the single
|
curl_prot_t protocol; /* See CURLPROTO_* - this needs to be the single
|
||||||
specific protocol bit */
|
specific protocol bit */
|
||||||
|
|
|
@ -161,6 +161,7 @@ const struct Curl_handler Curl_handler_scp = {
|
||||||
ZERO_NULL, /* write_resp_hd */
|
ZERO_NULL, /* write_resp_hd */
|
||||||
ZERO_NULL, /* connection_check */
|
ZERO_NULL, /* connection_check */
|
||||||
ZERO_NULL, /* attach connection */
|
ZERO_NULL, /* attach connection */
|
||||||
|
ZERO_NULL, /* follow */
|
||||||
PORT_SSH, /* defport */
|
PORT_SSH, /* defport */
|
||||||
CURLPROTO_SCP, /* protocol */
|
CURLPROTO_SCP, /* protocol */
|
||||||
CURLPROTO_SCP, /* family */
|
CURLPROTO_SCP, /* family */
|
||||||
|
@ -189,6 +190,7 @@ const struct Curl_handler Curl_handler_sftp = {
|
||||||
ZERO_NULL, /* write_resp_hd */
|
ZERO_NULL, /* write_resp_hd */
|
||||||
ZERO_NULL, /* connection_check */
|
ZERO_NULL, /* connection_check */
|
||||||
ZERO_NULL, /* attach connection */
|
ZERO_NULL, /* attach connection */
|
||||||
|
ZERO_NULL, /* follow */
|
||||||
PORT_SSH, /* defport */
|
PORT_SSH, /* defport */
|
||||||
CURLPROTO_SFTP, /* protocol */
|
CURLPROTO_SFTP, /* protocol */
|
||||||
CURLPROTO_SFTP, /* family */
|
CURLPROTO_SFTP, /* family */
|
||||||
|
|
|
@ -139,6 +139,7 @@ const struct Curl_handler Curl_handler_scp = {
|
||||||
ZERO_NULL, /* write_resp_hd */
|
ZERO_NULL, /* write_resp_hd */
|
||||||
ZERO_NULL, /* connection_check */
|
ZERO_NULL, /* connection_check */
|
||||||
ssh_attach, /* attach */
|
ssh_attach, /* attach */
|
||||||
|
ZERO_NULL, /* follow */
|
||||||
PORT_SSH, /* defport */
|
PORT_SSH, /* defport */
|
||||||
CURLPROTO_SCP, /* protocol */
|
CURLPROTO_SCP, /* protocol */
|
||||||
CURLPROTO_SCP, /* family */
|
CURLPROTO_SCP, /* family */
|
||||||
|
@ -169,6 +170,7 @@ const struct Curl_handler Curl_handler_sftp = {
|
||||||
ZERO_NULL, /* write_resp_hd */
|
ZERO_NULL, /* write_resp_hd */
|
||||||
ZERO_NULL, /* connection_check */
|
ZERO_NULL, /* connection_check */
|
||||||
ssh_attach, /* attach */
|
ssh_attach, /* attach */
|
||||||
|
ZERO_NULL, /* follow */
|
||||||
PORT_SSH, /* defport */
|
PORT_SSH, /* defport */
|
||||||
CURLPROTO_SFTP, /* protocol */
|
CURLPROTO_SFTP, /* protocol */
|
||||||
CURLPROTO_SFTP, /* family */
|
CURLPROTO_SFTP, /* family */
|
||||||
|
|
|
@ -95,6 +95,7 @@ const struct Curl_handler Curl_handler_scp = {
|
||||||
ZERO_NULL, /* write_resp_hd */
|
ZERO_NULL, /* write_resp_hd */
|
||||||
ZERO_NULL, /* connection_check */
|
ZERO_NULL, /* connection_check */
|
||||||
ZERO_NULL, /* attach connection */
|
ZERO_NULL, /* attach connection */
|
||||||
|
ZERO_NULL, /* follow */
|
||||||
PORT_SSH, /* defport */
|
PORT_SSH, /* defport */
|
||||||
CURLPROTO_SCP, /* protocol */
|
CURLPROTO_SCP, /* protocol */
|
||||||
PROTOPT_DIRLOCK | PROTOPT_CLOSEACTION
|
PROTOPT_DIRLOCK | PROTOPT_CLOSEACTION
|
||||||
|
@ -125,6 +126,7 @@ const struct Curl_handler Curl_handler_sftp = {
|
||||||
ZERO_NULL, /* write_resp_hd */
|
ZERO_NULL, /* write_resp_hd */
|
||||||
ZERO_NULL, /* connection_check */
|
ZERO_NULL, /* connection_check */
|
||||||
ZERO_NULL, /* attach connection */
|
ZERO_NULL, /* attach connection */
|
||||||
|
ZERO_NULL, /* follow */
|
||||||
PORT_SSH, /* defport */
|
PORT_SSH, /* defport */
|
||||||
CURLPROTO_SFTP, /* protocol */
|
CURLPROTO_SFTP, /* protocol */
|
||||||
CURLPROTO_SFTP, /* family */
|
CURLPROTO_SFTP, /* family */
|
||||||
|
|
2
lib/ws.c
2
lib/ws.c
|
@ -1334,6 +1334,7 @@ const struct Curl_handler Curl_handler_ws = {
|
||||||
Curl_http_write_resp_hd, /* write_resp_hd */
|
Curl_http_write_resp_hd, /* write_resp_hd */
|
||||||
ZERO_NULL, /* connection_check */
|
ZERO_NULL, /* connection_check */
|
||||||
ZERO_NULL, /* attach connection */
|
ZERO_NULL, /* attach connection */
|
||||||
|
Curl_http_follow, /* follow */
|
||||||
PORT_HTTP, /* defport */
|
PORT_HTTP, /* defport */
|
||||||
CURLPROTO_WS, /* protocol */
|
CURLPROTO_WS, /* protocol */
|
||||||
CURLPROTO_HTTP, /* family */
|
CURLPROTO_HTTP, /* family */
|
||||||
|
@ -1360,6 +1361,7 @@ const struct Curl_handler Curl_handler_wss = {
|
||||||
Curl_http_write_resp_hd, /* write_resp_hd */
|
Curl_http_write_resp_hd, /* write_resp_hd */
|
||||||
ZERO_NULL, /* connection_check */
|
ZERO_NULL, /* connection_check */
|
||||||
ZERO_NULL, /* attach connection */
|
ZERO_NULL, /* attach connection */
|
||||||
|
Curl_http_follow, /* follow */
|
||||||
PORT_HTTPS, /* defport */
|
PORT_HTTPS, /* defport */
|
||||||
CURLPROTO_WSS, /* protocol */
|
CURLPROTO_WSS, /* protocol */
|
||||||
CURLPROTO_HTTP, /* family */
|
CURLPROTO_HTTP, /* family */
|
||||||
|
|
Loading…
Reference in New Issue
Block a user