Avoid zero-length writes to flush connection filter stream
- This works around a zero-length write bug in SslStream. - We already assume connection filter streams auto flush. #1195
This commit is contained in:
parent
f5d6f39fa0
commit
b3aca0413c
|
|
@ -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<byte> 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,7 +26,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
|||
{
|
||||
private static readonly ArraySegment<byte> _endChunkedResponseBytes = CreateAsciiByteArraySegment("0\r\n\r\n");
|
||||
private static readonly ArraySegment<byte> _continueBytes = CreateAsciiByteArraySegment("HTTP/1.1 100 Continue\r\n\r\n");
|
||||
private static readonly ArraySegment<byte> _emptyData = new ArraySegment<byte>(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<byte> 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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,6 +15,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
|||
{
|
||||
void Write(ArraySegment<byte> buffer, bool chunk = false);
|
||||
Task WriteAsync(ArraySegment<byte> buffer, bool chunk = false, CancellationToken cancellationToken = default(CancellationToken));
|
||||
void Flush();
|
||||
Task FlushAsync(CancellationToken cancellationToken = default(CancellationToken));
|
||||
|
||||
/// <summary>
|
||||
/// Returns an iterator pointing to the tail of the response buffer. Response data can be appended
|
||||
|
|
|
|||
|
|
@ -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<byte> _emptyData = new ArraySegment<byte>(new byte[0]);
|
||||
private static readonly WaitCallback _returnBlocks = (state) => ReturnBlocks((MemoryPoolBlock)state);
|
||||
private static readonly Action<object> _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)
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue