diff --git a/packages/hurl/src/cli/options.rs b/packages/hurl/src/cli/options.rs index f2ff628526e..e170c3d7730 100644 --- a/packages/hurl/src/cli/options.rs +++ b/packages/hurl/src/cli/options.rs @@ -61,6 +61,7 @@ pub struct CliOptions { pub user_agent: Option, pub variables: HashMap, pub verbose: bool, + pub very_verbose: bool, } #[derive(Clone, Debug, PartialEq, Eq)] @@ -297,10 +298,15 @@ pub fn app(version: &str) -> Command { .long("verbose") .help("Turn on verbose output"), ) + .arg( + clap::Arg::new("very_verbose") + .long("very-verbose") + .help("Turn on verbose output, uncluding HTTP response"), + ) } pub fn parse_options(matches: &ArgMatches) -> Result { - let cacert_file = match get_string(&matches, "cacert_file") { + let cacert_file = match get_string(matches, "cacert_file") { None => None, Some(filename) => { if !Path::new(&filename).is_file() { @@ -311,9 +317,9 @@ pub fn parse_options(matches: &ArgMatches) -> Result { } } }; - let color = output_color(matches.clone()); - let compressed = has_flag(&matches, "compressed"); - let connect_timeout = match get_string(&matches, "connect_timeout") { + let color = output_color(matches); + let compressed = has_flag(matches, "compressed"); + let connect_timeout = match get_string(matches, "connect_timeout") { None => ClientOptions::default().connect_timeout, Some(s) => match s.parse::() { Ok(n) => Duration::from_secs(n), @@ -324,14 +330,13 @@ pub fn parse_options(matches: &ArgMatches) -> Result { } }, }; - let cookie_input_file = get_string(&matches, "cookies_input_file"); - let cookie_output_file = get_string(&matches, "cookies_output_file"); - - let fail_fast = !has_flag(&matches, "fail_at_end"); - let file_root = get_string(&matches, "file_root"); - let follow_location = has_flag(&matches, "follow_location"); - let glob_files = match_glob_files(&matches)?; - let report_html = get_string(&matches, "report_html"); + let cookie_input_file = get_string(matches, "cookies_input_file"); + let cookie_output_file = get_string(matches, "cookies_output_file"); + let fail_fast = !has_flag(matches, "fail_at_end"); + let file_root = get_string(matches, "file_root"); + let follow_location = has_flag(matches, "follow_location"); + let glob_files = match_glob_files(matches)?; + let report_html = get_string(matches, "report_html"); let html_dir = if let Some(dir) = report_html { let path = Path::new(&dir); if !path.exists() { @@ -353,12 +358,12 @@ pub fn parse_options(matches: &ArgMatches) -> Result { } else { None }; - let ignore_asserts = has_flag(&matches, "ignore_asserts"); - let include = has_flag(&matches, "include"); - let insecure = has_flag(&matches, "insecure"); - let interactive = has_flag(&matches, "interactive"); - let junit_file = get_string(&matches, "junit"); - let max_redirect = match get_string(&matches, "max_redirects").as_deref() { + let ignore_asserts = has_flag(matches, "ignore_asserts"); + let include = has_flag(matches, "include"); + let insecure = has_flag(matches, "insecure"); + let interactive = has_flag(matches, "interactive"); + let junit_file = get_string(matches, "junit"); + let max_redirect = match get_string(matches, "max_redirects").as_deref() { None => Some(50), Some("-1") => None, Some(s) => match s.parse::() { @@ -370,20 +375,20 @@ pub fn parse_options(matches: &ArgMatches) -> Result { } }, }; - let no_proxy = get_string(&matches, "proxy"); - let output = get_string(&matches, "output"); - let test = has_flag(&matches, "test"); - let output_type = if has_flag(&matches, "json") { + let no_proxy = get_string(matches, "proxy"); + let output = get_string(matches, "output"); + let test = has_flag(matches, "test"); + let output_type = if has_flag(matches, "json") { OutputType::Json - } else if has_flag(&matches, "no_output") || test { + } else if has_flag(matches, "no_output") || test { OutputType::NoOutput } else { OutputType::ResponseBody }; - let progress = has_flag(&matches, "progress") || test; - let proxy = get_string(&matches, "proxy"); - let summary = has_flag(&matches, "summary") || test; - let timeout = match get_string(&matches, "max_time") { + let progress = has_flag(matches, "progress") || test; + let proxy = get_string(matches, "proxy"); + let summary = has_flag(matches, "summary") || test; + let timeout = match get_string(matches, "max_time") { None => ClientOptions::default().timeout, Some(s) => match s.parse::() { Ok(n) => Duration::from_secs(n), @@ -394,11 +399,12 @@ pub fn parse_options(matches: &ArgMatches) -> Result { } }, }; - let to_entry = to_entry(matches.clone())?; - let user = get_string(&matches, "user"); - let user_agent = get_string(&matches, "user_agent"); + let to_entry = to_entry(matches)?; + let user = get_string(matches, "user"); + let user_agent = get_string(matches, "user_agent"); let variables = variables(matches.clone())?; - let verbose = has_flag(&matches, "verbose") || has_flag(&matches, "interactive"); + let very_verbose = has_flag(matches, "very_verbose"); + let verbose = has_flag(matches, "verbose") || has_flag(matches, "interactive") || very_verbose; Ok(CliOptions { cacert_file, @@ -430,21 +436,22 @@ pub fn parse_options(matches: &ArgMatches) -> Result { user_agent, variables, verbose, + very_verbose, }) } -pub fn output_color(matches: ArgMatches) -> bool { - if has_flag(&matches, "color") { +pub fn output_color(matches: &ArgMatches) -> bool { + if has_flag(matches, "color") { true - } else if has_flag(&matches, "no_color") { + } else if has_flag(matches, "no_color") { false } else { atty::is(Stream::Stdout) } } -fn to_entry(matches: ArgMatches) -> Result, CliError> { - match get_string(&matches, "to_entry") { +fn to_entry(matches: &ArgMatches) -> Result, CliError> { + match get_string(matches, "to_entry") { Some(value) => match value.parse() { Ok(v) => Ok(Some(v)), Err(_) => Err(CliError { diff --git a/packages/hurl/src/http/client.rs b/packages/hurl/src/http/client.rs index 9945e85d815..938e3a86e33 100644 --- a/packages/hurl/src/http/client.rs +++ b/packages/hurl/src/http/client.rs @@ -28,7 +28,7 @@ use super::options::ClientOptions; use super::request::*; use super::request_spec::*; use super::response::*; -use crate::http::HttpError; +use crate::http::{HttpError, Verbosity}; use std::str::FromStr; use url::Url; @@ -70,8 +70,11 @@ impl Client { } } + /// Execute an HTTP request and returns a list /// - /// Execute an http request + /// # Arguments + /// + /// * request - A request specification /// pub fn execute_with_redirect( &mut self, @@ -153,7 +156,7 @@ impl Client { self.set_headers(request); - let verbose = self.options.verbose; + let verbose = self.options.verbosity != None; let mut request_headers: Vec
= vec![]; let start = Instant::now(); @@ -493,7 +496,7 @@ impl Client { /// Add cookie to Cookiejar /// pub fn add_cookie(&mut self, cookie: Cookie) { - if self.options.verbose { + if self.options.verbosity != None { eprintln!("* add to cookie store: {}", cookie); } self.handle @@ -505,7 +508,7 @@ impl Client { /// Clear cookie storage /// pub fn clear_cookie_storage(&mut self) { - if self.options.verbose { + if self.options.verbosity != None { eprintln!("* clear cookie storage"); } self.handle.cookie_list("ALL").unwrap(); diff --git a/packages/hurl/src/http/mod.rs b/packages/hurl/src/http/mod.rs index 871d3c76f0d..061f244ce65 100644 --- a/packages/hurl/src/http/mod.rs +++ b/packages/hurl/src/http/mod.rs @@ -20,7 +20,7 @@ pub use self::client::Client; pub use self::cookie::{CookieAttribute, ResponseCookie}; pub use self::core::{Cookie, Header, Param, RequestCookie}; pub use self::error::HttpError; -pub use self::options::ClientOptions; +pub use self::options::{ClientOptions, Verbosity}; pub use self::request::Request; #[cfg(test)] pub use self::request_spec::tests::*; diff --git a/packages/hurl/src/http/options.rs b/packages/hurl/src/http/options.rs index b64a04fbfd5..2d75e1bc779 100644 --- a/packages/hurl/src/http/options.rs +++ b/packages/hurl/src/http/options.rs @@ -26,7 +26,7 @@ pub struct ClientOptions { pub cookie_input_file: Option, pub proxy: Option, pub no_proxy: Option, - pub verbose: bool, + pub verbosity: Option, pub insecure: bool, pub timeout: Duration, pub connect_timeout: Duration, @@ -36,6 +36,12 @@ pub struct ClientOptions { pub context_dir: PathBuf, } +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum Verbosity { + Verbose, + VeryVerbose, +} + impl Default for ClientOptions { fn default() -> Self { ClientOptions { @@ -45,7 +51,7 @@ impl Default for ClientOptions { cookie_input_file: None, proxy: None, no_proxy: None, - verbose: false, + verbosity: None, insecure: false, timeout: Duration::from_secs(300), connect_timeout: Duration::from_secs(300), @@ -130,7 +136,7 @@ mod tests { cookie_input_file: Some("cookie_file".to_string()), proxy: Some("localhost:3128".to_string()), no_proxy: None, - verbose: true, + verbosity: None, insecure: true, timeout: Duration::from_secs(10), connect_timeout: Duration::from_secs(20), diff --git a/packages/hurl/src/main.rs b/packages/hurl/src/main.rs index 3fa9752c74d..e2b58f39aec 100644 --- a/packages/hurl/src/main.rs +++ b/packages/hurl/src/main.rs @@ -27,6 +27,7 @@ use colored::*; use hurl::cli; use hurl::cli::{CliError, CliOptions, OutputType}; use hurl::http; +use hurl::http::Verbosity; use hurl::report; use hurl::report::canonicalize_filename; use hurl::runner; @@ -147,7 +148,11 @@ fn execute( let cacert_file = cli_options.cacert_file.clone(); let follow_location = cli_options.follow_location; - let verbose = cli_options.verbose; + let verbosity = match (cli_options.verbose, cli_options.very_verbose) { + (true, true) => Some(Verbosity::VeryVerbose), + (true, _) => Some(Verbosity::Verbose), + _ => None, + }; let insecure = cli_options.insecure; let max_redirect = cli_options.max_redirect; let proxy = cli_options.proxy.clone(); @@ -177,7 +182,7 @@ fn execute( cookie_input_file, proxy, no_proxy, - verbose, + verbosity, insecure, timeout, connect_timeout, @@ -204,12 +209,14 @@ fn execute( let to_entry = cli_options.to_entry; let context_dir = context_dir.to_path_buf(); let ignore_asserts = cli_options.ignore_asserts; + let very_verbose = cli_options.very_verbose; let options = RunnerOptions { fail_fast, variables, to_entry, context_dir, ignore_asserts, + very_verbose, pre_entry, post_entry, }; @@ -262,9 +269,11 @@ fn main() { let matches = app.clone().get_matches(); init_colored(); - let verbose = cli::has_flag(&matches, "verbose") || cli::has_flag(&matches, "interactive"); + let verbose = cli::has_flag(&matches, "verbose") + || cli::has_flag(&matches, "very_verbose") + || cli::has_flag(&matches, "interactive"); let log_verbose = cli::make_logger_verbose(verbose); - let color = cli::output_color(matches.clone()); + let color = cli::output_color(&matches); let log_error_message = cli::make_logger_error_message(color); let cli_options = unwrap_or_exit(cli::parse_options(&matches), &log_error_message); diff --git a/packages/hurl/src/runner/core.rs b/packages/hurl/src/runner/core.rs index 84343c3028e..da27dba2cd1 100644 --- a/packages/hurl/src/runner/core.rs +++ b/packages/hurl/src/runner/core.rs @@ -30,6 +30,7 @@ pub struct RunnerOptions { pub to_entry: Option, pub context_dir: PathBuf, pub ignore_asserts: bool, + pub very_verbose: bool, // If true, log body response in verbose mode. pub pre_entry: fn(Entry) -> bool, pub post_entry: fn() -> bool, } diff --git a/packages/hurl/src/runner/entry.rs b/packages/hurl/src/runner/entry.rs index b79f0f882d1..802a4836c01 100644 --- a/packages/hurl/src/runner/entry.rs +++ b/packages/hurl/src/runner/entry.rs @@ -87,7 +87,7 @@ pub fn run( } else { log_error_message( true, - format!("cookie string can not be parsed: '{}'", s).as_str(), + format!("Cookie string can not be parsed: '{}'", s).as_str(), ); } } @@ -101,7 +101,7 @@ pub fn run( log_verbose(cookie.to_string().as_str()); } log_verbose(""); - log_request(log_verbose, &http_request); + log_request_spec(log_verbose, &http_request); log_verbose( format!( "Request can be run with the following curl command:\n* {}\n*", @@ -157,7 +157,7 @@ pub fn run( } }, }; - // update variables now! + // Update variables now! for capture_result in captures.clone() { variables.insert(capture_result.name, capture_result.value); } @@ -189,7 +189,7 @@ pub fn run( .collect(); if !captures.is_empty() { - log_verbose("Captures"); + log_verbose("Captures:"); for capture in captures.clone() { log_verbose(format!("{}: {}", capture.name, capture.value).as_str()); } @@ -211,37 +211,44 @@ pub fn run( entry_results } -pub fn log_request(log_verbose: impl Fn(&str), request: &http::RequestSpec) { +/// Log a HTTP request spec +/// +/// # Arguments +/// +/// * log_verbose - a log function +/// * request - an HTTP request spec +/// +fn log_request_spec(log_verbose: impl Fn(&str), request: &http::RequestSpec) { log_verbose("Request:"); log_verbose(format!("{} {}", request.method, request.url).as_str()); - for header in request.headers.clone() { + for header in &request.headers { log_verbose(header.to_string().as_str()); } if !request.querystring.is_empty() { log_verbose("[QueryStringParams]"); - for param in request.querystring.clone() { + for param in &request.querystring { log_verbose(param.to_string().as_str()); } } if !request.form.is_empty() { log_verbose("[FormParams]"); - for param in request.form.clone() { + for param in &request.form { log_verbose(param.to_string().as_str()); } } if !request.multipart.is_empty() { log_verbose("[MultipartFormData]"); - for param in request.multipart.clone() { + for param in &request.multipart { log_verbose(param.to_string().as_str()); } } if !request.cookies.is_empty() { log_verbose("[Cookies]"); - for cookie in request.cookies.clone() { + for cookie in &request.cookies { log_verbose(cookie.to_string().as_str()); } } - if let Some(s) = request.content_type.clone() { + if let Some(s) = &request.content_type { log_verbose(""); log_verbose(format!("Implicit content-type={}", s).as_str()); } diff --git a/packages/hurl/src/runner/hurl_file.rs b/packages/hurl/src/runner/hurl_file.rs index 3b966c594f7..fd30c015a37 100644 --- a/packages/hurl/src/runner/hurl_file.rs +++ b/packages/hurl/src/runner/hurl_file.rs @@ -35,7 +35,7 @@ use super::entry; /// use hurl::runner; /// /// // Parse Hurl file -/// let filename = "sample.hurl".to_string(); +/// let filename = "sample.hurl"; /// let s = r#" /// GET http://localhost:8000/hello /// HTTP/1.0 200 @@ -59,6 +59,7 @@ use super::entry; /// to_entry: None, /// context_dir: PathBuf::new(), /// ignore_asserts: false, +/// very_verbose: false, /// pre_entry: |_| true, /// post_entry: || true, /// }; diff --git a/packages/hurl/tests/runner.rs b/packages/hurl/tests/runner.rs index d80c6fa4645..b6cdf30b721 100644 --- a/packages/hurl/tests/runner.rs +++ b/packages/hurl/tests/runner.rs @@ -56,6 +56,7 @@ fn test_hurl_file() { to_entry: None, context_dir: PathBuf::new(), ignore_asserts: false, + very_verbose: false, pre_entry: |_| true, post_entry: || true, }; @@ -68,7 +69,7 @@ fn test_hurl_file() { hurl_file, &mut client, //&mut variables, - filename.to_string(), + filename, &options, &log_verbose, &log_error_message, @@ -186,6 +187,7 @@ fn test_hello() { to_entry: None, context_dir: PathBuf::new(), ignore_asserts: false, + very_verbose: false, pre_entry: |_| true, post_entry: || true, }; @@ -195,7 +197,7 @@ fn test_hello() { let _hurl_log = runner::run_hurl_file( hurl_file, &mut client, - String::from("filename"), + "filename", &options, &log_verbose, &log_error_message,