feat: add multipart/form-data request body#146
Conversation
Add MultipartBody, a multipart/form-data (RFC 7578) RequestBody for form and file uploads, plus MultipartBody.Part and a builder. - A single shared frame function (frameOf/closingDelimiter) backs both writeTo and contentLength, so the declared length cannot drift from the bytes actually written. - File parts stream off disk via FileRequestBody; value parts are encoded through the Serde SPI at construction time, keeping sdk-core free of any embedded serializer. - mediaType() reports multipart/form-data with the generated boundary parameter; the boundary is a random token, overridable on the builder. - The body is replayable when every part body is replayable, and falls back to buffering otherwise. Part names and filenames are escaped so a value can never break the header framing.
|
This adds a IssuesUser-supplied boundary isn't validated for CR/LF or File parts omit |
Adds
MultipartBody, amultipart/form-data(RFC 7578)RequestBodyfor form and file uploads, tosdk-core.What
MultipartBodyextendsRequestBody;MultipartBody.Partmodels a single field (Content-Dispositionfrom name + optional filename, optionalContent-Type, and a body). AMultipartBody.Builder(implementingBuilder<MultipartBody>) assembles parts and generates a boundary.mediaType()returnsmultipart/form-data; boundary=<token>. The boundary is a random token by default (dexpace-prefix + 32 token chars) and is overridable viabuilder().boundary(...).FileRequestBody(so a transport that recognisesFileRequestBodycan still take its zero-copy path for the file region). Value parts are encoded through theSerdeSPI at construction time, so no serializer is needed at send time andsdk-corekeeps zero embedded-serialization deps (no Jackson in core).toReplayablebuffering. Part names and filenames are escaped (", CR, LF percent-encoded) so a value can never break the header framing.Frame size cannot drift
writeToandcontentLengthboth go through a single shared frame function (frameOffor each part plusclosingDelimiter).contentLengthsums exactly the bytes those functions produce, so the declared length and the bytes actually written are computed from the same source and cannot diverge. A test assertscontentLengthequals the byte count produced bywriteTofor value-only and mixed file+value bodies.Tests
MultipartBodyTestcovers: boundary generation/uniqueness, media-type shape, exactcontentLength == writeTobyte count (value and mixed file parts), unknown-length propagation, full multi-part wire-format round trip, file-partfilename/Content-Typeemission, single closing delimiter, name/filename escaping, replayability of all-replayable bodies, buffering fallback for a non-replayable part, and Serde-driven value parts.Gated build
Run from the worktree with the module-scoped gates:
Result: BUILD SUCCESSFUL (all green).
:sdk-core:apiDumpwas run and the regeneratedsdk-core/api/sdk-core.apiis committed.Closes #61