Invert the dependency between connection adapters and Frame (#1822)
* Invert the dependency between connection adapters and Frame - Removed PrepareRequest from IAdaptedConnection and instead added a feature collection to the ConnectionAdapterContext. This allows features to be set once by the adapter instead of per request. It's the Frame's job to copy features from the connection level feature collection into the per request feature collection. - Set the scheme to "https" based on the presence of ITlsConnectionFeature. - Always set ITlsConnection feature if the HttpsAdaptedConnection doesn't throw during the handshake
This commit is contained in:
parent
90ee2252a0
commit
b9518e3684
|
|
@ -2,6 +2,7 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.IO;
|
||||
using Microsoft.AspNetCore.Http.Features;
|
||||
|
||||
namespace Microsoft.AspNetCore.Server.Kestrel.Core.Adapter.Internal
|
||||
{
|
||||
|
|
@ -9,11 +10,14 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Adapter.Internal
|
|||
// we want to add more connection metadata later.
|
||||
public class ConnectionAdapterContext
|
||||
{
|
||||
internal ConnectionAdapterContext(Stream connectionStream)
|
||||
internal ConnectionAdapterContext(IFeatureCollection features, Stream connectionStream)
|
||||
{
|
||||
Features = features;
|
||||
ConnectionStream = connectionStream;
|
||||
}
|
||||
|
||||
public IFeatureCollection Features { get; }
|
||||
|
||||
public Stream ConnectionStream { get; }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,14 +3,11 @@
|
|||
|
||||
using System;
|
||||
using System.IO;
|
||||
using Microsoft.AspNetCore.Http.Features;
|
||||
|
||||
namespace Microsoft.AspNetCore.Server.Kestrel.Core.Adapter.Internal
|
||||
{
|
||||
public interface IAdaptedConnection : IDisposable
|
||||
{
|
||||
Stream ConnectionStream { get; }
|
||||
|
||||
void PrepareRequest(IFeatureCollection requestFeatures);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,10 +40,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Adapter.Internal
|
|||
|
||||
public Stream ConnectionStream { get; }
|
||||
|
||||
public void PrepareRequest(IFeatureCollection requestFeatures)
|
||||
{
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ using System.Diagnostics;
|
|||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http.Features;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Core.Adapter.Internal;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure;
|
||||
|
|
@ -20,7 +21,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
|
|||
{
|
||||
private readonly FrameConnectionContext _context;
|
||||
private readonly Frame _frame;
|
||||
private readonly List<IConnectionAdapter> _connectionAdapters;
|
||||
private List<IAdaptedConnection> _adaptedConnections;
|
||||
private readonly TaskCompletionSource<object> _socketClosedTcs = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||
|
||||
private long _lastTimestamp;
|
||||
|
|
@ -33,7 +34,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
|
|||
{
|
||||
_context = context;
|
||||
_frame = context.Frame;
|
||||
_connectionAdapters = context.ConnectionAdapters;
|
||||
}
|
||||
|
||||
public string ConnectionId => _context.ConnectionId;
|
||||
|
|
@ -78,7 +78,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
|
|||
var input = _context.Input.Reader;
|
||||
var output = _context.Output;
|
||||
|
||||
if (_connectionAdapters.Count > 0)
|
||||
if (_context.ConnectionAdapters.Count > 0)
|
||||
{
|
||||
adaptedPipeline = new AdaptedPipeline(input,
|
||||
output,
|
||||
|
|
@ -159,17 +159,19 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
|
|||
|
||||
private async Task<Stream> ApplyConnectionAdaptersAsync()
|
||||
{
|
||||
var features = new FeatureCollection();
|
||||
var connectionAdapters = _context.ConnectionAdapters;
|
||||
var stream = new RawStream(_context.Input.Reader, _context.Output.Writer);
|
||||
var adapterContext = new ConnectionAdapterContext(stream);
|
||||
var adaptedConnections = new IAdaptedConnection[_connectionAdapters.Count];
|
||||
var adapterContext = new ConnectionAdapterContext(features, stream);
|
||||
_adaptedConnections = new List<IAdaptedConnection>(connectionAdapters.Count);
|
||||
|
||||
try
|
||||
{
|
||||
for (var i = 0; i < _connectionAdapters.Count; i++)
|
||||
for (var i = 0; i < connectionAdapters.Count; i++)
|
||||
{
|
||||
var adaptedConnection = await _connectionAdapters[i].OnConnectionAsync(adapterContext);
|
||||
adaptedConnections[i] = adaptedConnection;
|
||||
adapterContext = new ConnectionAdapterContext(adaptedConnection.ConnectionStream);
|
||||
var adaptedConnection = await connectionAdapters[i].OnConnectionAsync(adapterContext);
|
||||
_adaptedConnections.Add(adaptedConnection);
|
||||
adapterContext = new ConnectionAdapterContext(features, adaptedConnection.ConnectionStream);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
|
@ -180,7 +182,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
|
|||
}
|
||||
finally
|
||||
{
|
||||
_frame.AdaptedConnections = adaptedConnections;
|
||||
_frame.ConnectionFeatures = features;
|
||||
}
|
||||
|
||||
return adapterContext.ConnectionStream;
|
||||
|
|
@ -188,10 +190,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
|
|||
|
||||
private void DisposeAdaptedConnections()
|
||||
{
|
||||
var adaptedConnections = _frame.AdaptedConnections;
|
||||
var adaptedConnections = _adaptedConnections;
|
||||
if (adaptedConnections != null)
|
||||
{
|
||||
for (int i = adaptedConnections.Length - 1; i >= 0; i--)
|
||||
for (int i = adaptedConnections.Count - 1; i >= 0; i--)
|
||||
{
|
||||
adaptedConnections[i].Dispose();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@ using System.Text;
|
|||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
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;
|
||||
|
|
@ -21,6 +20,7 @@ using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions;
|
|||
using Microsoft.Extensions.Internal;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
using Microsoft.AspNetCore.Http.Features;
|
||||
|
||||
// ReSharper disable AccessToModifiedClosure
|
||||
|
||||
|
|
@ -103,7 +103,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
|
|||
|
||||
public IPipeReader Input { get; set; }
|
||||
public OutputProducer Output { get; set; }
|
||||
public IAdaptedConnection[] AdaptedConnections { get; set; }
|
||||
public IFeatureCollection ConnectionFeatures { get; set; }
|
||||
public ITimeoutControl TimeoutControl { get; set; }
|
||||
|
||||
protected IKestrelTrace Log => ServiceContext.Log;
|
||||
|
|
@ -349,18 +349,17 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
|
|||
RequestHeaders = FrameRequestHeaders;
|
||||
ResponseHeaders = FrameResponseHeaders;
|
||||
|
||||
if (AdaptedConnections != null)
|
||||
if (ConnectionFeatures != null)
|
||||
{
|
||||
try
|
||||
foreach (var feature in ConnectionFeatures)
|
||||
{
|
||||
foreach (var adaptedConnection in AdaptedConnections)
|
||||
// Set the scheme to https if there's an ITlsConnectionFeature
|
||||
if (feature.Key == typeof(ITlsConnectionFeature))
|
||||
{
|
||||
adaptedConnection.PrepareRequest(this);
|
||||
Scheme = "https";
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.LogError(0, ex, $"Uncaught exception from the {nameof(IAdaptedConnection.PrepareRequest)} method of an {nameof(IAdaptedConnection)}.");
|
||||
|
||||
FastFeatureSet(feature.Key, feature.Value);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -109,6 +109,12 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Https
|
|||
return _closedAdaptedConnection;
|
||||
}
|
||||
|
||||
// Always set the feature even though the cert might be null
|
||||
context.Features.Set<ITlsConnectionFeature>(new TlsConnectionFeature
|
||||
{
|
||||
ClientCertificate = (X509Certificate2)sslStream.RemoteCertificate
|
||||
});
|
||||
|
||||
return new HttpsAdaptedConnection(sslStream);
|
||||
}
|
||||
|
||||
|
|
@ -123,17 +129,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Https
|
|||
|
||||
public Stream ConnectionStream => _sslStream;
|
||||
|
||||
public void PrepareRequest(IFeatureCollection requestFeatures)
|
||||
{
|
||||
var clientCertificate = (X509Certificate2)_sslStream.RemoteCertificate;
|
||||
if (clientCertificate != null)
|
||||
{
|
||||
requestFeatures.Set<ITlsConnectionFeature>(new TlsConnectionFeature { ClientCertificate = clientCertificate });
|
||||
}
|
||||
|
||||
requestFeatures.Get<IHttpRequestFeature>().Scheme = "https";
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_sslStream.Dispose();
|
||||
|
|
@ -144,10 +139,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Https
|
|||
{
|
||||
public Stream ConnectionStream { get; } = new ClosedStream();
|
||||
|
||||
public void PrepareRequest(IFeatureCollection requestFeatures)
|
||||
{
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ using System.Net;
|
|||
using System.Net.Sockets;
|
||||
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.Internal;
|
||||
using Microsoft.AspNetCore.Testing;
|
||||
|
|
@ -218,10 +217,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
|
|||
|
||||
public Stream ConnectionStream { get; }
|
||||
|
||||
public void PrepareRequest(IFeatureCollection requestFeatures)
|
||||
{
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
|
|
|
|||
|
|
@ -94,7 +94,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
|
|||
|
||||
using (var server = new TestServer(context =>
|
||||
{
|
||||
Assert.Equal(context.Features.Get<ITlsConnectionFeature>(), null);
|
||||
var tlsFeature = context.Features.Get<ITlsConnectionFeature>();
|
||||
Assert.NotNull(tlsFeature);
|
||||
Assert.Null(tlsFeature.ClientCertificate);
|
||||
return context.Response.WriteAsync("hello world");
|
||||
},
|
||||
serviceContext, listenOptions))
|
||||
|
|
|
|||
|
|
@ -27,10 +27,6 @@ namespace Microsoft.AspNetCore.Testing
|
|||
|
||||
public Stream ConnectionStream { get; }
|
||||
|
||||
public void PrepareRequest(IFeatureCollection requestFeatures)
|
||||
{
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue