#2139 Add ListenLocalhost and ListenAnyIP
This commit is contained in:
parent
186e9806cd
commit
89d1862f21
|
|
@ -76,6 +76,13 @@ namespace SampleApp
|
|||
listenOptions.UseConnectionLogging();
|
||||
});
|
||||
|
||||
options.ListenLocalhost(basePort + 2, listenOptions =>
|
||||
{
|
||||
listenOptions.UseHttps("testCert.pfx", "testPassword");
|
||||
});
|
||||
|
||||
options.ListenAnyIP(basePort + 3);
|
||||
|
||||
options.UseSystemd();
|
||||
|
||||
// The following section should be used to demo sockets
|
||||
|
|
|
|||
|
|
@ -0,0 +1,37 @@
|
|||
// Copyright (c) .NET Foundation. 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.IO;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Microsoft.AspNetCore.Server.Kestrel.Core
|
||||
{
|
||||
internal class AnyIPListenOptions : ListenOptions
|
||||
{
|
||||
internal AnyIPListenOptions(int port)
|
||||
: base(new IPEndPoint(IPAddress.IPv6Any, port))
|
||||
{
|
||||
}
|
||||
|
||||
internal override async Task BindAsync(AddressBindContext context)
|
||||
{
|
||||
// when address is 'http://hostname:port', 'http://*:port', or 'http://+:port'
|
||||
try
|
||||
{
|
||||
await base.BindAsync(context).ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex) when (!(ex is IOException))
|
||||
{
|
||||
context.Logger.LogDebug(CoreStrings.FormatFallbackToIPv4Any(IPEndPoint.Port));
|
||||
|
||||
// for machines that do not support IPv6
|
||||
IPEndPoint = new IPEndPoint(IPAddress.Any, IPEndPoint.Port);
|
||||
await base.BindAsync(context).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
// Copyright (c) .NET Foundation. 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.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
|
||||
{
|
||||
internal class AddressBindContext
|
||||
{
|
||||
public ICollection<string> Addresses { get; set; }
|
||||
public List<ListenOptions> ListenOptions { get; set; }
|
||||
public KestrelServerOptions ServerOptions { get; set; }
|
||||
public ILogger Logger { get; set; }
|
||||
public IDefaultHttpsProvider DefaultHttpsProvider { get; set; }
|
||||
|
||||
public Func<ListenOptions, Task> CreateBinding { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -47,17 +47,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
|
|||
await strategy.BindAsync(context).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private class AddressBindContext
|
||||
{
|
||||
public ICollection<string> Addresses { get; set; }
|
||||
public List<ListenOptions> ListenOptions { get; set; }
|
||||
public KestrelServerOptions ServerOptions { get; set; }
|
||||
public ILogger Logger { get; set; }
|
||||
public IDefaultHttpsProvider DefaultHttpsProvider { get; set; }
|
||||
|
||||
public Func<ListenOptions, Task> CreateBinding { get; set; }
|
||||
}
|
||||
|
||||
private static IStrategy CreateStrategy(ListenOptions[] listenOptions, string[] addresses, bool preferAddresses)
|
||||
{
|
||||
var hasListenOptions = listenOptions.Length > 0;
|
||||
|
|
@ -109,10 +98,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
|
|||
return true;
|
||||
}
|
||||
|
||||
private static Task BindEndpointAsync(IPEndPoint endpoint, AddressBindContext context)
|
||||
=> BindEndpointAsync(new ListenOptions(endpoint), context);
|
||||
|
||||
private static async Task BindEndpointAsync(ListenOptions endpoint, AddressBindContext context)
|
||||
internal static async Task BindEndpointAsync(ListenOptions endpoint, AddressBindContext context)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
|
@ -126,60 +112,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
|
|||
context.ListenOptions.Add(endpoint);
|
||||
}
|
||||
|
||||
private static async Task BindLocalhostAsync(ServerAddress address, AddressBindContext context, bool https)
|
||||
{
|
||||
if (address.Port == 0)
|
||||
{
|
||||
throw new InvalidOperationException(CoreStrings.DynamicPortOnLocalhostNotSupported);
|
||||
}
|
||||
|
||||
var exceptions = new List<Exception>();
|
||||
|
||||
try
|
||||
{
|
||||
var options = new ListenOptions(new IPEndPoint(IPAddress.Loopback, address.Port));
|
||||
await BindEndpointAsync(options, context).ConfigureAwait(false);
|
||||
|
||||
if (https)
|
||||
{
|
||||
options.KestrelServerOptions = context.ServerOptions;
|
||||
context.DefaultHttpsProvider.ConfigureHttps(options);
|
||||
}
|
||||
}
|
||||
catch (Exception ex) when (!(ex is IOException))
|
||||
{
|
||||
context.Logger.LogWarning(0, CoreStrings.NetworkInterfaceBindingFailed, address, "IPv4 loopback", ex.Message);
|
||||
exceptions.Add(ex);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var options = new ListenOptions(new IPEndPoint(IPAddress.IPv6Loopback, address.Port));
|
||||
await BindEndpointAsync(options, context).ConfigureAwait(false);
|
||||
|
||||
if (https)
|
||||
{
|
||||
options.KestrelServerOptions = context.ServerOptions;
|
||||
context.DefaultHttpsProvider.ConfigureHttps(options);
|
||||
}
|
||||
}
|
||||
catch (Exception ex) when (!(ex is IOException))
|
||||
{
|
||||
context.Logger.LogWarning(0, CoreStrings.NetworkInterfaceBindingFailed, address, "IPv6 loopback", ex.Message);
|
||||
exceptions.Add(ex);
|
||||
}
|
||||
|
||||
if (exceptions.Count == 2)
|
||||
{
|
||||
throw new IOException(CoreStrings.FormatAddressBindingFailed(address), new AggregateException(exceptions));
|
||||
}
|
||||
|
||||
// If StartLocalhost doesn't throw, there is at least one listener.
|
||||
// The port cannot change for "localhost".
|
||||
context.Addresses.Add(address.ToString());
|
||||
}
|
||||
|
||||
private static async Task BindAddressAsync(string address, AddressBindContext context)
|
||||
internal static ListenOptions ParseAddress(string address, KestrelServerOptions serverOptions, IDefaultHttpsProvider defaultHttpsProvider)
|
||||
{
|
||||
var parsedAddress = ServerAddress.FromUrl(address);
|
||||
var https = false;
|
||||
|
|
@ -202,47 +135,29 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
|
|||
if (parsedAddress.IsUnixPipe)
|
||||
{
|
||||
options = new ListenOptions(parsedAddress.UnixPipePath);
|
||||
await BindEndpointAsync(options, context).ConfigureAwait(false);
|
||||
context.Addresses.Add(options.GetDisplayName());
|
||||
}
|
||||
else if (string.Equals(parsedAddress.Host, "localhost", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
// "localhost" for both IPv4 and IPv6 can't be represented as an IPEndPoint.
|
||||
await BindLocalhostAsync(parsedAddress, context, https).ConfigureAwait(false);
|
||||
options = new LocalhostListenOptions(parsedAddress.Port);
|
||||
}
|
||||
else if (TryCreateIPEndPoint(parsedAddress, out var endpoint))
|
||||
{
|
||||
options = new ListenOptions(endpoint);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (TryCreateIPEndPoint(parsedAddress, out var endpoint))
|
||||
{
|
||||
options = new ListenOptions(endpoint);
|
||||
await BindEndpointAsync(options, context).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
// when address is 'http://hostname:port', 'http://*:port', or 'http://+:port'
|
||||
try
|
||||
{
|
||||
options = new ListenOptions(new IPEndPoint(IPAddress.IPv6Any, parsedAddress.Port));
|
||||
await BindEndpointAsync(options, context).ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex) when (!(ex is IOException))
|
||||
{
|
||||
context.Logger.LogDebug(CoreStrings.FormatFallbackToIPv4Any(parsedAddress.Port));
|
||||
|
||||
// for machines that do not support IPv6
|
||||
options = new ListenOptions(new IPEndPoint(IPAddress.Any, parsedAddress.Port));
|
||||
await BindEndpointAsync(options, context).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
context.Addresses.Add(options.GetDisplayName());
|
||||
// when address is 'http://hostname:port', 'http://*:port', or 'http://+:port'
|
||||
options = new AnyIPListenOptions(parsedAddress.Port);
|
||||
}
|
||||
|
||||
if (https && options != null)
|
||||
if (https)
|
||||
{
|
||||
options.KestrelServerOptions = context.ServerOptions;
|
||||
context.DefaultHttpsProvider.ConfigureHttps(options);
|
||||
options.KestrelServerOptions = serverOptions;
|
||||
defaultHttpsProvider.ConfigureHttps(options);
|
||||
}
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
private interface IStrategy
|
||||
|
|
@ -256,7 +171,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
|
|||
{
|
||||
context.Logger.LogDebug(CoreStrings.BindingToDefaultAddress, Constants.DefaultServerAddress);
|
||||
|
||||
await BindLocalhostAsync(ServerAddress.FromUrl(Constants.DefaultServerAddress), context, https: false).ConfigureAwait(false);
|
||||
await ParseAddress(Constants.DefaultServerAddress, context.ServerOptions, context.DefaultHttpsProvider)
|
||||
.BindAsync(context).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -308,9 +224,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
|
|||
{
|
||||
foreach (var endpoint in _endpoints)
|
||||
{
|
||||
await BindEndpointAsync(endpoint, context).ConfigureAwait(false);
|
||||
|
||||
context.Addresses.Add(endpoint.GetDisplayName());
|
||||
await endpoint.BindAsync(context).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -328,7 +242,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
|
|||
{
|
||||
foreach (var address in _addresses)
|
||||
{
|
||||
await BindAddressAsync(address, context).ConfigureAwait(false);
|
||||
await ParseAddress(address, context.ServerOptions, context.DefaultHttpsProvider)
|
||||
.BindAsync(context).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -105,6 +105,40 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core
|
|||
ListenOptions.Add(listenOptions);
|
||||
}
|
||||
|
||||
public void ListenLocalhost(int port) => ListenLocalhost(port, options => { });
|
||||
|
||||
public void ListenLocalhost(int port, Action<ListenOptions> configure)
|
||||
{
|
||||
if (configure == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(configure));
|
||||
}
|
||||
|
||||
var listenOptions = new LocalhostListenOptions(port)
|
||||
{
|
||||
KestrelServerOptions = this,
|
||||
};
|
||||
configure(listenOptions);
|
||||
ListenOptions.Add(listenOptions);
|
||||
}
|
||||
|
||||
public void ListenAnyIP(int port) => ListenAnyIP(port, options => { });
|
||||
|
||||
public void ListenAnyIP(int port, Action<ListenOptions> configure)
|
||||
{
|
||||
if (configure == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(configure));
|
||||
}
|
||||
|
||||
var listenOptions = new AnyIPListenOptions(port)
|
||||
{
|
||||
KestrelServerOptions = this,
|
||||
};
|
||||
configure(listenOptions);
|
||||
ListenOptions.Add(listenOptions);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Bind to given Unix domain socket path.
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ using System.Net;
|
|||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Protocols;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Core.Adapter.Internal;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal;
|
||||
|
||||
namespace Microsoft.AspNetCore.Server.Kestrel.Core
|
||||
|
|
@ -140,7 +141,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core
|
|||
/// <summary>
|
||||
/// Gets the name of this endpoint to display on command-line when the web server starts.
|
||||
/// </summary>
|
||||
internal string GetDisplayName()
|
||||
internal virtual string GetDisplayName()
|
||||
{
|
||||
var scheme = ConnectionAdapters.Any(f => f.IsHttps)
|
||||
? "https"
|
||||
|
|
@ -182,5 +183,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core
|
|||
|
||||
return app;
|
||||
}
|
||||
|
||||
internal virtual async Task BindAsync(AddressBindContext context)
|
||||
{
|
||||
await AddressBinder.BindEndpointAsync(this, context).ConfigureAwait(false);
|
||||
context.Addresses.Add(GetDisplayName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,88 @@
|
|||
// Copyright (c) .NET Foundation. 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.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Microsoft.AspNetCore.Server.Kestrel.Core
|
||||
{
|
||||
internal class LocalhostListenOptions : ListenOptions
|
||||
{
|
||||
internal LocalhostListenOptions(int port)
|
||||
: base(new IPEndPoint(IPAddress.Loopback, port))
|
||||
{
|
||||
if (port == 0)
|
||||
{
|
||||
throw new InvalidOperationException(CoreStrings.DynamicPortOnLocalhostNotSupported);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of this endpoint to display on command-line when the web server starts.
|
||||
/// </summary>
|
||||
internal override string GetDisplayName()
|
||||
{
|
||||
var scheme = ConnectionAdapters.Any(f => f.IsHttps)
|
||||
? "https"
|
||||
: "http";
|
||||
|
||||
return $"{scheme}://localhost:{IPEndPoint.Port}";
|
||||
}
|
||||
|
||||
internal override async Task BindAsync(AddressBindContext context)
|
||||
{
|
||||
var exceptions = new List<Exception>();
|
||||
|
||||
try
|
||||
{
|
||||
var v4Options = Clone(IPAddress.Loopback);
|
||||
await AddressBinder.BindEndpointAsync(v4Options, context).ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex) when (!(ex is IOException))
|
||||
{
|
||||
context.Logger.LogWarning(0, CoreStrings.NetworkInterfaceBindingFailed, GetDisplayName(), "IPv4 loopback", ex.Message);
|
||||
exceptions.Add(ex);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var v6Options = Clone(IPAddress.IPv6Loopback);
|
||||
await AddressBinder.BindEndpointAsync(v6Options, context).ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex) when (!(ex is IOException))
|
||||
{
|
||||
context.Logger.LogWarning(0, CoreStrings.NetworkInterfaceBindingFailed, GetDisplayName(), "IPv6 loopback", ex.Message);
|
||||
exceptions.Add(ex);
|
||||
}
|
||||
|
||||
if (exceptions.Count == 2)
|
||||
{
|
||||
throw new IOException(CoreStrings.FormatAddressBindingFailed(GetDisplayName()), new AggregateException(exceptions));
|
||||
}
|
||||
|
||||
// If StartLocalhost doesn't throw, there is at least one listener.
|
||||
// The port cannot change for "localhost".
|
||||
context.Addresses.Add(GetDisplayName());
|
||||
}
|
||||
|
||||
// used for cloning to two IPEndpoints
|
||||
private ListenOptions Clone(IPAddress address)
|
||||
{
|
||||
var options = new ListenOptions(new IPEndPoint(address, IPEndPoint.Port))
|
||||
{
|
||||
HandleType = HandleType,
|
||||
KestrelServerOptions = KestrelServerOptions,
|
||||
NoDelay = NoDelay,
|
||||
Protocols = Protocols,
|
||||
};
|
||||
options.ConnectionAdapters.AddRange(ConnectionAdapters);
|
||||
return options;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,12 +2,12 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Protocols;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal;
|
||||
using Microsoft.AspNetCore.Testing;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using Moq;
|
||||
|
|
@ -51,24 +51,49 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
|
|||
[InlineData("randomhost")]
|
||||
[InlineData("+")]
|
||||
[InlineData("contoso.com")]
|
||||
public async Task DefaultsToIPv6AnyOnInvalidIPAddress(string host)
|
||||
public void ParseAddressDefaultsToAnyIPOnInvalidIPAddress(string host)
|
||||
{
|
||||
var addresses = new ServerAddressesFeature();
|
||||
addresses.Addresses.Add($"http://{host}");
|
||||
var options = new KestrelServerOptions();
|
||||
var listenOptions = AddressBinder.ParseAddress($"http://{host}", options, Mock.Of<IDefaultHttpsProvider>());
|
||||
Assert.IsType<AnyIPListenOptions>(listenOptions);
|
||||
Assert.Equal(ListenType.IPEndPoint, listenOptions.Type);
|
||||
Assert.Equal(IPAddress.IPv6Any, listenOptions.IPEndPoint.Address);
|
||||
Assert.Equal(80, listenOptions.IPEndPoint.Port);
|
||||
}
|
||||
|
||||
var tcs = new TaskCompletionSource<ListenOptions>();
|
||||
await AddressBinder.BindAsync(addresses,
|
||||
options,
|
||||
NullLogger.Instance,
|
||||
Mock.Of<IDefaultHttpsProvider>(),
|
||||
endpoint =>
|
||||
{
|
||||
tcs.TrySetResult(endpoint);
|
||||
return Task.CompletedTask;
|
||||
});
|
||||
var result = await tcs.Task;
|
||||
Assert.Equal(IPAddress.IPv6Any, result.IPEndPoint.Address);
|
||||
[Fact]
|
||||
public void ParseAddressLocalhost()
|
||||
{
|
||||
var options = new KestrelServerOptions();
|
||||
var listenOptions = AddressBinder.ParseAddress("http://localhost", options, Mock.Of<IDefaultHttpsProvider>());
|
||||
Assert.IsType<LocalhostListenOptions>(listenOptions);
|
||||
Assert.Equal(ListenType.IPEndPoint, listenOptions.Type);
|
||||
Assert.Equal(IPAddress.Loopback, listenOptions.IPEndPoint.Address);
|
||||
Assert.Equal(80, listenOptions.IPEndPoint.Port);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ParseAddressUnixPipe()
|
||||
{
|
||||
var options = new KestrelServerOptions();
|
||||
var listenOptions = AddressBinder.ParseAddress("http://unix:/tmp/kestrel-test.sock", options, Mock.Of<IDefaultHttpsProvider>());
|
||||
Assert.Equal(ListenType.SocketPath, listenOptions.Type);
|
||||
Assert.Equal("/tmp/kestrel-test.sock", listenOptions.SocketPath);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("http://10.10.10.10:5000/", "10.10.10.10", 5000)]
|
||||
[InlineData("http://[::1]:5000", "::1", 5000)]
|
||||
[InlineData("http://[::1]", "::1", 80)]
|
||||
[InlineData("http://127.0.0.1", "127.0.0.1", 80)]
|
||||
[InlineData("https://127.0.0.1", "127.0.0.1", 443)]
|
||||
public void ParseAddressIP(string address, string ip, int port)
|
||||
{
|
||||
var options = new KestrelServerOptions();
|
||||
var listenOptions = AddressBinder.ParseAddress(address, options, Mock.Of<IDefaultHttpsProvider>());
|
||||
Assert.Equal(ListenType.IPEndPoint, listenOptions.Type);
|
||||
Assert.Equal(IPAddress.Parse(ip), listenOptions.IPEndPoint.Address);
|
||||
Assert.Equal(port, listenOptions.IPEndPoint.Port);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
|
|||
|
|
@ -187,7 +187,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
|
|||
private Task RegisterAddresses_Success(string addressInput, string testUrl, int testPort = 0)
|
||||
=> RegisterAddresses_Success(addressInput, new[] { testUrl }, testPort);
|
||||
|
||||
private async Task RegisterAddresses_StaticPort_Success(string addressInput, string[] testUrls)
|
||||
private Task RegisterAddresses_StaticPort_Success(string addressInput, string[] testUrls) =>
|
||||
RunTestWithStaticPort(port => RegisterAddresses_Success($"{addressInput}:{port}", testUrls, port));
|
||||
|
||||
private async Task RunTestWithStaticPort(Func<int, Task> test)
|
||||
{
|
||||
var retryCount = 0;
|
||||
var errors = new List<Exception>();
|
||||
|
|
@ -197,7 +200,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
|
|||
try
|
||||
{
|
||||
var port = GetNextPort();
|
||||
await RegisterAddresses_Success($"{addressInput}:{port}", testUrls, port);
|
||||
await test(port);
|
||||
return;
|
||||
}
|
||||
catch (XunitException)
|
||||
|
|
@ -254,34 +257,93 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
|
|||
}
|
||||
}
|
||||
|
||||
private async Task RegisterIPEndPoint_StaticPort_Success(IPAddress address, string testUrl)
|
||||
private Task RegisterIPEndPoint_StaticPort_Success(IPAddress address, string testUrl)
|
||||
=> RunTestWithStaticPort(port => RegisterIPEndPoint_Success(new IPEndPoint(address, port), testUrl, port));
|
||||
|
||||
[ConditionalFact]
|
||||
public async Task ListenAnyIP_IPv4_Success()
|
||||
{
|
||||
var retryCount = 0;
|
||||
var errors = new List<Exception>();
|
||||
await ListenAnyIP_Success(new[] { "http://localhost", "http://127.0.0.1" });
|
||||
}
|
||||
|
||||
while (retryCount < MaxRetries)
|
||||
[ConditionalFact]
|
||||
[IPv6SupportedCondition]
|
||||
public async Task ListenAnyIP_IPv6_Success()
|
||||
{
|
||||
await ListenAnyIP_Success(new[] { "http://[::1]", "http://localhost", "http://127.0.0.1" });
|
||||
}
|
||||
|
||||
[ConditionalFact]
|
||||
[NetworkIsReachable]
|
||||
public async Task ListenAnyIP_HostName_Success()
|
||||
{
|
||||
var hostName = Dns.GetHostName();
|
||||
await ListenAnyIP_Success(new[] { $"http://{hostName}" });
|
||||
}
|
||||
|
||||
private async Task ListenAnyIP_Success(string[] testUrls, int testPort = 0)
|
||||
{
|
||||
var hostBuilder = TransportSelector.GetWebHostBuilder()
|
||||
.UseKestrel(options =>
|
||||
{
|
||||
options.ListenAnyIP(testPort);
|
||||
})
|
||||
.ConfigureLogging(_configureLoggingDelegate)
|
||||
.Configure(ConfigureEchoAddress);
|
||||
|
||||
using (var host = hostBuilder.Build())
|
||||
{
|
||||
try
|
||||
{
|
||||
var port = GetNextPort();
|
||||
await RegisterIPEndPoint_Success(new IPEndPoint(address, port), testUrl, port);
|
||||
return;
|
||||
}
|
||||
catch (XunitException)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
errors.Add(ex);
|
||||
}
|
||||
host.Start();
|
||||
|
||||
retryCount++;
|
||||
foreach (var testUrl in testUrls.Select(testUrl => $"{testUrl}:{(testPort == 0 ? host.GetPort() : testPort)}"))
|
||||
{
|
||||
var response = await HttpClientSlim.GetStringAsync(testUrl, validateCertificate: false);
|
||||
|
||||
// Compare the response with Uri.ToString(), rather than testUrl directly.
|
||||
// Required to handle IPv6 addresses with zone index, like "fe80::3%1"
|
||||
Assert.Equal(new Uri(testUrl).ToString(), response);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (errors.Any())
|
||||
[ConditionalFact]
|
||||
public async Task ListenLocalhost_IPv4LocalhostStaticPort_Success()
|
||||
{
|
||||
await ListenLocalhost_StaticPort_Success(new[] { "http://localhost", "http://127.0.0.1" });
|
||||
}
|
||||
|
||||
[ConditionalFact]
|
||||
[IPv6SupportedCondition]
|
||||
public async Task ListenLocalhost_IPv6LocalhostStaticPort_Success()
|
||||
{
|
||||
await ListenLocalhost_StaticPort_Success(new[] { "http://localhost", "http://127.0.0.1", "http://[::1]" });
|
||||
}
|
||||
|
||||
private Task ListenLocalhost_StaticPort_Success(string[] testUrls) =>
|
||||
RunTestWithStaticPort(port => ListenLocalhost_Success(testUrls, port));
|
||||
|
||||
private async Task ListenLocalhost_Success(string[] testUrls, int testPort = 0)
|
||||
{
|
||||
var hostBuilder = TransportSelector.GetWebHostBuilder()
|
||||
.UseKestrel(options =>
|
||||
{
|
||||
options.ListenLocalhost(testPort);
|
||||
})
|
||||
.ConfigureLogging(_configureLoggingDelegate)
|
||||
.Configure(ConfigureEchoAddress);
|
||||
|
||||
using (var host = hostBuilder.Build())
|
||||
{
|
||||
throw new AggregateException(errors);
|
||||
host.Start();
|
||||
|
||||
foreach (var testUrl in testUrls.Select(testUrl => $"{testUrl}:{(testPort == 0 ? host.GetPort() : testPort)}"))
|
||||
{
|
||||
var response = await HttpClientSlim.GetStringAsync(testUrl, validateCertificate: false);
|
||||
|
||||
// Compare the response with Uri.ToString(), rather than testUrl directly.
|
||||
// Required to handle IPv6 addresses with zone index, like "fe80::3%1"
|
||||
Assert.Equal(new Uri(testUrl).ToString(), response);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Tests
|
|||
_bufferPool = new MemoryPool();
|
||||
_mockLibuv = new MockLibuv();
|
||||
|
||||
var libuvTransport = new LibuvTransport(_mockLibuv, new TestLibuvTransportContext(), new ListenOptions(0));
|
||||
var libuvTransport = new LibuvTransport(_mockLibuv, new TestLibuvTransportContext(), new ListenOptions((ulong)0));
|
||||
_libuvThread = new LibuvThread(libuvTransport, maxLoops: 1);
|
||||
_libuvThread.StartAsync().Wait();
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue