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

View File

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

View File

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

View File

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

View File

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