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.Diagnostics;
using System.Globalization;
using Microsoft.AspNet.Logging;
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
{
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 =
(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);
}
}
}

View File

@ -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<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>
/// 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 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<Prefix> UriPrefixes
public List<Prefix> UriPrefixes
{
get { return _uriPrefixes; }
}

View File

@ -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; }

View File

@ -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<TraceEventType, int, object, Exception, Func<object, Exception, string>, bool>;
using OpaqueFunc = Func<IDictionary<string, object>, 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; }
}

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.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<object, Task>;
using LoggerFactoryFunc = Func<string, Func<TraceEventType, int, object, Exception, Func<object, Exception, string>, bool>>;
/// <summary>
/// Implements the Katana setup pattern for this server.
/// Implements the setup process for this server.
/// </summary>
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>
/// Populates the server capabilities.
/// Also included is a configurable instance of the server.
/// Creates a configurable instance of the server.
/// </summary>
/// <param name="properties"></param>
[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);
}
/// <summary>
/// Creates a server and starts listening on the given addresses.
/// </summary>
/// <param name="app">The application entry point.</param>
/// <param name="properties">The configuration.</param>
/// <param name="app">The per-request application entry point.</param>
/// <param name="server">The value returned </param>
/// <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)
{
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>();
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?
}
}
}

View File

@ -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<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 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<string, object> _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<object>(ProcessRequestAsync);
_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
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<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);
throw new InvalidOperationException("No address prefixes were defined.");
}
LogHelper.LogInfo(_logger, "Start");
_listener.Start();
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 Microsoft.Net.Runtime;
@ -7,7 +8,9 @@ namespace Microsoft.AspNet.Hosting.Server
[AssemblyNeutral]
public interface IServerFactory
{
IServerConfiguration CreateConfiguration();
IDisposable Start(IServerConfiguration serverConfig, Func<object, Task> app);
// IServerConfiguration CreateConfiguration();
// IDisposable Start(IServerConfiguration serverConfig, Func<object, Task> app);
IDisposable Start(Func<object, Task> app);
}
}
*/

View File

@ -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

View File

@ -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<string, object> address = new Dictionary<string, object>();
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<string> SendRequestAsync(string uri)

View File

@ -191,20 +191,13 @@ namespace Microsoft.AspNet.Server.WebListener.Test
[Fact]
public async Task Server_SetQueueLimit_Success()
{
IDictionary<string, object> address = new Dictionary<string, object>();
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);

View File

@ -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<string, object> address = new Dictionary<string, object>();
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);
}
}
}

View File

@ -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": {