diff --git a/src/Microsoft.AspNet.Server.Kestrel/Http/Frame.cs b/src/Microsoft.AspNet.Server.Kestrel/Http/Frame.cs index 426f17b283..5dfca52bb9 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Http/Frame.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Http/Frame.cs @@ -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(); diff --git a/src/Microsoft.AspNet.Server.Kestrel/Http/FrameOfT.cs b/src/Microsoft.AspNet.Server.Kestrel/Http/FrameOfT.cs index 4410cdc8cd..9e932e2380 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Http/FrameOfT.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Http/FrameOfT.cs @@ -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; diff --git a/src/Microsoft.AspNet.Server.Kestrel/Http/FrameRequestStream.cs b/src/Microsoft.AspNet.Server.Kestrel/Http/FrameRequestStream.cs index 2c19b40dd8..c008d7cfc5 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Http/FrameRequestStream.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Http/FrameRequestStream.cs @@ -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 } } diff --git a/src/Microsoft.AspNet.Server.Kestrel/Http/FrameResponseStream.cs b/src/Microsoft.AspNet.Server.Kestrel/Http/FrameResponseStream.cs index 827234f872..832a112274 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Http/FrameResponseStream.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Http/FrameResponseStream.cs @@ -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(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 } } diff --git a/test/Microsoft.AspNet.Server.KestrelTests/MessageBodyTests.cs b/test/Microsoft.AspNet.Server.KestrelTests/MessageBodyTests.cs index b5dbfc021a..19e7d2e9b2 100644 --- a/test/Microsoft.AspNet.Server.KestrelTests/MessageBodyTests.cs +++ b/test/Microsoft.AspNet.Server.KestrelTests/MessageBodyTests.cs @@ -21,7 +21,7 @@ namespace Microsoft.AspNet.Server.KestrelTests { var input = new TestInput(); var body = MessageBody.For("HTTP/1.0", new Dictionary(), 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(), 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(), 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);