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 System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Server.Kestrel.Internal.Http;
|
using Microsoft.AspNetCore.Server.Kestrel.Internal.Http;
|
||||||
using Microsoft.AspNetCore.Server.Kestrel.Internal.Infrastructure;
|
using Microsoft.AspNetCore.Server.Kestrel.Internal.Infrastructure;
|
||||||
|
using Microsoft.Extensions.Internal;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Server.Kestrel.Filter.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)
|
public void Write(ArraySegment<byte> buffer, bool chunk)
|
||||||
{
|
{
|
||||||
if (buffer.Count == 0 )
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (chunk && buffer.Array != null)
|
if (chunk && buffer.Array != null)
|
||||||
{
|
{
|
||||||
var beginChunkBytes = ChunkWriter.BeginChunkBytes(buffer.Count);
|
var beginChunkBytes = ChunkWriter.BeginChunkBytes(buffer.Count);
|
||||||
|
|
@ -117,5 +113,15 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Filter.Internal
|
||||||
|
|
||||||
end.Block.Pool.Return(end.Block);
|
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> _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> _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[] _bytesConnectionClose = Encoding.ASCII.GetBytes("\r\nConnection: close");
|
||||||
private static readonly byte[] _bytesConnectionKeepAlive = Encoding.ASCII.GetBytes("\r\nConnection: keep-alive");
|
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()
|
public void Flush()
|
||||||
{
|
{
|
||||||
ProduceStartAndFireOnStarting().GetAwaiter().GetResult();
|
ProduceStartAndFireOnStarting().GetAwaiter().GetResult();
|
||||||
SocketOutput.Write(_emptyData);
|
SocketOutput.Flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task FlushAsync(CancellationToken cancellationToken)
|
public async Task FlushAsync(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
await ProduceStartAndFireOnStarting();
|
await ProduceStartAndFireOnStarting();
|
||||||
await SocketOutput.WriteAsync(_emptyData, cancellationToken: cancellationToken);
|
await SocketOutput.FlushAsync(cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Write(ArraySegment<byte> data)
|
public void Write(ArraySegment<byte> data)
|
||||||
|
|
@ -768,7 +767,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
||||||
ProduceStart(appCompleted: true);
|
ProduceStart(appCompleted: true);
|
||||||
|
|
||||||
// Force flush
|
// Force flush
|
||||||
await SocketOutput.WriteAsync(_emptyData);
|
await SocketOutput.FlushAsync();
|
||||||
|
|
||||||
await WriteSuffix();
|
await WriteSuffix();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
||||||
{
|
{
|
||||||
void Write(ArraySegment<byte> buffer, bool chunk = false);
|
void Write(ArraySegment<byte> buffer, bool chunk = false);
|
||||||
Task WriteAsync(ArraySegment<byte> buffer, bool chunk = false, CancellationToken cancellationToken = default(CancellationToken));
|
Task WriteAsync(ArraySegment<byte> buffer, bool chunk = false, CancellationToken cancellationToken = default(CancellationToken));
|
||||||
|
void Flush();
|
||||||
|
Task FlushAsync(CancellationToken cancellationToken = default(CancellationToken));
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns an iterator pointing to the tail of the response buffer. Response data can be appended
|
/// 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
|
// 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 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 WaitCallback _returnBlocks = (state) => ReturnBlocks((MemoryPoolBlock)state);
|
||||||
private static readonly Action<object> _connectionCancellation = (state) => ((SocketOutput)state).CancellationTriggered();
|
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);
|
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)
|
private static void BytesBetween(MemoryPoolIterator start, MemoryPoolIterator end, out int bytes, out int buffers)
|
||||||
{
|
{
|
||||||
if (start.Block == end.Block)
|
if (start.Block == end.Block)
|
||||||
|
|
|
||||||
|
|
@ -29,5 +29,14 @@ namespace Microsoft.AspNetCore.Server.KestrelTests.TestHelpers
|
||||||
{
|
{
|
||||||
return TaskCache.CompletedTask;
|
return TaskCache.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Flush()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task FlushAsync(CancellationToken cancellationToken = new CancellationToken())
|
||||||
|
{
|
||||||
|
return TaskCache.CompletedTask;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue