curl: add --follow

Makes curl follow redirects an act on the response code and change a
custom method accordingly, contrary to --location.

Potential future command line to send QUERY and following a redirect
according to the status code:

    curl -d "request-body" -X QUERY --follow https://example.com

add test 794,796,797

Assisted-by: Daniel Böhmer <post@daniel-boehmer.de>

Closes #16543
This commit is contained in:
Daniel Stenberg 2025-04-25 13:09:00 +02:00
parent 29a6e15b27
commit 13cbabf05f
No known key found for this signature in database
GPG key ID: 5CC908FDB71E12C2
16 changed files with 340 additions and 10 deletions

View file

@ -512,8 +512,7 @@ static CURLcode http_setopts(struct OperationConfig *config,
{
long postRedir = 0;
my_setopt_long(curl, CURLOPT_FOLLOWLOCATION,
config->followlocation);
my_setopt_long(curl, CURLOPT_FOLLOWLOCATION, config->followlocation);
my_setopt_long(curl, CURLOPT_UNRESTRICTED_AUTH,
config->unrestricted_auth);
my_setopt_str(curl, CURLOPT_AWS_SIGV4, config->aws_sigv4);

View file

@ -224,6 +224,7 @@ struct OperationConfig {
long happy_eyeballs_timeout_ms; /* happy eyeballs timeout in milliseconds.
0 is valid. default: CURL_HET_DEFAULT. */
unsigned long timecond;
long followlocation; /* follow http redirects mode */
HttpReq httpreq;
long proxyver; /* set to CURLPROXY_HTTP* define */
long ftp_ssl_ccc_mode;
@ -264,7 +265,6 @@ struct OperationConfig {
BIT(show_headers); /* show headers to data output */
BIT(no_body); /* do not get the body */
BIT(dirlistonly); /* only get the FTP dir list */
BIT(followlocation); /* follow http redirects */
BIT(unrestricted_auth); /* Continue to send authentication (user+password)
when following redirects, even when hostname
changed */

View file

@ -144,6 +144,7 @@ static const struct LongShort aliases[]= {
{"fail-early", ARG_BOOL, ' ', C_FAIL_EARLY},
{"fail-with-body", ARG_BOOL, ' ', C_FAIL_WITH_BODY},
{"false-start", ARG_BOOL, ' ', C_FALSE_START},
{"follow", ARG_BOOL, ' ', C_FOLLOW},
{"form", ARG_STRG, 'F', C_FORM},
{"form-escape", ARG_BOOL, ' ', C_FORM_ESCAPE},
{"form-string", ARG_STRG, ' ', C_FORM_STRING},
@ -2098,12 +2099,6 @@ static ParameterError opt_bool(struct OperationConfig *config,
case C_LIST_ONLY: /* --list-only */
config->dirlistonly = toggle; /* only list the names of the FTP dir */
break;
case C_LOCATION_TRUSTED: /* --location-trusted */
config->unrestricted_auth = toggle;
FALLTHROUGH();
case C_LOCATION: /* --location */
config->followlocation = toggle; /* Follow Location: HTTP headers */
break;
case C_MANUAL: /* --manual */
if(toggle) /* --no-manual shows no manual... */
return PARAM_MANUAL_REQUESTED;
@ -2166,6 +2161,19 @@ static ParameterError opt_bool(struct OperationConfig *config,
case C_MPTCP: /* --mptcp */
config->mptcp = toggle;
break;
case C_LOCATION_TRUSTED: /* --location-trusted */
config->unrestricted_auth = toggle;
FALLTHROUGH();
case C_LOCATION: /* --location */
if(config->followlocation == CURLFOLLOW_OBEYCODE)
warnf(global, "--location overrides --follow");
config->followlocation = toggle ? CURLFOLLOW_ALL : 0;
break;
case C_FOLLOW: /* --follow */
if(config->followlocation == CURLFOLLOW_ALL)
warnf(global, "--follow overrides --location");
config->followlocation = toggle ? CURLFOLLOW_OBEYCODE : 0;
break;
default:
return PARAM_OPTION_UNKNOWN;
}

View file

@ -90,6 +90,7 @@ typedef enum {
C_FAIL_EARLY,
C_FAIL_WITH_BODY,
C_FALSE_START,
C_FOLLOW,
C_FORM,
C_FORM_ESCAPE,
C_FORM_STRING,

View file

@ -201,6 +201,9 @@ const struct helptxt helptext[] = {
{" --false-start",
"Enable TLS False Start",
CURLHELP_DEPRECATED},
{" --follow",
"Follow redirects per spec",
CURLHELP_HTTP},
{"-F, --form <name=content>",
"Specify multipart MIME data",
CURLHELP_HTTP | CURLHELP_UPLOAD | CURLHELP_POST | CURLHELP_IMAP |

View file

@ -150,6 +150,14 @@ const struct NameValue setopt_nv_CURL_NETRC[] = {
NVEND,
};
const struct NameValue setopt_nv_CURLOPT_FOLLOWLOCATION[] = {
NV(0L),
NV(CURLFOLLOW_ALL),
NV(CURLFOLLOW_OBEYCODE),
NV(CURLFOLLOW_FIRSTONLY),
NVEND,
};
/* These options have non-zero default values. */
static const struct NameValue setopt_nv_CURLNONZERODEFAULTS[] = {
NV1(CURLOPT_SSL_VERIFYPEER, 1),

View file

@ -54,6 +54,7 @@ extern const struct NameValue setopt_nv_CURLFTPSSL_CCC[];
extern const struct NameValue setopt_nv_CURLUSESSL[];
extern const struct NameValueUnsigned setopt_nv_CURLSSLOPT[];
extern const struct NameValue setopt_nv_CURL_NETRC[];
extern const struct NameValue setopt_nv_CURLOPT_FOLLOWLOCATION[];
extern const struct NameValueUnsigned setopt_nv_CURLAUTH[];
extern const struct NameValueUnsigned setopt_nv_CURLHSTS[];