fix(openapi): let httpx set the multipart boundary Content-Type#6393
Open
anxkhn wants to merge 1 commit into
Open
fix(openapi): let httpx set the multipart boundary Content-Type#6393anxkhn wants to merge 1 commit into
anxkhn wants to merge 1 commit into
Conversation
For multipart/form-data request bodies, RestApiTool passed files= to httpx (which builds a boundary-delimited body and generates a matching Content-Type: multipart/form-data; boundary=... header) but also forced header_params["Content-Type"] = "multipart/form-data". The forced, boundary-less header overrode httpx's generated one, so the sent request advertised no boundary while the body used one, leaving RFC 7578 servers unable to parse the parts. Skip setting Content-Type for the multipart/form-data branch so httpx emits the boundary-bearing header. Other media types (json, x-www-form-urlencoded, octet-stream, text/plain) are unchanged. Add a regression test that builds the httpx request and asserts the wire Content-Type carries the boundary matching the body delimiter, and update the existing multipart test that previously asserted the boundary-less header. Signed-off-by: Anas Khan <83116240+anxkhn@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Please ensure you have read the contribution guide before creating a pull request.
Link to Issue or Description of Change
1. Link to an existing issue (if applicable):
2. Or, if no issue exists, describe the change:
Problem:
RestApiToolsends everymultipart/form-datarequest body with a boundary-lessContent-Type: multipart/form-dataheader, so the request cannot be parsed by thereceiving server.
In
_prepare_request_params(
src/google/adk/tools/openapi_tool/openapi_spec_parser/rest_api_tool.py) amultipart body is passed to httpx via
body_kwargs["files"] = body_data. Whenfiles=is used, httpx builds a boundary-delimited body and generates a matchingContent-Type: multipart/form-data; boundary=<...>header. The method thenunconditionally runs
header_params["Content-Type"] = mime_type, which overrideshttpx's generated header with a plain
multipart/form-datathat has noboundaryparameter. The sent request therefore advertises no boundary while the body uses
one, so an RFC 7578 server cannot find the part delimiters (typically a 400 or
empty/dropped fields). Every multipart upload tool generated from an OpenAPI spec
is affected.
Reproduced on the pinned httpx 0.28.1: the outgoing request body starts with
--<hex>\r\n...but theContent-Typeheader is exactlymultipart/form-datawith no
boundary=.Solution:
Skip forcing
Content-Typeonly for themultipart/form-databranch, so httpxemits the correct boundary-bearing header it derives from the
filespayload. Allother media types (
application/json,application/x-www-form-urlencoded,application/octet-stream,text/plain) keep settingContent-Typeexactly asbefore.
This is the minimal fix: one guard condition plus an explanatory comment. No other
behavior changes.
Testing Plan
Unit Tests:
Changes in
tests/unittests/tools/openapi_tool/openapi_spec_parser/test_rest_api_tool.py:test_prepare_request_params_multipart_content_type_has_boundary: buildsthe request httpx would actually send
(
httpx.Client().build_request(**request_params)) and asserts the wireContent-Typestarts withmultipart/form-data; boundary=and that the requestbody begins with the matching
--<boundary>delimiter. This is a wire-levelregression test; the previous test only inspected the intermediate dict and so
masked the bug.
test_prepare_request_params_multipart: it previouslyasserted the boundary-less header (
== "multipart/form-data"); it now assertsContent-Typeis not forced by the tool ("Content-Type" not in headers).Effectiveness (red/green): reverting the source guard back to
if mime_type:makesboth multipart tests fail with the exact bug signature
(
"multipart/form-data".startswith("multipart/form-data; boundary=")isFalse);restoring the guard makes them pass.
pytestsummary (httpx 0.28.1):Manual End-to-End (E2E) Tests:
Minimal reproduction of the wire behavior on the pinned httpx:
Checklist
Additional context
Scope is intentionally tiny: one guard condition and a comment in
rest_api_tool.py, plus the two test updates above.Files changed
src/google/adk/tools/openapi_tool/openapi_spec_parser/rest_api_tool.py(+5 / -1: the
!= "multipart/form-data"guard + 4-line explanatory comment)tests/unittests/tools/openapi_tool/openapi_spec_parser/test_rest_api_tool.py(+53 / -1: new wire-level regression test; corrected the masking assertion)
httpxwas already imported in the test file (no new import).