diff --git a/src/Microsoft.AspNetCore.Server.Kestrel/Http/Connection.cs b/src/Microsoft.AspNetCore.Server.Kestrel/Http/Connection.cs
index 15f57a8076..3d335971e5 100644
--- a/src/Microsoft.AspNetCore.Server.Kestrel/Http/Connection.cs
+++ b/src/Microsoft.AspNetCore.Server.Kestrel/Http/Connection.cs
@@ -61,7 +61,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http
}
// Don't initialize _frame until SocketInput and SocketOutput are set to their final values.
- if (ConnectionFilter == null)
+ if (ServerInformation.ConnectionFilter == null)
{
SocketInput = _rawSocketInput;
SocketOutput = _rawSocketOutput;
@@ -81,7 +81,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http
try
{
- ConnectionFilter.OnConnectionAsync(_filterContext).ContinueWith((task, state) =>
+ ServerInformation.ConnectionFilter.OnConnectionAsync(_filterContext).ContinueWith((task, state) =>
{
var connection = (Connection)state;
diff --git a/src/Microsoft.AspNetCore.Server.Kestrel/Http/Frame.cs b/src/Microsoft.AspNetCore.Server.Kestrel/Http/Frame.cs
index b84f63e87f..f40d4a06ee 100644
--- a/src/Microsoft.AspNetCore.Server.Kestrel/Http/Frame.cs
+++ b/src/Microsoft.AspNetCore.Server.Kestrel/Http/Frame.cs
@@ -45,6 +45,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http
private readonly object _onStartingSync = new Object();
private readonly object _onCompletedSync = new Object();
+ protected bool _poolingPermitted = true;
private Headers _frameHeaders;
private Streams _frameStreams;
@@ -279,7 +280,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http
DuplexStream = null;
var frameStreams = _frameStreams;
_frameStreams = null;
- HttpComponentFactory.DisposeStreams(frameStreams, poolingPermitted: (poolingPermitted && ReuseStreams));
+ HttpComponentFactory.DisposeStreams(frameStreams, poolingPermitted);
}
}
diff --git a/src/Microsoft.AspNetCore.Server.Kestrel/Http/FrameOfT.cs b/src/Microsoft.AspNetCore.Server.Kestrel/Http/FrameOfT.cs
index 1c0191c6cb..affb4d5867 100644
--- a/src/Microsoft.AspNetCore.Server.Kestrel/Http/FrameOfT.cs
+++ b/src/Microsoft.AspNetCore.Server.Kestrel/Http/FrameOfT.cs
@@ -131,14 +131,15 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http
}
catch (Exception ex)
{
+ // Error occurred, do not return components to pool
+ _poolingPermitted = false;
Log.LogWarning(0, ex, "Connection processing ended abnormally");
}
finally
{
- // Error occurred, do not return components to pool
- ResetComponents(poolingPermitted: false);
try
{
+ ResetComponents(poolingPermitted: _poolingPermitted);
_abortedCts = null;
// If _requestAborted is set, the connection has already been closed.
diff --git a/src/Microsoft.AspNetCore.Server.Kestrel/Http/TcpListener.cs b/src/Microsoft.AspNetCore.Server.Kestrel/Http/TcpListener.cs
index 4c0f8f19ba..fc845b5239 100644
--- a/src/Microsoft.AspNetCore.Server.Kestrel/Http/TcpListener.cs
+++ b/src/Microsoft.AspNetCore.Server.Kestrel/Http/TcpListener.cs
@@ -23,7 +23,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http
{
var socket = new UvTcpHandle(Log);
socket.Init(Thread.Loop, Thread.QueueCloseHandle);
- socket.NoDelay(NoDelay);
+ socket.NoDelay(ServerInformation.NoDelay);
socket.Bind(ServerAddress);
socket.Listen(Constants.ListenBacklog, (stream, status, error, state) => ConnectionCallback(stream, status, error, state), this);
return socket;
@@ -41,7 +41,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http
try
{
acceptSocket.Init(Thread.Loop, Thread.QueueCloseHandle);
- acceptSocket.NoDelay(NoDelay);
+ acceptSocket.NoDelay(ServerInformation.NoDelay);
listenSocket.Accept(acceptSocket);
DispatchConnection(acceptSocket);
diff --git a/src/Microsoft.AspNetCore.Server.Kestrel/Http/TcpListenerPrimary.cs b/src/Microsoft.AspNetCore.Server.Kestrel/Http/TcpListenerPrimary.cs
index 6c1697cfe8..e791d30568 100644
--- a/src/Microsoft.AspNetCore.Server.Kestrel/Http/TcpListenerPrimary.cs
+++ b/src/Microsoft.AspNetCore.Server.Kestrel/Http/TcpListenerPrimary.cs
@@ -25,7 +25,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http
{
var socket = new UvTcpHandle(Log);
socket.Init(Thread.Loop, Thread.QueueCloseHandle);
- socket.NoDelay(NoDelay);
+ socket.NoDelay(ServerInformation.NoDelay);
socket.Bind(ServerAddress);
socket.Listen(Constants.ListenBacklog, (stream, status, error, state) => ConnectionCallback(stream, status, error, state), this);
return socket;
@@ -43,7 +43,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http
try
{
acceptSocket.Init(Thread.Loop, Thread.QueueCloseHandle);
- acceptSocket.NoDelay(NoDelay);
+ acceptSocket.NoDelay(ServerInformation.NoDelay);
listenSocket.Accept(acceptSocket);
DispatchConnection(acceptSocket);
diff --git a/src/Microsoft.AspNetCore.Server.Kestrel/Http/TcpListenerSecondary.cs b/src/Microsoft.AspNetCore.Server.Kestrel/Http/TcpListenerSecondary.cs
index ce0616171b..d52ac62b7e 100644
--- a/src/Microsoft.AspNetCore.Server.Kestrel/Http/TcpListenerSecondary.cs
+++ b/src/Microsoft.AspNetCore.Server.Kestrel/Http/TcpListenerSecondary.cs
@@ -21,7 +21,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http
{
var acceptSocket = new UvTcpHandle(Log);
acceptSocket.Init(Thread.Loop, Thread.QueueCloseHandle);
- acceptSocket.NoDelay(NoDelay);
+ acceptSocket.NoDelay(ServerInformation.NoDelay);
return acceptSocket;
}
}
diff --git a/src/Microsoft.AspNetCore.Server.Kestrel/IKestrelServerInformation.cs b/src/Microsoft.AspNetCore.Server.Kestrel/IKestrelServerInformation.cs
index a08db08ea6..fa81591879 100644
--- a/src/Microsoft.AspNetCore.Server.Kestrel/IKestrelServerInformation.cs
+++ b/src/Microsoft.AspNetCore.Server.Kestrel/IKestrelServerInformation.cs
@@ -12,17 +12,13 @@ namespace Microsoft.AspNetCore.Server.Kestrel
bool NoDelay { get; set; }
///
- /// Gets or sets a flag that instructs whether it is safe to
- /// reuse the Request and Response objects
+ /// Gets or sets values that instruct whether it is safe to
+ /// pool the Request and Response objects, Headers etc
/// for another request after the Response's OnCompleted callback has fired.
- /// When this is set to true it is not safe to retain references to these streams after this event has fired.
- /// It is false by default.
+ /// When these values are greater than zero it is not safe to retain references to feature components after this event has fired.
+ /// They are zero by default.
///
- ///
- /// When this is set to true it is not safe to retain references to these streams after this event has fired.
- /// It is false by default.
- ///
- bool ReuseStreams { get; set; }
+ KestrelServerPoolingParameters PoolingParameters { get; }
IConnectionFilter ConnectionFilter { get; set; }
}
diff --git a/src/Microsoft.AspNetCore.Server.Kestrel/Infrastructure/HttpComponentFactory.cs b/src/Microsoft.AspNetCore.Server.Kestrel/Infrastructure/HttpComponentFactory.cs
index 98cdfef311..f58102c1bb 100644
--- a/src/Microsoft.AspNetCore.Server.Kestrel/Infrastructure/HttpComponentFactory.cs
+++ b/src/Microsoft.AspNetCore.Server.Kestrel/Infrastructure/HttpComponentFactory.cs
@@ -2,17 +2,24 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Concurrent;
-using Microsoft.AspNetCore.Server.Kestrel.Http;
using System.Text;
+using Microsoft.AspNetCore.Server.Kestrel.Http;
namespace Microsoft.AspNetCore.Server.Kestrel.Infrastructure
{
class HttpComponentFactory : IHttpComponentFactory
{
- private const int _maxPooledComponents = 128;
+
private ConcurrentQueue _streamPool = new ConcurrentQueue();
private ConcurrentQueue _headerPool = new ConcurrentQueue();
+ public IKestrelServerInformation ServerInformation { get; set; }
+
+ public HttpComponentFactory(IKestrelServerInformation serverInformation)
+ {
+ ServerInformation = serverInformation;
+ }
+
public Streams CreateStreams(FrameContext owner)
{
Streams streams;
@@ -29,7 +36,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Infrastructure
public void DisposeStreams(Streams streams, bool poolingPermitted)
{
- if (poolingPermitted && _streamPool.Count < _maxPooledComponents)
+ if (poolingPermitted && _streamPool.Count < ServerInformation.PoolingParameters.MaxPooledStreams)
{
streams.Uninitialize();
@@ -53,7 +60,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Infrastructure
public void DisposeHeaders(Headers headers, bool poolingPermitted)
{
- if (poolingPermitted && _headerPool.Count < _maxPooledComponents)
+ if (poolingPermitted && _headerPool.Count < ServerInformation.PoolingParameters.MaxPooledHeaders)
{
headers.Uninitialize();
diff --git a/src/Microsoft.AspNetCore.Server.Kestrel/Infrastructure/IHttpComponentFactory.cs b/src/Microsoft.AspNetCore.Server.Kestrel/Infrastructure/IHttpComponentFactory.cs
index b02300774a..620c663fbb 100644
--- a/src/Microsoft.AspNetCore.Server.Kestrel/Infrastructure/IHttpComponentFactory.cs
+++ b/src/Microsoft.AspNetCore.Server.Kestrel/Infrastructure/IHttpComponentFactory.cs
@@ -7,6 +7,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Infrastructure
{
interface IHttpComponentFactory
{
+ IKestrelServerInformation ServerInformation { get; set; }
+
Streams CreateStreams(FrameContext owner);
void DisposeStreams(Streams streams, bool poolingPermitted);
diff --git a/src/Microsoft.AspNetCore.Server.Kestrel/KestrelServer.cs b/src/Microsoft.AspNetCore.Server.Kestrel/KestrelServer.cs
index 6e3826d971..b6768de672 100644
--- a/src/Microsoft.AspNetCore.Server.Kestrel/KestrelServer.cs
+++ b/src/Microsoft.AspNetCore.Server.Kestrel/KestrelServer.cs
@@ -54,9 +54,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel
try
{
var information = (KestrelServerInformation)Features.Get();
+ var componentFactory = Features.Get();
var dateHeaderValueManager = new DateHeaderValueManager();
var trace = new KestrelTrace(_logger);
- var componentFactory = new HttpComponentFactory();
var engine = new KestrelEngine(new ServiceContext
{
FrameFactory = (context, remoteEP, localEP, prepareRequest) =>
@@ -67,9 +67,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel
Log = trace,
ThreadPool = new LoggingThreadPool(trace),
DateHeaderValueManager = dateHeaderValueManager,
- ConnectionFilter = information.ConnectionFilter,
- NoDelay = information.NoDelay,
- ReuseStreams = information.ReuseStreams,
+ ServerInformation = information,
HttpComponentFactory = componentFactory
});
diff --git a/src/Microsoft.AspNetCore.Server.Kestrel/KestrelServerInformation.cs b/src/Microsoft.AspNetCore.Server.Kestrel/KestrelServerInformation.cs
index 6f05a1da58..b1d4d335ab 100644
--- a/src/Microsoft.AspNetCore.Server.Kestrel/KestrelServerInformation.cs
+++ b/src/Microsoft.AspNetCore.Server.Kestrel/KestrelServerInformation.cs
@@ -22,7 +22,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel
Addresses = GetAddresses(configuration);
ThreadCount = GetThreadCount(configuration);
NoDelay = GetNoDelay(configuration);
- ReuseStreams = GetReuseStreams(configuration);
+ PoolingParameters = new KestrelServerPoolingParameters(configuration);
}
public ICollection Addresses { get; }
@@ -31,7 +31,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel
public bool NoDelay { get; set; }
- public bool ReuseStreams { get; set; }
+ public KestrelServerPoolingParameters PoolingParameters { get; }
public IConnectionFilter ConnectionFilter { get; set; }
@@ -110,18 +110,5 @@ namespace Microsoft.AspNetCore.Server.Kestrel
return true;
}
-
- private static bool GetReuseStreams(IConfiguration configuration)
- {
- var reuseStreamsString = configuration["kestrel.reuseStreams"];
-
- bool reuseStreams;
- if (bool.TryParse(reuseStreamsString, out reuseStreams))
- {
- return reuseStreams;
- }
-
- return false;
- }
}
}
diff --git a/src/Microsoft.AspNetCore.Server.Kestrel/KestrelServerPoolingParameters.cs b/src/Microsoft.AspNetCore.Server.Kestrel/KestrelServerPoolingParameters.cs
new file mode 100644
index 0000000000..9a019d1d94
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Server.Kestrel/KestrelServerPoolingParameters.cs
@@ -0,0 +1,43 @@
+// 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;
+using System.Globalization;
+using Microsoft.Extensions.Configuration;
+
+namespace Microsoft.AspNetCore.Server.Kestrel
+{
+ public class KestrelServerPoolingParameters
+ {
+ public KestrelServerPoolingParameters(IConfiguration configuration)
+ {
+ if (configuration == null)
+ {
+ throw new ArgumentNullException(nameof(configuration));
+ }
+
+ MaxPooledStreams = GetPooledCount(configuration["kestrel.maxPooledStreams"]);
+ MaxPooledHeaders = GetPooledCount(configuration["kestrel.maxPooledHeaders"]);
+ }
+
+ public int MaxPooledStreams { get; set; }
+
+ public int MaxPooledHeaders { get; set; }
+
+ private static int GetPooledCount(string countString)
+ {
+ if (string.IsNullOrEmpty(countString))
+ {
+ return 0;
+ }
+
+ int count;
+ if (int.TryParse(countString, NumberStyles.Integer, CultureInfo.InvariantCulture, out count))
+ {
+ return count;
+ }
+
+ return 0;
+ }
+ }
+}
diff --git a/src/Microsoft.AspNetCore.Server.Kestrel/ServerFactory.cs b/src/Microsoft.AspNetCore.Server.Kestrel/ServerFactory.cs
index 06a7fb3686..c6bf62f604 100644
--- a/src/Microsoft.AspNetCore.Server.Kestrel/ServerFactory.cs
+++ b/src/Microsoft.AspNetCore.Server.Kestrel/ServerFactory.cs
@@ -6,6 +6,7 @@ using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Hosting.Server;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Server.Features;
+using Microsoft.AspNetCore.Server.Kestrel.Infrastructure;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
@@ -28,8 +29,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel
public IServer CreateServer(IConfiguration configuration)
{
var information = new KestrelServerInformation(configuration);
+ var componentFactory = new HttpComponentFactory(information);
var serverFeatures = new FeatureCollection();
serverFeatures.Set(information);
+ serverFeatures.Set(componentFactory);
serverFeatures.Set(information);
return new KestrelServer(serverFeatures, _appLifetime, _loggerFactory.CreateLogger("Microsoft.AspNetCore.Server.Kestrel"));
}
diff --git a/src/Microsoft.AspNetCore.Server.Kestrel/ServiceContext.cs b/src/Microsoft.AspNetCore.Server.Kestrel/ServiceContext.cs
index b0d26c0953..f702fd1c5e 100644
--- a/src/Microsoft.AspNetCore.Server.Kestrel/ServiceContext.cs
+++ b/src/Microsoft.AspNetCore.Server.Kestrel/ServiceContext.cs
@@ -24,9 +24,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel
ThreadPool = context.ThreadPool;
FrameFactory = context.FrameFactory;
DateHeaderValueManager = context.DateHeaderValueManager;
- ConnectionFilter = context.ConnectionFilter;
- NoDelay = context.NoDelay;
- ReuseStreams = context.ReuseStreams;
+ ServerInformation = context.ServerInformation;
HttpComponentFactory = context.HttpComponentFactory;
}
@@ -40,11 +38,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel
public DateHeaderValueManager DateHeaderValueManager { get; set; }
- public IConnectionFilter ConnectionFilter { get; set; }
-
- public bool NoDelay { get; set; }
-
- public bool ReuseStreams { get; set; }
+ public IKestrelServerInformation ServerInformation { get; set; }
internal IHttpComponentFactory HttpComponentFactory { get; set; }
}
diff --git a/test/Microsoft.AspNetCore.Server.Kestrel.FunctionalTests/RequestTests.cs b/test/Microsoft.AspNetCore.Server.Kestrel.FunctionalTests/RequestTests.cs
index 3f9b0870dd..a6fac125ea 100644
--- a/test/Microsoft.AspNetCore.Server.Kestrel.FunctionalTests/RequestTests.cs
+++ b/test/Microsoft.AspNetCore.Server.Kestrel.FunctionalTests/RequestTests.cs
@@ -132,7 +132,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
[ConditionalFact]
[FrameworkSkipCondition(RuntimeFrameworks.Mono, SkipReason = "Test hangs after execution on Mono.")]
- public async Task RequestPathIsNormalized()
+ public void RequestPathIsNormalized()
{
var port = PortManager.GetPort();
var config = new ConfigurationBuilder().AddInMemoryCollection(
diff --git a/test/Microsoft.AspNetCore.Server.KestrelTests/ConnectionFilterTests.cs b/test/Microsoft.AspNetCore.Server.KestrelTests/ConnectionFilterTests.cs
index 353d998e5d..dce414fa3e 100644
--- a/test/Microsoft.AspNetCore.Server.KestrelTests/ConnectionFilterTests.cs
+++ b/test/Microsoft.AspNetCore.Server.KestrelTests/ConnectionFilterTests.cs
@@ -36,10 +36,8 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
public async Task CanReadAndWriteWithRewritingConnectionFilter()
{
var filter = new RewritingConnectionFilter();
- var serviceContext = new TestServiceContext()
- {
- ConnectionFilter = filter
- };
+ var serviceContext = new TestServiceContext(filter);
+
var sendString = "POST / HTTP/1.0\r\n\r\nHello World?";
using (var server = new TestServer(App, serviceContext))
@@ -62,10 +60,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
[FrameworkSkipCondition(RuntimeFrameworks.Mono, SkipReason = "Test hangs after execution on Mono.")]
public async Task CanReadAndWriteWithAsyncConnectionFilter()
{
- var serviceContext = new TestServiceContext()
- {
- ConnectionFilter = new AsyncConnectionFilter()
- };
+ var serviceContext = new TestServiceContext(new AsyncConnectionFilter());
using (var server = new TestServer(App, serviceContext))
{
@@ -87,10 +82,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
[FrameworkSkipCondition(RuntimeFrameworks.Mono, SkipReason = "Test hangs after execution on Mono.")]
public async Task ThrowingSynchronousConnectionFilterDoesNotCrashServer()
{
- var serviceContext = new TestServiceContext()
- {
- ConnectionFilter = new ThrowingConnectionFilter()
- };
+ var serviceContext = new TestServiceContext(new ThrowingConnectionFilter());
using (var server = new TestServer(App, serviceContext))
{
diff --git a/test/Microsoft.AspNetCore.Server.KestrelTests/EngineTests.cs b/test/Microsoft.AspNetCore.Server.KestrelTests/EngineTests.cs
index 42315796d5..73c159299d 100644
--- a/test/Microsoft.AspNetCore.Server.KestrelTests/EngineTests.cs
+++ b/test/Microsoft.AspNetCore.Server.KestrelTests/EngineTests.cs
@@ -35,10 +35,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
new TestServiceContext()
},
{
- new TestServiceContext
- {
- ConnectionFilter = new PassThroughConnectionFilter()
- }
+ new TestServiceContext(new PassThroughConnectionFilter())
}
};
}
@@ -188,7 +185,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
[MemberData(nameof(ConnectionFilterData))]
public async Task ReuseStreamsOn(ServiceContext testContext)
{
- testContext.ReuseStreams = true;
+ testContext.ServerInformation.PoolingParameters.MaxPooledStreams = 120;
var streamCount = 0;
var loopCount = 20;
@@ -231,7 +228,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
[MemberData(nameof(ConnectionFilterData))]
public async Task ReuseStreamsOff(ServiceContext testContext)
{
- testContext.ReuseStreams = false;
+ testContext.ServerInformation.PoolingParameters.MaxPooledStreams = 0;
var streamCount = 0;
var loopCount = 20;
diff --git a/test/Microsoft.AspNetCore.Server.KestrelTests/FrameResponseHeadersTests.cs b/test/Microsoft.AspNetCore.Server.KestrelTests/FrameResponseHeadersTests.cs
index 8bdf782e9f..30c03a0b35 100644
--- a/test/Microsoft.AspNetCore.Server.KestrelTests/FrameResponseHeadersTests.cs
+++ b/test/Microsoft.AspNetCore.Server.KestrelTests/FrameResponseHeadersTests.cs
@@ -6,6 +6,7 @@ using System.Collections.Generic;
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;
@@ -16,12 +17,15 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
[Fact]
public void InitialDictionaryContainsServerAndDate()
{
+ var configuration = new ConfigurationBuilder().Build();
+ var serverInformation = new KestrelServerInformation(configuration);
var connectionContext = new ConnectionContext
{
DateHeaderValueManager = new DateHeaderValueManager(),
ServerAddress = ServerAddress.FromUrl("http://localhost:5000"),
- HttpComponentFactory = new HttpComponentFactory()
- };
+ ServerInformation = serverInformation,
+ HttpComponentFactory = new HttpComponentFactory(serverInformation)
+ };
var frame = new Frame