Skip to content

Commit 93f992d

Browse files
committed
Refacto on follow location types.
1 parent f00ade9 commit 93f992d

11 files changed

Lines changed: 93 additions & 61 deletions

File tree

packages/hurl/src/cli/options/mod.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ use std::str::FromStr;
3232
use std::time::Duration;
3333

3434
use hurl::http;
35-
use hurl::http::{Header, HeaderVec, RequestedHttpVersion};
35+
use hurl::http::{CredentialForwarding, FollowLocation, Header, HeaderVec, RequestedHttpVersion};
3636
use hurl::pretty::PrettyMode;
3737
use hurl::runner::Output;
3838
use hurl::util::logger;
@@ -341,6 +341,11 @@ impl CliOptions {
341341
let digest = self.digest;
342342
let follow_location = self.follow_location;
343343
let follow_location_trusted = self.follow_location_trusted;
344+
let follow_location = match (follow_location, follow_location_trusted) {
345+
(true, true) => FollowLocation::Follow(CredentialForwarding::AllHosts),
346+
(true, false) => FollowLocation::Follow(CredentialForwarding::OnlyInitialHost),
347+
(false, _) => FollowLocation::No,
348+
};
344349
let from_entry = self.from_entry;
345350

346351
// TODO: do we want to manage the headers with no content? There are two type of no-content
@@ -409,7 +414,6 @@ impl CliOptions {
409414
.context_dir(&context_dir)
410415
.cookie_input_file(cookie_input_file)
411416
.follow_location(follow_location)
412-
.follow_location_trusted(follow_location_trusted)
413417
.from_entry(from_entry)
414418
.headers(headers)
415419
.http_version(http_version)

packages/hurl/src/http/client.rs

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,9 @@ use super::header::{
4141
use super::ip::IpAddr;
4242
use super::options::{ClientOptions, Verbosity};
4343
use super::param::Param;
44-
use super::request::{IpResolve, Request, RequestedHttpVersion};
44+
use super::request::{
45+
CredentialForwarding, FollowLocation, IpResolve, Request, RequestedHttpVersion,
46+
};
4547
use super::request_cookie::RequestCookie;
4648
use super::request_spec::{Body, FileParam, Method, MultipartParam, RequestSpec};
4749
use super::response::{HttpVersion, Response};
@@ -100,7 +102,7 @@ impl Client {
100102
loop {
101103
let call = self.execute(&request_spec, &options, logger)?;
102104
// If we don't follow redirection, we can early exit here.
103-
if !options.follow_location {
105+
if matches!(options.follow_location, FollowLocation::No) {
104106
calls.push(call);
105107
break;
106108
}
@@ -135,7 +137,7 @@ impl Client {
135137
if should_strip_credentials_on_redirect(
136138
original_url,
137139
&redirect_url,
138-
options.follow_location_trusted,
140+
options.follow_location,
139141
) {
140142
headers.retain(|h| !h.name_eq(AUTHORIZATION));
141143
headers.retain(|h| !h.name_eq(COOKIE));
@@ -860,9 +862,12 @@ impl Client {
860862
fn should_strip_credentials_on_redirect(
861863
original_url: &Url,
862864
redirect_url: &Url,
863-
follow_location_trusted: bool,
865+
follow_location: FollowLocation,
864866
) -> bool {
865-
if follow_location_trusted {
867+
if matches!(
868+
follow_location,
869+
FollowLocation::Follow(CredentialForwarding::AllHosts)
870+
) {
866871
return false;
867872
}
868873
// Different origin != strip credentials
@@ -1108,48 +1113,48 @@ mod tests {
11081113
let url3 = Url::from_str("https://example.com").unwrap();
11091114
let url4 = Url::from_str("http://other.com").unwrap();
11101115

1111-
let follow_location_trusted = false;
1116+
let follow_location = FollowLocation::Follow(CredentialForwarding::OnlyInitialHost);
11121117
assert!(should_strip_credentials_on_redirect(
11131118
&url1,
11141119
&url2,
1115-
follow_location_trusted
1120+
follow_location
11161121
));
11171122
assert!(should_strip_credentials_on_redirect(
11181123
&url1,
11191124
&url3,
1120-
follow_location_trusted
1125+
follow_location
11211126
));
11221127
assert!(should_strip_credentials_on_redirect(
11231128
&url1,
11241129
&url4,
1125-
follow_location_trusted
1130+
follow_location
11261131
));
11271132
assert!(should_strip_credentials_on_redirect(
11281133
&url1,
11291134
&url3,
1130-
follow_location_trusted
1135+
follow_location
11311136
));
11321137

1133-
let follow_location_trusted = true;
1138+
let follow_location = FollowLocation::Follow(CredentialForwarding::AllHosts);
11341139
assert!(!should_strip_credentials_on_redirect(
11351140
&url1,
11361141
&url2,
1137-
follow_location_trusted
1142+
follow_location
11381143
));
11391144
assert!(!should_strip_credentials_on_redirect(
11401145
&url1,
11411146
&url3,
1142-
follow_location_trusted
1147+
follow_location
11431148
));
11441149
assert!(!should_strip_credentials_on_redirect(
11451150
&url1,
11461151
&url4,
1147-
follow_location_trusted
1152+
follow_location
11481153
));
11491154
assert!(!should_strip_credentials_on_redirect(
11501155
&url1,
11511156
&url3,
1152-
follow_location_trusted
1157+
follow_location
11531158
));
11541159
}
11551160

packages/hurl/src/http/curl_cmd.rs

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ use super::cookie_store::CookieStore;
2828
use super::header::{Header, HeaderVec, CONTENT_TYPE};
2929
use super::options::ClientOptions;
3030
use super::param::Param;
31-
use super::request::{IpResolve, RequestedHttpVersion};
31+
use super::request::{CredentialForwarding, FollowLocation, IpResolve, RequestedHttpVersion};
3232
use super::request_spec::{Body, FileParam, Method, MultipartParam, RequestSpec};
3333

3434
/// Represents a curl command, with arguments.
@@ -93,7 +93,7 @@ impl CurlCmd {
9393
}
9494

9595
/// Returns the curl args corresponding to the HTTP method, from a request spec.
96-
fn method_params(request_spec: &RequestSpec, follow_location: bool) -> Vec<String> {
96+
fn method_params(request_spec: &RequestSpec, follow_location: FollowLocation) -> Vec<String> {
9797
let has_body = !request_spec.multipart.is_empty()
9898
|| !request_spec.form.is_empty()
9999
|| !request_spec.body.bytes().is_empty();
@@ -294,7 +294,7 @@ fn encode_bytes(bytes: &[u8]) -> String {
294294

295295
impl Method {
296296
/// Returns the curl args for HTTP method, given the request has a body or not.
297-
fn curl_args(&self, has_body: bool, follow_location: bool) -> Vec<String> {
297+
fn curl_args(&self, has_body: bool, follow_location: FollowLocation) -> Vec<String> {
298298
match self.0.as_str() {
299299
"GET" => {
300300
if has_body {
@@ -317,7 +317,7 @@ impl Method {
317317
// method of the redirection steps.
318318
if has_body {
319319
vec![]
320-
} else if follow_location {
320+
} else if matches!(follow_location, FollowLocation::Follow(_)) {
321321
vec!["--data".to_string(), "''".to_string()]
322322
} else {
323323
vec!["--request".to_string(), "POST".to_string()]
@@ -442,10 +442,14 @@ impl ClientOptions {
442442
IpResolve::IpV4 => arguments.push("--ipv4".to_string()),
443443
IpResolve::IpV6 => arguments.push("--ipv6".to_string()),
444444
}
445-
if self.follow_location_trusted {
446-
arguments.push("--location-trusted".to_string());
447-
} else if self.follow_location {
448-
arguments.push("--location".to_string());
445+
match self.follow_location {
446+
FollowLocation::Follow(CredentialForwarding::OnlyInitialHost) => {
447+
arguments.push("--location".to_string());
448+
}
449+
FollowLocation::Follow(CredentialForwarding::AllHosts) => {
450+
arguments.push("--location-trusted".to_string());
451+
}
452+
FollowLocation::No => {}
449453
}
450454
if let Some(max_filesize) = self.max_filesize {
451455
arguments.push("--max-filesize".to_string());
@@ -682,8 +686,7 @@ mod tests {
682686
connects_to: vec!["example.com:443:host-47.example.com:443".to_string()],
683687
cookie_input_file: Some("cookie_file".to_string()),
684688
digest: false,
685-
follow_location: true,
686-
follow_location_trusted: false,
689+
follow_location: FollowLocation::Follow(CredentialForwarding::OnlyInitialHost),
687690
headers,
688691
http_version: RequestedHttpVersion::Http10,
689692
insecure: true,

packages/hurl/src/http/mod.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,9 @@ pub use self::header::{
3030
};
3131
pub(crate) use self::options::{ClientOptions, Verbosity};
3232
pub(crate) use self::param::Param;
33-
pub use self::request::{IpResolve, Request, RequestedHttpVersion};
33+
pub use self::request::{
34+
CredentialForwarding, FollowLocation, IpResolve, Request, RequestedHttpVersion,
35+
};
3436
pub(crate) use self::request_cookie::RequestCookie;
3537
pub(crate) use self::request_spec::{Body, FileParam, Method, MultipartParam, RequestSpec};
3638
pub use self::response::{HttpVersion, Response};

packages/hurl/src/http/options.rs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ use std::time::Duration;
2020
use hurl_core::types::{BytesPerSec, Count};
2121

2222
use super::header::HeaderVec;
23-
use super::request::{IpResolve, RequestedHttpVersion};
23+
use super::request::{FollowLocation, IpResolve, RequestedHttpVersion};
2424

2525
#[derive(Debug, Clone)]
2626
pub struct ClientOptions {
@@ -38,8 +38,7 @@ pub struct ClientOptions {
3838
pub connects_to: Vec<String>,
3939
pub cookie_input_file: Option<String>,
4040
pub digest: bool,
41-
pub follow_location: bool,
42-
pub follow_location_trusted: bool,
41+
pub follow_location: FollowLocation,
4342
pub headers: HeaderVec,
4443
pub http_version: RequestedHttpVersion,
4544
pub insecure: bool,
@@ -88,8 +87,7 @@ impl Default for ClientOptions {
8887
connects_to: vec![],
8988
cookie_input_file: None,
9089
digest: false,
91-
follow_location: false,
92-
follow_location_trusted: false,
90+
follow_location: FollowLocation::default(),
9391
headers: HeaderVec::new(),
9492
http_version: RequestedHttpVersion::default(),
9593
insecure: false,

packages/hurl/src/http/request.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,26 @@ pub enum IpResolve {
7171
IpV6,
7272
}
7373

74+
/// Do the HTTP client follow redirection, or not?
75+
#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)]
76+
pub enum FollowLocation {
77+
/// No redirection is followed
78+
#[default]
79+
No,
80+
/// Redirection is followed, credentials are passed only to the initial host, or all hosts
81+
/// depending on the credential forwarding mode.
82+
Follow(CredentialForwarding),
83+
}
84+
85+
#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)]
86+
pub enum CredentialForwarding {
87+
/// Credentials are only forwarded to the initial host
88+
#[default]
89+
OnlyInitialHost,
90+
/// Credentials are forwarded to all hosts involved in the redirection
91+
AllHosts,
92+
}
93+
7494
impl Request {
7595
/// Creates a new request.
7696
pub fn new(method: &str, url: Url, headers: HeaderVec, body: Vec<u8>) -> Self {

packages/hurl/src/runner/entry.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -257,7 +257,6 @@ impl ClientOptions {
257257
cookie_input_file: runner_options.cookie_input_file.clone(),
258258
digest: runner_options.digest,
259259
follow_location: runner_options.follow_location,
260-
follow_location_trusted: runner_options.follow_location_trusted,
261260
headers: runner_options.headers.clone(),
262261
http_version: runner_options.http_version,
263262
ip_resolve: runner_options.ip_resolve,

packages/hurl/src/runner/hurl_file.rs

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ use hurl_core::input::Input;
2525
use hurl_core::parser;
2626
use hurl_core::types::{Count, Index};
2727

28-
use crate::http::{Call, Client};
28+
use crate::http::{Call, Client, CredentialForwarding, FollowLocation};
2929
use crate::util::logger::{ErrorFormat, Logger, LoggerOptions};
3030
use crate::util::term::{Stderr, Stdout, WriteMode};
3131

@@ -51,6 +51,7 @@ use super::variable::VariableSet;
5151
///
5252
/// ```
5353
/// use std::collections::HashMap;
54+
/// use hurl::http::{CredentialForwarding, FollowLocation};
5455
/// use hurl::runner;
5556
/// use hurl::runner::{Value, RunnerOptionsBuilder, VariableSet};
5657
/// use hurl::util::logger::{LoggerOptionsBuilder, Verbosity};
@@ -66,7 +67,7 @@ use super::variable::VariableSet;
6667
///
6768
/// // Define runner and logger options
6869
/// let runner_opts = RunnerOptionsBuilder::new()
69-
/// .follow_location(true)
70+
/// .follow_location(FollowLocation::Follow(CredentialForwarding::AllHosts))
7071
/// .build();
7172
/// let logger_opts = LoggerOptionsBuilder::new()
7273
/// .verbosity(Some(Verbosity::Verbose))
@@ -471,7 +472,15 @@ fn get_non_default_options(options: &RunnerOptions) -> Vec<(&'static str, String
471472
}
472473

473474
if options.follow_location != default_options.follow_location {
474-
non_default_options.push(("follow redirect", options.follow_location.to_string()));
475+
match options.follow_location {
476+
FollowLocation::No => {}
477+
FollowLocation::Follow(CredentialForwarding::OnlyInitialHost) => {
478+
non_default_options.push(("follow redirect", "true".to_string()));
479+
}
480+
FollowLocation::Follow(CredentialForwarding::AllHosts) => {
481+
non_default_options.push(("follow redirect", "true (trusted)".to_string()));
482+
}
483+
}
475484
}
476485

477486
if options.insecure != default_options.insecure {

packages/hurl/src/runner/options.rs

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ use hurl_core::ast::{
2121
};
2222
use hurl_core::types::{BytesPerSec, Count, DurationUnit};
2323

24-
use crate::http::{Header, IpResolve, RequestedHttpVersion};
24+
use crate::http::{CredentialForwarding, FollowLocation, Header, IpResolve, RequestedHttpVersion};
2525
use crate::util::logger::{Logger, Verbosity};
2626

2727
use super::error::{RunnerError, RunnerErrorKind};
@@ -167,14 +167,19 @@ pub fn get_entry_options(
167167
}
168168
OptionKind::FollowLocation(value) => {
169169
let value = eval_boolean_option(value, variables)?;
170-
entry_options.follow_location = value;
170+
if value {
171+
entry_options.follow_location =
172+
FollowLocation::Follow(CredentialForwarding::OnlyInitialHost);
173+
} else {
174+
entry_options.follow_location = FollowLocation::No;
175+
}
171176
}
172177
OptionKind::FollowLocationTrusted(value) => {
173178
let value = eval_boolean_option(value, variables)?;
174179
if value {
175-
entry_options.follow_location = true;
180+
entry_options.follow_location =
181+
FollowLocation::Follow(CredentialForwarding::AllHosts);
176182
}
177-
entry_options.follow_location_trusted = value;
178183
}
179184
OptionKind::Insecure(value) => {
180185
let value = eval_boolean_option(value, variables)?;

0 commit comments

Comments
 (0)