diff --git a/src/Microsoft.AspNetCore.Server.Kestrel/Filter/Internal/StreamSocketOutput.cs b/src/Microsoft.AspNetCore.Server.Kestrel/Filter/Internal/StreamSocketOutput.cs index 52397633fc..bce699f693 100644 --- a/src/Microsoft.AspNetCore.Server.Kestrel/Filter/Internal/StreamSocketOutput.cs +++ b/src/Microsoft.AspNetCore.Server.Kestrel/Filter/Internal/StreamSocketOutput.cs @@ -8,6 +8,7 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.AspNetCore.Server.Kestrel.Internal.Http; using Microsoft.AspNetCore.Server.Kestrel.Internal.Infrastructure; +using Microsoft.Extensions.Internal; namespace Microsoft.AspNetCore.Server.Kestrel.Filter.Internal { @@ -34,11 +35,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Filter.Internal public void Write(ArraySegment buffer, bool chunk) { - if (buffer.Count == 0 ) - { - return; - } - if (chunk && buffer.Array != null) { var beginChunkBytes = ChunkWriter.BeginChunkBytes(buffer.Count); @@ -117,5 +113,15 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Filter.Internal end.Block.Pool.Return(end.Block); } + + // Flush no-ops. We rely on connection filter streams to auto-flush. + public void Flush() + { + } + + public Task FlushAsync(CancellationToken cancellationToken) + { + return TaskCache.CompletedTask; + } } } diff --git a/src/Microsoft.AspNetCore.Server.Kestrel/Internal/Http/Frame.cs b/src/Microsoft.AspNetCore.Server.Kestrel/Internal/Http/Frame.cs index be3f0f083b..c4a0c50d85 100644 --- a/src/Microsoft.AspNetCore.Server.Kestrel/Internal/Http/Frame.cs +++ b/src/Microsoft.AspNetCore.Server.Kestrel/Internal/Http/Frame.cs @@ -26,7 +26,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http { private static readonly ArraySegment _endChunkedResponseBytes = CreateAsciiByteArraySegment("0\r\n\r\n"); private static readonly ArraySegment _continueBytes = CreateAsciiByteArraySegment("HTTP/1.1 100 Continue\r\n\r\n"); - private static readonly ArraySegment _emptyData = new ArraySegment(new byte[0]); private static readonly byte[] _bytesConnectionClose = Encoding.ASCII.GetBytes("\r\nConnection: close"); private static readonly byte[] _bytesConnectionKeepAlive = Encoding.ASCII.GetBytes("\r\nConnection: keep-alive"); @@ -506,13 +505,13 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http public void Flush() { ProduceStartAndFireOnStarting().GetAwaiter().GetResult(); - SocketOutput.Write(_emptyData); + SocketOutput.Flush(); } public async Task FlushAsync(CancellationToken cancellationToken) { await ProduceStartAndFireOnStarting(); - await SocketOutput.WriteAsync(_emptyData, cancellationToken: cancellationToken); + await SocketOutput.FlushAsync(cancellationToken); } public void Write(ArraySegment data) @@ -768,7 +767,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http ProduceStart(appCompleted: true); // Force flush - await SocketOutput.WriteAsync(_emptyData); + await SocketOutput.FlushAsync(); await WriteSuffix(); } diff --git a/src/Microsoft.AspNetCore.Server.Kestrel/Internal/Http/ISocketOutput.cs b/src/Microsoft.AspNetCore.Server.Kestrel/Internal/Http/ISocketOutput.cs index 29b57b8c90..e5c4e4102e 100644 --- a/src/Microsoft.AspNetCore.Server.Kestrel/Internal/Http/ISocketOutput.cs +++ b/src/Microsoft.AspNetCore.Server.Kestrel/Internal/Http/ISocketOutput.cs @@ -15,6 +15,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http { void Write(ArraySegment buffer, bool chunk = false); Task WriteAsync(ArraySegment buffer, bool chunk = false, CancellationToken cancellationToken = default(CancellationToken)); + void Flush(); + Task FlushAsync(CancellationToken cancellationToken = default(CancellationToken)); /// /// Returns an iterator pointing to the tail of the response buffer. Response data can be appended diff --git a/src/Microsoft.AspNetCore.Server.Kestrel/Internal/Http/SocketOutput.cs b/src/Microsoft.AspNetCore.Server.Kestrel/Internal/Http/SocketOutput.cs index a926230b83..86b274dd12 100644 --- a/src/Microsoft.AspNetCore.Server.Kestrel/Internal/Http/SocketOutput.cs +++ b/src/Microsoft.AspNetCore.Server.Kestrel/Internal/Http/SocketOutput.cs @@ -21,6 +21,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http // Well behaved WriteAsync users should await returned task, so there is no need to allocate more per connection by default private const int _initialTaskQueues = 1; + private static readonly ArraySegment _emptyData = new ArraySegment(new byte[0]); private static readonly WaitCallback _returnBlocks = (state) => ReturnBlocks((MemoryPoolBlock)state); private static readonly Action _connectionCancellation = (state) => ((SocketOutput)state).CancellationTriggered(); @@ -534,6 +535,16 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http return WriteAsync(buffer, cancellationToken, chunk); } + void ISocketOutput.Flush() + { + WriteAsync(_emptyData, default(CancellationToken), isSync: true).GetAwaiter().GetResult(); + } + + Task ISocketOutput.FlushAsync(CancellationToken cancellationToken) + { + return WriteAsync(_emptyData, cancellationToken); + } + private static void BytesBetween(MemoryPoolIterator start, MemoryPoolIterator end, out int bytes, out int buffers) { if (start.Block == end.Block) diff --git a/test/Microsoft.AspNetCore.Server.KestrelTests/TestHelpers/MockSocketOuptut.cs b/test/Microsoft.AspNetCore.Server.KestrelTests/TestHelpers/MockSocketOuptut.cs index bdddcd1504..b5f65900b4 100644 --- a/test/Microsoft.AspNetCore.Server.KestrelTests/TestHelpers/MockSocketOuptut.cs +++ b/test/Microsoft.AspNetCore.Server.KestrelTests/TestHelpers/MockSocketOuptut.cs @@ -29,5 +29,14 @@ namespace Microsoft.AspNetCore.Server.KestrelTests.TestHelpers { return TaskCache.CompletedTask; } + + public void Flush() + { + } + + public Task FlushAsync(CancellationToken cancellationToken = new CancellationToken()) + { + return TaskCache.CompletedTask; + } } }