Consider ASP.NET Core's ResponseBufferingMiddleware:
public class ResponseBufferingMiddleware
{
private readonly RequestDelegate _next;
public ResponseBufferingMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext httpContext)
{
var originalResponseBody = httpContext.Response.Body;
// no-op if buffering is already available.
if (originalResponseBody.CanSeek)
{
await _next(httpContext);
return;
}
var originalBufferingFeature = httpContext.Features.Get<IHttpBufferingFeature>();
var originalSendFileFeature = httpContext.Features.Get<IHttpSendFileFeature>();
try
{
// Shim the response stream
var bufferStream = new BufferingWriteStream(originalResponseBody);
httpContext.Response.Body = bufferStream;
httpContext.Features.Set<IHttpBufferingFeature>(new HttpBufferingFeature(bufferStream, originalBufferingFeature));
if (originalSendFileFeature != null)
{
httpContext.Features.Set<IHttpSendFileFeature>(new SendFileFeatureWrapper(originalSendFileFeature, bufferStream));
}
await _next(httpContext);
// If we're still buffered, set the content-length header and flush the buffer.
// Only if the content-length header is not already set, and some content was buffered.
if (!httpContext.Response.HasStarted && bufferStream.CanSeek && bufferStream.Length > 0)
{
if (!httpContext.Response.ContentLength.HasValue)
{
httpContext.Response.ContentLength = bufferStream.Length;
}
await bufferStream.FlushAsync();
}
}
finally
{
// undo everything
httpContext.Features.Set(originalBufferingFeature);
httpContext.Features.Set(originalSendFileFeature);
httpContext.Response.Body = originalResponseBody;
}
}
}
Link to actual file: https://github.com/aspnet/BasicMiddleware/blob/dev/src/Microsoft.AspNetCore.Buffering/ResponseBufferingMiddleware.cs
Why is this not a memory leak? They create a new BufferingWriteStream (https://github.com/aspnet/BasicMiddleware/blob/dev/src/Microsoft.AspNetCore.Buffering/BufferingWriteStream.cs), but never dispose of it. Does the HTTP Pipeline automatically dispose of streams after a finished request or something?
This stream is just a wrapper around other stream you pass in its constructor (originalResponseBody in this case). As such - there is no need to dispose it - by itself it does not contain any unmanaged resources it can clear\release, nor does it contain any other disposable components it should dispose, so there is no reason for it to implement IDisposable, but it inherits from class that already implements it. It just proxies all Stream methods (like Read and so on) to the wrapped stream with some additional logic (buffering).
Related
After trawling the internet for hours, I'm lost on how to solve my problem for ASP.NET Core 2.x.
I am generating a CSV on the fly (which can take several minutes) and then trying to send that back to the client. Lots of clients are timing out before I start sending a response, so I am trying to stream the file back to them (with an immediate 200 response) and write to the stream asynchronously. It seemed like this was possible with PushStreamContent previously in ASP, but I'm unsure how to structure my code so the CSV generation is done asynchronously and returning an HTTP response immediately.
[HttpGet("csv")]
public async Task<FileStreamResult> GetCSV(long id)
{
// this stage can take 2+ mins, which obviously blocks the response
var data = await GetData(id);
var records = _csvGenerator.GenerateRecords(data);
// using the CsvHelper Nuget package
var stream = new MemoryStream();
var writer = new StreamWriter(stream);
var csv = new CsvWriter(writer);
csv.WriteRecords(stream, records);
await writer.FlushAsync();
return new FileStreamResult(stream, new MediaTypeHeaderValue("text/csv))
{
FileDownloadName = "results.csv"
};
}
If you make a request to this controller method, you'll get nothing until the whole CSV has finished generating and then you finally get a response, by which point most client requests have timed out.
I've tried wrapping the CSV generation code in a Task.Run() but that has not helped my issue either.
There isn't a PushStreamContext kind of type built-in to ASP.NET Core. You can, however, build your own FileCallbackResult which does the same thing. This example code should do it:
public class FileCallbackResult : FileResult
{
private Func<Stream, ActionContext, Task> _callback;
public FileCallbackResult(MediaTypeHeaderValue contentType, Func<Stream, ActionContext, Task> callback)
: base(contentType?.ToString())
{
if (callback == null)
throw new ArgumentNullException(nameof(callback));
_callback = callback;
}
public override Task ExecuteResultAsync(ActionContext context)
{
if (context == null)
throw new ArgumentNullException(nameof(context));
var executor = new FileCallbackResultExecutor(context.HttpContext.RequestServices.GetRequiredService<ILoggerFactory>());
return executor.ExecuteAsync(context, this);
}
private sealed class FileCallbackResultExecutor : FileResultExecutorBase
{
public FileCallbackResultExecutor(ILoggerFactory loggerFactory)
: base(CreateLogger<FileCallbackResultExecutor>(loggerFactory))
{
}
public Task ExecuteAsync(ActionContext context, FileCallbackResult result)
{
SetHeadersAndLog(context, result, null);
return result._callback(context.HttpContext.Response.Body, context);
}
}
}
Usage:
[HttpGet("csv")]
public IActionResult GetCSV(long id)
{
return new FileCallbackResult(new MediaTypeHeaderValue("text/csv"), async (outputStream, _) =>
{
var data = await GetData(id);
var records = _csvGenerator.GenerateRecords(data);
var writer = new StreamWriter(outputStream);
var csv = new CsvWriter(writer);
csv.WriteRecords(stream, records);
await writer.FlushAsync();
})
{
FileDownloadName = "results.csv"
};
}
Bear in mind that FileCallbackResult has the same limitations as PushStreamContext: that if an error occurs in the callback, the web server has no good way of notifying the client of that error. All you can do is propagate the exception, which will cause ASP.NET to clamp the connection shut early, so clients get a "connection unexpectedly closed" or "download aborted" error. This is because HTTP sends the error code first, in the header, before the body starts streaming.
If document generation takes 2+ minutes it should be asynchronous. It could be like this:
client sends request to generate document
you accept request, start generation in background and reply with message like generation has been started, we will notify you
on client you periodically check whether document is ready and get the link finally
You also can do it with signalr. Steps are the same but it's not needed for client to check the status of document. You can push the link when document is completed.
I want to implement simple video file streaming.
There is my API controller:
[HttpGet]
[Route("api/VideoContent")]
public HttpResponseMessage GetVideoContent([FromUri] string fileName)
{
if (fileName == null)
{
return new HttpResponseMessage(HttpStatusCode.BadRequest);
}
if (Request.Headers.Range != null)
{
try
{
//using (FileStream fileStream = _videoFileProvider.GetFileStream(fileName))
//{
HttpResponseMessage partialResponse = Request.CreateResponse(HttpStatusCode.PartialContent);
FileStream fileStream = _videoFileProvider.GetFileStream(fileName);
partialResponse.Content = new ByteRangeStreamContent(fileStream, Request.Headers.Range, new MediaTypeHeaderValue("video/mp4"));
return partialResponse;
//}
}
catch (Exception)
{
return new HttpResponseMessage(HttpStatusCode.InternalServerError);
}
}
return new HttpResponseMessage(HttpStatusCode.RequestedRangeNotSatisfiable);
}
This code is working, but as you see fileStream not disposed. I tried to use using block (commented lines), but this code doesn't work - in debug mode method run without exceptions, but browser shows response with 500 error code.
Where is my mistake? Why I am getting 500 Internal Server Error? How to correctly dispose file stream in my case?
AFAIK, what have you implemented for downloading content without disposing filestream is right.
As you have been using HttpResponseMessage for returning response which is automatically disposed by the framework itself after done with sending response to the client.
This has already been pointed by MSFT guy in comment of another post
If you looks at dispose method of HttpResponseMessage in 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 can see _content has been disposed which is of type HttpContent i.e. in your case, object of ByteRangeStreamContent set in the Content property of HttpResponseMessage.
Disposing ByteRangeStreamContent object implemented in following way :
protected override void Dispose(bool disposing)
{
Contract.Assert(_byteRangeContent != null);
if (disposing)
{
if (!_disposed)
{
_byteRangeContent.Dispose();
_content.Dispose();
_disposed = true;
}
}
base.Dispose(disposing);
}
In above Dispose method of ByteRangeStreamContent, you can see that it is disposing itself and disposing _content(in your case FileStream) as well which is stream used for creating ByteRangeStreamContent object.
I strongly believe, your implementation without disposing filestream is correct as disposing starts in sequence when done with sending response to client.
I have an application that downloads massive amounts of pdfs from the web. From time to time, I get a HttpRequestException, contaning the message: Error while copying content to a stream.
So, I am trying to unit test my code to handle this situation. My current code for downloading is:
var request = await httpClient.GetAsync(url);
// This is where the exception would be thrown
await request.Content.ReadAsByteArrayAsync());
Now I am trying to simulate a HttpRequestException, so that I can unit test the code above, but I dont know how to do it. Can anyone please help?
Thanks in advance!
The key here is creating an HttpContent that throws an exception:
public class ExceptionThrowingContent : HttpContent
{
private readonly Exception exception;
public ExceptionThrowingContent(Exception exception)
{
this.exception = exception;
}
protected override Task SerializeToStreamAsync(Stream stream, TransportContext context)
{
return Task.FromException(exception);
}
protected override bool TryComputeLength(out long length)
{
length = 0L;
return false;
}
}
Once you have that, you can use something like my own mockhttp to mock the request:
var handler = new MockHttpMessageHandler();
handler.When("http://tempuri.org/url")
.Respond(new ExceptionThrowingContent(ex));
var mockClient = new HttpClient(handler);
// pass mockHandler to your component
Now, if your component passes in HttpCompletionOption.ResponseHeadersRead when it makes the request, the exception will be thrown at await Content.ReadAsByteArrayAsync(). If not, HttpClient will attempt to buffer the response so the exception will be thrown at await HttpClient.GetAsync().
If the response is being buffered, it's realistically "impossible" for an exception to be thrown at ReadAsByteArrayAsync so there's no point in attempting to simulate it. ("impossible" outside an OutOfMemoryException)
With MockHttp it is easy setup HttpClient tests in a fluent manner you can return a customized HttpContent that throws a HttpRequestException like in this example.
[TestMethod]
[ExpectedException(typeof(HttpRequestException))]
public async Task Test()
{
var content = new ContentWithException();
var mockHttp = new MockHttpMessageHandler();
mockHttp.Expect("http://localhost/mypdfDownload").Respond(content);
var client = new HttpClient(mockHttp);
var response = await client.GetAsync("http://localhost/mypdfDownload");
await response.Content.ReadAsByteArrayAsync();
}
private class ContentWithException : HttpContent
{
protected override Task SerializeToStreamAsync(Stream stream, TransportContext context)
{
throw new HttpRequestException();
}
protected override bool TryComputeLength(out long length)
{
length = 0;
return false;
}
}
I have a webapi that returns some protobuf-net serialized items after being transformed from a database column. The number of items can be rather large so I'd like to avoid materializing them all in memory and rather stream them out however, I never get a single one out before my Stream throws an already disposed exception. If I materialize the list it does work but I'd like to avoid it.
Here is what I'm doing::
private override async Task<IEnumerable<MyObj>> GetRawData(int id)
{
using(var sqlConn = GetOpenConnection())
{
using (var sqlReader =(await sqlConn.ExecuteReaderAsync(
_workingQuery,new {id = id}, 60)
.ConfigureAwait(false) as SqlDataReader))
{
await sqlReader.ReadAsync().ConfigureAwait(false);
using (var stream = sqlReader.GetStream(0))
{
return _serializerModel.DeserializeItems<MyObj>(stream, PrefixStyle.Base128, 1)
}
}
}
}
private async Task<IHttpActionResult> TransformData(int id)
{
var data=await GetRawData().ConfigureAwait(false);
return Ok(new {Data=data.Where(m=>...).Select(m=>...)})
}
[HttpGet,Route("api/myroute/{id}"]
public async Task<IHttpActionResult> GetObjs(int id)
{
return await TransformData();
}
However, I end getting an error about reading a disposed stream. How can I avoid this error?
The long and short of it is that you are returning a non-enumerated sequence, and closing (disposing) everything it needs before it ever gets to the caller. You're going to need to either:
enumerate the sequence eagerly (buffer in memory) - for example, adding .ToList()
restructure the code so that nothing is disposed until the end of the iteration
For the latter, I would be tempted to use an iterator block for the central part (after all the await). Something like:
bool dispose = true;
SqlConnection conn = null;
//...others
try {
conn = ...
...await...
var result = InnerMethod(conn, stream, ..., )
// ^^^ everything now known / obtained from DB
dispose = false; // handing lifetime management to the inner method
return result;
} finally {
if(dispose) {
using(conn) {}
//.//
}
}
IEnumerable<...> Inner(...) { // not async
using (conn)
using (stream) {
foreach(var item in ...) {
yield return item;
}
}
}
This line
return _serializerModel.DeserializeItems<MyObj>(stream, PrefixStyle.Base128, 1)
Returns just an indexer. As you seem to be aware the result is not yet materialized. However, right after the return, the using block disposes your Stream.
The solution would be to have GetRawData return the Stream. Then, inside TransformData, inside a using block for the Stream, you deserialize, filter and return the results.
The second potential issue is that your current approach processes the whole result and then sends it to the client all at once.
In order to have Web API send a streamed result you need to use HttpResponseMessage, and pass a Stream to it's Content property.
Here's an example: http://weblogs.asp.net/andresv/asynchronous-streaming-in-asp-net-webapi
When you want to stream a Stream to another Stream (I know, it sounds funny), you need to keep the ReaderStream open until the WriterStream is done writing. This is a high level representation:
using (ReaderStream)
{
using (WriterStream)
{
// write object from ReaderStream to WriterStream
}
}
As mentioned in a couple other posts (see References below) I am attempting to create response filters in order to modify content being produced by another web application.
I have the basic string transformation logic working and encapsulated into Filters that derive from a common FilterBase. However, the logic must operate on the full content, not chunks of content. Therefore I need to cache the chunks as they are written and perform the filter when all the writes are completed.
As shown below I created a new ResponseFilter derived from MemoryStream. On Write, the content is cached to another MemoryStream. On Flush, the full content, now in the MemoryStream is converted to a string and the Filter logic kicks in. The modified content is then written back out to the originating stream.
However, on every second request (basically when a new Filter is instantiated over the previous one) the previous filter's Flush method is being executed. At this point the the application crashes on the _outputStream.Write() method as the _cachedStream is empty.
The order of event is as follows:
First Request
Write method is called
Flush method is called
Close method is called
Close method is called
At this point the app returns and the proper content is displayed.
Second Request
Flush method is called
Application crashes on _outputStream.Write. ArgumentOutOfRangeException (offset).
Continue through crash (w/ in Visual Studio)
Close method is called
There are a couple of questions I have:
Why is Close called twice?
Why is Flush called after Closed was called?
To Jay's point below, Flush may be called before the stream is completely read, where should the filter logic reside? In Close? In Flush but with "if closing"?
What is the proper implementation for a Response Filter that works on the entire content at once?
Note: I experience the exact same behavior (minus Close events) if I do not override the Close method.
public class ResponseFilter : MemoryStream
{
private readonly Stream _outputStream;
private MemoryStream _cachedStream = new MemoryStream(1024);
private readonly FilterBase _filter;
public ResponseFilter (Stream outputStream, FilterBase filter)
{
_outputStream = outputStream;
_filter = filter;
}
// Flush is called on the second, fourth, and so on, page request (second request) with empty content.
public override void Flush()
{
Encoding encoding = HttpContext.Current.Response.ContentEncoding;
string cachedContent = encoding.GetString(_cachedStream.ToArray());
// Filter the cached content
cachedContent = _filter.Filter(cachedContent);
byte[] buffer = encoding.GetBytes(cachedContent);
_cachedStream = new MemoryStream();
_cachedStream.Write(buffer, 0, buffer.Length);
// Write new content to stream
_outputStream.Write(_cachedStream.ToArray(), 0, (int)_cachedStream.Length);
_cachedStream.SetLength(0);
_outputStream.Flush();
}
// Write is called on the first, third, and so on, page request.
public override void Write(byte[] buffer, int offset, int count)
{
// Cache the content.
_cachedStream.Write(buffer, 0, count);
}
public override void Close()
{
_outputStream.Close();
}
}
// Example usage in a custom HTTP Module on the BeginRequest event.
FilterBase transformFilter = new MapServiceJsonResponseFilter();
response.Filter = new ResponseFilter(response.Filter, transformFilter);
References:
How do I deploy a managed HTTP Module Site Wide?
Creating multiple (15+) HTTP Response filters, Inheritance vs. Composition w/ Injection
Thanks to a tip from Jay regarding Flush being called for incremental writes I have been able to make the Filter work as desired by performing the filtering logic only if the Filter is closing and has not yet closed. This ensures that the Filter only Flushes once when the Stream is closing. I accomplished this with a few simple fields, _isClosing and _isClosed as shown in the final code below.
public class ResponseFilter : MemoryStream
{
private readonly Stream _outputStream;
private MemoryStream _cachedStream = new MemoryStream(1024);
private readonly FilterBase _filter;
private bool _isClosing;
private bool _isClosed;
public ResponseFilter (Stream outputStream, FilterBase filter)
{
_outputStream = outputStream;
_filter = filter;
}
public override void Flush()
{
if (_isClosing && !_isClosed)
{
Encoding encoding = HttpContext.Current.Response.ContentEncoding;
string cachedContent = encoding.GetString(_cachedStream.ToArray());
// Filter the cached content
cachedContent = _filter.Filter(cachedContent);
byte[] buffer = encoding.GetBytes(cachedContent);
_cachedStream = new MemoryStream();
_cachedStream.Write(buffer, 0, buffer.Length);
// Write new content to stream
_outputStream.Write(_cachedStream.ToArray(), 0, (int)_cachedStream.Length);
_cachedStream.SetLength(0);
_outputStream.Flush();
}
}
public override void Write(byte[] buffer, int offset, int count)
{
_cachedStream.Write(buffer, 0, count);
}
public override void Close()
{
_isClosing = true;
Flush();
_isClosed = true;
_isClosing = false;
_outputStream.Close();
}
}
I have not yet found answers to my other questions above so I will not mark this answer as excepted at this time.
Flush is not being called explicitly. Perhaps it is called when the code realizes a new object is needed, or perhaps as a result of a finalizer.
I think one can call flush after any incremental write, so I'm not sure that a call to flush is an adequate indication of a complete message anyway.