#160 Refactor options/settings
This commit is contained in:
parent
cd886802fe
commit
e39ea62808
|
|
@ -16,9 +16,11 @@ namespace HelloWorld
|
||||||
|
|
||||||
public static async Task Run(string[] args)
|
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();
|
listener.Start();
|
||||||
|
|
||||||
Console.WriteLine("Running...");
|
Console.WriteLine("Running...");
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ namespace HotAddSample
|
||||||
{
|
{
|
||||||
loggerfactory.AddConsole(LogLevel.Information);
|
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/");
|
addresses.Add("http://localhost:12346/pathBase/");
|
||||||
|
|
||||||
app.Use(async (context, next) =>
|
app.Use(async (context, next) =>
|
||||||
|
|
|
||||||
|
|
@ -19,8 +19,8 @@ namespace SelfHostServer
|
||||||
// Server options can be configured here instead of in Main.
|
// Server options can be configured here instead of in Main.
|
||||||
services.Configure<WebListenerOptions>(options =>
|
services.Configure<WebListenerOptions>(options =>
|
||||||
{
|
{
|
||||||
options.Listener.AuthenticationManager.AuthenticationSchemes = AuthenticationSchemes.None;
|
options.ListenerSettings.Authentication.Schemes = AuthenticationSchemes.None;
|
||||||
options.Listener.AuthenticationManager.AllowAnonymous = true;
|
options.ListenerSettings.Authentication.AllowAnonymous = true;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -52,8 +52,8 @@ namespace SelfHostServer
|
||||||
.UseStartup<Startup>()
|
.UseStartup<Startup>()
|
||||||
.UseWebListener(options =>
|
.UseWebListener(options =>
|
||||||
{
|
{
|
||||||
options.Listener.AuthenticationManager.AuthenticationSchemes = AuthenticationSchemes.None;
|
options.ListenerSettings.Authentication.Schemes = AuthenticationSchemes.None;
|
||||||
options.Listener.AuthenticationManager.AllowAnonymous = true;
|
options.ListenerSettings.Authentication.AllowAnonymous = true;
|
||||||
})
|
})
|
||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// Copyright (c) Microsoft Open Technologies, Inc.
|
// Copyright (c) Microsoft Open Technologies, Inc.
|
||||||
// All Rights Reserved
|
// All Rights Reserved
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// 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)
|
public void Configure(WebListenerOptions options)
|
||||||
{
|
{
|
||||||
options.Listener = new Microsoft.Net.Http.Server.WebListener(_loggerFactory);
|
options.ListenerSettings.Logger = _loggerFactory.CreateLogger<Microsoft.Net.Http.Server.WebListener>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -57,15 +57,16 @@ namespace Microsoft.AspNetCore.Server.WebListener
|
||||||
throw new ArgumentNullException(nameof(loggerFactory));
|
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));
|
_logger = LogHelper.CreateLogger(loggerFactory, typeof(MessagePump));
|
||||||
Features = new FeatureCollection();
|
Features = new FeatureCollection();
|
||||||
_serverAddresses = new ServerAddressesFeature();
|
_serverAddresses = new ServerAddressesFeature();
|
||||||
Features.Set<IServerAddressesFeature>(_serverAddresses);
|
Features.Set<IServerAddressesFeature>(_serverAddresses);
|
||||||
|
|
||||||
_processRequest = new Action<object>(ProcessRequestAsync);
|
_processRequest = new Action<object>(ProcessRequestAsync);
|
||||||
_maxAccepts = options.Value?.MaxAccepts ?? WebListenerOptions.DefaultMaxAccepts;
|
_maxAccepts = optionsInstance.MaxAccepts;
|
||||||
EnableResponseCaching = options.Value?.EnableResponseCaching ?? true;
|
EnableResponseCaching = optionsInstance.EnableResponseCaching;
|
||||||
_shutdownSignal = new ManualResetEvent(false);
|
_shutdownSignal = new ManualResetEvent(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -94,7 +95,7 @@ namespace Microsoft.AspNetCore.Server.WebListener
|
||||||
|
|
||||||
_application = new ApplicationWrapper<TContext>(application);
|
_application = new ApplicationWrapper<TContext>(application);
|
||||||
|
|
||||||
if (_listener.UrlPrefixes.Count == 0)
|
if (_listener.Settings.UrlPrefixes.Count == 0)
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException("No address prefixes were defined.");
|
throw new InvalidOperationException("No address prefixes were defined.");
|
||||||
}
|
}
|
||||||
|
|
@ -224,7 +225,7 @@ namespace Microsoft.AspNetCore.Server.WebListener
|
||||||
{
|
{
|
||||||
foreach (var value in addresses)
|
foreach (var value in addresses)
|
||||||
{
|
{
|
||||||
listener.UrlPrefixes.Add(UrlPrefix.Create(value));
|
listener.Settings.UrlPrefixes.Add(UrlPrefix.Create(value));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@
|
||||||
// permissions and limitations under the License.
|
// permissions and limitations under the License.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using Microsoft.Net.Http.Server;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Server.WebListener
|
namespace Microsoft.AspNetCore.Server.WebListener
|
||||||
{
|
{
|
||||||
|
|
@ -23,10 +24,21 @@ namespace Microsoft.AspNetCore.Server.WebListener
|
||||||
{
|
{
|
||||||
internal static readonly int DefaultMaxAccepts = 5 * Environment.ProcessorCount;
|
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;
|
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;
|
public bool EnableResponseCaching { get; set; } = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Security.Claims;
|
using System.Security.Claims;
|
||||||
|
|
@ -42,24 +43,21 @@ namespace Microsoft.Net.Http.Server
|
||||||
private static readonly int AuthInfoSize =
|
private static readonly int AuthInfoSize =
|
||||||
Marshal.SizeOf<HttpApi.HTTP_SERVER_AUTHENTICATION_INFO>();
|
Marshal.SizeOf<HttpApi.HTTP_SERVER_AUTHENTICATION_INFO>();
|
||||||
|
|
||||||
private WebListener _server;
|
private UrlGroup _urlGroup;
|
||||||
private AuthenticationSchemes _authSchemes;
|
private AuthenticationSchemes _authSchemes;
|
||||||
private bool _allowAnonymous = true;
|
private bool _allowAnonymous = true;
|
||||||
|
|
||||||
internal AuthenticationManager(WebListener listener)
|
internal AuthenticationManager()
|
||||||
{
|
{
|
||||||
_server = listener;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#region Properties
|
public AuthenticationSchemes Schemes
|
||||||
|
|
||||||
public AuthenticationSchemes AuthenticationSchemes
|
|
||||||
{
|
{
|
||||||
get { return _authSchemes; }
|
get { return _authSchemes; }
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
_authSchemes = value;
|
_authSchemes = value;
|
||||||
SetServerSecurity();
|
SetUrlGroupSecurity();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -69,10 +67,21 @@ namespace Microsoft.Net.Http.Server
|
||||||
set { _allowAnonymous = value; }
|
set { _allowAnonymous = value; }
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion Properties
|
internal void SetUrlGroupSecurity(UrlGroup urlGroup)
|
||||||
|
|
||||||
private unsafe void SetServerSecurity()
|
|
||||||
{
|
{
|
||||||
|
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 =
|
HttpApi.HTTP_SERVER_AUTHENTICATION_INFO authInfo =
|
||||||
new HttpApi.HTTP_SERVER_AUTHENTICATION_INFO();
|
new HttpApi.HTTP_SERVER_AUTHENTICATION_INFO();
|
||||||
|
|
||||||
|
|
@ -91,7 +100,7 @@ namespace Microsoft.Net.Http.Server
|
||||||
|
|
||||||
IntPtr infoptr = new IntPtr(&authInfo);
|
IntPtr infoptr = new IntPtr(&authInfo);
|
||||||
|
|
||||||
_server.UrlGroup.SetProperty(
|
_urlGroup.SetProperty(
|
||||||
HttpApi.HTTP_SERVER_PROPERTY.HttpServerAuthenticationProperty,
|
HttpApi.HTTP_SERVER_PROPERTY.HttpServerAuthenticationProperty,
|
||||||
infoptr, (uint)AuthInfoSize);
|
infoptr, (uint)AuthInfoSize);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -29,16 +29,6 @@ namespace Microsoft.Net.Http.Server
|
||||||
{
|
{
|
||||||
internal static class LogHelper
|
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)
|
internal static void LogInfo(ILogger logger, string data)
|
||||||
{
|
{
|
||||||
if (logger == null)
|
if (logger == null)
|
||||||
|
|
@ -86,29 +76,5 @@ namespace Microsoft.Net.Http.Server
|
||||||
logger.LogError(location + "; " + message);
|
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()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,11 +16,8 @@
|
||||||
// permissions and limitations under the License.
|
// permissions and limitations under the License.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace Microsoft.Net.Http.Server
|
namespace Microsoft.Net.Http.Server
|
||||||
|
|
@ -32,6 +29,7 @@ namespace Microsoft.Net.Http.Server
|
||||||
|
|
||||||
private readonly UrlGroup _urlGroup;
|
private readonly UrlGroup _urlGroup;
|
||||||
private readonly ILogger _logger;
|
private readonly ILogger _logger;
|
||||||
|
private bool _disposed;
|
||||||
|
|
||||||
internal RequestQueue(UrlGroup urlGroup, ILogger logger)
|
internal RequestQueue(UrlGroup urlGroup, ILogger logger)
|
||||||
{
|
{
|
||||||
|
|
@ -66,6 +64,7 @@ namespace Microsoft.Net.Http.Server
|
||||||
|
|
||||||
internal unsafe void AttachToUrlGroup()
|
internal unsafe void AttachToUrlGroup()
|
||||||
{
|
{
|
||||||
|
CheckDisposed();
|
||||||
// Set the association between request queue and url group. After this, requests for registered urls will
|
// Set the association between request queue and url group. After this, requests for registered urls will
|
||||||
// get delivered to this request queue.
|
// get delivered to this request queue.
|
||||||
|
|
||||||
|
|
@ -81,6 +80,7 @@ namespace Microsoft.Net.Http.Server
|
||||||
|
|
||||||
internal unsafe void DetachFromUrlGroup()
|
internal unsafe void DetachFromUrlGroup()
|
||||||
{
|
{
|
||||||
|
CheckDisposed();
|
||||||
// Break the association between request queue and url group. After this, requests for registered urls
|
// Break the association between request queue and url group. After this, requests for registered urls
|
||||||
// will get 503s.
|
// will get 503s.
|
||||||
// Note that this method may be called multiple times (Stop() and then Abort()). This
|
// 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.
|
// The listener must be active for this to work.
|
||||||
internal unsafe void SetLengthLimit(long length)
|
internal unsafe void SetLengthLimit(long length)
|
||||||
{
|
{
|
||||||
|
CheckDisposed();
|
||||||
|
|
||||||
var result = HttpApi.HttpSetRequestQueueProperty(Handle,
|
var result = HttpApi.HttpSetRequestQueueProperty(Handle,
|
||||||
HttpApi.HTTP_SERVER_PROPERTY.HttpServerQueueLengthProperty,
|
HttpApi.HTTP_SERVER_PROPERTY.HttpServerQueueLengthProperty,
|
||||||
new IntPtr((void*)&length), (uint)Marshal.SizeOf<long>(), 0, IntPtr.Zero);
|
new IntPtr((void*)&length), (uint)Marshal.SizeOf<long>(), 0, IntPtr.Zero);
|
||||||
|
|
@ -112,8 +114,22 @@ namespace Microsoft.Net.Http.Server
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
|
if (_disposed)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_disposed = true;
|
||||||
BoundHandle.Dispose();
|
BoundHandle.Dispose();
|
||||||
Handle.Dispose();
|
Handle.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void CheckDisposed()
|
||||||
|
{
|
||||||
|
if (_disposed)
|
||||||
|
{
|
||||||
|
throw new ObjectDisposedException(this.GetType().FullName);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -50,7 +50,8 @@ namespace Microsoft.Net.Http.Server
|
||||||
internal void SetProperty(HttpApi.HTTP_SERVER_PROPERTY property, IntPtr info, uint infosize, bool throwOnError = true)
|
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");
|
Debug.Assert(info != IntPtr.Zero, "SetUrlGroupProperty called with invalid pointer");
|
||||||
|
CheckDisposed();
|
||||||
|
|
||||||
var statusCode = HttpApi.HttpSetUrlGroupProperty(Id, property, info, infosize);
|
var statusCode = HttpApi.HttpSetUrlGroupProperty(Id, property, info, infosize);
|
||||||
|
|
||||||
if (statusCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS)
|
if (statusCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS)
|
||||||
|
|
@ -67,6 +68,7 @@ namespace Microsoft.Net.Http.Server
|
||||||
internal void RegisterPrefix(string uriPrefix, int contextId)
|
internal void RegisterPrefix(string uriPrefix, int contextId)
|
||||||
{
|
{
|
||||||
LogHelper.LogInfo(_logger, "Listening on prefix: " + uriPrefix);
|
LogHelper.LogInfo(_logger, "Listening on prefix: " + uriPrefix);
|
||||||
|
CheckDisposed();
|
||||||
|
|
||||||
var statusCode = HttpApi.HttpAddUrlToUrlGroup(Id, uriPrefix, (ulong)contextId, 0);
|
var statusCode = HttpApi.HttpAddUrlToUrlGroup(Id, uriPrefix, (ulong)contextId, 0);
|
||||||
|
|
||||||
|
|
@ -86,6 +88,7 @@ namespace Microsoft.Net.Http.Server
|
||||||
internal bool UnregisterPrefix(string uriPrefix)
|
internal bool UnregisterPrefix(string uriPrefix)
|
||||||
{
|
{
|
||||||
LogHelper.LogInfo(_logger, "Stop listening on prefix: " + uriPrefix);
|
LogHelper.LogInfo(_logger, "Stop listening on prefix: " + uriPrefix);
|
||||||
|
CheckDisposed();
|
||||||
|
|
||||||
var statusCode = HttpApi.HttpRemoveUrlFromUrlGroup(Id, uriPrefix, 0);
|
var statusCode = HttpApi.HttpRemoveUrlFromUrlGroup(Id, uriPrefix, 0);
|
||||||
|
|
||||||
|
|
@ -115,5 +118,13 @@ namespace Microsoft.Net.Http.Server
|
||||||
}
|
}
|
||||||
Id = 0;
|
Id = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void CheckDisposed()
|
||||||
|
{
|
||||||
|
if (_disposed)
|
||||||
|
{
|
||||||
|
throw new ObjectDisposedException(this.GetType().FullName);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -85,7 +85,7 @@ namespace Microsoft.Net.Http.Server
|
||||||
QueryString = Marshal.PtrToStringUni((IntPtr)cookedUrl.pQueryString, cookedUrl.QueryStringLength / 2);
|
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);
|
var originalPath = RequestUriBuilder.GetRequestPath(RawUrl, cookedUrlPath, RequestContext.Logger);
|
||||||
|
|
||||||
// These paths are both unescaped already.
|
// These paths are both unescaped already.
|
||||||
|
|
|
||||||
|
|
@ -64,7 +64,7 @@ namespace Microsoft.Net.Http.Server
|
||||||
_expectedBodyLength = 0;
|
_expectedBodyLength = 0;
|
||||||
_nativeStream = null;
|
_nativeStream = null;
|
||||||
_cacheTtl = null;
|
_cacheTtl = null;
|
||||||
_authChallenges = RequestContext.Server.AuthenticationManager.AuthenticationSchemes;
|
_authChallenges = RequestContext.Server.Settings.Authentication.Schemes;
|
||||||
}
|
}
|
||||||
|
|
||||||
private enum ResponseState
|
private enum ResponseState
|
||||||
|
|
@ -412,7 +412,7 @@ namespace Microsoft.Net.Http.Server
|
||||||
// 401
|
// 401
|
||||||
if (StatusCode == (ushort)HttpStatusCode.Unauthorized)
|
if (StatusCode == (ushort)HttpStatusCode.Unauthorized)
|
||||||
{
|
{
|
||||||
RequestContext.Server.AuthenticationManager.SetAuthenticationChallenge(RequestContext);
|
RequestContext.Server.Settings.Authentication.SetAuthenticationChallenge(RequestContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
var flags = HttpApi.HTTP_FLAGS.NONE;
|
var flags = HttpApi.HTTP_FLAGS.NONE;
|
||||||
|
|
|
||||||
|
|
@ -171,7 +171,7 @@ namespace Microsoft.Net.Http.Server
|
||||||
IntPtr.Zero);
|
IntPtr.Zero);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_requestContext.Server.IgnoreWriteExceptions)
|
if (_requestContext.Server.Settings.IgnoreWriteExceptions)
|
||||||
{
|
{
|
||||||
statusCode = UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS;
|
statusCode = UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
@ -338,7 +338,7 @@ namespace Microsoft.Net.Http.Server
|
||||||
if (statusCode != ErrorCodes.ERROR_SUCCESS && statusCode != ErrorCodes.ERROR_IO_PENDING)
|
if (statusCode != ErrorCodes.ERROR_SUCCESS && statusCode != ErrorCodes.ERROR_IO_PENDING)
|
||||||
{
|
{
|
||||||
asyncResult.Dispose();
|
asyncResult.Dispose();
|
||||||
if (_requestContext.Server.IgnoreWriteExceptions && started)
|
if (_requestContext.Server.Settings.IgnoreWriteExceptions && started)
|
||||||
{
|
{
|
||||||
asyncResult.Complete();
|
asyncResult.Complete();
|
||||||
}
|
}
|
||||||
|
|
@ -602,7 +602,7 @@ namespace Microsoft.Net.Http.Server
|
||||||
if (statusCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS && statusCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_IO_PENDING)
|
if (statusCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS && statusCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_IO_PENDING)
|
||||||
{
|
{
|
||||||
asyncResult.Dispose();
|
asyncResult.Dispose();
|
||||||
if (_requestContext.Server.IgnoreWriteExceptions && started)
|
if (_requestContext.Server.Settings.IgnoreWriteExceptions && started)
|
||||||
{
|
{
|
||||||
asyncResult.Complete();
|
asyncResult.Complete();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -37,14 +37,12 @@ namespace Microsoft.Net.Http.Server
|
||||||
private static readonly int TimeoutLimitSize =
|
private static readonly int TimeoutLimitSize =
|
||||||
Marshal.SizeOf<HttpApi.HTTP_TIMEOUT_LIMIT_INFO>();
|
Marshal.SizeOf<HttpApi.HTTP_TIMEOUT_LIMIT_INFO>();
|
||||||
|
|
||||||
private WebListener _server;
|
private UrlGroup _urlGroup;
|
||||||
private int[] _timeouts;
|
private int[] _timeouts;
|
||||||
private uint _minSendBytesPerSecond;
|
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
|
// 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
|
// 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
|
// 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");
|
throw new ArgumentOutOfRangeException("value");
|
||||||
}
|
}
|
||||||
|
|
||||||
SetServerTimeouts(_timeouts, (uint)value);
|
SetUrlGroupTimeouts(_timeouts, (uint)value);
|
||||||
_minSendBytesPerSecond = (uint)value;
|
_minSendBytesPerSecond = (uint)value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -211,12 +209,24 @@ namespace Microsoft.Net.Http.Server
|
||||||
// call succeeds, update local state.
|
// call succeeds, update local state.
|
||||||
var newTimeouts = (int[])_timeouts.Clone();
|
var newTimeouts = (int[])_timeouts.Clone();
|
||||||
newTimeouts[(int)type] = (int)timeoutValue;
|
newTimeouts[(int)type] = (int)timeoutValue;
|
||||||
SetServerTimeouts(newTimeouts, _minSendBytesPerSecond);
|
SetUrlGroupTimeouts(newTimeouts, _minSendBytesPerSecond);
|
||||||
_timeouts[(int)type] = (int)timeoutValue;
|
_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();
|
var timeoutinfo = new HttpApi.HTTP_TIMEOUT_LIMIT_INFO();
|
||||||
|
|
||||||
timeoutinfo.Flags = HttpApi.HTTP_FLAGS.HTTP_PROPERTY_FLAG_PRESENT;
|
timeoutinfo.Flags = HttpApi.HTTP_FLAGS.HTTP_PROPERTY_FLAG_PRESENT;
|
||||||
|
|
@ -234,7 +244,7 @@ namespace Microsoft.Net.Http.Server
|
||||||
|
|
||||||
var infoptr = new IntPtr(&timeoutinfo);
|
var infoptr = new IntPtr(&timeoutinfo);
|
||||||
|
|
||||||
_server.UrlGroup.SetProperty(
|
_urlGroup.SetProperty(
|
||||||
HttpApi.HTTP_SERVER_PROPERTY.HttpServerTimeoutsProperty,
|
HttpApi.HTTP_SERVER_PROPERTY.HttpServerTimeoutsProperty,
|
||||||
infoptr, (uint)TimeoutLimitSize);
|
infoptr, (uint)TimeoutLimitSize);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
// 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.
|
// 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;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
|
@ -12,13 +11,12 @@ namespace Microsoft.Net.Http.Server
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class UrlPrefixCollection : ICollection<UrlPrefix>
|
public class UrlPrefixCollection : ICollection<UrlPrefix>
|
||||||
{
|
{
|
||||||
private readonly WebListener _webListener;
|
|
||||||
private readonly IDictionary<int, UrlPrefix> _prefixes = new Dictionary<int, UrlPrefix>(1);
|
private readonly IDictionary<int, UrlPrefix> _prefixes = new Dictionary<int, UrlPrefix>(1);
|
||||||
|
private UrlGroup _urlGroup;
|
||||||
private int _nextId = 1;
|
private int _nextId = 1;
|
||||||
|
|
||||||
internal UrlPrefixCollection(WebListener webListener)
|
internal UrlPrefixCollection()
|
||||||
{
|
{
|
||||||
_webListener = webListener;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public int Count
|
public int Count
|
||||||
|
|
@ -47,9 +45,9 @@ namespace Microsoft.Net.Http.Server
|
||||||
lock (_prefixes)
|
lock (_prefixes)
|
||||||
{
|
{
|
||||||
var id = _nextId++;
|
var id = _nextId++;
|
||||||
if (_webListener.IsListening)
|
if (_urlGroup != null)
|
||||||
{
|
{
|
||||||
_webListener.UrlGroup.RegisterPrefix(item.FullPrefix, id);
|
_urlGroup.RegisterPrefix(item.FullPrefix, id);
|
||||||
}
|
}
|
||||||
_prefixes.Add(id, item);
|
_prefixes.Add(id, item);
|
||||||
}
|
}
|
||||||
|
|
@ -67,7 +65,7 @@ namespace Microsoft.Net.Http.Server
|
||||||
{
|
{
|
||||||
lock (_prefixes)
|
lock (_prefixes)
|
||||||
{
|
{
|
||||||
if (_webListener.IsListening)
|
if (_urlGroup != null)
|
||||||
{
|
{
|
||||||
UnregisterAllPrefixes();
|
UnregisterAllPrefixes();
|
||||||
}
|
}
|
||||||
|
|
@ -106,9 +104,9 @@ namespace Microsoft.Net.Http.Server
|
||||||
if (pair.Value.Equals(item))
|
if (pair.Value.Equals(item))
|
||||||
{
|
{
|
||||||
id = pair.Key;
|
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();
|
return GetEnumerator();
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void RegisterAllPrefixes()
|
internal void RegisterAllPrefixes(UrlGroup urlGroup)
|
||||||
{
|
{
|
||||||
lock (_prefixes)
|
lock (_prefixes)
|
||||||
{
|
{
|
||||||
|
_urlGroup = urlGroup;
|
||||||
// go through the uri list and register for each one of them
|
// go through the uri list and register for each one of them
|
||||||
foreach (var pair in _prefixes)
|
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.
|
// 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)
|
foreach (var prefix in _prefixes.Values)
|
||||||
{
|
{
|
||||||
// ignore possible failures
|
// ignore possible failures
|
||||||
_webListener.UrlGroup.UnregisterPrefix(prefix.FullPrefix);
|
_urlGroup.UnregisterPrefix(prefix.FullPrefix);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,6 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
@ -36,8 +35,6 @@ namespace Microsoft.Net.Http.Server
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed class WebListener : IDisposable
|
public sealed class WebListener : IDisposable
|
||||||
{
|
{
|
||||||
private const long DefaultRequestQueueLength = 1000; // Http.sys default.
|
|
||||||
|
|
||||||
// Win8# 559317 fixed a bug in Http.sys's HttpReceiveClientCertificate method.
|
// 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
|
// Without this fix IOCP callbacks were not being called although ERROR_IO_PENDING was
|
||||||
// returned from HttpReceiveClientCertificate when using the
|
// 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.
|
// 0.5 seconds per request. Respond with a 400 Bad Request.
|
||||||
private const int UnknownHeaderLimit = 1000;
|
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 volatile State _state; // m_State is set only within lock blocks, but often read outside locks.
|
||||||
|
|
||||||
private bool _ignoreWriteExceptions;
|
|
||||||
private ServerSession _serverSession;
|
private ServerSession _serverSession;
|
||||||
private UrlGroup _urlGroup;
|
private UrlGroup _urlGroup;
|
||||||
private RequestQueue _requestQueue;
|
private RequestQueue _requestQueue;
|
||||||
private TimeoutManager _timeoutManager;
|
|
||||||
private AuthenticationManager _authManager;
|
|
||||||
private DisconnectListener _disconnectListener;
|
private DisconnectListener _disconnectListener;
|
||||||
|
|
||||||
private object _internalLock;
|
private object _internalLock;
|
||||||
|
|
||||||
private UrlPrefixCollection _urlPrefixes;
|
|
||||||
|
|
||||||
// The native request queue
|
|
||||||
private long? _requestQueueLength;
|
|
||||||
|
|
||||||
public WebListener()
|
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)
|
if (!HttpApi.Supported)
|
||||||
{
|
{
|
||||||
throw new PlatformNotSupportedException();
|
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 ==
|
Settings = settings;
|
||||||
HttpApi.HTTP_API_VERSION.Version20, "Invalid Http api version");
|
|
||||||
|
|
||||||
_state = State.Stopped;
|
_state = State.Stopped;
|
||||||
_internalLock = new object();
|
_internalLock = new object();
|
||||||
|
|
||||||
_urlPrefixes = new UrlPrefixCollection(this);
|
|
||||||
_timeoutManager = new TimeoutManager(this);
|
|
||||||
_authManager = new AuthenticationManager(this);
|
|
||||||
|
|
||||||
// V2 initialization sequence:
|
// V2 initialization sequence:
|
||||||
// 1. Create server session
|
// 1. Create server session
|
||||||
// 2. Create url group
|
// 2. Create url group
|
||||||
|
|
@ -107,11 +94,11 @@ namespace Microsoft.Net.Http.Server
|
||||||
{
|
{
|
||||||
_serverSession = new ServerSession();
|
_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)
|
catch (Exception exception)
|
||||||
{
|
{
|
||||||
|
|
@ -119,7 +106,7 @@ namespace Microsoft.Net.Http.Server
|
||||||
_requestQueue?.Dispose();
|
_requestQueue?.Dispose();
|
||||||
_urlGroup?.Dispose();
|
_urlGroup?.Dispose();
|
||||||
_serverSession?.Dispose();
|
_serverSession?.Dispose();
|
||||||
LogHelper.LogException(_logger, ".Ctor", exception);
|
LogHelper.LogException(Logger, ".Ctor", exception);
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -133,7 +120,7 @@ namespace Microsoft.Net.Http.Server
|
||||||
|
|
||||||
internal ILogger Logger
|
internal ILogger Logger
|
||||||
{
|
{
|
||||||
get { return _logger; }
|
get { return Settings.Logger; }
|
||||||
}
|
}
|
||||||
|
|
||||||
internal UrlGroup UrlGroup
|
internal UrlGroup UrlGroup
|
||||||
|
|
@ -151,66 +138,13 @@ namespace Microsoft.Net.Http.Server
|
||||||
get { return _disconnectListener; }
|
get { return _disconnectListener; }
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: https://github.com/aspnet/WebListener/issues/173
|
public WebListenerSettings Settings { get; }
|
||||||
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 bool IsListening
|
public bool IsListening
|
||||||
{
|
{
|
||||||
get { return _state == State.Started; }
|
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>
|
/// <summary>
|
||||||
/// Start accepting incoming requests.
|
/// Start accepting incoming requests.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -218,7 +152,7 @@ namespace Microsoft.Net.Http.Server
|
||||||
{
|
{
|
||||||
CheckDisposed();
|
CheckDisposed();
|
||||||
|
|
||||||
LogHelper.LogInfo(_logger, "Start");
|
LogHelper.LogInfo(Logger, "Start");
|
||||||
|
|
||||||
// Make sure there are no race conditions between Start/Stop/Abort/Close/Dispose.
|
// 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
|
// Start needs to setup all resources. Abort/Stop must not interfere while Start is
|
||||||
|
|
@ -233,12 +167,16 @@ namespace Microsoft.Net.Http.Server
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Settings.Authentication.SetUrlGroupSecurity(UrlGroup);
|
||||||
|
Settings.Timeouts.SetUrlGroupTimeouts(UrlGroup);
|
||||||
|
Settings.SetRequestQueueLimit(RequestQueue);
|
||||||
|
|
||||||
_requestQueue.AttachToUrlGroup();
|
_requestQueue.AttachToUrlGroup();
|
||||||
|
|
||||||
// All resources are set up correctly. Now add all prefixes.
|
// All resources are set up correctly. Now add all prefixes.
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_urlPrefixes.RegisterAllPrefixes();
|
Settings.UrlPrefixes.RegisterAllPrefixes(UrlGroup);
|
||||||
}
|
}
|
||||||
catch (WebListenerException)
|
catch (WebListenerException)
|
||||||
{
|
{
|
||||||
|
|
@ -254,7 +192,7 @@ namespace Microsoft.Net.Http.Server
|
||||||
// Make sure the HttpListener instance can't be used if Start() failed.
|
// Make sure the HttpListener instance can't be used if Start() failed.
|
||||||
_state = State.Disposed;
|
_state = State.Disposed;
|
||||||
DisposeInternal();
|
DisposeInternal();
|
||||||
LogHelper.LogException(_logger, "Start", exception);
|
LogHelper.LogException(Logger, "Start", exception);
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -272,7 +210,7 @@ namespace Microsoft.Net.Http.Server
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_urlPrefixes.UnregisterAllPrefixes();
|
Settings.UrlPrefixes.UnregisterAllPrefixes();
|
||||||
|
|
||||||
_state = State.Stopped;
|
_state = State.Stopped;
|
||||||
|
|
||||||
|
|
@ -281,7 +219,7 @@ namespace Microsoft.Net.Http.Server
|
||||||
}
|
}
|
||||||
catch (Exception exception)
|
catch (Exception exception)
|
||||||
{
|
{
|
||||||
LogHelper.LogException(_logger, "Stop", exception);
|
LogHelper.LogException(Logger, "Stop", exception);
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -309,14 +247,14 @@ namespace Microsoft.Net.Http.Server
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
LogHelper.LogInfo(_logger, "Dispose");
|
LogHelper.LogInfo(Logger, "Dispose");
|
||||||
|
|
||||||
Stop();
|
Stop();
|
||||||
DisposeInternal();
|
DisposeInternal();
|
||||||
}
|
}
|
||||||
catch (Exception exception)
|
catch (Exception exception)
|
||||||
{
|
{
|
||||||
LogHelper.LogException(_logger, "Dispose", exception);
|
LogHelper.LogException(Logger, "Dispose", exception);
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
|
|
@ -371,7 +309,7 @@ namespace Microsoft.Net.Http.Server
|
||||||
}
|
}
|
||||||
catch (Exception exception)
|
catch (Exception exception)
|
||||||
{
|
{
|
||||||
LogHelper.LogException(_logger, "GetContextAsync", exception);
|
LogHelper.LogException(Logger, "GetContextAsync", exception);
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -392,10 +330,10 @@ namespace Microsoft.Net.Http.Server
|
||||||
internal unsafe bool ValidateAuth(NativeRequestContext requestMemory)
|
internal unsafe bool ValidateAuth(NativeRequestContext requestMemory)
|
||||||
{
|
{
|
||||||
var requestV2 = (HttpApi.HTTP_REQUEST_V2*)requestMemory.RequestBlob;
|
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,
|
SendError(requestMemory.RequestBlob->RequestId, HttpStatusCode.Unauthorized,
|
||||||
AuthenticationManager.GenerateChallenges(AuthenticationManager.AuthenticationSchemes));
|
AuthenticationManager.GenerateChallenges(Settings.Authentication.Schemes));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -509,7 +447,7 @@ namespace Microsoft.Net.Http.Server
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void CheckDisposed()
|
private void CheckDisposed()
|
||||||
{
|
{
|
||||||
if (_state == State.Disposed)
|
if (_state == State.Disposed)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -197,7 +197,7 @@ namespace Microsoft.AspNetCore.Server.WebListener
|
||||||
|
|
||||||
foreach (string path in new[] { "/", "/11", "/2/3", "/2", "/11/2" })
|
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));
|
server.Start(new DummyApplication(app));
|
||||||
|
|
|
||||||
|
|
@ -256,8 +256,8 @@ namespace Microsoft.AspNetCore.Server.WebListener
|
||||||
using (Utilities.CreateHttpServer(out address, httpContext => Task.FromResult(0))) { }
|
using (Utilities.CreateHttpServer(out address, httpContext => Task.FromResult(0))) { }
|
||||||
|
|
||||||
var server = new MessagePump(Options.Create(new WebListenerOptions()), new LoggerFactory());
|
var server = new MessagePump(Options.Create(new WebListenerOptions()), new LoggerFactory());
|
||||||
server.Listener.UrlPrefixes.Add(UrlPrefix.Create(address));
|
server.Listener.Settings.UrlPrefixes.Add(UrlPrefix.Create(address));
|
||||||
server.Listener.SetRequestQueueLimit(1001);
|
server.Listener.Settings.RequestQueueLimit = 1001;
|
||||||
|
|
||||||
using (server)
|
using (server)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -67,8 +67,8 @@ namespace Microsoft.AspNetCore.Server.WebListener
|
||||||
|
|
||||||
var server = new MessagePump(Options.Create(new WebListenerOptions()), new LoggerFactory());
|
var server = new MessagePump(Options.Create(new WebListenerOptions()), new LoggerFactory());
|
||||||
server.Features.Get<IServerAddressesFeature>().Addresses.Add(baseAddress);
|
server.Features.Get<IServerAddressesFeature>().Addresses.Add(baseAddress);
|
||||||
server.Listener.AuthenticationManager.AuthenticationSchemes = authType;
|
server.Listener.Settings.Authentication.Schemes = authType;
|
||||||
server.Listener.AuthenticationManager.AllowAnonymous = allowAnonymous;
|
server.Listener.Settings.Authentication.AllowAnonymous = allowAnonymous;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
server.Start(new DummyApplication(app));
|
server.Start(new DummyApplication(app));
|
||||||
|
|
|
||||||
|
|
@ -136,7 +136,7 @@ namespace Microsoft.Net.Http.Server
|
||||||
var uriBuilder = new UriBuilder(root);
|
var uriBuilder = new UriBuilder(root);
|
||||||
foreach (string path in new[] { "/", "/11", "/2/3", "/2", "/11/2" })
|
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();
|
server.Start();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -171,7 +171,7 @@ namespace Microsoft.Net.Http.Server
|
||||||
string address;
|
string address;
|
||||||
using (var server = Utilities.CreateHttpServer(out address))
|
using (var server = Utilities.CreateHttpServer(out address))
|
||||||
{
|
{
|
||||||
server.SetRequestQueueLimit(1001);
|
server.Settings.RequestQueueLimit = 1001;
|
||||||
var responseTask = SendRequestAsync(address);
|
var responseTask = SendRequestAsync(address);
|
||||||
|
|
||||||
var context = await server.AcceptAsync();
|
var context = await server.AcceptAsync();
|
||||||
|
|
@ -199,7 +199,7 @@ namespace Microsoft.Net.Http.Server
|
||||||
Assert.Equal(string.Empty, response);
|
Assert.Equal(string.Empty, response);
|
||||||
|
|
||||||
address += "pathbase/";
|
address += "pathbase/";
|
||||||
server.UrlPrefixes.Add(address);
|
server.Settings.UrlPrefixes.Add(address);
|
||||||
|
|
||||||
responseTask = SendRequestAsync(address);
|
responseTask = SendRequestAsync(address);
|
||||||
|
|
||||||
|
|
@ -220,7 +220,7 @@ namespace Microsoft.Net.Http.Server
|
||||||
using (var server = Utilities.CreateHttpServer(out address))
|
using (var server = Utilities.CreateHttpServer(out address))
|
||||||
{
|
{
|
||||||
address += "pathbase/";
|
address += "pathbase/";
|
||||||
server.UrlPrefixes.Add(address);
|
server.Settings.UrlPrefixes.Add(address);
|
||||||
var responseTask = SendRequestAsync(address);
|
var responseTask = SendRequestAsync(address);
|
||||||
|
|
||||||
var context = await server.AcceptAsync();
|
var context = await server.AcceptAsync();
|
||||||
|
|
@ -231,7 +231,7 @@ namespace Microsoft.Net.Http.Server
|
||||||
var response = await responseTask;
|
var response = await responseTask;
|
||||||
Assert.Equal(string.Empty, response);
|
Assert.Equal(string.Empty, response);
|
||||||
|
|
||||||
Assert.True(server.UrlPrefixes.Remove(address));
|
Assert.True(server.Settings.UrlPrefixes.Remove(address));
|
||||||
|
|
||||||
responseTask = SendRequestAsync(address);
|
responseTask = SendRequestAsync(address);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,8 +17,8 @@ namespace Microsoft.Net.Http.Server
|
||||||
internal static WebListener CreateHttpAuthServer(AuthenticationSchemes authScheme, bool allowAnonymos, out string baseAddress)
|
internal static WebListener CreateHttpAuthServer(AuthenticationSchemes authScheme, bool allowAnonymos, out string baseAddress)
|
||||||
{
|
{
|
||||||
var listener = CreateHttpServer(out baseAddress);
|
var listener = CreateHttpServer(out baseAddress);
|
||||||
listener.AuthenticationManager.AuthenticationSchemes = authScheme;
|
listener.Settings.Authentication.Schemes = authScheme;
|
||||||
listener.AuthenticationManager.AllowAnonymous = allowAnonymos;
|
listener.Settings.Authentication.AllowAnonymous = allowAnonymos;
|
||||||
return listener;
|
return listener;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -45,7 +45,7 @@ namespace Microsoft.Net.Http.Server
|
||||||
root = prefix.Scheme + "://" + prefix.Host + ":" + prefix.Port;
|
root = prefix.Scheme + "://" + prefix.Host + ":" + prefix.Port;
|
||||||
baseAddress = prefix.ToString();
|
baseAddress = prefix.ToString();
|
||||||
var listener = new WebListener();
|
var listener = new WebListener();
|
||||||
listener.UrlPrefixes.Add(prefix);
|
listener.Settings.UrlPrefixes.Add(prefix);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
listener.Start();
|
listener.Start();
|
||||||
|
|
@ -61,7 +61,6 @@ namespace Microsoft.Net.Http.Server
|
||||||
throw new Exception("Failed to locate a free port.");
|
throw new Exception("Failed to locate a free port.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
internal static WebListener CreateHttpsServer()
|
internal static WebListener CreateHttpsServer()
|
||||||
{
|
{
|
||||||
return CreateServer("https", "localhost", 9090, string.Empty);
|
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)
|
internal static WebListener CreateServer(string scheme, string host, int port, string path)
|
||||||
{
|
{
|
||||||
WebListener listener = new WebListener();
|
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();
|
listener.Start();
|
||||||
return listener;
|
return listener;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue