fix: let httpx set the multipart boundary Content-Type
Merge https://github.com/google/adk-python/pull/6393 PiperOrigin-RevId: 951002160
This commit is contained in:
committed by
Copybara-Service
parent
08d5e50866
commit
3367b5bf72
@@ -454,7 +454,11 @@ class RestApiTool(BaseTool):
|
||||
elif mime_type == "text/plain":
|
||||
body_kwargs["data"] = body_data
|
||||
|
||||
if mime_type:
|
||||
# For multipart/form-data the Content-Type is left unset so httpx can
|
||||
# generate it from the `files` payload together with the required
|
||||
# boundary parameter. Forcing a boundary-less header here would make the
|
||||
# request body unparsable by the server.
|
||||
if mime_type and mime_type != "multipart/form-data":
|
||||
header_params["Content-Type"] = mime_type
|
||||
break # Process only the first mime_type
|
||||
|
||||
|
||||
@@ -673,7 +673,59 @@ class TestRestApiTool:
|
||||
request_params = tool._prepare_request_params(params, kwargs)
|
||||
|
||||
assert request_params["files"] == {"file1": b"file_content"}
|
||||
assert request_params["headers"]["Content-Type"] == "multipart/form-data"
|
||||
# For multipart/form-data the boundary-bearing Content-Type must be set by
|
||||
# httpx (from the `files` payload), not forced here. Forcing a boundary-less
|
||||
# "multipart/form-data" header would override the boundary httpx generates
|
||||
# and make the request body unparsable.
|
||||
assert "Content-Type" not in request_params["headers"]
|
||||
|
||||
def test_prepare_request_params_multipart_content_type_has_boundary(
|
||||
self, sample_endpoint, sample_auth_credential, sample_auth_scheme
|
||||
):
|
||||
mock_operation = Operation(
|
||||
operationId="test_op",
|
||||
requestBody=RequestBody(
|
||||
content={
|
||||
"multipart/form-data": MediaType(
|
||||
schema=OpenAPISchema(
|
||||
type="object",
|
||||
properties={
|
||||
"file1": OpenAPISchema(
|
||||
type="string", format="binary"
|
||||
)
|
||||
},
|
||||
)
|
||||
)
|
||||
}
|
||||
),
|
||||
)
|
||||
tool = RestApiTool(
|
||||
name="test_tool",
|
||||
description="test",
|
||||
endpoint=sample_endpoint,
|
||||
operation=mock_operation,
|
||||
auth_credential=sample_auth_credential,
|
||||
auth_scheme=sample_auth_scheme,
|
||||
)
|
||||
params = [
|
||||
ApiParameter(
|
||||
original_name="file1",
|
||||
py_name="file1",
|
||||
param_location="body",
|
||||
param_schema=OpenAPISchema(type="string", format="binary"),
|
||||
)
|
||||
]
|
||||
kwargs = {"file1": b"file_content"}
|
||||
|
||||
request_params = tool._prepare_request_params(params, kwargs)
|
||||
|
||||
# Build the request httpx would actually send and assert the wire
|
||||
# Content-Type carries the multipart boundary that matches the body.
|
||||
request = httpx.Client().build_request(**request_params)
|
||||
content_type = request.headers["Content-Type"]
|
||||
assert content_type.startswith("multipart/form-data; boundary=")
|
||||
boundary = content_type.split("boundary=", 1)[1]
|
||||
assert request.read().startswith(f"--{boundary}".encode())
|
||||
|
||||
def test_prepare_request_params_octet_stream(
|
||||
self, sample_endpoint, sample_auth_scheme, sample_auth_credential
|
||||
|
||||
Reference in New Issue
Block a user