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:
- Copy the bytes.
- Record them into the DVR event stream.
- 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.
Summary
aws_smithy_http_client::test_util::dvr::RecordingClientcan cause real AWS SDK requests with non-streaming request bodies to fail (observed as400 Bad Requestfromawselb/2.0). The same request succeeds when using the standard client withoutRecordingClient.Reproduction
RecordingClientas the SDK HTTP client (viaaws_config::defaults(...).http_client(recording_client.clone())).SdkBody::bytes()is available), e.g. Bedrock Runtimeconverse_stream.Observed failure:
400 Bad Requestwithcontent-type: text/htmlfromawselb/2.0Content-Length, etc.Root cause
RecordingClientrecords 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()returnsSome), buffer the bytes up front:SdkBodycreated 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'ssize_hint()consistent withContent-Length.Notes
I've already fixed the issue locally and I'll be making a PR momentarily.