From ad75d936f6e45adc5e5cc05aac3dd602d1b0ac91 Mon Sep 17 00:00:00 2001 From: ducaale Date: Sat, 12 Mar 2022 21:13:04 +0000 Subject: [PATCH 01/11] limit long_help chars to 100 per line --- src/cli.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/cli.rs b/src/cli.rs index 90ce1f46..ec23b96e 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -370,6 +370,7 @@ impl Cli { {after-help}\ ", ) + .max_term_width(100) .print_long_help() .unwrap(); } else { @@ -396,7 +397,7 @@ impl Cli { match cli.raw_method_or_url.as_str() { "help" => { - app.print_long_help().unwrap(); + app.max_term_width(100).print_long_help().unwrap(); println!(); safe_exit(); } From 696bd8e0cfa932c2f0888bb904d28da0cdb90591 Mon Sep 17 00:00:00 2001 From: ducaale Date: Sat, 12 Mar 2022 21:16:55 +0000 Subject: [PATCH 02/11] add --meta flag and parse -v from occurance --- src/cli.rs | 62 +++++++++++++++++++++++++++++++++++--------------- src/main.rs | 1 + src/to_curl.rs | 2 +- 3 files changed, 46 insertions(+), 19 deletions(-) diff --git a/src/cli.rs b/src/cli.rs index ec23b96e..b60dd5cf 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -53,7 +53,7 @@ pub struct Cli { pub form: bool, /// Like --form, but force a multipart/form-data request even without files. - #[clap(short = 'm', long, overrides_with_all = &["json", "form"])] + #[clap(long, overrides_with_all = &["json", "form"])] pub multipart: bool, /// Pass raw request data without extra processing. @@ -80,13 +80,16 @@ pub struct Cli { #[clap(long, value_name = "MIME_TYPE")] pub response_mime: Option, - /// String specifying what the output should contain. + /// String specifying what the output should contain /// - /// Use `H` and `B` for request header and body respectively, - /// and `h` and `b` for response hader and body. + /// 'H' request headers + /// 'B' request body + /// 'h' response headers + /// 'b' response body + /// 'm' response metadata /// /// Example: `--print=Hb` - #[clap(short = 'p', long, value_name = "FORMAT")] + #[clap(short = 'p', long, value_name = "FORMAT", verbatim_doc_comment)] pub print: Option, /// Print only the response headers. Shortcut for --print=h. @@ -97,9 +100,13 @@ pub struct Cli { #[clap(short = 'b', long)] pub body: bool, + /// Print only the response metadata. Shortcut for --print=m. + #[clap(short = 'm', long)] + pub meta: bool, + /// Print the whole request as well as the response. - #[clap(short = 'v', long)] - pub verbose: bool, + #[clap(short = 'v', long, parse(from_occurrences))] + pub verbose: usize, /// Show any intermediary requests/responses while following redirects with --follow. #[clap(long)] @@ -293,14 +300,14 @@ pub struct Cli { /// Optional key-value pairs to be included in the request /// - /// - key==value to add a parameter to the URL - /// - key=value to add a JSON field (--json) or form field (--form) - /// - key:=value to add a complex JSON value (e.g. `numbers:=[1,2,3]`) - /// - key@filename to upload a file from filename (with --form) - /// - @filename to use a file as the request body - /// - header:value to add a header - /// - header: to unset a header - /// - header; to add a header with an empty value + /// key==value add a parameter to the URL + /// key=value add a JSON field (--json) or form field (--form) + /// key:=value add a complex JSON value (e.g. `numbers:=[1,2,3]`) + /// key@filename upload a file from filename (with --form) + /// @filename use a file as the request body + /// header:value add a header + /// header: unset a header + /// header; add a header with an empty value /// /// A backslash can be used to escape special characters (e.g. weird\:key=value). #[clap(value_name = "REQUEST_ITEM", verbatim_doc_comment)] @@ -460,7 +467,7 @@ impl Cli { /// Set flags that are implied by other flags and report conflicting flags. fn process_relations(&mut self, matches: &clap::ArgMatches) -> clap::Result<()> { - if self.verbose { + if self.verbose > 0 { self.all = true; } if self.curl_long { @@ -740,23 +747,26 @@ pub struct Print { pub request_body: bool, pub response_headers: bool, pub response_body: bool, + pub response_meta: bool, } impl Print { pub fn new( - verbose: bool, + verbose: usize, headers: bool, body: bool, + meta: bool, quiet: bool, offline: bool, buffer: &Buffer, ) -> Self { - if verbose { + if verbose > 0 { Print { request_headers: true, request_body: true, response_headers: true, response_body: true, + response_meta: verbose > 1, } } else if quiet { Print { @@ -764,6 +774,7 @@ impl Print { request_body: false, response_headers: false, response_body: false, + response_meta: false, } } else if offline { Print { @@ -771,6 +782,7 @@ impl Print { request_body: true, response_headers: false, response_body: false, + response_meta: false, } } else if headers { Print { @@ -778,6 +790,7 @@ impl Print { request_body: false, response_headers: true, response_body: false, + response_meta: false, } } else if body || !buffer.is_terminal() { Print { @@ -785,6 +798,15 @@ impl Print { request_body: false, response_headers: false, response_body: true, + response_meta: false, + } + } else if meta { + Print { + request_headers: false, + request_body: false, + response_headers: false, + response_body: false, + response_meta: true, } } else { Print { @@ -792,6 +814,7 @@ impl Print { request_body: false, response_headers: true, response_body: true, + response_meta: false, } } } @@ -804,6 +827,7 @@ impl FromStr for Print { let mut request_body = false; let mut response_headers = false; let mut response_body = false; + let mut response_meta = false; for char in s.chars() { match char { @@ -811,6 +835,7 @@ impl FromStr for Print { 'B' => request_body = true, 'h' => response_headers = true, 'b' => response_body = true, + 'm' => response_meta = true, char => return Err(anyhow!("{:?} is not a valid value", char)), } } @@ -820,6 +845,7 @@ impl FromStr for Print { request_body, response_headers, response_body, + response_meta, }; Ok(p) } diff --git a/src/main.rs b/src/main.rs index 3db4ed61..96e70ebf 100644 --- a/src/main.rs +++ b/src/main.rs @@ -447,6 +447,7 @@ fn run(args: Cli) -> Result { args.verbose, args.headers, args.body, + args.meta, args.quiet, args.offline, &buffer, diff --git a/src/to_curl.rs b/src/to_curl.rs index 8fb329de..ed382275 100644 --- a/src/to_curl.rs +++ b/src/to_curl.rs @@ -124,7 +124,7 @@ pub fn translate(args: Cli) -> Result { // - .curl and .curl_long: you are here // Output options - if args.verbose { + if args.verbose > 0 { // Far from an exact match, but it does print the request headers cmd.opt("-v", "--verbose"); } From 06f6247c519327a01588004ba0fd67866d4a3113 Mon Sep 17 00:00:00 2001 From: ducaale Date: Sat, 12 Mar 2022 21:18:49 +0000 Subject: [PATCH 03/11] support response_meta in middleware --- src/auth.rs | 14 +++++++++----- src/main.rs | 4 ++-- src/middleware.rs | 46 +++++++++++++++++++++++++++++++++++----------- src/redirect.rs | 20 ++++++++++++++------ 4 files changed, 60 insertions(+), 24 deletions(-) diff --git a/src/auth.rs b/src/auth.rs index 1b7684fe..4884e80c 100644 --- a/src/auth.rs +++ b/src/auth.rs @@ -6,7 +6,7 @@ use reqwest::header::{HeaderValue, AUTHORIZATION, WWW_AUTHENTICATE}; use reqwest::StatusCode; use crate::cli::AuthType; -use crate::middleware::{Context, Middleware}; +use crate::middleware::{Context, Middleware, ResponseMeta}; use crate::netrc; use crate::regex; use crate::utils::clone_request; @@ -82,8 +82,12 @@ impl<'a> DigestAuthMiddleware<'a> { } impl<'a> Middleware for DigestAuthMiddleware<'a> { - fn handle(&mut self, mut ctx: Context, mut request: Request) -> Result { - let response = self.next(&mut ctx, clone_request(&mut request)?)?; + fn handle( + &mut self, + mut ctx: Context, + mut request: Request, + ) -> Result<(Response, ResponseMeta)> { + let (response, response_meta) = self.next(&mut ctx, clone_request(&mut request)?)?; match response.headers().get(WWW_AUTHENTICATE) { Some(wwwauth) if response.status() == StatusCode::UNAUTHORIZED => { let mut context = digest_auth::AuthContext::new( @@ -99,10 +103,10 @@ impl<'a> Middleware for DigestAuthMiddleware<'a> { request .headers_mut() .insert(AUTHORIZATION, HeaderValue::from_str(&answer)?); - self.print(&mut ctx, response, &mut request)?; + self.print(&mut ctx, response, response_meta, &mut request)?; Ok(self.next(&mut ctx, request)?) } - _ => Ok(response), + _ => Ok((response, response_meta)), } } } diff --git a/src/main.rs b/src/main.rs index 96e70ebf..f0b1f57b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -467,11 +467,11 @@ fn run(args: Cli) -> Result { } if !args.offline { - let response = { + let (response, response_meta) = { let history_print = args.history_print.unwrap_or(print); let mut client = ClientWithMiddleware::new(&client); if args.all { - client = client.with_printer(|prev_response, next_request| { + client = client.with_printer(|prev_response, prev_response_meta, next_request| { if history_print.response_headers { printer.print_response_headers(&prev_response)?; } diff --git a/src/middleware.rs b/src/middleware.rs index 5d8b5d76..ef30a7a4 100644 --- a/src/middleware.rs +++ b/src/middleware.rs @@ -1,16 +1,30 @@ +use std::time::Instant; + use anyhow::Result; use reqwest::blocking::{Client, Request, Response}; +pub struct ResponseMeta { + pub starting_time: Instant, +} + +impl ResponseMeta { + fn new(starting_time: Instant) -> Self { + ResponseMeta { starting_time } + } +} + pub struct Context<'a, 'b> { client: &'a Client, - printer: Option<&'a mut (dyn FnMut(Response, &mut Request) -> Result<()> + 'b)>, + printer: Option<&'a mut (dyn FnMut(Response, ResponseMeta, &mut Request) -> Result<()> + 'b)>, middlewares: &'a mut [Box], } impl<'a, 'b> Context<'a, 'b> { fn new( client: &'a Client, - printer: Option<&'a mut (dyn FnMut(Response, &mut Request) -> Result<()> + 'b)>, + printer: Option< + &'a mut (dyn FnMut(Response, ResponseMeta, &mut Request) -> Result<()> + 'b), + >, middlewares: &'a mut [Box], ) -> Self { Context { @@ -20,9 +34,13 @@ impl<'a, 'b> Context<'a, 'b> { } } - fn execute(&mut self, request: Request) -> Result { + fn execute(&mut self, request: Request) -> Result<(Response, ResponseMeta)> { match self.middlewares { - [] => Ok(self.client.execute(request)?), + [] => { + let starting_time = Instant::now(); + let response = self.client.execute(request)?; + Ok((response, ResponseMeta::new(starting_time))) + } [ref mut head, tail @ ..] => head.handle( #[allow(clippy::needless_option_as_deref)] Context::new(self.client, self.printer.as_deref_mut(), tail), @@ -33,15 +51,21 @@ impl<'a, 'b> Context<'a, 'b> { } pub trait Middleware { - fn handle(&mut self, ctx: Context, request: Request) -> Result; + fn handle(&mut self, ctx: Context, request: Request) -> Result<(Response, ResponseMeta)>; - fn next(&self, ctx: &mut Context, request: Request) -> Result { + fn next(&self, ctx: &mut Context, request: Request) -> Result<(Response, ResponseMeta)> { ctx.execute(request) } - fn print(&self, ctx: &mut Context, response: Response, request: &mut Request) -> Result<()> { + fn print( + &self, + ctx: &mut Context, + response: Response, + response_meta: ResponseMeta, + request: &mut Request, + ) -> Result<()> { if let Some(ref mut printer) = ctx.printer { - printer(response, request)?; + printer(response, response_meta, request)?; } Ok(()) @@ -50,7 +74,7 @@ pub trait Middleware { pub struct ClientWithMiddleware<'a, T> where - T: FnMut(Response, &mut Request) -> Result<()>, + T: FnMut(Response, ResponseMeta, &mut Request) -> Result<()>, { client: &'a Client, printer: Option, @@ -59,7 +83,7 @@ where impl<'a, T> ClientWithMiddleware<'a, T> where - T: FnMut(Response, &mut Request) -> Result<()> + 'a, + T: FnMut(Response, ResponseMeta, &mut Request) -> Result<()> + 'a, { pub fn new(client: &'a Client) -> Self { ClientWithMiddleware { @@ -79,7 +103,7 @@ where self } - pub fn execute(&mut self, request: Request) -> Result { + pub fn execute(&mut self, request: Request) -> Result<(Response, ResponseMeta)> { let mut ctx = Context::new( self.client, self.printer.as_mut().map(|p| p as _), diff --git a/src/redirect.rs b/src/redirect.rs index 70c8dc98..0e06e11e 100644 --- a/src/redirect.rs +++ b/src/redirect.rs @@ -6,7 +6,7 @@ use reqwest::header::{ }; use reqwest::{Method, StatusCode, Url}; -use crate::middleware::{Context, Middleware}; +use crate::middleware::{Context, Middleware, ResponseMeta}; use crate::utils::clone_request; pub struct RedirectFollower { @@ -20,11 +20,15 @@ impl RedirectFollower { } impl Middleware for RedirectFollower { - fn handle(&mut self, mut ctx: Context, mut first_request: Request) -> Result { + fn handle( + &mut self, + mut ctx: Context, + mut first_request: Request, + ) -> Result<(Response, ResponseMeta)> { // This buffers the body in case we need it again later // reqwest does *not* do this, it ignores 307/308 with a streaming body let mut request = clone_request(&mut first_request)?; - let mut response = self.next(&mut ctx, first_request)?; + let (mut response, mut response_meta) = self.next(&mut ctx, first_request)?; let mut remaining_redirects = self.max_redirects - 1; while let Some(mut next_request) = get_next_request(request, &response) { @@ -36,12 +40,16 @@ impl Middleware for RedirectFollower { self.max_redirects )); } - self.print(&mut ctx, response, &mut next_request)?; + self.print(&mut ctx, response, response_meta, &mut next_request)?; request = clone_request(&mut next_request)?; - response = self.next(&mut ctx, next_request)?; + + // TODO: use destructuring assignment once MSRV >= 1.59 + let temp = self.next(&mut ctx, next_request)?; + response = temp.0; + response_meta = temp.1; } - Ok(response) + Ok((response, response_meta)) } } From 1ccad9c3728c5215ef3db7133b31fde701587458 Mon Sep 17 00:00:00 2001 From: ducaale Date: Sat, 12 Mar 2022 21:19:34 +0000 Subject: [PATCH 04/11] support outputting elapsed_time --- src/main.rs | 15 +++++++++++++-- src/printer.rs | 9 +++++++++ 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/src/main.rs b/src/main.rs index f0b1f57b..652d743f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -483,6 +483,9 @@ fn run(args: Cli) -> Result { )?; printer.print_separator()?; } + if history_print.response_meta { + printer.print_response_meta(prev_response_meta)?; + } if history_print.request_headers { printer.print_request_headers(next_request, &*cookie_jar)?; } @@ -528,8 +531,16 @@ fn run(args: Cli) -> Result { args.quiet, )?; } - } else if print.response_body { - printer.print_response_body(response, response_charset, response_mime)?; + } else { + if print.response_body { + printer.print_response_body(response, response_charset, response_mime)?; + if print.response_meta { + printer.print_separator()?; + } + } + if print.response_meta { + printer.print_response_meta(response_meta)?; + } } } diff --git a/src/printer.rs b/src/printer.rs index 7e144b3c..e20c55df 100644 --- a/src/printer.rs +++ b/src/printer.rs @@ -16,6 +16,7 @@ use crate::{ buffer::Buffer, cli::{Pretty, Theme}, formatting::{get_json_formatter, Highlighter}, + middleware::ResponseMeta, utils::{copy_largebuf, test_mode, BUFFER_SIZE}, }; @@ -491,6 +492,14 @@ impl Printer { self.buffer.flush()?; Ok(()) } + + pub fn print_response_meta(&mut self, response_meta: ResponseMeta) -> anyhow::Result<()> { + let elapsed_time = response_meta.starting_time.elapsed().as_secs_f64(); + self.buffer + .print(format!("Elapsed time: {:.5}s", elapsed_time))?; + self.buffer.print("\n\n")?; + Ok(()) + } } pub enum ContentType { From 1472b0075f270b2eccc0d2615d1b9df76a86c68e Mon Sep 17 00:00:00 2001 From: ducaale Date: Tue, 15 Mar 2022 19:34:11 +0000 Subject: [PATCH 05/11] use response.extensions to store metadata --- Cargo.lock | 109 +++++++++++++++++++++++----------------------- Cargo.toml | 2 +- src/auth.rs | 14 +++--- src/main.rs | 8 ++-- src/middleware.rs | 46 ++++++++++--------- src/redirect.rs | 20 +++------ 6 files changed, 98 insertions(+), 101 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2bbe77e2..6684623b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -491,19 +491,6 @@ version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "522de2a0fe3e380f1bc577ba0474108faf3f6b18321dbf60b3b9c39a75073377" -[[package]] -name = "futures-macro" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18e4a4b95cea4b4ccbcf1c5675ca7c4ee4e9e75eb79944d07defde18068f79bb" -dependencies = [ - "autocfg", - "proc-macro-hack", - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "futures-sink" version = "0.3.17" @@ -525,13 +512,10 @@ dependencies = [ "autocfg", "futures-core", "futures-io", - "futures-macro", "futures-task", "memchr", "pin-project-lite", "pin-utils", - "proc-macro-hack", - "proc-macro-nested", "slab", ] @@ -567,9 +551,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.3.4" +version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7f3675cfef6a30c8031cf9e6493ebdc3bb3272a3fea3923c4210d1830e6a472" +checksum = "62eeb471aa3e3c9197aa4bfeabfe02982f6dc96f750486c0bb0009ac58b26d2b" dependencies = [ "bytes", "fnv", @@ -619,7 +603,7 @@ checksum = "527e8c9ac747e28542699a951517aa9a6945af506cd1f2e1b53a576c17b6cc11" dependencies = [ "bytes", "fnv", - "itoa", + "itoa 0.4.8", ] [[package]] @@ -660,7 +644,7 @@ dependencies = [ "http-body", "httparse", "httpdate", - "itoa", + "itoa 0.4.8", "pin-project-lite", "socket2", "tokio", @@ -671,17 +655,15 @@ dependencies = [ [[package]] name = "hyper-rustls" -version = "0.22.1" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f9f7a97316d44c0af9b0301e65010573a853a9fc97046d7331d7f6bc0fd5a64" +checksum = "d87c48c02e0dc5e3b849a2041db3029fd066650f8f717c07bf8ed78ccb895cac" dependencies = [ - "futures-util", + "http", "hyper", - "log", "rustls", "tokio", "tokio-rustls", - "webpki", ] [[package]] @@ -760,6 +742,12 @@ version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" +[[package]] +name = "itoa" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35" + [[package]] name = "js-sys" version = "0.3.54" @@ -1176,12 +1164,6 @@ version = "0.5.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" -[[package]] -name = "proc-macro-nested" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc881b2c22681370c6a780e47af9840ef841837bc98118431d4e1868bd0c1086" - [[package]] name = "proc-macro2" version = "1.0.29" @@ -1311,9 +1293,9 @@ dependencies = [ [[package]] name = "reqwest" -version = "0.11.6" +version = "0.11.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66d2927ca2f685faf0fc620ac4834690d29e7abb153add10f5812eef20b5e280" +checksum = "46a1f7aa4f35e5e8b4160449f51afc758f0ce6454315a9fa7d0d113e958c41eb" dependencies = [ "async-compression", "base64", @@ -1323,6 +1305,7 @@ dependencies = [ "encoding_rs", "futures-core", "futures-util", + "h2", "http", "http-body", "hyper", @@ -1337,12 +1320,13 @@ dependencies = [ "native-tls", "percent-encoding", "pin-project-lite", + "proc-macro-hack", "rustls", "rustls-native-certs", + "rustls-pemfile 0.3.0", "serde", "serde_json", "serde_urlencoded", - "time", "tokio", "tokio-native-tls", "tokio-rustls", @@ -1392,11 +1376,10 @@ dependencies = [ [[package]] name = "rustls" -version = "0.19.1" +version = "0.20.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35edb675feee39aec9c99fa5ff985081995a06d594114ae14cbe797ad7b7a6d7" +checksum = "4fbfeb8d0ddb84706bc597a5574ab8912817c52a397f819e5b614e2265206921" dependencies = [ - "base64", "log", "ring", "sct", @@ -1405,16 +1388,34 @@ dependencies = [ [[package]] name = "rustls-native-certs" -version = "0.5.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a07b7c1885bd8ed3831c289b7870b13ef46fe0e856d288c30d9cc17d75a2092" +checksum = "5ca9ebdfa27d3fc180e42879037b5338ab1c040c06affd00d8338598e7800943" dependencies = [ "openssl-probe", - "rustls", + "rustls-pemfile 0.2.1", "schannel", "security-framework", ] +[[package]] +name = "rustls-pemfile" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5eebeaeb360c87bfb72e84abdb3447159c0eaececf1bef2aecd65a8be949d1c9" +dependencies = [ + "base64", +] + +[[package]] +name = "rustls-pemfile" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ee86d63972a7c661d1536fefe8c3c8407321c3df668891286de28abcd087360" +dependencies = [ + "base64", +] + [[package]] name = "ryu" version = "1.0.5" @@ -1448,9 +1449,9 @@ dependencies = [ [[package]] name = "sct" -version = "0.6.1" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b362b83898e0e69f38515b82ee15aa80636befe47c3b6d3d89a911e78fc228ce" +checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" dependencies = [ "ring", "untrusted", @@ -1521,19 +1522,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7f9e390c27c3c0ce8bc5d725f6e4d30a29d26659494aa4b17535f7522c5c950" dependencies = [ "indexmap", - "itoa", + "itoa 0.4.8", "ryu", "serde", ] [[package]] name = "serde_urlencoded" -version = "0.7.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edfa57a7f8d9c1d260a549e7224100f6c43d43f9103e06dd8b4095a9b2b43ce9" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" dependencies = [ "form_urlencoded", - "itoa", + "itoa 1.0.1", "ryu", "serde", ] @@ -1819,9 +1820,9 @@ dependencies = [ [[package]] name = "tokio-rustls" -version = "0.22.0" +version = "0.23.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc6844de72e57df1980054b38be3a9f4702aba4858be64dd700181a8a6d0e1b6" +checksum = "a27d5f2b839802bd8267fa19b0530f5a08b9c08cd417976be2a65d130fe1c11b" dependencies = [ "rustls", "tokio", @@ -2084,9 +2085,9 @@ dependencies = [ [[package]] name = "webpki" -version = "0.21.4" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8e38c0608262c46d4a56202ebabdeb094cef7e560ca7a226c6bf055188aa4ea" +checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd" dependencies = [ "ring", "untrusted", @@ -2094,9 +2095,9 @@ dependencies = [ [[package]] name = "webpki-roots" -version = "0.21.1" +version = "0.22.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aabe153544e473b775453675851ecc86863d2a81d786d741f6b76778f2a48940" +checksum = "552ceb903e957524388c4d3475725ff2c8b7960922063af6ce53c9a43da07449" dependencies = [ "webpki", ] @@ -2134,9 +2135,9 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "winreg" -version = "0.7.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0120db82e8a1e0b9fb3345a539c478767c0048d842860994d96113d5b667bd69" +checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" dependencies = [ "winapi", ] diff --git a/Cargo.toml b/Cargo.toml index 2bfe701b..0af0d3e9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -43,7 +43,7 @@ time = "0.2.26" url = "2.2.2" [dependencies.reqwest] -version = "0.11.6" +version = "0.11.10" default-features = false features = ["rustls-tls", "rustls-tls-webpki-roots", "rustls-tls-native-roots", "json", "gzip", "brotli", "deflate", "multipart", "blocking", "socks", "cookies"] diff --git a/src/auth.rs b/src/auth.rs index 4884e80c..1b7684fe 100644 --- a/src/auth.rs +++ b/src/auth.rs @@ -6,7 +6,7 @@ use reqwest::header::{HeaderValue, AUTHORIZATION, WWW_AUTHENTICATE}; use reqwest::StatusCode; use crate::cli::AuthType; -use crate::middleware::{Context, Middleware, ResponseMeta}; +use crate::middleware::{Context, Middleware}; use crate::netrc; use crate::regex; use crate::utils::clone_request; @@ -82,12 +82,8 @@ impl<'a> DigestAuthMiddleware<'a> { } impl<'a> Middleware for DigestAuthMiddleware<'a> { - fn handle( - &mut self, - mut ctx: Context, - mut request: Request, - ) -> Result<(Response, ResponseMeta)> { - let (response, response_meta) = self.next(&mut ctx, clone_request(&mut request)?)?; + fn handle(&mut self, mut ctx: Context, mut request: Request) -> Result { + let response = self.next(&mut ctx, clone_request(&mut request)?)?; match response.headers().get(WWW_AUTHENTICATE) { Some(wwwauth) if response.status() == StatusCode::UNAUTHORIZED => { let mut context = digest_auth::AuthContext::new( @@ -103,10 +99,10 @@ impl<'a> Middleware for DigestAuthMiddleware<'a> { request .headers_mut() .insert(AUTHORIZATION, HeaderValue::from_str(&answer)?); - self.print(&mut ctx, response, response_meta, &mut request)?; + self.print(&mut ctx, response, &mut request)?; Ok(self.next(&mut ctx, request)?) } - _ => Ok((response, response_meta)), + _ => Ok(response), } } } diff --git a/src/main.rs b/src/main.rs index 652d743f..a0d052e5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -34,7 +34,7 @@ use crate::auth::{Auth, DigestAuthMiddleware}; use crate::buffer::Buffer; use crate::cli::{BodyType, Cli, HttpVersion, Print, Proxy, Verify}; use crate::download::{download_file, get_file_size}; -use crate::middleware::ClientWithMiddleware; +use crate::middleware::{ClientWithMiddleware, ResponseExt}; use crate::printer::Printer; use crate::request_items::{Body, FORM_CONTENT_TYPE, JSON_ACCEPT, JSON_CONTENT_TYPE}; use crate::session::Session; @@ -467,11 +467,12 @@ fn run(args: Cli) -> Result { } if !args.offline { - let (response, response_meta) = { + let response = { let history_print = args.history_print.unwrap_or(print); let mut client = ClientWithMiddleware::new(&client); if args.all { - client = client.with_printer(|prev_response, prev_response_meta, next_request| { + client = client.with_printer(|prev_response, next_request| { + let prev_response_meta = prev_response.metadata().clone(); if history_print.response_headers { printer.print_response_headers(&prev_response)?; } @@ -532,6 +533,7 @@ fn run(args: Cli) -> Result { )?; } } else { + let response_meta = response.metadata().clone(); if print.response_body { printer.print_response_body(response, response_charset, response_mime)?; if print.response_meta { diff --git a/src/middleware.rs b/src/middleware.rs index ef30a7a4..307c45d5 100644 --- a/src/middleware.rs +++ b/src/middleware.rs @@ -3,6 +3,7 @@ use std::time::Instant; use anyhow::Result; use reqwest::blocking::{Client, Request, Response}; +#[derive(Clone)] pub struct ResponseMeta { pub starting_time: Instant, } @@ -13,18 +14,26 @@ impl ResponseMeta { } } +pub trait ResponseExt { + fn metadata(&self) -> &ResponseMeta; +} + +impl ResponseExt for Response { + fn metadata(&self) -> &ResponseMeta { + self.extensions().get::().unwrap() + } +} + pub struct Context<'a, 'b> { client: &'a Client, - printer: Option<&'a mut (dyn FnMut(Response, ResponseMeta, &mut Request) -> Result<()> + 'b)>, + printer: Option<&'a mut (dyn FnMut(Response, &mut Request) -> Result<()> + 'b)>, middlewares: &'a mut [Box], } impl<'a, 'b> Context<'a, 'b> { fn new( client: &'a Client, - printer: Option< - &'a mut (dyn FnMut(Response, ResponseMeta, &mut Request) -> Result<()> + 'b), - >, + printer: Option<&'a mut (dyn FnMut(Response, &mut Request) -> Result<()> + 'b)>, middlewares: &'a mut [Box], ) -> Self { Context { @@ -34,12 +43,15 @@ impl<'a, 'b> Context<'a, 'b> { } } - fn execute(&mut self, request: Request) -> Result<(Response, ResponseMeta)> { + fn execute(&mut self, request: Request) -> Result { match self.middlewares { [] => { let starting_time = Instant::now(); - let response = self.client.execute(request)?; - Ok((response, ResponseMeta::new(starting_time))) + let mut response = self.client.execute(request)?; + response + .extensions_mut() + .insert(ResponseMeta::new(starting_time)); + Ok(response) } [ref mut head, tail @ ..] => head.handle( #[allow(clippy::needless_option_as_deref)] @@ -51,21 +63,15 @@ impl<'a, 'b> Context<'a, 'b> { } pub trait Middleware { - fn handle(&mut self, ctx: Context, request: Request) -> Result<(Response, ResponseMeta)>; + fn handle(&mut self, ctx: Context, request: Request) -> Result; - fn next(&self, ctx: &mut Context, request: Request) -> Result<(Response, ResponseMeta)> { + fn next(&self, ctx: &mut Context, request: Request) -> Result { ctx.execute(request) } - fn print( - &self, - ctx: &mut Context, - response: Response, - response_meta: ResponseMeta, - request: &mut Request, - ) -> Result<()> { + fn print(&self, ctx: &mut Context, response: Response, request: &mut Request) -> Result<()> { if let Some(ref mut printer) = ctx.printer { - printer(response, response_meta, request)?; + printer(response, request)?; } Ok(()) @@ -74,7 +80,7 @@ pub trait Middleware { pub struct ClientWithMiddleware<'a, T> where - T: FnMut(Response, ResponseMeta, &mut Request) -> Result<()>, + T: FnMut(Response, &mut Request) -> Result<()>, { client: &'a Client, printer: Option, @@ -83,7 +89,7 @@ where impl<'a, T> ClientWithMiddleware<'a, T> where - T: FnMut(Response, ResponseMeta, &mut Request) -> Result<()> + 'a, + T: FnMut(Response, &mut Request) -> Result<()> + 'a, { pub fn new(client: &'a Client) -> Self { ClientWithMiddleware { @@ -103,7 +109,7 @@ where self } - pub fn execute(&mut self, request: Request) -> Result<(Response, ResponseMeta)> { + pub fn execute(&mut self, request: Request) -> Result { let mut ctx = Context::new( self.client, self.printer.as_mut().map(|p| p as _), diff --git a/src/redirect.rs b/src/redirect.rs index 0e06e11e..70c8dc98 100644 --- a/src/redirect.rs +++ b/src/redirect.rs @@ -6,7 +6,7 @@ use reqwest::header::{ }; use reqwest::{Method, StatusCode, Url}; -use crate::middleware::{Context, Middleware, ResponseMeta}; +use crate::middleware::{Context, Middleware}; use crate::utils::clone_request; pub struct RedirectFollower { @@ -20,15 +20,11 @@ impl RedirectFollower { } impl Middleware for RedirectFollower { - fn handle( - &mut self, - mut ctx: Context, - mut first_request: Request, - ) -> Result<(Response, ResponseMeta)> { + fn handle(&mut self, mut ctx: Context, mut first_request: Request) -> Result { // This buffers the body in case we need it again later // reqwest does *not* do this, it ignores 307/308 with a streaming body let mut request = clone_request(&mut first_request)?; - let (mut response, mut response_meta) = self.next(&mut ctx, first_request)?; + let mut response = self.next(&mut ctx, first_request)?; let mut remaining_redirects = self.max_redirects - 1; while let Some(mut next_request) = get_next_request(request, &response) { @@ -40,16 +36,12 @@ impl Middleware for RedirectFollower { self.max_redirects )); } - self.print(&mut ctx, response, response_meta, &mut next_request)?; + self.print(&mut ctx, response, &mut next_request)?; request = clone_request(&mut next_request)?; - - // TODO: use destructuring assignment once MSRV >= 1.59 - let temp = self.next(&mut ctx, next_request)?; - response = temp.0; - response_meta = temp.1; + response = self.next(&mut ctx, next_request)?; } - Ok((response, response_meta)) + Ok(response) } } From d8c12e624eeb507c02e912fe238c545d43546471 Mon Sep 17 00:00:00 2001 From: ducaale Date: Sat, 19 Mar 2022 21:11:42 +0000 Subject: [PATCH 06/11] don't set a width limit when generating manpages --- src/cli.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/cli.rs b/src/cli.rs index b60dd5cf..42cf1084 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -377,7 +377,6 @@ impl Cli { {after-help}\ ", ) - .max_term_width(100) .print_long_help() .unwrap(); } else { From b35334e8b70b2a853c5e8184f40fe273ca3b10bf Mon Sep 17 00:00:00 2001 From: ducaale Date: Sun, 17 Apr 2022 13:15:13 +0100 Subject: [PATCH 07/11] calculate response body download time separately --- src/main.rs | 8 +++----- src/middleware.rs | 27 ++++++++++++++------------- src/printer.rs | 17 ++++++++++++----- 3 files changed, 29 insertions(+), 23 deletions(-) diff --git a/src/main.rs b/src/main.rs index bd48f023..1f12df3f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -35,7 +35,7 @@ use crate::auth::{Auth, DigestAuthMiddleware}; use crate::buffer::Buffer; use crate::cli::{BodyType, Cli, HttpVersion, Print, Proxy, Verify}; use crate::download::{download_file, get_file_size}; -use crate::middleware::{ClientWithMiddleware, ResponseExt}; +use crate::middleware::ClientWithMiddleware; use crate::printer::Printer; use crate::request_items::{Body, FORM_CONTENT_TYPE, JSON_ACCEPT, JSON_CONTENT_TYPE}; use crate::session::Session; @@ -476,7 +476,6 @@ fn run(args: Cli) -> Result { let mut client = ClientWithMiddleware::new(&client); if args.all { client = client.with_printer(|prev_response, next_request| { - let prev_response_meta = prev_response.metadata().clone(); if history_print.response_headers { printer.print_response_headers(prev_response)?; } @@ -489,7 +488,7 @@ fn run(args: Cli) -> Result { printer.print_separator()?; } if history_print.response_meta { - printer.print_response_meta(prev_response_meta)?; + printer.print_response_meta(prev_response)?; } if history_print.request_headers { printer.print_request_headers(next_request, &*cookie_jar)?; @@ -537,7 +536,6 @@ fn run(args: Cli) -> Result { )?; } } else { - let response_meta = response.metadata().clone(); if print.response_body { printer.print_response_body(&mut response, response_charset, response_mime)?; if print.response_meta { @@ -545,7 +543,7 @@ fn run(args: Cli) -> Result { } } if print.response_meta { - printer.print_response_meta(response_meta)?; + printer.print_response_meta(&response)?; } } } diff --git a/src/middleware.rs b/src/middleware.rs index 9fe4601c..115d6b3e 100644 --- a/src/middleware.rs +++ b/src/middleware.rs @@ -1,27 +1,27 @@ -use std::time::Instant; +use std::time::{Duration, Instant}; use anyhow::Result; use reqwest::blocking::{Client, Request, Response}; #[derive(Clone)] pub struct ResponseMeta { - pub starting_time: Instant, -} - -impl ResponseMeta { - fn new(starting_time: Instant) -> Self { - ResponseMeta { starting_time } - } + pub request_duration: Duration, + pub content_download_duration: Option, } pub trait ResponseExt { - fn metadata(&self) -> &ResponseMeta; + fn meta(&self) -> &ResponseMeta; + fn meta_mut(&mut self) -> &mut ResponseMeta; } impl ResponseExt for Response { - fn metadata(&self) -> &ResponseMeta { + fn meta(&self) -> &ResponseMeta { self.extensions().get::().unwrap() } + + fn meta_mut(&mut self) -> &mut ResponseMeta { + self.extensions_mut().get_mut::().unwrap() + } } pub struct Context<'a, 'b> { @@ -48,9 +48,10 @@ impl<'a, 'b> Context<'a, 'b> { [] => { let starting_time = Instant::now(); let mut response = self.client.execute(request)?; - response - .extensions_mut() - .insert(ResponseMeta::new(starting_time)); + response.extensions_mut().insert(ResponseMeta { + request_duration: starting_time.elapsed(), + content_download_duration: None, + }); Ok(response) } [ref mut head, tail @ ..] => head.handle( diff --git a/src/printer.rs b/src/printer.rs index 0dec28c5..0c81b345 100644 --- a/src/printer.rs +++ b/src/printer.rs @@ -1,5 +1,6 @@ use std::borrow::Cow; use std::io::{self, BufRead, BufReader, Read, Write}; +use std::time::Instant; use encoding_rs::Encoding; use encoding_rs_io::DecodeReaderBytesBuilder; @@ -17,7 +18,7 @@ use crate::{ buffer::Buffer, cli::{Pretty, Theme}, formatting::{get_json_formatter, Highlighter}, - middleware::ResponseMeta, + middleware::ResponseExt, utils::{copy_largebuf, test_mode, BUFFER_SIZE}, }; @@ -429,6 +430,7 @@ impl Printer { encoding: Option<&'static Encoding>, mime: Option<&str>, ) -> anyhow::Result<()> { + let starting_time = Instant::now(); let url = response.url().clone(); let content_type = mime.map_or_else(|| get_content_type(response.headers()), ContentType::from); @@ -486,7 +488,6 @@ impl Printer { match decode_blob(&buf, encoding, &url) { None => { self.buffer.print(BINARY_SUPPRESSOR)?; - return Ok(()); } Some(text) => { self.print_body_text(content_type, &text)?; @@ -495,13 +496,19 @@ impl Printer { }; } self.buffer.flush()?; + drop(body); // silence the borrow checker + response.meta_mut().content_download_duration = Some(starting_time.elapsed()); Ok(()) } - pub fn print_response_meta(&mut self, response_meta: ResponseMeta) -> anyhow::Result<()> { - let elapsed_time = response_meta.starting_time.elapsed().as_secs_f64(); + pub fn print_response_meta(&mut self, response: &Response) -> anyhow::Result<()> { + let meta = response.meta(); + let mut total_elapsed_time = meta.request_duration.as_secs_f64(); + if let Some(content_download_duration) = meta.content_download_duration { + total_elapsed_time += content_download_duration.as_secs_f64(); + } self.buffer - .print(format!("Elapsed time: {:.5}s", elapsed_time))?; + .print(format!("Elapsed time: {:.5}s", total_elapsed_time))?; self.buffer.print("\n\n")?; Ok(()) } From d4d8758003acc5e51390cf0897175adac8e9febb Mon Sep 17 00:00:00 2001 From: ducaale Date: Sat, 20 Aug 2022 12:25:24 +0100 Subject: [PATCH 08/11] update docs for --verbose and --print --- src/cli.rs | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/src/cli.rs b/src/cli.rs index 032dd58f..ee7c4ceb 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -81,15 +81,21 @@ pub struct Cli { pub response_mime: Option, /// String specifying what the output should contain - /// - /// 'H' request headers - /// 'B' request body - /// 'h' response headers - /// 'b' response body - /// 'm' response metadata - /// - /// Example: `--print=Hb` - #[clap(short = 'p', long, value_name = "FORMAT", verbatim_doc_comment)] + #[clap( + short = 'p', + long, + value_name = "FORMAT", + long_help = "\ +String specifying what the output should contain + + 'H' request headers + 'B' request body + 'h' response headers + 'b' response body + 'm' response metadata + +Example: `--print=Hb`" + )] pub print: Option, /// Print only the response headers. Shortcut for --print=h. @@ -105,6 +111,8 @@ pub struct Cli { pub meta: bool, /// Print the whole request as well as the response. + /// + /// Using verbose twice i.e. -vv will print the response metadata as well. #[clap(short = 'v', long, parse(from_occurrences))] pub verbose: usize, From 081607ee5f2f524ea40d2eb53d5856a7c397fc0a Mon Sep 17 00:00:00 2001 From: ducaale Date: Sat, 20 Aug 2022 13:29:59 +0100 Subject: [PATCH 09/11] update predicates dependency --- Cargo.lock | 32 ++++++++------------------------ Cargo.toml | 2 +- 2 files changed, 9 insertions(+), 25 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d8677403..74e1ec74 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -46,7 +46,7 @@ checksum = "e996dc7940838b7ef1096b882e29ec30a3149a3a443cdc8dba19ed382eca1fe2" dependencies = [ "bstr", "doc-comment", - "predicates 2.0.2", + "predicates", "predicates-core", "predicates-tree", "wait-timeout", @@ -308,12 +308,6 @@ dependencies = [ "cfg-if", ] -[[package]] -name = "difference" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "524cbf6897b527295dff137cec09ecf3a05f4fddffd7dfcd1585403449e74198" - [[package]] name = "difflib" version = "0.4.0" @@ -418,9 +412,9 @@ dependencies = [ [[package]] name = "float-cmp" -version = "0.8.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1267f4ac4f343772758f7b1bdcbe767c218bbab93bb432acbf5162bbf85a6c4" +checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4" dependencies = [ "num-traits", ] @@ -1082,28 +1076,18 @@ checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" [[package]] name = "predicates" -version = "1.0.8" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f49cfaf7fdaa3bfacc6fa3e7054e65148878354a5cfddcf661df4c851f8021df" +checksum = "a5aab5be6e4732b473071984b3164dbbfb7a3674d30ea5ff44410b6bcd960c3c" dependencies = [ - "difference", + "difflib", "float-cmp", + "itertools", "normalize-line-endings", "predicates-core", "regex", ] -[[package]] -name = "predicates" -version = "2.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c143348f141cc87aab5b950021bac6145d0e5ae754b0591de23244cee42c9308" -dependencies = [ - "difflib", - "itertools", - "predicates-core", -] - [[package]] name = "predicates-core" version = "1.0.2" @@ -2168,7 +2152,7 @@ dependencies = [ "once_cell", "os_display", "pem", - "predicates 1.0.8", + "predicates", "rand", "regex", "reqwest", diff --git a/Cargo.toml b/Cargo.toml index eb7b8daa..3a246f88 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -61,7 +61,7 @@ features = ["parsing", "html", "yaml-load", "dump-load", "dump-create", "regex-o assert_cmd = "2.0" form_urlencoded = "1.0.1" indoc = "1.0" -predicates = "1.0.7" +predicates = "2.1.1" hyper = { version = "0.14", features = ["server"] } tokio = { version = "1", features = ["rt", "sync", "time"] } tempfile = "3.2.0" From 97e564b5d859409078c0581bea9e0c12e17547ab Mon Sep 17 00:00:00 2001 From: ducaale Date: Sat, 20 Aug 2022 13:30:17 +0100 Subject: [PATCH 10/11] add tests for response meta --- tests/cli.rs | 61 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/tests/cli.rs b/tests/cli.rs index 6408fda2..d63febc8 100644 --- a/tests/cli.rs +++ b/tests/cli.rs @@ -3027,3 +3027,64 @@ fn empty_response_with_content_encoding_and_content_length() { "#}); } + +#[test] +fn response_meta() { + let server = server::http(|_req| async move { + hyper::Response::builder() + .header("date", "N/A") + .body("Hello!".into()) + .unwrap() + }); + + get_command() + .arg("--print=m") + .arg(server.base_url()) + .assert() + .stdout(contains("Elapsed time: ")); +} + +#[test] +fn redirect_with_respone_meta() { + let server = server::http(|req| async move { + match req.uri().path() { + "/first_page" => hyper::Response::builder() + .status(302) + .header("Date", "N/A") + .header("Location", "/second_page") + .body("redirecting...".into()) + .unwrap(), + "/second_page" => hyper::Response::builder() + .header("Date", "N/A") + .body("final destination".into()) + .unwrap(), + _ => panic!("unknown path"), + } + }); + + get_command() + .arg(server.url("/first_page")) + .arg("--follow") + .arg("-vv") + .assert() + .stdout(contains("Elapsed time: ").count(2)); + + get_command() + .arg(server.url("/first_page")) + .arg("--follow") + .arg("--meta") + .assert() + .stdout(contains("Elapsed time: ").count(1)); +} + +#[cfg(feature = "online-tests")] +#[test] +fn digest_auth_with_response_meta() { + get_command() + .arg("--auth-type=digest") + .arg("--auth=ahmed:12345") + .arg("-vv") + .arg("httpbin.org/digest-auth/5/ahmed/12345") + .assert() + .stdout(contains("Elapsed time: ").count(2)); +} From 82370ec033721e9c2294c0dc08ed9f116fac8961 Mon Sep 17 00:00:00 2001 From: ducaale Date: Tue, 3 Jan 2023 00:30:46 +0200 Subject: [PATCH 11/11] regenerate completions and manpages --- completions/_xh | 7 +++++-- completions/_xh.ps1 | 3 +++ completions/xh.bash | 2 +- completions/xh.fish | 2 ++ doc/xh.1 | 13 +++++++++++-- 5 files changed, 22 insertions(+), 5 deletions(-) diff --git a/completions/_xh b/completions/_xh index 6020ebc0..8d77f459 100644 --- a/completions/_xh +++ b/completions/_xh @@ -58,8 +58,10 @@ none\:"Disable both coloring and formatting"))' \ '--headers[Print only the response headers. Shortcut for --print=h]' \ '-b[Print only the response body. Shortcut for --print=b]' \ '--body[Print only the response body. Shortcut for --print=b]' \ -'-v[Print the whole request as well as the response]' \ -'--verbose[Print the whole request as well as the response]' \ +'-m[Print only the response metadata. Shortcut for --print=m]' \ +'--meta[Print only the response metadata. Shortcut for --print=m]' \ +'*-v[Print the whole request as well as the response]' \ +'*--verbose[Print the whole request as well as the response]' \ '--all[Show any intermediary requests/responses while following redirects with --follow]' \ '-4[Resolve hostname to ipv4 addresses only]' \ '--ipv4[Resolve hostname to ipv4 addresses only]' \ @@ -97,6 +99,7 @@ none\:"Disable both coloring and formatting"))' \ '--no-print[]' \ '--no-headers[]' \ '--no-body[]' \ +'--no-meta[]' \ '--no-verbose[]' \ '--no-all[]' \ '--no-history-print[]' \ diff --git a/completions/_xh.ps1 b/completions/_xh.ps1 index 137b2e1e..3bb4995f 100644 --- a/completions/_xh.ps1 +++ b/completions/_xh.ps1 @@ -61,6 +61,8 @@ Register-ArgumentCompleter -Native -CommandName 'xh' -ScriptBlock { [CompletionResult]::new('--headers', 'headers', [CompletionResultType]::ParameterName, 'Print only the response headers. Shortcut for --print=h') [CompletionResult]::new('-b', 'b', [CompletionResultType]::ParameterName, 'Print only the response body. Shortcut for --print=b') [CompletionResult]::new('--body', 'body', [CompletionResultType]::ParameterName, 'Print only the response body. Shortcut for --print=b') + [CompletionResult]::new('-m', 'm', [CompletionResultType]::ParameterName, 'Print only the response metadata. Shortcut for --print=m') + [CompletionResult]::new('--meta', 'meta', [CompletionResultType]::ParameterName, 'Print only the response metadata. Shortcut for --print=m') [CompletionResult]::new('-v', 'v', [CompletionResultType]::ParameterName, 'Print the whole request as well as the response') [CompletionResult]::new('--verbose', 'verbose', [CompletionResultType]::ParameterName, 'Print the whole request as well as the response') [CompletionResult]::new('--all', 'all', [CompletionResultType]::ParameterName, 'Show any intermediary requests/responses while following redirects with --follow') @@ -100,6 +102,7 @@ Register-ArgumentCompleter -Native -CommandName 'xh' -ScriptBlock { [CompletionResult]::new('--no-print', 'no-print', [CompletionResultType]::ParameterName, 'no-print') [CompletionResult]::new('--no-headers', 'no-headers', [CompletionResultType]::ParameterName, 'no-headers') [CompletionResult]::new('--no-body', 'no-body', [CompletionResultType]::ParameterName, 'no-body') + [CompletionResult]::new('--no-meta', 'no-meta', [CompletionResultType]::ParameterName, 'no-meta') [CompletionResult]::new('--no-verbose', 'no-verbose', [CompletionResultType]::ParameterName, 'no-verbose') [CompletionResult]::new('--no-all', 'no-all', [CompletionResultType]::ParameterName, 'no-all') [CompletionResult]::new('--no-history-print', 'no-history-print', [CompletionResultType]::ParameterName, 'no-history-print') diff --git a/completions/xh.bash b/completions/xh.bash index 96e2c740..de652002 100644 --- a/completions/xh.bash +++ b/completions/xh.bash @@ -19,7 +19,7 @@ _xh() { case "${cmd}" in xh) - opts="-V -j -f -s -p -h -b -v -P -4 -6 -q -S -o -d -c -A -a -F -I --help --version --json --form --multipart --raw --pretty --style --response-charset --response-mime --print --headers --body --verbose --all --history-print --ipv4 --ipv6 --quiet --stream --output --download --continue --session --session-read-only --auth-type --auth --bearer --ignore-netrc --offline --check-status --follow --max-redirects --timeout --proxy --verify --cert --cert-key --ssl --native-tls --default-scheme --https --http-version --ignore-stdin --curl --curl-long --no-help --no-version --no-json --no-form --no-multipart --no-raw --no-pretty --no-style --no-response-charset --no-response-mime --no-print --no-headers --no-body --no-verbose --no-all --no-history-print --no-ipv4 --no-ipv6 --no-quiet --no-stream --no-output --no-download --no-continue --no-session --no-session-read-only --no-auth-type --no-auth --no-bearer --no-ignore-netrc --no-offline --no-check-status --no-follow --no-max-redirects --no-timeout --no-proxy --no-verify --no-cert --no-cert-key --no-ssl --no-native-tls --no-default-scheme --no-https --no-http-version --no-ignore-stdin --no-curl --no-curl-long <[METHOD] URL> ..." + opts="-V -j -f -s -p -h -b -m -v -P -4 -6 -q -S -o -d -c -A -a -F -I --help --version --json --form --multipart --raw --pretty --style --response-charset --response-mime --print --headers --body --meta --verbose --all --history-print --ipv4 --ipv6 --quiet --stream --output --download --continue --session --session-read-only --auth-type --auth --bearer --ignore-netrc --offline --check-status --follow --max-redirects --timeout --proxy --verify --cert --cert-key --ssl --native-tls --default-scheme --https --http-version --ignore-stdin --curl --curl-long --no-help --no-version --no-json --no-form --no-multipart --no-raw --no-pretty --no-style --no-response-charset --no-response-mime --no-print --no-headers --no-body --no-meta --no-verbose --no-all --no-history-print --no-ipv4 --no-ipv6 --no-quiet --no-stream --no-output --no-download --no-continue --no-session --no-session-read-only --no-auth-type --no-auth --no-bearer --no-ignore-netrc --no-offline --no-check-status --no-follow --no-max-redirects --no-timeout --no-proxy --no-verify --no-cert --no-cert-key --no-ssl --no-native-tls --no-default-scheme --no-https --no-http-version --no-ignore-stdin --no-curl --no-curl-long <[METHOD] URL> ..." if [[ ${cur} == -* || ${COMP_CWORD} -eq 1 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 diff --git a/completions/xh.fish b/completions/xh.fish index 3cafecb9..78c07af2 100644 --- a/completions/xh.fish +++ b/completions/xh.fish @@ -27,6 +27,7 @@ complete -c xh -s f -l form -d 'Serialize data items from the command line as fo complete -c xh -l multipart -d 'Like --form, but force a multipart/form-data request even without files' complete -c xh -s h -l headers -d 'Print only the response headers. Shortcut for --print=h' complete -c xh -s b -l body -d 'Print only the response body. Shortcut for --print=b' +complete -c xh -s m -l meta -d 'Print only the response metadata. Shortcut for --print=m' complete -c xh -s v -l verbose -d 'Print the whole request as well as the response' complete -c xh -l all -d 'Show any intermediary requests/responses while following redirects with --follow' complete -c xh -s 4 -l ipv4 -d 'Resolve hostname to ipv4 addresses only' @@ -57,6 +58,7 @@ complete -c xh -l no-response-mime complete -c xh -l no-print complete -c xh -l no-headers complete -c xh -l no-body +complete -c xh -l no-meta complete -c xh -l no-verbose complete -c xh -l no-all complete -c xh -l no-history-print diff --git a/doc/xh.1 b/doc/xh.1 index 8fa2fd81..50678977 100644 --- a/doc/xh.1 +++ b/doc/xh.1 @@ -132,9 +132,13 @@ Override the response mime type for coloring and formatting for the terminal. Example: \-\-response\-mime=application/json. .TP 4 \fB\-p\fR, \fB\-\-print\fR=\fIFORMAT\fR -String specifying what the output should contain. +String specifying what the output should contain -Use "H" and "B" for request header and body respectively, and "h" and "b" for response header and body. + 'H' request headers + 'B' request body + 'h' response headers + 'b' response body + 'm' response metadata Example: \-\-print=Hb. .TP 4 @@ -144,11 +148,16 @@ Print only the response headers. Shortcut for \-\-print=h. \fB\-b\fR, \fB\-\-body\fR Print only the response body. Shortcut for \-\-print=b. .TP 4 +\fB\-m\fR, \fB\-\-meta\fR +Print only the response metadata. Shortcut for \-\-print=m. +.TP 4 \fB\-v\fR, \fB\-\-verbose\fR Print the whole request as well as the response. Additionally, this enables \-\-all for printing intermediary requests/responses while following redirects. +Using verbose twice i.e. \-vv will print the response metadata as well. + Equivalent to \-\-print=HhBb \-\-all. .TP 4 \fB\-\-all\fR