mirror of
https://github.com/Orange-OpenSource/hurl.git
synced 2024-09-11 05:35:53 +03:00
feat: introduce curl --path-as-is
option
This commit is contained in:
parent
1c1b7dd8af
commit
34137c0e0d
@ -171,6 +171,7 @@ will follow a redirection only for the second entry.
|
||||
| <a href="#no-output" id="no-output"><code>--no-output</code></a> | Suppress output. By default, Hurl outputs the body of the last response.<br> |
|
||||
| <a href="#noproxy" id="noproxy"><code>--noproxy <HOST(S)></code></a> | Comma-separated list of hosts which do not use a proxy.<br>Override value from Environment variable no_proxy.<br> |
|
||||
| <a href="#output" id="output"><code>-o, --output <FILE></code></a> | Write output to FILE instead of stdout.<br> |
|
||||
| <a href="#path-as-is" id="path-as-is"><code>--path-as-is</code></a> | Tell curl to not handle sequences of /../ or /./ in the given URL path. Normally curl will squash or merge them according to standards but with this option set you tell it not to do that.<br> |
|
||||
| <a href="#proxy" id="proxy"><code>-x, --proxy <[PROTOCOL://]HOST[:PORT]></code></a> | Use the specified proxy.<br> |
|
||||
| <a href="#report-junit" id="report-junit"><code>--report-junit <FILE></code></a> | Generate JUnit File.<br><br>If the FILE report already exists, it will be updated with the new test results.<br> |
|
||||
| <a href="#report-html" id="report-html"><code>--report-html <DIR></code></a> | Generate HTML report in DIR.<br><br>If the HTML report already exists, it will be updated with the new test results.<br> |
|
||||
@ -224,3 +225,4 @@ Using an environment variable to set the proxy has the same effect as using the
|
||||
|
||||
curl(1) hurlfmt(1)
|
||||
|
||||
|
||||
|
@ -640,6 +640,7 @@ compressed: true # request a compressed response
|
||||
insecure: true # allows insecure SSL connections and transfers
|
||||
location: true # follow redirection for this request
|
||||
max-redirs: 10 # maximum number of redirections
|
||||
path-as-is: true # tell curl to not handle sequences of /../ or /./ in the given URL path
|
||||
variable: country=Italy # define variable country
|
||||
variable: planet=Earth # define variable planet
|
||||
verbose: true # allow verbose output
|
||||
|
@ -239,6 +239,13 @@ pub fn output() -> clap::Arg {
|
||||
.num_args(1)
|
||||
}
|
||||
|
||||
pub fn path_as_is() -> clap::Arg {
|
||||
clap::Arg::new("path_as_is")
|
||||
.long("path_as_is")
|
||||
.help("Tell curl to not handle sequences of /../ or /./ in the given URL path")
|
||||
.action(ArgAction::SetTrue)
|
||||
}
|
||||
|
||||
pub fn proxy() -> clap::Arg {
|
||||
clap::Arg::new("proxy")
|
||||
.short('x')
|
||||
|
@ -227,6 +227,10 @@ pub fn output_type(arg_matches: &ArgMatches) -> OutputType {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn path_as_is(arg_matches: &ArgMatches) -> bool {
|
||||
has_flag(arg_matches, "path_as_is")
|
||||
}
|
||||
|
||||
pub fn progress_bar(arg_matches: &ArgMatches) -> bool {
|
||||
let verbose = verbose(arg_matches) || very_verbose(arg_matches);
|
||||
test(arg_matches)
|
||||
|
@ -60,6 +60,7 @@ pub struct Options {
|
||||
pub no_proxy: Option<String>,
|
||||
pub output: Option<String>,
|
||||
pub output_type: OutputType,
|
||||
pub path_as_is: bool,
|
||||
pub progress_bar: bool,
|
||||
pub proxy: Option<String>,
|
||||
pub resolves: Vec<String>,
|
||||
@ -148,6 +149,7 @@ pub fn parse() -> Result<Options, OptionsError> {
|
||||
.arg(commands::no_output())
|
||||
.arg(commands::noproxy())
|
||||
.arg(commands::output())
|
||||
.arg(commands::path_as_is())
|
||||
.arg(commands::proxy())
|
||||
.arg(commands::report_html())
|
||||
.arg(commands::report_junit())
|
||||
@ -206,6 +208,7 @@ fn parse_matches(arg_matches: &ArgMatches) -> Result<Options, OptionsError> {
|
||||
let max_redirect = matches::max_redirect(arg_matches);
|
||||
let no_proxy = matches::no_proxy(arg_matches);
|
||||
let progress_bar = matches::progress_bar(arg_matches);
|
||||
let path_as_is = matches::path_as_is(arg_matches);
|
||||
let proxy = matches::proxy(arg_matches);
|
||||
let output = matches::output(arg_matches);
|
||||
let output_type = matches::output_type(arg_matches);
|
||||
@ -244,6 +247,7 @@ fn parse_matches(arg_matches: &ArgMatches) -> Result<Options, OptionsError> {
|
||||
junit_file,
|
||||
max_redirect,
|
||||
no_proxy,
|
||||
path_as_is,
|
||||
progress_bar,
|
||||
proxy,
|
||||
output,
|
||||
@ -279,6 +283,7 @@ impl Options {
|
||||
let follow_location = self.follow_location;
|
||||
let insecure = self.insecure;
|
||||
let max_redirect = self.max_redirect;
|
||||
let path_as_is = self.path_as_is.clone();
|
||||
let proxy = self.proxy.clone();
|
||||
let no_proxy = self.no_proxy.clone();
|
||||
let cookie_input_file = self.cookie_input_file.clone();
|
||||
@ -332,6 +337,7 @@ impl Options {
|
||||
.insecure(insecure)
|
||||
.max_redirect(max_redirect)
|
||||
.no_proxy(no_proxy)
|
||||
.path_as_is(path_as_is)
|
||||
.post_entry(post_entry)
|
||||
.pre_entry(pre_entry)
|
||||
.proxy(proxy)
|
||||
|
@ -149,6 +149,7 @@ impl Client {
|
||||
self.handle.ssl_key(client_key_file).unwrap();
|
||||
self.handle.ssl_cert_type("PEM").unwrap();
|
||||
}
|
||||
self.handle.path_as_is(options.path_as_is).unwrap();
|
||||
if let Some(proxy) = options.proxy.clone() {
|
||||
self.handle.proxy(proxy.as_str()).unwrap();
|
||||
}
|
||||
|
@ -31,6 +31,7 @@ pub struct ClientOptions {
|
||||
pub insecure: bool,
|
||||
pub max_redirect: Option<usize>,
|
||||
pub no_proxy: Option<String>,
|
||||
pub path_as_is: bool,
|
||||
pub proxy: Option<String>,
|
||||
pub resolves: Vec<String>,
|
||||
pub retry: Retry,
|
||||
@ -61,6 +62,7 @@ impl Default for ClientOptions {
|
||||
insecure: false,
|
||||
max_redirect: Some(50),
|
||||
no_proxy: None,
|
||||
path_as_is: false,
|
||||
proxy: None,
|
||||
resolves: vec![],
|
||||
retry: Retry::None,
|
||||
@ -119,6 +121,9 @@ impl ClientOptions {
|
||||
arguments.push("--max-redirs".to_string());
|
||||
arguments.push(max_redirect.to_string());
|
||||
}
|
||||
if self.path_as_is {
|
||||
arguments.push("--path-as-is".to_string());
|
||||
}
|
||||
if let Some(ref proxy) = self.proxy {
|
||||
arguments.push("--proxy".to_string());
|
||||
arguments.push(format!("'{proxy}'"));
|
||||
@ -160,6 +165,7 @@ mod tests {
|
||||
follow_location: true,
|
||||
max_redirect: Some(10),
|
||||
cookie_input_file: Some("cookie_file".to_string()),
|
||||
path_as_is: true,
|
||||
proxy: Some("localhost:3128".to_string()),
|
||||
no_proxy: None,
|
||||
verbosity: None,
|
||||
@ -189,6 +195,7 @@ mod tests {
|
||||
"--location".to_string(),
|
||||
"--max-redirs".to_string(),
|
||||
"10".to_string(),
|
||||
"--path-as-is".to_string(),
|
||||
"--proxy".to_string(),
|
||||
"'localhost:3128'".to_string(),
|
||||
"--resolve".to_string(),
|
||||
|
@ -227,6 +227,7 @@ impl ClientOptions {
|
||||
follow_location: runner_options.follow_location,
|
||||
max_redirect: runner_options.max_redirect,
|
||||
cookie_input_file: runner_options.cookie_input_file.clone(),
|
||||
path_as_is: runner_options.path_as_is,
|
||||
proxy: runner_options.proxy.clone(),
|
||||
no_proxy: runner_options.no_proxy.clone(),
|
||||
verbosity: match verbosity {
|
||||
@ -334,6 +335,10 @@ pub fn get_entry_options(
|
||||
runner_options.max_redirect = Some(option.value);
|
||||
logger.debug(format!("max-redirs: {}", option.value).as_str());
|
||||
}
|
||||
EntryOption::PathAsIs(option) => {
|
||||
runner_options.path_as_is = option.value;
|
||||
logger.debug(format!("path-as-is: {}", option.value).as_str());
|
||||
}
|
||||
EntryOption::Proxy(option) => {
|
||||
runner_options.proxy = Some(option.value.clone());
|
||||
logger.debug(format!("proxy: {}", option.value).as_str());
|
||||
|
@ -36,6 +36,7 @@ pub struct RunnerOptionsBuilder {
|
||||
insecure: bool,
|
||||
max_redirect: Option<usize>,
|
||||
no_proxy: Option<String>,
|
||||
path_as_is: bool,
|
||||
post_entry: Option<fn() -> bool>,
|
||||
pre_entry: Option<fn(Entry) -> bool>,
|
||||
proxy: Option<String>,
|
||||
@ -66,6 +67,7 @@ impl Default for RunnerOptionsBuilder {
|
||||
insecure: false,
|
||||
max_redirect: Some(50),
|
||||
no_proxy: None,
|
||||
path_as_is: false,
|
||||
post_entry: None,
|
||||
pre_entry: None,
|
||||
proxy: None,
|
||||
@ -181,6 +183,12 @@ impl RunnerOptionsBuilder {
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the path-as-is flag.
|
||||
pub fn path_as_is(&mut self, path_as_is: bool) -> &mut Self {
|
||||
self.path_as_is = path_as_is;
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets list of hosts which do not use a proxy.
|
||||
pub fn no_proxy(&mut self, no_proxy: Option<String>) -> &mut Self {
|
||||
self.no_proxy = no_proxy;
|
||||
@ -279,6 +287,7 @@ impl RunnerOptionsBuilder {
|
||||
insecure: self.insecure,
|
||||
max_redirect: self.max_redirect,
|
||||
no_proxy: self.no_proxy.clone(),
|
||||
path_as_is: self.path_as_is,
|
||||
post_entry: self.post_entry,
|
||||
pre_entry: self.pre_entry,
|
||||
proxy: self.proxy.clone(),
|
||||
@ -310,6 +319,7 @@ pub struct RunnerOptions {
|
||||
pub(crate) insecure: bool,
|
||||
pub(crate) max_redirect: Option<usize>,
|
||||
pub(crate) no_proxy: Option<String>,
|
||||
pub(crate) path_as_is: bool,
|
||||
pub(crate) post_entry: Option<fn() -> bool>,
|
||||
pub(crate) pre_entry: Option<fn(Entry) -> bool>,
|
||||
pub(crate) proxy: Option<String>,
|
||||
|
@ -706,6 +706,7 @@ pub enum EntryOption {
|
||||
Insecure(InsecureOption),
|
||||
FollowLocation(FollowLocationOption),
|
||||
MaxRedirect(MaxRedirectOption),
|
||||
PathAsIs(PathAsIsOption),
|
||||
Proxy(ProxyOption),
|
||||
Retry(RetryOption),
|
||||
RetryInterval(RetryIntervalOption),
|
||||
@ -764,6 +765,16 @@ pub struct ClientKeyOption {
|
||||
pub line_terminator0: LineTerminator,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct PathAsIsOption {
|
||||
pub line_terminators: Vec<LineTerminator>,
|
||||
pub space0: Whitespace,
|
||||
pub space1: Whitespace,
|
||||
pub space2: Whitespace,
|
||||
pub value: bool,
|
||||
pub line_terminator0: LineTerminator,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct ProxyOption {
|
||||
pub line_terminators: Vec<LineTerminator>,
|
||||
|
@ -209,6 +209,7 @@ impl HtmlFormatter {
|
||||
EntryOption::Insecure(option) => self.fmt_insecure_option(option),
|
||||
EntryOption::FollowLocation(option) => self.fmt_follow_location_option(option),
|
||||
EntryOption::MaxRedirect(option) => self.fmt_max_redirect_option(option),
|
||||
EntryOption::PathAsIs(option) => self.fmt_path_as_is_option(option),
|
||||
EntryOption::Proxy(option) => self.fmt_proxy_option(option),
|
||||
EntryOption::Retry(option) => self.fmt_retry_option(option),
|
||||
EntryOption::RetryInterval(option) => self.fmt_retry_interval_option(option),
|
||||
@ -309,6 +310,19 @@ impl HtmlFormatter {
|
||||
self.fmt_lt(&option.line_terminator0);
|
||||
}
|
||||
|
||||
fn fmt_path_as_is_option(&mut self, option: &PathAsIsOption) {
|
||||
self.fmt_lts(&option.line_terminators);
|
||||
self.fmt_span_open("line");
|
||||
self.fmt_space(&option.space0);
|
||||
self.fmt_string("path-as-is");
|
||||
self.fmt_space(&option.space1);
|
||||
self.buffer.push(':');
|
||||
self.fmt_space(&option.space2);
|
||||
self.fmt_bool(option.value);
|
||||
self.fmt_span_close();
|
||||
self.fmt_lt(&option.line_terminator0);
|
||||
}
|
||||
|
||||
fn fmt_proxy_option(&mut self, option: &ProxyOption) {
|
||||
self.fmt_lts(&option.line_terminators);
|
||||
self.fmt_span_open("line");
|
||||
|
@ -364,6 +364,7 @@ fn option(reader: &mut Reader) -> ParseResult<'static, EntryOption> {
|
||||
option_insecure,
|
||||
option_follow_location,
|
||||
option_max_redirect,
|
||||
option_path_as_is,
|
||||
option_proxy,
|
||||
option_retry,
|
||||
option_retry_interval,
|
||||
@ -531,6 +532,28 @@ fn option_max_redirect(reader: &mut Reader) -> ParseResult<'static, EntryOption>
|
||||
Ok(EntryOption::MaxRedirect(option))
|
||||
}
|
||||
|
||||
fn option_path_as_is(reader: &mut Reader) -> ParseResult<'static, EntryOption> {
|
||||
let line_terminators = optional_line_terminators(reader)?;
|
||||
let space0 = zero_or_more_spaces(reader)?;
|
||||
try_literal("path-as-is", reader)?;
|
||||
let space1 = zero_or_more_spaces(reader)?;
|
||||
try_literal(":", reader)?;
|
||||
let space2 = zero_or_more_spaces(reader)?;
|
||||
let value = nonrecover(boolean, reader)?;
|
||||
let line_terminator0 = line_terminator(reader)?;
|
||||
|
||||
let option = PathAsIsOption {
|
||||
line_terminators,
|
||||
space0,
|
||||
space1,
|
||||
space2,
|
||||
value,
|
||||
line_terminator0,
|
||||
};
|
||||
|
||||
Ok(EntryOption::PathAsIs(option))
|
||||
}
|
||||
|
||||
fn option_proxy(reader: &mut Reader) -> ParseResult<'static, EntryOption> {
|
||||
let line_terminators = optional_line_terminators(reader)?;
|
||||
let space0 = zero_or_more_spaces(reader)?;
|
||||
|
@ -863,6 +863,7 @@ impl Tokenizable for EntryOption {
|
||||
EntryOption::Insecure(option) => option.tokenize(),
|
||||
EntryOption::FollowLocation(option) => option.tokenize(),
|
||||
EntryOption::MaxRedirect(option) => option.tokenize(),
|
||||
EntryOption::PathAsIs(option) => option.tokenize(),
|
||||
EntryOption::Proxy(option) => option.tokenize(),
|
||||
EntryOption::Retry(option) => option.tokenize(),
|
||||
EntryOption::RetryInterval(option) => option.tokenize(),
|
||||
@ -1020,6 +1021,27 @@ impl Tokenizable for MaxRedirectOption {
|
||||
}
|
||||
}
|
||||
|
||||
impl Tokenizable for PathAsIsOption {
|
||||
fn tokenize(&self) -> Vec<Token> {
|
||||
let mut tokens: Vec<Token> = vec![];
|
||||
tokens.append(
|
||||
&mut self
|
||||
.line_terminators
|
||||
.iter()
|
||||
.flat_map(|e| e.tokenize())
|
||||
.collect(),
|
||||
);
|
||||
tokens.append(&mut self.space0.tokenize());
|
||||
tokens.push(Token::String("path-as-is".to_string()));
|
||||
tokens.append(&mut self.space1.tokenize());
|
||||
tokens.push(Token::Colon(String::from(":")));
|
||||
tokens.append(&mut self.space2.tokenize());
|
||||
tokens.push(Token::Boolean(self.value.to_string()));
|
||||
tokens.append(&mut self.line_terminator0.tokenize());
|
||||
tokens
|
||||
}
|
||||
}
|
||||
|
||||
impl Tokenizable for ProxyOption {
|
||||
fn tokenize(&self) -> Vec<Token> {
|
||||
let mut tokens: Vec<Token> = vec![];
|
||||
|
Loading…
Reference in New Issue
Block a user