Co-authored-by: crazywoola <427733928@qq.com>
This commit is contained in:
@@ -277,6 +277,22 @@ class Executor:
|
|||||||
elif self.auth.config.type == "custom":
|
elif self.auth.config.type == "custom":
|
||||||
headers[authorization.config.header] = authorization.config.api_key or ""
|
headers[authorization.config.header] = authorization.config.api_key or ""
|
||||||
|
|
||||||
|
# Handle Content-Type for multipart/form-data requests
|
||||||
|
# Fix for issue #22880: Missing boundary when using multipart/form-data
|
||||||
|
body = self.node_data.body
|
||||||
|
if body and body.type == "form-data":
|
||||||
|
# For multipart/form-data with files, let httpx handle the boundary automatically
|
||||||
|
# by not setting Content-Type header when files are present
|
||||||
|
if not self.files or all(f[0] == "__multipart_placeholder__" for f in self.files):
|
||||||
|
# Only set Content-Type when there are no actual files
|
||||||
|
# This ensures httpx generates the correct boundary
|
||||||
|
if "content-type" not in (k.lower() for k in headers):
|
||||||
|
headers["Content-Type"] = "multipart/form-data"
|
||||||
|
elif body and body.type in BODY_TYPE_TO_CONTENT_TYPE:
|
||||||
|
# Set Content-Type for other body types
|
||||||
|
if "content-type" not in (k.lower() for k in headers):
|
||||||
|
headers["Content-Type"] = BODY_TYPE_TO_CONTENT_TYPE[body.type]
|
||||||
|
|
||||||
return headers
|
return headers
|
||||||
|
|
||||||
def _validate_and_parse_response(self, response: httpx.Response) -> Response:
|
def _validate_and_parse_response(self, response: httpx.Response) -> Response:
|
||||||
@@ -384,15 +400,24 @@ class Executor:
|
|||||||
# '__multipart_placeholder__' is inserted to force multipart encoding but is not a real file.
|
# '__multipart_placeholder__' is inserted to force multipart encoding but is not a real file.
|
||||||
# This prevents logging meaningless placeholder entries.
|
# This prevents logging meaningless placeholder entries.
|
||||||
if self.files and not all(f[0] == "__multipart_placeholder__" for f in self.files):
|
if self.files and not all(f[0] == "__multipart_placeholder__" for f in self.files):
|
||||||
for key, (filename, content, mime_type) in self.files:
|
for file_entry in self.files:
|
||||||
|
# file_entry should be (key, (filename, content, mime_type)), but handle edge cases
|
||||||
|
if len(file_entry) != 2 or not isinstance(file_entry[1], tuple) or len(file_entry[1]) < 2:
|
||||||
|
continue # skip malformed entries
|
||||||
|
key = file_entry[0]
|
||||||
|
content = file_entry[1][1]
|
||||||
body_string += f"--{boundary}\r\n"
|
body_string += f"--{boundary}\r\n"
|
||||||
body_string += f'Content-Disposition: form-data; name="{key}"\r\n\r\n'
|
body_string += f'Content-Disposition: form-data; name="{key}"\r\n\r\n'
|
||||||
# decode content
|
# decode content safely
|
||||||
try:
|
if isinstance(content, bytes):
|
||||||
body_string += content.decode("utf-8")
|
try:
|
||||||
except UnicodeDecodeError:
|
body_string += content.decode("utf-8")
|
||||||
# fix: decode binary content
|
except UnicodeDecodeError:
|
||||||
pass
|
body_string += content.decode("utf-8", errors="replace")
|
||||||
|
elif isinstance(content, str):
|
||||||
|
body_string += content
|
||||||
|
else:
|
||||||
|
body_string += f"[Unsupported content type: {type(content).__name__}]"
|
||||||
body_string += "\r\n"
|
body_string += "\r\n"
|
||||||
body_string += f"--{boundary}--\r\n"
|
body_string += f"--{boundary}--\r\n"
|
||||||
elif self.node_data.body:
|
elif self.node_data.body:
|
||||||
|
Reference in New Issue
Block a user