Undo use of pipewriter in FileBufferingWriteStream (#21833)
This commit is contained in:
parent
0e652d1aba
commit
48261fdada
|
|
@ -77,8 +77,6 @@ namespace Microsoft.AspNetCore.WebUtilities
|
||||||
[System.Diagnostics.DebuggerStepThroughAttribute]
|
[System.Diagnostics.DebuggerStepThroughAttribute]
|
||||||
public override System.Threading.Tasks.ValueTask DisposeAsync() { throw null; }
|
public override System.Threading.Tasks.ValueTask DisposeAsync() { throw null; }
|
||||||
[System.Diagnostics.DebuggerStepThroughAttribute]
|
[System.Diagnostics.DebuggerStepThroughAttribute]
|
||||||
public System.Threading.Tasks.Task DrainBufferAsync(System.IO.Pipelines.PipeWriter destination, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
|
|
||||||
[System.Diagnostics.DebuggerStepThroughAttribute]
|
|
||||||
public System.Threading.Tasks.Task DrainBufferAsync(System.IO.Stream destination, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
|
public System.Threading.Tasks.Task DrainBufferAsync(System.IO.Stream destination, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
|
||||||
public override void Flush() { }
|
public override void Flush() { }
|
||||||
public override System.Threading.Tasks.Task FlushAsync(System.Threading.CancellationToken cancellationToken) { throw null; }
|
public override System.Threading.Tasks.Task FlushAsync(System.Threading.CancellationToken cancellationToken) { throw null; }
|
||||||
|
|
|
||||||
|
|
@ -186,25 +186,8 @@ namespace Microsoft.AspNetCore.WebUtilities
|
||||||
// unspooled content. Copy the FileStream content first when available.
|
// unspooled content. Copy the FileStream content first when available.
|
||||||
if (FileStream != null)
|
if (FileStream != null)
|
||||||
{
|
{
|
||||||
// We make a new stream for async reads from disk and async writes to the destination
|
await FileStream.FlushAsync(cancellationToken);
|
||||||
await using var readStream = new FileStream(FileStream.Name, FileMode.Open, FileAccess.Read, FileShare.Delete | FileShare.ReadWrite, bufferSize: 1, useAsync: true);
|
|
||||||
|
|
||||||
await readStream.CopyToAsync(destination, cancellationToken);
|
|
||||||
|
|
||||||
// This is created with delete on close
|
|
||||||
await FileStream.DisposeAsync();
|
|
||||||
FileStream = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
await PagedByteBuffer.MoveToAsync(destination, cancellationToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task DrainBufferAsync(PipeWriter destination, CancellationToken cancellationToken = default)
|
|
||||||
{
|
|
||||||
// When not null, FileStream always has "older" spooled content. The PagedByteBuffer always has "newer"
|
|
||||||
// unspooled content. Copy the FileStream content first when available.
|
|
||||||
if (FileStream != null)
|
|
||||||
{
|
|
||||||
// We make a new stream for async reads from disk and async writes to the destination
|
// We make a new stream for async reads from disk and async writes to the destination
|
||||||
await using var readStream = new FileStream(FileStream.Name, FileMode.Open, FileAccess.Read, FileShare.Delete | FileShare.ReadWrite, bufferSize: 1, useAsync: true);
|
await using var readStream = new FileStream(FileStream.Name, FileMode.Open, FileAccess.Read, FileShare.Delete | FileShare.ReadWrite, bufferSize: 1, useAsync: true);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -371,6 +371,24 @@ namespace Microsoft.AspNetCore.WebUtilities
|
||||||
Assert.Equal(0, bufferingStream.Length);
|
Assert.Equal(0, bufferingStream.Length);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task DrainBufferAsync_IncludesContentPossiblyBufferedByFileStream()
|
||||||
|
{
|
||||||
|
// We want to ensure that the FileStream (which has a 1-byte buffer) flushes prior to the other read stream reading input.
|
||||||
|
// Arrange
|
||||||
|
var input = new byte[] { 3, };
|
||||||
|
using var bufferingStream = new FileBufferingWriteStream(0, tempFileDirectoryAccessor: () => TempDirectory);
|
||||||
|
bufferingStream.Write(input, 0, input.Length);
|
||||||
|
var memoryStream = new MemoryStream();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
await bufferingStream.DrainBufferAsync(memoryStream, default);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(input, memoryStream.ToArray());
|
||||||
|
Assert.Equal(0, bufferingStream.Length);
|
||||||
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
|
|
||||||
|
|
@ -261,7 +261,7 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
||||||
if (fileBufferingWriteStream != null)
|
if (fileBufferingWriteStream != null)
|
||||||
{
|
{
|
||||||
response.ContentLength = fileBufferingWriteStream.Length;
|
response.ContentLength = fileBufferingWriteStream.Length;
|
||||||
await fileBufferingWriteStream.DrainBufferAsync(response.BodyWriter);
|
await fileBufferingWriteStream.DrainBufferAsync(response.Body);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
|
|
|
||||||
|
|
@ -155,7 +155,7 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
||||||
if (fileBufferingWriteStream != null)
|
if (fileBufferingWriteStream != null)
|
||||||
{
|
{
|
||||||
response.ContentLength = fileBufferingWriteStream.Length;
|
response.ContentLength = fileBufferingWriteStream.Length;
|
||||||
await fileBufferingWriteStream.DrainBufferAsync(response.BodyWriter);
|
await fileBufferingWriteStream.DrainBufferAsync(response.Body);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
|
|
|
||||||
|
|
@ -154,15 +154,17 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
|
||||||
Assert.Equal(expected, await response.Content.ReadAsStringAsync());
|
Assert.Equal(expected, await response.Content.ReadAsStringAsync());
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Theory]
|
||||||
public virtual async Task Formatting_LargeObject()
|
[InlineData(65 * 1024)]
|
||||||
|
[InlineData(2 * 1024 * 1024)]
|
||||||
|
public virtual async Task Formatting_LargeObject(int size)
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var expectedName = "This is long so we can test large objects " + new string('a', 1024 * 65);
|
var expectedName = "This is long so we can test large objects " + new string('a', size);
|
||||||
var expected = $"{{\"id\":10,\"name\":\"{expectedName}\",\"streetName\":null}}";
|
var expected = $"{{\"id\":10,\"name\":\"{expectedName}\",\"streetName\":null}}";
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var response = await Client.GetAsync($"/JsonOutputFormatter/{nameof(JsonOutputFormatterController.LargeObjectResult)}");
|
var response = await Client.GetAsync($"/JsonOutputFormatter/{nameof(JsonOutputFormatterController.LargeObjectResult)}/{size}");
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
await response.AssertStatusCodeAsync(HttpStatusCode.OK);
|
await response.AssertStatusCodeAsync(HttpStatusCode.OK);
|
||||||
|
|
|
||||||
|
|
@ -44,12 +44,12 @@ namespace FormatterWebSite.Controllers
|
||||||
["Key3"] = null,
|
["Key3"] = null,
|
||||||
};
|
};
|
||||||
|
|
||||||
[HttpGet]
|
[HttpGet("{size:int}")]
|
||||||
public ActionResult<SimpleModel> LargeObjectResult() =>
|
public ActionResult<SimpleModel> LargeObjectResult(int size) =>
|
||||||
new SimpleModel
|
new SimpleModel
|
||||||
{
|
{
|
||||||
Id = 10,
|
Id = 10,
|
||||||
Name = "This is long so we can test large objects " + new string('a', 1024 * 65),
|
Name = "This is long so we can test large objects " + new string('a', size),
|
||||||
};
|
};
|
||||||
|
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue