[SOLVED] Passing Stream from ReadAsStreamAsync as API response and avoiding large in memory objects

Issue

I’m working with two API’s which are being used to download large documents from an external system.

I call API 1 with some information about a document to be retrieved, API 1 then calls API 2 with that same information acting as a ‘middleman’. API 1 gets document binary back which it then passes along to the original caller.

What I’d like to avoid is creating large in memory objects representing these documents and just pass a stream as the response all the way through, is this possible with the described setup (‘middleman’ API)?

Basic example of what I have (in API 1):

var request = new HttpRequestMessage(HttpMethod.Post, "https://example.com/api/getdocs");
request.Content = parameters;

// 'ResponseHeadersRead' - indicate completion on reading of headers and not entire response...
var response = await httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead);

if (response.StatusCode == HttpStatusCode.OK)
{
     var stream = await response.Content.ReadAsStreamAsync();

     return this.Ok(stream);
}
else
{
    // Logging/error handling...
}

The response from this is being read as a stream by the original caller and then passed to a FileStreamResult which downloads the document on the client machine.

My question(s):

  • Is the var stream = await response.Content.ReadAsStreamAsync() line still creating a large in memory object representing the document?
  • Does the above stream need to be disposed or will the underlying HttpContent take care of cleaning up the stream? If it does need to be disposed, how can I do this whilst still passing a stream response to the caller? (is that even possible?)

Solution

The stream which you are passing to Ok should not be disposed. Ok will wrap the stream and feed it back as a response to the client, so if you dispose it then the response will fail.

Technically speaking, at least according to IDisposable mantra, you should dispose response. However, doing so will cause it to dispose the stream also, and therefore you should not dispose response either.

Don’t fear that it will cause anything to leak: HttpResponseMessage does not dispose anything other than the content stream. See the below source code:

     protected virtual void Dispose(bool disposing)
     {
         // The reason for this type to implement IDisposable is that it contains instances of types that implement
         // IDisposable (content). 
         if (disposing && !disposed)
         {
             disposed = true;
             if (content != null)
             {
                 content.Dispose();
             }
         }
     }

You should however dispose request with a using block, although that also just disposes the content, in your case parameters.

Answered By – Charlieface

Answer Checked By – Terry (BugsFixing Volunteer)

Leave a Reply

Your email address will not be published. Required fields are marked *