Amortize Stream Costs
This commit is contained in:
parent
349af50977
commit
f5e45accac
|
|
@ -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();
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue