| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688 |
- commit 89d1862f211e15d8aae367a6d86de26b173bf6b0
- Author: Chris Ross (ASP.NET) <[email protected]>
- Date: Tue Nov 14 15:54:30 2017 -0800
- #2139 Add ListenLocalhost and ListenAnyIP
- diff --git a/samples/SampleApp/Startup.cs b/samples/SampleApp/Startup.cs
- index 562f3a020dc..bf8ce0e1a38 100644
- --- a/samples/SampleApp/Startup.cs
- +++ b/samples/SampleApp/Startup.cs
- @@ -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
- diff --git a/src/Kestrel.Core/AnyIPListenOptions.cs b/src/Kestrel.Core/AnyIPListenOptions.cs
- new file mode 100644
- index 00000000000..2639337dd72
- --- /dev/null
- +++ b/src/Kestrel.Core/AnyIPListenOptions.cs
- @@ -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);
- + }
- + }
- + }
- +}
- diff --git a/src/Kestrel.Core/Internal/AddressBindContext.cs b/src/Kestrel.Core/Internal/AddressBindContext.cs
- new file mode 100644
- index 00000000000..e2f46e60257
- --- /dev/null
- +++ b/src/Kestrel.Core/Internal/AddressBindContext.cs
- @@ -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; }
- + }
- +}
- diff --git a/src/Kestrel.Core/Internal/AddressBinder.cs b/src/Kestrel.Core/Internal/AddressBinder.cs
- index 808675c0fb8..5334308ad83 100644
- --- a/src/Kestrel.Core/Internal/AddressBinder.cs
- +++ b/src/Kestrel.Core/Internal/AddressBinder.cs
- @@ -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);
- }
- }
- }
- diff --git a/src/Kestrel.Core/KestrelServerOptions.cs b/src/Kestrel.Core/KestrelServerOptions.cs
- index 136b983533b..3d70664cbe7 100644
- --- a/src/Kestrel.Core/KestrelServerOptions.cs
- +++ b/src/Kestrel.Core/KestrelServerOptions.cs
- @@ -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>
- diff --git a/src/Kestrel.Core/ListenOptions.cs b/src/Kestrel.Core/ListenOptions.cs
- index 19e94c43a6e..08e728a35cf 100644
- --- a/src/Kestrel.Core/ListenOptions.cs
- +++ b/src/Kestrel.Core/ListenOptions.cs
- @@ -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());
- + }
- }
- }
- diff --git a/src/Kestrel.Core/LocalhostListenOptions.cs b/src/Kestrel.Core/LocalhostListenOptions.cs
- new file mode 100644
- index 00000000000..39c49abbfc4
- --- /dev/null
- +++ b/src/Kestrel.Core/LocalhostListenOptions.cs
- @@ -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;
- + }
- + }
- +}
- diff --git a/test/Kestrel.Core.Tests/AddressBinderTests.cs b/test/Kestrel.Core.Tests/AddressBinderTests.cs
- index d42a598e9c4..f2580353db4 100644
- --- a/test/Kestrel.Core.Tests/AddressBinderTests.cs
- +++ b/test/Kestrel.Core.Tests/AddressBinderTests.cs
- @@ -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]
- diff --git a/test/Kestrel.FunctionalTests/AddressRegistrationTests.cs b/test/Kestrel.FunctionalTests/AddressRegistrationTests.cs
- index 446a201f71d..58d7369b500 100644
- --- a/test/Kestrel.FunctionalTests/AddressRegistrationTests.cs
- +++ b/test/Kestrel.FunctionalTests/AddressRegistrationTests.cs
- @@ -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)
- - {
- - try
- - {
- - var port = GetNextPort();
- - await RegisterIPEndPoint_Success(new IPEndPoint(address, port), testUrl, port);
- - return;
- - }
- - catch (XunitException)
- + [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 =>
- {
- - throw;
- - }
- - catch (Exception ex)
- + options.ListenAnyIP(testPort);
- + })
- + .ConfigureLogging(_configureLoggingDelegate)
- + .Configure(ConfigureEchoAddress);
- +
- + using (var host = hostBuilder.Build())
- + {
- + host.Start();
- +
- + foreach (var testUrl in testUrls.Select(testUrl => $"{testUrl}:{(testPort == 0 ? host.GetPort() : testPort)}"))
- {
- - errors.Add(ex);
- - }
- + var response = await HttpClientSlim.GetStringAsync(testUrl, validateCertificate: false);
-
- - retryCount++;
- + // 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);
- + }
- }
- }
-
- diff --git a/test/Kestrel.Transport.Libuv.Tests/LibuvOutputConsumerTests.cs b/test/Kestrel.Transport.Libuv.Tests/LibuvOutputConsumerTests.cs
- index 2a7b0ee4c17..54dd7ed0132 100644
- --- a/test/Kestrel.Transport.Libuv.Tests/LibuvOutputConsumerTests.cs
- +++ b/test/Kestrel.Transport.Libuv.Tests/LibuvOutputConsumerTests.cs
- @@ -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();
- }
|