Skip to content

fix(multipart): omit nil header string values and support additionalValuesToMerge - #2220

Open
Hardik K (hardikkaurani) wants to merge 3 commits into
microsoft:mainfrom
hardikkaurani:fix/multipart-omit-nil-headers-and-additional-values
Open

Hardik K (hardikkaurani) wants to merge 3 commits into
microsoft:mainfrom
hardikkaurani:fix/multipart-omit-nil-headers-and-additional-values

Conversation

@hardikkaurani

Copy link
Copy Markdown
Contributor

Summary of Changes

  1. Omit Nil Header String Values in writeStringValue:

    • In writeStringValue(key, value), if key != null && !key.isEmpty() and value == null, it previously serialized key + "\r\n" without a colon or value, creating a malformed MIME header line that violates RFC 2046 and RFC 7230/5322 syntax.
    • Now checks if (key != null && !key.isEmpty() && value == null), returning immediately so that headers with null values are cleanly omitted, matching the Kiota serialization convention across SDKs.
    • Preserves delimiter emission when key == null || key.isEmpty() and value == null ("\r\n").
  2. Support additionalValuesToMerge in writeObjectValue:

    • Previously writeObjectValue validated Objects.requireNonNull(additionalValuesToMerge) but completely ignored the array without serializing any additional MultipartBody parts.
    • Now filters non-null additional values and iterates through them, verifying type (MultipartBody), invoking lifecycle callbacks (onBeforeObjectSerialization, onStartObjectSerialization, onAfterObjectSerialization), and serializing each part (consistent with FormSerializationWriter and JsonSerializationWriter).
  3. Unit Tests Added in MultipartSerializationWriterTests:

    • writesStringValueWithKeyValue: verifies standard header serialization (Content-Type: text/plain).
    • writeStringValueWithNilValueOmitsHeader: verifies headers with null values are omitted.
    • writeStringValueWithEmptyKeyAndNilValueWritesDelimiter: verifies delimiter emission (\r\n).
    • writeStringValueWithEmptyKeyAndValueWritesRawLine: verifies raw line serialization.
    • writesAStructuredObjectWithAdditionalValuesToMerge: verifies merging multiple MultipartBody parts.
    • throwsOnInvalidAdditionalValuesToMerge: asserts runtime exception for non-MultipartBody instances in additional values.

@hardikkaurani
Hardik K (hardikkaurani) requested a review from a team as a code owner October 25, 2026 04:51
Copilot AI balanced review requested due to automatic review settings October 25, 2026 04:51

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has insufficient remaining quota.

@hardikkaurani

Copy link
Copy Markdown
Contributor Author

Hi Vincent Biret (@baywet),

I've opened this PR in components/serialization/multipart to:

  • Omit headers when a key is provided with a null value in writeStringValue(), preventing malformed header lines without colon/value (Header\r\n).
  • Support additionalValuesToMerge in writeObjectValue() by iterating over and serializing additional MultipartBody parts with lifecycle callbacks (aligning with FormSerializationWriter and JsonSerializationWriter).

Unit tests covering header omission, additional values merging, and invalid part validation have been added, and spotlessCheck / test suites pass. Whenever convenient, could you please take a look? Thanks for your time!

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Additional bodies are appended after the primary closing boundary, causing MIME parsers to ignore their parts instead of merging them.

3 open issues

Review effort: Balanced

Copilot AI balanced review requested due to automatic review settings October 25, 2026 14:49

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has insufficient remaining quota.

@hardikkaurani

Copy link
Copy Markdown
Contributor Author

Hi Vincent Biret (@baywet),

I have updated the PR in commit \29b7efa0\ to address the review recommendations:

  1. Shared Multipart Boundary Envelope: \writeObjectValue\ now serializes all parts from merged \MultipartBody\ instances under a single shared boundary (derived from the root body) and suppresses intermediate closing boundaries, ensuring only a single terminating delimiter (----) is emitted so parts are not treated as MIME epilogue.
  2. Android Compatibility in Tests: Replaced \InputStream.readAllBytes()\ with \Compatibility.readAllBytes()\ to support Android API levels < 33.
  3. Isolated Validation & Boundary-Aware Assertions: Separated \�dditionalValuesToMerge\ validation testing with explicit exception message assertions, and added boundary-aware assertions verifying the full multi-part stream and absence of secondary boundaries.

Local Gradle test suites and \spotlessCheck\ pass cleanly. Whenever convenient, please take another look. Thank you!

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Boundary rewriting also affects string payloads, which can discard content or prematurely close the multipart message.

1 open issue
3 resolved since last review

Review effort: Balanced

Comment on lines +46 to +50
if (value.equals("--" + currentTargetBoundary)) {
valueToWrite = "--" + currentReplacementBoundary;
} else if (value.equals("--" + currentTargetBoundary + "--")) {
if (isLastBody) {
valueToWrite = "--" + currentReplacementBoundary + "--";

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Resolved: Introduced an explicit FramingState state machine in MultipartSerializationWriter to track MIME framing phases (EXPECTING_FIRST_BOUNDARY, IN_HEADERS, EXPECTING_PAYLOAD, EXPECTING_DELIMITER_AFTER_PAYLOAD, EXPECTING_BOUNDARY_OR_CLOSING, and FINISHED). Boundary rewriting and intermediate closing delimiter suppression are now strictly scoped to structural framing delimiters (EXPECTING_FIRST_BOUNDARY and EXPECTING_BOUNDARY_OR_CLOSING), ensuring string payloads matching boundary or delimiter patterns are emitted untouched as message content. Added regression test preservesStringPayloadMatchingBoundaryOrClosingDelimiterWhenMerging in MultipartSerializationWriterTests.

Copilot AI balanced review requested due to automatic review settings October 26, 2026 21:38

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has insufficient remaining quota.

@hardikkaurani

Copy link
Copy Markdown
Contributor Author

Hi Vincent Biret (@baywet),

I've pushed an update in commit 720ecebb addressing the review feedback:

  • Framing-Scoped Boundary Rewriting: Introduced a FramingState enum in MultipartSerializationWriter to track MIME framing transitions (EXPECTING_FIRST_BOUNDARY, IN_HEADERS, EXPECTING_PAYLOAD, EXPECTING_DELIMITER_AFTER_PAYLOAD, EXPECTING_BOUNDARY_OR_CLOSING, and FINISHED). Boundary rewriting and intermediate closing delimiter suppression are now strictly restricted to structural framing delimiters (EXPECTING_FIRST_BOUNDARY and EXPECTING_BOUNDARY_OR_CLOSING). This ensures that string payloads matching boundary or delimiter patterns (sent via writeStringValue("", payload)) are emitted untouched without triggering unintended delimiter suppression or premature stream termination.
  • Regression Testing: Added preservesStringPayloadMatchingBoundaryOrClosingDelimiterWhenMerging in MultipartSerializationWriterTests to verify that string payloads matching body closing delimiters are fully preserved in both non-final and final bodies.
  • All tests, spotlessCheck, and spotbugs pass cleanly.

Whenever convenient, please take another look. Thank you!

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

2 participants