Skip to content

Commit

Permalink
Add --ipv4/--ipv6 option
Browse files Browse the repository at this point in the history
  • Loading branch information
jcamiel committed Oct 18, 2023
1 parent ab16f58 commit c0157e2
Show file tree
Hide file tree
Showing 17 changed files with 203 additions and 34 deletions.
6 changes: 6 additions & 0 deletions integration/tests_ok/help.out.pattern
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,12 @@ Options:
Allow insecure SSL connections
--interactive
Turn on interactive mode
-4, --ipv4
Tell Hurl to use IPv4 addresses only when resolving host names, and not for example try
IPv6
-6, --ipv6
Tell Hurl to use IPv6 addresses only when resolving host names, and not for example try
IPv4
--json
Output each Hurl file result to JSON
--max-redirs <NUM>
Expand Down
29 changes: 29 additions & 0 deletions integration/tests_ok/ip.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
from app import app
from ipaddress import ip_address, IPv4Address, IPv6Address
from flask import Response, request


@app.route("/ip")
def ip():
request_ip = request.remote_addr
return Response(request_ip)


@app.route("/check-ipv4")
def check_ipv4():
request_ip = request.remote_addr
if type(ip_address(request_ip)) is IPv4Address:
status = 200
else:
status = 400
return Response(request_ip, status=status)


@app.route("/check-ipv6")
def check_ipv6():
request_ip = request.remote_addr
if type(ip_address(request_ip)) is IPv6Address:
status = 200
else:
status = 400
return Response(request_ip, status=status)
16 changes: 16 additions & 0 deletions packages/hurl/src/cli/options/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,22 @@ pub fn interactive() -> clap::Arg {
.action(ArgAction::SetTrue)
}

pub fn ipv4() -> clap::Arg {
clap::Arg::new("ipv4")
.long("ipv4")
.short('4')
.help("Tell Hurl to use IPv4 addresses only when resolving host names, and not for example try IPv6")
.action(ArgAction::SetTrue)
}

pub fn ipv6() -> clap::Arg {
clap::Arg::new("ipv6")
.long("ipv6")
.short('6')
.help("Tell Hurl to use IPv6 addresses only when resolving host names, and not for example try IPv4")
.action(ArgAction::SetTrue)
}

pub fn json() -> clap::Arg {
clap::Arg::new("json")
.long("json")
Expand Down
12 changes: 11 additions & 1 deletion packages/hurl/src/cli/options/matches.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
*/
use super::variables::{parse as parse_variable, parse_value};
use super::OptionsError;
use crate::cli::options::{ErrorFormat, HttpVersion};
use crate::cli::options::{ErrorFormat, HttpVersion, IpResolve};
use crate::cli::OutputType;
use clap::ArgMatches;
use hurl::runner::Value;
Expand Down Expand Up @@ -224,6 +224,16 @@ pub fn interactive(arg_matches: &ArgMatches) -> bool {
has_flag(arg_matches, "interactive")
}

pub fn ip_resolve(arg_matches: &ArgMatches) -> Option<IpResolve> {
if has_flag(arg_matches, "ipv6") {
Some(IpResolve::IpV6)
} else if has_flag(arg_matches, "ipv4") {
Some(IpResolve::IpV4)
} else {
None
}
}

pub fn junit_file(arg_matches: &ArgMatches) -> Option<String> {
get::<String>(arg_matches, "junit")
}
Expand Down
37 changes: 31 additions & 6 deletions packages/hurl/src/cli/options/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ pub struct Options {
pub input_files: Vec<String>,
pub insecure: bool,
pub interactive: bool,
pub ip_resolve: Option<IpResolve>,
pub junit_file: Option<String>,
pub max_redirect: Option<usize>,
pub no_proxy: Option<String>,
Expand Down Expand Up @@ -112,13 +113,28 @@ pub enum HttpVersion {
V3,
}

impl From<HttpVersion> for http::RequestedHttpVersion {
impl From<HttpVersion> for RequestedHttpVersion {
fn from(value: HttpVersion) -> Self {
match value {
HttpVersion::V10 => http::RequestedHttpVersion::Http10,
HttpVersion::V11 => http::RequestedHttpVersion::Http11,
HttpVersion::V2 => http::RequestedHttpVersion::Http2,
HttpVersion::V3 => http::RequestedHttpVersion::Http3,
HttpVersion::V10 => RequestedHttpVersion::Http10,
HttpVersion::V11 => RequestedHttpVersion::Http11,
HttpVersion::V2 => RequestedHttpVersion::Http2,
HttpVersion::V3 => RequestedHttpVersion::Http3,
}
}
}

#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum IpResolve {
IpV4,
IpV6,
}

impl From<IpResolve> for http::IpResolve {
fn from(value: IpResolve) -> Self {
match value {
IpResolve::IpV4 => http::IpResolve::IpV4,
IpResolve::IpV6 => http::IpResolve::IpV6,
}
}
}
Expand Down Expand Up @@ -174,6 +190,8 @@ pub fn parse() -> Result<Options, OptionsError> {
.arg(commands::input_files())
.arg(commands::insecure())
.arg(commands::interactive())
.arg(commands::ipv4())
.arg(commands::ipv6())
.arg(commands::json())
.arg(commands::max_redirects())
.arg(commands::max_time())
Expand Down Expand Up @@ -240,6 +258,7 @@ fn parse_matches(arg_matches: &ArgMatches) -> Result<Options, OptionsError> {
let input_files = matches::input_files(arg_matches)?;
let insecure = matches::insecure(arg_matches);
let interactive = matches::interactive(arg_matches);
let ip_resolve = matches::ip_resolve(arg_matches);
let junit_file = matches::junit_file(arg_matches);
let max_redirect = matches::max_redirect(arg_matches);
let no_proxy = matches::no_proxy(arg_matches);
Expand Down Expand Up @@ -284,6 +303,7 @@ fn parse_matches(arg_matches: &ArgMatches) -> Result<Options, OptionsError> {
input_files,
insecure,
interactive,
ip_resolve,
junit_file,
max_redirect,
no_proxy,
Expand Down Expand Up @@ -323,11 +343,15 @@ impl Options {
let client_key_file = self.client_key_file.clone();
let connects_to = self.connects_to.clone();
let follow_location = self.follow_location;
let insecure = self.insecure;
let http_version = match self.http_version {
Some(version) => version.into(),
None => RequestedHttpVersion::default(),
};
let ip_resolve = match self.ip_resolve {
Some(ip) => ip.into(),
None => http::IpResolve::default(),
};
let insecure = self.insecure;
let max_redirect = self.max_redirect;
let path_as_is = self.path_as_is;
let proxy = self.proxy.clone();
Expand Down Expand Up @@ -385,6 +409,7 @@ impl Options {
.http_version(http_version)
.ignore_asserts(ignore_asserts)
.insecure(insecure)
.ip_resolve(ip_resolve)
.max_redirect(max_redirect)
.no_proxy(no_proxy)
.path_as_is(path_as_is)
Expand Down
12 changes: 12 additions & 0 deletions packages/hurl/src/http/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,8 @@ impl Client {
}
self.handle.http_version(options.http_version.into())?;

self.handle.ip_resolve(options.ip_resolve.into())?;

// Activates the access of certificates info chain after a transfer has been executed.
self.handle.certinfo(true)?;

Expand Down Expand Up @@ -851,6 +853,16 @@ impl From<RequestedHttpVersion> for easy::HttpVersion {
}
}

impl From<IpResolve> for easy::IpResolve {
fn from(value: IpResolve) -> Self {
match value {
IpResolve::Default => easy::IpResolve::Any,
IpResolve::IpV4 => easy::IpResolve::V4,
IpResolve::IpV6 => easy::IpResolve::V6,
}
}
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down
1 change: 1 addition & 0 deletions packages/hurl/src/http/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ pub(crate) use self::core::{Cookie, Param, RequestCookie};
pub(crate) use self::error::HttpError;
pub use self::header::Header;
pub(crate) use self::options::{ClientOptions, Verbosity};
pub use self::request::IpResolve;
pub use self::request::Request;
pub use self::request::RequestedHttpVersion;
pub(crate) use self::request_spec::{Body, FileParam, Method, MultipartParam, RequestSpec};
Expand Down
62 changes: 37 additions & 25 deletions packages/hurl/src/http/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
*
*/
use crate::http::request::RequestedHttpVersion;
use crate::http::IpResolve;
use hurl_core::ast::Retry;
use std::time::Duration;

Expand All @@ -32,6 +33,7 @@ pub struct ClientOptions {
pub follow_location: bool,
pub http_version: RequestedHttpVersion,
pub insecure: bool,
pub ip_resolve: IpResolve,
pub max_redirect: Option<usize>,
pub no_proxy: Option<String>,
pub path_as_is: bool,
Expand Down Expand Up @@ -66,6 +68,7 @@ impl Default for ClientOptions {
follow_location: false,
http_version: RequestedHttpVersion::default(),
insecure: false,
ip_resolve: IpResolve::default(),
max_redirect: Some(50),
no_proxy: None,
path_as_is: false,
Expand Down Expand Up @@ -127,6 +130,12 @@ impl ClientOptions {
RequestedHttpVersion::Http2 => arguments.push("--http2".to_string()),
RequestedHttpVersion::Http3 => arguments.push("--http3".to_string()),
}
match self.ip_resolve {
IpResolve::Default => {}
IpResolve::IpV4 => arguments.push("--ipv4".to_string()),
IpResolve::IpV6 => arguments.push("--ipv6".to_string()),
}

if self.follow_location {
arguments.push("--location".to_string());
}
Expand Down Expand Up @@ -186,6 +195,7 @@ mod tests {
follow_location: true,
http_version: RequestedHttpVersion::Http10,
insecure: true,
ip_resolve: IpResolve::IpV6,
max_redirect: Some(10),
path_as_is: true,
proxy: Some("localhost:3128".to_string()),
Expand All @@ -203,32 +213,34 @@ mod tests {
}
.curl_args(),
[
"--compressed".to_string(),
"--connect-timeout".to_string(),
"20".to_string(),
"--connect-to".to_string(),
"example.com:443:host-47.example.com:443".to_string(),
"--cookie".to_string(),
"cookie_file".to_string(),
"--insecure".to_string(),
"--http1.0".to_string(),
"--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(),
"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(),
"'user:password'".to_string(),
"--user-agent".to_string(),
"'my-useragent'".to_string(),
"--compressed",
"--connect-timeout",
"20",
"--connect-to",
"example.com:443:host-47.example.com:443",
"--cookie",
"cookie_file",
"--http1.0",
"--insecure",
"--ipv6",
"--location",
"--max-redirs",
"10",
"--path-as-is",
"--proxy",
"'localhost:3128'",
"--resolve",
"foo.com:80:192.168.0.1",
"--resolve",
"bar.com:443:127.0.0.1",
"--timeout",
"10",
"--user",
"'user:password'",
"--user-agent",
"'my-useragent'",
]
.map(|a| a.to_string())
);
}
}
8 changes: 8 additions & 0 deletions packages/hurl/src/http/request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,14 @@ impl fmt::Display for RequestedHttpVersion {
}
}

#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)]
pub enum IpResolve {
#[default]
Default, // Default, can use addresses of all IP versions that your system allows.
IpV4,
IpV6,
}

impl Request {
/// Extracts query string params from the url of the request.
pub fn query_string_params(&self) -> Vec<Param> {
Expand Down
1 change: 1 addition & 0 deletions packages/hurl/src/runner/entry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,7 @@ impl ClientOptions {
cookie_input_file: runner_options.cookie_input_file.clone(),
follow_location: runner_options.follow_location,
http_version: runner_options.http_version,
ip_resolve: runner_options.ip_resolve,
max_redirect: runner_options.max_redirect,
path_as_is: runner_options.path_as_is,
proxy: runner_options.proxy.clone(),
Expand Down
16 changes: 15 additions & 1 deletion packages/hurl/src/runner/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
*
*/

use crate::http::RequestedHttpVersion;
use crate::http::{IpResolve, RequestedHttpVersion};
use crate::runner::template;
use crate::runner::{Error, RunnerOptions, Value};
use crate::util::logger::{Logger, Verbosity};
Expand Down Expand Up @@ -114,6 +114,20 @@ pub fn get_entry_options(
}
OptionKind::FollowLocation(value) => runner_options.follow_location = *value,
OptionKind::Insecure(value) => runner_options.insecure = *value,
OptionKind::IpV4(value) => {
if *value {
runner_options.ip_resolve = IpResolve::IpV4
} else {
runner_options.ip_resolve = IpResolve::IpV6
}
}
OptionKind::IpV6(value) => {
if *value {
runner_options.ip_resolve = IpResolve::IpV6
} else {
runner_options.ip_resolve = IpResolve::IpV4
}
}
OptionKind::MaxRedirect(value) => runner_options.max_redirect = Some(*value),
OptionKind::PathAsIs(value) => runner_options.path_as_is = *value,
OptionKind::Proxy(value) => runner_options.proxy = Some(value.clone()),
Expand Down
Loading

0 comments on commit c0157e2

Please sign in to comment.