diff --git a/README.md b/README.md index fa34730194b..32afab238c4 100644 --- a/README.md +++ b/README.md @@ -1490,7 +1490,7 @@ to configure Hurl, there are three sources from the lowest priority (most easily | --path-as-is | Tell Hurl to not handle sequences of /../ or /./ in the given URL path. Normally Hurl will squash or merge them according to standards but with this option set you tell it not to do that.
| | --pinnedpubkey <HASHES> | When negotiating a TLS or SSL connection, the server sends a certificate indicating its identity. A public key is extracted from this certificate and if it does not exactly match the public key provided to this option, Hurl aborts the connection before sending or receiving any data.
| | -x, --proxy <[PROTOCOL://]HOST[:PORT]> | Use the specified proxy.

Environment variables: http_proxy https_proxy all_proxy
| -| --proxy-header <HEADER> | Extra header to include in the request when sending to a proxy. These headers are only applied when a proxy is involved; if Hurl connects directly to the target server, they are ignored.

This is a cli-only option.
| +| --proxy-header <HEADER> | Extra header to include in the request when sending to a proxy. These headers are only applied when a proxy is involved; if Hurl connects directly to the target server, they are ignored.

Environment variables: HURL_PROXY_HEADER='name1:value1|name2:value2' (headers are separated by |)

This is a cli-only option.
| | --resolve <HOST:PORT:ADDR> | Provide a custom address for a specific host and port pair. Using this, you can make the Hurl requests(s) use a specified address and prevent the otherwise normally resolved address to be used. Consider it a sort of /etc/hosts alternative provided on the command line.
| | --ssl-no-revoke | (Windows) This option tells Hurl to disable certificate revocation checks. WARNING: this option loosens the SSL security, and by using this flag you ask for exactly that.

This is a cli-only option.
| | --unix-socket <PATH> | (HTTP) Connect through this Unix domain socket, instead of using the network.
| diff --git a/docs/manual.md b/docs/manual.md index 0baeab149fe..b6d1ad470cf 100644 --- a/docs/manual.md +++ b/docs/manual.md @@ -190,7 +190,7 @@ to configure Hurl, there are three sources from the lowest priority (most easily | --path-as-is | Tell Hurl to not handle sequences of /../ or /./ in the given URL path. Normally Hurl will squash or merge them according to standards but with this option set you tell it not to do that.
| | --pinnedpubkey <HASHES> | When negotiating a TLS or SSL connection, the server sends a certificate indicating its identity. A public key is extracted from this certificate and if it does not exactly match the public key provided to this option, Hurl aborts the connection before sending or receiving any data.
| | -x, --proxy <[PROTOCOL://]HOST[:PORT]> | Use the specified proxy.

Environment variables: http_proxy https_proxy all_proxy
| -| --proxy-header <HEADER> | Extra header to include in the request when sending to a proxy. These headers are only applied when a proxy is involved; if Hurl connects directly to the target server, they are ignored.

This is a cli-only option.
| +| --proxy-header <HEADER> | Extra header to include in the request when sending to a proxy. These headers are only applied when a proxy is involved; if Hurl connects directly to the target server, they are ignored.

Environment variables: HURL_PROXY_HEADER='name1:value1|name2:value2' (headers are separated by |)

This is a cli-only option.
| | --resolve <HOST:PORT:ADDR> | Provide a custom address for a specific host and port pair. Using this, you can make the Hurl requests(s) use a specified address and prevent the otherwise normally resolved address to be used. Consider it a sort of /etc/hosts alternative provided on the command line.
| | --ssl-no-revoke | (Windows) This option tells Hurl to disable certificate revocation checks. WARNING: this option loosens the SSL security, and by using this flag you ask for exactly that.

This is a cli-only option.
| | --unix-socket <PATH> | (HTTP) Connect through this Unix domain socket, instead of using the network.
| diff --git a/docs/manual/hurl.1 b/docs/manual/hurl.1 index 5f8b37d3e84..3c6888f5668 100644 --- a/docs/manual/hurl.1 +++ b/docs/manual/hurl.1 @@ -1,4 +1,4 @@ -.TH hurl 1 "20 Jun 2026" "hurl 8.1.0" " Hurl Manual" +.TH hurl 1 "21 Jun 2026" "hurl 8.1.0" " Hurl Manual" .SH NAME hurl - run and test HTTP requests. @@ -334,6 +334,8 @@ Environment variables: http_proxy https_proxy all_proxy Extra header to include in the request when sending to a proxy. These headers are only applied when a proxy is involved; if Hurl connects directly to the target server, they are ignored. +Environment variables: HURL_PROXY_HEADER='name1:value1|name2:value2' (headers are separated by |) + This is a cli-only option. .IP "--resolve " diff --git a/docs/manual/hurl.md b/docs/manual/hurl.md index e647ab79b41..7ce19cec6d5 100644 --- a/docs/manual/hurl.md +++ b/docs/manual/hurl.md @@ -349,6 +349,8 @@ Environment variables: http_proxy https_proxy all_proxy Extra header to include in the request when sending to a proxy. These headers are only applied when a proxy is involved; if Hurl connects directly to the target server, they are ignored. +Environment variables: HURL_PROXY_HEADER='name1:value1|name2:value2' (headers are separated by |) + This is a cli-only option. #### --resolve {#resolve} diff --git a/docs/spec/options/hurl/proxy_header.option b/docs/spec/options/hurl/proxy_header.option index 10db095a252..be2cba717b2 100644 --- a/docs/spec/options/hurl/proxy_header.option +++ b/docs/spec/options/hurl/proxy_header.option @@ -5,5 +5,6 @@ help: Extra header to include in the request when sending to a proxy help_heading: HTTP options multi: append cli_only: true +env_var: HURL_PROXY_HEADER='name1:value1|name2:value2' (headers are separated by |) --- Extra header to include in the request when sending to a proxy. These headers are only applied when a proxy is involved; if Hurl connects directly to the target server, they are ignored. diff --git a/integration/hurl/tests_ssl/cacert_with_proxy_header_env_var.err.pattern b/integration/hurl/tests_ssl/cacert_with_proxy_header_env_var.err.pattern new file mode 100644 index 00000000000..93141fb1ee8 --- /dev/null +++ b/integration/hurl/tests_ssl/cacert_with_proxy_header_env_var.err.pattern @@ -0,0 +1,21 @@ +> CONNECT 127.0.0.1:8002 HTTP/1.1 +> Host: 127.0.0.1:8002 +> Proxy-Connection: Keep-Alive +> X-TO-PROXY-1: to-proxy-1 +> X-TO-PROXY-2: to-proxy-2 +> +< HTTP/1.1 200 Connection established +< +> GET /hello HTTP/1.1 +> Host: 127.0.0.1:8002 +> Accept: */* +> X-TO-SERVER: to-server +> User-Agent: hurl/<<<.*>>> +> +< HTTP/1.1 200 OK +< Server: Werkzeug/<<<.*?>>> Python/<<<.*>>> +< Date: <<<.*>>> +< Content-Type: text/html; charset=utf-8 +< Content-Length: 12 +< Connection: close +< diff --git a/integration/hurl/tests_ssl/cacert_with_proxy_header_env_var.out b/integration/hurl/tests_ssl/cacert_with_proxy_header_env_var.out new file mode 100644 index 00000000000..c57eff55ebc --- /dev/null +++ b/integration/hurl/tests_ssl/cacert_with_proxy_header_env_var.out @@ -0,0 +1 @@ +Hello World! \ No newline at end of file diff --git a/integration/hurl/tests_ssl/cacert_with_proxy_header_env_var.ps1 b/integration/hurl/tests_ssl/cacert_with_proxy_header_env_var.ps1 new file mode 100644 index 00000000000..5c5e35e1b82 --- /dev/null +++ b/integration/hurl/tests_ssl/cacert_with_proxy_header_env_var.ps1 @@ -0,0 +1,11 @@ +Set-StrictMode -Version latest +$ErrorActionPreference = 'Stop' + +# Does not work without --ssl-no-revoke +# $env:HURL_PROXY_HEADER = 'X-TO-PROXY-1: to-proxy-1|X-TO-PROXY-2: to-proxy-2' +# hurl --verbosity brief ` +# --proxy http://127.0.0.1:3128 ` +# --header X-TO-SERVER:to-server ` +# --cacert tests_ssl/certs/server/cert.pem ` +# tests_ssl/cacert_with_proxy.hurl +exit 255 diff --git a/integration/hurl/tests_ssl/cacert_with_proxy_header_env_var.sh b/integration/hurl/tests_ssl/cacert_with_proxy_header_env_var.sh new file mode 100755 index 00000000000..133b20842a5 --- /dev/null +++ b/integration/hurl/tests_ssl/cacert_with_proxy_header_env_var.sh @@ -0,0 +1,9 @@ +#!/bin/bash +set -Eeuo pipefail + +export HURL_PROXY_HEADER="X-TO-PROXY-1: to-proxy-1|X-TO-PROXY-2: to-proxy-2" +hurl --verbosity brief \ + --proxy http://127.0.0.1:3128 \ + --header X-TO-SERVER:to-server \ + --cacert tests_ssl/certs/server/cert.pem \ + tests_ssl/cacert_with_proxy.hurl diff --git a/packages/hurl/README.md b/packages/hurl/README.md index 46edffca9ec..495eb22d24c 100644 --- a/packages/hurl/README.md +++ b/packages/hurl/README.md @@ -1490,7 +1490,7 @@ to configure Hurl, there are three sources from the lowest priority (most easily | --path-as-is | Tell Hurl to not handle sequences of /../ or /./ in the given URL path. Normally Hurl will squash or merge them according to standards but with this option set you tell it not to do that.
| | --pinnedpubkey <HASHES> | When negotiating a TLS or SSL connection, the server sends a certificate indicating its identity. A public key is extracted from this certificate and if it does not exactly match the public key provided to this option, Hurl aborts the connection before sending or receiving any data.
| | -x, --proxy <[PROTOCOL://]HOST[:PORT]> | Use the specified proxy.

Environment variables: http_proxy https_proxy all_proxy
| -| --proxy-header <HEADER> | Extra header to include in the request when sending to a proxy. These headers are only applied when a proxy is involved; if Hurl connects directly to the target server, they are ignored.

This is a cli-only option.
| +| --proxy-header <HEADER> | Extra header to include in the request when sending to a proxy. These headers are only applied when a proxy is involved; if Hurl connects directly to the target server, they are ignored.

Environment variables: HURL_PROXY_HEADER='name1:value1|name2:value2' (headers are separated by |)

This is a cli-only option.
| | --resolve <HOST:PORT:ADDR> | Provide a custom address for a specific host and port pair. Using this, you can make the Hurl requests(s) use a specified address and prevent the otherwise normally resolved address to be used. Consider it a sort of /etc/hosts alternative provided on the command line.
| | --ssl-no-revoke | (Windows) This option tells Hurl to disable certificate revocation checks. WARNING: this option loosens the SSL security, and by using this flag you ask for exactly that.

This is a cli-only option.
| | --unix-socket <PATH> | (HTTP) Connect through this Unix domain socket, instead of using the network.
| diff --git a/packages/hurl/src/cli/options/context.rs b/packages/hurl/src/cli/options/context.rs index 9fe44660858..6af178800b5 100644 --- a/packages/hurl/src/cli/options/context.rs +++ b/packages/hurl/src/cli/options/context.rs @@ -76,6 +76,7 @@ pub const HURL_NO_JSONPATH_COERCION: &str = "HURL_NO_JSONPATH_COERCION"; pub const HURL_NO_OUTPUT: &str = "HURL_NO_OUTPUT"; pub const HURL_NO_PRETTY: &str = "HURL_NO_PRETTY"; pub const HURL_PRETTY: &str = "HURL_PRETTY"; +pub const HURL_PROXY_HEADER: &str = "HURL_PROXY_HEADER"; pub const HURL_RETRY: &str = "HURL_RETRY"; pub const HURL_RETRY_INTERVAL: &str = "HURL_RETRY_INTERVAL"; pub const HURL_SECRET_PREFIX: &str = "HURL_SECRET_"; @@ -295,6 +296,13 @@ impl RunContext { self.get_env_var_bool(HURL_PRETTY) } + /// Returns the Hurl proxy headers injected by environment variables. + pub fn proxy_header_env_var(&self) -> Option<&str> { + self.hurl_env_vars + .get(HURL_PROXY_HEADER) + .map(|v| v.as_str()) + } + /// Returns the env var for retry count. pub fn retry_env_var(&self) -> Option<&str> { self.hurl_env_vars.get(HURL_RETRY).map(|v| v.as_str()) diff --git a/packages/hurl/src/cli/options/env_vars.rs b/packages/hurl/src/cli/options/env_vars.rs index 1b423b23091..18f1635c035 100644 --- a/packages/hurl/src/cli/options/env_vars.rs +++ b/packages/hurl/src/cli/options/env_vars.rs @@ -27,8 +27,8 @@ use hurl_core::types::{BytesPerSec, Count, DurationUnit}; use super::context::{ HURL_CONNECT_TIMEOUT, HURL_DELAY, HURL_ERROR_FORMAT, HURL_FOLLOW_LOCATION, HURL_FOLLOW_LOCATION_TRUSTED, HURL_HEADER, HURL_JOBS, HURL_LIMIT_RATE, HURL_MAX_FILESIZE, - HURL_MAX_REDIRS, HURL_MAX_TIME, HURL_NO_HEADER, HURL_RETRY, HURL_RETRY_INTERVAL, - HURL_VERBOSITY, + HURL_MAX_REDIRS, HURL_MAX_TIME, HURL_NO_HEADER, HURL_PROXY_HEADER, HURL_RETRY, + HURL_RETRY_INTERVAL, HURL_VERBOSITY, }; use super::variables::TypeKind; use super::{ @@ -124,7 +124,7 @@ fn headers( let headers = header.split("|").map(|h| h.to_string()).collect::>(); for h in &headers { if !h.contains(':') { - let msg = format!("Invalid header <{h}> missing `:`"); + let msg = format!("Invalid header <{h}>, missing `:`"); return Err(err_from_cli_err(CliOptionsError::Error(msg), HURL_HEADER)); } } @@ -263,6 +263,30 @@ fn no_headers( Ok(all_no_headers) } +fn proxy_headers( + context: &RunContext, + default_value: Vec, +) -> Result, CliOptionsError> { + let mut all_proxy_headers = default_value; + if let Some(proxy_header) = context.proxy_header_env_var() { + let proxy_headers = proxy_header + .split("|") + .map(|h| h.to_string()) + .collect::>(); + for h in &proxy_headers { + if !h.contains(':') { + let msg = format!("Invalid proxy header <{h}>, missing `:`"); + return Err(err_from_cli_err( + CliOptionsError::Error(msg), + HURL_PROXY_HEADER, + )); + } + } + all_proxy_headers.extend(proxy_headers); + } + Ok(all_proxy_headers) +} + fn no_jsonpath_coercion(context: &RunContext, default_value: bool) -> bool { context .no_jsonpath_coercion_env_var() @@ -410,6 +434,7 @@ pub fn parse_env_vars( let parallel = parallel(context, default_options.parallel); let pretty = pretty(context, default_options.pretty); let progress_bar = progress_bar(context, default_options.progress_bar); + let proxy_headers = proxy_headers(context, default_options.proxy_headers)?; let retry = retry(context, default_options.retry)?; let retry_interval = retry_interval(context, default_options.retry_interval)?; let secrets = secrets(context, default_options.secrets)?; @@ -447,6 +472,7 @@ pub fn parse_env_vars( parallel, pretty, progress_bar, + proxy_headers, retry, retry_interval, secrets,