Amortize Stream Costs

This commit is contained in:
Ben Adams 2015-12-15 04:24:54 +00:00
parent 349af50977
commit f5e45accac
5 changed files with 45 additions and 19 deletions

View File

@ -57,6 +57,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
internal FrameRequestStream _requestBody;
internal FrameResponseStream _responseBody;
internal FrameDuplexStream _duplexStream;
protected bool _responseStarted;
protected bool _keepAlive;
@ -86,6 +87,9 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
_localEndPoint = localEndPoint;
_prepareRequest = prepareRequest;
_pathBase = context.ServerAddress.PathBase;
_requestBody = new FrameRequestStream();
_responseBody = new FrameResponseStream(this);
_duplexStream = new FrameDuplexStream(_requestBody, _responseBody);
FrameControl = this;
Reset();

View File

@ -64,11 +64,9 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
{
var messageBody = MessageBody.For(HttpVersion, _requestHeaders, this);
_keepAlive = messageBody.RequestKeepAlive;
_requestBody = new FrameRequestStream(messageBody);
RequestBody = _requestBody;
_responseBody = new FrameResponseStream(this);
ResponseBody = _responseBody;
DuplexStream = new FrameDuplexStream(RequestBody, ResponseBody);
RequestBody = _requestBody.StartAcceptingReads(messageBody);
ResponseBody = _responseBody.StartAcceptingWrites();
DuplexStream = _duplexStream;
_abortedCts = null;
_manuallySetRequestAbortToken = null;

View File

@ -10,12 +10,12 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
{
public class FrameRequestStream : Stream
{
private readonly MessageBody _body;
private MessageBody _body;
private StreamState _state;
public FrameRequestStream(MessageBody body)
public FrameRequestStream()
{
_body = body;
_state = StreamState.Closed;
}
public override bool CanRead { get { return true; } }
@ -112,18 +112,30 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
throw new NotImplementedException();
}
public Stream StartAcceptingReads(MessageBody body)
{
// Only start if not aborted
if (_state == StreamState.Closed)
{
_state = StreamState.Open;
_body = body;
}
return this;
}
public void StopAcceptingReads()
{
// Can't use dispose (or close) as can be disposed too early by user code
// As exampled in EngineTests.ZeroContentLengthNotSetAutomaticallyForCertainStatusCodes
_state = StreamState.Disposed;
_state = StreamState.Closed;
_body = null;
}
public void Abort()
{
// We don't want to throw an ODE until the app func actually completes.
// If the request is aborted, we throw an IOException instead.
if (_state != StreamState.Disposed)
if (_state != StreamState.Closed)
{
_state = StreamState.Aborted;
}
@ -135,7 +147,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
{
case StreamState.Open:
return;
case StreamState.Disposed:
case StreamState.Closed:
throw new ObjectDisposedException(nameof(FrameRequestStream));
case StreamState.Aborted:
throw new IOException("The request has been aborted.");
@ -145,7 +157,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
private enum StreamState
{
Open,
Disposed,
Closed,
Aborted
}
}

View File

@ -16,6 +16,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
public FrameResponseStream(FrameContext context)
{
_context = context;
_state = StreamState.Closed;
}
public override bool CanRead => false;
@ -77,18 +78,29 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
return _context.FrameControl.WriteAsync(new ArraySegment<byte>(buffer, offset, count), cancellationToken);
}
public Stream StartAcceptingWrites()
{
// Only start if not aborted
if (_state == StreamState.Closed)
{
_state = StreamState.Open;
}
return this;
}
public void StopAcceptingWrites()
{
// Can't use dispose (or close) as can be disposed too early by user code
// As exampled in EngineTests.ZeroContentLengthNotSetAutomaticallyForCertainStatusCodes
_state = StreamState.Disposed;
_state = StreamState.Closed;
}
public void Abort()
{
// We don't want to throw an ODE until the app func actually completes.
// If the request is aborted, we throw an IOException instead.
if (_state != StreamState.Disposed)
if (_state != StreamState.Closed)
{
_state = StreamState.Aborted;
}
@ -100,7 +112,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
{
case StreamState.Open:
return;
case StreamState.Disposed:
case StreamState.Closed:
throw new ObjectDisposedException(nameof(FrameResponseStream));
case StreamState.Aborted:
throw new IOException("The request has been aborted.");
@ -110,7 +122,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
private enum StreamState
{
Open,
Disposed,
Closed,
Aborted
}
}

View File

@ -21,7 +21,7 @@ namespace Microsoft.AspNet.Server.KestrelTests
{
var input = new TestInput();
var body = MessageBody.For("HTTP/1.0", new Dictionary<string, StringValues>(), input.FrameContext);
var stream = new FrameRequestStream(body);
var stream = new FrameRequestStream().StartAcceptingReads(body);
input.Add("Hello", true);
@ -39,7 +39,7 @@ namespace Microsoft.AspNet.Server.KestrelTests
{
var input = new TestInput();
var body = MessageBody.For("HTTP/1.0", new Dictionary<string, StringValues>(), input.FrameContext);
var stream = new FrameRequestStream(body);
var stream = new FrameRequestStream().StartAcceptingReads(body);
input.Add("Hello", true);
@ -57,7 +57,7 @@ namespace Microsoft.AspNet.Server.KestrelTests
{
var input = new TestInput();
var body = MessageBody.For("HTTP/1.0", new Dictionary<string, StringValues>(), input.FrameContext);
var stream = new FrameRequestStream(body);
var stream = new FrameRequestStream().StartAcceptingReads(body);
// Input needs to be greater than 4032 bytes to allocate a block not backed by a slab.
var largeInput = new string('a', 8192);