[Fixes #7658] FileStreamResultExecutor fails to Dispose FileStream
This commit is contained in:
parent
6e4bd1eb46
commit
4f7d53f4e7
|
|
@ -17,7 +17,7 @@ namespace Microsoft.AspNetCore.Mvc.Infrastructure
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public virtual Task ExecuteAsync(ActionContext context, FileStreamResult result)
|
public virtual async Task ExecuteAsync(ActionContext context, FileStreamResult result)
|
||||||
{
|
{
|
||||||
if (context == null)
|
if (context == null)
|
||||||
{
|
{
|
||||||
|
|
@ -29,31 +29,38 @@ namespace Microsoft.AspNetCore.Mvc.Infrastructure
|
||||||
throw new ArgumentNullException(nameof(result));
|
throw new ArgumentNullException(nameof(result));
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger.ExecutingFileResult(result);
|
using (result.FileStream)
|
||||||
|
|
||||||
long? fileLength = null;
|
|
||||||
if (result.FileStream.CanSeek)
|
|
||||||
{
|
{
|
||||||
fileLength = result.FileStream.Length;
|
Logger.ExecutingFileResult(result);
|
||||||
|
|
||||||
|
long? fileLength = null;
|
||||||
|
if (result.FileStream.CanSeek)
|
||||||
|
{
|
||||||
|
fileLength = result.FileStream.Length;
|
||||||
|
}
|
||||||
|
|
||||||
|
var (range, rangeLength, serveBody) = SetHeadersAndLog(
|
||||||
|
context,
|
||||||
|
result,
|
||||||
|
fileLength,
|
||||||
|
result.EnableRangeProcessing,
|
||||||
|
result.LastModified,
|
||||||
|
result.EntityTag);
|
||||||
|
|
||||||
|
if (!serveBody)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await WriteFileAsync(context, result, range, rangeLength);
|
||||||
}
|
}
|
||||||
|
|
||||||
var (range, rangeLength, serveBody) = SetHeadersAndLog(
|
|
||||||
context,
|
|
||||||
result,
|
|
||||||
fileLength,
|
|
||||||
result.EnableRangeProcessing,
|
|
||||||
result.LastModified,
|
|
||||||
result.EntityTag);
|
|
||||||
|
|
||||||
if (!serveBody)
|
|
||||||
{
|
|
||||||
return Task.CompletedTask;
|
|
||||||
}
|
|
||||||
|
|
||||||
return WriteFileAsync(context, result, range, rangeLength);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual Task WriteFileAsync(ActionContext context, FileStreamResult result, RangeItemHeaderValue range, long rangeLength)
|
protected virtual Task WriteFileAsync(
|
||||||
|
ActionContext context,
|
||||||
|
FileStreamResult result,
|
||||||
|
RangeItemHeaderValue range,
|
||||||
|
long rangeLength)
|
||||||
{
|
{
|
||||||
if (context == null)
|
if (context == null)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -126,6 +126,7 @@ namespace Microsoft.AspNetCore.Mvc
|
||||||
Assert.Equal(contentRange.ToString(), httpResponse.Headers[HeaderNames.ContentRange]);
|
Assert.Equal(contentRange.ToString(), httpResponse.Headers[HeaderNames.ContentRange]);
|
||||||
Assert.Equal(contentLength, httpResponse.ContentLength);
|
Assert.Equal(contentLength, httpResponse.ContentLength);
|
||||||
Assert.Equal(expectedString, body);
|
Assert.Equal(expectedString, body);
|
||||||
|
Assert.False(readStream.CanSeek);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
@ -174,6 +175,7 @@ namespace Microsoft.AspNetCore.Mvc
|
||||||
Assert.Equal(contentRange.ToString(), httpResponse.Headers[HeaderNames.ContentRange]);
|
Assert.Equal(contentRange.ToString(), httpResponse.Headers[HeaderNames.ContentRange]);
|
||||||
Assert.Equal(5, httpResponse.ContentLength);
|
Assert.Equal(5, httpResponse.ContentLength);
|
||||||
Assert.Equal("Hello", body);
|
Assert.Equal("Hello", body);
|
||||||
|
Assert.False(readStream.CanSeek);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
@ -217,6 +219,7 @@ namespace Microsoft.AspNetCore.Mvc
|
||||||
Assert.Equal(lastModified.ToString("R"), httpResponse.Headers[HeaderNames.LastModified]);
|
Assert.Equal(lastModified.ToString("R"), httpResponse.Headers[HeaderNames.LastModified]);
|
||||||
Assert.Equal(entityTag.ToString(), httpResponse.Headers[HeaderNames.ETag]);
|
Assert.Equal(entityTag.ToString(), httpResponse.Headers[HeaderNames.ETag]);
|
||||||
Assert.Equal("Hello World", body);
|
Assert.Equal("Hello World", body);
|
||||||
|
Assert.False(readStream.CanSeek);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
@ -261,6 +264,7 @@ namespace Microsoft.AspNetCore.Mvc
|
||||||
Assert.Equal(lastModified.ToString("R"), httpResponse.Headers[HeaderNames.LastModified]);
|
Assert.Equal(lastModified.ToString("R"), httpResponse.Headers[HeaderNames.LastModified]);
|
||||||
Assert.Equal(entityTag.ToString(), httpResponse.Headers[HeaderNames.ETag]);
|
Assert.Equal(entityTag.ToString(), httpResponse.Headers[HeaderNames.ETag]);
|
||||||
Assert.Equal("Hello World", body);
|
Assert.Equal("Hello World", body);
|
||||||
|
Assert.False(readStream.CanSeek);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
|
|
@ -303,6 +307,7 @@ namespace Microsoft.AspNetCore.Mvc
|
||||||
Assert.Equal(lastModified.ToString("R"), httpResponse.Headers[HeaderNames.LastModified]);
|
Assert.Equal(lastModified.ToString("R"), httpResponse.Headers[HeaderNames.LastModified]);
|
||||||
Assert.Equal(entityTag.ToString(), httpResponse.Headers[HeaderNames.ETag]);
|
Assert.Equal(entityTag.ToString(), httpResponse.Headers[HeaderNames.ETag]);
|
||||||
Assert.Equal("Hello World", body);
|
Assert.Equal("Hello World", body);
|
||||||
|
Assert.False(readStream.CanSeek);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
|
|
@ -346,6 +351,7 @@ namespace Microsoft.AspNetCore.Mvc
|
||||||
Assert.Equal(contentRange.ToString(), httpResponse.Headers[HeaderNames.ContentRange]);
|
Assert.Equal(contentRange.ToString(), httpResponse.Headers[HeaderNames.ContentRange]);
|
||||||
Assert.Equal(11, httpResponse.ContentLength);
|
Assert.Equal(11, httpResponse.ContentLength);
|
||||||
Assert.Empty(body);
|
Assert.Empty(body);
|
||||||
|
Assert.False(readStream.CanSeek);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
@ -389,6 +395,7 @@ namespace Microsoft.AspNetCore.Mvc
|
||||||
Assert.Empty(httpResponse.Headers[HeaderNames.ContentRange]);
|
Assert.Empty(httpResponse.Headers[HeaderNames.ContentRange]);
|
||||||
Assert.NotEmpty(httpResponse.Headers[HeaderNames.LastModified]);
|
Assert.NotEmpty(httpResponse.Headers[HeaderNames.LastModified]);
|
||||||
Assert.Empty(body);
|
Assert.Empty(body);
|
||||||
|
Assert.False(readStream.CanSeek);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
@ -432,6 +439,7 @@ namespace Microsoft.AspNetCore.Mvc
|
||||||
Assert.Empty(httpResponse.Headers[HeaderNames.ContentRange]);
|
Assert.Empty(httpResponse.Headers[HeaderNames.ContentRange]);
|
||||||
Assert.NotEmpty(httpResponse.Headers[HeaderNames.LastModified]);
|
Assert.NotEmpty(httpResponse.Headers[HeaderNames.LastModified]);
|
||||||
Assert.Empty(body);
|
Assert.Empty(body);
|
||||||
|
Assert.False(readStream.CanSeek);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
|
|
@ -480,6 +488,7 @@ namespace Microsoft.AspNetCore.Mvc
|
||||||
Assert.Equal("bytes", httpResponse.Headers[HeaderNames.AcceptRanges]);
|
Assert.Equal("bytes", httpResponse.Headers[HeaderNames.AcceptRanges]);
|
||||||
Assert.Equal(contentRange.ToString(), httpResponse.Headers[HeaderNames.ContentRange]);
|
Assert.Equal(contentRange.ToString(), httpResponse.Headers[HeaderNames.ContentRange]);
|
||||||
Assert.Empty(body);
|
Assert.Empty(body);
|
||||||
|
Assert.False(readStream.CanSeek);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
@ -541,6 +550,7 @@ namespace Microsoft.AspNetCore.Mvc
|
||||||
// Assert
|
// Assert
|
||||||
var outBytes = outStream.ToArray();
|
var outBytes = outStream.ToArray();
|
||||||
Assert.True(originalBytes.SequenceEqual(outBytes));
|
Assert.True(originalBytes.SequenceEqual(outBytes));
|
||||||
|
Assert.False(originalStream.CanSeek);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
@ -570,6 +580,31 @@ namespace Microsoft.AspNetCore.Mvc
|
||||||
var outBytes = outStream.ToArray();
|
var outBytes = outStream.ToArray();
|
||||||
Assert.True(originalBytes.SequenceEqual(outBytes));
|
Assert.True(originalBytes.SequenceEqual(outBytes));
|
||||||
Assert.Equal(expectedContentType, httpContext.Response.ContentType);
|
Assert.Equal(expectedContentType, httpContext.Response.ContentType);
|
||||||
|
Assert.False(originalStream.CanSeek);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task HeadRequest_DoesNotWriteToBody_AndClosesReadStream()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var readStream = new MemoryStream(Encoding.UTF8.GetBytes("Hello, World!"));
|
||||||
|
|
||||||
|
var httpContext = GetHttpContext();
|
||||||
|
httpContext.Request.Method = "HEAD";
|
||||||
|
var outStream = new MemoryStream();
|
||||||
|
httpContext.Response.Body = outStream;
|
||||||
|
|
||||||
|
var actionContext = new ActionContext(httpContext, new RouteData(), new ActionDescriptor());
|
||||||
|
|
||||||
|
var result = new FileStreamResult(readStream, "text/plain");
|
||||||
|
|
||||||
|
// Act
|
||||||
|
await result.ExecuteResultAsync(actionContext);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.False(readStream.CanSeek);
|
||||||
|
Assert.Equal(200, httpContext.Response.StatusCode);
|
||||||
|
Assert.Equal(0, httpContext.Response.Body.Length);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static IServiceCollection CreateServices()
|
private static IServiceCollection CreateServices()
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue