From 80244451efc432d3c427c50aaa2888d66acdba20 Mon Sep 17 00:00:00 2001 From: Bibek Date: Mon, 23 Mar 2026 14:38:43 -0400 Subject: [PATCH 1/3] fix: skip Content-Length validation when Content-Encoding is set --- httpie/downloads.py | 11 ++++++++++- tests/test_downloads.py | 11 +++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/httpie/downloads.py b/httpie/downloads.py index 9c4b895e6f..57e2b722fa 100644 --- a/httpie/downloads.py +++ b/httpie/downloads.py @@ -218,8 +218,17 @@ def start( # FIXME: some servers still might sent Content-Encoding: gzip # + # Skip Content-Length validation when Content-Encoding is present. + # Per RFC 9110, Content-Length reflects the encoded (compressed) size, + # but the requests library transparently decompresses, making written + # bytes exceed Content-Length. Skip the check to match curl/browser behaviour. + # See: https://github.com/httpie/cli/issues/1642 try: - total_size = int(final_response.headers['Content-Length']) + content_encoding = final_response.headers.get('Content-Encoding') + if content_encoding: + total_size = None # cannot reliably compare compressed vs decompressed bytes + else: + total_size = int(final_response.headers['Content-Length']) except (KeyError, ValueError, TypeError): total_size = None diff --git a/tests/test_downloads.py b/tests/test_downloads.py index b646a0e6a5..558c840fa5 100644 --- a/tests/test_downloads.py +++ b/tests/test_downloads.py @@ -259,3 +259,14 @@ def test_download_with_redirect_original_url_used_for_filename(self, httpbin): assert os.listdir('.') == [expected_filename] finally: os.chdir(orig_cwd) + + def test_download_gzip_no_false_incomplete(httpbin): + #Regression test for https://github.com/httpie/cli/issues/1642 + #Content-Encoding: gzip must not trigger a false "Incomplete download" error. + r = http( + '--download', + httpbin + '/gzip', # returns gzip-compressed JSON + env=MockEnvironment(), + ) + assert 'Incomplete download' not in r.stderr + assert r.exit_status == ExitStatus.SUCCESS From 1f1679d576fd7b43c40b229cf2a84d1fe323fdcd Mon Sep 17 00:00:00 2001 From: Bibek Date: Mon, 23 Mar 2026 14:53:25 -0400 Subject: [PATCH 2/3] fix indents and skip Content-Length validation when Content-Encoding is set --- gzip.json | 1 + gzip.json-1 | 1 + tests/test_downloads.py | 14 +++++++++----- 3 files changed, 11 insertions(+), 5 deletions(-) create mode 100644 gzip.json create mode 100644 gzip.json-1 diff --git a/gzip.json b/gzip.json new file mode 100644 index 0000000000..23d42473dd --- /dev/null +++ b/gzip.json @@ -0,0 +1 @@ +{"gzipped":true,"headers":{"Accept":"*/*","Accept-Encoding":"identity","Connection":"keep-alive","Host":"127.0.0.1:57698","User-Agent":"HTTPie/3.2.4"},"method":"GET","origin":"127.0.0.1"} diff --git a/gzip.json-1 b/gzip.json-1 new file mode 100644 index 0000000000..5cf7126963 --- /dev/null +++ b/gzip.json-1 @@ -0,0 +1 @@ +{"gzipped":true,"headers":{"Accept":"*/*","Accept-Encoding":"identity","Connection":"keep-alive","Host":"127.0.0.1:57705","User-Agent":"HTTPie/3.2.4"},"method":"GET","origin":"127.0.0.1"} diff --git a/tests/test_downloads.py b/tests/test_downloads.py index 558c840fa5..61e090f9ec 100644 --- a/tests/test_downloads.py +++ b/tests/test_downloads.py @@ -12,6 +12,7 @@ parse_content_range, filename_from_content_disposition, filename_from_url, get_unique_filename, ContentRangeError, Downloader, PARTIAL_CONTENT ) +from httpie.status import ExitStatus from .utils import http, MockEnvironment @@ -259,14 +260,17 @@ def test_download_with_redirect_original_url_used_for_filename(self, httpbin): assert os.listdir('.') == [expected_filename] finally: os.chdir(orig_cwd) - - def test_download_gzip_no_false_incomplete(httpbin): - #Regression test for https://github.com/httpie/cli/issues/1642 - #Content-Encoding: gzip must not trigger a false "Incomplete download" error. + + def test_download_gzip_no_false_incomplete(self, httpbin): + # Regression test for https://github.com/httpie/cli/issues/1642 + # Skip Content-Length validation when Content-Encoding is present. + # Per RFC 9110, Content-Length reflects the encoded (compressed) size, + # but the requests library transparently decompresses, making written + # bytes exceed Content-Length. Skip the check to match curl/browser behaviour. r = http( '--download', httpbin + '/gzip', # returns gzip-compressed JSON env=MockEnvironment(), ) assert 'Incomplete download' not in r.stderr - assert r.exit_status == ExitStatus.SUCCESS + assert r.exit_status == ExitStatus.SUCCESS From 2af4eefb80bbcd475a7ab765b0217a552bb29097 Mon Sep 17 00:00:00 2001 From: Bibek Date: Mon, 23 Mar 2026 17:12:01 -0400 Subject: [PATCH 3/3] removed --- gzip.json | 1 - gzip.json-1 | 1 - 2 files changed, 2 deletions(-) delete mode 100644 gzip.json delete mode 100644 gzip.json-1 diff --git a/gzip.json b/gzip.json deleted file mode 100644 index 23d42473dd..0000000000 --- a/gzip.json +++ /dev/null @@ -1 +0,0 @@ -{"gzipped":true,"headers":{"Accept":"*/*","Accept-Encoding":"identity","Connection":"keep-alive","Host":"127.0.0.1:57698","User-Agent":"HTTPie/3.2.4"},"method":"GET","origin":"127.0.0.1"} diff --git a/gzip.json-1 b/gzip.json-1 deleted file mode 100644 index 5cf7126963..0000000000 --- a/gzip.json-1 +++ /dev/null @@ -1 +0,0 @@ -{"gzipped":true,"headers":{"Accept":"*/*","Accept-Encoding":"identity","Connection":"keep-alive","Host":"127.0.0.1:57705","User-Agent":"HTTPie/3.2.4"},"method":"GET","origin":"127.0.0.1"}