Remove ListenOptions.Scheme and move IConnectionFilter to .Internal namespace
Add an internal API to ListenOptions to determine if an endpoint is configured to use HTTPS. This is a temporary as the design of connection adapters and configuration will churn before release.
This commit is contained in:
parent
8258d300e2
commit
ed4a27a827
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
using System.IO;
|
||||
|
||||
namespace Microsoft.AspNetCore.Server.Kestrel.Core.Adapter
|
||||
namespace Microsoft.AspNetCore.Server.Kestrel.Core.Adapter.Internal
|
||||
{
|
||||
// Even though this only includes the non-adapted ConnectionStream currently, this is a context in case
|
||||
// we want to add more connection metadata later.
|
||||
|
|
@ -4,7 +4,7 @@
|
|||
using System.IO;
|
||||
using Microsoft.AspNetCore.Http.Features;
|
||||
|
||||
namespace Microsoft.AspNetCore.Server.Kestrel.Core.Adapter
|
||||
namespace Microsoft.AspNetCore.Server.Kestrel.Core.Adapter.Internal
|
||||
{
|
||||
public interface IAdaptedConnection
|
||||
{
|
||||
|
|
@ -3,10 +3,11 @@
|
|||
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.AspNetCore.Server.Kestrel.Core.Adapter
|
||||
namespace Microsoft.AspNetCore.Server.Kestrel.Core.Adapter.Internal
|
||||
{
|
||||
public interface IConnectionAdapter
|
||||
{
|
||||
bool IsHttps { get; }
|
||||
Task<IAdaptedConnection> OnConnectionAsync(ConnectionAdapterContext context);
|
||||
}
|
||||
}
|
||||
|
|
@ -24,6 +24,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Adapter
|
|||
_logger = logger;
|
||||
}
|
||||
|
||||
public bool IsHttps => false;
|
||||
|
||||
public Task<IAdaptedConnection> OnConnectionAsync(ConnectionAdapterContext context)
|
||||
{
|
||||
return Task.FromResult<IAdaptedConnection>(
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
|
|||
});
|
||||
|
||||
_serviceContext.Log.ConnectionStart(connectionId);
|
||||
KestrelEventSource.Log.ConnectionStart(_listenOptions, connection, connectionInfo);
|
||||
KestrelEventSource.Log.ConnectionStart(connection, connectionInfo);
|
||||
|
||||
// Since data cannot be added to the inputPipe by the transport until OnConnection returns,
|
||||
// Frame.RequestProcessingAsync is guaranteed to unblock the transport thread before calling
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Core.Adapter;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Core.Adapter.Internal;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Internal.System.IO.Pipelines;
|
||||
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ using System.Text;
|
|||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Core.Adapter;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Core.Adapter.Internal;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Internal.System;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Internal.System.IO.Pipelines;
|
||||
|
|
|
|||
|
|
@ -26,14 +26,13 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure
|
|||
// - Avoid renaming methods or parameters marked with EventAttribute. EventSource uses these to form the event object.
|
||||
|
||||
[NonEvent]
|
||||
public void ConnectionStart(ListenOptions listenOptions, IConnectionContext context, IConnectionInformation information)
|
||||
public void ConnectionStart(IConnectionContext context, IConnectionInformation information)
|
||||
{
|
||||
// avoid allocating strings unless this event source is enabled
|
||||
if (IsEnabled())
|
||||
{
|
||||
ConnectionStart(
|
||||
context.ConnectionId,
|
||||
listenOptions.Scheme,
|
||||
information.LocalEndPoint.ToString(),
|
||||
information.RemoteEndPoint.ToString());
|
||||
}
|
||||
|
|
@ -42,14 +41,12 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure
|
|||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
[Event(1, Level = EventLevel.Verbose)]
|
||||
private void ConnectionStart(string connectionId,
|
||||
string scheme,
|
||||
string localEndPoint,
|
||||
string remoteEndPoint)
|
||||
{
|
||||
WriteEvent(
|
||||
1,
|
||||
connectionId,
|
||||
scheme,
|
||||
localEndPoint,
|
||||
remoteEndPoint
|
||||
);
|
||||
|
|
|
|||
|
|
@ -153,6 +153,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core
|
|||
{
|
||||
throw new InvalidOperationException($"HTTPS endpoints can only be configured using {nameof(KestrelServerOptions)}.{nameof(KestrelServerOptions.Listen)}().");
|
||||
}
|
||||
else if (!parsedAddress.Scheme.Equals("http", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
throw new InvalidOperationException($"Unrecognized scheme in server address '{address}'. Only 'http://' is supported.");
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(parsedAddress.PathBase))
|
||||
{
|
||||
|
|
@ -166,10 +170,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core
|
|||
|
||||
if (parsedAddress.IsUnixPipe)
|
||||
{
|
||||
listenOptions.Add(new ListenOptions(parsedAddress.UnixPipePath)
|
||||
{
|
||||
Scheme = parsedAddress.Scheme,
|
||||
});
|
||||
listenOptions.Add(new ListenOptions(parsedAddress.UnixPipePath));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -185,10 +186,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core
|
|||
else
|
||||
{
|
||||
// These endPoints will be added later to _serverAddresses.Addresses
|
||||
listenOptions.Add(new ListenOptions(CreateIPEndPoint(parsedAddress))
|
||||
{
|
||||
Scheme = parsedAddress.Scheme,
|
||||
});
|
||||
listenOptions.Add(new ListenOptions(CreateIPEndPoint(parsedAddress)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -209,8 +207,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core
|
|||
throw new IOException($"Failed to bind to address {endPoint}: address already in use.", ex);
|
||||
}
|
||||
|
||||
// If requested port was "0", replace with assigned dynamic port.
|
||||
_serverAddresses.Addresses.Add(endPoint.ToString());
|
||||
_serverAddresses.Addresses.Add(endPoint.GetDisplayName());
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
|
@ -285,10 +282,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core
|
|||
|
||||
try
|
||||
{
|
||||
var ipv4ListenOptions = new ListenOptions(new IPEndPoint(IPAddress.Loopback, parsedAddress.Port))
|
||||
{
|
||||
Scheme = parsedAddress.Scheme,
|
||||
};
|
||||
var ipv4ListenOptions = new ListenOptions(new IPEndPoint(IPAddress.Loopback, parsedAddress.Port));
|
||||
|
||||
var connectionHandler = new ConnectionHandler<TContext>(ipv4ListenOptions, serviceContext, application);
|
||||
var transport = _transportFactory.Create(ipv4ListenOptions, connectionHandler);
|
||||
|
|
@ -307,10 +301,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core
|
|||
|
||||
try
|
||||
{
|
||||
var ipv6ListenOptions = new ListenOptions(new IPEndPoint(IPAddress.IPv6Loopback, parsedAddress.Port))
|
||||
{
|
||||
Scheme = parsedAddress.Scheme,
|
||||
};
|
||||
var ipv6ListenOptions = new ListenOptions(new IPEndPoint(IPAddress.IPv6Loopback, parsedAddress.Port));
|
||||
|
||||
var connectionHandler = new ConnectionHandler<TContext>(ipv6ListenOptions, serviceContext, application);
|
||||
var transport = _transportFactory.Create(ipv6ListenOptions, connectionHandler);
|
||||
|
|
@ -349,4 +340,4 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core
|
|||
return new IPEndPoint(ip, address.Port);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,8 +3,9 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Core.Adapter;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Core.Adapter.Internal;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions;
|
||||
|
||||
namespace Microsoft.AspNetCore.Server.Kestrel.Core
|
||||
|
|
@ -82,26 +83,28 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core
|
|||
/// </remarks>
|
||||
public List<IConnectionAdapter> ConnectionAdapters { get; } = new List<IConnectionAdapter>();
|
||||
|
||||
// Scheme is hopefully only a temporary measure for back compat with IServerAddressesFeature.
|
||||
// TODO: Allow connection adapters to configure the scheme
|
||||
public string Scheme { get; set; } = "http";
|
||||
|
||||
public override string ToString()
|
||||
/// <summary>
|
||||
/// Gets the name of this endpoint to display on command-line when the web server starts.
|
||||
/// </summary>
|
||||
internal string GetDisplayName()
|
||||
{
|
||||
// Use http scheme for all addresses. If https should be used for this endPoint,
|
||||
// it can still be configured for this endPoint specifically.
|
||||
var scheme = ConnectionAdapters.Any(f => f.IsHttps)
|
||||
? "https"
|
||||
: "http";
|
||||
|
||||
switch (Type)
|
||||
{
|
||||
case ListenType.IPEndPoint:
|
||||
return $"{Scheme}://{IPEndPoint}";
|
||||
return $"{scheme}://{IPEndPoint}";
|
||||
case ListenType.SocketPath:
|
||||
return $"{Scheme}://unix:{SocketPath}";
|
||||
return $"{scheme}://unix:{SocketPath}";
|
||||
case ListenType.FileHandle:
|
||||
// This was never supported via --server.urls, so no need to include Scheme.
|
||||
return "http://<file handle>";
|
||||
return $"{scheme}://<file handle>";
|
||||
default:
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
}
|
||||
|
||||
public override string ToString() => GetDisplayName();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ using System.Net.Security;
|
|||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http.Features;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Core.Adapter;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Core.Adapter.Internal;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Https.Internal;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
|
|
@ -40,6 +40,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Https
|
|||
_logger = loggerFactory?.CreateLogger(nameof(HttpsConnectionAdapter));
|
||||
}
|
||||
|
||||
public bool IsHttps => true;
|
||||
|
||||
public async Task<IAdaptedConnection> OnConnectionAsync(ConnectionAdapterContext context)
|
||||
{
|
||||
SslStream sslStream;
|
||||
|
|
|
|||
|
|
@ -260,6 +260,22 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
|
|||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("https://localhost")]
|
||||
[InlineData("ftp://localhost")]
|
||||
public void ThrowsForUnsupportedAddressFromHosting(string addr)
|
||||
{
|
||||
var hostBuilder = new WebHostBuilder()
|
||||
.UseKestrel()
|
||||
.UseUrls(addr)
|
||||
.Configure(ConfigureEchoAddress);
|
||||
|
||||
using (var host = hostBuilder.Build())
|
||||
{
|
||||
Assert.Throws<InvalidOperationException>(() => host.Start());
|
||||
}
|
||||
}
|
||||
|
||||
private void ThrowsWhenBindingLocalhostToAddressInUse(AddressFamily addressFamily, IPAddress address)
|
||||
{
|
||||
using (var socket = new Socket(addressFamily, SocketType.Stream, ProtocolType.Tcp))
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ using System.Threading;
|
|||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http.Features;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Core;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Core.Adapter;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Core.Adapter.Internal;
|
||||
using Microsoft.AspNetCore.Testing;
|
||||
using Xunit;
|
||||
|
||||
|
|
@ -112,6 +112,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
|
|||
{
|
||||
private RewritingStream _rewritingStream;
|
||||
|
||||
public bool IsHttps => false;
|
||||
|
||||
public Task<IAdaptedConnection> OnConnectionAsync(ConnectionAdapterContext context)
|
||||
{
|
||||
_rewritingStream = new RewritingStream(context.ConnectionStream);
|
||||
|
|
@ -123,6 +125,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
|
|||
|
||||
private class AsyncConnectionAdapter : IConnectionAdapter
|
||||
{
|
||||
public bool IsHttps => false;
|
||||
|
||||
public async Task<IAdaptedConnection> OnConnectionAsync(ConnectionAdapterContext context)
|
||||
{
|
||||
await Task.Delay(100);
|
||||
|
|
@ -132,6 +136,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
|
|||
|
||||
private class ThrowingConnectionAdapter : IConnectionAdapter
|
||||
{
|
||||
public bool IsHttps => false;
|
||||
|
||||
public Task<IAdaptedConnection> OnConnectionAsync(ConnectionAdapterContext context)
|
||||
{
|
||||
throw new Exception();
|
||||
|
|
|
|||
|
|
@ -55,8 +55,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
|
|||
|
||||
{
|
||||
var start = Assert.Single(events, e => e.EventName == "ConnectionStart");
|
||||
Assert.All(new[] { "connectionId", "scheme", "remoteEndPoint", "localEndPoint" }, p => Assert.Contains(p, start.PayloadNames));
|
||||
Assert.Equal("http", GetProperty(start, "scheme"));
|
||||
Assert.All(new[] { "connectionId", "remoteEndPoint", "localEndPoint" }, p => Assert.Contains(p, start.PayloadNames));
|
||||
Assert.Equal($"127.0.0.1:{port}", GetProperty(start, "localEndPoint"));
|
||||
}
|
||||
{
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ using System.Threading.Tasks;
|
|||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Core;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal.Networking;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Tests.TestHelpers;
|
||||
|
|
@ -46,7 +47,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Tests
|
|||
await libuvThreadPrimary.StartAsync();
|
||||
var listenerPrimary = new ListenerPrimary(transportContextPrimary);
|
||||
await listenerPrimary.StartAsync(pipeName, pipeMessage, listenOptions, libuvThreadPrimary);
|
||||
var address = listenOptions.ToString();
|
||||
var address = GetUri(listenOptions);
|
||||
|
||||
// Until a secondary listener is added, TCP connections get dispatched directly
|
||||
Assert.Equal("Primary", await HttpClientSlim.GetStringAsync(address));
|
||||
|
|
@ -108,7 +109,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Tests
|
|||
await libuvThreadPrimary.StartAsync();
|
||||
var listenerPrimary = new ListenerPrimary(transportContextPrimary);
|
||||
await listenerPrimary.StartAsync(pipeName, pipeMessage, listenOptions, libuvThreadPrimary);
|
||||
var address = listenOptions.ToString();
|
||||
var address = GetUri(listenOptions);
|
||||
|
||||
// Add secondary listener
|
||||
var libuvThreadSecondary = new LibuvThread(libuvTransport);
|
||||
|
|
@ -217,7 +218,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Tests
|
|||
await libuvThreadPrimary.StartAsync();
|
||||
var listenerPrimary = new ListenerPrimary(transportContextPrimary);
|
||||
await listenerPrimary.StartAsync(pipeName, pipeMessage, listenOptions, libuvThreadPrimary);
|
||||
var address = listenOptions.ToString();
|
||||
var address = GetUri(listenOptions);
|
||||
|
||||
// Add secondary listener with wrong pipe message
|
||||
var libuvThreadSecondary = new LibuvThread(libuvTransport);
|
||||
|
|
@ -249,7 +250,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Tests
|
|||
}
|
||||
|
||||
private static async Task AssertResponseEventually(
|
||||
string address,
|
||||
Uri address,
|
||||
string expected,
|
||||
string[] allowed = null,
|
||||
int maxRetries = 100,
|
||||
|
|
@ -273,5 +274,19 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Tests
|
|||
|
||||
Assert.True(false, $"'{address}' failed to respond with '{expected}' in {maxRetries} retries.");
|
||||
}
|
||||
|
||||
private static Uri GetUri(ListenOptions options)
|
||||
{
|
||||
if (options.Type != ListenType.IPEndPoint)
|
||||
{
|
||||
throw new InvalidOperationException($"Could not determine a proper URI for options with Type {options.Type}");
|
||||
}
|
||||
|
||||
var scheme = options.ConnectionAdapters.Any(f => f.IsHttps)
|
||||
? "https"
|
||||
: "http";
|
||||
|
||||
return new Uri($"{scheme}://{options.IPEndPoint}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,6 +11,8 @@ namespace Microsoft.AspNetCore.Testing
|
|||
{
|
||||
public class PassThroughConnectionAdapter : IConnectionAdapter
|
||||
{
|
||||
public bool IsHttps => false;
|
||||
|
||||
public Task<IAdaptedConnection> OnConnectionAsync(ConnectionAdapterContext context)
|
||||
{
|
||||
var adapted = new AdaptedConnection(new LoggingStream(context.ConnectionStream, new TestApplicationErrorLogger()));
|
||||
|
|
|
|||
Loading…
Reference in New Issue