From 9e8032cc8a975bf3e001b3b8f5a1b1d4c247663c Mon Sep 17 00:00:00 2001 From: Chris Ross Date: Wed, 19 Mar 2014 11:48:39 -0700 Subject: [PATCH] Update IServerFactory, ILoggerFactory. --- .../LogHelper.cs | 24 ++++---- .../OwinWebListener.cs | 9 ++- .../Prefix.cs | 47 ++++++++++++++- .../RequestProcessing/RequestContext.cs | 4 +- .../ServerConfiguration.cs | 29 --------- .../ServerFactory.cs | 59 ++++++++++++------- .../WebListenerWrapper.cs | 46 ++++++++------- .../fx/IServerConfiguration.cs | 14 ----- .../fx/IServerFactory.cs | 9 ++- .../project.json | 5 +- .../RequestTests.cs | 14 ++--- .../ServerTests.cs | 17 ++---- .../Utilities.cs | 19 ++---- .../project.json | 5 +- 14 files changed, 154 insertions(+), 147 deletions(-) delete mode 100644 src/Microsoft.AspNet.Server.WebListener/ServerConfiguration.cs delete mode 100644 src/Microsoft.AspNet.Server.WebListener/fx/IServerConfiguration.cs diff --git a/src/Microsoft.AspNet.Server.WebListener/LogHelper.cs b/src/Microsoft.AspNet.Server.WebListener/LogHelper.cs index a6513df571..cc0fc9d4a4 100644 --- a/src/Microsoft.AspNet.Server.WebListener/LogHelper.cs +++ b/src/Microsoft.AspNet.Server.WebListener/LogHelper.cs @@ -7,12 +7,10 @@ using System; using System.Diagnostics; using System.Globalization; +using Microsoft.AspNet.Logging; namespace Microsoft.AspNet.Server.WebListener { - using LoggerFactoryFunc = Func, bool>>; - using LoggerFunc = Func, bool>; - internal static class LogHelper { private static readonly Func LogState = @@ -21,17 +19,17 @@ namespace Microsoft.AspNet.Server.WebListener private static readonly Func LogStateAndError = (state, error) => string.Format(CultureInfo.CurrentCulture, "{0}\r\n{1}", state, error); - internal static LoggerFunc CreateLogger(LoggerFactoryFunc factory, Type type) + internal static ILogger CreateLogger(ILoggerFactory factory, Type type) { if (factory == null) { return null; } - return factory(type.FullName); + return factory.Create(type.FullName); } - internal static void LogInfo(LoggerFunc logger, string data) + internal static void LogInfo(ILogger logger, string data) { if (logger == null) { @@ -39,11 +37,11 @@ namespace Microsoft.AspNet.Server.WebListener } else { - logger(TraceEventType.Information, 0, data, null, LogState); + logger.WriteInformation(data); } } - internal static void LogVerbose(LoggerFunc logger, string data) + internal static void LogVerbose(ILogger logger, string data) { if (logger == null) { @@ -51,11 +49,11 @@ namespace Microsoft.AspNet.Server.WebListener } else { - logger(TraceEventType.Verbose, 0, data, null, LogState); + logger.WriteVerbose(data); } } - internal static void LogException(LoggerFunc logger, string location, Exception exception) + internal static void LogException(ILogger logger, string location, Exception exception) { if (logger == null) { @@ -63,11 +61,11 @@ namespace Microsoft.AspNet.Server.WebListener } else { - logger(TraceEventType.Error, 0, location, exception, LogStateAndError); + logger.WriteError(location, exception); } } - internal static void LogError(LoggerFunc logger, string location, string message) + internal static void LogError(ILogger logger, string location, string message) { if (logger == null) { @@ -75,7 +73,7 @@ namespace Microsoft.AspNet.Server.WebListener } else { - logger(TraceEventType.Error, 0, location + ": " + message, null, LogState); + logger.WriteError(location + "; " + message); } } } diff --git a/src/Microsoft.AspNet.Server.WebListener/OwinWebListener.cs b/src/Microsoft.AspNet.Server.WebListener/OwinWebListener.cs index 904f7a032e..28deb59011 100644 --- a/src/Microsoft.AspNet.Server.WebListener/OwinWebListener.cs +++ b/src/Microsoft.AspNet.Server.WebListener/OwinWebListener.cs @@ -15,12 +15,11 @@ using System.Runtime.InteropServices; using System.Security.Authentication.ExtendedProtection; using System.Threading; using System.Threading.Tasks; +using Microsoft.AspNet.Logging; namespace Microsoft.AspNet.Server.WebListener { using AppFunc = Func; - using LoggerFactoryFunc = Func, bool>>; - using LoggerFunc = Func, bool>; /// /// An HTTP server wrapping the Http.Sys APIs that accepts requests and passes them on to the given OWIN application. @@ -58,7 +57,7 @@ namespace Microsoft.AspNet.Server.WebListener private readonly ConcurrentDictionary _connectionCancellationTokens; - private LoggerFunc _logger; + private ILogger _logger; private SafeHandle _requestQueueHandle; private volatile State _state; // m_State is set only within lock blocks, but often read outside locks. @@ -101,12 +100,12 @@ namespace Microsoft.AspNet.Server.WebListener Disposed, } - internal LoggerFunc Logger + internal ILogger Logger { get { return _logger; } } - internal List UriPrefixes + public List UriPrefixes { get { return _uriPrefixes; } } diff --git a/src/Microsoft.AspNet.Server.WebListener/Prefix.cs b/src/Microsoft.AspNet.Server.WebListener/Prefix.cs index 08a0fe61c5..a9c7d642d1 100644 --- a/src/Microsoft.AspNet.Server.WebListener/Prefix.cs +++ b/src/Microsoft.AspNet.Server.WebListener/Prefix.cs @@ -9,7 +9,7 @@ using System.Globalization; namespace Microsoft.AspNet.Server.WebListener { - internal class Prefix + public class Prefix { private Prefix(bool isHttps, string scheme, string host, string port, int portValue, string path) { @@ -76,6 +76,51 @@ namespace Microsoft.AspNet.Server.WebListener return new Prefix(isHttps, scheme, host, port, portValue, path); } + public static Prefix Create(string prefix) + { + string scheme = null; + string host = null; + string port = null; + string path = null; + string whole = prefix ?? string.Empty; + + int delimiterStart1 = whole.IndexOf("://", StringComparison.Ordinal); + if (delimiterStart1 < 0) + { + throw new FormatException("Invalid prefix, missing scheme separator: " + prefix); + } + int delimiterEnd1 = delimiterStart1 + "://".Length; + + int delimiterStart3 = whole.IndexOf("/", delimiterEnd1, StringComparison.Ordinal); + if (delimiterStart3 < 0) + { + delimiterStart3 = whole.Length; + } + int delimiterStart2 = whole.LastIndexOf(":", delimiterStart3 - 1, delimiterStart3 - delimiterEnd1, StringComparison.Ordinal); + int delimiterEnd2 = delimiterStart2 + ":".Length; + if (delimiterStart2 < 0) + { + delimiterStart2 = delimiterStart3; + delimiterEnd2 = delimiterStart3; + } + + scheme = whole.Substring(0, delimiterStart1); + string portString = whole.Substring(delimiterEnd2, delimiterStart3 - delimiterEnd2); + int ignored; + if (int.TryParse(portString, NumberStyles.Integer, CultureInfo.InvariantCulture, out ignored)) + { + host = whole.Substring(delimiterEnd1, delimiterStart2 - delimiterEnd1); + port = portString; + } + else + { + host = whole.Substring(delimiterEnd1, delimiterStart3 - delimiterEnd1); + } + path = whole.Substring(delimiterStart3); + + return Prefix.Create(scheme, host, port, path); + } + public bool IsHttps { get; private set; } public string Scheme { get; private set; } public string Host { get; private set; } diff --git a/src/Microsoft.AspNet.Server.WebListener/RequestProcessing/RequestContext.cs b/src/Microsoft.AspNet.Server.WebListener/RequestProcessing/RequestContext.cs index 8c4b8d30f0..b326432b45 100644 --- a/src/Microsoft.AspNet.Server.WebListener/RequestProcessing/RequestContext.cs +++ b/src/Microsoft.AspNet.Server.WebListener/RequestProcessing/RequestContext.cs @@ -11,10 +11,10 @@ using System.Diagnostics.CodeAnalysis; using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; +using Microsoft.AspNet.Logging; namespace Microsoft.AspNet.Server.WebListener { - using LoggerFunc = Func, bool>; using OpaqueFunc = Func, Task>; internal sealed class RequestContext : IDisposable @@ -88,7 +88,7 @@ namespace Microsoft.AspNet.Server.WebListener } } - internal LoggerFunc Logger + internal ILogger Logger { get { return Server.Logger; } } diff --git a/src/Microsoft.AspNet.Server.WebListener/ServerConfiguration.cs b/src/Microsoft.AspNet.Server.WebListener/ServerConfiguration.cs deleted file mode 100644 index a050ec4810..0000000000 --- a/src/Microsoft.AspNet.Server.WebListener/ServerConfiguration.cs +++ /dev/null @@ -1,29 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using Microsoft.AspNet.Hosting.Server; - -namespace Microsoft.AspNet.Server.WebListener -{ - internal class ServerConfiguration : IServerConfiguration - { - internal ServerConfiguration() - { - Addresses = new List>(1); - } - - public IList> Addresses - { - get; - internal set; - } - - public object AdvancedConfiguration - { - get; - internal set; - } - } -} diff --git a/src/Microsoft.AspNet.Server.WebListener/ServerFactory.cs b/src/Microsoft.AspNet.Server.WebListener/ServerFactory.cs index fc067472ce..ef06a65e13 100644 --- a/src/Microsoft.AspNet.Server.WebListener/ServerFactory.cs +++ b/src/Microsoft.AspNet.Server.WebListener/ServerFactory.cs @@ -19,66 +19,81 @@ using System; using System.Collections.Generic; -using System.Diagnostics; using System.Diagnostics.CodeAnalysis; -using System.Reflection; using System.Threading.Tasks; +using Microsoft.AspNet.Abstractions; +using Microsoft.AspNet.ConfigurationModel; using Microsoft.AspNet.Hosting.Server; +using Microsoft.AspNet.Logging; namespace Microsoft.AspNet.Server.WebListener { using AppFunc = Func; - using LoggerFactoryFunc = Func, bool>>; /// - /// Implements the Katana setup pattern for this server. + /// Implements the setup process for this server. /// public class ServerFactory : IServerFactory { - private LoggerFactoryFunc _loggerFactory; + private ILoggerFactory _loggerFactory; - public ServerFactory() + public ServerFactory(ILoggerFactory loggerFactory) { - // TODO: Get services from DI, like logger factory. + _loggerFactory = loggerFactory; } /// - /// Populates the server capabilities. - /// Also included is a configurable instance of the server. + /// Creates a configurable instance of the server. /// /// [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "Disposed by caller")] - public IServerConfiguration CreateConfiguration() + public IServerInformation Initialize(IConfiguration config) { - ServerConfiguration serverConfig = new ServerConfiguration(); - serverConfig.AdvancedConfiguration = new OwinWebListener(); - return serverConfig; + OwinWebListener listener = new OwinWebListener(); + ParseAddresses(config, listener); + return new WebListenerWrapper(listener, _loggerFactory); } /// - /// Creates a server and starts listening on the given addresses. /// - /// The application entry point. - /// The configuration. + /// The per-request application entry point. + /// The value returned /// The server. Invoke Dispose to shut down. - public IDisposable Start(IServerConfiguration serverConfig, AppFunc app) + public IDisposable Start(IServerInformation server, AppFunc app) { - if (serverConfig == null) + if (server == null) { - throw new ArgumentNullException("serverConfig"); + throw new ArgumentNullException("server"); } if (app == null) { throw new ArgumentNullException("app"); } - OwinWebListener server = (OwinWebListener)serverConfig.AdvancedConfiguration; + WebListenerWrapper wrapper = server as WebListenerWrapper; + if (wrapper == null) + { + throw new ArgumentException("server"); + } // TODO: var capabilities = new Dictionary(); - WebListenerWrapper wrapper = new WebListenerWrapper(server); - wrapper.Start(app, serverConfig.Addresses, _loggerFactory); + wrapper.Start(app); return wrapper; } + + private void ParseAddresses(IConfiguration config, OwinWebListener listener) + { + // TODO: Key format? + string urls; + if (config != null && config.TryGet("server.urls", out urls) && !string.IsNullOrEmpty(urls)) + { + foreach (var value in urls.Split(';')) + { + listener.UriPrefixes.Add(Prefix.Create(value)); + } + } + // TODO: look for just a port option? + } } } diff --git a/src/Microsoft.AspNet.Server.WebListener/WebListenerWrapper.cs b/src/Microsoft.AspNet.Server.WebListener/WebListenerWrapper.cs index 899a3a6638..8f32b84abe 100644 --- a/src/Microsoft.AspNet.Server.WebListener/WebListenerWrapper.cs +++ b/src/Microsoft.AspNet.Server.WebListener/WebListenerWrapper.cs @@ -2,23 +2,23 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Diagnostics.Contracts; -using System.Diagnostics; +using System.Threading; using System.Threading.Tasks; +using Microsoft.AspNet.Abstractions; +using Microsoft.AspNet.Logging; namespace Microsoft.AspNet.Server.WebListener { using AppFunc = Func; - using LoggerFactoryFunc = Func, bool>>; - using LoggerFunc = Func, bool>; - using System.Threading; - public class WebListenerWrapper : IDisposable + public class WebListenerWrapper : IServerInformation, IDisposable { private static readonly int DefaultMaxAccepts = 5 * Environment.ProcessorCount; - private OwinWebListener _listener; + private readonly OwinWebListener _listener; + private readonly ILogger _logger; + private AppFunc _appFunc; - private LoggerFunc _logger; private PumpLimits _pumpLimits; private int _acceptorCounts; @@ -26,39 +26,43 @@ namespace Microsoft.AspNet.Server.WebListener // TODO: private IDictionary _capabilities; - internal WebListenerWrapper(OwinWebListener listener) + internal WebListenerWrapper(OwinWebListener listener, ILoggerFactory loggerFactory) { Contract.Assert(listener != null); _listener = listener; + _logger = LogHelper.CreateLogger(loggerFactory, typeof(WebListenerWrapper)); _processRequest = new Action(ProcessRequestAsync); _pumpLimits = new PumpLimits(DefaultMaxAccepts); } - internal void Start(AppFunc app, IList> addresses, LoggerFactoryFunc loggerFactory) + public OwinWebListener Listener + { + get { return _listener; } + } + + // Microsoft.AspNet.Server.WebListener + public string Name + { + get { return System.Reflection.IntrospectionExtensions.GetTypeInfo(GetType()).Assembly.GetName().Name; } + } + + internal void Start(AppFunc app) { // Can't call Start twice Contract.Assert(_appFunc == null); Contract.Assert(app != null); - Contract.Assert(addresses != null); _appFunc = app; - _logger = LogHelper.CreateLogger(loggerFactory, typeof(WebListenerWrapper)); - LogHelper.LogInfo(_logger, "Start"); - foreach (var address in addresses) + if (_listener.UriPrefixes.Count == 0) { - // Build addresses from parts - var scheme = address.Get("scheme") ?? Constants.HttpScheme; - var host = address.Get("host") ?? "localhost"; - var port = address.Get("port") ?? "5000"; - var path = address.Get("path") ?? string.Empty; - - Prefix prefix = Prefix.Create(scheme, host, port, path); - _listener.UriPrefixes.Add(prefix); + throw new InvalidOperationException("No address prefixes were defined."); } + LogHelper.LogInfo(_logger, "Start"); + _listener.Start(); ActivateRequestProcessingLimits(); diff --git a/src/Microsoft.AspNet.Server.WebListener/fx/IServerConfiguration.cs b/src/Microsoft.AspNet.Server.WebListener/fx/IServerConfiguration.cs deleted file mode 100644 index 1eaf850ffd..0000000000 --- a/src/Microsoft.AspNet.Server.WebListener/fx/IServerConfiguration.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Threading.Tasks; -using Microsoft.Net.Runtime; - -namespace Microsoft.AspNet.Hosting.Server -{ - [AssemblyNeutral] - public interface IServerConfiguration - { - IList> Addresses { get; } - object AdvancedConfiguration { get; } - } -} diff --git a/src/Microsoft.AspNet.Server.WebListener/fx/IServerFactory.cs b/src/Microsoft.AspNet.Server.WebListener/fx/IServerFactory.cs index 02f525dff9..42495b42f8 100644 --- a/src/Microsoft.AspNet.Server.WebListener/fx/IServerFactory.cs +++ b/src/Microsoft.AspNet.Server.WebListener/fx/IServerFactory.cs @@ -1,4 +1,5 @@ -using System; +/* TODO: Take a temp dependency on Ms.Aspnet.Hosting until AssemblyNeutral gets fixed. +using System; using System.Threading.Tasks; using Microsoft.Net.Runtime; @@ -7,7 +8,9 @@ namespace Microsoft.AspNet.Hosting.Server [AssemblyNeutral] public interface IServerFactory { - IServerConfiguration CreateConfiguration(); - IDisposable Start(IServerConfiguration serverConfig, Func app); + // IServerConfiguration CreateConfiguration(); + // IDisposable Start(IServerConfiguration serverConfig, Func app); + IDisposable Start(Func app); } } +*/ \ No newline at end of file diff --git a/src/Microsoft.AspNet.Server.WebListener/project.json b/src/Microsoft.AspNet.Server.WebListener/project.json index 4296fd3058..38ac1dbd11 100644 --- a/src/Microsoft.AspNet.Server.WebListener/project.json +++ b/src/Microsoft.AspNet.Server.WebListener/project.json @@ -2,8 +2,11 @@ "version": "0.1-alpha-*", "dependencies": { "Microsoft.AspNet.Abstractions": "0.1-alpha-*", + "Microsoft.AspNet.ConfigurationModel": "0.1-alpha-*", "Microsoft.AspNet.HttpFeature": "0.1-alpha-*", - "Microsoft.AspNet.FeatureModel": "0.1-alpha-*" + "Microsoft.AspNet.FeatureModel": "0.1-alpha-*", + "Microsoft.AspNet.Logging": "0.1-alpha-*", + "Microsoft.AspNet.Hosting": "0.1-alpha-*" }, "compilationOptions": { "allowUnsafe": true diff --git a/test/Microsoft.AspNet.Server.WebListener.Test/RequestTests.cs b/test/Microsoft.AspNet.Server.WebListener.Test/RequestTests.cs index 7eb5ce5bca..7cfe0f4f1a 100644 --- a/test/Microsoft.AspNet.Server.WebListener.Test/RequestTests.cs +++ b/test/Microsoft.AspNet.Server.WebListener.Test/RequestTests.cs @@ -150,21 +150,15 @@ namespace Microsoft.AspNet.Server.WebListener.Test private IDisposable CreateServer(AppFunc app) { - ServerFactory factory = new ServerFactory(); - IServerConfiguration config = factory.CreateConfiguration(); + ServerFactory factory = new ServerFactory(null); + WebListenerWrapper wrapper = (WebListenerWrapper)factory.Initialize(null); foreach (string path in new[] { "/", "/11", "/2/3", "/2", "/11/2" }) { - IDictionary address = new Dictionary(); - address["scheme"] = "http"; - address["host"] = "localhost"; - address["port"] = "8080"; - address["path"] = path; - - config.Addresses.Add(address); + wrapper.Listener.UriPrefixes.Add(Prefix.Create("http", "localhost", "8080", path)); } - return factory.Start(config, app); + return factory.Start(wrapper, app); } private async Task SendRequestAsync(string uri) diff --git a/test/Microsoft.AspNet.Server.WebListener.Test/ServerTests.cs b/test/Microsoft.AspNet.Server.WebListener.Test/ServerTests.cs index a8b31ae327..ecbbc5c68a 100644 --- a/test/Microsoft.AspNet.Server.WebListener.Test/ServerTests.cs +++ b/test/Microsoft.AspNet.Server.WebListener.Test/ServerTests.cs @@ -191,20 +191,13 @@ namespace Microsoft.AspNet.Server.WebListener.Test [Fact] public async Task Server_SetQueueLimit_Success() { - IDictionary address = new Dictionary(); - address["scheme"] = "http"; - address["host"] = "localhost"; - address["port"] = "8080"; - address["path"] = string.Empty; + ServerFactory factory = new ServerFactory(null); + WebListenerWrapper wrapper = (WebListenerWrapper)factory.Initialize(null); + wrapper.Listener.UriPrefixes.Add(Prefix.Create("http://localhost:8080")); - ServerFactory factory = new ServerFactory(); - IServerConfiguration config = factory.CreateConfiguration(); - config.Addresses.Add(address); + wrapper.Listener.SetRequestQueueLimit(1001); - OwinWebListener listener = (OwinWebListener)config.AdvancedConfiguration; - listener.SetRequestQueueLimit(1001); - - using (factory.Start(config, env => Task.FromResult(0))) + using (factory.Start(wrapper, env => Task.FromResult(0))) { string response = await SendRequestAsync(Address); Assert.Equal(string.Empty, response); diff --git a/test/Microsoft.AspNet.Server.WebListener.Test/Utilities.cs b/test/Microsoft.AspNet.Server.WebListener.Test/Utilities.cs index 280f8e4536..abc80060dd 100644 --- a/test/Microsoft.AspNet.Server.WebListener.Test/Utilities.cs +++ b/test/Microsoft.AspNet.Server.WebListener.Test/Utilities.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Threading.Tasks; +using Microsoft.AspNet.Abstractions; using Microsoft.AspNet.Hosting.Server; namespace Microsoft.AspNet.Server.WebListener.Test @@ -31,20 +32,12 @@ namespace Microsoft.AspNet.Server.WebListener.Test internal static IDisposable CreateServer(string scheme, string host, string port, string path, AuthenticationType authType, AppFunc app) { - IDictionary address = new Dictionary(); - address["scheme"] = scheme; - address["host"] = host; - address["port"] = port; - address["path"] = path; + ServerFactory factory = new ServerFactory(null); + WebListenerWrapper wrapper = (WebListenerWrapper)factory.Initialize(null); + wrapper.Listener.UriPrefixes.Add(Prefix.Create(scheme, host, port, path)); + wrapper.Listener.AuthenticationManager.AuthenticationTypes = authType; - ServerFactory factory = new ServerFactory(); - IServerConfiguration config = factory.CreateConfiguration(); - config.Addresses.Add(address); - - OwinWebListener listener = (OwinWebListener)config.AdvancedConfiguration; - listener.AuthenticationManager.AuthenticationTypes = authType; - - return factory.Start(config, app); + return factory.Start(wrapper, app); } } } diff --git a/test/Microsoft.AspNet.Server.WebListener.Test/project.json b/test/Microsoft.AspNet.Server.WebListener.Test/project.json index 48f45fc05b..a2837ddee9 100644 --- a/test/Microsoft.AspNet.Server.WebListener.Test/project.json +++ b/test/Microsoft.AspNet.Server.WebListener.Test/project.json @@ -3,9 +3,12 @@ "dependencies": { "Microsoft.AspNet.Server.WebListener" : "", "Microsoft.AspNet.Abstractions" : "0.1-alpha-*", + "Microsoft.AspNet.ConfigurationModel": "0.1-alpha-*", "Microsoft.AspNet.HttpFeature" : "0.1-alpha-*", "Microsoft.AspNet.FeatureModel" : "0.1-alpha-*", - "Microsoft.AspNet.PipelineCore" : "0.1-alpha-*" + "Microsoft.AspNet.PipelineCore" : "0.1-alpha-*", + "Microsoft.AspNet.Logging": "0.1-alpha-*", + "Microsoft.AspNet.Hosting": "0.1-alpha-*" }, "configurations": { "net45": {