From 38c47501ab50378ee2d05cdb1d9bddc895fb8a81 Mon Sep 17 00:00:00 2001 From: jcamiel Date: Thu, 8 Dec 2022 18:21:51 +0100 Subject: [PATCH] Add --resolve options --- CHANGELOG.md | 4 ++- integration/tests_ok/resolve.curl | 3 +++ integration/tests_ok/resolve.hurl | 13 ++++++++++ integration/tests_ok/resolve.options | 6 +++++ integration/tests_ok/resolve.out | 1 + packages/hurl/src/cli/options.rs | 15 ++++++++++- packages/hurl/src/http/client.rs | 29 +++++++++++----------- packages/hurl/src/http/options.rs | 14 +++++++++++ packages/hurl/src/runner/entry.rs | 1 + packages/hurl/src/runner/runner_options.rs | 4 +++ 10 files changed, 74 insertions(+), 16 deletions(-) create mode 100644 integration/tests_ok/resolve.curl create mode 100644 integration/tests_ok/resolve.hurl create mode 100644 integration/tests_ok/resolve.options create mode 100644 integration/tests_ok/resolve.out diff --git a/CHANGELOG.md b/CHANGELOG.md index 5adbe3d01..82f7ec244 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ Thanks to [@devnoname120](https://github.com/devnoname120), [@dalejefferson-rnf](https://github.com/dalejefferson-rnf), + Enhancements: * Support new one line string [#1041](https://github.com/Orange-OpenSource/hurl/issues/1041) @@ -23,6 +24,8 @@ Enhancements: * Add GraphQL support [#504](https://github.com/Orange-OpenSource/hurl/issues/504) +* Add --resolve option [#379](https://github.com/Orange-OpenSource/hurl/issues/379) + Bugs Fixed: @@ -31,7 +34,6 @@ Bugs Fixed: * Fix querystring key parsing [#1027](https://github.com/Orange-OpenSource/hurl/issues/1027) - [1.8.0 (2022-11-02)](https://github.com/Orange-OpenSource/hurl/blob/master/CHANGELOG.md#1.8.0) ======================================================================================================================== diff --git a/integration/tests_ok/resolve.curl b/integration/tests_ok/resolve.curl new file mode 100644 index 000000000..aaea26368 --- /dev/null +++ b/integration/tests_ok/resolve.curl @@ -0,0 +1,3 @@ +curl 'http://foo.com:8000/hello' --resolve foo.com:8000:127.0.0.1 --resolve bar.com:8000:127.0.0.1 --resolve baz.com:8000:127.0.0.1 +curl 'http://bar.com:8000/hello' --resolve foo.com:8000:127.0.0.1 --resolve bar.com:8000:127.0.0.1 --resolve baz.com:8000:127.0.0.1 +curl 'http://baz.com:8000/hello' --resolve foo.com:8000:127.0.0.1 --resolve bar.com:8000:127.0.0.1 --resolve baz.com:8000:127.0.0.1 \ No newline at end of file diff --git a/integration/tests_ok/resolve.hurl b/integration/tests_ok/resolve.hurl new file mode 100644 index 000000000..4bd4a8f4b --- /dev/null +++ b/integration/tests_ok/resolve.hurl @@ -0,0 +1,13 @@ +GET http://foo.com:8000/hello +HTTP 200 +`Hello World!` + + +GET http://bar.com:8000/hello +HTTP 200 +`Hello World!` + + +GET http://baz.com:8000/hello +HTTP 200 +`Hello World!` diff --git a/integration/tests_ok/resolve.options b/integration/tests_ok/resolve.options new file mode 100644 index 000000000..e2ebcadec --- /dev/null +++ b/integration/tests_ok/resolve.options @@ -0,0 +1,6 @@ +--resolve +foo.com:8000:127.0.0.1 +--resolve +bar.com:8000:127.0.0.1 +--resolve +baz.com:8000:127.0.0.1 diff --git a/integration/tests_ok/resolve.out b/integration/tests_ok/resolve.out new file mode 100644 index 000000000..c57eff55e --- /dev/null +++ b/integration/tests_ok/resolve.out @@ -0,0 +1 @@ +Hello World! \ No newline at end of file diff --git a/packages/hurl/src/cli/options.rs b/packages/hurl/src/cli/options.rs index 0a570f2b4..52225282e 100644 --- a/packages/hurl/src/cli/options.rs +++ b/packages/hurl/src/cli/options.rs @@ -56,6 +56,7 @@ pub struct CliOptions { pub output: Option, pub output_type: OutputType, pub proxy: Option, + pub resolves: Vec, pub retry: bool, pub retry_interval: Duration, pub retry_max_count: Option, @@ -300,6 +301,15 @@ pub fn app(version: &str) -> Command { .help("Retry requests on errors") .action(ArgAction::SetTrue) ) + .arg( + clap::Arg::new("resolve") + .long("resolve") + .value_name("HOST:PORT:ADDR") + .help("Provide a custom address for a specific host and port pair") + .action(ArgAction::Append) + .number_of_values(1) + .num_args(1) + ) .arg( clap::Arg::new("retry_interval") .long("retry-interval") @@ -470,6 +480,7 @@ pub fn parse_options(matches: &ArgMatches) -> Result { OutputType::ResponseBody }; let proxy = get::(matches, "proxy"); + let resolves = get_strings(matches, "resolve").unwrap_or_default(); let retry = has_flag(matches, "retry"); let retry_interval = get::(matches, "retry_interval").unwrap(); let retry_interval = Duration::from_millis(retry_interval); @@ -511,6 +522,7 @@ pub fn parse_options(matches: &ArgMatches) -> Result { output, output_type, proxy, + resolves, retry, retry_interval, retry_max_count, @@ -626,11 +638,12 @@ fn match_glob_files(matches: &ArgMatches) -> Result, CliError> { Ok(filenames) } -/// Returns a optional value of type `T` from the command line `matches` given the option `name`. +/// Returns an optional value of type `T` from the command line `matches` given the option `name`. fn get(matches: &ArgMatches, name: &str) -> Option { matches.get_one::(name).cloned() } +/// Returns an optional list of `String` from the command line `matches` given the option `name`. pub fn get_strings(matches: &ArgMatches, name: &str) -> Option> { matches .get_many::(name) diff --git a/packages/hurl/src/http/client.rs b/packages/hurl/src/http/client.rs index aca07dcbb..19709993d 100644 --- a/packages/hurl/src/http/client.rs +++ b/packages/hurl/src/http/client.rs @@ -31,6 +31,7 @@ use super::response::*; use super::{Header, HttpError, Verbosity}; use crate::cli::Logger; use crate::http::ContextDir; +use curl::easy::List; use std::str::FromStr; use url::Url; @@ -62,14 +63,8 @@ impl Client { } } - /// Executes an HTTP request, optionally follows redirection and returns a + /// Executes an HTTP request `request_spec`, optionally follows redirection and returns a /// list of pair of [`Request`], [`Response`]. - /// - /// # Arguments - /// - /// * `request_spec` - A request specification - /// * `options`- Options for this execution - /// * `logger`- A logger pub fn execute_with_redirect( &mut self, request_spec: &RequestSpec, @@ -117,14 +112,8 @@ impl Client { Ok(calls) } - /// Executes an HTTP request, without following redirection and returns a + /// Executes an HTTP request `request_spec`, without following redirection and returns a /// pair of [`Request`], [`Response`]. - /// - /// # Arguments - /// - /// * `request_spec` - A request specification - /// * `options`- Options for this execution - /// * `logger`- A logger pub fn execute( &mut self, request_spec: &RequestSpec, @@ -137,6 +126,12 @@ impl Client { // to capture HTTP request headers in libcurl `debug_function`. That's the only // way to get access to the outgoing headers. self.handle.verbose(true).unwrap(); + + if !options.resolves.is_empty() { + let resolves = to_list(&options.resolves); + self.handle.resolve(resolves).unwrap(); + } + self.handle.ssl_verify_host(!options.insecure).unwrap(); self.handle.ssl_verify_peer(!options.insecure).unwrap(); if let Some(cacert_file) = options.cacert_file.clone() { @@ -710,6 +705,12 @@ pub fn decode_header(data: &[u8]) -> Option { } } +fn to_list(items: &[String]) -> List { + let mut list = List::new(); + items.iter().for_each(|l| list.append(l).unwrap()); + list +} + #[cfg(test)] mod tests { use super::*; diff --git a/packages/hurl/src/http/options.rs b/packages/hurl/src/http/options.rs index fb686bece..c4c6487d0 100644 --- a/packages/hurl/src/http/options.rs +++ b/packages/hurl/src/http/options.rs @@ -29,6 +29,7 @@ pub struct ClientOptions { pub no_proxy: Option, pub verbosity: Option, pub insecure: bool, + pub resolves: Vec, pub retry_max_count: Option, pub timeout: Duration, pub connect_timeout: Duration, @@ -56,6 +57,7 @@ impl Default for ClientOptions { no_proxy: None, verbosity: None, insecure: false, + resolves: vec![], retry_max_count: Some(10), timeout: Duration::from_secs(300), connect_timeout: Duration::from_secs(300), @@ -117,6 +119,10 @@ impl ClientOptions { arguments.push("--proxy".to_string()); arguments.push(format!("'{}'", proxy)); } + for resolve in self.resolves.iter() { + arguments.push("--resolve".to_string()); + arguments.push(resolve.clone()); + } if self.timeout != ClientOptions::default().timeout { arguments.push("--timeout".to_string()); arguments.push(self.timeout.as_secs().to_string()); @@ -153,6 +159,10 @@ mod tests { no_proxy: None, verbosity: None, insecure: true, + resolves: vec![ + "foo.com:80:192.168.0.1".to_string(), + "bar.com:443:127.0.0.1".to_string() + ], retry_max_count: Some(10), timeout: Duration::from_secs(10), connect_timeout: Duration::from_secs(20), @@ -173,6 +183,10 @@ mod tests { "10".to_string(), "--proxy".to_string(), "'localhost:3128'".to_string(), + "--resolve".to_string(), + "foo.com:80:192.168.0.1".to_string(), + "--resolve".to_string(), + "bar.com:443:127.0.0.1".to_string(), "--timeout".to_string(), "10".to_string(), "--user".to_string(), diff --git a/packages/hurl/src/runner/entry.rs b/packages/hurl/src/runner/entry.rs index 90758c456..f00390127 100644 --- a/packages/hurl/src/runner/entry.rs +++ b/packages/hurl/src/runner/entry.rs @@ -207,6 +207,7 @@ impl From<&RunnerOptions> for ClientOptions { Verbosity::VeryVerbose => http::Verbosity::VeryVerbose, }), insecure: runner_options.insecure, + resolves: runner_options.resolves.clone(), retry_max_count: runner_options.retry_max_count, timeout: runner_options.timeout, connect_timeout: runner_options.connect_timeout, diff --git a/packages/hurl/src/runner/runner_options.rs b/packages/hurl/src/runner/runner_options.rs index d74a08046..45b657ee5 100644 --- a/packages/hurl/src/runner/runner_options.rs +++ b/packages/hurl/src/runner/runner_options.rs @@ -41,6 +41,7 @@ pub struct RunnerOptions { pub post_entry: Option bool>, pub pre_entry: Option bool>, pub proxy: Option, + pub resolves: Vec, pub retry: bool, pub retry_interval: Duration, pub retry_max_count: Option, @@ -71,6 +72,7 @@ impl Default for RunnerOptions { post_entry: None, pre_entry: None, proxy: None, + resolves: vec![], retry: false, retry_interval: Duration::from_millis(1000), retry_max_count: Some(10), @@ -129,6 +131,7 @@ impl RunnerOptions { }; let fail_fast = cli_options.fail_fast; let to_entry = cli_options.to_entry; + let resolves = cli_options.resolves.clone(); let retry = cli_options.retry; let retry_interval = cli_options.retry_interval; let retry_max_count = cli_options.retry_max_count; @@ -151,6 +154,7 @@ impl RunnerOptions { post_entry, pre_entry, proxy, + resolves, retry, retry_interval, retry_max_count,