4

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?)
DGibbs
  • 14,316
  • 7
  • 44
  • 83
  • Relevant: https://stackoverflow.com/questions/38083206/not-calling-dispose-on-httprequestmessage-and-httpresponsemessage-in-asp-net-cor Basically, as you can see from [the source code](https://github.com/microsoft/referencesource/blob/5697c29004a34d80acdaf5742d7e699022c64ecd/System/net/System/Net/Http/HttpRequestMessage.cs#L200), the only thing `HttpRequestMessage.Dispose` does is dispose its `Content` stream, which ASP.Net will do for you if you pass it to `Ok` – Charlieface Mar 15 '22 at 09:22
  • @Charlieface That's brilliant, exactly what I wanted to know. – DGibbs Mar 18 '22 at 08:43

2 Answers2

1

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.

Charlieface
  • 52,284
  • 6
  • 19
  • 43
0
  1. FileStreamResult reads bytes from an input stream to a buffer (wich is 4kb by default) and writes them to an output stream. So no large in memory object should be created.
  2. Input stream read process in FileStreamResult is wrapped in using statement. It will be disposed correctly.
  • Thanks for your answer - I know that `FileStreamResult` will dispose of the stream itself internally but my question is specifically about the stream created in the 'middleman' API layer. Does `HttpContent` handle cleaning up of its own resources? If it doesn't, how can I safely dispose of this stream whilst still returning it? – DGibbs Mar 15 '22 at 08:10