diff --git a/src/Microsoft.AspNetCore.Server.Kestrel/Http/Frame.cs b/src/Microsoft.AspNetCore.Server.Kestrel/Http/Frame.cs
index fb35280475..3b7e861ff7 100644
--- a/src/Microsoft.AspNetCore.Server.Kestrel/Http/Frame.cs
+++ b/src/Microsoft.AspNetCore.Server.Kestrel/Http/Frame.cs
@@ -174,19 +174,29 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http
public void InitializeHeaders()
{
- _frameHeaders = HttpComponentFactory.CreateHeaders(DateHeaderValueManager);
- RequestHeaders = _frameHeaders.RequestHeaders;
- ResponseHeaders = _frameHeaders.ResponseHeaders;
+ if (_frameHeaders == null)
+ {
+ _frameHeaders = new Headers(ServerOptions);
+ RequestHeaders = _frameHeaders.RequestHeaders;
+ ResponseHeaders = _frameHeaders.ResponseHeaders;
+ }
+
+ _frameHeaders.Initialize(DateHeaderValueManager);
}
public void InitializeStreams(MessageBody messageBody)
{
- _frameStreams = HttpComponentFactory.CreateStreams(this);
+ if (_frameStreams == null)
+ {
+ _frameStreams = new Streams(this);
+ RequestBody = _frameStreams.RequestBody;
+ ResponseBody = _frameStreams.ResponseBody;
+ DuplexStream = _frameStreams.DuplexStream;
+ }
- RequestBody = _frameStreams.RequestBody.StartAcceptingReads(messageBody);
- ResponseBody = _frameStreams.ResponseBody.StartAcceptingWrites();
- DuplexStream = _frameStreams.DuplexStream;
+ _frameStreams.RequestBody.StartAcceptingReads(messageBody);
+ _frameStreams.ResponseBody.StartAcceptingWrites();
}
public void PauseStreams()
@@ -209,7 +219,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http
public void Reset()
{
- ResetComponents();
+ _frameHeaders?.Reset();
_onStarting = null;
_onCompleted = null;
@@ -246,26 +256,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http
_abortedCts = null;
}
- protected void ResetComponents()
- {
- var frameHeaders = Interlocked.Exchange(ref _frameHeaders, null);
- if (frameHeaders != null)
- {
- RequestHeaders = null;
- ResponseHeaders = null;
- HttpComponentFactory.DisposeHeaders(frameHeaders);
- }
-
- var frameStreams = Interlocked.Exchange(ref _frameStreams, null);
- if (frameStreams != null)
- {
- RequestBody = null;
- ResponseBody = null;
- DuplexStream = null;
- HttpComponentFactory.DisposeStreams(frameStreams);
- }
- }
-
///
/// Called once by Connection class to begin the RequestProcessingAsync loop.
///
diff --git a/src/Microsoft.AspNetCore.Server.Kestrel/Http/FrameOfT.cs b/src/Microsoft.AspNetCore.Server.Kestrel/Http/FrameOfT.cs
index 6c03a812d2..c0181c5463 100644
--- a/src/Microsoft.AspNetCore.Server.Kestrel/Http/FrameOfT.cs
+++ b/src/Microsoft.AspNetCore.Server.Kestrel/Http/FrameOfT.cs
@@ -155,7 +155,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http
{
await TryProduceInvalidRequestResponse();
- ResetComponents();
_abortedCts = null;
// If _requestAborted is set, the connection has already been closed.
diff --git a/src/Microsoft.AspNetCore.Server.Kestrel/Http/FrameRequestStream.cs b/src/Microsoft.AspNetCore.Server.Kestrel/Http/FrameRequestStream.cs
index a02413fdb9..c19a10a5dc 100644
--- a/src/Microsoft.AspNetCore.Server.Kestrel/Http/FrameRequestStream.cs
+++ b/src/Microsoft.AspNetCore.Server.Kestrel/Http/FrameRequestStream.cs
@@ -9,7 +9,7 @@ using Microsoft.AspNetCore.Server.Kestrel.Infrastructure;
namespace Microsoft.AspNetCore.Server.Kestrel.Http
{
- public class FrameRequestStream : Stream
+ class FrameRequestStream : Stream
{
private MessageBody _body;
private FrameStreamState _state;
@@ -132,7 +132,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http
throw new NotSupportedException();
}
- public Stream StartAcceptingReads(MessageBody body)
+ public void StartAcceptingReads(MessageBody body)
{
// Only start if not aborted
if (_state == FrameStreamState.Closed)
@@ -140,7 +140,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http
_state = FrameStreamState.Open;
_body = body;
}
- return this;
}
public void PauseAcceptingReads()
diff --git a/src/Microsoft.AspNetCore.Server.Kestrel/Http/FrameResponseStream.cs b/src/Microsoft.AspNetCore.Server.Kestrel/Http/FrameResponseStream.cs
index 522e805e10..82edafb318 100644
--- a/src/Microsoft.AspNetCore.Server.Kestrel/Http/FrameResponseStream.cs
+++ b/src/Microsoft.AspNetCore.Server.Kestrel/Http/FrameResponseStream.cs
@@ -14,8 +14,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http
private IFrameControl _frameControl;
private FrameStreamState _state;
- public FrameResponseStream()
+ public FrameResponseStream(IFrameControl frameControl)
{
+ _frameControl = frameControl;
_state = FrameStreamState.Closed;
}
@@ -94,15 +95,13 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http
return task;
}
- public Stream StartAcceptingWrites()
+ public void StartAcceptingWrites()
{
// Only start if not aborted
if (_state == FrameStreamState.Closed)
{
_state = FrameStreamState.Open;
}
-
- return this;
}
public void PauseAcceptingWrites()
@@ -134,17 +133,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http
}
}
- public void Initialize(IFrameControl frameControl)
- {
- _frameControl = frameControl;
- }
-
- public void Uninitialize()
- {
- _frameControl = null;
- _state = FrameStreamState.Closed;
- }
-
private Task ValidateState(CancellationToken cancellationToken)
{
switch (_state)
diff --git a/src/Microsoft.AspNetCore.Server.Kestrel/Infrastructure/Headers.cs b/src/Microsoft.AspNetCore.Server.Kestrel/Infrastructure/Headers.cs
new file mode 100644
index 0000000000..1f81dcdea8
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Server.Kestrel/Infrastructure/Headers.cs
@@ -0,0 +1,40 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Text;
+using Microsoft.AspNetCore.Server.Kestrel.Http;
+
+namespace Microsoft.AspNetCore.Server.Kestrel.Infrastructure
+{
+ class Headers
+ {
+ public static readonly byte[] BytesServer = Encoding.ASCII.GetBytes("\r\nServer: Kestrel");
+
+ private readonly KestrelServerOptions _options;
+
+ public Headers(KestrelServerOptions options)
+ {
+ _options = options;
+ }
+
+ public void Initialize(DateHeaderValueManager dateValueManager)
+ {
+ var dateHeaderValues = dateValueManager.GetDateHeaderValues();
+ ResponseHeaders.SetRawDate(dateHeaderValues.String, dateHeaderValues.Bytes);
+
+ if (_options.AddServerHeader)
+ {
+ ResponseHeaders.SetRawServer("Kestrel", BytesServer);
+ }
+ }
+
+ public FrameRequestHeaders RequestHeaders { get; } = new FrameRequestHeaders();
+ public FrameResponseHeaders ResponseHeaders { get; } = new FrameResponseHeaders();
+
+ public void Reset()
+ {
+ RequestHeaders.Reset();
+ ResponseHeaders.Reset();
+ }
+ }
+}
diff --git a/src/Microsoft.AspNetCore.Server.Kestrel/Infrastructure/HttpComponentFactory.cs b/src/Microsoft.AspNetCore.Server.Kestrel/Infrastructure/HttpComponentFactory.cs
deleted file mode 100644
index baeb975233..0000000000
--- a/src/Microsoft.AspNetCore.Server.Kestrel/Infrastructure/HttpComponentFactory.cs
+++ /dev/null
@@ -1,128 +0,0 @@
-// Copyright (c) .NET Foundation. All rights reserved.
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-
-using System.Collections.Concurrent;
-using System.Text;
-using Microsoft.AspNetCore.Server.Kestrel.Http;
-
-namespace Microsoft.AspNetCore.Server.Kestrel.Infrastructure
-{
- class HttpComponentFactory : IHttpComponentFactory
- {
-
- private ConcurrentQueue _streamPool = new ConcurrentQueue();
- private ConcurrentQueue _headerPool = new ConcurrentQueue();
-
- public KestrelServerOptions ServerOptions { get; set; }
-
- public HttpComponentFactory(KestrelServerOptions serverOptions)
- {
- ServerOptions = serverOptions;
- }
-
- public Streams CreateStreams(IFrameControl frameControl)
- {
- Streams streams;
-
- if (!_streamPool.TryDequeue(out streams))
- {
- streams = new Streams();
- }
-
- streams.Initialize(frameControl);
-
- return streams;
- }
-
- public void DisposeStreams(Streams streams)
- {
- if (_streamPool.Count < ServerOptions.MaxPooledStreams)
- {
- streams.Uninitialize();
-
- _streamPool.Enqueue(streams);
- }
- }
-
- public Headers CreateHeaders(DateHeaderValueManager dateValueManager)
- {
- Headers headers;
-
- if (!_headerPool.TryDequeue(out headers))
- {
- headers = new Headers(ServerOptions);
- }
-
- headers.Initialize(dateValueManager);
-
- return headers;
- }
-
- public void DisposeHeaders(Headers headers)
- {
- if (_headerPool.Count < ServerOptions.MaxPooledHeaders)
- {
- headers.Uninitialize();
-
- _headerPool.Enqueue(headers);
- }
- }
- }
-
- internal class Headers
- {
- public static readonly byte[] BytesServer = Encoding.ASCII.GetBytes("\r\nServer: Kestrel");
-
- public readonly FrameRequestHeaders RequestHeaders = new FrameRequestHeaders();
- public readonly FrameResponseHeaders ResponseHeaders = new FrameResponseHeaders();
-
- private readonly KestrelServerOptions _options;
-
- public Headers(KestrelServerOptions options)
- {
- _options = options;
- }
-
- public void Initialize(DateHeaderValueManager dateValueManager)
- {
- var dateHeaderValues = dateValueManager.GetDateHeaderValues();
-
- ResponseHeaders.SetRawDate(dateHeaderValues.String, dateHeaderValues.Bytes);
-
- if (_options.AddServerHeader)
- {
- ResponseHeaders.SetRawServer(Constants.ServerName, BytesServer);
- }
- }
-
- public void Uninitialize()
- {
- RequestHeaders.Reset();
- ResponseHeaders.Reset();
- }
- }
-
- internal class Streams
- {
- public readonly FrameRequestStream RequestBody;
- public readonly FrameResponseStream ResponseBody;
- public readonly FrameDuplexStream DuplexStream;
-
- public Streams()
- {
- RequestBody = new FrameRequestStream();
- ResponseBody = new FrameResponseStream();
- DuplexStream = new FrameDuplexStream(RequestBody, ResponseBody);
- }
-
- public void Initialize(IFrameControl frameControl)
- {
- ResponseBody.Initialize(frameControl);
- }
-
- public void Uninitialize()
- {
- ResponseBody.Uninitialize();
- }
- }
-}
diff --git a/src/Microsoft.AspNetCore.Server.Kestrel/Infrastructure/IHttpComponentFactory.cs b/src/Microsoft.AspNetCore.Server.Kestrel/Infrastructure/IHttpComponentFactory.cs
deleted file mode 100644
index 3dcb234bbd..0000000000
--- a/src/Microsoft.AspNetCore.Server.Kestrel/Infrastructure/IHttpComponentFactory.cs
+++ /dev/null
@@ -1,20 +0,0 @@
-// Copyright (c) .NET Foundation. All rights reserved.
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-
-using Microsoft.AspNetCore.Server.Kestrel.Http;
-
-namespace Microsoft.AspNetCore.Server.Kestrel.Infrastructure
-{
- interface IHttpComponentFactory
- {
- KestrelServerOptions ServerOptions { get; set; }
-
- Streams CreateStreams(IFrameControl frameControl);
-
- void DisposeStreams(Streams streams);
-
- Headers CreateHeaders(DateHeaderValueManager dateValueManager);
-
- void DisposeHeaders(Headers headers);
- }
-}
diff --git a/src/Microsoft.AspNetCore.Server.Kestrel/Infrastructure/Streams.cs b/src/Microsoft.AspNetCore.Server.Kestrel/Infrastructure/Streams.cs
new file mode 100644
index 0000000000..af0a3384c2
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Server.Kestrel/Infrastructure/Streams.cs
@@ -0,0 +1,21 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using Microsoft.AspNetCore.Server.Kestrel.Http;
+
+namespace Microsoft.AspNetCore.Server.Kestrel.Infrastructure
+{
+ class Streams
+ {
+ public Streams(IFrameControl frameControl)
+ {
+ RequestBody = new FrameRequestStream();
+ ResponseBody = new FrameResponseStream(frameControl);
+ DuplexStream = new FrameDuplexStream(RequestBody, ResponseBody);
+ }
+
+ public FrameRequestStream RequestBody { get; }
+ public FrameResponseStream ResponseBody { get; }
+ public FrameDuplexStream DuplexStream { get; }
+ }
+}
diff --git a/src/Microsoft.AspNetCore.Server.Kestrel/KestrelServer.cs b/src/Microsoft.AspNetCore.Server.Kestrel/KestrelServer.cs
index 35b134d152..558794bf6a 100644
--- a/src/Microsoft.AspNetCore.Server.Kestrel/KestrelServer.cs
+++ b/src/Microsoft.AspNetCore.Server.Kestrel/KestrelServer.cs
@@ -44,8 +44,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel
_applicationLifetime = applicationLifetime;
_logger = loggerFactory.CreateLogger(typeof(KestrelServer).GetTypeInfo().Namespace);
Features = new FeatureCollection();
- var componentFactory = new HttpComponentFactory(Options);
- Features.Set(componentFactory);
_serverAddresses = new ServerAddressesFeature();
Features.Set(_serverAddresses);
}
@@ -65,7 +63,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel
try
{
- var componentFactory = Features.Get();
var dateHeaderValueManager = new DateHeaderValueManager();
var trace = new KestrelTrace(_logger);
var engine = new KestrelEngine(new ServiceContext
@@ -78,8 +75,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel
Log = trace,
ThreadPool = new LoggingThreadPool(trace),
DateHeaderValueManager = dateHeaderValueManager,
- ServerOptions = Options,
- HttpComponentFactory = componentFactory
+ ServerOptions = Options
});
_disposables.Push(engine);
diff --git a/src/Microsoft.AspNetCore.Server.Kestrel/KestrelServerOptions.cs b/src/Microsoft.AspNetCore.Server.Kestrel/KestrelServerOptions.cs
index 708eb240b2..7a6c7953e7 100644
--- a/src/Microsoft.AspNetCore.Server.Kestrel/KestrelServerOptions.cs
+++ b/src/Microsoft.AspNetCore.Server.Kestrel/KestrelServerOptions.cs
@@ -12,24 +12,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel
public IConnectionFilter ConnectionFilter { get; set; }
- ///
- /// Gets or sets value that instructs whether it is safe to
- /// pool the Request and Response objects
- /// for another request after the Response's OnCompleted callback has fired.
- /// When this values is greater than zero, it is not safe to retain references to feature components after this event has fired.
- /// Value is zero by default.
- ///
- public int MaxPooledStreams { get; set; }
-
- ///
- /// Gets or sets value that instructs whether it is safe to
- /// pool the Request and Response headers
- /// for another request after the Response's OnCompleted callback has fired.
- /// When this values is greater than zero, it is not safe to retain references to feature components after this event has fired.
- /// Value is zero by default.
- ///
- public int MaxPooledHeaders { get; set; }
-
public bool NoDelay { get; set; } = true;
///
diff --git a/src/Microsoft.AspNetCore.Server.Kestrel/ServiceContext.cs b/src/Microsoft.AspNetCore.Server.Kestrel/ServiceContext.cs
index 72103028d1..a2e1aa4f53 100644
--- a/src/Microsoft.AspNetCore.Server.Kestrel/ServiceContext.cs
+++ b/src/Microsoft.AspNetCore.Server.Kestrel/ServiceContext.cs
@@ -2,10 +2,7 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
-using System.Net;
using Microsoft.AspNetCore.Hosting;
-using Microsoft.AspNetCore.Http.Features;
-using Microsoft.AspNetCore.Server.Kestrel.Filter;
using Microsoft.AspNetCore.Server.Kestrel.Http;
using Microsoft.AspNetCore.Server.Kestrel.Infrastructure;
@@ -25,7 +22,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel
FrameFactory = context.FrameFactory;
DateHeaderValueManager = context.DateHeaderValueManager;
ServerOptions = context.ServerOptions;
- HttpComponentFactory = context.HttpComponentFactory;
}
public IApplicationLifetime AppLifetime { get; set; }
@@ -39,7 +35,5 @@ namespace Microsoft.AspNetCore.Server.Kestrel
public DateHeaderValueManager DateHeaderValueManager { get; set; }
public KestrelServerOptions ServerOptions { get; set; }
-
- internal IHttpComponentFactory HttpComponentFactory { get; set; }
}
}
diff --git a/test/Microsoft.AspNetCore.Server.KestrelTests/EngineTests.cs b/test/Microsoft.AspNetCore.Server.KestrelTests/EngineTests.cs
index 413a3c64f2..a7c88d914e 100644
--- a/test/Microsoft.AspNetCore.Server.KestrelTests/EngineTests.cs
+++ b/test/Microsoft.AspNetCore.Server.KestrelTests/EngineTests.cs
@@ -170,13 +170,15 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
[Theory]
[MemberData(nameof(ConnectionFilterData))]
- public async Task ReuseStreamsOn(ServiceContext testContext)
+ public async Task HeadersAndStreamsAreReused(ServiceContext testContext)
{
- testContext.ServerOptions.MaxPooledStreams = 120;
-
var streamCount = 0;
+ var requestHeadersCount = 0;
+ var responseHeadersCount = 0;
var loopCount = 20;
Stream lastStream = null;
+ IHeaderDictionary lastRequestHeaders = null;
+ IHeaderDictionary lastResponseHeaders = null;
using (var server = new TestServer(
context =>
@@ -186,6 +188,16 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
lastStream = context.Request.Body;
streamCount++;
}
+ if (context.Request.Headers != lastRequestHeaders)
+ {
+ lastRequestHeaders = context.Request.Headers;
+ requestHeadersCount++;
+ }
+ if (context.Response.Headers != lastResponseHeaders)
+ {
+ lastResponseHeaders = context.Response.Headers;
+ responseHeadersCount++;
+ }
context.Response.Headers.Clear();
return context.Request.Body.CopyToAsync(context.Response.Body);
},
@@ -208,49 +220,8 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
}
Assert.Equal(1, streamCount);
- }
- }
-
- [Theory]
- [MemberData(nameof(ConnectionFilterData))]
- public async Task ReuseStreamsOff(ServiceContext testContext)
- {
- testContext.ServerOptions.MaxPooledStreams = 0;
-
- var streamCount = 0;
- var loopCount = 20;
- Stream lastStream = null;
-
- using (var server = new TestServer(
- context =>
- {
- if (context.Request.Body != lastStream)
- {
- lastStream = context.Request.Body;
- streamCount++;
- }
- context.Response.Headers.Clear();
- return context.Request.Body.CopyToAsync(context.Response.Body);
- },
- testContext))
- {
-
- using (var connection = new TestConnection(server.Port))
- {
- var requestData =
- Enumerable.Repeat("GET / HTTP/1.1\r\n", loopCount)
- .Concat(new[] { "GET / HTTP/1.1\r\nConnection: close\r\n\r\nGoodbye" });
-
- var responseData =
- Enumerable.Repeat("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n", loopCount)
- .Concat(new[] { "HTTP/1.1 200 OK\r\nConnection: close\r\n\r\nGoodbye" });
-
- await connection.SendEnd(requestData.ToArray());
-
- await connection.ReceiveEnd(responseData.ToArray());
- }
-
- Assert.Equal(loopCount + 1, streamCount);
+ Assert.Equal(1, requestHeadersCount);
+ Assert.Equal(1, responseHeadersCount);
}
}
diff --git a/test/Microsoft.AspNetCore.Server.KestrelTests/FrameResponseHeadersTests.cs b/test/Microsoft.AspNetCore.Server.KestrelTests/FrameResponseHeadersTests.cs
index 097d4dac9a..41f3fd3f78 100644
--- a/test/Microsoft.AspNetCore.Server.KestrelTests/FrameResponseHeadersTests.cs
+++ b/test/Microsoft.AspNetCore.Server.KestrelTests/FrameResponseHeadersTests.cs
@@ -6,8 +6,6 @@ using System.Collections.Generic;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Server.Kestrel;
using Microsoft.AspNetCore.Server.Kestrel.Http;
-using Microsoft.AspNetCore.Server.Kestrel.Infrastructure;
-using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Primitives;
using Xunit;
@@ -27,7 +25,6 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
DateHeaderValueManager = new DateHeaderValueManager(),
ServerAddress = ServerAddress.FromUrl("http://localhost:5000"),
ServerOptions = serverOptions,
- HttpComponentFactory = new HttpComponentFactory(serverOptions)
};
var frame = new Frame