Update IServerFactory, ILoggerFactory.

This commit is contained in:
Chris Ross 2014-03-19 11:48:39 -07:00
parent b374c1b7e3
commit 9e8032cc8a
14 changed files with 154 additions and 147 deletions

View File

@ -7,12 +7,10 @@
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using System.Globalization; using System.Globalization;
using Microsoft.AspNet.Logging;
namespace Microsoft.AspNet.Server.WebListener namespace Microsoft.AspNet.Server.WebListener
{ {
using LoggerFactoryFunc = Func<string, Func<TraceEventType, int, object, Exception, Func<object, Exception, string>, bool>>;
using LoggerFunc = Func<TraceEventType, int, object, Exception, Func<object, Exception, string>, bool>;
internal static class LogHelper internal static class LogHelper
{ {
private static readonly Func<object, Exception, string> LogState = private static readonly Func<object, Exception, string> LogState =
@ -21,17 +19,17 @@ namespace Microsoft.AspNet.Server.WebListener
private static readonly Func<object, Exception, string> LogStateAndError = private static readonly Func<object, Exception, string> LogStateAndError =
(state, error) => string.Format(CultureInfo.CurrentCulture, "{0}\r\n{1}", state, error); (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) if (factory == null)
{ {
return 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) if (logger == null)
{ {
@ -39,11 +37,11 @@ namespace Microsoft.AspNet.Server.WebListener
} }
else 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) if (logger == null)
{ {
@ -51,11 +49,11 @@ namespace Microsoft.AspNet.Server.WebListener
} }
else 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) if (logger == null)
{ {
@ -63,11 +61,11 @@ namespace Microsoft.AspNet.Server.WebListener
} }
else 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) if (logger == null)
{ {
@ -75,7 +73,7 @@ namespace Microsoft.AspNet.Server.WebListener
} }
else else
{ {
logger(TraceEventType.Error, 0, location + ": " + message, null, LogState); logger.WriteError(location + "; " + message);
} }
} }
} }

View File

@ -15,12 +15,11 @@ using System.Runtime.InteropServices;
using System.Security.Authentication.ExtendedProtection; using System.Security.Authentication.ExtendedProtection;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNet.Logging;
namespace Microsoft.AspNet.Server.WebListener namespace Microsoft.AspNet.Server.WebListener
{ {
using AppFunc = Func<object, Task>; using AppFunc = Func<object, Task>;
using LoggerFactoryFunc = Func<string, Func<TraceEventType, int, object, Exception, Func<object, Exception, string>, bool>>;
using LoggerFunc = Func<TraceEventType, int, object, Exception, Func<object, Exception, string>, bool>;
/// <summary> /// <summary>
/// An HTTP server wrapping the Http.Sys APIs that accepts requests and passes them on to the given OWIN application. /// 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<ulong, ConnectionCancellation> _connectionCancellationTokens; private readonly ConcurrentDictionary<ulong, ConnectionCancellation> _connectionCancellationTokens;
private LoggerFunc _logger; private ILogger _logger;
private SafeHandle _requestQueueHandle; private SafeHandle _requestQueueHandle;
private volatile State _state; // m_State is set only within lock blocks, but often read outside locks. 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, Disposed,
} }
internal LoggerFunc Logger internal ILogger Logger
{ {
get { return _logger; } get { return _logger; }
} }
internal List<Prefix> UriPrefixes public List<Prefix> UriPrefixes
{ {
get { return _uriPrefixes; } get { return _uriPrefixes; }
} }

View File

@ -9,7 +9,7 @@ using System.Globalization;
namespace Microsoft.AspNet.Server.WebListener 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) 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); 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 bool IsHttps { get; private set; }
public string Scheme { get; private set; } public string Scheme { get; private set; }
public string Host { get; private set; } public string Host { get; private set; }

View File

@ -11,10 +11,10 @@ using System.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNet.Logging;
namespace Microsoft.AspNet.Server.WebListener namespace Microsoft.AspNet.Server.WebListener
{ {
using LoggerFunc = Func<TraceEventType, int, object, Exception, Func<object, Exception, string>, bool>;
using OpaqueFunc = Func<IDictionary<string, object>, Task>; using OpaqueFunc = Func<IDictionary<string, object>, Task>;
internal sealed class RequestContext : IDisposable internal sealed class RequestContext : IDisposable
@ -88,7 +88,7 @@ namespace Microsoft.AspNet.Server.WebListener
} }
} }
internal LoggerFunc Logger internal ILogger Logger
{ {
get { return Server.Logger; } get { return Server.Logger; }
} }

View File

@ -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<IDictionary<string, object>>(1);
}
public IList<IDictionary<string, object>> Addresses
{
get;
internal set;
}
public object AdvancedConfiguration
{
get;
internal set;
}
}
}

View File

@ -19,66 +19,81 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using System.Reflection;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNet.Abstractions;
using Microsoft.AspNet.ConfigurationModel;
using Microsoft.AspNet.Hosting.Server; using Microsoft.AspNet.Hosting.Server;
using Microsoft.AspNet.Logging;
namespace Microsoft.AspNet.Server.WebListener namespace Microsoft.AspNet.Server.WebListener
{ {
using AppFunc = Func<object, Task>; using AppFunc = Func<object, Task>;
using LoggerFactoryFunc = Func<string, Func<TraceEventType, int, object, Exception, Func<object, Exception, string>, bool>>;
/// <summary> /// <summary>
/// Implements the Katana setup pattern for this server. /// Implements the setup process for this server.
/// </summary> /// </summary>
public class ServerFactory : IServerFactory 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;
} }
/// <summary> /// <summary>
/// Populates the server capabilities. /// Creates a configurable instance of the server.
/// Also included is a configurable instance of the server.
/// </summary> /// </summary>
/// <param name="properties"></param> /// <param name="properties"></param>
[SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "Disposed by caller")] [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(); OwinWebListener listener = new OwinWebListener();
serverConfig.AdvancedConfiguration = new OwinWebListener(); ParseAddresses(config, listener);
return serverConfig; return new WebListenerWrapper(listener, _loggerFactory);
} }
/// <summary> /// <summary>
/// Creates a server and starts listening on the given addresses.
/// </summary> /// </summary>
/// <param name="app">The application entry point.</param> /// <param name="app">The per-request application entry point.</param>
/// <param name="properties">The configuration.</param> /// <param name="server">The value returned </param>
/// <returns>The server. Invoke Dispose to shut down.</returns> /// <returns>The server. Invoke Dispose to shut down.</returns>
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) if (app == null)
{ {
throw new ArgumentNullException("app"); 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<string, object>(); // TODO: var capabilities = new Dictionary<string, object>();
WebListenerWrapper wrapper = new WebListenerWrapper(server);
wrapper.Start(app, serverConfig.Addresses, _loggerFactory); wrapper.Start(app);
return wrapper; 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?
}
} }
} }

View File

@ -2,23 +2,23 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.Contracts; using System.Diagnostics.Contracts;
using System.Diagnostics; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNet.Abstractions;
using Microsoft.AspNet.Logging;
namespace Microsoft.AspNet.Server.WebListener namespace Microsoft.AspNet.Server.WebListener
{ {
using AppFunc = Func<object, Task>; using AppFunc = Func<object, Task>;
using LoggerFactoryFunc = Func<string, Func<TraceEventType, int, object, Exception, Func<object, Exception, string>, bool>>;
using LoggerFunc = Func<TraceEventType, int, object, Exception, Func<object, Exception, string>, bool>;
using System.Threading;
public class WebListenerWrapper : IDisposable public class WebListenerWrapper : IServerInformation, IDisposable
{ {
private static readonly int DefaultMaxAccepts = 5 * Environment.ProcessorCount; private static readonly int DefaultMaxAccepts = 5 * Environment.ProcessorCount;
private OwinWebListener _listener; private readonly OwinWebListener _listener;
private readonly ILogger _logger;
private AppFunc _appFunc; private AppFunc _appFunc;
private LoggerFunc _logger;
private PumpLimits _pumpLimits; private PumpLimits _pumpLimits;
private int _acceptorCounts; private int _acceptorCounts;
@ -26,39 +26,43 @@ namespace Microsoft.AspNet.Server.WebListener
// TODO: private IDictionary<string, object> _capabilities; // TODO: private IDictionary<string, object> _capabilities;
internal WebListenerWrapper(OwinWebListener listener) internal WebListenerWrapper(OwinWebListener listener, ILoggerFactory loggerFactory)
{ {
Contract.Assert(listener != null); Contract.Assert(listener != null);
_listener = listener; _listener = listener;
_logger = LogHelper.CreateLogger(loggerFactory, typeof(WebListenerWrapper));
_processRequest = new Action<object>(ProcessRequestAsync); _processRequest = new Action<object>(ProcessRequestAsync);
_pumpLimits = new PumpLimits(DefaultMaxAccepts); _pumpLimits = new PumpLimits(DefaultMaxAccepts);
} }
internal void Start(AppFunc app, IList<IDictionary<string, object>> 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 // Can't call Start twice
Contract.Assert(_appFunc == null); Contract.Assert(_appFunc == null);
Contract.Assert(app != null); Contract.Assert(app != null);
Contract.Assert(addresses != null);
_appFunc = app; _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 throw new InvalidOperationException("No address prefixes were defined.");
var scheme = address.Get<string>("scheme") ?? Constants.HttpScheme;
var host = address.Get<string>("host") ?? "localhost";
var port = address.Get<string>("port") ?? "5000";
var path = address.Get<string>("path") ?? string.Empty;
Prefix prefix = Prefix.Create(scheme, host, port, path);
_listener.UriPrefixes.Add(prefix);
} }
LogHelper.LogInfo(_logger, "Start");
_listener.Start(); _listener.Start();
ActivateRequestProcessingLimits(); ActivateRequestProcessingLimits();

View File

@ -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<IDictionary<string, object>> Addresses { get; }
object AdvancedConfiguration { get; }
}
}

View File

@ -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 System.Threading.Tasks;
using Microsoft.Net.Runtime; using Microsoft.Net.Runtime;
@ -7,7 +8,9 @@ namespace Microsoft.AspNet.Hosting.Server
[AssemblyNeutral] [AssemblyNeutral]
public interface IServerFactory public interface IServerFactory
{ {
IServerConfiguration CreateConfiguration(); // IServerConfiguration CreateConfiguration();
IDisposable Start(IServerConfiguration serverConfig, Func<object, Task> app); // IDisposable Start(IServerConfiguration serverConfig, Func<object, Task> app);
IDisposable Start(Func<object, Task> app);
} }
} }
*/

View File

@ -2,8 +2,11 @@
"version": "0.1-alpha-*", "version": "0.1-alpha-*",
"dependencies": { "dependencies": {
"Microsoft.AspNet.Abstractions": "0.1-alpha-*", "Microsoft.AspNet.Abstractions": "0.1-alpha-*",
"Microsoft.AspNet.ConfigurationModel": "0.1-alpha-*",
"Microsoft.AspNet.HttpFeature": "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": { "compilationOptions": {
"allowUnsafe": true "allowUnsafe": true

View File

@ -150,21 +150,15 @@ namespace Microsoft.AspNet.Server.WebListener.Test
private IDisposable CreateServer(AppFunc app) private IDisposable CreateServer(AppFunc app)
{ {
ServerFactory factory = new ServerFactory(); ServerFactory factory = new ServerFactory(null);
IServerConfiguration config = factory.CreateConfiguration(); WebListenerWrapper wrapper = (WebListenerWrapper)factory.Initialize(null);
foreach (string path in new[] { "/", "/11", "/2/3", "/2", "/11/2" }) foreach (string path in new[] { "/", "/11", "/2/3", "/2", "/11/2" })
{ {
IDictionary<string, object> address = new Dictionary<string, object>(); wrapper.Listener.UriPrefixes.Add(Prefix.Create("http", "localhost", "8080", path));
address["scheme"] = "http";
address["host"] = "localhost";
address["port"] = "8080";
address["path"] = path;
config.Addresses.Add(address);
} }
return factory.Start(config, app); return factory.Start(wrapper, app);
} }
private async Task<string> SendRequestAsync(string uri) private async Task<string> SendRequestAsync(string uri)

View File

@ -191,20 +191,13 @@ namespace Microsoft.AspNet.Server.WebListener.Test
[Fact] [Fact]
public async Task Server_SetQueueLimit_Success() public async Task Server_SetQueueLimit_Success()
{ {
IDictionary<string, object> address = new Dictionary<string, object>(); ServerFactory factory = new ServerFactory(null);
address["scheme"] = "http"; WebListenerWrapper wrapper = (WebListenerWrapper)factory.Initialize(null);
address["host"] = "localhost"; wrapper.Listener.UriPrefixes.Add(Prefix.Create("http://localhost:8080"));
address["port"] = "8080";
address["path"] = string.Empty;
ServerFactory factory = new ServerFactory(); wrapper.Listener.SetRequestQueueLimit(1001);
IServerConfiguration config = factory.CreateConfiguration();
config.Addresses.Add(address);
OwinWebListener listener = (OwinWebListener)config.AdvancedConfiguration; using (factory.Start(wrapper, env => Task.FromResult(0)))
listener.SetRequestQueueLimit(1001);
using (factory.Start(config, env => Task.FromResult(0)))
{ {
string response = await SendRequestAsync(Address); string response = await SendRequestAsync(Address);
Assert.Equal(string.Empty, response); Assert.Equal(string.Empty, response);

View File

@ -1,6 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNet.Abstractions;
using Microsoft.AspNet.Hosting.Server; using Microsoft.AspNet.Hosting.Server;
namespace Microsoft.AspNet.Server.WebListener.Test 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) internal static IDisposable CreateServer(string scheme, string host, string port, string path, AuthenticationType authType, AppFunc app)
{ {
IDictionary<string, object> address = new Dictionary<string, object>(); ServerFactory factory = new ServerFactory(null);
address["scheme"] = scheme; WebListenerWrapper wrapper = (WebListenerWrapper)factory.Initialize(null);
address["host"] = host; wrapper.Listener.UriPrefixes.Add(Prefix.Create(scheme, host, port, path));
address["port"] = port; wrapper.Listener.AuthenticationManager.AuthenticationTypes = authType;
address["path"] = path;
ServerFactory factory = new ServerFactory(); return factory.Start(wrapper, app);
IServerConfiguration config = factory.CreateConfiguration();
config.Addresses.Add(address);
OwinWebListener listener = (OwinWebListener)config.AdvancedConfiguration;
listener.AuthenticationManager.AuthenticationTypes = authType;
return factory.Start(config, app);
} }
} }
} }

View File

@ -3,9 +3,12 @@
"dependencies": { "dependencies": {
"Microsoft.AspNet.Server.WebListener" : "", "Microsoft.AspNet.Server.WebListener" : "",
"Microsoft.AspNet.Abstractions" : "0.1-alpha-*", "Microsoft.AspNet.Abstractions" : "0.1-alpha-*",
"Microsoft.AspNet.ConfigurationModel": "0.1-alpha-*",
"Microsoft.AspNet.HttpFeature" : "0.1-alpha-*", "Microsoft.AspNet.HttpFeature" : "0.1-alpha-*",
"Microsoft.AspNet.FeatureModel" : "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": { "configurations": {
"net45": { "net45": {