From: Daniel Stenberg Date: Fri, 25 Apr 2025 11:09:00 +0000 (+0200) Subject: curl: add --follow X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=refs%2Fpull%2F16543%2Fhead;p=thirdparty%2Fcurl.git 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 Closes #16543 --- diff --git a/docs/cmdline-opts/Makefile.inc b/docs/cmdline-opts/Makefile.inc index c5f99477fa..768529847a 100644 --- a/docs/cmdline-opts/Makefile.inc +++ b/docs/cmdline-opts/Makefile.inc @@ -101,6 +101,7 @@ DPAGES = \ fail-with-body.md \ fail.md \ false-start.md \ + follow.md \ form-escape.md \ form-string.md \ form.md \ diff --git a/docs/cmdline-opts/follow.md b/docs/cmdline-opts/follow.md new file mode 100644 index 0000000000..9d4225ab35 --- /dev/null +++ b/docs/cmdline-opts/follow.md @@ -0,0 +1,25 @@ +--- +c: Copyright (C) Daniel Stenberg, , et al. +SPDX-License-Identifier: curl +Long: follow +Help: Follow redirects per spec +Category: http +Added: 8.16.0 +Multi: boolean +See-also: + - request + - location +Example: + - -X POST --follow $URL +--- + +# `--follow` + +Instructs curl to follow HTTP redirects and to do the custom request method +set with --request when following redirects as the HTTP specification says. + +The method string set with --request is used in subsequent requests for the +status codes 307 or 308, but may be reset to GET for 301, 302 and 303. + +This is subtly different than --location, as that option always set the custom +method in all subsequent requests independent of response code. diff --git a/docs/cmdline-opts/location-trusted.md b/docs/cmdline-opts/location-trusted.md index 5e20d8cc1f..7d9810802c 100644 --- a/docs/cmdline-opts/location-trusted.md +++ b/docs/cmdline-opts/location-trusted.md @@ -9,6 +9,7 @@ Added: 7.10.4 Multi: boolean See-also: - user + - follow Example: - --location-trusted -u user:password $URL - --location-trusted -H "Cookie: session=abc" $URL diff --git a/docs/cmdline-opts/location.md b/docs/cmdline-opts/location.md index 8d17e45e28..86ae7e3580 100644 --- a/docs/cmdline-opts/location.md +++ b/docs/cmdline-opts/location.md @@ -11,6 +11,7 @@ Multi: boolean See-also: - resolve - alt-svc + - follow Example: - -L $URL --- diff --git a/docs/options-in-versions b/docs/options-in-versions index bf34e76f2c..20f4ea013d 100644 --- a/docs/options-in-versions +++ b/docs/options-in-versions @@ -199,6 +199,7 @@ --range (-r) 4.0 --rate 7.84.0 --raw 7.16.2 +--follow 8.16.0 --referer (-e) 4.0 --remote-header-name (-J) 7.20.0 --remote-name (-O) 4.0 diff --git a/src/config2setopts.c b/src/config2setopts.c index 9a2b20eae2..dfc41398e9 100644 --- a/src/config2setopts.c +++ b/src/config2setopts.c @@ -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); diff --git a/src/tool_cfgable.h b/src/tool_cfgable.h index d23902885e..b8756dae10 100644 --- a/src/tool_cfgable.h +++ b/src/tool_cfgable.h @@ -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 */ diff --git a/src/tool_getparam.c b/src/tool_getparam.c index 4540a59136..b2e651541a 100644 --- a/src/tool_getparam.c +++ b/src/tool_getparam.c @@ -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}, @@ -2097,12 +2098,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; @@ -2165,6 +2160,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; } diff --git a/src/tool_getparam.h b/src/tool_getparam.h index 47e87eab92..184cecb528 100644 --- a/src/tool_getparam.h +++ b/src/tool_getparam.h @@ -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, diff --git a/src/tool_listhelp.c b/src/tool_listhelp.c index 8572250b5c..8e11dca1c9 100644 --- a/src/tool_listhelp.c +++ b/src/tool_listhelp.c @@ -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 ", "Specify multipart MIME data", CURLHELP_HTTP | CURLHELP_UPLOAD | CURLHELP_POST | CURLHELP_IMAP | diff --git a/src/tool_setopt.c b/src/tool_setopt.c index 52c8010466..5c905c86fd 100644 --- a/src/tool_setopt.c +++ b/src/tool_setopt.c @@ -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), diff --git a/src/tool_setopt.h b/src/tool_setopt.h index 8869f2e781..2d179fa993 100644 --- a/src/tool_setopt.h +++ b/src/tool_setopt.h @@ -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[]; diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am index 4fda5f8178..98e2829231 100644 --- a/tests/data/Makefile.am +++ b/tests/data/Makefile.am @@ -110,7 +110,7 @@ test736 test737 test738 test739 test740 test741 test742 test743 test744 \ test745 test746 test747 test748 test749 test750 test751 test752 test753 \ test754 test755 test756 test757 \ test780 test781 test782 test783 test784 test785 test786 test787 test788 \ -test789 test790 test791 test792 test793 \ +test789 test790 test791 test792 test793 test794 test796 test797 \ \ test799 test800 test801 test802 test803 test804 test805 test806 test807 \ test808 test809 test810 test811 test812 test813 test814 test815 test816 \ diff --git a/tests/data/test794 b/tests/data/test794 new file mode 100644 index 0000000000..b6af9677ad --- /dev/null +++ b/tests/data/test794 @@ -0,0 +1,95 @@ + + + +--follow +--location + + +# +# Server-side + + +HTTP/1.1 302 OK +Date: Thu, 09 Nov 2010 14:49:00 GMT +Server: test-server/fake +Last-Modified: Tue, 13 Jun 2000 12:10:00 GMT +ETag: "21025-dc7-39462498" +Accept-Ranges: bytes +Location: %TESTNUMBER0001 +Content-Length: 6 +Connection: close +Content-Type: text/html +Funny-head: yesyes + +-foo- + + +HTTP/1.1 200 OK +Content-Length: 6 +Connection: close +Content-Type: text/html + +-bar- + + +HTTP/1.1 302 OK +Date: Thu, 09 Nov 2010 14:49:00 GMT +Server: test-server/fake +Last-Modified: Tue, 13 Jun 2000 12:10:00 GMT +ETag: "21025-dc7-39462498" +Accept-Ranges: bytes +Location: %TESTNUMBER0001 +Content-Length: 6 +Connection: close +Content-Type: text/html +Funny-head: yesyes + +HTTP/1.1 200 OK +Content-Length: 6 +Connection: close +Content-Type: text/html + +-bar- + + + +# Client-side + + +http + + +http + + + +--follow + --location with custom POST method, 302 => GET + + +http://%HOSTIP:%HTTPPORT/%TESTNUMBER --no-progress-meter -X IGLOO -d moo --location --follow + + + +# Verify data after the test has been "shot" + + +^User-Agent:.* + + +IGLOO /%TESTNUMBER HTTP/1.1 +Host: %HOSTIP:%HTTPPORT +Accept: */* +Content-Length: 3 +Content-Type: application/x-www-form-urlencoded + +mooGET /%TESTNUMBER0001 HTTP/1.1 +Host: %HOSTIP:%HTTPPORT +Accept: */* + + + + +Warning: --follow overrides --location + + + diff --git a/tests/data/test796 b/tests/data/test796 new file mode 100644 index 0000000000..2e0c4077b4 --- /dev/null +++ b/tests/data/test796 @@ -0,0 +1,92 @@ + + + +--follow +--location + + +# +# Server-side + + +HTTP/1.1 302 OK +Date: Thu, 09 Nov 2010 14:49:00 GMT +Server: test-server/fake +Last-Modified: Tue, 13 Jun 2000 12:10:00 GMT +ETag: "21025-dc7-39462498" +Accept-Ranges: bytes +Location: %TESTNUMBER0001 +Content-Length: 6 +Connection: close +Content-Type: text/html +Funny-head: yesyes + +-foo- + + +HTTP/1.1 200 OK +Content-Length: 6 +Connection: close +Content-Type: text/html + +-bar- + + +HTTP/1.1 302 OK +Date: Thu, 09 Nov 2010 14:49:00 GMT +Server: test-server/fake +Last-Modified: Tue, 13 Jun 2000 12:10:00 GMT +ETag: "21025-dc7-39462498" +Accept-Ranges: bytes +Location: %TESTNUMBER0001 +Content-Length: 6 +Connection: close +Content-Type: text/html +Funny-head: yesyes + +HTTP/1.1 200 OK +Content-Length: 6 +Connection: close +Content-Type: text/html + +-bar- + + + +# Client-side + + +http + + +http + + + +--follow with custom POST method, 302 => GET + + +http://%HOSTIP:%HTTPPORT/%TESTNUMBER -X IGLOO -d moo --follow + + + +# Verify data after the test has been "shot" + + +^User-Agent:.* + + +IGLOO /%TESTNUMBER HTTP/1.1 +Host: %HOSTIP:%HTTPPORT +Accept: */* +Content-Length: 3 +Content-Type: application/x-www-form-urlencoded + +mooGET /%TESTNUMBER0001 HTTP/1.1 +Host: %HOSTIP:%HTTPPORT +Accept: */* + + + + + diff --git a/tests/data/test797 b/tests/data/test797 new file mode 100644 index 0000000000..b0602c1302 --- /dev/null +++ b/tests/data/test797 @@ -0,0 +1,94 @@ + + + +--follow +--location + + +# +# Server-side + + +HTTP/1.1 308 OK +Date: Thu, 09 Nov 2010 14:49:00 GMT +Server: test-server/fake +Last-Modified: Tue, 13 Jun 2000 12:10:00 GMT +ETag: "21025-dc7-39462498" +Accept-Ranges: bytes +Location: %TESTNUMBER0001 +Content-Length: 6 +Connection: close +Content-Type: text/html +Funny-head: yesyes + +-foo- + + +HTTP/1.1 200 OK +Content-Length: 6 +Connection: close +Content-Type: text/html + +-bar- + + +HTTP/1.1 308 OK +Date: Thu, 09 Nov 2010 14:49:00 GMT +Server: test-server/fake +Last-Modified: Tue, 13 Jun 2000 12:10:00 GMT +ETag: "21025-dc7-39462498" +Accept-Ranges: bytes +Location: %TESTNUMBER0001 +Content-Length: 6 +Connection: close +Content-Type: text/html +Funny-head: yesyes + +HTTP/1.1 200 OK +Content-Length: 6 +Connection: close +Content-Type: text/html + +-bar- + + + +# Client-side + + +http + + +http + + + +--follow with custom POST method, 308 => custom + + +http://%HOSTIP:%HTTPPORT/%TESTNUMBER -X IGLOO -d moo --follow + + + +# Verify data after the test has been "shot" + + +^User-Agent:.* + + +IGLOO /%TESTNUMBER HTTP/1.1 +Host: %HOSTIP:%HTTPPORT +Accept: */* +Content-Length: 3 +Content-Type: application/x-www-form-urlencoded + +mooIGLOO /%TESTNUMBER0001 HTTP/1.1 +Host: %HOSTIP:%HTTPPORT +Accept: */* +Content-Length: 3 +Content-Type: application/x-www-form-urlencoded + +moo + + +