From 400ae664bba12a2522ea464e2374dfb0d469344a Mon Sep 17 00:00:00 2001 From: teawoong Kim <116135174+ultramancode@users.noreply.github.com> Date: Wed, 28 May 2025 18:04:38 +0900 Subject: [PATCH] fix(http): force multipart/form-data even without files(#20322) (#20323) --- api/core/workflow/nodes/http_request/executor.py | 9 ++++++++- .../nodes/http_request/test_http_request_executor.py | 4 +++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/api/core/workflow/nodes/http_request/executor.py b/api/core/workflow/nodes/http_request/executor.py index 2c42f5a1be..e28ac6343b 100644 --- a/api/core/workflow/nodes/http_request/executor.py +++ b/api/core/workflow/nodes/http_request/executor.py @@ -235,6 +235,10 @@ class Executor: files[key].append(file_tuple) # convert files to list for httpx request + # If there are no actual files, we still need to force httpx to use `multipart/form-data`. + # This is achieved by inserting a harmless placeholder file that will be ignored by the server. + if not files: + self.files = [("__multipart_placeholder__", ("", b"", "application/octet-stream"))] if files: self.files = [] for key, file_tuples in files.items(): @@ -373,7 +377,10 @@ class Executor: raw += f"{k}: {v}\r\n" body_string = "" - if self.files: + # Only log actual files if present. + # '__multipart_placeholder__' is inserted to force multipart encoding but is not a real file. + # This prevents logging meaningless placeholder entries. + if self.files and not all(f[0] == "__multipart_placeholder__" for f in self.files): for key, (filename, content, mime_type) in self.files: body_string += f"--{boundary}\r\n" body_string += f'Content-Disposition: form-data; name="{key}"\r\n\r\n' diff --git a/api/tests/unit_tests/core/workflow/nodes/http_request/test_http_request_executor.py b/api/tests/unit_tests/core/workflow/nodes/http_request/test_http_request_executor.py index 58b910e17b..d066fc1e33 100644 --- a/api/tests/unit_tests/core/workflow/nodes/http_request/test_http_request_executor.py +++ b/api/tests/unit_tests/core/workflow/nodes/http_request/test_http_request_executor.py @@ -246,7 +246,9 @@ def test_executor_with_form_data(): assert "multipart/form-data" in executor.headers["Content-Type"] assert executor.params == [] assert executor.json is None - assert executor.files is None + # '__multipart_placeholder__' is expected when no file inputs exist, + # to ensure the request is treated as multipart/form-data by the backend. + assert executor.files == [("__multipart_placeholder__", ("", b"", "application/octet-stream"))] assert executor.content is None # Check that the form data is correctly loaded in executor.data