#160 Refactor options/settings

This commit is contained in:
Chris R 2016-08-18 13:46:36 -07:00
parent cd886802fe
commit e39ea62808
23 changed files with 275 additions and 198 deletions

View File

@ -16,9 +16,11 @@ namespace HelloWorld
public static async Task Run(string[] args)
{
using (WebListener listener = new WebListener())
var settings = new WebListenerSettings();
settings.UrlPrefixes.Add("http://localhost:8080");
using (WebListener listener = new WebListener(settings))
{
listener.UrlPrefixes.Add("http://localhost:8080");
listener.Start();
Console.WriteLine("Running...");

View File

@ -16,7 +16,7 @@ namespace HotAddSample
{
loggerfactory.AddConsole(LogLevel.Information);
var addresses = app.ServerFeatures.Get<WebListener>().UrlPrefixes;
var addresses = app.ServerFeatures.Get<WebListener>().Settings.UrlPrefixes;
addresses.Add("http://localhost:12346/pathBase/");
app.Use(async (context, next) =>

View File

@ -19,8 +19,8 @@ namespace SelfHostServer
// Server options can be configured here instead of in Main.
services.Configure<WebListenerOptions>(options =>
{
options.Listener.AuthenticationManager.AuthenticationSchemes = AuthenticationSchemes.None;
options.Listener.AuthenticationManager.AllowAnonymous = true;
options.ListenerSettings.Authentication.Schemes = AuthenticationSchemes.None;
options.ListenerSettings.Authentication.AllowAnonymous = true;
});
}
@ -52,8 +52,8 @@ namespace SelfHostServer
.UseStartup<Startup>()
.UseWebListener(options =>
{
options.Listener.AuthenticationManager.AuthenticationSchemes = AuthenticationSchemes.None;
options.Listener.AuthenticationManager.AllowAnonymous = true;
options.ListenerSettings.Authentication.Schemes = AuthenticationSchemes.None;
options.ListenerSettings.Authentication.AllowAnonymous = true;
})
.Build();

View File

@ -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");
@ -31,7 +31,7 @@ namespace Microsoft.AspNetCore.Server.WebListener.Internal
public void Configure(WebListenerOptions options)
{
options.Listener = new Microsoft.Net.Http.Server.WebListener(_loggerFactory);
options.ListenerSettings.Logger = _loggerFactory.CreateLogger<Microsoft.Net.Http.Server.WebListener>();
}
}
}
}

View File

@ -57,15 +57,16 @@ namespace Microsoft.AspNetCore.Server.WebListener
throw new ArgumentNullException(nameof(loggerFactory));
}
_listener = options.Value?.Listener ?? new Microsoft.Net.Http.Server.WebListener(loggerFactory);
var optionsInstance = options.Value;
_listener = new Microsoft.Net.Http.Server.WebListener(optionsInstance.ListenerSettings);
_logger = LogHelper.CreateLogger(loggerFactory, typeof(MessagePump));
Features = new FeatureCollection();
_serverAddresses = new ServerAddressesFeature();
Features.Set<IServerAddressesFeature>(_serverAddresses);
_processRequest = new Action<object>(ProcessRequestAsync);
_maxAccepts = options.Value?.MaxAccepts ?? WebListenerOptions.DefaultMaxAccepts;
EnableResponseCaching = options.Value?.EnableResponseCaching ?? true;
_maxAccepts = optionsInstance.MaxAccepts;
EnableResponseCaching = optionsInstance.EnableResponseCaching;
_shutdownSignal = new ManualResetEvent(false);
}
@ -94,7 +95,7 @@ namespace Microsoft.AspNetCore.Server.WebListener
_application = new ApplicationWrapper<TContext>(application);
if (_listener.UrlPrefixes.Count == 0)
if (_listener.Settings.UrlPrefixes.Count == 0)
{
throw new InvalidOperationException("No address prefixes were defined.");
}
@ -224,7 +225,7 @@ namespace Microsoft.AspNetCore.Server.WebListener
{
foreach (var value in addresses)
{
listener.UrlPrefixes.Add(UrlPrefix.Create(value));
listener.Settings.UrlPrefixes.Add(UrlPrefix.Create(value));
}
}

View File

@ -16,6 +16,7 @@
// permissions and limitations under the License.
using System;
using Microsoft.Net.Http.Server;
namespace Microsoft.AspNetCore.Server.WebListener
{
@ -23,10 +24,21 @@ namespace Microsoft.AspNetCore.Server.WebListener
{
internal static readonly int DefaultMaxAccepts = 5 * Environment.ProcessorCount;
public Microsoft.Net.Http.Server.WebListener Listener { get; set; } = new Microsoft.Net.Http.Server.WebListener();
/// <summary>
/// Settings for the underlying WebListener instance.
/// </summary>
public WebListenerSettings ListenerSettings { get; } = new WebListenerSettings();
/// <summary>
/// The maximum number of concurrent calls to WebListener.AcceptAsync().
/// </summary>
public int MaxAccepts { get; set; } = DefaultMaxAccepts;
/// <summary>
/// Attempts kernel mode caching for responses with eligible headers. The response may not include
/// Set-Cookie, Vary, or Pragma headers. It must include a Cache-Control header with Public and
/// either a Shared-Max-Age or Max-Age value, or an Expires header.
/// </summary>
public bool EnableResponseCaching { get; set; } = true;
}
}

View File

@ -23,6 +23,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.Security.Claims;
@ -42,24 +43,21 @@ namespace Microsoft.Net.Http.Server
private static readonly int AuthInfoSize =
Marshal.SizeOf<HttpApi.HTTP_SERVER_AUTHENTICATION_INFO>();
private WebListener _server;
private UrlGroup _urlGroup;
private AuthenticationSchemes _authSchemes;
private bool _allowAnonymous = true;
internal AuthenticationManager(WebListener listener)
internal AuthenticationManager()
{
_server = listener;
}
#region Properties
public AuthenticationSchemes AuthenticationSchemes
public AuthenticationSchemes Schemes
{
get { return _authSchemes; }
set
{
_authSchemes = value;
SetServerSecurity();
SetUrlGroupSecurity();
}
}
@ -69,10 +67,21 @@ namespace Microsoft.Net.Http.Server
set { _allowAnonymous = value; }
}
#endregion Properties
private unsafe void SetServerSecurity()
internal void SetUrlGroupSecurity(UrlGroup urlGroup)
{
Debug.Assert(_urlGroup == null, "SetUrlGroupSecurity called more than once.");
_urlGroup = urlGroup;
SetUrlGroupSecurity();
}
private unsafe void SetUrlGroupSecurity()
{
if (_urlGroup == null)
{
// Not started yet.
return;
}
HttpApi.HTTP_SERVER_AUTHENTICATION_INFO authInfo =
new HttpApi.HTTP_SERVER_AUTHENTICATION_INFO();
@ -91,7 +100,7 @@ namespace Microsoft.Net.Http.Server
IntPtr infoptr = new IntPtr(&authInfo);
_server.UrlGroup.SetProperty(
_urlGroup.SetProperty(
HttpApi.HTTP_SERVER_PROPERTY.HttpServerAuthenticationProperty,
infoptr, (uint)AuthInfoSize);
}

View File

@ -29,16 +29,6 @@ namespace Microsoft.Net.Http.Server
{
internal static class LogHelper
{
internal static ILogger CreateLogger(ILoggerFactory factory, Type type)
{
if (factory == null)
{
return new NullLogger();
}
return factory.CreateLogger(type.FullName);
}
internal static void LogInfo(ILogger logger, string data)
{
if (logger == null)
@ -86,29 +76,5 @@ namespace Microsoft.Net.Http.Server
logger.LogError(location + "; " + message);
}
}
private class NullLogger : ILogger
{
public IDisposable BeginScope<TState>(TState state)
{
return new NullDispose();
}
public bool IsEnabled(LogLevel logLevel)
{
return false;
}
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
{
}
private class NullDispose : IDisposable
{
public void Dispose()
{
}
}
}
}
}

View File

@ -16,11 +16,8 @@
// permissions and limitations under the License.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
namespace Microsoft.Net.Http.Server
@ -32,6 +29,7 @@ namespace Microsoft.Net.Http.Server
private readonly UrlGroup _urlGroup;
private readonly ILogger _logger;
private bool _disposed;
internal RequestQueue(UrlGroup urlGroup, ILogger logger)
{
@ -66,6 +64,7 @@ namespace Microsoft.Net.Http.Server
internal unsafe void AttachToUrlGroup()
{
CheckDisposed();
// Set the association between request queue and url group. After this, requests for registered urls will
// get delivered to this request queue.
@ -81,6 +80,7 @@ namespace Microsoft.Net.Http.Server
internal unsafe void DetachFromUrlGroup()
{
CheckDisposed();
// Break the association between request queue and url group. After this, requests for registered urls
// will get 503s.
// Note that this method may be called multiple times (Stop() and then Abort()). This
@ -100,6 +100,8 @@ namespace Microsoft.Net.Http.Server
// The listener must be active for this to work.
internal unsafe void SetLengthLimit(long length)
{
CheckDisposed();
var result = HttpApi.HttpSetRequestQueueProperty(Handle,
HttpApi.HTTP_SERVER_PROPERTY.HttpServerQueueLengthProperty,
new IntPtr((void*)&length), (uint)Marshal.SizeOf<long>(), 0, IntPtr.Zero);
@ -112,8 +114,22 @@ namespace Microsoft.Net.Http.Server
public void Dispose()
{
if (_disposed)
{
return;
}
_disposed = true;
BoundHandle.Dispose();
Handle.Dispose();
}
private void CheckDisposed()
{
if (_disposed)
{
throw new ObjectDisposedException(this.GetType().FullName);
}
}
}
}

View File

@ -50,7 +50,8 @@ namespace Microsoft.Net.Http.Server
internal void SetProperty(HttpApi.HTTP_SERVER_PROPERTY property, IntPtr info, uint infosize, bool throwOnError = true)
{
Debug.Assert(info != IntPtr.Zero, "SetUrlGroupProperty called with invalid pointer");
CheckDisposed();
var statusCode = HttpApi.HttpSetUrlGroupProperty(Id, property, info, infosize);
if (statusCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS)
@ -67,6 +68,7 @@ namespace Microsoft.Net.Http.Server
internal void RegisterPrefix(string uriPrefix, int contextId)
{
LogHelper.LogInfo(_logger, "Listening on prefix: " + uriPrefix);
CheckDisposed();
var statusCode = HttpApi.HttpAddUrlToUrlGroup(Id, uriPrefix, (ulong)contextId, 0);
@ -86,6 +88,7 @@ namespace Microsoft.Net.Http.Server
internal bool UnregisterPrefix(string uriPrefix)
{
LogHelper.LogInfo(_logger, "Stop listening on prefix: " + uriPrefix);
CheckDisposed();
var statusCode = HttpApi.HttpRemoveUrlFromUrlGroup(Id, uriPrefix, 0);
@ -115,5 +118,13 @@ namespace Microsoft.Net.Http.Server
}
Id = 0;
}
private void CheckDisposed()
{
if (_disposed)
{
throw new ObjectDisposedException(this.GetType().FullName);
}
}
}
}

View File

@ -85,7 +85,7 @@ namespace Microsoft.Net.Http.Server
QueryString = Marshal.PtrToStringUni((IntPtr)cookedUrl.pQueryString, cookedUrl.QueryStringLength / 2);
}
var prefix = requestContext.Server.UrlPrefixes.GetPrefix((int)memoryBlob.RequestBlob->UrlContext);
var prefix = requestContext.Server.Settings.UrlPrefixes.GetPrefix((int)memoryBlob.RequestBlob->UrlContext);
var originalPath = RequestUriBuilder.GetRequestPath(RawUrl, cookedUrlPath, RequestContext.Logger);
// These paths are both unescaped already.

View File

@ -64,7 +64,7 @@ namespace Microsoft.Net.Http.Server
_expectedBodyLength = 0;
_nativeStream = null;
_cacheTtl = null;
_authChallenges = RequestContext.Server.AuthenticationManager.AuthenticationSchemes;
_authChallenges = RequestContext.Server.Settings.Authentication.Schemes;
}
private enum ResponseState
@ -412,7 +412,7 @@ namespace Microsoft.Net.Http.Server
// 401
if (StatusCode == (ushort)HttpStatusCode.Unauthorized)
{
RequestContext.Server.AuthenticationManager.SetAuthenticationChallenge(RequestContext);
RequestContext.Server.Settings.Authentication.SetAuthenticationChallenge(RequestContext);
}
var flags = HttpApi.HTTP_FLAGS.NONE;

View File

@ -171,7 +171,7 @@ namespace Microsoft.Net.Http.Server
IntPtr.Zero);
}
if (_requestContext.Server.IgnoreWriteExceptions)
if (_requestContext.Server.Settings.IgnoreWriteExceptions)
{
statusCode = UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS;
}
@ -338,7 +338,7 @@ namespace Microsoft.Net.Http.Server
if (statusCode != ErrorCodes.ERROR_SUCCESS && statusCode != ErrorCodes.ERROR_IO_PENDING)
{
asyncResult.Dispose();
if (_requestContext.Server.IgnoreWriteExceptions && started)
if (_requestContext.Server.Settings.IgnoreWriteExceptions && started)
{
asyncResult.Complete();
}
@ -602,7 +602,7 @@ namespace Microsoft.Net.Http.Server
if (statusCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS && statusCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_IO_PENDING)
{
asyncResult.Dispose();
if (_requestContext.Server.IgnoreWriteExceptions && started)
if (_requestContext.Server.Settings.IgnoreWriteExceptions && started)
{
asyncResult.Complete();
}

View File

@ -37,14 +37,12 @@ namespace Microsoft.Net.Http.Server
private static readonly int TimeoutLimitSize =
Marshal.SizeOf<HttpApi.HTTP_TIMEOUT_LIMIT_INFO>();
private WebListener _server;
private UrlGroup _urlGroup;
private int[] _timeouts;
private uint _minSendBytesPerSecond;
internal TimeoutManager(WebListener listener)
internal TimeoutManager()
{
_server = listener;
// We have to maintain local state since we allow applications to set individual timeouts. Native Http
// API for setting timeouts expects all timeout values in every call so we have remember timeout values
// to fill in the blanks. Except MinSendBytesPerSecond, local state for remaining five timeouts is
@ -180,7 +178,7 @@ namespace Microsoft.Net.Http.Server
throw new ArgumentOutOfRangeException("value");
}
SetServerTimeouts(_timeouts, (uint)value);
SetUrlGroupTimeouts(_timeouts, (uint)value);
_minSendBytesPerSecond = (uint)value;
}
}
@ -211,12 +209,24 @@ namespace Microsoft.Net.Http.Server
// call succeeds, update local state.
var newTimeouts = (int[])_timeouts.Clone();
newTimeouts[(int)type] = (int)timeoutValue;
SetServerTimeouts(newTimeouts, _minSendBytesPerSecond);
SetUrlGroupTimeouts(newTimeouts, _minSendBytesPerSecond);
_timeouts[(int)type] = (int)timeoutValue;
}
private unsafe void SetServerTimeouts(int[] timeouts, uint minSendBytesPerSecond)
internal void SetUrlGroupTimeouts(UrlGroup urlGroup)
{
_urlGroup = urlGroup;
SetUrlGroupTimeouts(_timeouts, _minSendBytesPerSecond);
}
private unsafe void SetUrlGroupTimeouts(int[] timeouts, uint minSendBytesPerSecond)
{
if (_urlGroup == null)
{
// Not started yet
return;
}
var timeoutinfo = new HttpApi.HTTP_TIMEOUT_LIMIT_INFO();
timeoutinfo.Flags = HttpApi.HTTP_FLAGS.HTTP_PROPERTY_FLAG_PRESENT;
@ -234,7 +244,7 @@ namespace Microsoft.Net.Http.Server
var infoptr = new IntPtr(&timeoutinfo);
_server.UrlGroup.SetProperty(
_urlGroup.SetProperty(
HttpApi.HTTP_SERVER_PROPERTY.HttpServerTimeoutsProperty,
infoptr, (uint)TimeoutLimitSize);
}

View File

@ -1,7 +1,6 @@
// Copyright (c) Microsoft Open Technologies, Inc. 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;
using System.Collections.Generic;
@ -12,13 +11,12 @@ namespace Microsoft.Net.Http.Server
/// </summary>
public class UrlPrefixCollection : ICollection<UrlPrefix>
{
private readonly WebListener _webListener;
private readonly IDictionary<int, UrlPrefix> _prefixes = new Dictionary<int, UrlPrefix>(1);
private UrlGroup _urlGroup;
private int _nextId = 1;
internal UrlPrefixCollection(WebListener webListener)
internal UrlPrefixCollection()
{
_webListener = webListener;
}
public int Count
@ -47,9 +45,9 @@ namespace Microsoft.Net.Http.Server
lock (_prefixes)
{
var id = _nextId++;
if (_webListener.IsListening)
if (_urlGroup != null)
{
_webListener.UrlGroup.RegisterPrefix(item.FullPrefix, id);
_urlGroup.RegisterPrefix(item.FullPrefix, id);
}
_prefixes.Add(id, item);
}
@ -67,7 +65,7 @@ namespace Microsoft.Net.Http.Server
{
lock (_prefixes)
{
if (_webListener.IsListening)
if (_urlGroup != null)
{
UnregisterAllPrefixes();
}
@ -106,9 +104,9 @@ namespace Microsoft.Net.Http.Server
if (pair.Value.Equals(item))
{
id = pair.Key;
if (_webListener.IsListening)
if (_urlGroup != null)
{
_webListener.UrlGroup.UnregisterPrefix(pair.Value.FullPrefix);
_urlGroup.UnregisterPrefix(pair.Value.FullPrefix);
}
}
}
@ -134,15 +132,16 @@ namespace Microsoft.Net.Http.Server
return GetEnumerator();
}
internal void RegisterAllPrefixes()
internal void RegisterAllPrefixes(UrlGroup urlGroup)
{
lock (_prefixes)
{
_urlGroup = urlGroup;
// go through the uri list and register for each one of them
foreach (var pair in _prefixes)
{
// We'll get this index back on each request and use it to look up the prefix to calculate PathBase.
_webListener.UrlGroup.RegisterPrefix(pair.Value.FullPrefix, pair.Key);
_urlGroup.RegisterPrefix(pair.Value.FullPrefix, pair.Key);
}
}
}
@ -155,7 +154,7 @@ namespace Microsoft.Net.Http.Server
foreach (var prefix in _prefixes.Values)
{
// ignore possible failures
_webListener.UrlGroup.UnregisterPrefix(prefix.FullPrefix);
_urlGroup.UnregisterPrefix(prefix.FullPrefix);
}
}
}

View File

@ -24,7 +24,6 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
@ -36,8 +35,6 @@ namespace Microsoft.Net.Http.Server
/// </summary>
public sealed class WebListener : IDisposable
{
private const long DefaultRequestQueueLength = 1000; // Http.sys default.
// Win8# 559317 fixed a bug in Http.sys's HttpReceiveClientCertificate method.
// Without this fix IOCP callbacks were not being called although ERROR_IO_PENDING was
// returned from HttpReceiveClientCertificate when using the
@ -53,49 +50,39 @@ namespace Microsoft.Net.Http.Server
// 0.5 seconds per request. Respond with a 400 Bad Request.
private const int UnknownHeaderLimit = 1000;
private ILogger _logger;
private volatile State _state; // m_State is set only within lock blocks, but often read outside locks.
private bool _ignoreWriteExceptions;
private ServerSession _serverSession;
private UrlGroup _urlGroup;
private RequestQueue _requestQueue;
private TimeoutManager _timeoutManager;
private AuthenticationManager _authManager;
private DisconnectListener _disconnectListener;
private object _internalLock;
private UrlPrefixCollection _urlPrefixes;
// The native request queue
private long? _requestQueueLength;
public WebListener()
: this(null)
: this(new WebListenerSettings())
{
}
public WebListener(ILoggerFactory factory)
public WebListener(WebListenerSettings settings)
{
if (settings == null)
{
throw new ArgumentNullException(nameof(settings));
}
if (!HttpApi.Supported)
{
throw new PlatformNotSupportedException();
}
_logger = LogHelper.CreateLogger(factory, typeof(WebListener));
Debug.Assert(HttpApi.ApiVersion == HttpApi.HTTP_API_VERSION.Version20, "Invalid Http api version");
Debug.Assert(HttpApi.ApiVersion ==
HttpApi.HTTP_API_VERSION.Version20, "Invalid Http api version");
Settings = settings;
_state = State.Stopped;
_internalLock = new object();
_urlPrefixes = new UrlPrefixCollection(this);
_timeoutManager = new TimeoutManager(this);
_authManager = new AuthenticationManager(this);
// V2 initialization sequence:
// 1. Create server session
// 2. Create url group
@ -107,11 +94,11 @@ namespace Microsoft.Net.Http.Server
{
_serverSession = new ServerSession();
_urlGroup = new UrlGroup(_serverSession, _logger);
_urlGroup = new UrlGroup(_serverSession, Logger);
_requestQueue = new RequestQueue(_urlGroup, _logger);
_requestQueue = new RequestQueue(_urlGroup, Logger);
_disconnectListener = new DisconnectListener(_requestQueue, _logger);
_disconnectListener = new DisconnectListener(_requestQueue, Logger);
}
catch (Exception exception)
{
@ -119,7 +106,7 @@ namespace Microsoft.Net.Http.Server
_requestQueue?.Dispose();
_urlGroup?.Dispose();
_serverSession?.Dispose();
LogHelper.LogException(_logger, ".Ctor", exception);
LogHelper.LogException(Logger, ".Ctor", exception);
throw;
}
}
@ -133,7 +120,7 @@ namespace Microsoft.Net.Http.Server
internal ILogger Logger
{
get { return _logger; }
get { return Settings.Logger; }
}
internal UrlGroup UrlGroup
@ -151,66 +138,13 @@ namespace Microsoft.Net.Http.Server
get { return _disconnectListener; }
}
// TODO: https://github.com/aspnet/WebListener/issues/173
internal bool IgnoreWriteExceptions
{
get { return _ignoreWriteExceptions; }
set
{
CheckDisposed();
_ignoreWriteExceptions = value;
}
}
public UrlPrefixCollection UrlPrefixes
{
get { return _urlPrefixes; }
}
/// <summary>
/// Exposes the Http.Sys timeout configurations. These may also be configured in the registry.
/// </summary>
public TimeoutManager TimeoutManager
{
get { return _timeoutManager; }
}
/// <summary>
/// Http.Sys authentication settings.
/// </summary>
public AuthenticationManager AuthenticationManager
{
get { return _authManager; }
}
public WebListenerSettings Settings { get; }
public bool IsListening
{
get { return _state == State.Started; }
}
/// <summary>
/// Sets the maximum number of requests that will be queued up in Http.Sys.
/// </summary>
/// <param name="limit"></param>
public void SetRequestQueueLimit(long limit)
{
CheckDisposed();
if (limit <= 0)
{
throw new ArgumentOutOfRangeException("limit", limit, string.Empty);
}
// Don't try to change it if the new limit is the same
if ((!_requestQueueLength.HasValue && limit == DefaultRequestQueueLength)
|| (_requestQueueLength.HasValue && limit == _requestQueueLength.Value))
{
return;
}
_requestQueueLength = limit;
_requestQueue.SetLengthLimit(_requestQueueLength.Value);
}
/// <summary>
/// Start accepting incoming requests.
/// </summary>
@ -218,7 +152,7 @@ namespace Microsoft.Net.Http.Server
{
CheckDisposed();
LogHelper.LogInfo(_logger, "Start");
LogHelper.LogInfo(Logger, "Start");
// Make sure there are no race conditions between Start/Stop/Abort/Close/Dispose.
// Start needs to setup all resources. Abort/Stop must not interfere while Start is
@ -233,12 +167,16 @@ namespace Microsoft.Net.Http.Server
return;
}
Settings.Authentication.SetUrlGroupSecurity(UrlGroup);
Settings.Timeouts.SetUrlGroupTimeouts(UrlGroup);
Settings.SetRequestQueueLimit(RequestQueue);
_requestQueue.AttachToUrlGroup();
// All resources are set up correctly. Now add all prefixes.
try
{
_urlPrefixes.RegisterAllPrefixes();
Settings.UrlPrefixes.RegisterAllPrefixes(UrlGroup);
}
catch (WebListenerException)
{
@ -254,7 +192,7 @@ namespace Microsoft.Net.Http.Server
// Make sure the HttpListener instance can't be used if Start() failed.
_state = State.Disposed;
DisposeInternal();
LogHelper.LogException(_logger, "Start", exception);
LogHelper.LogException(Logger, "Start", exception);
throw;
}
}
@ -272,7 +210,7 @@ namespace Microsoft.Net.Http.Server
return;
}
_urlPrefixes.UnregisterAllPrefixes();
Settings.UrlPrefixes.UnregisterAllPrefixes();
_state = State.Stopped;
@ -281,7 +219,7 @@ namespace Microsoft.Net.Http.Server
}
catch (Exception exception)
{
LogHelper.LogException(_logger, "Stop", exception);
LogHelper.LogException(Logger, "Stop", exception);
throw;
}
}
@ -309,14 +247,14 @@ namespace Microsoft.Net.Http.Server
{
return;
}
LogHelper.LogInfo(_logger, "Dispose");
LogHelper.LogInfo(Logger, "Dispose");
Stop();
DisposeInternal();
}
catch (Exception exception)
{
LogHelper.LogException(_logger, "Dispose", exception);
LogHelper.LogException(Logger, "Dispose", exception);
throw;
}
finally
@ -371,7 +309,7 @@ namespace Microsoft.Net.Http.Server
}
catch (Exception exception)
{
LogHelper.LogException(_logger, "GetContextAsync", exception);
LogHelper.LogException(Logger, "GetContextAsync", exception);
throw;
}
@ -392,10 +330,10 @@ namespace Microsoft.Net.Http.Server
internal unsafe bool ValidateAuth(NativeRequestContext requestMemory)
{
var requestV2 = (HttpApi.HTTP_REQUEST_V2*)requestMemory.RequestBlob;
if (!AuthenticationManager.AllowAnonymous && !AuthenticationManager.CheckAuthenticated(requestV2->pRequestInfo))
if (!Settings.Authentication.AllowAnonymous && !AuthenticationManager.CheckAuthenticated(requestV2->pRequestInfo))
{
SendError(requestMemory.RequestBlob->RequestId, HttpStatusCode.Unauthorized,
AuthenticationManager.GenerateChallenges(AuthenticationManager.AuthenticationSchemes));
AuthenticationManager.GenerateChallenges(Settings.Authentication.Schemes));
return false;
}
return true;
@ -509,7 +447,7 @@ namespace Microsoft.Net.Http.Server
}
}
internal void CheckDisposed()
private void CheckDisposed()
{
if (_state == State.Disposed)
{

View File

@ -0,0 +1,114 @@
// 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.Logging.Abstractions.Internal;
namespace Microsoft.Net.Http.Server
{
public class WebListenerSettings
{
private const long DefaultRequestQueueLength = 1000; // Http.sys default.
// The native request queue
private long _requestQueueLength = DefaultRequestQueueLength;
private RequestQueue _requestQueue;
private ILogger _logger = NullLogger.Instance;
public WebListenerSettings()
{
}
/// <summary>
/// The logger that will be used to create the WebListener instance. This should not be changed
/// after creating the listener.
/// </summary>
public ILogger Logger
{
get { return _logger; }
set
{
if (value == null)
{
throw new ArgumentNullException(nameof(value));
}
_logger = value;
}
}
/// <summary>
/// The url prefixes to register with Http.Sys. These may be modified at any time prior to disposing
/// the listener.
/// </summary>
public UrlPrefixCollection UrlPrefixes { get; } = new UrlPrefixCollection();
/// <summary>
/// Http.Sys authentication settings. These may be modified at any time prior to disposing
/// the listener.
/// </summary>
public AuthenticationManager Authentication { get; } = new AuthenticationManager();
/// <summary>
/// Exposes the Http.Sys timeout configurations. These may also be configured in the registry.
/// These may be modified at any time prior to disposing the listener.
/// </summary>
public TimeoutManager Timeouts { get; } = new TimeoutManager();
// TODO: https://github.com/aspnet/WebListener/issues/173
/// <summary>
/// Gets or Sets if response body writes that fail due to client disconnects should throw exceptions or
/// complete normally. The default is true.
/// </summary>
internal bool IgnoreWriteExceptions { get; set; } = true;
/// <summary>
/// Gets or sets the maximum number of requests that will be queued up in Http.Sys.
/// </summary>
public long RequestQueueLimit
{
get
{
return _requestQueueLength;
}
set
{
if (value <= 0)
{
throw new ArgumentOutOfRangeException(nameof(value), value, string.Empty);
}
if (_requestQueue != null)
{
_requestQueue.SetLengthLimit(_requestQueueLength);
}
// Only store it if it succeeds or hasn't started yet
_requestQueueLength = value;
}
}
internal void SetRequestQueueLimit(RequestQueue requestQueue)
{
_requestQueue = requestQueue;
if (_requestQueueLength != DefaultRequestQueueLength)
{
_requestQueue.SetLengthLimit(_requestQueueLength);
}
}
}
}

View File

@ -197,7 +197,7 @@ namespace Microsoft.AspNetCore.Server.WebListener
foreach (string path in new[] { "/", "/11", "/2/3", "/2", "/11/2" })
{
server.Listener.UrlPrefixes.Add(UrlPrefix.Create(rootUri.Scheme, rootUri.Host, rootUri.Port, path));
server.Listener.Settings.UrlPrefixes.Add(UrlPrefix.Create(rootUri.Scheme, rootUri.Host, rootUri.Port, path));
}
server.Start(new DummyApplication(app));

View File

@ -256,8 +256,8 @@ namespace Microsoft.AspNetCore.Server.WebListener
using (Utilities.CreateHttpServer(out address, httpContext => Task.FromResult(0))) { }
var server = new MessagePump(Options.Create(new WebListenerOptions()), new LoggerFactory());
server.Listener.UrlPrefixes.Add(UrlPrefix.Create(address));
server.Listener.SetRequestQueueLimit(1001);
server.Listener.Settings.UrlPrefixes.Add(UrlPrefix.Create(address));
server.Listener.Settings.RequestQueueLimit = 1001;
using (server)
{

View File

@ -67,8 +67,8 @@ namespace Microsoft.AspNetCore.Server.WebListener
var server = new MessagePump(Options.Create(new WebListenerOptions()), new LoggerFactory());
server.Features.Get<IServerAddressesFeature>().Addresses.Add(baseAddress);
server.Listener.AuthenticationManager.AuthenticationSchemes = authType;
server.Listener.AuthenticationManager.AllowAnonymous = allowAnonymous;
server.Listener.Settings.Authentication.Schemes = authType;
server.Listener.Settings.Authentication.AllowAnonymous = allowAnonymous;
try
{
server.Start(new DummyApplication(app));

View File

@ -136,7 +136,7 @@ namespace Microsoft.Net.Http.Server
var uriBuilder = new UriBuilder(root);
foreach (string path in new[] { "/", "/11", "/2/3", "/2", "/11/2" })
{
server.UrlPrefixes.Add(UrlPrefix.Create(uriBuilder.Scheme, uriBuilder.Host, uriBuilder.Port, path));
server.Settings.UrlPrefixes.Add(UrlPrefix.Create(uriBuilder.Scheme, uriBuilder.Host, uriBuilder.Port, path));
}
server.Start();

View File

@ -171,7 +171,7 @@ namespace Microsoft.Net.Http.Server
string address;
using (var server = Utilities.CreateHttpServer(out address))
{
server.SetRequestQueueLimit(1001);
server.Settings.RequestQueueLimit = 1001;
var responseTask = SendRequestAsync(address);
var context = await server.AcceptAsync();
@ -199,7 +199,7 @@ namespace Microsoft.Net.Http.Server
Assert.Equal(string.Empty, response);
address += "pathbase/";
server.UrlPrefixes.Add(address);
server.Settings.UrlPrefixes.Add(address);
responseTask = SendRequestAsync(address);
@ -220,7 +220,7 @@ namespace Microsoft.Net.Http.Server
using (var server = Utilities.CreateHttpServer(out address))
{
address += "pathbase/";
server.UrlPrefixes.Add(address);
server.Settings.UrlPrefixes.Add(address);
var responseTask = SendRequestAsync(address);
var context = await server.AcceptAsync();
@ -231,7 +231,7 @@ namespace Microsoft.Net.Http.Server
var response = await responseTask;
Assert.Equal(string.Empty, response);
Assert.True(server.UrlPrefixes.Remove(address));
Assert.True(server.Settings.UrlPrefixes.Remove(address));
responseTask = SendRequestAsync(address);

View File

@ -17,8 +17,8 @@ namespace Microsoft.Net.Http.Server
internal static WebListener CreateHttpAuthServer(AuthenticationSchemes authScheme, bool allowAnonymos, out string baseAddress)
{
var listener = CreateHttpServer(out baseAddress);
listener.AuthenticationManager.AuthenticationSchemes = authScheme;
listener.AuthenticationManager.AllowAnonymous = allowAnonymos;
listener.Settings.Authentication.Schemes = authScheme;
listener.Settings.Authentication.AllowAnonymous = allowAnonymos;
return listener;
}
@ -45,7 +45,7 @@ namespace Microsoft.Net.Http.Server
root = prefix.Scheme + "://" + prefix.Host + ":" + prefix.Port;
baseAddress = prefix.ToString();
var listener = new WebListener();
listener.UrlPrefixes.Add(prefix);
listener.Settings.UrlPrefixes.Add(prefix);
try
{
listener.Start();
@ -61,7 +61,6 @@ namespace Microsoft.Net.Http.Server
throw new Exception("Failed to locate a free port.");
}
internal static WebListener CreateHttpsServer()
{
return CreateServer("https", "localhost", 9090, string.Empty);
@ -70,7 +69,7 @@ namespace Microsoft.Net.Http.Server
internal static WebListener CreateServer(string scheme, string host, int port, string path)
{
WebListener listener = new WebListener();
listener.UrlPrefixes.Add(UrlPrefix.Create(scheme, host, port, path));
listener.Settings.UrlPrefixes.Add(UrlPrefix.Create(scheme, host, port, path));
listener.Start();
return listener;
}