Skip to content

RecordingClient breaks requests with non-streaming request bodies (HTTP 400 from ELB) #4495

@signadou

Description

@signadou

Summary

aws_smithy_http_client::test_util::dvr::RecordingClient can cause real AWS SDK requests with non-streaming request bodies to fail (observed as 400 Bad Request from awselb/2.0). The same request succeeds when using the standard client without RecordingClient.

Reproduction

  • Use RecordingClient as the SDK HTTP client (via aws_config::defaults(...).http_client(recording_client.clone())).
  • Call an operation with a small JSON request body (non-streaming; SdkBody::bytes() is available), e.g. Bedrock Runtime converse_stream.

Observed failure:

  • Response is 400 Bad Request with content-type: text/html from awselb/2.0
  • The request is correctly signed and has Content-Length, etc.
  • If request body recording is disabled (i.e., don't swap the request body), the request succeeds.

Root cause

RecordingClient records request bodies by replacing the body with a channel-backed streaming body and spawning a task to forward data from the original body into that channel. For non-streaming request bodies, this channel-based approach can cause timing/framing issues (particularly with HTTP/2), leading to malformed requests on the wire.

Proposed fix

For non-streaming request bodies (SdkBody::bytes() returns Some), buffer the bytes up front:

  1. Copy the bytes.
  2. Record them into the DVR event stream.
  3. Replace the request body with a new SdkBody created from the same bytes.

For streaming request bodies, keep the existing channel-based path.

Additionally, preserve size hints for the channel-backed body by passing through content_length() to keep the replacement body's size_hint() consistent with Content-Length.

Notes

  • Response body recording continues to use the channel-based approach (works fine since we're consuming, not producing).
  • This is in test-util DVR code; no change to production client stacks.

I've already fixed the issue locally and I'll be making a PR momentarily.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions