diff --git a/samples/SampleApp/Startup.cs b/samples/SampleApp/Startup.cs index 1590a8dac3..7baa8d1c22 100644 --- a/samples/SampleApp/Startup.cs +++ b/samples/SampleApp/Startup.cs @@ -3,12 +3,9 @@ using System; using System.IO; -using System.Security.Cryptography.X509Certificates; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Server.Kestrel; -using Microsoft.AspNetCore.Server.Kestrel.Filter; using Microsoft.Extensions.Logging; using Microsoft.Extensions.PlatformAbstractions; @@ -18,25 +15,8 @@ namespace SampleApp { public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory, IApplicationEnvironment env) { - var ksi = app.ServerFeatures.Get(); - //ksi.ThreadCount = 4; - ksi.NoDelay = true; - loggerFactory.AddConsole(LogLevel.Trace); - var testCertPath = Path.Combine(env.ApplicationBasePath, "testCert.pfx"); - - if (File.Exists(testCertPath)) - { - app.UseKestrelHttps(new X509Certificate2(testCertPath, "testPassword")); - } - else - { - Console.WriteLine("Could not find certificate at '{0}'. HTTPS is not enabled.", testCertPath); - } - - app.UseKestrelConnectionLogging(); - app.Run(async context => { Console.WriteLine("{0} {1}{2}{3}", @@ -63,7 +43,13 @@ namespace SampleApp { var host = new WebHostBuilder() .UseDefaultHostingConfiguration(args) - .UseKestrel() + .UseKestrel(options => + { + // options.ThreadCount = 4; + options.NoDelay = true; + options.UseHttps("testCert.pfx", "testPassword"); + options.UseConnectionLogging(); + }) .UseUrls("http://localhost:5000", "https://localhost:5001") .UseContentRoot(Directory.GetCurrentDirectory()) .UseStartup() diff --git a/src/Microsoft.AspNetCore.Server.Kestrel.Https/HttpsApplicationBuilderExtensions.cs b/src/Microsoft.AspNetCore.Server.Kestrel.Https/HttpsApplicationBuilderExtensions.cs deleted file mode 100644 index eb8a95862b..0000000000 --- a/src/Microsoft.AspNetCore.Server.Kestrel.Https/HttpsApplicationBuilderExtensions.cs +++ /dev/null @@ -1,34 +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.Security.Cryptography.X509Certificates; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Http.Features; -using Microsoft.AspNetCore.Server.Kestrel.Https; - -namespace Microsoft.AspNetCore.Server.Kestrel.Filter -{ - public static class HttpsApplicationBuilderExtensions - { - public static IApplicationBuilder UseKestrelHttps(this IApplicationBuilder app, X509Certificate2 cert) - { - return app.UseKestrelHttps(new HttpsConnectionFilterOptions { ServerCertificate = cert}); - } - - public static IApplicationBuilder UseKestrelHttps(this IApplicationBuilder app, HttpsConnectionFilterOptions options) - { - var serverInfo = app.ServerFeatures.Get(); - - if (serverInfo == null) - { - return app; - } - - var prevFilter = serverInfo.ConnectionFilter ?? new NoOpConnectionFilter(); - - serverInfo.ConnectionFilter = new HttpsConnectionFilter(options, prevFilter); - - return app; - } - } -} diff --git a/src/Microsoft.AspNetCore.Server.Kestrel.Https/KestrelServerOptionsHttpsExtensions.cs b/src/Microsoft.AspNetCore.Server.Kestrel.Https/KestrelServerOptionsHttpsExtensions.cs new file mode 100644 index 0000000000..db1d28bd6a --- /dev/null +++ b/src/Microsoft.AspNetCore.Server.Kestrel.Https/KestrelServerOptionsHttpsExtensions.cs @@ -0,0 +1,90 @@ +// 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.IO; +using System.Security.Cryptography.X509Certificates; +using Microsoft.AspNetCore.Server.Kestrel; +using Microsoft.AspNetCore.Server.Kestrel.Filter; +using Microsoft.AspNetCore.Server.Kestrel.Https; +using Microsoft.Extensions.DependencyInjection; + +namespace Microsoft.AspNetCore.Hosting +{ + public static class KestrelServerOptionsHttpsExtensions + { + /// + /// Configure Kestrel to use HTTPS. + /// + /// + /// The Microsoft.AspNetCore.Server.KestrelServerOptions to configure. + /// + /// + /// The name of a certificate file, relative to the directory that contains the application content files. + /// + /// + /// The Microsoft.AspNetCore.Server.KestrelServerOptions. + /// + public static KestrelServerOptions UseHttps(this KestrelServerOptions options, string fileName) + { + var env = options.ApplicationServices.GetRequiredService(); + return options.UseHttps(new X509Certificate2(Path.Combine(env.ContentRootPath, fileName))); + } + + /// + /// Configure Kestrel to use HTTPS. + /// + /// + /// The Microsoft.AspNetCore.Server.KestrelServerOptions to configure. + /// + /// + /// The name of a certificate file, relative to the directory that contains the application content files. + /// + /// + /// The password required to access the X.509 certificate data. + /// + /// + /// The Microsoft.AspNetCore.Server.KestrelServerOptions. + /// + public static KestrelServerOptions UseHttps(this KestrelServerOptions options, string fileName, string password) + { + var env = options.ApplicationServices.GetRequiredService(); + return options.UseHttps(new X509Certificate2(Path.Combine(env.ContentRootPath, fileName), password)); + } + + /// + /// Configure Kestrel to use HTTPS. + /// + /// + /// The Microsoft.AspNetCore.Server.KestrelServerOptions to configure. + /// + /// + /// The X.509 certificate. + /// + /// + /// The Microsoft.AspNetCore.Server.KestrelServerOptions. + /// + public static KestrelServerOptions UseHttps(this KestrelServerOptions options, X509Certificate2 serverCertificate) + { + return options.UseHttps(new HttpsConnectionFilterOptions { ServerCertificate = serverCertificate }); + } + + /// + /// Configure Kestrel to use HTTPS. + /// + /// + /// The Microsoft.AspNetCore.Server.KestrelServerOptions to configure. + /// + /// + /// Options to configure HTTPS. + /// + /// + /// The Microsoft.AspNetCore.Server.KestrelServerOptions. + /// + public static KestrelServerOptions UseHttps(this KestrelServerOptions options, HttpsConnectionFilterOptions httpsOptions) + { + var prevFilter = options.ConnectionFilter ?? new NoOpConnectionFilter(); + options.ConnectionFilter = new HttpsConnectionFilter(httpsOptions, prevFilter); + return options; + } + } +} diff --git a/src/Microsoft.AspNetCore.Server.Kestrel/Filter/KestrelServerOptionsConnectionLoggingExtensions.cs b/src/Microsoft.AspNetCore.Server.Kestrel/Filter/KestrelServerOptionsConnectionLoggingExtensions.cs new file mode 100644 index 0000000000..a46d0646eb --- /dev/null +++ b/src/Microsoft.AspNetCore.Server.Kestrel/Filter/KestrelServerOptionsConnectionLoggingExtensions.cs @@ -0,0 +1,39 @@ +// 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; +using Microsoft.AspNetCore.Server.Kestrel.Filter; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; + +namespace Microsoft.AspNetCore.Hosting +{ + public static class KestrelServerOptionsConnectionLoggingExtensions + { + /// + /// Emits verbose logs for bytes read from and written to the connection. + /// + /// + /// The Microsoft.AspNetCore.Server.KestrelServerOptions. + /// + public static KestrelServerOptions UseConnectionLogging(this KestrelServerOptions options) + { + return options.UseConnectionLogging(nameof(LoggingConnectionFilter)); + } + + /// + /// Emits verbose logs for bytes read from and written to the connection. + /// + /// + /// The Microsoft.AspNetCore.Server.KestrelServerOptions. + /// + public static KestrelServerOptions UseConnectionLogging(this KestrelServerOptions options, string loggerName) + { + var prevFilter = options.ConnectionFilter ?? new NoOpConnectionFilter(); + var loggerFactory = options.ApplicationServices.GetRequiredService(); + var logger = loggerFactory.CreateLogger(loggerName ?? nameof(LoggingConnectionFilter)); + options.ConnectionFilter = new LoggingConnectionFilter(logger, prevFilter); + return options; + } + } +} diff --git a/src/Microsoft.AspNetCore.Server.Kestrel/Filter/LoggingFilterApplicationBuilderExtensions.cs b/src/Microsoft.AspNetCore.Server.Kestrel/Filter/LoggingFilterApplicationBuilderExtensions.cs deleted file mode 100644 index 8d373f5897..0000000000 --- a/src/Microsoft.AspNetCore.Server.Kestrel/Filter/LoggingFilterApplicationBuilderExtensions.cs +++ /dev/null @@ -1,39 +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.Builder; -using Microsoft.AspNetCore.Http.Features; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; - -namespace Microsoft.AspNetCore.Server.Kestrel.Filter -{ - public static class LoggingFilterApplicationBuilderExtensions - { - /// - /// Emits verbose logs for bytes read from and written to the connection. - /// - /// - public static IApplicationBuilder UseKestrelConnectionLogging(this IApplicationBuilder app) - { - return app.UseKestrelConnectionLogging(nameof(LoggingConnectionFilter)); - } - - /// - /// Emits verbose logs for bytes read from and written to the connection. - /// - /// - public static IApplicationBuilder UseKestrelConnectionLogging(this IApplicationBuilder app, string loggerName) - { - var serverInfo = app.ServerFeatures.Get(); - if (serverInfo != null) - { - var prevFilter = serverInfo.ConnectionFilter ?? new NoOpConnectionFilter(); - var loggerFactory = app.ApplicationServices.GetRequiredService(); - var logger = loggerFactory.CreateLogger(loggerName ?? nameof(LoggingConnectionFilter)); - serverInfo.ConnectionFilter = new LoggingConnectionFilter(logger, prevFilter); - } - return app; - } - } -} diff --git a/src/Microsoft.AspNetCore.Server.Kestrel/Http/Connection.cs b/src/Microsoft.AspNetCore.Server.Kestrel/Http/Connection.cs index fbd690eea7..71a40284b0 100644 --- a/src/Microsoft.AspNetCore.Server.Kestrel/Http/Connection.cs +++ b/src/Microsoft.AspNetCore.Server.Kestrel/Http/Connection.cs @@ -70,7 +70,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http } // Don't initialize _frame until SocketInput and SocketOutput are set to their final values. - if (ServerInformation.ConnectionFilter == null) + if (ServerOptions.ConnectionFilter == null) { lock (_stateLock) { @@ -100,7 +100,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http try { - ServerInformation.ConnectionFilter.OnConnectionAsync(_filterContext).ContinueWith((task, state) => + ServerOptions.ConnectionFilter.OnConnectionAsync(_filterContext).ContinueWith((task, state) => { var connection = (Connection)state; diff --git a/src/Microsoft.AspNetCore.Server.Kestrel/Http/TcpListener.cs b/src/Microsoft.AspNetCore.Server.Kestrel/Http/TcpListener.cs index 1b1927e27c..94c7cdecd9 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(ServerInformation.NoDelay); + socket.NoDelay(ServerOptions.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(ServerInformation.NoDelay); + acceptSocket.NoDelay(ServerOptions.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 e791d30568..80691b51ec 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(ServerInformation.NoDelay); + socket.NoDelay(ServerOptions.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(ServerInformation.NoDelay); + acceptSocket.NoDelay(ServerOptions.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 d52ac62b7e..1a19445d50 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(ServerInformation.NoDelay); + acceptSocket.NoDelay(ServerOptions.NoDelay); return acceptSocket; } } diff --git a/src/Microsoft.AspNetCore.Server.Kestrel/IKestrelServerInformation.cs b/src/Microsoft.AspNetCore.Server.Kestrel/IKestrelServerInformation.cs deleted file mode 100644 index 87a6b645b2..0000000000 --- a/src/Microsoft.AspNetCore.Server.Kestrel/IKestrelServerInformation.cs +++ /dev/null @@ -1,35 +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; -using Microsoft.AspNetCore.Server.Kestrel.Filter; - -namespace Microsoft.AspNetCore.Server.Kestrel -{ - public interface IKestrelServerInformation - { - int ThreadCount { get; set; } - - /// - /// The amount of time after the server begins shutting down before connections will be forcefully closed. - /// By default, Kestrel will wait 5 seconds for any ongoing requests to complete before terminating - /// the connection. - /// A custom timeout can be configured using the "kestrel.shutdownTimeout" key in . - /// The value will be parsed as a float representing the timeout in seconds. - /// - TimeSpan ShutdownTimeout { get; set; } - - bool NoDelay { get; set; } - - /// - /// 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 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. - /// - 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 b0be4ad410..cacd005b55 100644 --- a/src/Microsoft.AspNetCore.Server.Kestrel/Infrastructure/HttpComponentFactory.cs +++ b/src/Microsoft.AspNetCore.Server.Kestrel/Infrastructure/HttpComponentFactory.cs @@ -13,11 +13,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Infrastructure private ConcurrentQueue _streamPool = new ConcurrentQueue(); private ConcurrentQueue _headerPool = new ConcurrentQueue(); - public IKestrelServerInformation ServerInformation { get; set; } + public KestrelServerOptions ServerOptions { get; set; } - public HttpComponentFactory(IKestrelServerInformation serverInformation) + public HttpComponentFactory(KestrelServerOptions serverOptions) { - ServerInformation = serverInformation; + ServerOptions = serverOptions; } public Streams CreateStreams(FrameContext owner) @@ -36,7 +36,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Infrastructure public void DisposeStreams(Streams streams) { - if (_streamPool.Count < ServerInformation.PoolingParameters.MaxPooledStreams) + if (_streamPool.Count < ServerOptions.MaxPooledStreams) { streams.Uninitialize(); @@ -60,7 +60,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Infrastructure public void DisposeHeaders(Headers headers) { - if (_headerPool.Count < ServerInformation.PoolingParameters.MaxPooledHeaders) + if (_headerPool.Count < ServerOptions.MaxPooledHeaders) { headers.Uninitialize(); diff --git a/src/Microsoft.AspNetCore.Server.Kestrel/Infrastructure/IHttpComponentFactory.cs b/src/Microsoft.AspNetCore.Server.Kestrel/Infrastructure/IHttpComponentFactory.cs index c64f039680..926d9cfd26 100644 --- a/src/Microsoft.AspNetCore.Server.Kestrel/Infrastructure/IHttpComponentFactory.cs +++ b/src/Microsoft.AspNetCore.Server.Kestrel/Infrastructure/IHttpComponentFactory.cs @@ -7,7 +7,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Infrastructure { interface IHttpComponentFactory { - IKestrelServerInformation ServerInformation { get; set; } + KestrelServerOptions ServerOptions { get; set; } Streams CreateStreams(FrameContext owner); diff --git a/src/Microsoft.AspNetCore.Server.Kestrel/Internal/KestrelServerOptionsSetup.cs b/src/Microsoft.AspNetCore.Server.Kestrel/Internal/KestrelServerOptionsSetup.cs new file mode 100644 index 0000000000..93fda55f07 --- /dev/null +++ b/src/Microsoft.AspNetCore.Server.Kestrel/Internal/KestrelServerOptionsSetup.cs @@ -0,0 +1,23 @@ +// 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 Microsoft.Extensions.Options; + +namespace Microsoft.AspNetCore.Server.Kestrel.Internal +{ + public class KestrelServerOptionsSetup : IConfigureOptions + { + private IServiceProvider _services; + + public KestrelServerOptionsSetup(IServiceProvider services) + { + _services = services; + } + + public void Configure(KestrelServerOptions options) + { + options.ApplicationServices = _services; + } + } +} diff --git a/src/Microsoft.AspNetCore.Server.Kestrel/Internal/ServerAddressesFeature.cs b/src/Microsoft.AspNetCore.Server.Kestrel/Internal/ServerAddressesFeature.cs new file mode 100644 index 0000000000..6294923d6f --- /dev/null +++ b/src/Microsoft.AspNetCore.Server.Kestrel/Internal/ServerAddressesFeature.cs @@ -0,0 +1,13 @@ +//// 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.Generic; +using Microsoft.AspNetCore.Server.Features; + +namespace Microsoft.AspNetCore.Server.Kestrel.Internal +{ + public class ServerAddressesFeature : IServerAddressesFeature + { + public ICollection Addresses { get; } = new List(); + } +} diff --git a/src/Microsoft.AspNetCore.Server.Kestrel/KestrelEngine.cs b/src/Microsoft.AspNetCore.Server.Kestrel/KestrelEngine.cs index ae4f84735c..db0e28956d 100644 --- a/src/Microsoft.AspNetCore.Server.Kestrel/KestrelEngine.cs +++ b/src/Microsoft.AspNetCore.Server.Kestrel/KestrelEngine.cs @@ -114,7 +114,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel disposeTasks.Add(listener.DisposeAsync()); } - if (!Task.WhenAll(disposeTasks).Wait(ServerInformation.ShutdownTimeout)) + if (!Task.WhenAll(disposeTasks).Wait(ServerOptions.ShutdownTimeout)) { Log.NotAllConnectionsClosedGracefully(); } diff --git a/src/Microsoft.AspNetCore.Server.Kestrel/KestrelServer.cs b/src/Microsoft.AspNetCore.Server.Kestrel/KestrelServer.cs index 6d38e3e2b5..636710dd72 100644 --- a/src/Microsoft.AspNetCore.Server.Kestrel/KestrelServer.cs +++ b/src/Microsoft.AspNetCore.Server.Kestrel/KestrelServer.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting.Server; using Microsoft.AspNetCore.Http.Features; +using Microsoft.AspNetCore.Server.Features; using Microsoft.AspNetCore.Server.Kestrel.Http; using Microsoft.AspNetCore.Server.Kestrel.Infrastructure; using Microsoft.Extensions.Logging; @@ -18,13 +19,18 @@ namespace Microsoft.AspNetCore.Server.Kestrel private readonly IApplicationLifetime _applicationLifetime; private readonly ILogger _logger; - public KestrelServer(IFeatureCollection features, IApplicationLifetime applicationLifetime, ILogger logger) + public KestrelServer(IFeatureCollection features, KestrelServerOptions options, IApplicationLifetime applicationLifetime, ILogger logger) { if (features == null) { throw new ArgumentNullException(nameof(features)); } + if (options == null) + { + throw new ArgumentNullException(nameof(options)); + } + if (applicationLifetime == null) { throw new ArgumentNullException(nameof(applicationLifetime)); @@ -38,10 +44,13 @@ namespace Microsoft.AspNetCore.Server.Kestrel _applicationLifetime = applicationLifetime; _logger = logger; Features = features; + Options = options; } public IFeatureCollection Features { get; } + public KestrelServerOptions Options { get; } + public void Start(IHttpApplication application) { if (_disposables != null) @@ -53,7 +62,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel try { - var information = (KestrelServerInformation)Features.Get(); var componentFactory = Features.Get(); var dateHeaderValueManager = new DateHeaderValueManager(); var trace = new KestrelTrace(_logger); @@ -67,14 +75,14 @@ namespace Microsoft.AspNetCore.Server.Kestrel Log = trace, ThreadPool = new LoggingThreadPool(trace), DateHeaderValueManager = dateHeaderValueManager, - ServerInformation = information, + ServerOptions = Options, HttpComponentFactory = componentFactory }); _disposables.Push(engine); _disposables.Push(dateHeaderValueManager); - var threadCount = information.ThreadCount; + var threadCount = Options.ThreadCount; if (threadCount <= 0) { @@ -86,7 +94,13 @@ namespace Microsoft.AspNetCore.Server.Kestrel engine.Start(threadCount); var atLeastOneListener = false; - foreach (var address in information.Addresses) + var addressesFeature = Features.Get(); + if (addressesFeature == null) + { + throw new InvalidOperationException($"{nameof(IServerAddressesFeature)} is missing."); + } + + foreach (var address in addressesFeature.Addresses) { var parsedAddress = ServerAddress.FromUrl(address); if (parsedAddress == null) diff --git a/src/Microsoft.AspNetCore.Server.Kestrel/KestrelServerInformation.cs b/src/Microsoft.AspNetCore.Server.Kestrel/KestrelServerInformation.cs deleted file mode 100644 index 58f44cede3..0000000000 --- a/src/Microsoft.AspNetCore.Server.Kestrel/KestrelServerInformation.cs +++ /dev/null @@ -1,130 +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; -using System.Collections.Generic; -using System.Globalization; -using Microsoft.AspNetCore.Server.Features; -using Microsoft.AspNetCore.Server.Kestrel.Filter; -using Microsoft.Extensions.Configuration; - -namespace Microsoft.AspNetCore.Server.Kestrel -{ - public class KestrelServerInformation : IKestrelServerInformation, IServerAddressesFeature - { - public KestrelServerInformation(IConfiguration configuration) - { - if (configuration == null) - { - throw new ArgumentNullException(nameof(configuration)); - } - - Addresses = GetAddresses(configuration); - ThreadCount = GetThreadCount(configuration); - ShutdownTimeout = GetShutdownTimeout(configuration); - NoDelay = GetNoDelay(configuration); - PoolingParameters = new KestrelServerPoolingParameters(configuration); - } - - public ICollection Addresses { get; } - - public int ThreadCount { get; set; } - - public TimeSpan ShutdownTimeout { get; set; } - - public bool NoDelay { get; set; } - - public KestrelServerPoolingParameters PoolingParameters { get; } - - public IConnectionFilter ConnectionFilter { get; set; } - - private static int ProcessorThreadCount - { - get - { - // Actual core count would be a better number - // rather than logical cores which includes hyper-threaded cores. - // Divide by 2 for hyper-threading, and good defaults (still need threads to do webserving). - var threadCount = Environment.ProcessorCount >> 1; - - if (threadCount < 1) - { - // Ensure shifted value is at least one - return 1; - } - - if (threadCount > 16) - { - // Receive Side Scaling RSS Processor count currently maxes out at 16 - // would be better to check the NIC's current hardware queues; but xplat... - return 16; - } - - return threadCount; - } - } - - private static ICollection GetAddresses(IConfiguration configuration) - { - var addresses = new List(); - - var urls = configuration["server.urls"]; - - if (!string.IsNullOrEmpty(urls)) - { - addresses.AddRange(urls.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries)); - } - - return addresses; - } - - private static int GetThreadCount(IConfiguration configuration) - { - var threadCountString = configuration["kestrel.threadCount"]; - - if (string.IsNullOrEmpty(threadCountString)) - { - return ProcessorThreadCount; - } - - int threadCount; - if (int.TryParse(threadCountString, NumberStyles.Integer, CultureInfo.InvariantCulture, out threadCount)) - { - return threadCount; - } - - return ProcessorThreadCount; - } - - private TimeSpan GetShutdownTimeout(IConfiguration configuration) - { - var shutdownTimeoutString = configuration["kestrel.shutdownTimeout"]; - - float shutdownTimeout; - if (float.TryParse(shutdownTimeoutString, NumberStyles.Float, CultureInfo.InvariantCulture, out shutdownTimeout)) - { - return TimeSpan.FromSeconds(shutdownTimeout); - } - - return TimeSpan.FromSeconds(5); - } - - private static bool GetNoDelay(IConfiguration configuration) - { - var noDelayString = configuration["kestrel.noDelay"]; - - if (string.IsNullOrEmpty(noDelayString)) - { - return true; - } - - bool noDelay; - if (bool.TryParse(noDelayString, out noDelay)) - { - return noDelay; - } - - return true; - } - } -} diff --git a/src/Microsoft.AspNetCore.Server.Kestrel/KestrelServerOptions.cs b/src/Microsoft.AspNetCore.Server.Kestrel/KestrelServerOptions.cs new file mode 100644 index 0000000000..ddf85f9a27 --- /dev/null +++ b/src/Microsoft.AspNetCore.Server.Kestrel/KestrelServerOptions.cs @@ -0,0 +1,70 @@ +// 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 Microsoft.AspNetCore.Server.Kestrel.Filter; + +namespace Microsoft.AspNetCore.Server.Kestrel +{ + public class KestrelServerOptions + { + public IServiceProvider ApplicationServices { get; set; } + + 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; + + /// + /// The amount of time after the server begins shutting down before connections will be forcefully closed. + /// By default, Kestrel will wait 5 seconds for any ongoing requests to complete before terminating + /// the connection. + /// + public TimeSpan ShutdownTimeout { get; set; } = TimeSpan.FromSeconds(5); + + public int ThreadCount { get; set; } = ProcessorThreadCount; + + private static int ProcessorThreadCount + { + get + { + // Actual core count would be a better number + // rather than logical cores which includes hyper-threaded cores. + // Divide by 2 for hyper-threading, and good defaults (still need threads to do webserving). + var threadCount = Environment.ProcessorCount >> 1; + + if (threadCount < 1) + { + // Ensure shifted value is at least one + return 1; + } + + if (threadCount > 16) + { + // Receive Side Scaling RSS Processor count currently maxes out at 16 + // would be better to check the NIC's current hardware queues; but xplat... + return 16; + } + + return threadCount; + } + } + } +} diff --git a/src/Microsoft.AspNetCore.Server.Kestrel/KestrelServerPoolingParameters.cs b/src/Microsoft.AspNetCore.Server.Kestrel/KestrelServerPoolingParameters.cs deleted file mode 100644 index 9a019d1d94..0000000000 --- a/src/Microsoft.AspNetCore.Server.Kestrel/KestrelServerPoolingParameters.cs +++ /dev/null @@ -1,43 +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; -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/KestrelWebHostBuilderExtensions.cs b/src/Microsoft.AspNetCore.Server.Kestrel/KestrelWebHostBuilderExtensions.cs deleted file mode 100644 index aa6115094e..0000000000 --- a/src/Microsoft.AspNetCore.Server.Kestrel/KestrelWebHostBuilderExtensions.cs +++ /dev/null @@ -1,16 +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.Reflection; -using Microsoft.AspNetCore.Server.Kestrel; - -namespace Microsoft.AspNetCore.Hosting -{ - public static class KestrelWebHostBuilderExtensions - { - public static IWebHostBuilder UseKestrel(this IWebHostBuilder hostBuilder) - { - return hostBuilder.UseServer(typeof(KestrelServer).GetTypeInfo().Assembly.FullName); - } - } -} diff --git a/src/Microsoft.AspNetCore.Server.Kestrel/ServerFactory.cs b/src/Microsoft.AspNetCore.Server.Kestrel/ServerFactory.cs index c6bf62f604..ada5a92948 100644 --- a/src/Microsoft.AspNetCore.Server.Kestrel/ServerFactory.cs +++ b/src/Microsoft.AspNetCore.Server.Kestrel/ServerFactory.cs @@ -7,8 +7,10 @@ using Microsoft.AspNetCore.Hosting.Server; using Microsoft.AspNetCore.Http.Features; using Microsoft.AspNetCore.Server.Features; using Microsoft.AspNetCore.Server.Kestrel.Infrastructure; +using Microsoft.AspNetCore.Server.Kestrel.Internal; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; namespace Microsoft.AspNetCore.Server.Kestrel { @@ -19,22 +21,37 @@ namespace Microsoft.AspNetCore.Server.Kestrel { private readonly IApplicationLifetime _appLifetime; private readonly ILoggerFactory _loggerFactory; + private readonly KestrelServerOptions _options; - public ServerFactory(IApplicationLifetime appLifetime, ILoggerFactory loggerFactory) + public ServerFactory(IApplicationLifetime appLifetime, ILoggerFactory loggerFactory, IOptions optionsAccessor) { _appLifetime = appLifetime; _loggerFactory = loggerFactory; + _options = optionsAccessor.Value; } public IServer CreateServer(IConfiguration configuration) { - var information = new KestrelServerInformation(configuration); - var componentFactory = new HttpComponentFactory(information); + var componentFactory = new HttpComponentFactory(_options); var serverFeatures = new FeatureCollection(); - serverFeatures.Set(information); serverFeatures.Set(componentFactory); - serverFeatures.Set(information); - return new KestrelServer(serverFeatures, _appLifetime, _loggerFactory.CreateLogger("Microsoft.AspNetCore.Server.Kestrel")); + serverFeatures.Set(GetAddresses(configuration)); + return new KestrelServer(serverFeatures, _options, _appLifetime, _loggerFactory.CreateLogger("Microsoft.AspNetCore.Server.Kestrel")); } + + private static IServerAddressesFeature GetAddresses(IConfiguration configuration) + { + var addressesFeature = new ServerAddressesFeature(); + var urls = configuration["server.urls"]; + if (!string.IsNullOrEmpty(urls)) + { + foreach (var value in urls.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries)) + { + addressesFeature.Addresses.Add(value); + } + } + return addressesFeature; + } + } } diff --git a/src/Microsoft.AspNetCore.Server.Kestrel/ServiceContext.cs b/src/Microsoft.AspNetCore.Server.Kestrel/ServiceContext.cs index a591a671ff..72103028d1 100644 --- a/src/Microsoft.AspNetCore.Server.Kestrel/ServiceContext.cs +++ b/src/Microsoft.AspNetCore.Server.Kestrel/ServiceContext.cs @@ -24,7 +24,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel ThreadPool = context.ThreadPool; FrameFactory = context.FrameFactory; DateHeaderValueManager = context.DateHeaderValueManager; - ServerInformation = context.ServerInformation; + ServerOptions = context.ServerOptions; HttpComponentFactory = context.HttpComponentFactory; } @@ -38,7 +38,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel public DateHeaderValueManager DateHeaderValueManager { get; set; } - public IKestrelServerInformation ServerInformation { get; set; } + public KestrelServerOptions ServerOptions { get; set; } internal IHttpComponentFactory HttpComponentFactory { get; set; } } diff --git a/src/Microsoft.AspNetCore.Server.Kestrel/WebHostBuilderKestrelExtensions.cs b/src/Microsoft.AspNetCore.Server.Kestrel/WebHostBuilderKestrelExtensions.cs new file mode 100644 index 0000000000..7632647d03 --- /dev/null +++ b/src/Microsoft.AspNetCore.Server.Kestrel/WebHostBuilderKestrelExtensions.cs @@ -0,0 +1,52 @@ +// 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 Microsoft.AspNetCore.Hosting.Server; +using Microsoft.AspNetCore.Server.Kestrel; +using Microsoft.AspNetCore.Server.Kestrel.Internal; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; + +namespace Microsoft.AspNetCore.Hosting +{ + public static class WebHostBuilderKestrelExtensions + { + /// + /// Specify Kestrel as the server to be used by the web host. + /// + /// + /// The Microsoft.AspNetCore.Hosting.IWebHostBuilder to configure. + /// + /// + /// The Microsoft.AspNetCore.Hosting.IWebHostBuilder. + /// + public static IWebHostBuilder UseKestrel(this IWebHostBuilder hostBuilder) + { + return hostBuilder.ConfigureServices(services => services.AddSingleton()); + } + + /// + /// Specify Kestrel as the server to be used by the web host. + /// + /// + /// The Microsoft.AspNetCore.Hosting.IWebHostBuilder to configure. + /// + /// + /// A callback to configure Kestrel options. + /// + /// + /// The Microsoft.AspNetCore.Hosting.IWebHostBuilder. + /// + public static IWebHostBuilder UseKestrel(this IWebHostBuilder hostBuilder, Action options) + { + hostBuilder.ConfigureServices(services => + { + services.AddTransient, KestrelServerOptionsSetup>(); + services.Configure(options); + }); + + return hostBuilder.UseKestrel(); + } + } +} diff --git a/test/Microsoft.AspNetCore.Server.Kestrel.FunctionalTests/ThreadCountTests.cs b/test/Microsoft.AspNetCore.Server.Kestrel.FunctionalTests/ThreadCountTests.cs index fff6fb4497..543d1d2a4e 100644 --- a/test/Microsoft.AspNetCore.Server.Kestrel.FunctionalTests/ThreadCountTests.cs +++ b/test/Microsoft.AspNetCore.Server.Kestrel.FunctionalTests/ThreadCountTests.cs @@ -26,11 +26,12 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests var hostBuilder = new WebHostBuilder() .UseConfiguration(config) - .UseKestrel() + .UseKestrel(options => + { + options.ThreadCount = threadCount; + }) .Configure(app => { - var serverInfo = app.ServerFeatures.Get(); - serverInfo.ThreadCount = threadCount; app.Run(context => { return context.Response.WriteAsync("Hello World"); diff --git a/test/Microsoft.AspNetCore.Server.KestrelTests/EngineTests.cs b/test/Microsoft.AspNetCore.Server.KestrelTests/EngineTests.cs index 9c42dac2af..c068803b40 100644 --- a/test/Microsoft.AspNetCore.Server.KestrelTests/EngineTests.cs +++ b/test/Microsoft.AspNetCore.Server.KestrelTests/EngineTests.cs @@ -173,7 +173,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests [MemberData(nameof(ConnectionFilterData))] public async Task ReuseStreamsOn(ServiceContext testContext) { - testContext.ServerInformation.PoolingParameters.MaxPooledStreams = 120; + testContext.ServerOptions.MaxPooledStreams = 120; var streamCount = 0; var loopCount = 20; @@ -216,7 +216,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests [MemberData(nameof(ConnectionFilterData))] public async Task ReuseStreamsOff(ServiceContext testContext) { - testContext.ServerInformation.PoolingParameters.MaxPooledStreams = 0; + testContext.ServerOptions.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 f03deed0e3..88ea59e93e 100644 --- a/test/Microsoft.AspNetCore.Server.KestrelTests/FrameResponseHeadersTests.cs +++ b/test/Microsoft.AspNetCore.Server.KestrelTests/FrameResponseHeadersTests.cs @@ -17,15 +17,14 @@ namespace Microsoft.AspNetCore.Server.KestrelTests [Fact] public void InitialDictionaryContainsServerAndDate() { - var configuration = new ConfigurationBuilder().Build(); - var serverInformation = new KestrelServerInformation(configuration); + var serverOptions = new KestrelServerOptions(); var connectionContext = new ConnectionContext { DateHeaderValueManager = new DateHeaderValueManager(), ServerAddress = ServerAddress.FromUrl("http://localhost:5000"), - ServerInformation = serverInformation, - HttpComponentFactory = new HttpComponentFactory(serverInformation) - }; + ServerOptions = serverOptions, + HttpComponentFactory = new HttpComponentFactory(serverOptions) + }; var frame = new Frame(application: null, context: connectionContext); frame.InitializeHeaders(); @@ -51,14 +50,13 @@ namespace Microsoft.AspNetCore.Server.KestrelTests [Fact] public void InitialEntriesCanBeCleared() { - var configuration = new ConfigurationBuilder().Build(); - var serverInformation = new KestrelServerInformation(configuration); + var serverOptions = new KestrelServerOptions(); var connectionContext = new ConnectionContext { DateHeaderValueManager = new DateHeaderValueManager(), ServerAddress = ServerAddress.FromUrl("http://localhost:5000"), - ServerInformation = serverInformation, - HttpComponentFactory = new HttpComponentFactory(serverInformation) + ServerOptions = serverOptions, + HttpComponentFactory = new HttpComponentFactory(serverOptions) }; var frame = new Frame(application: null, context: connectionContext); frame.InitializeHeaders(); diff --git a/test/Microsoft.AspNetCore.Server.KestrelTests/KestrelServerInformationTests.cs b/test/Microsoft.AspNetCore.Server.KestrelTests/KestrelServerInformationTests.cs deleted file mode 100644 index d8594e623c..0000000000 --- a/test/Microsoft.AspNetCore.Server.KestrelTests/KestrelServerInformationTests.cs +++ /dev/null @@ -1,144 +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; -using System.Collections.Generic; -using Microsoft.AspNetCore.Server.Kestrel; -using Microsoft.Extensions.Configuration; -using Xunit; - -namespace Microsoft.AspNetCore.Server.KestrelTests -{ - public class KestrelServerInformationTests - { - [Fact] - public void NullConfigurationThrows() - { - Assert.Throws(() => new KestrelServerInformation(null)); - } - - [Fact] - public void SetThreadCountUsingConfiguration() - { - const int expected = 42; - - var values = new Dictionary - { - { "kestrel.threadCount", expected.ToString() } - }; - - var configuration = new ConfigurationBuilder() - .AddInMemoryCollection(values) - .Build(); - - var information = new KestrelServerInformation(configuration); - - Assert.Equal(expected, information.ThreadCount); - } - - [Fact] - public void SetThreadCountUsingProcessorCount() - { - // Ideally we'd mock Environment.ProcessorCount to test edge cases. - var expected = Clamp(Environment.ProcessorCount >> 1, 1, 16); - - var configuration = new ConfigurationBuilder().Build(); - - var information = new KestrelServerInformation(configuration); - - Assert.Equal(expected, information.ThreadCount); - } - - [Fact] - public void SetAddressesUsingConfiguration() - { - var expected = new List { "http://localhost:1337", "https://localhost:42" }; - - var values = new Dictionary - { - { "server.urls", string.Join(";", expected) } - }; - - var configuration = new ConfigurationBuilder() - .AddInMemoryCollection(values) - .Build(); - - var information = new KestrelServerInformation(configuration); - - Assert.Equal(expected, information.Addresses); - } - - [Fact] - public void SetNoDelayUsingConfiguration() - { - var values = new Dictionary - { - { "kestrel.noDelay", "false" } - }; - - var configuration = new ConfigurationBuilder() - .AddInMemoryCollection(values) - .Build(); - - var information = new KestrelServerInformation(configuration); - - Assert.False(information.NoDelay); - } - - [Theory] - [InlineData(null, 0)] - [InlineData("", 0)] - [InlineData("0", 0)] - [InlineData("00", 0)] - [InlineData("0.0", 0)] - [InlineData("1", 1)] - [InlineData("16", 16)] - [InlineData("1000", 1000)] - public void SetMaxPooledStreamsUsingConfiguration(string input, int expected) - { - var values = new Dictionary - { - { "kestrel.maxPooledStreams", input } - }; - - var configuration = new ConfigurationBuilder() - .AddInMemoryCollection(values) - .Build(); - - var information = new KestrelServerInformation(configuration); - - Assert.Equal(expected, information.PoolingParameters.MaxPooledStreams); - } - - - [Theory] - [InlineData(null, 0)] - [InlineData("", 0)] - [InlineData("0", 0)] - [InlineData("00", 0)] - [InlineData("0.0", 0)] - [InlineData("1", 1)] - [InlineData("16", 16)] - [InlineData("1000", 1000)] - public void SetMaxPooledHeadersUsingConfiguration(string input, int expected) - { - var values = new Dictionary - { - { "kestrel.maxPooledHeaders", input } - }; - - var configuration = new ConfigurationBuilder() - .AddInMemoryCollection(values) - .Build(); - - var information = new KestrelServerInformation(configuration); - - Assert.Equal(expected, information.PoolingParameters.MaxPooledHeaders); - } - - private static int Clamp(int value, int min, int max) - { - return value < min ? min : value > max ? max : value; - } - } -} \ No newline at end of file diff --git a/test/Microsoft.AspNetCore.Server.KestrelTests/KestrelServerOptionsTests.cs b/test/Microsoft.AspNetCore.Server.KestrelTests/KestrelServerOptionsTests.cs new file mode 100644 index 0000000000..ff03086b71 --- /dev/null +++ b/test/Microsoft.AspNetCore.Server.KestrelTests/KestrelServerOptionsTests.cs @@ -0,0 +1,30 @@ +// 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.Collections.Generic; +using Microsoft.AspNetCore.Server.Kestrel; +using Microsoft.Extensions.Configuration; +using Xunit; + +namespace Microsoft.AspNetCore.Server.KestrelTests +{ + public class KestrelServerInformationTests + { + [Fact] + public void SetThreadCountUsingProcessorCount() + { + // Ideally we'd mock Environment.ProcessorCount to test edge cases. + var expected = Clamp(Environment.ProcessorCount >> 1, 1, 16); + + var information = new KestrelServerOptions(); + + Assert.Equal(expected, information.ThreadCount); + } + + private static int Clamp(int value, int min, int max) + { + return value < min ? min : value > max ? max : value; + } + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNetCore.Server.KestrelTests/KestrelServerTests.cs b/test/Microsoft.AspNetCore.Server.KestrelTests/KestrelServerTests.cs index 8c4e8c1490..89b828f468 100644 --- a/test/Microsoft.AspNetCore.Server.KestrelTests/KestrelServerTests.cs +++ b/test/Microsoft.AspNetCore.Server.KestrelTests/KestrelServerTests.cs @@ -4,9 +4,10 @@ using System; using Microsoft.AspNetCore.Hosting.Server; using Microsoft.AspNetCore.Http.Features; +using Microsoft.AspNetCore.Server.Features; using Microsoft.AspNetCore.Server.Kestrel; using Microsoft.AspNetCore.Server.Kestrel.Infrastructure; -using Microsoft.Extensions.Configuration; +using Microsoft.AspNetCore.Server.Kestrel.Internal; using Xunit; namespace Microsoft.AspNetCore.Server.KestrelTests @@ -18,11 +19,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests [InlineData(-1337)] public void StartWithNonPositiveThreadCountThrows(int threadCount) { - var server = CreateServer(configuration => - new KestrelServerInformation(configuration) - { - ThreadCount = threadCount - }); + var server = CreateServer(new KestrelServerOptions() { ThreadCount = threadCount }); var exception = Assert.Throws(() => StartDummyApplication(server)); @@ -32,11 +29,9 @@ namespace Microsoft.AspNetCore.Server.KestrelTests [Fact] public void StartWithInvalidAddressThrows() { - var server = CreateServer(configuration => - new KestrelServerInformation(configuration) - { - Addresses = {"http:/asdf"} - }); + var addressesFeature = new ServerAddressesFeature(); + addressesFeature.Addresses.Add("http:/asdf"); + var server = CreateServer(new KestrelServerOptions(), addressesFeature); var exception = Assert.Throws(() => StartDummyApplication(server)); @@ -46,32 +41,25 @@ namespace Microsoft.AspNetCore.Server.KestrelTests [Fact] public void StartWithEmptyAddressesThrows() { - var server = CreateServer(configuration => - { - var information = new KestrelServerInformation(configuration); - - information.Addresses.Clear(); - - return information; - }); + var server = CreateServer(new KestrelServerOptions(), new ServerAddressesFeature()); var exception = Assert.Throws(() => StartDummyApplication(server)); Assert.Equal("No recognized listening addresses were configured.", exception.Message); } - private static KestrelServer CreateServer(Func serverInformationFactory) + private static KestrelServer CreateServer(KestrelServerOptions options, IServerAddressesFeature addressesFeature = null) { - var configuration = new ConfigurationBuilder().Build(); - var information = serverInformationFactory(configuration); - var features = new FeatureCollection(); - features.Set(information); + if (addressesFeature != null) + { + features.Set(addressesFeature); + } var lifetime = new LifetimeNotImplemented(); var logger = new TestApplicationErrorLogger(); - return new KestrelServer(features, lifetime, logger); + return new KestrelServer(features, options, lifetime, logger); } private static void StartDummyApplication(IServer server) diff --git a/test/Microsoft.AspNetCore.Server.KestrelTests/TestServiceContext.cs b/test/Microsoft.AspNetCore.Server.KestrelTests/TestServiceContext.cs index ff08f19714..7cfe7df885 100644 --- a/test/Microsoft.AspNetCore.Server.KestrelTests/TestServiceContext.cs +++ b/test/Microsoft.AspNetCore.Server.KestrelTests/TestServiceContext.cs @@ -22,17 +22,16 @@ namespace Microsoft.AspNetCore.Server.KestrelTests ThreadPool = new LoggingThreadPool(Log); DateHeaderValueManager = new TestDateHeaderValueManager(); - var configuration = new ConfigurationBuilder().Build(); - ServerInformation = new KestrelServerInformation(configuration); - ServerInformation.ShutdownTimeout = TimeSpan.FromSeconds(5); + ServerOptions = new KestrelServerOptions(); + ServerOptions.ShutdownTimeout = TimeSpan.FromSeconds(5); - HttpComponentFactory = new HttpComponentFactory(ServerInformation); + HttpComponentFactory = new HttpComponentFactory(ServerOptions); } public TestServiceContext(IConnectionFilter filter) : this() { - ServerInformation.ConnectionFilter = filter; + ServerOptions.ConnectionFilter = filter; } public RequestDelegate App