From 8bfca83e4820d0d9b09a4b27f99127289fea9e80 Mon Sep 17 00:00:00 2001 From: Synvoya <16019863+Synvoya@users.noreply.github.com> Date: Sat, 11 Jul 2026 21:49:22 +1000 Subject: [PATCH] Honor multipart boundary from an explicit Content-Type header When a multipart boundary was supplied only via a Content-Type header (no --boundary flag), HTTPie declared that boundary in the header but wrote the body with a different random boundary, producing an unparseable request. Reconcile the encoder's boundary with the one declared in the header; --boundary still takes precedence. --- httpie/uploads.py | 8 ++++++++ tests/test_uploads.py | 18 ++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/httpie/uploads.py b/httpie/uploads.py index 4a993b3a25..7659d98f76 100644 --- a/httpie/uploads.py +++ b/httpie/uploads.py @@ -233,6 +233,14 @@ def get_multipart_data_and_content_type( content_type: str = None, ) -> Tuple['MultipartEncoder', str]: from requests_toolbelt import MultipartEncoder + from .utils import parse_content_type_header + + if not boundary and content_type: + # Honor a boundary specified via an explicit `Content-Type` header + # (e.g. `Content-Type:multipart/form-data; boundary=xoxo`) so that the + # body delimiter matches the boundary declared in the header. + _, ct_params = parse_content_type_header(content_type) + boundary = ct_params.get('boundary') encoder = MultipartEncoder( fields=data.items(), diff --git a/tests/test_uploads.py b/tests/test_uploads.py index e6bb80ac70..40ba4ecf7a 100644 --- a/tests/test_uploads.py +++ b/tests/test_uploads.py @@ -300,6 +300,24 @@ def test_multipart_custom_content_type_boundary_preserved(self, httpbin): assert f'multipart/magic; boundary={boundary_in_header}' in r assert r.count(boundary_in_body) == 3 + def test_multipart_boundary_from_content_type_without_flag(self, httpbin): + # A boundary specified only via an explicit Content-Type header + # (no --boundary) must be used for the body delimiter too. + boundary = 'HTTPIE_FTW' + r = http( + '--print=HB', + '--check-status', + '--multipart', + httpbin + '/post', + f'Content-Type: multipart/form-data; boundary={boundary}', + 'AAAA=AAA', + 'BBB=BBB', + ) + assert f'multipart/form-data; boundary={boundary}' in r + # 1 in the header + 3 body delimiters (two parts + closing). Without the + # fix the body uses a random boundary and this count is 1. + assert r.count(boundary) == 4 + def test_multipart_chunked(self, httpbin_with_chunked_support): r = http( '--verbose',