Skip to content

ClientModel: Change approach to buffering response.Content #41080

@annelo-msft

Description

@annelo-msft

Problem Description

The APIs we have today in Azure.Core around Response.Content and Response.ContentStream and buffering are not transparent. It is not clear to the end-user which responses are buffered and which are not. The only way for users to discover this is to read the docs for a service method.

High-level proposal

Make a change to the current implementation of the PipelineResponse.Content property such that we no longer throw if the response has not been buffered (i.e. PipelineMessage.BufferResponse has been set to false by the client-author). This means that end-users can always access PipelineResponse.Content and if it is not buffered, it will buffer the response in the response.Content field. In the rare situation where response.Content is larger than what we can fit in memory, the user will either get an IOException or -- since we will have wrapped the live network stream in a ReadTimeoutStream -- the buffering will throw a TaskCanceledException due to the network timeout being applied.

Implementation proposal

(From @KrzysztofCwalina and under investigation per feasibility in #41693 and related PRs)

Could we do the following?

In the case of unbuffered responses:

  1. Service method creates, composes, and sends an instance of HttpMessage.
  2. The transport stashed HttpRequestMessage in the Response
  3. Service method returns Response to the user.
  4. If the user accesses Content first, we read the live stream, buffer into byte[], and return BinaryData wrapping the byte[]. If a subsequent call accesses ContentStream we either throw or return MemoryStream wrapping the byte[]
  5. If the user accesses ContentStream first, any subsequent access to Content or ContentStream will throw InvalidOperationException

In the case of buffered responses:

  1. Service method creates, composes, and sends an instance of HttpMessage.
  2. The transport reads the live stream into a byte[], stashes the byte[] in Response, disposes the HttpRequestMessage
  3. Service method returns Response to the user.
  4. If the caller accesses Content, we return BinaryData wrapping the byte[]
  5. If the caller accesses ContetnStream, we return MemoryStream wrapping the byte[]

Prior notes

See this comment thread for details: #41016 (comment)

This issue tracks the work to change how response buffering works for ClientModel responses. @KrzysztofCwalina would like to optimize for buffered content in the BinaryData response.Content property and not retain the MemoryStream we used to buffer the content in the ContentStream property. In a greenfield scenario, this would be great, but with a lot of code built on top of Azure.Core's response.ContentStream property and the idea that Content throws if ContentStream doesn't hold the buffered MemoryStream, we would likely need to go through breaking change review with the arch board to assess the feasibility of this change.

A proposed implementation that helps identify the CI tests that are affected by this change can be found here: #41047

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions