diff --git a/CHANGELOG.md b/CHANGELOG.md index dd200332..bb7b8797 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ This project adheres to [Semantic Versioning](https://semver.org/). - Added support for HTTP/2, and HTTP/3 protocols. ([#523](https://github.com/httpie/cli/issues/523), [#692](https://github.com/httpie/cli/issues/692), [#1531](https://github.com/httpie/cli/pull/1531)) - Added support for early (informational) responses. ([#752](https://github.com/httpie/cli/issues/752)) ([#1531](https://github.com/httpie/cli/pull/1531)) - Added support for IPv4/IPv6 enforcement with `-6` and `-4`. ([#94](https://github.com/httpie/cli/issues/94), [#1531](https://github.com/httpie/cli/pull/1531)) +- Added support for Happy Eyeballs algorithm via `--heb` flag (disabled by default). [#1599](https://github.com/httpie/cli/issues/1599) [#1531](https://github.com/httpie/cli/pull/1531) - Added support for alternative DNS resolvers via `--resolver`. DNS over HTTPS, DNS over TLS, DNS over QUIC, and DNS over UDP are accepted. ([#99](https://github.com/httpie/cli/issues/99), [#1531](https://github.com/httpie/cli/pull/1531)) - Added support for binding to a specific network adapter with `--interface`. ([#1422](https://github.com/httpie/cli/issues/1422), [#1531](https://github.com/httpie/cli/pull/1531)) - Added support for specifying the local port with `--local-port`. ([#1456](https://github.com/httpie/cli/issues/1456), [#1531](https://github.com/httpie/cli/pull/1531)) diff --git a/docs/README.md b/docs/README.md index cddb8621..8d339be8 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1970,6 +1970,17 @@ You can specify multiple entries, concatenated with a comma: $ https --resolver "pie.dev:10.10.4.1,re.pie.dev:10.10.8.1" pie.dev/get ``` +## Happy Eyeballs + +By default, when HTTPie establish the connection it asks for the IP(v4 or v6) records of +the requested domain and then tries them sequentially preferring IPv6 by default. This +may induce longer connection delays and in some case hangs due to an unresponsive endpoint. +To concurrently try to connect to available IP(v4 or v6), set the following flag: + +```bash +$ https --heb pie.dev/get +``` + ## Network interface In order to bind emitted request from a specific network adapter you can use the `--interface` flag. diff --git a/httpie/cli/definition.py b/httpie/cli/definition.py index 2f6bf55c..44061593 100644 --- a/httpie/cli/definition.py +++ b/httpie/cli/definition.py @@ -864,6 +864,20 @@ network.add_argument( """ ) +network.add_argument( + "--heb", + default=False, + dest="happy_eyeballs", + action="store_true", + short_help="Establish the connection using IETF Happy Eyeballs algorithm", + help=""" + By default, when HTTPie establish the connection it asks for the IP(v4 or v6) records of + the requested domain and then tries them sequentially preferring IPv6 by default. This + may induce longer connection delays and in some case hangs due to an unresponsive endpoint. + To concurrently try to connect to available IP(v4 or v6), set this flag. + + """ +) network.add_argument( "--resolver", default=[], diff --git a/httpie/client.py b/httpie/client.py index a771ac54..63c5bd72 100644 --- a/httpie/client.py +++ b/httpie/client.py @@ -118,6 +118,7 @@ def collect_messages( disable_ipv4=args.ipv6, source_address=source_address, quic_cache=env.config.quic_file, + happy_eyeballs=args.happy_eyeballs, ) if args.disable_http3 is False and args.force_http3 is True: @@ -237,6 +238,7 @@ def build_requests_session( disable_ipv6: bool = False, source_address: typing.Tuple[str, int] = None, quic_cache: typing.Optional[Path] = None, + happy_eyeballs: bool = False, ) -> niquests.Session: requests_session = niquests.Session() @@ -260,6 +262,8 @@ def build_requests_session( source_address=source_address, disable_http1=disable_http1, disable_http2=disable_http2, + disable_http3=disable_http3, + happy_eyeballs=happy_eyeballs, ) https_adapter = HTTPieHTTPSAdapter( ciphers=ciphers, @@ -276,6 +280,7 @@ def build_requests_session( disable_ipv6=disable_ipv6, source_address=source_address, quic_cache_layer=requests_session.quic_cache_layer, + happy_eyeballs=happy_eyeballs, ) requests_session.mount('http://', http_adapter) requests_session.mount('https://', https_adapter) diff --git a/tests/test_network.py b/tests/test_network.py index 0be34d04..44804700 100644 --- a/tests/test_network.py +++ b/tests/test_network.py @@ -36,3 +36,13 @@ def test_ensure_interface_and_port_parameters(httpbin): assert r.exit_status == 0 assert HTTP_OK in r + + +def test_happy_eyeballs(remote_httpbin_secure): + r = http( + "--heb", # this will automatically and concurrently try IPv6 and IPv4 endpoints + remote_httpbin_secure + "/get", + ) + + assert r.exit_status == 0 + assert HTTP_OK in r