diff --git a/samples/HotAddSample/Startup.cs b/samples/HotAddSample/Startup.cs index 0a37ddf937..bfa147d918 100644 --- a/samples/HotAddSample/Startup.cs +++ b/samples/HotAddSample/Startup.cs @@ -3,7 +3,7 @@ using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.Features; -using Microsoft.AspNetCore.Server.Features; +using Microsoft.AspNetCore.Hosting.Server.Features; using Microsoft.Extensions.Logging; using Microsoft.Net.Http.Server; @@ -94,7 +94,7 @@ namespace HotAddSample var host = new WebHostBuilder() .UseDefaultHostingConfiguration(args) .UseStartup() - .UseServer("Microsoft.AspNetCore.Server.WebListener") + .UseWebListener() .Build(); host.Run(); diff --git a/samples/SelfHostServer/Startup.cs b/samples/SelfHostServer/Startup.cs index e10079e497..0e276faca8 100644 --- a/samples/SelfHostServer/Startup.cs +++ b/samples/SelfHostServer/Startup.cs @@ -15,9 +15,6 @@ namespace SelfHostServer { public void Configure(IApplicationBuilder app, ILoggerFactory loggerfactory) { - var listener = app.ServerFeatures.Get(); - listener.AuthenticationManager.AuthenticationSchemes = AuthenticationSchemes.AllowAnonymous; - loggerfactory.AddConsole(LogLevel.Debug); app.Run(async context => @@ -43,7 +40,10 @@ namespace SelfHostServer var host = new WebHostBuilder() .UseDefaultHostingConfiguration(args) .UseStartup() - .UseServer("Microsoft.AspNetCore.Server.WebListener") + .UseWebListener(options => + { + options.Listener.AuthenticationManager.AuthenticationSchemes = AuthenticationSchemes.AllowAnonymous; + }) .Build(); host.Run(); diff --git a/src/Microsoft.AspNetCore.Server.WebListener/Internal/WebListenerOptionsSetup.cs b/src/Microsoft.AspNetCore.Server.WebListener/Internal/WebListenerOptionsSetup.cs new file mode 100644 index 0000000000..5e27bbf8c2 --- /dev/null +++ b/src/Microsoft.AspNetCore.Server.WebListener/Internal/WebListenerOptionsSetup.cs @@ -0,0 +1,38 @@ +// Copyright (c) Microsoft Open Technologies, Inc. +// All Rights Reserved +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING +// WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF +// TITLE, FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR +// NON-INFRINGEMENT. +// See the Apache 2 License for the specific language governing +// permissions and limitations under the License. + +using System; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; + +namespace Microsoft.AspNetCore.Server.WebListener.Internal +{ + public class WebListenerOptionsSetup : IConfigureOptions + { + private ILoggerFactory _loggerFactory; + + public WebListenerOptionsSetup(ILoggerFactory loggerFactory) + { + _loggerFactory = loggerFactory; + } + + public void Configure(WebListenerOptions options) + { + options.Listener = new Microsoft.Net.Http.Server.WebListener(_loggerFactory); + } + } +} diff --git a/src/Microsoft.AspNetCore.Server.WebListener/MessagePump.cs b/src/Microsoft.AspNetCore.Server.WebListener/MessagePump.cs index cb2bca862d..554a090c8d 100644 --- a/src/Microsoft.AspNetCore.Server.WebListener/MessagePump.cs +++ b/src/Microsoft.AspNetCore.Server.WebListener/MessagePump.cs @@ -21,14 +21,15 @@ using System.Diagnostics.Contracts; using System.Threading; using System.Threading.Tasks; using Microsoft.AspNetCore.Hosting.Server; +using Microsoft.AspNetCore.Hosting.Server.Features; using Microsoft.AspNetCore.Http.Features; -using Microsoft.AspNetCore.Server.Features; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; using Microsoft.Net.Http.Server; namespace Microsoft.AspNetCore.Server.WebListener { - internal class MessagePump : IServer + public class MessagePump : IServer { private static readonly int DefaultMaxAccepts = 5 * Environment.ProcessorCount; @@ -44,25 +45,32 @@ namespace Microsoft.AspNetCore.Server.WebListener private bool _stopping; private int _outstandingRequests; private ManualResetEvent _shutdownSignal; - - internal MessagePump(Microsoft.Net.Http.Server.WebListener listener, ILoggerFactory loggerFactory, IFeatureCollection features) + + private readonly ServerAddressesFeature _serverAddresses; + + public MessagePump(IOptions options, ILoggerFactory loggerFactory) { - if (features == null) + if (options == null) { - throw new ArgumentNullException(nameof(features)); + throw new ArgumentNullException(nameof(options)); + } + if (loggerFactory == null) + { + throw new ArgumentNullException(nameof(loggerFactory)); } - Contract.Assert(listener != null); - _listener = listener; + _listener = options.Value?.Listener ?? new Microsoft.Net.Http.Server.WebListener(loggerFactory); _logger = LogHelper.CreateLogger(loggerFactory, typeof(MessagePump)); - Features = features; + Features = new FeatureCollection(); + _serverAddresses = new ServerAddressesFeature(); + Features.Set(_serverAddresses); _processRequest = new Action(ProcessRequestAsync); _maxAccepts = DefaultMaxAccepts; _shutdownSignal = new ManualResetEvent(false); } - internal Microsoft.Net.Http.Server.WebListener Listener + public Microsoft.Net.Http.Server.WebListener Listener { get { return _listener; } } @@ -94,13 +102,7 @@ namespace Microsoft.AspNetCore.Server.WebListener throw new ArgumentNullException(nameof(application)); } - var addressesFeature = Features.Get(); - if (addressesFeature == null) - { - throw new InvalidOperationException($"{nameof(IServerAddressesFeature)} is missing."); - } - - ParseAddresses(addressesFeature.Addresses, Listener); + ParseAddresses(_serverAddresses.Addresses, Listener); // Can't call Start twice Contract.Assert(_application == null); diff --git a/src/Microsoft.AspNetCore.Server.WebListener/ServerFactory.cs b/src/Microsoft.AspNetCore.Server.WebListener/ServerFactory.cs deleted file mode 100644 index b37bfb7dd8..0000000000 --- a/src/Microsoft.AspNetCore.Server.WebListener/ServerFactory.cs +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright (c) Microsoft Open Technologies, Inc. -// All Rights Reserved -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR -// CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING -// WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF -// TITLE, FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR -// NON-INFRINGEMENT. -// See the Apache 2 License for the specific language governing -// permissions and limitations under the License. - -// ----------------------------------------------------------------------- -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// ----------------------------------------------------------------------- -// Copyright 2011-2012 Katana contributors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -using System; -using System.Diagnostics.CodeAnalysis; -using Microsoft.AspNetCore.Hosting.Server; -using Microsoft.AspNetCore.Http.Features; -using Microsoft.AspNetCore.Server.Features; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.Logging; - -namespace Microsoft.AspNetCore.Server.WebListener -{ - /// - /// Implements the setup process for this server. - /// - public class ServerFactory : IServerFactory - { - private ILoggerFactory _loggerFactory; - - public ServerFactory(ILoggerFactory loggerFactory) - { - _loggerFactory = loggerFactory; - } - - /// - /// Creates a configurable instance of the server. - /// - /// - /// The server. Invoke Dispose to shut down. - [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "Disposed by caller")] - public IServer CreateServer(IConfiguration configuration) - { - Microsoft.Net.Http.Server.WebListener listener = new Microsoft.Net.Http.Server.WebListener(_loggerFactory); - var serverFeatures = new FeatureCollection(); - serverFeatures.Set(listener); - serverFeatures.Set(SplitAddresses(configuration)); - - return new MessagePump(listener, _loggerFactory, serverFeatures); - } - - private IServerAddressesFeature SplitAddresses(IConfiguration config) - { - var addressesFeature = new ServerAddressesFeature(); - if (config != null && !string.IsNullOrEmpty(config["server.urls"])) - { - var urls = config["server.urls"]; - foreach (var value in urls.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries)) - { - addressesFeature.Addresses.Add(value); - } - } - return addressesFeature; - } - } -} diff --git a/src/Microsoft.AspNetCore.Server.WebListener/WebHostBuilderWebListenerExtensions.cs b/src/Microsoft.AspNetCore.Server.WebListener/WebHostBuilderWebListenerExtensions.cs new file mode 100644 index 0000000000..f2cfe9deb2 --- /dev/null +++ b/src/Microsoft.AspNetCore.Server.WebListener/WebHostBuilderWebListenerExtensions.cs @@ -0,0 +1,69 @@ +// Copyright (c) Microsoft Open Technologies, Inc. +// All Rights Reserved +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING +// WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF +// TITLE, FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR +// NON-INFRINGEMENT. +// See the Apache 2 License for the specific language governing +// permissions and limitations under the License. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Hosting.Server; +using Microsoft.AspNetCore.Server.WebListener; +using Microsoft.AspNetCore.Server.WebListener.Internal; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; + +namespace Microsoft.AspNetCore.Hosting +{ + public static class WebHostBuilderWebListenerExtensions + { + /// + /// Specify WebListener 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 UseWebListener(this IWebHostBuilder hostBuilder) + { + return hostBuilder.ConfigureServices(services => services.AddSingleton()); + } + + /// + /// Specify WebListener as the server to be used by the web host. + /// + /// + /// The Microsoft.AspNetCore.Hosting.IWebHostBuilder to configure. + /// + /// + /// A callback to configure WebListener options. + /// + /// + /// The Microsoft.AspNetCore.Hosting.IWebHostBuilder. + /// + public static IWebHostBuilder UseWebListener(this IWebHostBuilder hostBuilder, Action options) + { + hostBuilder.ConfigureServices(services => + { + services.AddTransient, WebListenerOptionsSetup>(); + services.Configure(options); + }); + + return hostBuilder.UseWebListener(); + } + } +} diff --git a/src/Microsoft.AspNetCore.Server.WebListener/ServerAddressesFeature.cs b/src/Microsoft.AspNetCore.Server.WebListener/WebListenerOptions.cs similarity index 72% rename from src/Microsoft.AspNetCore.Server.WebListener/ServerAddressesFeature.cs rename to src/Microsoft.AspNetCore.Server.WebListener/WebListenerOptions.cs index f7ca6cc178..814314e9a2 100644 --- a/src/Microsoft.AspNetCore.Server.WebListener/ServerAddressesFeature.cs +++ b/src/Microsoft.AspNetCore.Server.WebListener/WebListenerOptions.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Open Technologies, Inc. +// Copyright (c) Microsoft Open Technologies, Inc. // All Rights Reserved // // Licensed under the Apache License, Version 2.0 (the "License"); @@ -15,13 +15,15 @@ // See the Apache 2 License for the specific language governing // permissions and limitations under the License. +using System; using System.Collections.Generic; -using Microsoft.AspNetCore.Server.Features; +using System.Linq; +using System.Threading.Tasks; namespace Microsoft.AspNetCore.Server.WebListener { - internal class ServerAddressesFeature : IServerAddressesFeature + public class WebListenerOptions { - public ICollection Addresses { get; } = new List(); + public Microsoft.Net.Http.Server.WebListener Listener { get; set; } = new Microsoft.Net.Http.Server.WebListener(); } } diff --git a/test/Microsoft.AspNetCore.Server.WebListener.FunctionalTests/RequestTests.cs b/test/Microsoft.AspNetCore.Server.WebListener.FunctionalTests/RequestTests.cs index 6f57582cb9..7b574ffca5 100644 --- a/test/Microsoft.AspNetCore.Server.WebListener.FunctionalTests/RequestTests.cs +++ b/test/Microsoft.AspNetCore.Server.WebListener.FunctionalTests/RequestTests.cs @@ -24,6 +24,8 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Hosting.Server; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.Features; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; using Microsoft.Net.Http.Server; using Xunit; @@ -187,13 +189,11 @@ namespace Microsoft.AspNetCore.Server.WebListener var dynamicServer = Utilities.CreateHttpServerReturnRoot("/", out root, app); dynamicServer.Dispose(); var rootUri = new Uri(root); - var factory = new ServerFactory(loggerFactory: null); - var server = factory.CreateServer(configuration: null); - var listener = server.Features.Get(); + var server = new MessagePump(Options.Create(new WebListenerOptions()), new LoggerFactory()); foreach (string path in new[] { "/", "/11", "/2/3", "/2", "/11/2" }) { - listener.UrlPrefixes.Add(UrlPrefix.Create(rootUri.Scheme, rootUri.Host, rootUri.Port, path)); + server.Listener.UrlPrefixes.Add(UrlPrefix.Create(rootUri.Scheme, rootUri.Host, rootUri.Port, path)); } server.Start(new DummyApplication(app)); diff --git a/test/Microsoft.AspNetCore.Server.WebListener.FunctionalTests/ServerTests.cs b/test/Microsoft.AspNetCore.Server.WebListener.FunctionalTests/ServerTests.cs index 2c95f450bb..a439324a81 100644 --- a/test/Microsoft.AspNetCore.Server.WebListener.FunctionalTests/ServerTests.cs +++ b/test/Microsoft.AspNetCore.Server.WebListener.FunctionalTests/ServerTests.cs @@ -26,6 +26,8 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.Features; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; using Microsoft.Net.Http.Server; using Xunit; @@ -253,11 +255,9 @@ namespace Microsoft.AspNetCore.Server.WebListener string address; using (Utilities.CreateHttpServer(out address, httpContext => Task.FromResult(0))) { } - var factory = new ServerFactory(loggerFactory: null); - var server = factory.CreateServer(configuration: null); - var listener = server.Features.Get(); - listener.UrlPrefixes.Add(UrlPrefix.Create(address)); - listener.SetRequestQueueLimit(1001); + var server = new MessagePump(Options.Create(new WebListenerOptions()), new LoggerFactory()); + server.Listener.UrlPrefixes.Add(UrlPrefix.Create(address)); + server.Listener.SetRequestQueueLimit(1001); using (server) { diff --git a/test/Microsoft.AspNetCore.Server.WebListener.FunctionalTests/Utilities.cs b/test/Microsoft.AspNetCore.Server.WebListener.FunctionalTests/Utilities.cs index 689a0ed98e..b78a3d8b15 100644 --- a/test/Microsoft.AspNetCore.Server.WebListener.FunctionalTests/Utilities.cs +++ b/test/Microsoft.AspNetCore.Server.WebListener.FunctionalTests/Utilities.cs @@ -17,8 +17,10 @@ using System; using Microsoft.AspNetCore.Hosting.Server; +using Microsoft.AspNetCore.Hosting.Server.Features; using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Server.Features; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; using Microsoft.Net.Http.Server; namespace Microsoft.AspNetCore.Server.WebListener @@ -53,7 +55,6 @@ namespace Microsoft.AspNetCore.Server.WebListener internal static IServer CreateDynamicHttpServer(string basePath, AuthenticationSchemes authType, out string root, out string baseAddress, RequestDelegate app) { - var factory = new ServerFactory(loggerFactory: null); lock (PortLock) { while (NextPort < MaxPort) @@ -64,10 +65,9 @@ namespace Microsoft.AspNetCore.Server.WebListener root = prefix.Scheme + "://" + prefix.Host + ":" + prefix.Port; baseAddress = prefix.ToString(); - var server = factory.CreateServer(configuration: null); - var listener = server.Features.Get(); - listener.UrlPrefixes.Add(prefix); - listener.AuthenticationManager.AuthenticationSchemes = authType; + var server = new MessagePump(Options.Create(new WebListenerOptions()), new LoggerFactory()); + server.Features.Get().Addresses.Add(baseAddress); + server.Listener.AuthenticationManager.AuthenticationSchemes = authType; try { server.Start(new DummyApplication(app)); @@ -89,8 +89,7 @@ namespace Microsoft.AspNetCore.Server.WebListener internal static IServer CreateServer(string scheme, string host, int port, string path, RequestDelegate app) { - var factory = new ServerFactory(loggerFactory: null); - var server = factory.CreateServer(configuration: null); + var server = new MessagePump(Options.Create(new WebListenerOptions()), new LoggerFactory()); server.Features.Get().Addresses.Add(UrlPrefix.Create(scheme, host, port, path).ToString()); server.Start(new DummyApplication(app)); return server;