Remove synchronous Write APIs (#1838)

This commit is contained in:
David Fowler 2017-05-15 19:04:04 -07:00 committed by GitHub
parent 9ab09dbe48
commit cf16d601d6
8 changed files with 24 additions and 130 deletions

View File

@ -503,64 +503,13 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
} }
} }
public void Flush() public async Task FlushAsync(CancellationToken cancellationToken = default(CancellationToken))
{
InitializeResponse(0).GetAwaiter().GetResult();
Output.Flush();
}
public async Task FlushAsync(CancellationToken cancellationToken)
{ {
await InitializeResponse(0); await InitializeResponse(0);
await Output.FlushAsync(cancellationToken); await Output.FlushAsync(cancellationToken);
} }
public void Write(ArraySegment<byte> data) public Task WriteAsync(ArraySegment<byte> data, CancellationToken cancellationToken = default(CancellationToken))
{
// For the first write, ensure headers are flushed if Write(Chunked) isn't called.
var firstWrite = !HasResponseStarted;
if (firstWrite)
{
InitializeResponse(data.Count).GetAwaiter().GetResult();
}
else
{
VerifyAndUpdateWrite(data.Count);
}
if (_canHaveBody)
{
if (_autoChunk)
{
if (data.Count == 0)
{
if (firstWrite)
{
Flush();
}
return;
}
WriteChunked(data);
}
else
{
CheckLastWrite();
Output.Write(data);
}
}
else
{
HandleNonBodyResponseWrite();
if (firstWrite)
{
Flush();
}
}
}
public Task WriteAsync(ArraySegment<byte> data, CancellationToken cancellationToken)
{ {
if (!HasResponseStarted) if (!HasResponseStarted)
{ {
@ -679,11 +628,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
} }
} }
private void WriteChunked(ArraySegment<byte> data)
{
Output.Write(data, chunk: true);
}
private Task WriteChunkedAsync(ArraySegment<byte> data, CancellationToken cancellationToken) private Task WriteChunkedAsync(ArraySegment<byte> data, CancellationToken cancellationToken)
{ {
return Output.WriteAsync(data, chunk: true, cancellationToken: cancellationToken); return Output.WriteAsync(data, chunk: true, cancellationToken: cancellationToken);
@ -707,12 +651,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
return; return;
} }
StringValues expect;
if (_httpVersion == Http.HttpVersion.Http11 && if (_httpVersion == Http.HttpVersion.Http11 &&
RequestHeaders.TryGetValue("Expect", out expect) && RequestHeaders.TryGetValue("Expect", out var expect) &&
(expect.FirstOrDefault() ?? "").Equals("100-continue", StringComparison.OrdinalIgnoreCase)) (expect.FirstOrDefault() ?? "").Equals("100-continue", StringComparison.OrdinalIgnoreCase))
{ {
Output.Write(_continueBytes); Output.WriteAsync(_continueBytes).GetAwaiter().GetResult();
} }
} }

View File

@ -33,9 +33,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
public override void Flush() public override void Flush()
{ {
ValidateState(default(CancellationToken)); FlushAsync(default(CancellationToken)).GetAwaiter().GetResult();
_frameControl.Flush();
} }
public override Task FlushAsync(CancellationToken cancellationToken) public override Task FlushAsync(CancellationToken cancellationToken)
@ -60,9 +58,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
public override void Write(byte[] buffer, int offset, int count) public override void Write(byte[] buffer, int offset, int count)
{ {
ValidateState(default(CancellationToken)); WriteAsync(buffer, offset, count, default(CancellationToken)).GetAwaiter().GetResult();
_frameControl.Write(new ArraySegment<byte>(buffer, offset, count));
} }
public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state) public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state)

View File

@ -10,9 +10,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
public interface IFrameControl public interface IFrameControl
{ {
void ProduceContinue(); void ProduceContinue();
void Write(ArraySegment<byte> data);
Task WriteAsync(ArraySegment<byte> data, CancellationToken cancellationToken); Task WriteAsync(ArraySegment<byte> data, CancellationToken cancellationToken);
void Flush();
Task FlushAsync(CancellationToken cancellationToken); Task FlushAsync(CancellationToken cancellationToken);
} }
} }

View File

@ -39,11 +39,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
_flushCompleted = OnFlushCompleted; _flushCompleted = OnFlushCompleted;
} }
public void Write(ArraySegment<byte> buffer, bool chunk = false)
{
WriteAsync(buffer, default(CancellationToken), chunk).GetAwaiter().GetResult();
}
public Task WriteAsync(ArraySegment<byte> buffer, bool chunk = false, CancellationToken cancellationToken = default(CancellationToken)) public Task WriteAsync(ArraySegment<byte> buffer, bool chunk = false, CancellationToken cancellationToken = default(CancellationToken))
{ {
if (cancellationToken.IsCancellationRequested) if (cancellationToken.IsCancellationRequested)
@ -54,11 +49,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
return WriteAsync(buffer, cancellationToken, chunk); return WriteAsync(buffer, cancellationToken, chunk);
} }
public void Flush()
{
WriteAsync(_emptyData, default(CancellationToken)).GetAwaiter().GetResult();
}
public Task FlushAsync(CancellationToken cancellationToken = default(CancellationToken)) public Task FlushAsync(CancellationToken cancellationToken = default(CancellationToken))
{ {
return WriteAsync(_emptyData, cancellationToken); return WriteAsync(_emptyData, cancellationToken);

View File

@ -211,10 +211,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
} }
[Fact] [Fact]
public void ThrowsWhenStatusCodeIsSetAfterResponseStarted() public async Task ThrowsWhenStatusCodeIsSetAfterResponseStarted()
{ {
// Act // Act
_frame.Write(new ArraySegment<byte>(new byte[1])); await _frame.WriteAsync(new ArraySegment<byte>(new byte[1]));
// Assert // Assert
Assert.True(_frame.HasResponseStarted); Assert.True(_frame.HasResponseStarted);
@ -222,10 +222,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
} }
[Fact] [Fact]
public void ThrowsWhenReasonPhraseIsSetAfterResponseStarted() public async Task ThrowsWhenReasonPhraseIsSetAfterResponseStarted()
{ {
// Act // Act
_frame.Write(new ArraySegment<byte>(new byte[1])); await _frame.WriteAsync(new ArraySegment<byte>(new byte[1]));
// Assert // Assert
Assert.True(_frame.HasResponseStarted); Assert.True(_frame.HasResponseStarted);
@ -233,9 +233,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
} }
[Fact] [Fact]
public void ThrowsWhenOnStartingIsSetAfterResponseStarted() public async Task ThrowsWhenOnStartingIsSetAfterResponseStarted()
{ {
_frame.Write(new ArraySegment<byte>(new byte[1])); await _frame.WriteAsync(new ArraySegment<byte>(new byte[1]));
// Act/Assert // Act/Assert
Assert.True(_frame.HasResponseStarted); Assert.True(_frame.HasResponseStarted);
@ -472,13 +472,13 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
} }
[Fact] [Fact]
public void WriteThrowsForNonBodyResponse() public async Task WriteThrowsForNonBodyResponse()
{ {
// Arrange // Arrange
((IHttpResponseFeature)_frame).StatusCode = StatusCodes.Status304NotModified; ((IHttpResponseFeature)_frame).StatusCode = StatusCodes.Status304NotModified;
// Act/Assert // Act/Assert
Assert.Throws<InvalidOperationException>(() => _frame.Write(new ArraySegment<byte>(new byte[1]))); await Assert.ThrowsAsync<InvalidOperationException>(() => _frame.WriteAsync(new ArraySegment<byte>(new byte[1])));
} }
[Fact] [Fact]
@ -493,14 +493,14 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
} }
[Fact] [Fact]
public void WriteDoesNotThrowForHeadResponse() public async Task WriteDoesNotThrowForHeadResponse()
{ {
// Arrange // Arrange
_frame.HttpVersion = "HTTP/1.1"; _frame.HttpVersion = "HTTP/1.1";
((IHttpRequestFeature)_frame).Method = "HEAD"; ((IHttpRequestFeature)_frame).Method = "HEAD";
// Act/Assert // Act/Assert
_frame.Write(new ArraySegment<byte>(new byte[1])); await _frame.WriteAsync(new ArraySegment<byte>(new byte[1]));
} }
[Fact] [Fact]
@ -515,7 +515,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
} }
[Fact] [Fact]
public void ManuallySettingTransferEncodingThrowsForHeadResponse() public async Task ManuallySettingTransferEncodingThrowsForHeadResponse()
{ {
// Arrange // Arrange
_frame.HttpVersion = "HTTP/1.1"; _frame.HttpVersion = "HTTP/1.1";
@ -525,11 +525,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
_frame.ResponseHeaders.Add("Transfer-Encoding", "chunked"); _frame.ResponseHeaders.Add("Transfer-Encoding", "chunked");
// Assert // Assert
Assert.Throws<InvalidOperationException>(() => _frame.Flush()); await Assert.ThrowsAsync<InvalidOperationException>(() => _frame.FlushAsync());
} }
[Fact] [Fact]
public void ManuallySettingTransferEncodingThrowsForNoBodyResponse() public async Task ManuallySettingTransferEncodingThrowsForNoBodyResponse()
{ {
// Arrange // Arrange
_frame.HttpVersion = "HTTP/1.1"; _frame.HttpVersion = "HTTP/1.1";
@ -539,7 +539,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
_frame.ResponseHeaders.Add("Transfer-Encoding", "chunked"); _frame.ResponseHeaders.Add("Transfer-Encoding", "chunked");
// Assert // Assert
Assert.Throws<InvalidOperationException>(() => _frame.Flush()); await Assert.ThrowsAsync<InvalidOperationException>(() => _frame.FlushAsync());
} }
[Fact] [Fact]
@ -558,7 +558,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
} }
[Fact] [Fact]
public void RequestAbortedTokenIsResetBeforeLastWriteWithContentLength() public async Task RequestAbortedTokenIsResetBeforeLastWriteWithContentLength()
{ {
_frame.ResponseHeaders["Content-Length"] = "12"; _frame.ResponseHeaders["Content-Length"] = "12";
@ -567,11 +567,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
foreach (var ch in "hello, worl") foreach (var ch in "hello, worl")
{ {
_frame.Write(new ArraySegment<byte>(new[] { (byte)ch })); await _frame.WriteAsync(new ArraySegment<byte>(new[] { (byte)ch }));
Assert.Same(original, _frame.RequestAborted.WaitHandle); Assert.Same(original, _frame.RequestAborted.WaitHandle);
} }
_frame.Write(new ArraySegment<byte>(new[] { (byte)'d' })); await _frame.WriteAsync(new ArraySegment<byte>(new[] { (byte)'d' }));
Assert.NotSame(original, _frame.RequestAborted.WaitHandle); Assert.NotSame(original, _frame.RequestAborted.WaitHandle);
} }

View File

@ -11,10 +11,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests.TestHelpers
{ {
public class MockFrameControl : IFrameControl public class MockFrameControl : IFrameControl
{ {
public void Flush()
{
}
public Task FlushAsync(CancellationToken cancellationToken) public Task FlushAsync(CancellationToken cancellationToken)
{ {
return TaskCache.CompletedTask; return TaskCache.CompletedTask;
@ -24,10 +20,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests.TestHelpers
{ {
} }
public void Write(ArraySegment<byte> data)
{
}
public Task WriteAsync(ArraySegment<byte> data, CancellationToken cancellationToken) public Task WriteAsync(ArraySegment<byte> data, CancellationToken cancellationToken)
{ {
return TaskCache.CompletedTask; return TaskCache.CompletedTask;

View File

@ -36,7 +36,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
FrameContext.FrameControl = this; FrameContext.FrameControl = this;
} }
public IPipe Pipe { get; } public IPipe Pipe { get; }
public Frame FrameContext { get; set; } public Frame FrameContext { get; set; }
@ -70,23 +70,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
{ {
} }
void IFrameControl.ProduceContinue()
{
}
void IFrameControl.Write(ArraySegment<byte> data)
{
}
Task IFrameControl.WriteAsync(ArraySegment<byte> data, CancellationToken cancellationToken) Task IFrameControl.WriteAsync(ArraySegment<byte> data, CancellationToken cancellationToken)
{ {
return TaskCache.CompletedTask; return TaskCache.CompletedTask;
} }
void IFrameControl.Flush()
{
}
Task IFrameControl.FlushAsync(CancellationToken cancellationToken) Task IFrameControl.FlushAsync(CancellationToken cancellationToken)
{ {
return TaskCache.CompletedTask; return TaskCache.CompletedTask;

View File

@ -9,7 +9,6 @@ using Microsoft.AspNetCore.Server.Kestrel.Core;
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal;
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http;
using Microsoft.AspNetCore.Server.Kestrel.Internal.System.IO.Pipelines; using Microsoft.AspNetCore.Server.Kestrel.Internal.System.IO.Pipelines;
using Microsoft.AspNetCore.Server.Kestrel.Performance.Mocks;
using Microsoft.AspNetCore.Testing; using Microsoft.AspNetCore.Testing;
namespace Microsoft.AspNetCore.Server.Kestrel.Performance namespace Microsoft.AspNetCore.Server.Kestrel.Performance
@ -38,18 +37,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Performance
_frameChunked.RequestHeaders.Add("Transfer-Encoding", "chunked"); _frameChunked.RequestHeaders.Add("Transfer-Encoding", "chunked");
} }
[Benchmark]
public void Write()
{
_frame.Write(new ArraySegment<byte>(_writeData));
}
[Benchmark]
public void WriteChunked()
{
_frameChunked.Write(new ArraySegment<byte>(_writeData));
}
[Benchmark] [Benchmark]
public async Task WriteAsync() public async Task WriteAsync()
{ {