#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)
|
||||
{
|
||||
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...");
|
||||
|
|
|
|||
|
|
@ -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) =>
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
||||
|
|
|
|||
|
|
@ -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>();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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" })
|
||||
{
|
||||
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));
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue