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:
Stefan Eissing 2025-01-23 11:48:06 +01:00 committed by Daniel Stenberg
parent e83818cae1
commit 1213c31272
No known key found for this signature in database
GPG Key ID: 5CC908FDB71E12C2
24 changed files with 347 additions and 292 deletions

View File

@ -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 */

View File

@ -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 */

View File

@ -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 */

View File

@ -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 */

View File

@ -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 */

View File

@ -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()
* *

View File

@ -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

View File

@ -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 */

View File

@ -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 */

View File

@ -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 */

View File

@ -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;

View File

@ -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 */

View File

@ -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 */

View File

@ -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 */

View File

@ -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 */

View File

@ -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 */

View File

@ -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 */

View File

@ -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 */

View File

@ -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);

View File

@ -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 */

View File

@ -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 */

View File

@ -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 */

View File

@ -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 */

View File

@ -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 */