From a2c4e3a654eb2bdc106be0aaa1d3fb94000195c5 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Sun, 1 Nov 2015 02:49:08 +0000 Subject: [PATCH 01/30] Task.Run -> ThreadPool.QueueUserWorkItem Task.Run eventually ends up being QueueUserWorkItem. The returned task is ignored, so no added goodness. Short running item. Cut out the middleman --- src/Microsoft.AspNet.Server.Kestrel/Http/SocketInput.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Microsoft.AspNet.Server.Kestrel/Http/SocketInput.cs b/src/Microsoft.AspNet.Server.Kestrel/Http/SocketInput.cs index 0eb74bb90..98910340a 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Http/SocketInput.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Http/SocketInput.cs @@ -128,7 +128,7 @@ public void IncomingComplete(int count, Exception error) if (awaitableState != _awaitableIsCompleted && awaitableState != _awaitableIsNotCompleted) { - Task.Run(awaitableState); + ThreadPool.QueueUserWorkItem((o) => ((Action)o)(), awaitableState); } } @@ -194,7 +194,7 @@ public void OnCompleted(Action continuation) } else if (awaitableState == _awaitableIsCompleted) { - Task.Run(continuation); + ThreadPool.QueueUserWorkItem((o) => ((Action)o)(), continuation); } else { From c0f82765aafe4328da49177d955d9fe8f6a1f978 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Tue, 3 Nov 2015 19:44:04 +0000 Subject: [PATCH 02/30] Move logging to new style --- .../Infrastructure/KestrelTrace.cs | 45 +++++++++++++++---- .../EngineTests.cs | 4 +- 2 files changed, 38 insertions(+), 11 deletions(-) diff --git a/src/Microsoft.AspNet.Server.Kestrel/Infrastructure/KestrelTrace.cs b/src/Microsoft.AspNet.Server.Kestrel/Infrastructure/KestrelTrace.cs index e55ad7284..4ac345e70 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Infrastructure/KestrelTrace.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Infrastructure/KestrelTrace.cs @@ -12,8 +12,35 @@ namespace Microsoft.AspNet.Server.Kestrel /// public class KestrelTrace : IKestrelTrace { + private static Action _connectionStart; + private static Action _connectionStop; + private static Action _connectionPause; + private static Action _connectionResume; + private static Action _connectionReadFin; + private static Action _connectionWriteFin; + private static Action _connectionWroteFin; + private static Action _connectionKeepAlive; + private static Action _connectionDisconnect; + protected readonly ILogger _logger; + static KestrelTrace() + { + _connectionStart = LoggerMessage.Define(LogLevel.Debug, 1, @"Connection id ""{ConnectionId}"" started."); + _connectionStop = LoggerMessage.Define(LogLevel.Debug, 2, @"Connection id ""{ConnectionId}"" stopped."); + // ConnectionRead: Reserved: 3 + _connectionPause = LoggerMessage.Define(LogLevel.Debug, 4, @"Connection id ""{ConnectionId}"" paused."); + _connectionResume = LoggerMessage.Define(LogLevel.Debug, 5, @"Connection id ""{ConnectionId}"" resumed."); + _connectionReadFin = LoggerMessage.Define(LogLevel.Debug, 6, @"Connection id ""{ConnectionId}"" received FIN."); + _connectionWriteFin = LoggerMessage.Define(LogLevel.Debug, 7, @"Connection id ""{ConnectionId}"" sending FIN."); + _connectionWroteFin = LoggerMessage.Define(LogLevel.Debug, 8, @"Connection id ""{ConnectionId}"" sent FIN with status ""{Status}""."); + _connectionKeepAlive = LoggerMessage.Define(LogLevel.Debug, 9, @"Connection id ""{ConnectionId}"" completed keep alive response."); + _connectionDisconnect = LoggerMessage.Define(LogLevel.Error, 10, @"Connection id ""{ConnectionId}"" disconnected."); + // ConnectionWrite: Reserved: 11 + // ConnectionWriteCallback: Reserved: 12 + // ApplicationError: Reserved: 13 - LoggerMessage.Define overload not present + } + public KestrelTrace(ILogger logger) { _logger = logger; @@ -21,12 +48,12 @@ public KestrelTrace(ILogger logger) public virtual void ConnectionStart(long connectionId) { - _logger.LogDebug(1, @"Connection id ""{ConnectionId}"" started.", connectionId); + _connectionStart(_logger, connectionId, null); } public virtual void ConnectionStop(long connectionId) { - _logger.LogDebug(2, @"Connection id ""{ConnectionId}"" stopped.", connectionId); + _connectionStop(_logger, connectionId, null); } public virtual void ConnectionRead(long connectionId, int count) @@ -37,37 +64,37 @@ public virtual void ConnectionRead(long connectionId, int count) public virtual void ConnectionPause(long connectionId) { - _logger.LogDebug(4, @"Connection id ""{ConnectionId}"" paused.", connectionId); + _connectionPause(_logger, connectionId, null); } public virtual void ConnectionResume(long connectionId) { - _logger.LogDebug(5, @"Connection id ""{ConnectionId}"" resumed.", connectionId); + _connectionResume(_logger, connectionId, null); } public virtual void ConnectionReadFin(long connectionId) { - _logger.LogDebug(6, @"Connection id ""{ConnectionId}"" received FIN.", connectionId); + _connectionReadFin(_logger, connectionId, null); } public virtual void ConnectionWriteFin(long connectionId) { - _logger.LogDebug(7, @"Connection id ""{ConnectionId}"" sending FIN.", connectionId); + _connectionWriteFin(_logger, connectionId, null); } public virtual void ConnectionWroteFin(long connectionId, int status) { - _logger.LogDebug(8, @"Connection id ""{ConnectionId}"" sent FIN with status ""{Status}"".", connectionId, status); + _connectionWroteFin(_logger, connectionId, status, null); } public virtual void ConnectionKeepAlive(long connectionId) { - _logger.LogDebug(9, @"Connection id ""{ConnectionId}"" completed keep alive response.", connectionId); + _connectionKeepAlive(_logger, connectionId, null); } public virtual void ConnectionDisconnect(long connectionId) { - _logger.LogDebug(10, @"Connection id ""{ConnectionId}"" disconnected.", connectionId); + _connectionDisconnect(_logger, connectionId, null); } public virtual void ConnectionWrite(long connectionId, int count) diff --git a/test/Microsoft.AspNet.Server.KestrelTests/EngineTests.cs b/test/Microsoft.AspNet.Server.KestrelTests/EngineTests.cs index 983017cfa..c89297337 100644 --- a/test/Microsoft.AspNet.Server.KestrelTests/EngineTests.cs +++ b/test/Microsoft.AspNet.Server.KestrelTests/EngineTests.cs @@ -904,12 +904,12 @@ private class TestApplicationErrorLogger : ILogger public IDisposable BeginScopeImpl(object state) { - throw new NotImplementedException(); + return new Disposable(() => { }); } public bool IsEnabled(LogLevel logLevel) { - throw new NotImplementedException(); + return true; } public void Log(LogLevel logLevel, int eventId, object state, Exception exception, Func formatter) From fb7465891e043ca636caa9cf53185212465fc80f Mon Sep 17 00:00:00 2001 From: Master T Date: Tue, 10 Nov 2015 18:47:01 +0100 Subject: [PATCH 03/30] Implement client certificate authentication --- .../ClientCertificateMode.cs | 14 ++++++++ .../HttpsApplicationBuilderExtensions.cs | 7 +++- .../HttpsConnectionFilter.cs | 34 +++++++++++++++++-- .../Filter/ConnectionFilterContext.cs | 4 ++- .../Http/Connection.cs | 9 ++--- .../Http/Frame.cs | 14 ++++++-- 6 files changed, 71 insertions(+), 11 deletions(-) create mode 100644 src/Microsoft.AspNet.Server.Kestrel.Https/ClientCertificateMode.cs diff --git a/src/Microsoft.AspNet.Server.Kestrel.Https/ClientCertificateMode.cs b/src/Microsoft.AspNet.Server.Kestrel.Https/ClientCertificateMode.cs new file mode 100644 index 000000000..637db9b79 --- /dev/null +++ b/src/Microsoft.AspNet.Server.Kestrel.Https/ClientCertificateMode.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Microsoft.AspNet.Server.Kestrel.Https +{ + public enum ClientCertificateMode + { + NoCertificate, + AllowCertificate, + RequireCertificate + } +} diff --git a/src/Microsoft.AspNet.Server.Kestrel.Https/HttpsApplicationBuilderExtensions.cs b/src/Microsoft.AspNet.Server.Kestrel.Https/HttpsApplicationBuilderExtensions.cs index 844435c15..807fec88c 100644 --- a/src/Microsoft.AspNet.Server.Kestrel.Https/HttpsApplicationBuilderExtensions.cs +++ b/src/Microsoft.AspNet.Server.Kestrel.Https/HttpsApplicationBuilderExtensions.cs @@ -11,6 +11,11 @@ namespace Microsoft.AspNet.Server.Kestrel.Https public static class HttpsApplicationBuilderExtensions { public static IApplicationBuilder UseKestrelHttps(this IApplicationBuilder app, X509Certificate2 cert) + { + return app.UseKestrelHttps(cert, ClientCertificateMode.NoCertificate); + } + + public static IApplicationBuilder UseKestrelHttps(this IApplicationBuilder app, X509Certificate2 cert, ClientCertificateMode mode) { var serverInfo = app.ServerFeatures.Get(); @@ -21,7 +26,7 @@ public static IApplicationBuilder UseKestrelHttps(this IApplicationBuilder app, var prevFilter = serverInfo.ConnectionFilter ?? new NoOpConnectionFilter(); - serverInfo.ConnectionFilter = new HttpsConnectionFilter(cert, prevFilter); + serverInfo.ConnectionFilter = new HttpsConnectionFilter(cert, mode, prevFilter); return app; } diff --git a/src/Microsoft.AspNet.Server.Kestrel.Https/HttpsConnectionFilter.cs b/src/Microsoft.AspNet.Server.Kestrel.Https/HttpsConnectionFilter.cs index 08cad7161..031ee54ab 100644 --- a/src/Microsoft.AspNet.Server.Kestrel.Https/HttpsConnectionFilter.cs +++ b/src/Microsoft.AspNet.Server.Kestrel.Https/HttpsConnectionFilter.cs @@ -6,15 +6,21 @@ using System.Security.Cryptography.X509Certificates; using System.Threading.Tasks; using Microsoft.AspNet.Server.Kestrel.Filter; +using System.Security.Authentication; namespace Microsoft.AspNet.Server.Kestrel.Https { public class HttpsConnectionFilter : IConnectionFilter { private readonly X509Certificate2 _cert; + private readonly ClientCertificateMode _clientCertMode; private readonly IConnectionFilter _previous; - public HttpsConnectionFilter(X509Certificate2 cert, IConnectionFilter previous) + public HttpsConnectionFilter(X509Certificate2 cert, IConnectionFilter previous) : + this(cert, ClientCertificateMode.NoCertificate, previous) + { } + + public HttpsConnectionFilter(X509Certificate2 cert, ClientCertificateMode mode, IConnectionFilter previous) { if (cert == null) { @@ -26,6 +32,7 @@ public HttpsConnectionFilter(X509Certificate2 cert, IConnectionFilter previous) } _cert = cert; + _clientCertMode = mode; _previous = previous; } @@ -35,8 +42,29 @@ public async Task OnConnection(ConnectionFilterContext context) if (string.Equals(context.Address.Scheme, "https", StringComparison.OrdinalIgnoreCase)) { - var sslStream = new SslStream(context.Connection); - await sslStream.AuthenticateAsServerAsync(_cert); + SslStream sslStream; + if (_clientCertMode == ClientCertificateMode.NoCertificate) + { + sslStream = new SslStream(context.Connection); + await sslStream.AuthenticateAsServerAsync(_cert); + } + else + { + sslStream = new SslStream(context.Connection, false, + (sender, certificate, chain, sslPolicyErrors) => + { + context.ClientCertificate = certificate as X509Certificate2; + if (sslPolicyErrors.HasFlag(SslPolicyErrors.RemoteCertificateNotAvailable)) + { + return _clientCertMode != ClientCertificateMode.RequireCertificate; + } + else + { + return sslPolicyErrors == SslPolicyErrors.None; + } + }); + await sslStream.AuthenticateAsServerAsync(_cert, true, SslProtocols.Default, false); + } context.Connection = sslStream; } } diff --git a/src/Microsoft.AspNet.Server.Kestrel/Filter/ConnectionFilterContext.cs b/src/Microsoft.AspNet.Server.Kestrel/Filter/ConnectionFilterContext.cs index 937e92bec..ab086bbf6 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Filter/ConnectionFilterContext.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Filter/ConnectionFilterContext.cs @@ -2,12 +2,14 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.IO; +using System.Security.Cryptography.X509Certificates; namespace Microsoft.AspNet.Server.Kestrel.Filter { public class ConnectionFilterContext { public ServerAddress Address { get; set; } - public Stream Connection { get; set; } + public Stream Connection { get; set; } + public X509Certificate2 ClientCertificate { get; set; } } } diff --git a/src/Microsoft.AspNet.Server.Kestrel/Http/Connection.cs b/src/Microsoft.AspNet.Server.Kestrel/Http/Connection.cs index 4061f1e80..b317275e3 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Http/Connection.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Http/Connection.cs @@ -8,6 +8,7 @@ using Microsoft.AspNet.Server.Kestrel.Infrastructure; using Microsoft.AspNet.Server.Kestrel.Networking; using Microsoft.Extensions.Logging; +using System.Security.Cryptography.X509Certificates; namespace Microsoft.AspNet.Server.Kestrel.Http { @@ -65,7 +66,7 @@ public void Start() SocketInput = _rawSocketInput; SocketOutput = _rawSocketOutput; - _frame = CreateFrame(); + _frame = CreateFrame(clientCertificate: null); _frame.Start(); } else @@ -107,7 +108,7 @@ private void ApplyConnectionFilter() SocketInput = filteredStreamAdapter.SocketInput; SocketOutput = filteredStreamAdapter.SocketOutput; - _frame = CreateFrame(); + _frame = CreateFrame(_filterContext.ClientCertificate); _frame.Start(); } @@ -155,9 +156,9 @@ private void OnRead(UvStreamHandle handle, int status) _rawSocketInput.IncomingComplete(readCount, error); } - private Frame CreateFrame() + private Frame CreateFrame(X509Certificate2 clientCertificate) { - return new Frame(this, _remoteEndPoint, _localEndPoint); + return new Frame(this, _remoteEndPoint, _localEndPoint, clientCertificate); } void IConnectionControl.Pause() diff --git a/src/Microsoft.AspNet.Server.Kestrel/Http/Frame.cs b/src/Microsoft.AspNet.Server.Kestrel/Http/Frame.cs index 36f1d9231..87cf39b25 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Http/Frame.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Http/Frame.cs @@ -14,6 +14,8 @@ using Microsoft.AspNet.Server.Kestrel.Infrastructure; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Primitives; +using System.Security.Cryptography.X509Certificates; +using Microsoft.AspNet.Http.Features.Internal; // ReSharper disable AccessToModifiedClosure @@ -49,19 +51,22 @@ public partial class Frame : FrameContext, IFrameControl private readonly IPEndPoint _localEndPoint; private readonly IPEndPoint _remoteEndPoint; + private readonly X509Certificate2 _clientCertificate; public Frame(ConnectionContext context) - : this(context, remoteEndPoint: null, localEndPoint: null) + : this(context, remoteEndPoint: null, localEndPoint: null, clientCertificate: null) { } public Frame(ConnectionContext context, IPEndPoint remoteEndPoint, - IPEndPoint localEndPoint) + IPEndPoint localEndPoint, + X509Certificate2 clientCertificate) : base(context) { _remoteEndPoint = remoteEndPoint; _localEndPoint = localEndPoint; + _clientCertificate = clientCertificate; FrameControl = this; Reset(); @@ -131,6 +136,11 @@ public void Reset() { httpConnectionFeature.IsLocal = false; } + + if (_clientCertificate != null) + { + _currentITlsConnectionFeature = new TlsConnectionFeature { ClientCertificate = _clientCertificate }; + } } public void ResetResponseHeaders() From 344c821f83979eb8b406d0df975d95b6d8d99ea5 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Tue, 10 Nov 2015 16:24:13 +0000 Subject: [PATCH 04/30] Remove sync block->task await rather than synchronously blocking and then returning a Task.FromResult --- .../Http/Frame.FeatureCollection.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/Microsoft.AspNet.Server.Kestrel/Http/Frame.FeatureCollection.cs b/src/Microsoft.AspNet.Server.Kestrel/Http/Frame.FeatureCollection.cs index 1a4efb32f..2159d7005 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Http/Frame.FeatureCollection.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Http/Frame.FeatureCollection.cs @@ -277,7 +277,7 @@ void IHttpResponseFeature.OnCompleted(Func callback, object state) OnCompleted(callback, state); } - Task IHttpUpgradeFeature.UpgradeAsync() + async Task IHttpUpgradeFeature.UpgradeAsync() { StatusCode = 101; ReasonPhrase = "Switching Protocols"; @@ -290,8 +290,10 @@ Task IHttpUpgradeFeature.UpgradeAsync() ResponseHeaders["Upgrade"] = values; } } - ProduceStartAndFireOnStarting(immediate: true).GetAwaiter().GetResult(); - return Task.FromResult(DuplexStream); + + await ProduceStartAndFireOnStarting(immediate: true); + + return DuplexStream; } IEnumerator> IEnumerable>.GetEnumerator() => FastEnumerable().GetEnumerator(); From 48b7b795403f71bf2f2973dc2cce117b16374013 Mon Sep 17 00:00:00 2001 From: Master T Date: Wed, 11 Nov 2015 07:13:10 +0100 Subject: [PATCH 05/30] Pass around ITlsConnectionFeature instead of X509Certificate2, code cleanup --- .../ClientCertificateMode.cs | 6 ++---- .../HttpsConnectionFilter.cs | 13 +++++++++---- .../Filter/ConnectionFilterContext.cs | 4 ++-- .../Http/Connection.cs | 12 +++++++----- src/Microsoft.AspNet.Server.Kestrel/Http/Frame.cs | 15 +++++---------- 5 files changed, 25 insertions(+), 25 deletions(-) diff --git a/src/Microsoft.AspNet.Server.Kestrel.Https/ClientCertificateMode.cs b/src/Microsoft.AspNet.Server.Kestrel.Https/ClientCertificateMode.cs index 637db9b79..34e8b839e 100644 --- a/src/Microsoft.AspNet.Server.Kestrel.Https/ClientCertificateMode.cs +++ b/src/Microsoft.AspNet.Server.Kestrel.Https/ClientCertificateMode.cs @@ -1,7 +1,5 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; +// 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. namespace Microsoft.AspNet.Server.Kestrel.Https { diff --git a/src/Microsoft.AspNet.Server.Kestrel.Https/HttpsConnectionFilter.cs b/src/Microsoft.AspNet.Server.Kestrel.Https/HttpsConnectionFilter.cs index 031ee54ab..53a78163b 100644 --- a/src/Microsoft.AspNet.Server.Kestrel.Https/HttpsConnectionFilter.cs +++ b/src/Microsoft.AspNet.Server.Kestrel.Https/HttpsConnectionFilter.cs @@ -7,6 +7,7 @@ using System.Threading.Tasks; using Microsoft.AspNet.Server.Kestrel.Filter; using System.Security.Authentication; +using Microsoft.AspNet.Http.Features.Internal; namespace Microsoft.AspNet.Server.Kestrel.Https { @@ -50,10 +51,13 @@ public async Task OnConnection(ConnectionFilterContext context) } else { - sslStream = new SslStream(context.Connection, false, - (sender, certificate, chain, sslPolicyErrors) => + sslStream = new SslStream(context.Connection, leaveInnerStreamOpen: false, + userCertificateValidationCallback: (sender, certificate, chain, sslPolicyErrors) => { - context.ClientCertificate = certificate as X509Certificate2; + context.TlsConnectionFeature = new TlsConnectionFeature() + { + ClientCertificate = certificate as X509Certificate2 + }; if (sslPolicyErrors.HasFlag(SslPolicyErrors.RemoteCertificateNotAvailable)) { return _clientCertMode != ClientCertificateMode.RequireCertificate; @@ -63,7 +67,8 @@ public async Task OnConnection(ConnectionFilterContext context) return sslPolicyErrors == SslPolicyErrors.None; } }); - await sslStream.AuthenticateAsServerAsync(_cert, true, SslProtocols.Default, false); + await sslStream.AuthenticateAsServerAsync(_cert, clientCertificateRequired: true, + enabledSslProtocols: SslProtocols.Default, checkCertificateRevocation: false); } context.Connection = sslStream; } diff --git a/src/Microsoft.AspNet.Server.Kestrel/Filter/ConnectionFilterContext.cs b/src/Microsoft.AspNet.Server.Kestrel/Filter/ConnectionFilterContext.cs index ab086bbf6..7b5ef1120 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Filter/ConnectionFilterContext.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Filter/ConnectionFilterContext.cs @@ -2,7 +2,7 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.IO; -using System.Security.Cryptography.X509Certificates; +using Microsoft.AspNet.Http.Features; namespace Microsoft.AspNet.Server.Kestrel.Filter { @@ -10,6 +10,6 @@ public class ConnectionFilterContext { public ServerAddress Address { get; set; } public Stream Connection { get; set; } - public X509Certificate2 ClientCertificate { get; set; } + public ITlsConnectionFeature TlsConnectionFeature { get; set; } } } diff --git a/src/Microsoft.AspNet.Server.Kestrel/Http/Connection.cs b/src/Microsoft.AspNet.Server.Kestrel/Http/Connection.cs index b317275e3..963249a2a 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Http/Connection.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Http/Connection.cs @@ -4,11 +4,11 @@ using System; using System.Net; using System.Threading; +using Microsoft.AspNet.Http.Features; using Microsoft.AspNet.Server.Kestrel.Filter; using Microsoft.AspNet.Server.Kestrel.Infrastructure; using Microsoft.AspNet.Server.Kestrel.Networking; using Microsoft.Extensions.Logging; -using System.Security.Cryptography.X509Certificates; namespace Microsoft.AspNet.Server.Kestrel.Http { @@ -34,6 +34,7 @@ public class Connection : ConnectionContext, IConnectionControl private IPEndPoint _remoteEndPoint; private IPEndPoint _localEndPoint; + private ITlsConnectionFeature _tlsConnectionFeature; public Connection(ListenerContext context, UvStreamHandle socket) : base(context) { @@ -66,7 +67,7 @@ public void Start() SocketInput = _rawSocketInput; SocketOutput = _rawSocketOutput; - _frame = CreateFrame(clientCertificate: null); + _frame = CreateFrame(); _frame.Start(); } else @@ -107,8 +108,9 @@ private void ApplyConnectionFilter() SocketInput = filteredStreamAdapter.SocketInput; SocketOutput = filteredStreamAdapter.SocketOutput; + _tlsConnectionFeature = _filterContext.TlsConnectionFeature; - _frame = CreateFrame(_filterContext.ClientCertificate); + _frame = CreateFrame(); _frame.Start(); } @@ -156,9 +158,9 @@ private void OnRead(UvStreamHandle handle, int status) _rawSocketInput.IncomingComplete(readCount, error); } - private Frame CreateFrame(X509Certificate2 clientCertificate) + private Frame CreateFrame() { - return new Frame(this, _remoteEndPoint, _localEndPoint, clientCertificate); + return new Frame(this, _remoteEndPoint, _localEndPoint, _tlsConnectionFeature); } void IConnectionControl.Pause() diff --git a/src/Microsoft.AspNet.Server.Kestrel/Http/Frame.cs b/src/Microsoft.AspNet.Server.Kestrel/Http/Frame.cs index 87cf39b25..f8bb4010f 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Http/Frame.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Http/Frame.cs @@ -14,8 +14,6 @@ using Microsoft.AspNet.Server.Kestrel.Infrastructure; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Primitives; -using System.Security.Cryptography.X509Certificates; -using Microsoft.AspNet.Http.Features.Internal; // ReSharper disable AccessToModifiedClosure @@ -51,22 +49,22 @@ public partial class Frame : FrameContext, IFrameControl private readonly IPEndPoint _localEndPoint; private readonly IPEndPoint _remoteEndPoint; - private readonly X509Certificate2 _clientCertificate; + private readonly ITlsConnectionFeature _tlsConnectionFeature; public Frame(ConnectionContext context) - : this(context, remoteEndPoint: null, localEndPoint: null, clientCertificate: null) + : this(context, remoteEndPoint: null, localEndPoint: null, tlsConnectionFeature: null) { } public Frame(ConnectionContext context, IPEndPoint remoteEndPoint, IPEndPoint localEndPoint, - X509Certificate2 clientCertificate) + ITlsConnectionFeature tlsConnectionFeature) : base(context) { _remoteEndPoint = remoteEndPoint; _localEndPoint = localEndPoint; - _clientCertificate = clientCertificate; + _tlsConnectionFeature = tlsConnectionFeature; FrameControl = this; Reset(); @@ -137,10 +135,7 @@ public void Reset() httpConnectionFeature.IsLocal = false; } - if (_clientCertificate != null) - { - _currentITlsConnectionFeature = new TlsConnectionFeature { ClientCertificate = _clientCertificate }; - } + _currentITlsConnectionFeature = _tlsConnectionFeature; } public void ResetResponseHeaders() From 26ff03de85be4efe8df8ac076088de590f49461e Mon Sep 17 00:00:00 2001 From: Master T Date: Wed, 11 Nov 2015 07:30:12 +0100 Subject: [PATCH 06/30] SslProtocols.Defaults not available on all platforms, use Tls (like dotnet5 SslStream does) --- .../HttpsConnectionFilter.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Microsoft.AspNet.Server.Kestrel.Https/HttpsConnectionFilter.cs b/src/Microsoft.AspNet.Server.Kestrel.Https/HttpsConnectionFilter.cs index 53a78163b..29f10ef51 100644 --- a/src/Microsoft.AspNet.Server.Kestrel.Https/HttpsConnectionFilter.cs +++ b/src/Microsoft.AspNet.Server.Kestrel.Https/HttpsConnectionFilter.cs @@ -68,7 +68,8 @@ public async Task OnConnection(ConnectionFilterContext context) } }); await sslStream.AuthenticateAsServerAsync(_cert, clientCertificateRequired: true, - enabledSslProtocols: SslProtocols.Default, checkCertificateRevocation: false); + enabledSslProtocols: SslProtocols.Tls12 | SslProtocols.Tls11 | SslProtocols.Tls, + checkCertificateRevocation: false); } context.Connection = sslStream; } From 449b303098d726d737b3003a47601220b040cc0b Mon Sep 17 00:00:00 2001 From: Master T Date: Wed, 11 Nov 2015 09:31:56 +0100 Subject: [PATCH 07/30] Add HttpsConnectionFilterOptions --- .../HttpsApplicationBuilderExtensions.cs | 6 +++--- .../HttpsConnectionFilter.cs | 14 +++++--------- .../HttpsConnectionFilterOptions.cs | 18 ++++++++++++++++++ .../HttpsConnectionFilterTests.cs | 3 ++- 4 files changed, 28 insertions(+), 13 deletions(-) create mode 100644 src/Microsoft.AspNet.Server.Kestrel.Https/HttpsConnectionFilterOptions.cs diff --git a/src/Microsoft.AspNet.Server.Kestrel.Https/HttpsApplicationBuilderExtensions.cs b/src/Microsoft.AspNet.Server.Kestrel.Https/HttpsApplicationBuilderExtensions.cs index 807fec88c..c595376aa 100644 --- a/src/Microsoft.AspNet.Server.Kestrel.Https/HttpsApplicationBuilderExtensions.cs +++ b/src/Microsoft.AspNet.Server.Kestrel.Https/HttpsApplicationBuilderExtensions.cs @@ -12,10 +12,10 @@ public static class HttpsApplicationBuilderExtensions { public static IApplicationBuilder UseKestrelHttps(this IApplicationBuilder app, X509Certificate2 cert) { - return app.UseKestrelHttps(cert, ClientCertificateMode.NoCertificate); + return app.UseKestrelHttps(new HttpsConnectionFilterOptions { ServerCertificate = cert}); } - public static IApplicationBuilder UseKestrelHttps(this IApplicationBuilder app, X509Certificate2 cert, ClientCertificateMode mode) + public static IApplicationBuilder UseKestrelHttps(this IApplicationBuilder app, HttpsConnectionFilterOptions options) { var serverInfo = app.ServerFeatures.Get(); @@ -26,7 +26,7 @@ public static IApplicationBuilder UseKestrelHttps(this IApplicationBuilder app, var prevFilter = serverInfo.ConnectionFilter ?? new NoOpConnectionFilter(); - serverInfo.ConnectionFilter = new HttpsConnectionFilter(cert, mode, prevFilter); + serverInfo.ConnectionFilter = new HttpsConnectionFilter(options, prevFilter); return app; } diff --git a/src/Microsoft.AspNet.Server.Kestrel.Https/HttpsConnectionFilter.cs b/src/Microsoft.AspNet.Server.Kestrel.Https/HttpsConnectionFilter.cs index 29f10ef51..71218e0aa 100644 --- a/src/Microsoft.AspNet.Server.Kestrel.Https/HttpsConnectionFilter.cs +++ b/src/Microsoft.AspNet.Server.Kestrel.Https/HttpsConnectionFilter.cs @@ -17,23 +17,19 @@ public class HttpsConnectionFilter : IConnectionFilter private readonly ClientCertificateMode _clientCertMode; private readonly IConnectionFilter _previous; - public HttpsConnectionFilter(X509Certificate2 cert, IConnectionFilter previous) : - this(cert, ClientCertificateMode.NoCertificate, previous) - { } - - public HttpsConnectionFilter(X509Certificate2 cert, ClientCertificateMode mode, IConnectionFilter previous) + public HttpsConnectionFilter(HttpsConnectionFilterOptions options, IConnectionFilter previous) { - if (cert == null) + if (options.ServerCertificate == null) { - throw new ArgumentNullException(nameof(cert)); + throw new ArgumentNullException(nameof(options.ServerCertificate)); } if (previous == null) { throw new ArgumentNullException(nameof(previous)); } - _cert = cert; - _clientCertMode = mode; + _cert = options.ServerCertificate; + _clientCertMode = options.ClientCertificateMode; _previous = previous; } diff --git a/src/Microsoft.AspNet.Server.Kestrel.Https/HttpsConnectionFilterOptions.cs b/src/Microsoft.AspNet.Server.Kestrel.Https/HttpsConnectionFilterOptions.cs new file mode 100644 index 000000000..574526387 --- /dev/null +++ b/src/Microsoft.AspNet.Server.Kestrel.Https/HttpsConnectionFilterOptions.cs @@ -0,0 +1,18 @@ +// 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.Security.Cryptography.X509Certificates; + +namespace Microsoft.AspNet.Server.Kestrel.Https +{ + public class HttpsConnectionFilterOptions + { + public HttpsConnectionFilterOptions() + { + ClientCertificateMode = ClientCertificateMode.NoCertificate; + } + + public X509Certificate2 ServerCertificate { get; set; } + public ClientCertificateMode ClientCertificateMode { get; set; } + } +} diff --git a/test/Microsoft.AspNet.Server.KestrelTests/HttpsConnectionFilterTests.cs b/test/Microsoft.AspNet.Server.KestrelTests/HttpsConnectionFilterTests.cs index b4e5dbe44..197d2d22e 100644 --- a/test/Microsoft.AspNet.Server.KestrelTests/HttpsConnectionFilterTests.cs +++ b/test/Microsoft.AspNet.Server.KestrelTests/HttpsConnectionFilterTests.cs @@ -53,7 +53,8 @@ public async Task CanReadAndWriteWithHttpsConnectionFilter() var serviceContext = new TestServiceContext() { ConnectionFilter = new HttpsConnectionFilter( - new X509Certificate2(@"TestResources/testCert.pfx", "testPassword"), + new HttpsConnectionFilterOptions + { ServerCertificate = new X509Certificate2(@"TestResources/testCert.pfx", "testPassword")}, new NoOpConnectionFilter()) }; From 0500043cc9c4464fda96103f32ca7f4ed667a5f2 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Wed, 11 Nov 2015 09:46:56 +0000 Subject: [PATCH 08/30] .Result -> .GetAwaiter().GetResult() --- src/Microsoft.AspNet.Server.Kestrel/Http/FrameRequestStream.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Microsoft.AspNet.Server.Kestrel/Http/FrameRequestStream.cs b/src/Microsoft.AspNet.Server.Kestrel/Http/FrameRequestStream.cs index 714728dd4..10e6e425c 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Http/FrameRequestStream.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Http/FrameRequestStream.cs @@ -50,7 +50,7 @@ public override void SetLength(long value) public override int Read(byte[] buffer, int offset, int count) { - return ReadAsync(buffer, offset, count).Result; + return ReadAsync(buffer, offset, count).GetAwaiter().GetResult(); } #if NET451 From ade0dbadb337b19b476f308e2f6a0ef42a11f846 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Wed, 11 Nov 2015 13:06:15 +0000 Subject: [PATCH 09/30] Slow date wait for CI Resolves #339 --- .../DateHeaderValueManagerTests.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/Microsoft.AspNet.Server.KestrelTests/DateHeaderValueManagerTests.cs b/test/Microsoft.AspNet.Server.KestrelTests/DateHeaderValueManagerTests.cs index ca4fd8ff0..ef5e31ca6 100644 --- a/test/Microsoft.AspNet.Server.KestrelTests/DateHeaderValueManagerTests.cs +++ b/test/Microsoft.AspNet.Server.KestrelTests/DateHeaderValueManagerTests.cs @@ -77,8 +77,8 @@ public async Task GetDateHeaderValue_ReturnsUpdatedValueAfterIdle() { UtcNow = now }; - var timeWithoutRequestsUntilIdle = TimeSpan.FromMilliseconds(50); - var timerInterval = TimeSpan.FromMilliseconds(10); + var timeWithoutRequestsUntilIdle = TimeSpan.FromMilliseconds(250); + var timerInterval = TimeSpan.FromMilliseconds(100); var dateHeaderValueManager = new DateHeaderValueManager(systemClock, timeWithoutRequestsUntilIdle, timerInterval); string result1; string result2; @@ -87,8 +87,8 @@ public async Task GetDateHeaderValue_ReturnsUpdatedValueAfterIdle() { result1 = dateHeaderValueManager.GetDateHeaderValue(); systemClock.UtcNow = future; - // Wait for twice the idle timeout to ensure the timer is stopped - await Task.Delay(timeWithoutRequestsUntilIdle.Add(timeWithoutRequestsUntilIdle)); + // Wait for longer than the idle timeout to ensure the timer is stopped + await Task.Delay(TimeSpan.FromSeconds(1)); result2 = dateHeaderValueManager.GetDateHeaderValue(); } finally From e5ff33eda2a581cd976dfca44fe38b19b1ec5821 Mon Sep 17 00:00:00 2001 From: Stephen Halter Date: Mon, 9 Nov 2015 19:31:46 -0800 Subject: [PATCH 10/30] Remove UvAsyncHandle.DangerousClose - This should stop the AVs we've been seeing in some of our test runs --- .../Infrastructure/KestrelThread.cs | 25 +++++++++++++++---- .../Networking/UvAsyncHandle.cs | 6 ----- 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/src/Microsoft.AspNet.Server.Kestrel/Infrastructure/KestrelThread.cs b/src/Microsoft.AspNet.Server.Kestrel/Infrastructure/KestrelThread.cs index 85a4b5062..a8608de70 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Infrastructure/KestrelThread.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Infrastructure/KestrelThread.cs @@ -67,10 +67,24 @@ public void Stop(TimeSpan timeout) Post(OnStop, null); if (!_thread.Join((int)timeout.TotalMilliseconds)) { - Post(OnStopRude, null); - if (!_thread.Join((int)timeout.TotalMilliseconds)) + try { - Post(OnStopImmediate, null); + Post(OnStopRude, null); + if (!_thread.Join((int)timeout.TotalMilliseconds)) + { + Post(OnStopImmediate, null); + if (!_thread.Join((int)timeout.TotalMilliseconds)) + { +#if NET451 + _thread.Abort(); +#endif + } + } + } + catch (ObjectDisposedException) + { + // REVIEW: Should we log something here? + // Until we rework this logic, ODEs are bound to happen sometimes. if (!_thread.Join((int)timeout.TotalMilliseconds)) { #if NET451 @@ -79,6 +93,7 @@ public void Stop(TimeSpan timeout) } } } + if (_closeError != null) { _closeError.Throw(); @@ -217,7 +232,7 @@ private void ThreadStart(object parameter) // run the loop one more time to delete the open handles _post.Reference(); - _post.DangerousClose(); + _post.Dispose(); _engine.Libuv.walk( _loop, @@ -231,7 +246,7 @@ private void ThreadStart(object parameter) }, IntPtr.Zero); - // Ensure the "DangerousClose" operation completes in the event loop. + // Ensure the Dispose operations complete in the event loop. var ran2 = _loop.Run(); _loop.Dispose(); diff --git a/src/Microsoft.AspNet.Server.Kestrel/Networking/UvAsyncHandle.cs b/src/Microsoft.AspNet.Server.Kestrel/Networking/UvAsyncHandle.cs index 9b4ecb60f..03c6ece9e 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Networking/UvAsyncHandle.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Networking/UvAsyncHandle.cs @@ -26,12 +26,6 @@ public void Init(UvLoopHandle loop, Action callback) _uv.async_init(loop, this, _uv_async_cb); } - public void DangerousClose() - { - Dispose(); - ReleaseHandle(); - } - public void Send() { _uv.async_send(this); From 86e1924cadde5342db9f7395979d6bd6490bdb34 Mon Sep 17 00:00:00 2001 From: "N. Taylor Mullen" Date: Thu, 12 Nov 2015 12:23:45 -0800 Subject: [PATCH 11/30] Remove System beta tag in project.json for coreclr packages. --- samples/SampleApp/project.json | 2 +- .../project.json | 4 +- .../project.json | 68 +++++++++---------- .../project.json | 4 +- .../project.json | 12 ++-- .../project.json | 2 +- .../project.json | 14 ++-- tools/Microsoft.StandardsPolice/project.json | 6 +- 8 files changed, 56 insertions(+), 56 deletions(-) diff --git a/samples/SampleApp/project.json b/samples/SampleApp/project.json index 751414ca5..f9e44a2a0 100644 --- a/samples/SampleApp/project.json +++ b/samples/SampleApp/project.json @@ -10,7 +10,7 @@ }, "dnxcore50": { "dependencies": { - "System.Console": "4.0.0-beta-*" + "System.Console": "4.0.0-*" } } }, diff --git a/src/Microsoft.AspNet.Server.Kestrel.Https/project.json b/src/Microsoft.AspNet.Server.Kestrel.Https/project.json index 5067c4bf9..9598ce03d 100644 --- a/src/Microsoft.AspNet.Server.Kestrel.Https/project.json +++ b/src/Microsoft.AspNet.Server.Kestrel.Https/project.json @@ -15,8 +15,8 @@ "net451": { }, "dotnet5.4": { "dependencies": { - "System.Net.Security": "4.0.0-beta-*" + "System.Net.Security": "4.0.0-*" } } } -} \ No newline at end of file +} diff --git a/src/Microsoft.AspNet.Server.Kestrel/project.json b/src/Microsoft.AspNet.Server.Kestrel/project.json index 2fb2fa965..edae68809 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/project.json +++ b/src/Microsoft.AspNet.Server.Kestrel/project.json @@ -8,7 +8,7 @@ "dependencies": { "Microsoft.AspNet.Hosting": "1.0.0-*", "Microsoft.Extensions.Logging.Abstractions": "1.0.0-*", - "System.Numerics.Vectors": "4.1.1-beta-*", + "System.Numerics.Vectors": "4.1.1-*", "Microsoft.StandardsPolice": { "version": "1.0.0-*", "type": "build" @@ -27,42 +27,42 @@ "net451": { }, "dnxcore50": { "dependencies": { - "System.Collections": "4.0.11-beta-*", - "System.Diagnostics.Debug": "4.0.11-beta-*", - "System.Diagnostics.TraceSource": "4.0.0-beta-*", - "System.Diagnostics.Tracing": "4.0.21-beta-*", - "System.Globalization": "4.0.11-beta-*", - "System.IO": "4.0.11-beta-*", - "System.Linq": "4.0.1-beta-*", - "System.Net.Primitives": "4.0.11-beta-*", - "System.Runtime.Extensions": "4.0.11-beta-*", - "System.Runtime.InteropServices": "4.0.21-beta-*", - "System.Text.Encoding": "4.0.11-beta-*", - "System.Threading": "4.0.11-beta-*", - "System.Threading.Tasks": "4.0.11-beta-*", - "System.Threading.Thread": "4.0.0-beta-*", - "System.Threading.ThreadPool": "4.0.10-beta-*", - "System.Threading.Timer": "4.0.1-beta-*" + "System.Collections": "4.0.11-*", + "System.Diagnostics.Debug": "4.0.11-*", + "System.Diagnostics.TraceSource": "4.0.0-*", + "System.Diagnostics.Tracing": "4.0.21-*", + "System.Globalization": "4.0.11-*", + "System.IO": "4.0.11-*", + "System.Linq": "4.0.1-*", + "System.Net.Primitives": "4.0.11-*", + "System.Runtime.Extensions": "4.0.11-*", + "System.Runtime.InteropServices": "4.0.21-*", + "System.Text.Encoding": "4.0.11-*", + "System.Threading": "4.0.11-*", + "System.Threading.Tasks": "4.0.11-*", + "System.Threading.Thread": "4.0.0-*", + "System.Threading.ThreadPool": "4.0.10-*", + "System.Threading.Timer": "4.0.1-*" } }, "dotnet5.4": { "dependencies": { - "System.Collections": "4.0.11-beta-*", - "System.Diagnostics.Debug": "4.0.11-beta-*", - "System.Diagnostics.TraceSource": "4.0.0-beta-*", - "System.Diagnostics.Tracing": "4.0.21-beta-*", - "System.Globalization": "4.0.11-beta-*", - "System.IO": "4.0.11-beta-*", - "System.Linq": "4.0.1-beta-*", - "System.Net.Primitives": "4.0.11-beta-*", - "System.Runtime.Extensions": "4.0.11-beta-*", - "System.Runtime.InteropServices": "4.0.21-beta-*", - "System.Text.Encoding": "4.0.11-beta-*", - "System.Threading": "4.0.11-beta-*", - "System.Threading.Tasks": "4.0.11-beta-*", - "System.Threading.Thread": "4.0.0-beta-*", - "System.Threading.ThreadPool": "4.0.10-beta-*", - "System.Threading.Timer": "4.0.1-beta-*" + "System.Collections": "4.0.11-*", + "System.Diagnostics.Debug": "4.0.11-*", + "System.Diagnostics.TraceSource": "4.0.0-*", + "System.Diagnostics.Tracing": "4.0.21-*", + "System.Globalization": "4.0.11-*", + "System.IO": "4.0.11-*", + "System.Linq": "4.0.1-*", + "System.Net.Primitives": "4.0.11-*", + "System.Runtime.Extensions": "4.0.11-*", + "System.Runtime.InteropServices": "4.0.21-*", + "System.Text.Encoding": "4.0.11-*", + "System.Threading": "4.0.11-*", + "System.Threading.Tasks": "4.0.11-*", + "System.Threading.Thread": "4.0.0-*", + "System.Threading.ThreadPool": "4.0.10-*", + "System.Threading.Timer": "4.0.1-*" } } }, @@ -87,4 +87,4 @@ "runtimes/win10-arm/native/": "runtimes/win10-arm/native/*", "runtimes/osx/native/": "runtimes/osx/native/*" } -} \ No newline at end of file +} diff --git a/test/Microsoft.AspNet.Server.Kestrel.FunctionalTests/project.json b/test/Microsoft.AspNet.Server.Kestrel.FunctionalTests/project.json index 7ecc7fb8a..4c78cd4ac 100644 --- a/test/Microsoft.AspNet.Server.Kestrel.FunctionalTests/project.json +++ b/test/Microsoft.AspNet.Server.Kestrel.FunctionalTests/project.json @@ -1,9 +1,9 @@ -{ +{ "version": "1.0.0-*", "dependencies": { "Microsoft.AspNet.Http.Abstractions": "1.0.0-*", "Microsoft.AspNet.Server.Kestrel": "1.0.0-*", - "System.Net.Http": "4.0.1-beta-*", + "System.Net.Http": "4.0.1-*", "xunit.runner.aspnet": "2.0.0-aspnet-*" }, "frameworks": { diff --git a/test/Microsoft.AspNet.Server.KestrelTests/project.json b/test/Microsoft.AspNet.Server.KestrelTests/project.json index 730c809d1..af2652e3a 100644 --- a/test/Microsoft.AspNet.Server.KestrelTests/project.json +++ b/test/Microsoft.AspNet.Server.KestrelTests/project.json @@ -5,17 +5,17 @@ "Microsoft.AspNet.Server.Kestrel": "1.0.0-*", "Microsoft.AspNet.Server.Kestrel.Https": "1.0.0-*", "Microsoft.AspNet.Testing": "1.0.0-*", - "System.Net.Http": "4.0.1-beta-*" + "System.Net.Http": "4.0.1-*" }, "frameworks": { "dnx451": { }, "dnxcore50": { "dependencies": { - "System.Diagnostics.TraceSource": "4.0.0-beta-*", - "System.IO": "4.0.11-beta-*", - "System.Net.Http.WinHttpHandler": "4.0.0-beta-*", - "System.Net.Sockets": "4.1.0-beta-*", - "System.Runtime.Handles": "4.0.1-beta-*" + "System.Diagnostics.TraceSource": "4.0.0-*", + "System.IO": "4.0.11-*", + "System.Net.Http.WinHttpHandler": "4.0.0-*", + "System.Net.Sockets": "4.1.0-*", + "System.Runtime.Handles": "4.0.1-*" } } }, diff --git a/tools/Microsoft.AspNet.Server.Kestrel.GeneratedCode/project.json b/tools/Microsoft.AspNet.Server.Kestrel.GeneratedCode/project.json index 465bd1b86..ede3a70f5 100644 --- a/tools/Microsoft.AspNet.Server.Kestrel.GeneratedCode/project.json +++ b/tools/Microsoft.AspNet.Server.Kestrel.GeneratedCode/project.json @@ -21,7 +21,7 @@ }, "dnxcore50": { "dependencies": { - "System.Console": "4.0.0-beta-*" + "System.Console": "4.0.0-*" } } } diff --git a/tools/Microsoft.AspNet.Server.Kestrel.LibuvCopier/project.json b/tools/Microsoft.AspNet.Server.Kestrel.LibuvCopier/project.json index 373002baf..a9b68f0ec 100644 --- a/tools/Microsoft.AspNet.Server.Kestrel.LibuvCopier/project.json +++ b/tools/Microsoft.AspNet.Server.Kestrel.LibuvCopier/project.json @@ -1,4 +1,4 @@ -{ +{ "version": "1.0.0-*", "description": "Microsoft.AspNet.Server.Kestrel.LibuvCopier Console Application", "authors": [ "pawelka" ], @@ -19,12 +19,12 @@ "dnx451": { }, "dnxcore50": { "dependencies": { - "Microsoft.CSharp": "4.0.1-beta-*", - "System.Collections": "4.0.11-beta-*", - "System.Console": "4.0.0-beta-*", - "System.Linq": "4.0.1-beta-*", - "System.Threading": "4.0.11-beta-*", - "System.IO.FileSystem": "4.0.1-beta-*" + "Microsoft.CSharp": "4.0.1-*", + "System.Collections": "4.0.11-*", + "System.Console": "4.0.0-*", + "System.Linq": "4.0.1-*", + "System.Threading": "4.0.11-*", + "System.IO.FileSystem": "4.0.1-*" } } } diff --git a/tools/Microsoft.StandardsPolice/project.json b/tools/Microsoft.StandardsPolice/project.json index 01f7813a3..7f24c4760 100644 --- a/tools/Microsoft.StandardsPolice/project.json +++ b/tools/Microsoft.StandardsPolice/project.json @@ -1,4 +1,4 @@ -{ +{ "version": "1.0.0-*", "compilationOptions": { @@ -32,12 +32,12 @@ }, "dotnet5.4": { "dependencies": { - "System.Console": "4.0.0-beta-*" + "System.Console": "4.0.0-*" } }, "dnxcore50": { "dependencies": { - "System.Console": "4.0.0-beta-*" + "System.Console": "4.0.0-*" } } } From 358e94faaf82fb544995cc2bd5b61f8b04b12619 Mon Sep 17 00:00:00 2001 From: Master T Date: Thu, 12 Nov 2015 22:09:33 +0100 Subject: [PATCH 12/30] Rewrite certificate validation callback, add tests where client does not provide a certificate and mode is set to Allow/RequireCertificate --- .../HttpsConnectionFilter.cs | 16 +-- .../HttpsConnectionFilterTests.cs | 97 ++++++++++++++++++- 2 files changed, 104 insertions(+), 9 deletions(-) diff --git a/src/Microsoft.AspNet.Server.Kestrel.Https/HttpsConnectionFilter.cs b/src/Microsoft.AspNet.Server.Kestrel.Https/HttpsConnectionFilter.cs index 71218e0aa..fc21e21ed 100644 --- a/src/Microsoft.AspNet.Server.Kestrel.Https/HttpsConnectionFilter.cs +++ b/src/Microsoft.AspNet.Server.Kestrel.Https/HttpsConnectionFilter.cs @@ -8,6 +8,7 @@ using Microsoft.AspNet.Server.Kestrel.Filter; using System.Security.Authentication; using Microsoft.AspNet.Http.Features.Internal; +using System.Diagnostics; namespace Microsoft.AspNet.Server.Kestrel.Https { @@ -50,18 +51,21 @@ public async Task OnConnection(ConnectionFilterContext context) sslStream = new SslStream(context.Connection, leaveInnerStreamOpen: false, userCertificateValidationCallback: (sender, certificate, chain, sslPolicyErrors) => { - context.TlsConnectionFeature = new TlsConnectionFeature() - { - ClientCertificate = certificate as X509Certificate2 - }; if (sslPolicyErrors.HasFlag(SslPolicyErrors.RemoteCertificateNotAvailable)) { return _clientCertMode != ClientCertificateMode.RequireCertificate; } - else + + if (sslPolicyErrors != SslPolicyErrors.None) { - return sslPolicyErrors == SslPolicyErrors.None; + return false; } + + context.TlsConnectionFeature = new TlsConnectionFeature() + { + ClientCertificate = certificate as X509Certificate2 ?? new X509Certificate2(certificate) + }; + return true; }); await sslStream.AuthenticateAsServerAsync(_cert, clientCertificateRequired: true, enabledSslProtocols: SslProtocols.Tls12 | SslProtocols.Tls11 | SslProtocols.Tls, diff --git a/test/Microsoft.AspNet.Server.KestrelTests/HttpsConnectionFilterTests.cs b/test/Microsoft.AspNet.Server.KestrelTests/HttpsConnectionFilterTests.cs index 197d2d22e..9d86e1829 100644 --- a/test/Microsoft.AspNet.Server.KestrelTests/HttpsConnectionFilterTests.cs +++ b/test/Microsoft.AspNet.Server.KestrelTests/HttpsConnectionFilterTests.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. #if DNX451 +using System; using System.Collections.Generic; using System.Net; using System.Net.Http; @@ -9,6 +10,7 @@ using System.Security.Cryptography.X509Certificates; using System.Threading.Tasks; using Microsoft.AspNet.Http; +using Microsoft.AspNet.Http.Features; using Microsoft.AspNet.Server.Kestrel.Filter; using Microsoft.AspNet.Server.Kestrel.Https; using Microsoft.AspNet.Testing.xunit; @@ -49,7 +51,7 @@ public async Task CanReadAndWriteWithHttpsConnectionFilter() { ServicePointManager.ServerCertificateValidationCallback += validationCallback; - var sereverAddress = "https://localhost:54321/"; + var serverAddress = "https://localhost:54321/"; var serviceContext = new TestServiceContext() { ConnectionFilter = new HttpsConnectionFilter( @@ -58,11 +60,11 @@ public async Task CanReadAndWriteWithHttpsConnectionFilter() new NoOpConnectionFilter()) }; - using (var server = new TestServer(App, serviceContext, sereverAddress)) + using (var server = new TestServer(App, serviceContext, serverAddress)) { using (var client = new HttpClient()) { - var result = await client.PostAsync(sereverAddress, new FormUrlEncodedContent(new[] { + var result = await client.PostAsync(serverAddress, new FormUrlEncodedContent(new[] { new KeyValuePair("content", "Hello World?") })); @@ -75,6 +77,95 @@ public async Task CanReadAndWriteWithHttpsConnectionFilter() ServicePointManager.ServerCertificateValidationCallback -= validationCallback; } } + + // https://github.com/aspnet/KestrelHttpServer/issues/240 + // This test currently fails on mono because of an issue with SslStream. + [ConditionalFact] + [OSSkipCondition(OperatingSystems.Linux)] + [OSSkipCondition(OperatingSystems.MacOSX)] + public async Task RequireCertificateFailsWhenNoCertificate() + { + RemoteCertificateValidationCallback validationCallback = + (sender, cert, chain, sslPolicyErrors) => true; + + try + { + ServicePointManager.ServerCertificateValidationCallback += validationCallback; + + var serverAddress = "https://localhost:54321/"; + var serviceContext = new TestServiceContext() + { + ConnectionFilter = new HttpsConnectionFilter( + new HttpsConnectionFilterOptions + { + ServerCertificate = new X509Certificate2(@"TestResources/testCert.pfx", "testPassword"), + ClientCertificateMode = ClientCertificateMode.RequireCertificate + }, + new NoOpConnectionFilter()) + }; + + using (var server = new TestServer(App, serviceContext, serverAddress)) + { + using (var client = new HttpClient()) + { + await Assert.ThrowsAnyAsync( + () => client.GetAsync(serverAddress)); + } + } + } + finally + { + ServicePointManager.ServerCertificateValidationCallback -= validationCallback; + } + } + + // https://github.com/aspnet/KestrelHttpServer/issues/240 + // This test currently fails on mono because of an issue with SslStream. + [ConditionalFact] + [OSSkipCondition(OperatingSystems.Linux)] + [OSSkipCondition(OperatingSystems.MacOSX)] + public async Task AllowCertificateContinuesWhenNoCertificate() + { + RemoteCertificateValidationCallback validationCallback = + (sender, cert, chain, sslPolicyErrors) => true; + + try + { + ServicePointManager.ServerCertificateValidationCallback += validationCallback; + + var serverAddress = "https://localhost:54321/"; + var serviceContext = new TestServiceContext() + { + ConnectionFilter = new HttpsConnectionFilter( + new HttpsConnectionFilterOptions + { + ServerCertificate = new X509Certificate2(@"TestResources/testCert.pfx", "testPassword"), + ClientCertificateMode = ClientCertificateMode.AllowCertificate + }, + new NoOpConnectionFilter()) + }; + + RequestDelegate app = context => + { + Assert.Equal(context.Features.Get(), null); + return context.Response.WriteAsync("hello world"); + }; + + using (var server = new TestServer(app, serviceContext, serverAddress)) + { + using (var client = new HttpClient()) + { + var result = await client.GetAsync(serverAddress); + + Assert.Equal("hello world", await result.Content.ReadAsStringAsync()); + } + } + } + finally + { + ServicePointManager.ServerCertificateValidationCallback -= validationCallback; + } + } } } #endif From 9e62d65787f6d9c69e5aac50b8d4356028fd9119 Mon Sep 17 00:00:00 2001 From: Master T Date: Thu, 12 Nov 2015 23:53:59 +0100 Subject: [PATCH 13/30] Add ClientCertificateValidationCallback option, add test to see the certificate is passed to the HttpContext --- .../ClientCertificateValidationCallback.cs | 12 ++++ .../HttpsConnectionFilter.cs | 21 +++++- .../HttpsConnectionFilterOptions.cs | 1 + .../HttpsConnectionFilterTests.cs | 67 +++++++++++++++++++ 4 files changed, 98 insertions(+), 3 deletions(-) create mode 100644 src/Microsoft.AspNet.Server.Kestrel.Https/ClientCertificateValidationCallback.cs diff --git a/src/Microsoft.AspNet.Server.Kestrel.Https/ClientCertificateValidationCallback.cs b/src/Microsoft.AspNet.Server.Kestrel.Https/ClientCertificateValidationCallback.cs new file mode 100644 index 000000000..432cc7af7 --- /dev/null +++ b/src/Microsoft.AspNet.Server.Kestrel.Https/ClientCertificateValidationCallback.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.Security; +using System.Security.Cryptography.X509Certificates; +using System.Threading.Tasks; + +namespace Microsoft.AspNet.Server.Kestrel.Https +{ + public delegate bool ClientCertificateValidationCallback( + X509Certificate2 certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors); +} diff --git a/src/Microsoft.AspNet.Server.Kestrel.Https/HttpsConnectionFilter.cs b/src/Microsoft.AspNet.Server.Kestrel.Https/HttpsConnectionFilter.cs index fc21e21ed..1b014043b 100644 --- a/src/Microsoft.AspNet.Server.Kestrel.Https/HttpsConnectionFilter.cs +++ b/src/Microsoft.AspNet.Server.Kestrel.Https/HttpsConnectionFilter.cs @@ -16,6 +16,7 @@ public class HttpsConnectionFilter : IConnectionFilter { private readonly X509Certificate2 _cert; private readonly ClientCertificateMode _clientCertMode; + private readonly ClientCertificateValidationCallback _clientValidationCallback; private readonly IConnectionFilter _previous; public HttpsConnectionFilter(HttpsConnectionFilterOptions options, IConnectionFilter previous) @@ -31,6 +32,7 @@ public HttpsConnectionFilter(HttpsConnectionFilterOptions options, IConnectionFi _cert = options.ServerCertificate; _clientCertMode = options.ClientCertificateMode; + _clientValidationCallback = options.ClientCertificateValidation; _previous = previous; } @@ -56,14 +58,27 @@ public async Task OnConnection(ConnectionFilterContext context) return _clientCertMode != ClientCertificateMode.RequireCertificate; } - if (sslPolicyErrors != SslPolicyErrors.None) + + if (_clientValidationCallback == null) + { + if (sslPolicyErrors != SslPolicyErrors.None) + { + return false; + } + } + X509Certificate2 certificate2 = certificate as X509Certificate2 ?? + new X509Certificate2(certificate); + if (_clientValidationCallback != null) { - return false; + if (!_clientValidationCallback(certificate2, chain, sslPolicyErrors)) + { + return false; + } } context.TlsConnectionFeature = new TlsConnectionFeature() { - ClientCertificate = certificate as X509Certificate2 ?? new X509Certificate2(certificate) + ClientCertificate = certificate2 }; return true; }); diff --git a/src/Microsoft.AspNet.Server.Kestrel.Https/HttpsConnectionFilterOptions.cs b/src/Microsoft.AspNet.Server.Kestrel.Https/HttpsConnectionFilterOptions.cs index 574526387..62c513c21 100644 --- a/src/Microsoft.AspNet.Server.Kestrel.Https/HttpsConnectionFilterOptions.cs +++ b/src/Microsoft.AspNet.Server.Kestrel.Https/HttpsConnectionFilterOptions.cs @@ -14,5 +14,6 @@ public HttpsConnectionFilterOptions() public X509Certificate2 ServerCertificate { get; set; } public ClientCertificateMode ClientCertificateMode { get; set; } + public ClientCertificateValidationCallback ClientCertificateValidation { get; set; } } } diff --git a/test/Microsoft.AspNet.Server.KestrelTests/HttpsConnectionFilterTests.cs b/test/Microsoft.AspNet.Server.KestrelTests/HttpsConnectionFilterTests.cs index 9d86e1829..a72c88137 100644 --- a/test/Microsoft.AspNet.Server.KestrelTests/HttpsConnectionFilterTests.cs +++ b/test/Microsoft.AspNet.Server.KestrelTests/HttpsConnectionFilterTests.cs @@ -4,10 +4,13 @@ #if DNX451 using System; using System.Collections.Generic; +using System.IO; using System.Net; using System.Net.Http; using System.Net.Security; +using System.Net.Sockets; using System.Security.Cryptography.X509Certificates; +using System.Text; using System.Threading.Tasks; using Microsoft.AspNet.Http; using Microsoft.AspNet.Http.Features; @@ -166,6 +169,70 @@ public async Task AllowCertificateContinuesWhenNoCertificate() ServicePointManager.ServerCertificateValidationCallback -= validationCallback; } } + + // https://github.com/aspnet/KestrelHttpServer/issues/240 + // This test currently fails on mono because of an issue with SslStream. + [ConditionalFact] + [OSSkipCondition(OperatingSystems.Linux)] + [OSSkipCondition(OperatingSystems.MacOSX)] + public async Task CertificatePassedToHttpContext() + { + RemoteCertificateValidationCallback validationCallback = + (sender, cert, chain, sslPolicyErrors) => true; + + try + { + ServicePointManager.ServerCertificateValidationCallback += validationCallback; + + var serverAddress = "https://localhost:54321/"; + var serviceContext = new TestServiceContext() + { + ConnectionFilter = new HttpsConnectionFilter( + new HttpsConnectionFilterOptions + { + ServerCertificate = new X509Certificate2(@"TestResources/testCert.pfx", "testPassword"), + ClientCertificateMode = ClientCertificateMode.RequireCertificate, + ClientCertificateValidation = (certificate, chain, sslPolicyErrors) => true + }, + new NoOpConnectionFilter()) + }; + + RequestDelegate app = context => + { + var tlsFeature = context.Features.Get(); + Assert.NotNull(tlsFeature); + Assert.NotNull(tlsFeature.ClientCertificate); + Assert.NotNull(context.Connection.ClientCertificate); + return context.Response.WriteAsync("hello world"); + }; + + using (var server = new TestServer(app, serviceContext, serverAddress)) + { + // SslStream is used to ensure the certificate is actually passed to the server + // HttpClient might not send the certificate because it is invalid or it doesn't match any + // of the certificate authorities sent by the server in the SSL handshake. + using (var client = new TcpClient()) + { + await client.ConnectAsync("127.0.0.1", 54321); + + SslStream stream = new SslStream(client.GetStream(), false, (sender, certificate, chain, errors) => true, + (sender, host, certificates, certificate, issuers) => new X509Certificate2(@"TestResources/testCert.pfx", "testPassword")); + await stream.AuthenticateAsClientAsync("localhost"); + + var request = Encoding.UTF8.GetBytes("GET / HTTP/1.0\r\n\r\n"); + await stream.WriteAsync(request, 0, request.Length); + + var reader = new StreamReader(stream); + var line = await reader.ReadLineAsync(); + Assert.Equal("HTTP/1.0 200 OK", line); + } + } + } + finally + { + ServicePointManager.ServerCertificateValidationCallback -= validationCallback; + } + } } } #endif From cee178fc6c09bc6852fecc002d5fcb563ffd897a Mon Sep 17 00:00:00 2001 From: Master T Date: Fri, 13 Nov 2015 00:04:13 +0100 Subject: [PATCH 14/30] Code cleanup --- .../ClientCertificateValidationCallback.cs | 7 +++---- .../HttpsConnectionFilter.cs | 5 ++--- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/Microsoft.AspNet.Server.Kestrel.Https/ClientCertificateValidationCallback.cs b/src/Microsoft.AspNet.Server.Kestrel.Https/ClientCertificateValidationCallback.cs index 432cc7af7..12616900c 100644 --- a/src/Microsoft.AspNet.Server.Kestrel.Https/ClientCertificateValidationCallback.cs +++ b/src/Microsoft.AspNet.Server.Kestrel.Https/ClientCertificateValidationCallback.cs @@ -1,9 +1,8 @@ -using System; -using System.Collections.Generic; -using System.Linq; +// 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.Net.Security; using System.Security.Cryptography.X509Certificates; -using System.Threading.Tasks; namespace Microsoft.AspNet.Server.Kestrel.Https { diff --git a/src/Microsoft.AspNet.Server.Kestrel.Https/HttpsConnectionFilter.cs b/src/Microsoft.AspNet.Server.Kestrel.Https/HttpsConnectionFilter.cs index 1b014043b..e5f588e2a 100644 --- a/src/Microsoft.AspNet.Server.Kestrel.Https/HttpsConnectionFilter.cs +++ b/src/Microsoft.AspNet.Server.Kestrel.Https/HttpsConnectionFilter.cs @@ -3,12 +3,11 @@ using System; using System.Net.Security; +using System.Security.Authentication; using System.Security.Cryptography.X509Certificates; using System.Threading.Tasks; -using Microsoft.AspNet.Server.Kestrel.Filter; -using System.Security.Authentication; using Microsoft.AspNet.Http.Features.Internal; -using System.Diagnostics; +using Microsoft.AspNet.Server.Kestrel.Filter; namespace Microsoft.AspNet.Server.Kestrel.Https { From 59cdd60af68b0c1ab050c2b6fb6422c0d42a6b1c Mon Sep 17 00:00:00 2001 From: Stephen Halter Date: Tue, 10 Nov 2015 15:44:00 -0800 Subject: [PATCH 15/30] Don't pre-complete too many writes --- .../Http/SocketOutput.cs | 4 +- .../SocketOutputTests.cs | 78 +++++++++++++++++++ 2 files changed, 81 insertions(+), 1 deletion(-) diff --git a/src/Microsoft.AspNet.Server.Kestrel/Http/SocketOutput.cs b/src/Microsoft.AspNet.Server.Kestrel/Http/SocketOutput.cs index 609d59ef3..2871341db 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Http/SocketOutput.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Http/SocketOutput.cs @@ -203,8 +203,10 @@ private void OnWriteCompleted(Queue> writtenBuffers, int stat (int)(_tasksPending.Peek().Task.AsyncState) <= bytesLeftToBuffer) { var tcs = _tasksPending.Dequeue(); + var bytesToWrite = (int)tcs.Task.AsyncState; - _numBytesPreCompleted += (int)(tcs.Task.AsyncState); + _numBytesPreCompleted += bytesToWrite; + bytesLeftToBuffer -= bytesToWrite; if (error == null) { diff --git a/test/Microsoft.AspNet.Server.KestrelTests/SocketOutputTests.cs b/test/Microsoft.AspNet.Server.KestrelTests/SocketOutputTests.cs index faf311634..d8d4f5cee 100644 --- a/test/Microsoft.AspNet.Server.KestrelTests/SocketOutputTests.cs +++ b/test/Microsoft.AspNet.Server.KestrelTests/SocketOutputTests.cs @@ -192,6 +192,84 @@ public void WritesDontCompleteImmediatelyWhenTooManyBytesIncludingNonImmediateAr } } + [Fact] + public void WritesDontGetCompletedTooQuickly() + { + // This should match _maxBytesPreCompleted in SocketOutput + var maxBytesPreCompleted = 65536; + var completeQueue = new Queue>(); + var onWriteWh = new ManualResetEventSlim(); + + // Arrange + var mockLibuv = new MockLibuv + { + OnWrite = (socket, buffers, triggerCompleted) => + { + completeQueue.Enqueue(triggerCompleted); + onWriteWh.Set(); + + return 0; + } + }; + + using (var kestrelEngine = new KestrelEngine(mockLibuv, new TestServiceContext())) + { + kestrelEngine.Start(count: 1); + + var kestrelThread = kestrelEngine.Threads[0]; + var socket = new MockSocket(kestrelThread.Loop.ThreadId, new TestKestrelTrace()); + var trace = new KestrelTrace(new TestKestrelTrace()); + var socketOutput = new SocketOutput(kestrelThread, socket, 0, trace); + + var bufferSize = maxBytesPreCompleted; + var buffer = new ArraySegment(new byte[bufferSize], 0, bufferSize); + + var completedWh = new ManualResetEventSlim(); + Action onCompleted = (Task t) => + { + Assert.Null(t.Exception); + completedWh.Set(); + }; + + var completedWh2 = new ManualResetEventSlim(); + Action onCompleted2 = (Task t) => + { + Assert.Null(t.Exception); + completedWh2.Set(); + }; + + // Act (Pre-complete the maximum number of bytes in preparation for the rest of the test) + socketOutput.WriteAsync(buffer).ContinueWith(onCompleted); + // Assert + // The first write should pre-complete since it is <= _maxBytesPreCompleted. + Assert.True(completedWh.Wait(1000)); + Assert.True(onWriteWh.Wait(1000)); + // Arrange + completedWh.Reset(); + onWriteWh.Reset(); + + // Act + socketOutput.WriteAsync(buffer).ContinueWith(onCompleted); + socketOutput.WriteAsync(buffer).ContinueWith(onCompleted2); + + Assert.True(onWriteWh.Wait(1000)); + completeQueue.Dequeue()(0); + + // Assert + // Too many bytes are already pre-completed for the third but not the second write to pre-complete. + // https://github.com/aspnet/KestrelHttpServer/issues/356 + Assert.True(completedWh.Wait(1000)); + Assert.False(completedWh2.Wait(1000)); + + // Act + completeQueue.Dequeue()(0); + + // Assert + // Finishing the first write should allow the second write to pre-complete. + Assert.True(completedWh2.Wait(1000)); + } + } + private class MockSocket : UvStreamHandle { public MockSocket(int threadId, IKestrelTrace logger) : base(logger) From fc346f7768f7b9a4a7ae921434cfee45972324b0 Mon Sep 17 00:00:00 2001 From: Stephen Halter Date: Tue, 10 Nov 2015 15:46:28 -0800 Subject: [PATCH 16/30] Avoid some closure allocations in SocketOutput --- src/Microsoft.AspNet.Server.Kestrel/Http/SocketOutput.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Microsoft.AspNet.Server.Kestrel/Http/SocketOutput.cs b/src/Microsoft.AspNet.Server.Kestrel/Http/SocketOutput.cs index 2871341db..d2534206e 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Http/SocketOutput.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Http/SocketOutput.cs @@ -293,7 +293,7 @@ public void DoWriteIfNeeded() var _this = (WriteContext)state; _this.WriteStatus = status; _this.WriteError = error; - DoShutdownIfNeeded(); + _this.DoShutdownIfNeeded(); }, this); } @@ -316,9 +316,9 @@ public void DoShutdownIfNeeded() var _this = (WriteContext)state; _this.ShutdownSendStatus = status; - Self._log.ConnectionWroteFin(Self._connectionId, status); + _this.Self._log.ConnectionWroteFin(Self._connectionId, status); - DoDisconnectIfNeeded(); + _this.DoDisconnectIfNeeded(); }, this); } From 52fe46968832e6c0520c6f90b79fb3d8d1d3db3b Mon Sep 17 00:00:00 2001 From: Murat Girgin Date: Thu, 12 Nov 2015 22:59:34 -0800 Subject: [PATCH 17/30] Update project.json --- src/Microsoft.AspNet.Server.Kestrel/project.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Microsoft.AspNet.Server.Kestrel/project.json b/src/Microsoft.AspNet.Server.Kestrel/project.json index edae68809..1582dd0e1 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/project.json +++ b/src/Microsoft.AspNet.Server.Kestrel/project.json @@ -1,6 +1,6 @@ { "version": "1.0.0-*", - "description": "ASP.NET 5 cross platform development web server.", + "description": "ASP.NET 5 cross platform web server.", "repository": { "type": "git", "url": "git://github.com/aspnet/kestrelhttpserver" From dd1ffa5f84674441b3fbe7b3c931504598f5bc1a Mon Sep 17 00:00:00 2001 From: Cesar Blum Silveira Date: Wed, 11 Nov 2015 05:41:28 -0800 Subject: [PATCH 18/30] Skip some tests on Mono to prevent CI hangs. --- .travis.yml | 10 ++- .../AddressRegistrationTests.cs | 35 +++++++-- .../IPv6SupportedConditionAttribute.cs | 48 ++++++++++++ .../RequestTests.cs | 15 ++-- .../ResponseTests.cs | 4 +- .../project.json | 2 + .../ChunkedResponseTests.cs | 16 ++-- .../ConnectionFilterTests.cs | 9 ++- .../EngineTests.cs | 75 +++++++++++++------ 9 files changed, 169 insertions(+), 45 deletions(-) create mode 100644 test/Microsoft.AspNet.Server.Kestrel.FunctionalTests/IPv6SupportedConditionAttribute.cs diff --git a/.travis.yml b/.travis.yml index 1218a0fc0..eef144e73 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,10 @@ language: csharp -sudo: false +sudo: required +dist: trusty +addons: + apt: + packages: + - libunwind8 install: - curl -sSL https://github.com/libuv/libuv/archive/v1.4.2.tar.gz | tar zxfv - -C /tmp && cd /tmp/libuv-1.4.2/ - sh autogen.sh @@ -9,4 +14,5 @@ install: - export LD_LIBRARY_PATH="$HOME/libuvinstall/lib" - cd $OLDPWD script: - - ./build.sh --quiet verify \ No newline at end of file + - export KOREBUILD_TEST_DNXCORE=1 + - ./build.sh --quiet verify diff --git a/test/Microsoft.AspNet.Server.Kestrel.FunctionalTests/AddressRegistrationTests.cs b/test/Microsoft.AspNet.Server.Kestrel.FunctionalTests/AddressRegistrationTests.cs index 00f6db432..9921a3d7f 100644 --- a/test/Microsoft.AspNet.Server.Kestrel.FunctionalTests/AddressRegistrationTests.cs +++ b/test/Microsoft.AspNet.Server.Kestrel.FunctionalTests/AddressRegistrationTests.cs @@ -8,6 +8,7 @@ using Microsoft.AspNet.Hosting; using Microsoft.AspNet.Http; using Microsoft.AspNet.Http.Extensions; +using Microsoft.AspNet.Testing.xunit; using Microsoft.Extensions.Configuration; using Xunit; @@ -15,7 +16,21 @@ namespace Microsoft.AspNet.Server.Kestrel.FunctionalTests { public class AddressRegistrationTests { - [Theory, MemberData(nameof(AddressRegistrationData))] + [ConditionalTheory, MemberData(nameof(AddressRegistrationDataIPv4))] + [FrameworkSkipCondition(RuntimeFrameworks.Mono, SkipReason = "Test hangs after execution on Mono.")] + public async Task RegisterAddresses_IPv4_Success(string addressInput, string[] testUrls) + { + await RegisterAddresses_Success(addressInput, testUrls); + } + + [ConditionalTheory, MemberData(nameof(AddressRegistrationDataIPv6))] + [FrameworkSkipCondition(RuntimeFrameworks.Mono, SkipReason = "Test hangs after execution on Mono.")] + [IPv6SupportedCondition] + public async Task RegisterAddresses_IPv6_Success(string addressInput, string[] testUrls) + { + await RegisterAddresses_Success(addressInput, testUrls); + } + public async Task RegisterAddresses_Success(string addressInput, string[] testUrls) { var config = new ConfigurationBuilder() @@ -27,7 +42,7 @@ public async Task RegisterAddresses_Success(string addressInput, string[] testUr var hostBuilder = new WebHostBuilder(config); hostBuilder.UseServerFactory("Microsoft.AspNet.Server.Kestrel"); - hostBuilder.UseStartup(ConfigureEchoAddress); + hostBuilder.UseStartup(ConfigureEchoAddress); using (var app = hostBuilder.Build().Start()) { @@ -42,22 +57,32 @@ public async Task RegisterAddresses_Success(string addressInput, string[] testUr } } - public static TheoryData AddressRegistrationData + public static TheoryData AddressRegistrationDataIPv4 { get { var dataset = new TheoryData(); dataset.Add("8787", new[] { "http://localhost:8787/" }); dataset.Add("8787;8788", new[] { "http://localhost:8787/", "http://localhost:8788/" }); + dataset.Add("http://127.0.0.1:8787/", new[] { "http://127.0.0.1:8787/", }); + dataset.Add("http://localhost:8787/base/path", new[] { "http://localhost:8787/base/path" }); + + return dataset; + } + } + + public static TheoryData AddressRegistrationDataIPv6 + { + get + { + var dataset = new TheoryData(); dataset.Add("http://*:8787/", new[] { "http://localhost:8787/", "http://127.0.0.1:8787/", "http://[::1]:8787/" }); dataset.Add("http://localhost:8787/", new[] { "http://localhost:8787/", "http://127.0.0.1:8787/", /* // https://github.com/aspnet/KestrelHttpServer/issues/231 "http://[::1]:8787/" */ }); - dataset.Add("http://127.0.0.1:8787/", new[] { "http://127.0.0.1:8787/", }); dataset.Add("http://[::1]:8787/", new[] { "http://[::1]:8787/", }); dataset.Add("http://127.0.0.1:8787/;http://[::1]:8787/", new[] { "http://127.0.0.1:8787/", "http://[::1]:8787/" }); - dataset.Add("http://localhost:8787/base/path", new[] { "http://localhost:8787/base/path" }); return dataset; } diff --git a/test/Microsoft.AspNet.Server.Kestrel.FunctionalTests/IPv6SupportedConditionAttribute.cs b/test/Microsoft.AspNet.Server.Kestrel.FunctionalTests/IPv6SupportedConditionAttribute.cs new file mode 100644 index 000000000..8415f0f04 --- /dev/null +++ b/test/Microsoft.AspNet.Server.Kestrel.FunctionalTests/IPv6SupportedConditionAttribute.cs @@ -0,0 +1,48 @@ +// 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.Net; +using System.Net.Sockets; +using Microsoft.AspNet.Testing.xunit; + +namespace Microsoft.AspNet.Server.Kestrel.FunctionalTests +{ + [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)] + public class IPv6SupportedConditionAttribute : Attribute, ITestCondition + { + private static Lazy _ipv6Supported = new Lazy(CanBindToIPv6Address); + + public bool IsMet + { + get + { + return _ipv6Supported.Value; + } + } + + public string SkipReason + { + get + { + return "IPv6 not supported on the host."; + } + } + + private static bool CanBindToIPv6Address() + { + try + { + using (var socket = new Socket(AddressFamily.InterNetworkV6, SocketType.Stream, ProtocolType.Tcp)) + { + socket.Bind(new IPEndPoint(IPAddress.IPv6Loopback, 8787)); + return true; + } + } + catch (SocketException) + { + return false; + } + } + } +} diff --git a/test/Microsoft.AspNet.Server.Kestrel.FunctionalTests/RequestTests.cs b/test/Microsoft.AspNet.Server.Kestrel.FunctionalTests/RequestTests.cs index 1c75f43a7..c125da04b 100644 --- a/test/Microsoft.AspNet.Server.Kestrel.FunctionalTests/RequestTests.cs +++ b/test/Microsoft.AspNet.Server.Kestrel.FunctionalTests/RequestTests.cs @@ -8,6 +8,7 @@ using Microsoft.AspNet.Builder; using Microsoft.AspNet.Hosting; using Microsoft.AspNet.Http; +using Microsoft.AspNet.Testing.xunit; using Microsoft.Extensions.Configuration; using Newtonsoft.Json; using Newtonsoft.Json.Linq; @@ -17,7 +18,8 @@ namespace Microsoft.AspNet.Server.Kestrel.FunctionalTests { public class RequestTests { - [Fact] + [ConditionalFact] + [FrameworkSkipCondition(RuntimeFrameworks.Mono, SkipReason = "Test hangs after execution on Mono.")] public async Task LargeUpload() { var config = new ConfigurationBuilder() @@ -69,15 +71,18 @@ public async Task LargeUpload() } } - [Theory] + [ConditionalTheory] [InlineData("127.0.0.1", "127.0.0.1", "8792")] [InlineData("localhost", "127.0.0.1", "8792")] + [FrameworkSkipCondition(RuntimeFrameworks.Mono, SkipReason = "Test hangs after execution on Mono.")] public Task RemoteIPv4Address(string requestAddress, string expectAddress, string port) { return TestRemoteIPAddress("localhost", requestAddress, expectAddress, port); } - [Fact] + [ConditionalFact] + [FrameworkSkipCondition(RuntimeFrameworks.Mono, SkipReason = "Test hangs after execution on Mono.")] + [IPv6SupportedCondition] public Task RemoteIPv6Address() { return TestRemoteIPAddress("[::1]", "[::1]", "::1", "8792"); @@ -92,7 +97,7 @@ private async Task TestRemoteIPAddress(string registerAddress, string requestAdd var builder = new WebHostBuilder(config) .UseServerFactory("Microsoft.AspNet.Server.Kestrel") - .UseStartup(app => + .UseStartup(app => { app.Run(async context => { @@ -124,4 +129,4 @@ await context.Response.WriteAsync(JsonConvert.SerializeObject(new } } } -} \ No newline at end of file +} diff --git a/test/Microsoft.AspNet.Server.Kestrel.FunctionalTests/ResponseTests.cs b/test/Microsoft.AspNet.Server.Kestrel.FunctionalTests/ResponseTests.cs index 245729d94..c2479c388 100644 --- a/test/Microsoft.AspNet.Server.Kestrel.FunctionalTests/ResponseTests.cs +++ b/test/Microsoft.AspNet.Server.Kestrel.FunctionalTests/ResponseTests.cs @@ -6,6 +6,7 @@ using System.Threading.Tasks; using Microsoft.AspNet.Builder; using Microsoft.AspNet.Hosting; +using Microsoft.AspNet.Testing.xunit; using Microsoft.Extensions.Configuration; using Xunit; @@ -13,7 +14,8 @@ namespace Microsoft.AspNet.Server.Kestrel.FunctionalTests { public class ResponseTests { - [Fact] + [ConditionalFact] + [FrameworkSkipCondition(RuntimeFrameworks.Mono, SkipReason = "Test hangs after execution on mono.")] public async Task LargeDownload() { var config = new ConfigurationBuilder() diff --git a/test/Microsoft.AspNet.Server.Kestrel.FunctionalTests/project.json b/test/Microsoft.AspNet.Server.Kestrel.FunctionalTests/project.json index 4c78cd4ac..b97b9efc4 100644 --- a/test/Microsoft.AspNet.Server.Kestrel.FunctionalTests/project.json +++ b/test/Microsoft.AspNet.Server.Kestrel.FunctionalTests/project.json @@ -3,7 +3,9 @@ "dependencies": { "Microsoft.AspNet.Http.Abstractions": "1.0.0-*", "Microsoft.AspNet.Server.Kestrel": "1.0.0-*", + "Microsoft.AspNet.Testing": "1.0.0-*", "System.Net.Http": "4.0.1-*", + "System.Net.NameResolution": "4.0.0-*", "xunit.runner.aspnet": "2.0.0-aspnet-*" }, "frameworks": { diff --git a/test/Microsoft.AspNet.Server.KestrelTests/ChunkedResponseTests.cs b/test/Microsoft.AspNet.Server.KestrelTests/ChunkedResponseTests.cs index ca01df5f4..ad0a9120c 100644 --- a/test/Microsoft.AspNet.Server.KestrelTests/ChunkedResponseTests.cs +++ b/test/Microsoft.AspNet.Server.KestrelTests/ChunkedResponseTests.cs @@ -4,13 +4,15 @@ using System; using System.Text; using System.Threading.Tasks; +using Microsoft.AspNet.Testing.xunit; using Xunit; namespace Microsoft.AspNet.Server.KestrelTests { public class ChunkedResponseTests { - [Fact] + [ConditionalFact] + [FrameworkSkipCondition(RuntimeFrameworks.Mono, SkipReason = "Test hangs after execution on Mono.")] public async Task ResponsesAreChunkedAutomatically() { using (var server = new TestServer(async httpContext => @@ -42,7 +44,8 @@ await connection.ReceiveEnd( } } - [Fact] + [ConditionalFact] + [FrameworkSkipCondition(RuntimeFrameworks.Mono, SkipReason = "Test hangs after execution on Mono.")] public async Task ZeroLengthWritesAreIgnored() { using (var server = new TestServer(async httpContext => @@ -75,7 +78,8 @@ await connection.ReceiveEnd( } } - [Fact] + [ConditionalFact] + [FrameworkSkipCondition(RuntimeFrameworks.Mono, SkipReason = "Test hangs after execution on Mono.")] public async Task EmptyResponseBodyHandledCorrectlyWithZeroLengthWrite() { using (var server = new TestServer(async httpContext => @@ -102,7 +106,8 @@ await connection.ReceiveEnd( } } - [Fact] + [ConditionalFact] + [FrameworkSkipCondition(RuntimeFrameworks.Mono, SkipReason = "Test hangs after execution on Mono.")] public async Task ConnectionClosedIfExeptionThrownAfterWrite() { using (var server = new TestServer(async httpContext => @@ -132,7 +137,8 @@ await connection.ReceiveEnd( } } - [Fact] + [ConditionalFact] + [FrameworkSkipCondition(RuntimeFrameworks.Mono, SkipReason = "Test hangs after execution on Mono.")] public async Task ConnectionClosedIfExeptionThrownAfterZeroLengthWrite() { using (var server = new TestServer(async httpContext => diff --git a/test/Microsoft.AspNet.Server.KestrelTests/ConnectionFilterTests.cs b/test/Microsoft.AspNet.Server.KestrelTests/ConnectionFilterTests.cs index d4dfc5fe9..5842e1d7c 100644 --- a/test/Microsoft.AspNet.Server.KestrelTests/ConnectionFilterTests.cs +++ b/test/Microsoft.AspNet.Server.KestrelTests/ConnectionFilterTests.cs @@ -3,9 +3,10 @@ using System.IO; using System.Threading.Tasks; -using Microsoft.AspNet.Server.Kestrel.Filter; using Microsoft.AspNet.Http; using Microsoft.AspNet.Http.Features; +using Microsoft.AspNet.Server.Kestrel.Filter; +using Microsoft.AspNet.Testing.xunit; using Xunit; namespace Microsoft.AspNet.Server.KestrelTests @@ -29,7 +30,8 @@ private async Task App(HttpContext httpContext) } } - [Fact] + [ConditionalFact] + [FrameworkSkipCondition(RuntimeFrameworks.Mono, SkipReason = "Test hangs after execution on Mono.")] public async Task CanReadAndWriteWithRewritingConnectionFilter() { var filter = new RewritingConnectionFilter(); @@ -55,7 +57,8 @@ await connection.ReceiveEnd( Assert.Equal(sendString.Length, filter.BytesRead); } - [Fact] + [ConditionalFact] + [FrameworkSkipCondition(RuntimeFrameworks.Mono, SkipReason = "Test hangs after execution on Mono.")] public async Task CanReadAndWriteWithAsyncConnectionFilter() { var serviceContext = new TestServiceContext() diff --git a/test/Microsoft.AspNet.Server.KestrelTests/EngineTests.cs b/test/Microsoft.AspNet.Server.KestrelTests/EngineTests.cs index c89297337..d5893e214 100644 --- a/test/Microsoft.AspNet.Server.KestrelTests/EngineTests.cs +++ b/test/Microsoft.AspNet.Server.KestrelTests/EngineTests.cs @@ -10,6 +10,7 @@ using Microsoft.AspNet.Http; using Microsoft.AspNet.Server.Kestrel; using Microsoft.AspNet.Server.Kestrel.Filter; +using Microsoft.AspNet.Testing.xunit; using Microsoft.Extensions.Logging; using Xunit; @@ -75,8 +76,9 @@ private Task EmptyApp(HttpContext httpContext) return Task.FromResult(null); } - [Theory] + [ConditionalTheory] [MemberData(nameof(ConnectionFilterData))] + [FrameworkSkipCondition(RuntimeFrameworks.Mono, SkipReason = "Test hangs after execution on Mono.")] public void EngineCanStartAndStop(ServiceContext testContext) { var engine = new KestrelEngine(testContext); @@ -84,8 +86,9 @@ public void EngineCanStartAndStop(ServiceContext testContext) engine.Dispose(); } - [Theory] + [ConditionalTheory] [MemberData(nameof(ConnectionFilterData))] + [FrameworkSkipCondition(RuntimeFrameworks.Mono, SkipReason = "Test hangs after execution on Mono.")] public void ListenerCanCreateAndDispose(ServiceContext testContext) { var engine = new KestrelEngine(testContext); @@ -96,8 +99,9 @@ public void ListenerCanCreateAndDispose(ServiceContext testContext) engine.Dispose(); } - [Theory] + [ConditionalTheory] [MemberData(nameof(ConnectionFilterData))] + [FrameworkSkipCondition(RuntimeFrameworks.Mono, SkipReason = "Test hangs after execution on Mono.")] public void ConnectionCanReadAndWrite(ServiceContext testContext) { var engine = new KestrelEngine(testContext); @@ -122,8 +126,9 @@ public void ConnectionCanReadAndWrite(ServiceContext testContext) } - [Theory] + [ConditionalTheory] [MemberData(nameof(ConnectionFilterData))] + [FrameworkSkipCondition(RuntimeFrameworks.Mono, SkipReason = "Test hangs after execution on Mono.")] public async Task Http10(ServiceContext testContext) { using (var server = new TestServer(App, testContext)) @@ -143,8 +148,9 @@ await connection.ReceiveEnd( } - [Theory] + [ConditionalTheory] [MemberData(nameof(ConnectionFilterData))] + [FrameworkSkipCondition(RuntimeFrameworks.Mono, SkipReason = "Test hangs after execution on Mono.")] public async Task Http11(ServiceContext testContext) { using (var server = new TestServer(AppChunked, testContext)) @@ -171,8 +177,9 @@ await connection.ReceiveEnd( } } - [Theory] + [ConditionalTheory] [MemberData(nameof(ConnectionFilterData))] + [FrameworkSkipCondition(RuntimeFrameworks.Mono, SkipReason = "Test hangs after execution on Mono.")] public async Task Http10ContentLength(ServiceContext testContext) { using (var server = new TestServer(App, testContext)) @@ -192,8 +199,9 @@ await connection.ReceiveEnd( } } - [Theory] + [ConditionalTheory] [MemberData(nameof(ConnectionFilterData))] + [FrameworkSkipCondition(RuntimeFrameworks.Mono, SkipReason = "Test hangs after execution on Mono.")] public async Task Http10TransferEncoding(ServiceContext testContext) { using (var server = new TestServer(App, testContext)) @@ -213,8 +221,9 @@ await connection.ReceiveEnd( } } - [Theory] + [ConditionalTheory] [MemberData(nameof(ConnectionFilterData))] + [FrameworkSkipCondition(RuntimeFrameworks.Mono, SkipReason = "Test hangs after execution on Mono.")] public async Task Http10KeepAlive(ServiceContext testContext) { using (var server = new TestServer(AppChunked, testContext)) @@ -242,8 +251,9 @@ await connection.ReceiveEnd( } } - [Theory] + [ConditionalTheory] [MemberData(nameof(ConnectionFilterData))] + [FrameworkSkipCondition(RuntimeFrameworks.Mono, SkipReason = "Test hangs after execution on Mono.")] public async Task Http10KeepAliveNotUsedIfResponseContentLengthNotSet(ServiceContext testContext) { using (var server = new TestServer(App, testContext)) @@ -272,8 +282,9 @@ await connection.ReceiveEnd( } } - [Theory] + [ConditionalTheory] [MemberData(nameof(ConnectionFilterData))] + [FrameworkSkipCondition(RuntimeFrameworks.Mono, SkipReason = "Test hangs after execution on Mono.")] public async Task Http10KeepAliveContentLength(ServiceContext testContext) { using (var server = new TestServer(AppChunked, testContext)) @@ -303,8 +314,9 @@ await connection.ReceiveEnd( } } - [Theory] + [ConditionalTheory] [MemberData(nameof(ConnectionFilterData))] + [FrameworkSkipCondition(RuntimeFrameworks.Mono, SkipReason = "Test hangs after execution on Mono.")] public async Task Http10KeepAliveTransferEncoding(ServiceContext testContext) { using (var server = new TestServer(AppChunked, testContext)) @@ -335,8 +347,9 @@ await connection.ReceiveEnd( } } - [Theory] + [ConditionalTheory] [MemberData(nameof(ConnectionFilterData))] + [FrameworkSkipCondition(RuntimeFrameworks.Mono, SkipReason = "Test hangs after execution on Mono.")] public async Task Expect100ContinueForBody(ServiceContext testContext) { using (var server = new TestServer(AppChunked, testContext)) @@ -361,8 +374,9 @@ await connection.Receive( } } - [Theory] + [ConditionalTheory] [MemberData(nameof(ConnectionFilterData))] + [FrameworkSkipCondition(RuntimeFrameworks.Mono, SkipReason = "Test hangs after execution on Mono.")] public async Task DisconnectingClient(ServiceContext testContext) { using (var server = new TestServer(App, testContext)) @@ -385,8 +399,9 @@ await connection.ReceiveEnd( } } - [Theory] + [ConditionalTheory] [MemberData(nameof(ConnectionFilterData))] + [FrameworkSkipCondition(RuntimeFrameworks.Mono, SkipReason = "Test hangs after execution on Mono.")] public async Task ZeroContentLengthSetAutomaticallyAfterNoWrites(ServiceContext testContext) { using (var server = new TestServer(EmptyApp, testContext)) @@ -413,8 +428,9 @@ await connection.ReceiveEnd( } } - [Theory] + [ConditionalTheory] [MemberData(nameof(ConnectionFilterData))] + [FrameworkSkipCondition(RuntimeFrameworks.Mono, SkipReason = "Test hangs after execution on Mono.")] public async Task ZeroContentLengthNotSetAutomaticallyForNonKeepAliveRequests(ServiceContext testContext) { using (var server = new TestServer(EmptyApp, testContext)) @@ -447,8 +463,9 @@ await connection.ReceiveEnd( } } - [Theory] + [ConditionalTheory] [MemberData(nameof(ConnectionFilterData))] + [FrameworkSkipCondition(RuntimeFrameworks.Mono, SkipReason = "Test hangs after execution on Mono.")] public async Task ZeroContentLengthNotSetAutomaticallyForHeadRequests(ServiceContext testContext) { using (var server = new TestServer(EmptyApp, testContext)) @@ -467,8 +484,9 @@ await connection.ReceiveEnd( } } - [Theory] + [ConditionalTheory] [MemberData(nameof(ConnectionFilterData))] + [FrameworkSkipCondition(RuntimeFrameworks.Mono, SkipReason = "Test hangs after execution on Mono.")] public async Task ZeroContentLengthNotSetAutomaticallyForCertainStatusCodes(ServiceContext testContext) { using (var server = new TestServer(async httpContext => @@ -520,8 +538,9 @@ await connection.ReceiveEnd( } } - [Theory] + [ConditionalTheory] [MemberData(nameof(ConnectionFilterData))] + [FrameworkSkipCondition(RuntimeFrameworks.Mono, SkipReason = "Test hangs after execution on Mono.")] public async Task ThrowingResultsIn500Response(ServiceContext testContext) { bool onStartingCalled = false; @@ -577,8 +596,9 @@ await connection.ReceiveEnd( } } - [Theory] + [ConditionalTheory] [MemberData(nameof(ConnectionFilterData))] + [FrameworkSkipCondition(RuntimeFrameworks.Mono, SkipReason = "Test hangs after execution on Mono.")] public async Task ThrowingAfterWritingKillsConnection(ServiceContext testContext) { bool onStartingCalled = false; @@ -619,8 +639,9 @@ await connection.ReceiveEnd( } } - [Theory] + [ConditionalTheory] [MemberData(nameof(ConnectionFilterData))] + [FrameworkSkipCondition(RuntimeFrameworks.Mono, SkipReason = "Test hangs after execution on Mono.")] public async Task ThrowingAfterPartialWriteKillsConnection(ServiceContext testContext) { bool onStartingCalled = false; @@ -661,8 +682,9 @@ await connection.ReceiveEnd( } } - [Theory] + [ConditionalTheory] [MemberData(nameof(ConnectionFilterData))] + [FrameworkSkipCondition(RuntimeFrameworks.Mono, SkipReason = "Test hangs after execution on Mono.")] public async Task ConnectionClosesWhenFinReceived(ServiceContext testContext) { using (var server = new TestServer(AppChunked, testContext)) @@ -688,8 +710,9 @@ await connection.ReceiveEnd( } } - [Theory] + [ConditionalTheory] [MemberData(nameof(ConnectionFilterData))] + [FrameworkSkipCondition(RuntimeFrameworks.Mono, SkipReason = "Test hangs after execution on Mono.")] public async Task ConnectionClosesWhenFinReceivedBeforeRequestCompletes(ServiceContext testContext) { using (var server = new TestServer(AppChunked, testContext)) @@ -730,8 +753,9 @@ await connection.ReceiveEnd( } } - [Theory] + [ConditionalTheory] [MemberData(nameof(ConnectionFilterData))] + [FrameworkSkipCondition(RuntimeFrameworks.Mono, SkipReason = "Test hangs after execution on Mono.")] public async Task ThrowingInOnStartingResultsInFailedWritesAnd500Response(ServiceContext testContext) { var onStartingCallCount1 = 0; @@ -803,7 +827,9 @@ await connection.ReceiveEnd( } } + [ConditionalTheory] [MemberData(nameof(ConnectionFilterData))] + [FrameworkSkipCondition(RuntimeFrameworks.Mono, SkipReason = "Test hangs after execution on Mono.")] public async Task ThrowingInOnCompletedIsLoggedAndClosesConnection(ServiceContext testContext) { var onCompletedCalled1 = false; @@ -852,8 +878,9 @@ await connection.ReceiveEnd( } } - [Theory] + [ConditionalTheory] [MemberData(nameof(ConnectionFilterData))] + [FrameworkSkipCondition(RuntimeFrameworks.Mono, SkipReason = "Test hangs after execution on Mono.")] public async Task RequestBodyIsConsumedAutomaticallyIfAppDoesntConsumeItFully(ServiceContext testContext) { using (var server = new TestServer(async httpContext => From 2ac5e4c790ced5f12813d2d6725126216afbb072 Mon Sep 17 00:00:00 2001 From: Cesar Blum Silveira Date: Fri, 13 Nov 2015 11:31:25 -0800 Subject: [PATCH 19/30] Remove unneeded dependency System.Net.NameResolution. --- .../Microsoft.AspNet.Server.Kestrel.FunctionalTests/project.json | 1 - 1 file changed, 1 deletion(-) diff --git a/test/Microsoft.AspNet.Server.Kestrel.FunctionalTests/project.json b/test/Microsoft.AspNet.Server.Kestrel.FunctionalTests/project.json index b97b9efc4..1e06cdc21 100644 --- a/test/Microsoft.AspNet.Server.Kestrel.FunctionalTests/project.json +++ b/test/Microsoft.AspNet.Server.Kestrel.FunctionalTests/project.json @@ -5,7 +5,6 @@ "Microsoft.AspNet.Server.Kestrel": "1.0.0-*", "Microsoft.AspNet.Testing": "1.0.0-*", "System.Net.Http": "4.0.1-*", - "System.Net.NameResolution": "4.0.0-*", "xunit.runner.aspnet": "2.0.0-aspnet-*" }, "frameworks": { From a3a49d21b83c0855a1a339f4fd3ed52a0425c524 Mon Sep 17 00:00:00 2001 From: Cesar Blum Silveira Date: Fri, 13 Nov 2015 14:29:03 -0800 Subject: [PATCH 20/30] Reset Frame.Scheme on each request (#366). --- .../Http/Frame.FeatureCollection.cs | 5 ++-- .../Http/Frame.cs | 2 ++ .../FrameFacts.cs | 26 +++++++++++++++++++ 3 files changed, 30 insertions(+), 3 deletions(-) create mode 100644 test/Microsoft.AspNet.Server.KestrelTests/FrameFacts.cs diff --git a/src/Microsoft.AspNet.Server.Kestrel/Http/Frame.FeatureCollection.cs b/src/Microsoft.AspNet.Server.Kestrel/Http/Frame.FeatureCollection.cs index 2159d7005..8cac73a94 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Http/Frame.FeatureCollection.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Http/Frame.FeatureCollection.cs @@ -24,7 +24,6 @@ public partial class Frame : IFeatureCollection, // then the list of `implementedFeatures` in the generated code project MUST also be updated. // See also: tools/Microsoft.AspNet.Server.Kestrel.GeneratedCode/FrameFeatureCollection.cs - private string _scheme; private string _pathBase; private int _featureRevision; @@ -90,12 +89,12 @@ string IHttpRequestFeature.Scheme { get { - return _scheme ?? "http"; + return Scheme ?? "http"; } set { - _scheme = value; + Scheme = value; } } diff --git a/src/Microsoft.AspNet.Server.Kestrel/Http/Frame.cs b/src/Microsoft.AspNet.Server.Kestrel/Http/Frame.cs index c08d0eb68..c13826964 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Http/Frame.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Http/Frame.cs @@ -67,6 +67,7 @@ public Frame(ConnectionContext context, Reset(); } + public string Scheme { get; set; } public string Method { get; set; } public string RequestUri { get; set; } public string Path { get; set; } @@ -102,6 +103,7 @@ public void Reset() ResetResponseHeaders(); ResetFeatureCollection(); + Scheme = null; Method = null; RequestUri = null; Path = null; diff --git a/test/Microsoft.AspNet.Server.KestrelTests/FrameFacts.cs b/test/Microsoft.AspNet.Server.KestrelTests/FrameFacts.cs new file mode 100644 index 000000000..d76c015fa --- /dev/null +++ b/test/Microsoft.AspNet.Server.KestrelTests/FrameFacts.cs @@ -0,0 +1,26 @@ +// 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 Microsoft.AspNet.Http.Features; +using Microsoft.AspNet.Server.Kestrel.Http; +using Xunit; + +namespace Microsoft.AspNet.Server.KestrelTests +{ + public class FrameFacts + { + [Fact] + public void ResetResetsScheme() + { + // Arrange + var frame = new Frame(new ConnectionContext() { DateHeaderValueManager = new DateHeaderValueManager() }); + frame.Scheme = "https"; + + // Act + frame.Reset(); + + // Assert + Assert.Equal("http", frame.Get().Scheme); + } + } +} From 5c52b8a11fa519ecff0d33b727b61801648a9038 Mon Sep 17 00:00:00 2001 From: Master T Date: Sat, 14 Nov 2015 03:19:39 +0100 Subject: [PATCH 21/30] Add IConnectionFilter.PrepareRequest and use it to set the ITlsConnectionFeature in HttpsConnectionFilter --- .../HttpsConnectionFilter.cs | 26 +++++++++++++------ .../Filter/ConnectionFilterContext.cs | 1 - .../Filter/IConnectionFilter.cs | 2 ++ .../Filter/NoOpConnectionFilter.cs | 4 +++ .../Http/Connection.cs | 5 +--- .../Http/Frame.cs | 11 ++++---- .../ConnectionFilterTests.cs | 6 +++++ 7 files changed, 37 insertions(+), 18 deletions(-) diff --git a/src/Microsoft.AspNet.Server.Kestrel.Https/HttpsConnectionFilter.cs b/src/Microsoft.AspNet.Server.Kestrel.Https/HttpsConnectionFilter.cs index e5f588e2a..b7ef21f7d 100644 --- a/src/Microsoft.AspNet.Server.Kestrel.Https/HttpsConnectionFilter.cs +++ b/src/Microsoft.AspNet.Server.Kestrel.Https/HttpsConnectionFilter.cs @@ -6,6 +6,7 @@ using System.Security.Authentication; using System.Security.Cryptography.X509Certificates; using System.Threading.Tasks; +using Microsoft.AspNet.Http.Features; using Microsoft.AspNet.Http.Features.Internal; using Microsoft.AspNet.Server.Kestrel.Filter; @@ -13,10 +14,11 @@ namespace Microsoft.AspNet.Server.Kestrel.Https { public class HttpsConnectionFilter : IConnectionFilter { - private readonly X509Certificate2 _cert; + private readonly X509Certificate2 _serverCert; private readonly ClientCertificateMode _clientCertMode; private readonly ClientCertificateValidationCallback _clientValidationCallback; private readonly IConnectionFilter _previous; + private X509Certificate2 _clientCert; public HttpsConnectionFilter(HttpsConnectionFilterOptions options, IConnectionFilter previous) { @@ -29,7 +31,7 @@ public HttpsConnectionFilter(HttpsConnectionFilterOptions options, IConnectionFi throw new ArgumentNullException(nameof(previous)); } - _cert = options.ServerCertificate; + _serverCert = options.ServerCertificate; _clientCertMode = options.ClientCertificateMode; _clientValidationCallback = options.ClientCertificateValidation; _previous = previous; @@ -45,7 +47,7 @@ public async Task OnConnection(ConnectionFilterContext context) if (_clientCertMode == ClientCertificateMode.NoCertificate) { sslStream = new SslStream(context.Connection); - await sslStream.AuthenticateAsServerAsync(_cert); + await sslStream.AuthenticateAsServerAsync(_serverCert); } else { @@ -75,18 +77,26 @@ public async Task OnConnection(ConnectionFilterContext context) } } - context.TlsConnectionFeature = new TlsConnectionFeature() - { - ClientCertificate = certificate2 - }; + _clientCert = certificate2; return true; }); - await sslStream.AuthenticateAsServerAsync(_cert, clientCertificateRequired: true, + await sslStream.AuthenticateAsServerAsync(_serverCert, clientCertificateRequired: true, enabledSslProtocols: SslProtocols.Tls12 | SslProtocols.Tls11 | SslProtocols.Tls, checkCertificateRevocation: false); } context.Connection = sslStream; } } + + public void PrepareRequest(IFeatureCollection features) + { + _previous.PrepareRequest(features); + + if (_clientCert != null) + { + features.Set( + new TlsConnectionFeature { ClientCertificate = _clientCert }); + } + } } } diff --git a/src/Microsoft.AspNet.Server.Kestrel/Filter/ConnectionFilterContext.cs b/src/Microsoft.AspNet.Server.Kestrel/Filter/ConnectionFilterContext.cs index 7b5ef1120..278ca5825 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Filter/ConnectionFilterContext.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Filter/ConnectionFilterContext.cs @@ -10,6 +10,5 @@ public class ConnectionFilterContext { public ServerAddress Address { get; set; } public Stream Connection { get; set; } - public ITlsConnectionFeature TlsConnectionFeature { get; set; } } } diff --git a/src/Microsoft.AspNet.Server.Kestrel/Filter/IConnectionFilter.cs b/src/Microsoft.AspNet.Server.Kestrel/Filter/IConnectionFilter.cs index accaa3b9d..8c4f827be 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Filter/IConnectionFilter.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Filter/IConnectionFilter.cs @@ -2,11 +2,13 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Threading.Tasks; +using Microsoft.AspNet.Http.Features; namespace Microsoft.AspNet.Server.Kestrel.Filter { public interface IConnectionFilter { Task OnConnection(ConnectionFilterContext context); + void PrepareRequest(IFeatureCollection features); } } diff --git a/src/Microsoft.AspNet.Server.Kestrel/Filter/NoOpConnectionFilter.cs b/src/Microsoft.AspNet.Server.Kestrel/Filter/NoOpConnectionFilter.cs index 65bb9e99b..798649181 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Filter/NoOpConnectionFilter.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Filter/NoOpConnectionFilter.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Threading.Tasks; +using Microsoft.AspNet.Http.Features; namespace Microsoft.AspNet.Server.Kestrel.Filter { @@ -13,5 +14,8 @@ public Task OnConnection(ConnectionFilterContext context) { return _empty; } + + public void PrepareRequest(IFeatureCollection features) + {} } } diff --git a/src/Microsoft.AspNet.Server.Kestrel/Http/Connection.cs b/src/Microsoft.AspNet.Server.Kestrel/Http/Connection.cs index 963249a2a..397e208b8 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Http/Connection.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Http/Connection.cs @@ -4,7 +4,6 @@ using System; using System.Net; using System.Threading; -using Microsoft.AspNet.Http.Features; using Microsoft.AspNet.Server.Kestrel.Filter; using Microsoft.AspNet.Server.Kestrel.Infrastructure; using Microsoft.AspNet.Server.Kestrel.Networking; @@ -34,7 +33,6 @@ public class Connection : ConnectionContext, IConnectionControl private IPEndPoint _remoteEndPoint; private IPEndPoint _localEndPoint; - private ITlsConnectionFeature _tlsConnectionFeature; public Connection(ListenerContext context, UvStreamHandle socket) : base(context) { @@ -108,7 +106,6 @@ private void ApplyConnectionFilter() SocketInput = filteredStreamAdapter.SocketInput; SocketOutput = filteredStreamAdapter.SocketOutput; - _tlsConnectionFeature = _filterContext.TlsConnectionFeature; _frame = CreateFrame(); _frame.Start(); @@ -160,7 +157,7 @@ private void OnRead(UvStreamHandle handle, int status) private Frame CreateFrame() { - return new Frame(this, _remoteEndPoint, _localEndPoint, _tlsConnectionFeature); + return new Frame(this, _remoteEndPoint, _localEndPoint, ConnectionFilter); } void IConnectionControl.Pause() diff --git a/src/Microsoft.AspNet.Server.Kestrel/Http/Frame.cs b/src/Microsoft.AspNet.Server.Kestrel/Http/Frame.cs index f8bb4010f..ec03d5fa4 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Http/Frame.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Http/Frame.cs @@ -11,6 +11,7 @@ using System.Threading.Tasks; using Microsoft.AspNet.Http; using Microsoft.AspNet.Http.Features; +using Microsoft.AspNet.Server.Kestrel.Filter; using Microsoft.AspNet.Server.Kestrel.Infrastructure; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Primitives; @@ -49,22 +50,22 @@ public partial class Frame : FrameContext, IFrameControl private readonly IPEndPoint _localEndPoint; private readonly IPEndPoint _remoteEndPoint; - private readonly ITlsConnectionFeature _tlsConnectionFeature; + private readonly IConnectionFilter _connectionFilter; public Frame(ConnectionContext context) - : this(context, remoteEndPoint: null, localEndPoint: null, tlsConnectionFeature: null) + : this(context, remoteEndPoint: null, localEndPoint: null, connectionFilter: null) { } public Frame(ConnectionContext context, IPEndPoint remoteEndPoint, IPEndPoint localEndPoint, - ITlsConnectionFeature tlsConnectionFeature) + IConnectionFilter connectionFilter) : base(context) { _remoteEndPoint = remoteEndPoint; _localEndPoint = localEndPoint; - _tlsConnectionFeature = tlsConnectionFeature; + _connectionFilter = connectionFilter; FrameControl = this; Reset(); @@ -135,7 +136,7 @@ public void Reset() httpConnectionFeature.IsLocal = false; } - _currentITlsConnectionFeature = _tlsConnectionFeature; + _connectionFilter?.PrepareRequest(this); } public void ResetResponseHeaders() diff --git a/test/Microsoft.AspNet.Server.KestrelTests/ConnectionFilterTests.cs b/test/Microsoft.AspNet.Server.KestrelTests/ConnectionFilterTests.cs index d4dfc5fe9..7931ccd91 100644 --- a/test/Microsoft.AspNet.Server.KestrelTests/ConnectionFilterTests.cs +++ b/test/Microsoft.AspNet.Server.KestrelTests/ConnectionFilterTests.cs @@ -92,6 +92,9 @@ public Task OnConnection(ConnectionFilterContext context) return _empty; } + public void PrepareRequest(IFeatureCollection frame) + {} + public int BytesRead => _rewritingStream.BytesRead; } @@ -107,6 +110,9 @@ public async Task OnConnection(ConnectionFilterContext context) context.Connection = new RewritingStream(oldConnection); } + + public void PrepareRequest(IFeatureCollection frame) + {} } private class RewritingStream : Stream From bed31a3b367bad0caa5802d32b9d74558beb32ff Mon Sep 17 00:00:00 2001 From: Master T Date: Tue, 10 Nov 2015 18:47:01 +0100 Subject: [PATCH 22/30] Implement client certificate authentication --- .../ClientCertificateMode.cs | 14 ++++++++ .../HttpsApplicationBuilderExtensions.cs | 7 +++- .../HttpsConnectionFilter.cs | 34 +++++++++++++++++-- .../Filter/ConnectionFilterContext.cs | 4 ++- .../Http/Connection.cs | 9 ++--- .../Http/Frame.cs | 14 ++++++-- 6 files changed, 71 insertions(+), 11 deletions(-) create mode 100644 src/Microsoft.AspNet.Server.Kestrel.Https/ClientCertificateMode.cs diff --git a/src/Microsoft.AspNet.Server.Kestrel.Https/ClientCertificateMode.cs b/src/Microsoft.AspNet.Server.Kestrel.Https/ClientCertificateMode.cs new file mode 100644 index 000000000..637db9b79 --- /dev/null +++ b/src/Microsoft.AspNet.Server.Kestrel.Https/ClientCertificateMode.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Microsoft.AspNet.Server.Kestrel.Https +{ + public enum ClientCertificateMode + { + NoCertificate, + AllowCertificate, + RequireCertificate + } +} diff --git a/src/Microsoft.AspNet.Server.Kestrel.Https/HttpsApplicationBuilderExtensions.cs b/src/Microsoft.AspNet.Server.Kestrel.Https/HttpsApplicationBuilderExtensions.cs index 844435c15..807fec88c 100644 --- a/src/Microsoft.AspNet.Server.Kestrel.Https/HttpsApplicationBuilderExtensions.cs +++ b/src/Microsoft.AspNet.Server.Kestrel.Https/HttpsApplicationBuilderExtensions.cs @@ -11,6 +11,11 @@ namespace Microsoft.AspNet.Server.Kestrel.Https public static class HttpsApplicationBuilderExtensions { public static IApplicationBuilder UseKestrelHttps(this IApplicationBuilder app, X509Certificate2 cert) + { + return app.UseKestrelHttps(cert, ClientCertificateMode.NoCertificate); + } + + public static IApplicationBuilder UseKestrelHttps(this IApplicationBuilder app, X509Certificate2 cert, ClientCertificateMode mode) { var serverInfo = app.ServerFeatures.Get(); @@ -21,7 +26,7 @@ public static IApplicationBuilder UseKestrelHttps(this IApplicationBuilder app, var prevFilter = serverInfo.ConnectionFilter ?? new NoOpConnectionFilter(); - serverInfo.ConnectionFilter = new HttpsConnectionFilter(cert, prevFilter); + serverInfo.ConnectionFilter = new HttpsConnectionFilter(cert, mode, prevFilter); return app; } diff --git a/src/Microsoft.AspNet.Server.Kestrel.Https/HttpsConnectionFilter.cs b/src/Microsoft.AspNet.Server.Kestrel.Https/HttpsConnectionFilter.cs index 08cad7161..031ee54ab 100644 --- a/src/Microsoft.AspNet.Server.Kestrel.Https/HttpsConnectionFilter.cs +++ b/src/Microsoft.AspNet.Server.Kestrel.Https/HttpsConnectionFilter.cs @@ -6,15 +6,21 @@ using System.Security.Cryptography.X509Certificates; using System.Threading.Tasks; using Microsoft.AspNet.Server.Kestrel.Filter; +using System.Security.Authentication; namespace Microsoft.AspNet.Server.Kestrel.Https { public class HttpsConnectionFilter : IConnectionFilter { private readonly X509Certificate2 _cert; + private readonly ClientCertificateMode _clientCertMode; private readonly IConnectionFilter _previous; - public HttpsConnectionFilter(X509Certificate2 cert, IConnectionFilter previous) + public HttpsConnectionFilter(X509Certificate2 cert, IConnectionFilter previous) : + this(cert, ClientCertificateMode.NoCertificate, previous) + { } + + public HttpsConnectionFilter(X509Certificate2 cert, ClientCertificateMode mode, IConnectionFilter previous) { if (cert == null) { @@ -26,6 +32,7 @@ public HttpsConnectionFilter(X509Certificate2 cert, IConnectionFilter previous) } _cert = cert; + _clientCertMode = mode; _previous = previous; } @@ -35,8 +42,29 @@ public async Task OnConnection(ConnectionFilterContext context) if (string.Equals(context.Address.Scheme, "https", StringComparison.OrdinalIgnoreCase)) { - var sslStream = new SslStream(context.Connection); - await sslStream.AuthenticateAsServerAsync(_cert); + SslStream sslStream; + if (_clientCertMode == ClientCertificateMode.NoCertificate) + { + sslStream = new SslStream(context.Connection); + await sslStream.AuthenticateAsServerAsync(_cert); + } + else + { + sslStream = new SslStream(context.Connection, false, + (sender, certificate, chain, sslPolicyErrors) => + { + context.ClientCertificate = certificate as X509Certificate2; + if (sslPolicyErrors.HasFlag(SslPolicyErrors.RemoteCertificateNotAvailable)) + { + return _clientCertMode != ClientCertificateMode.RequireCertificate; + } + else + { + return sslPolicyErrors == SslPolicyErrors.None; + } + }); + await sslStream.AuthenticateAsServerAsync(_cert, true, SslProtocols.Default, false); + } context.Connection = sslStream; } } diff --git a/src/Microsoft.AspNet.Server.Kestrel/Filter/ConnectionFilterContext.cs b/src/Microsoft.AspNet.Server.Kestrel/Filter/ConnectionFilterContext.cs index 937e92bec..ab086bbf6 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Filter/ConnectionFilterContext.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Filter/ConnectionFilterContext.cs @@ -2,12 +2,14 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.IO; +using System.Security.Cryptography.X509Certificates; namespace Microsoft.AspNet.Server.Kestrel.Filter { public class ConnectionFilterContext { public ServerAddress Address { get; set; } - public Stream Connection { get; set; } + public Stream Connection { get; set; } + public X509Certificate2 ClientCertificate { get; set; } } } diff --git a/src/Microsoft.AspNet.Server.Kestrel/Http/Connection.cs b/src/Microsoft.AspNet.Server.Kestrel/Http/Connection.cs index 4061f1e80..b317275e3 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Http/Connection.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Http/Connection.cs @@ -8,6 +8,7 @@ using Microsoft.AspNet.Server.Kestrel.Infrastructure; using Microsoft.AspNet.Server.Kestrel.Networking; using Microsoft.Extensions.Logging; +using System.Security.Cryptography.X509Certificates; namespace Microsoft.AspNet.Server.Kestrel.Http { @@ -65,7 +66,7 @@ public void Start() SocketInput = _rawSocketInput; SocketOutput = _rawSocketOutput; - _frame = CreateFrame(); + _frame = CreateFrame(clientCertificate: null); _frame.Start(); } else @@ -107,7 +108,7 @@ private void ApplyConnectionFilter() SocketInput = filteredStreamAdapter.SocketInput; SocketOutput = filteredStreamAdapter.SocketOutput; - _frame = CreateFrame(); + _frame = CreateFrame(_filterContext.ClientCertificate); _frame.Start(); } @@ -155,9 +156,9 @@ private void OnRead(UvStreamHandle handle, int status) _rawSocketInput.IncomingComplete(readCount, error); } - private Frame CreateFrame() + private Frame CreateFrame(X509Certificate2 clientCertificate) { - return new Frame(this, _remoteEndPoint, _localEndPoint); + return new Frame(this, _remoteEndPoint, _localEndPoint, clientCertificate); } void IConnectionControl.Pause() diff --git a/src/Microsoft.AspNet.Server.Kestrel/Http/Frame.cs b/src/Microsoft.AspNet.Server.Kestrel/Http/Frame.cs index c13826964..4395d5845 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Http/Frame.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Http/Frame.cs @@ -14,6 +14,8 @@ using Microsoft.AspNet.Server.Kestrel.Infrastructure; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Primitives; +using System.Security.Cryptography.X509Certificates; +using Microsoft.AspNet.Http.Features.Internal; // ReSharper disable AccessToModifiedClosure @@ -49,19 +51,22 @@ public partial class Frame : FrameContext, IFrameControl private readonly IPEndPoint _localEndPoint; private readonly IPEndPoint _remoteEndPoint; + private readonly X509Certificate2 _clientCertificate; public Frame(ConnectionContext context) - : this(context, remoteEndPoint: null, localEndPoint: null) + : this(context, remoteEndPoint: null, localEndPoint: null, clientCertificate: null) { } public Frame(ConnectionContext context, IPEndPoint remoteEndPoint, - IPEndPoint localEndPoint) + IPEndPoint localEndPoint, + X509Certificate2 clientCertificate) : base(context) { _remoteEndPoint = remoteEndPoint; _localEndPoint = localEndPoint; + _clientCertificate = clientCertificate; FrameControl = this; Reset(); @@ -133,6 +138,11 @@ public void Reset() { httpConnectionFeature.IsLocal = false; } + + if (_clientCertificate != null) + { + _currentITlsConnectionFeature = new TlsConnectionFeature { ClientCertificate = _clientCertificate }; + } } public void ResetResponseHeaders() From 8e56ef9e62b2e9e4b9d7745f6da0ff6cf8d1f67a Mon Sep 17 00:00:00 2001 From: Master T Date: Wed, 11 Nov 2015 07:13:10 +0100 Subject: [PATCH 23/30] Pass around ITlsConnectionFeature instead of X509Certificate2, code cleanup --- .../ClientCertificateMode.cs | 6 ++---- .../HttpsConnectionFilter.cs | 13 +++++++++---- .../Filter/ConnectionFilterContext.cs | 4 ++-- .../Http/Connection.cs | 12 +++++++----- src/Microsoft.AspNet.Server.Kestrel/Http/Frame.cs | 15 +++++---------- 5 files changed, 25 insertions(+), 25 deletions(-) diff --git a/src/Microsoft.AspNet.Server.Kestrel.Https/ClientCertificateMode.cs b/src/Microsoft.AspNet.Server.Kestrel.Https/ClientCertificateMode.cs index 637db9b79..34e8b839e 100644 --- a/src/Microsoft.AspNet.Server.Kestrel.Https/ClientCertificateMode.cs +++ b/src/Microsoft.AspNet.Server.Kestrel.Https/ClientCertificateMode.cs @@ -1,7 +1,5 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; +// 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. namespace Microsoft.AspNet.Server.Kestrel.Https { diff --git a/src/Microsoft.AspNet.Server.Kestrel.Https/HttpsConnectionFilter.cs b/src/Microsoft.AspNet.Server.Kestrel.Https/HttpsConnectionFilter.cs index 031ee54ab..53a78163b 100644 --- a/src/Microsoft.AspNet.Server.Kestrel.Https/HttpsConnectionFilter.cs +++ b/src/Microsoft.AspNet.Server.Kestrel.Https/HttpsConnectionFilter.cs @@ -7,6 +7,7 @@ using System.Threading.Tasks; using Microsoft.AspNet.Server.Kestrel.Filter; using System.Security.Authentication; +using Microsoft.AspNet.Http.Features.Internal; namespace Microsoft.AspNet.Server.Kestrel.Https { @@ -50,10 +51,13 @@ public async Task OnConnection(ConnectionFilterContext context) } else { - sslStream = new SslStream(context.Connection, false, - (sender, certificate, chain, sslPolicyErrors) => + sslStream = new SslStream(context.Connection, leaveInnerStreamOpen: false, + userCertificateValidationCallback: (sender, certificate, chain, sslPolicyErrors) => { - context.ClientCertificate = certificate as X509Certificate2; + context.TlsConnectionFeature = new TlsConnectionFeature() + { + ClientCertificate = certificate as X509Certificate2 + }; if (sslPolicyErrors.HasFlag(SslPolicyErrors.RemoteCertificateNotAvailable)) { return _clientCertMode != ClientCertificateMode.RequireCertificate; @@ -63,7 +67,8 @@ public async Task OnConnection(ConnectionFilterContext context) return sslPolicyErrors == SslPolicyErrors.None; } }); - await sslStream.AuthenticateAsServerAsync(_cert, true, SslProtocols.Default, false); + await sslStream.AuthenticateAsServerAsync(_cert, clientCertificateRequired: true, + enabledSslProtocols: SslProtocols.Default, checkCertificateRevocation: false); } context.Connection = sslStream; } diff --git a/src/Microsoft.AspNet.Server.Kestrel/Filter/ConnectionFilterContext.cs b/src/Microsoft.AspNet.Server.Kestrel/Filter/ConnectionFilterContext.cs index ab086bbf6..7b5ef1120 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Filter/ConnectionFilterContext.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Filter/ConnectionFilterContext.cs @@ -2,7 +2,7 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.IO; -using System.Security.Cryptography.X509Certificates; +using Microsoft.AspNet.Http.Features; namespace Microsoft.AspNet.Server.Kestrel.Filter { @@ -10,6 +10,6 @@ public class ConnectionFilterContext { public ServerAddress Address { get; set; } public Stream Connection { get; set; } - public X509Certificate2 ClientCertificate { get; set; } + public ITlsConnectionFeature TlsConnectionFeature { get; set; } } } diff --git a/src/Microsoft.AspNet.Server.Kestrel/Http/Connection.cs b/src/Microsoft.AspNet.Server.Kestrel/Http/Connection.cs index b317275e3..963249a2a 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Http/Connection.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Http/Connection.cs @@ -4,11 +4,11 @@ using System; using System.Net; using System.Threading; +using Microsoft.AspNet.Http.Features; using Microsoft.AspNet.Server.Kestrel.Filter; using Microsoft.AspNet.Server.Kestrel.Infrastructure; using Microsoft.AspNet.Server.Kestrel.Networking; using Microsoft.Extensions.Logging; -using System.Security.Cryptography.X509Certificates; namespace Microsoft.AspNet.Server.Kestrel.Http { @@ -34,6 +34,7 @@ public class Connection : ConnectionContext, IConnectionControl private IPEndPoint _remoteEndPoint; private IPEndPoint _localEndPoint; + private ITlsConnectionFeature _tlsConnectionFeature; public Connection(ListenerContext context, UvStreamHandle socket) : base(context) { @@ -66,7 +67,7 @@ public void Start() SocketInput = _rawSocketInput; SocketOutput = _rawSocketOutput; - _frame = CreateFrame(clientCertificate: null); + _frame = CreateFrame(); _frame.Start(); } else @@ -107,8 +108,9 @@ private void ApplyConnectionFilter() SocketInput = filteredStreamAdapter.SocketInput; SocketOutput = filteredStreamAdapter.SocketOutput; + _tlsConnectionFeature = _filterContext.TlsConnectionFeature; - _frame = CreateFrame(_filterContext.ClientCertificate); + _frame = CreateFrame(); _frame.Start(); } @@ -156,9 +158,9 @@ private void OnRead(UvStreamHandle handle, int status) _rawSocketInput.IncomingComplete(readCount, error); } - private Frame CreateFrame(X509Certificate2 clientCertificate) + private Frame CreateFrame() { - return new Frame(this, _remoteEndPoint, _localEndPoint, clientCertificate); + return new Frame(this, _remoteEndPoint, _localEndPoint, _tlsConnectionFeature); } void IConnectionControl.Pause() diff --git a/src/Microsoft.AspNet.Server.Kestrel/Http/Frame.cs b/src/Microsoft.AspNet.Server.Kestrel/Http/Frame.cs index 4395d5845..8860017b0 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Http/Frame.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Http/Frame.cs @@ -14,8 +14,6 @@ using Microsoft.AspNet.Server.Kestrel.Infrastructure; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Primitives; -using System.Security.Cryptography.X509Certificates; -using Microsoft.AspNet.Http.Features.Internal; // ReSharper disable AccessToModifiedClosure @@ -51,22 +49,22 @@ public partial class Frame : FrameContext, IFrameControl private readonly IPEndPoint _localEndPoint; private readonly IPEndPoint _remoteEndPoint; - private readonly X509Certificate2 _clientCertificate; + private readonly ITlsConnectionFeature _tlsConnectionFeature; public Frame(ConnectionContext context) - : this(context, remoteEndPoint: null, localEndPoint: null, clientCertificate: null) + : this(context, remoteEndPoint: null, localEndPoint: null, tlsConnectionFeature: null) { } public Frame(ConnectionContext context, IPEndPoint remoteEndPoint, IPEndPoint localEndPoint, - X509Certificate2 clientCertificate) + ITlsConnectionFeature tlsConnectionFeature) : base(context) { _remoteEndPoint = remoteEndPoint; _localEndPoint = localEndPoint; - _clientCertificate = clientCertificate; + _tlsConnectionFeature = tlsConnectionFeature; FrameControl = this; Reset(); @@ -139,10 +137,7 @@ public void Reset() httpConnectionFeature.IsLocal = false; } - if (_clientCertificate != null) - { - _currentITlsConnectionFeature = new TlsConnectionFeature { ClientCertificate = _clientCertificate }; - } + _currentITlsConnectionFeature = _tlsConnectionFeature; } public void ResetResponseHeaders() From f6ca379c528069e3ca37eea8c482e8f8251667c5 Mon Sep 17 00:00:00 2001 From: Master T Date: Wed, 11 Nov 2015 07:30:12 +0100 Subject: [PATCH 24/30] SslProtocols.Defaults not available on all platforms, use Tls (like dotnet5 SslStream does) --- .../HttpsConnectionFilter.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Microsoft.AspNet.Server.Kestrel.Https/HttpsConnectionFilter.cs b/src/Microsoft.AspNet.Server.Kestrel.Https/HttpsConnectionFilter.cs index 53a78163b..29f10ef51 100644 --- a/src/Microsoft.AspNet.Server.Kestrel.Https/HttpsConnectionFilter.cs +++ b/src/Microsoft.AspNet.Server.Kestrel.Https/HttpsConnectionFilter.cs @@ -68,7 +68,8 @@ public async Task OnConnection(ConnectionFilterContext context) } }); await sslStream.AuthenticateAsServerAsync(_cert, clientCertificateRequired: true, - enabledSslProtocols: SslProtocols.Default, checkCertificateRevocation: false); + enabledSslProtocols: SslProtocols.Tls12 | SslProtocols.Tls11 | SslProtocols.Tls, + checkCertificateRevocation: false); } context.Connection = sslStream; } From 6e5ea33cf70764d6039a8f3567d32103729f241c Mon Sep 17 00:00:00 2001 From: Master T Date: Wed, 11 Nov 2015 09:31:56 +0100 Subject: [PATCH 25/30] Add HttpsConnectionFilterOptions --- .../HttpsApplicationBuilderExtensions.cs | 6 +++--- .../HttpsConnectionFilter.cs | 14 +++++--------- .../HttpsConnectionFilterOptions.cs | 18 ++++++++++++++++++ .../HttpsConnectionFilterTests.cs | 3 ++- 4 files changed, 28 insertions(+), 13 deletions(-) create mode 100644 src/Microsoft.AspNet.Server.Kestrel.Https/HttpsConnectionFilterOptions.cs diff --git a/src/Microsoft.AspNet.Server.Kestrel.Https/HttpsApplicationBuilderExtensions.cs b/src/Microsoft.AspNet.Server.Kestrel.Https/HttpsApplicationBuilderExtensions.cs index 807fec88c..c595376aa 100644 --- a/src/Microsoft.AspNet.Server.Kestrel.Https/HttpsApplicationBuilderExtensions.cs +++ b/src/Microsoft.AspNet.Server.Kestrel.Https/HttpsApplicationBuilderExtensions.cs @@ -12,10 +12,10 @@ public static class HttpsApplicationBuilderExtensions { public static IApplicationBuilder UseKestrelHttps(this IApplicationBuilder app, X509Certificate2 cert) { - return app.UseKestrelHttps(cert, ClientCertificateMode.NoCertificate); + return app.UseKestrelHttps(new HttpsConnectionFilterOptions { ServerCertificate = cert}); } - public static IApplicationBuilder UseKestrelHttps(this IApplicationBuilder app, X509Certificate2 cert, ClientCertificateMode mode) + public static IApplicationBuilder UseKestrelHttps(this IApplicationBuilder app, HttpsConnectionFilterOptions options) { var serverInfo = app.ServerFeatures.Get(); @@ -26,7 +26,7 @@ public static IApplicationBuilder UseKestrelHttps(this IApplicationBuilder app, var prevFilter = serverInfo.ConnectionFilter ?? new NoOpConnectionFilter(); - serverInfo.ConnectionFilter = new HttpsConnectionFilter(cert, mode, prevFilter); + serverInfo.ConnectionFilter = new HttpsConnectionFilter(options, prevFilter); return app; } diff --git a/src/Microsoft.AspNet.Server.Kestrel.Https/HttpsConnectionFilter.cs b/src/Microsoft.AspNet.Server.Kestrel.Https/HttpsConnectionFilter.cs index 29f10ef51..71218e0aa 100644 --- a/src/Microsoft.AspNet.Server.Kestrel.Https/HttpsConnectionFilter.cs +++ b/src/Microsoft.AspNet.Server.Kestrel.Https/HttpsConnectionFilter.cs @@ -17,23 +17,19 @@ public class HttpsConnectionFilter : IConnectionFilter private readonly ClientCertificateMode _clientCertMode; private readonly IConnectionFilter _previous; - public HttpsConnectionFilter(X509Certificate2 cert, IConnectionFilter previous) : - this(cert, ClientCertificateMode.NoCertificate, previous) - { } - - public HttpsConnectionFilter(X509Certificate2 cert, ClientCertificateMode mode, IConnectionFilter previous) + public HttpsConnectionFilter(HttpsConnectionFilterOptions options, IConnectionFilter previous) { - if (cert == null) + if (options.ServerCertificate == null) { - throw new ArgumentNullException(nameof(cert)); + throw new ArgumentNullException(nameof(options.ServerCertificate)); } if (previous == null) { throw new ArgumentNullException(nameof(previous)); } - _cert = cert; - _clientCertMode = mode; + _cert = options.ServerCertificate; + _clientCertMode = options.ClientCertificateMode; _previous = previous; } diff --git a/src/Microsoft.AspNet.Server.Kestrel.Https/HttpsConnectionFilterOptions.cs b/src/Microsoft.AspNet.Server.Kestrel.Https/HttpsConnectionFilterOptions.cs new file mode 100644 index 000000000..574526387 --- /dev/null +++ b/src/Microsoft.AspNet.Server.Kestrel.Https/HttpsConnectionFilterOptions.cs @@ -0,0 +1,18 @@ +// 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.Security.Cryptography.X509Certificates; + +namespace Microsoft.AspNet.Server.Kestrel.Https +{ + public class HttpsConnectionFilterOptions + { + public HttpsConnectionFilterOptions() + { + ClientCertificateMode = ClientCertificateMode.NoCertificate; + } + + public X509Certificate2 ServerCertificate { get; set; } + public ClientCertificateMode ClientCertificateMode { get; set; } + } +} diff --git a/test/Microsoft.AspNet.Server.KestrelTests/HttpsConnectionFilterTests.cs b/test/Microsoft.AspNet.Server.KestrelTests/HttpsConnectionFilterTests.cs index cf68bd89b..d52aebe1e 100644 --- a/test/Microsoft.AspNet.Server.KestrelTests/HttpsConnectionFilterTests.cs +++ b/test/Microsoft.AspNet.Server.KestrelTests/HttpsConnectionFilterTests.cs @@ -59,7 +59,8 @@ public async Task CanReadAndWriteWithHttpsConnectionFilter() var serviceContext = new TestServiceContext() { ConnectionFilter = new HttpsConnectionFilter( - new X509Certificate2(@"TestResources/testCert.pfx", "testPassword"), + new HttpsConnectionFilterOptions + { ServerCertificate = new X509Certificate2(@"TestResources/testCert.pfx", "testPassword")}, new NoOpConnectionFilter()) }; From 35ef29fef64adc128a454d58d7f6476314f90737 Mon Sep 17 00:00:00 2001 From: Master T Date: Thu, 12 Nov 2015 22:09:33 +0100 Subject: [PATCH 26/30] Rewrite certificate validation callback, add tests where client does not provide a certificate and mode is set to Allow/RequireCertificate --- .../HttpsConnectionFilter.cs | 16 ++- .../HttpsConnectionFilterTests.cs | 114 +++++++++++++++++- 2 files changed, 120 insertions(+), 10 deletions(-) diff --git a/src/Microsoft.AspNet.Server.Kestrel.Https/HttpsConnectionFilter.cs b/src/Microsoft.AspNet.Server.Kestrel.Https/HttpsConnectionFilter.cs index 71218e0aa..fc21e21ed 100644 --- a/src/Microsoft.AspNet.Server.Kestrel.Https/HttpsConnectionFilter.cs +++ b/src/Microsoft.AspNet.Server.Kestrel.Https/HttpsConnectionFilter.cs @@ -8,6 +8,7 @@ using Microsoft.AspNet.Server.Kestrel.Filter; using System.Security.Authentication; using Microsoft.AspNet.Http.Features.Internal; +using System.Diagnostics; namespace Microsoft.AspNet.Server.Kestrel.Https { @@ -50,18 +51,21 @@ public async Task OnConnection(ConnectionFilterContext context) sslStream = new SslStream(context.Connection, leaveInnerStreamOpen: false, userCertificateValidationCallback: (sender, certificate, chain, sslPolicyErrors) => { - context.TlsConnectionFeature = new TlsConnectionFeature() - { - ClientCertificate = certificate as X509Certificate2 - }; if (sslPolicyErrors.HasFlag(SslPolicyErrors.RemoteCertificateNotAvailable)) { return _clientCertMode != ClientCertificateMode.RequireCertificate; } - else + + if (sslPolicyErrors != SslPolicyErrors.None) { - return sslPolicyErrors == SslPolicyErrors.None; + return false; } + + context.TlsConnectionFeature = new TlsConnectionFeature() + { + ClientCertificate = certificate as X509Certificate2 ?? new X509Certificate2(certificate) + }; + return true; }); await sslStream.AuthenticateAsServerAsync(_cert, clientCertificateRequired: true, enabledSslProtocols: SslProtocols.Tls12 | SslProtocols.Tls11 | SslProtocols.Tls, diff --git a/test/Microsoft.AspNet.Server.KestrelTests/HttpsConnectionFilterTests.cs b/test/Microsoft.AspNet.Server.KestrelTests/HttpsConnectionFilterTests.cs index d52aebe1e..852f34926 100644 --- a/test/Microsoft.AspNet.Server.KestrelTests/HttpsConnectionFilterTests.cs +++ b/test/Microsoft.AspNet.Server.KestrelTests/HttpsConnectionFilterTests.cs @@ -1,7 +1,7 @@ // 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; +#if DNX451 using System.Collections.Generic; using System.Net; using System.Net.Http; @@ -9,6 +9,7 @@ using System.Security.Cryptography.X509Certificates; using System.Threading.Tasks; using Microsoft.AspNet.Http; +using Microsoft.AspNet.Http.Features; using Microsoft.AspNet.Server.Kestrel.Filter; using Microsoft.AspNet.Server.Kestrel.Https; using Microsoft.AspNet.Testing.xunit; @@ -55,7 +56,7 @@ public async Task CanReadAndWriteWithHttpsConnectionFilter() handler.ServerCertificateValidationCallback += (sender, cert, chain, sslPolicyErrors) => true; #endif - var sereverAddress = "https://localhost:54321/"; + var serverAddress = "https://localhost:54321/"; var serviceContext = new TestServiceContext() { ConnectionFilter = new HttpsConnectionFilter( @@ -64,11 +65,11 @@ public async Task CanReadAndWriteWithHttpsConnectionFilter() new NoOpConnectionFilter()) }; - using (var server = new TestServer(App, serviceContext, sereverAddress)) + using (var server = new TestServer(App, serviceContext, serverAddress)) { using (var client = new HttpClient(handler)) { - var result = await client.PostAsync(sereverAddress, new FormUrlEncodedContent(new[] { + var result = await client.PostAsync(serverAddress, new FormUrlEncodedContent(new[] { new KeyValuePair("content", "Hello World?") })); @@ -80,6 +81,111 @@ public async Task CanReadAndWriteWithHttpsConnectionFilter() { #if DNX451 ServicePointManager.ServerCertificateValidationCallback -= validationCallback; +#endif + } + } + + // https://github.com/aspnet/KestrelHttpServer/issues/240 + // This test currently fails on mono because of an issue with SslStream. + [ConditionalFact] + [OSSkipCondition(OperatingSystems.Linux)] + [OSSkipCondition(OperatingSystems.MacOSX)] + public async Task RequireCertificateFailsWhenNoCertificate() + { + RemoteCertificateValidationCallback validationCallback = + (sender, cert, chain, sslPolicyErrors) => true; + + try + { +#if DNX451 + var handler = new HttpClientHandler(); + ServicePointManager.ServerCertificateValidationCallback += validationCallback; +#else + var handler = new WinHttpHandler(); + handler.ServerCertificateValidationCallback += (sender, cert, chain, sslPolicyErrors) => true; +#endif + + var serverAddress = "https://localhost:54321/"; + var serviceContext = new TestServiceContext() + { + ConnectionFilter = new HttpsConnectionFilter( + new HttpsConnectionFilterOptions + { + ServerCertificate = new X509Certificate2(@"TestResources/testCert.pfx", "testPassword"), + ClientCertificateMode = ClientCertificateMode.RequireCertificate + }, + new NoOpConnectionFilter()) + }; + + using (var server = new TestServer(App, serviceContext, serverAddress)) + { + using (var client = new HttpClient()) + { + await Assert.ThrowsAnyAsync( + () => client.GetAsync(serverAddress)); + } + } + } + finally + { +#if DNX451 + ServicePointManager.ServerCertificateValidationCallback -= validationCallback; +#endif + } + } + + // https://github.com/aspnet/KestrelHttpServer/issues/240 + // This test currently fails on mono because of an issue with SslStream. + [ConditionalFact] + [OSSkipCondition(OperatingSystems.Linux)] + [OSSkipCondition(OperatingSystems.MacOSX)] + public async Task AllowCertificateContinuesWhenNoCertificate() + { + RemoteCertificateValidationCallback validationCallback = + (sender, cert, chain, sslPolicyErrors) => true; + + try + { +#if DNX451 + var handler = new HttpClientHandler(); + ServicePointManager.ServerCertificateValidationCallback += validationCallback; +#else + var handler = new WinHttpHandler(); + handler.ServerCertificateValidationCallback += (sender, cert, chain, sslPolicyErrors) => true; +#endif + + var serverAddress = "https://localhost:54321/"; + var serviceContext = new TestServiceContext() + { + ConnectionFilter = new HttpsConnectionFilter( + new HttpsConnectionFilterOptions + { + ServerCertificate = new X509Certificate2(@"TestResources/testCert.pfx", "testPassword"), + ClientCertificateMode = ClientCertificateMode.AllowCertificate + }, + new NoOpConnectionFilter()) + }; + + RequestDelegate app = context => + { + Assert.Equal(context.Features.Get(), null); + return context.Response.WriteAsync("hello world"); + }; + + using (var server = new TestServer(app, serviceContext, serverAddress)) + { + using (var client = new HttpClient()) + { + var result = await client.GetAsync(serverAddress); + + Assert.Equal("hello world", await result.Content.ReadAsStringAsync()); + } + } + } + finally + { +#if DNX451 + ServicePointManager.ServerCertificateValidationCallback -= validationCallback; #endif } } From ea039aca5e932adad2cbe066d63a46584874fb45 Mon Sep 17 00:00:00 2001 From: Master T Date: Thu, 12 Nov 2015 23:53:59 +0100 Subject: [PATCH 27/30] Add ClientCertificateValidationCallback option, add test to see the certificate is passed to the HttpContext --- .../ClientCertificateValidationCallback.cs | 12 ++++ .../HttpsConnectionFilter.cs | 21 +++++- .../HttpsConnectionFilterOptions.cs | 1 + .../HttpsConnectionFilterTests.cs | 67 +++++++++++++++++++ 4 files changed, 98 insertions(+), 3 deletions(-) create mode 100644 src/Microsoft.AspNet.Server.Kestrel.Https/ClientCertificateValidationCallback.cs diff --git a/src/Microsoft.AspNet.Server.Kestrel.Https/ClientCertificateValidationCallback.cs b/src/Microsoft.AspNet.Server.Kestrel.Https/ClientCertificateValidationCallback.cs new file mode 100644 index 000000000..432cc7af7 --- /dev/null +++ b/src/Microsoft.AspNet.Server.Kestrel.Https/ClientCertificateValidationCallback.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.Security; +using System.Security.Cryptography.X509Certificates; +using System.Threading.Tasks; + +namespace Microsoft.AspNet.Server.Kestrel.Https +{ + public delegate bool ClientCertificateValidationCallback( + X509Certificate2 certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors); +} diff --git a/src/Microsoft.AspNet.Server.Kestrel.Https/HttpsConnectionFilter.cs b/src/Microsoft.AspNet.Server.Kestrel.Https/HttpsConnectionFilter.cs index fc21e21ed..1b014043b 100644 --- a/src/Microsoft.AspNet.Server.Kestrel.Https/HttpsConnectionFilter.cs +++ b/src/Microsoft.AspNet.Server.Kestrel.Https/HttpsConnectionFilter.cs @@ -16,6 +16,7 @@ public class HttpsConnectionFilter : IConnectionFilter { private readonly X509Certificate2 _cert; private readonly ClientCertificateMode _clientCertMode; + private readonly ClientCertificateValidationCallback _clientValidationCallback; private readonly IConnectionFilter _previous; public HttpsConnectionFilter(HttpsConnectionFilterOptions options, IConnectionFilter previous) @@ -31,6 +32,7 @@ public HttpsConnectionFilter(HttpsConnectionFilterOptions options, IConnectionFi _cert = options.ServerCertificate; _clientCertMode = options.ClientCertificateMode; + _clientValidationCallback = options.ClientCertificateValidation; _previous = previous; } @@ -56,14 +58,27 @@ public async Task OnConnection(ConnectionFilterContext context) return _clientCertMode != ClientCertificateMode.RequireCertificate; } - if (sslPolicyErrors != SslPolicyErrors.None) + + if (_clientValidationCallback == null) + { + if (sslPolicyErrors != SslPolicyErrors.None) + { + return false; + } + } + X509Certificate2 certificate2 = certificate as X509Certificate2 ?? + new X509Certificate2(certificate); + if (_clientValidationCallback != null) { - return false; + if (!_clientValidationCallback(certificate2, chain, sslPolicyErrors)) + { + return false; + } } context.TlsConnectionFeature = new TlsConnectionFeature() { - ClientCertificate = certificate as X509Certificate2 ?? new X509Certificate2(certificate) + ClientCertificate = certificate2 }; return true; }); diff --git a/src/Microsoft.AspNet.Server.Kestrel.Https/HttpsConnectionFilterOptions.cs b/src/Microsoft.AspNet.Server.Kestrel.Https/HttpsConnectionFilterOptions.cs index 574526387..62c513c21 100644 --- a/src/Microsoft.AspNet.Server.Kestrel.Https/HttpsConnectionFilterOptions.cs +++ b/src/Microsoft.AspNet.Server.Kestrel.Https/HttpsConnectionFilterOptions.cs @@ -14,5 +14,6 @@ public HttpsConnectionFilterOptions() public X509Certificate2 ServerCertificate { get; set; } public ClientCertificateMode ClientCertificateMode { get; set; } + public ClientCertificateValidationCallback ClientCertificateValidation { get; set; } } } diff --git a/test/Microsoft.AspNet.Server.KestrelTests/HttpsConnectionFilterTests.cs b/test/Microsoft.AspNet.Server.KestrelTests/HttpsConnectionFilterTests.cs index 852f34926..f6a2494a0 100644 --- a/test/Microsoft.AspNet.Server.KestrelTests/HttpsConnectionFilterTests.cs +++ b/test/Microsoft.AspNet.Server.KestrelTests/HttpsConnectionFilterTests.cs @@ -3,10 +3,13 @@ #if DNX451 using System.Collections.Generic; +using System.IO; using System.Net; using System.Net.Http; using System.Net.Security; +using System.Net.Sockets; using System.Security.Cryptography.X509Certificates; +using System.Text; using System.Threading.Tasks; using Microsoft.AspNet.Http; using Microsoft.AspNet.Http.Features; @@ -189,5 +192,69 @@ public async Task AllowCertificateContinuesWhenNoCertificate() #endif } } + + // https://github.com/aspnet/KestrelHttpServer/issues/240 + // This test currently fails on mono because of an issue with SslStream. + [ConditionalFact] + [OSSkipCondition(OperatingSystems.Linux)] + [OSSkipCondition(OperatingSystems.MacOSX)] + public async Task CertificatePassedToHttpContext() + { + RemoteCertificateValidationCallback validationCallback = + (sender, cert, chain, sslPolicyErrors) => true; + + try + { + ServicePointManager.ServerCertificateValidationCallback += validationCallback; + + var serverAddress = "https://localhost:54321/"; + var serviceContext = new TestServiceContext() + { + ConnectionFilter = new HttpsConnectionFilter( + new HttpsConnectionFilterOptions + { + ServerCertificate = new X509Certificate2(@"TestResources/testCert.pfx", "testPassword"), + ClientCertificateMode = ClientCertificateMode.RequireCertificate, + ClientCertificateValidation = (certificate, chain, sslPolicyErrors) => true + }, + new NoOpConnectionFilter()) + }; + + RequestDelegate app = context => + { + var tlsFeature = context.Features.Get(); + Assert.NotNull(tlsFeature); + Assert.NotNull(tlsFeature.ClientCertificate); + Assert.NotNull(context.Connection.ClientCertificate); + return context.Response.WriteAsync("hello world"); + }; + + using (var server = new TestServer(app, serviceContext, serverAddress)) + { + // SslStream is used to ensure the certificate is actually passed to the server + // HttpClient might not send the certificate because it is invalid or it doesn't match any + // of the certificate authorities sent by the server in the SSL handshake. + using (var client = new TcpClient()) + { + await client.ConnectAsync("127.0.0.1", 54321); + + SslStream stream = new SslStream(client.GetStream(), false, (sender, certificate, chain, errors) => true, + (sender, host, certificates, certificate, issuers) => new X509Certificate2(@"TestResources/testCert.pfx", "testPassword")); + await stream.AuthenticateAsClientAsync("localhost"); + + var request = Encoding.UTF8.GetBytes("GET / HTTP/1.0\r\n\r\n"); + await stream.WriteAsync(request, 0, request.Length); + + var reader = new StreamReader(stream); + var line = await reader.ReadLineAsync(); + Assert.Equal("HTTP/1.0 200 OK", line); + } + } + } + finally + { + ServicePointManager.ServerCertificateValidationCallback -= validationCallback; + } + } } } From d489e11daead65b4ea00c0da02e9efaf28d056af Mon Sep 17 00:00:00 2001 From: Master T Date: Fri, 13 Nov 2015 00:04:13 +0100 Subject: [PATCH 28/30] Code cleanup --- .../ClientCertificateValidationCallback.cs | 7 +++---- .../HttpsConnectionFilter.cs | 5 ++--- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/Microsoft.AspNet.Server.Kestrel.Https/ClientCertificateValidationCallback.cs b/src/Microsoft.AspNet.Server.Kestrel.Https/ClientCertificateValidationCallback.cs index 432cc7af7..12616900c 100644 --- a/src/Microsoft.AspNet.Server.Kestrel.Https/ClientCertificateValidationCallback.cs +++ b/src/Microsoft.AspNet.Server.Kestrel.Https/ClientCertificateValidationCallback.cs @@ -1,9 +1,8 @@ -using System; -using System.Collections.Generic; -using System.Linq; +// 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.Net.Security; using System.Security.Cryptography.X509Certificates; -using System.Threading.Tasks; namespace Microsoft.AspNet.Server.Kestrel.Https { diff --git a/src/Microsoft.AspNet.Server.Kestrel.Https/HttpsConnectionFilter.cs b/src/Microsoft.AspNet.Server.Kestrel.Https/HttpsConnectionFilter.cs index 1b014043b..e5f588e2a 100644 --- a/src/Microsoft.AspNet.Server.Kestrel.Https/HttpsConnectionFilter.cs +++ b/src/Microsoft.AspNet.Server.Kestrel.Https/HttpsConnectionFilter.cs @@ -3,12 +3,11 @@ using System; using System.Net.Security; +using System.Security.Authentication; using System.Security.Cryptography.X509Certificates; using System.Threading.Tasks; -using Microsoft.AspNet.Server.Kestrel.Filter; -using System.Security.Authentication; using Microsoft.AspNet.Http.Features.Internal; -using System.Diagnostics; +using Microsoft.AspNet.Server.Kestrel.Filter; namespace Microsoft.AspNet.Server.Kestrel.Https { From 1940c3a69b87172094fd2a6f8e3c4f5218108cb3 Mon Sep 17 00:00:00 2001 From: Master T Date: Sat, 14 Nov 2015 03:19:39 +0100 Subject: [PATCH 29/30] Add IConnectionFilter.PrepareRequest and use it to set the ITlsConnectionFeature in HttpsConnectionFilter --- .../HttpsConnectionFilter.cs | 26 +++++++++++++------ .../Filter/ConnectionFilterContext.cs | 1 - .../Filter/IConnectionFilter.cs | 2 ++ .../Filter/NoOpConnectionFilter.cs | 3 +++ .../Http/Connection.cs | 5 +--- .../Http/Frame.cs | 11 ++++---- .../ConnectionFilterTests.cs | 6 +++++ 7 files changed, 36 insertions(+), 18 deletions(-) diff --git a/src/Microsoft.AspNet.Server.Kestrel.Https/HttpsConnectionFilter.cs b/src/Microsoft.AspNet.Server.Kestrel.Https/HttpsConnectionFilter.cs index e5f588e2a..b7ef21f7d 100644 --- a/src/Microsoft.AspNet.Server.Kestrel.Https/HttpsConnectionFilter.cs +++ b/src/Microsoft.AspNet.Server.Kestrel.Https/HttpsConnectionFilter.cs @@ -6,6 +6,7 @@ using System.Security.Authentication; using System.Security.Cryptography.X509Certificates; using System.Threading.Tasks; +using Microsoft.AspNet.Http.Features; using Microsoft.AspNet.Http.Features.Internal; using Microsoft.AspNet.Server.Kestrel.Filter; @@ -13,10 +14,11 @@ namespace Microsoft.AspNet.Server.Kestrel.Https { public class HttpsConnectionFilter : IConnectionFilter { - private readonly X509Certificate2 _cert; + private readonly X509Certificate2 _serverCert; private readonly ClientCertificateMode _clientCertMode; private readonly ClientCertificateValidationCallback _clientValidationCallback; private readonly IConnectionFilter _previous; + private X509Certificate2 _clientCert; public HttpsConnectionFilter(HttpsConnectionFilterOptions options, IConnectionFilter previous) { @@ -29,7 +31,7 @@ public HttpsConnectionFilter(HttpsConnectionFilterOptions options, IConnectionFi throw new ArgumentNullException(nameof(previous)); } - _cert = options.ServerCertificate; + _serverCert = options.ServerCertificate; _clientCertMode = options.ClientCertificateMode; _clientValidationCallback = options.ClientCertificateValidation; _previous = previous; @@ -45,7 +47,7 @@ public async Task OnConnection(ConnectionFilterContext context) if (_clientCertMode == ClientCertificateMode.NoCertificate) { sslStream = new SslStream(context.Connection); - await sslStream.AuthenticateAsServerAsync(_cert); + await sslStream.AuthenticateAsServerAsync(_serverCert); } else { @@ -75,18 +77,26 @@ public async Task OnConnection(ConnectionFilterContext context) } } - context.TlsConnectionFeature = new TlsConnectionFeature() - { - ClientCertificate = certificate2 - }; + _clientCert = certificate2; return true; }); - await sslStream.AuthenticateAsServerAsync(_cert, clientCertificateRequired: true, + await sslStream.AuthenticateAsServerAsync(_serverCert, clientCertificateRequired: true, enabledSslProtocols: SslProtocols.Tls12 | SslProtocols.Tls11 | SslProtocols.Tls, checkCertificateRevocation: false); } context.Connection = sslStream; } } + + public void PrepareRequest(IFeatureCollection features) + { + _previous.PrepareRequest(features); + + if (_clientCert != null) + { + features.Set( + new TlsConnectionFeature { ClientCertificate = _clientCert }); + } + } } } diff --git a/src/Microsoft.AspNet.Server.Kestrel/Filter/ConnectionFilterContext.cs b/src/Microsoft.AspNet.Server.Kestrel/Filter/ConnectionFilterContext.cs index 7b5ef1120..278ca5825 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Filter/ConnectionFilterContext.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Filter/ConnectionFilterContext.cs @@ -10,6 +10,5 @@ public class ConnectionFilterContext { public ServerAddress Address { get; set; } public Stream Connection { get; set; } - public ITlsConnectionFeature TlsConnectionFeature { get; set; } } } diff --git a/src/Microsoft.AspNet.Server.Kestrel/Filter/IConnectionFilter.cs b/src/Microsoft.AspNet.Server.Kestrel/Filter/IConnectionFilter.cs index accaa3b9d..8c4f827be 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Filter/IConnectionFilter.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Filter/IConnectionFilter.cs @@ -2,11 +2,13 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Threading.Tasks; +using Microsoft.AspNet.Http.Features; namespace Microsoft.AspNet.Server.Kestrel.Filter { public interface IConnectionFilter { Task OnConnection(ConnectionFilterContext context); + void PrepareRequest(IFeatureCollection features); } } diff --git a/src/Microsoft.AspNet.Server.Kestrel/Filter/NoOpConnectionFilter.cs b/src/Microsoft.AspNet.Server.Kestrel/Filter/NoOpConnectionFilter.cs index b1217d303..c8196a3c6 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Filter/NoOpConnectionFilter.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Filter/NoOpConnectionFilter.cs @@ -12,5 +12,8 @@ public Task OnConnection(ConnectionFilterContext context) { return TaskUtilities.CompletedTask; } + + public void PrepareRequest(IFeatureCollection features) + {} } } diff --git a/src/Microsoft.AspNet.Server.Kestrel/Http/Connection.cs b/src/Microsoft.AspNet.Server.Kestrel/Http/Connection.cs index 963249a2a..397e208b8 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Http/Connection.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Http/Connection.cs @@ -4,7 +4,6 @@ using System; using System.Net; using System.Threading; -using Microsoft.AspNet.Http.Features; using Microsoft.AspNet.Server.Kestrel.Filter; using Microsoft.AspNet.Server.Kestrel.Infrastructure; using Microsoft.AspNet.Server.Kestrel.Networking; @@ -34,7 +33,6 @@ public class Connection : ConnectionContext, IConnectionControl private IPEndPoint _remoteEndPoint; private IPEndPoint _localEndPoint; - private ITlsConnectionFeature _tlsConnectionFeature; public Connection(ListenerContext context, UvStreamHandle socket) : base(context) { @@ -108,7 +106,6 @@ private void ApplyConnectionFilter() SocketInput = filteredStreamAdapter.SocketInput; SocketOutput = filteredStreamAdapter.SocketOutput; - _tlsConnectionFeature = _filterContext.TlsConnectionFeature; _frame = CreateFrame(); _frame.Start(); @@ -160,7 +157,7 @@ private void OnRead(UvStreamHandle handle, int status) private Frame CreateFrame() { - return new Frame(this, _remoteEndPoint, _localEndPoint, _tlsConnectionFeature); + return new Frame(this, _remoteEndPoint, _localEndPoint, ConnectionFilter); } void IConnectionControl.Pause() diff --git a/src/Microsoft.AspNet.Server.Kestrel/Http/Frame.cs b/src/Microsoft.AspNet.Server.Kestrel/Http/Frame.cs index 8860017b0..28d4397b2 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Http/Frame.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Http/Frame.cs @@ -11,6 +11,7 @@ using System.Threading.Tasks; using Microsoft.AspNet.Http; using Microsoft.AspNet.Http.Features; +using Microsoft.AspNet.Server.Kestrel.Filter; using Microsoft.AspNet.Server.Kestrel.Infrastructure; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Primitives; @@ -49,22 +50,22 @@ public partial class Frame : FrameContext, IFrameControl private readonly IPEndPoint _localEndPoint; private readonly IPEndPoint _remoteEndPoint; - private readonly ITlsConnectionFeature _tlsConnectionFeature; + private readonly IConnectionFilter _connectionFilter; public Frame(ConnectionContext context) - : this(context, remoteEndPoint: null, localEndPoint: null, tlsConnectionFeature: null) + : this(context, remoteEndPoint: null, localEndPoint: null, connectionFilter: null) { } public Frame(ConnectionContext context, IPEndPoint remoteEndPoint, IPEndPoint localEndPoint, - ITlsConnectionFeature tlsConnectionFeature) + IConnectionFilter connectionFilter) : base(context) { _remoteEndPoint = remoteEndPoint; _localEndPoint = localEndPoint; - _tlsConnectionFeature = tlsConnectionFeature; + _connectionFilter = connectionFilter; FrameControl = this; Reset(); @@ -137,7 +138,7 @@ public void Reset() httpConnectionFeature.IsLocal = false; } - _currentITlsConnectionFeature = _tlsConnectionFeature; + _connectionFilter?.PrepareRequest(this); } public void ResetResponseHeaders() diff --git a/test/Microsoft.AspNet.Server.KestrelTests/ConnectionFilterTests.cs b/test/Microsoft.AspNet.Server.KestrelTests/ConnectionFilterTests.cs index 5842e1d7c..0af98798c 100644 --- a/test/Microsoft.AspNet.Server.KestrelTests/ConnectionFilterTests.cs +++ b/test/Microsoft.AspNet.Server.KestrelTests/ConnectionFilterTests.cs @@ -95,6 +95,9 @@ public Task OnConnection(ConnectionFilterContext context) return _empty; } + public void PrepareRequest(IFeatureCollection frame) + {} + public int BytesRead => _rewritingStream.BytesRead; } @@ -110,6 +113,9 @@ public async Task OnConnection(ConnectionFilterContext context) context.Connection = new RewritingStream(oldConnection); } + + public void PrepareRequest(IFeatureCollection frame) + {} } private class RewritingStream : Stream From 4a940bdf457fe8ed3c25f23bb603fb06a7e436dd Mon Sep 17 00:00:00 2001 From: Master T Date: Sat, 14 Nov 2015 05:14:27 +0100 Subject: [PATCH 30/30] Merge with dev, check client certificate on CoreCLR --- .../HttpsConnectionFilter.cs | 8 ++++++++ .../Filter/NoOpConnectionFilter.cs | 1 + .../HttpsConnectionFilterTests.cs | 15 ++++++++++++++- 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/Microsoft.AspNet.Server.Kestrel.Https/HttpsConnectionFilter.cs b/src/Microsoft.AspNet.Server.Kestrel.Https/HttpsConnectionFilter.cs index b7ef21f7d..e66fa4b3c 100644 --- a/src/Microsoft.AspNet.Server.Kestrel.Https/HttpsConnectionFilter.cs +++ b/src/Microsoft.AspNet.Server.Kestrel.Https/HttpsConnectionFilter.cs @@ -67,8 +67,16 @@ public async Task OnConnection(ConnectionFilterContext context) return false; } } +#if DOTNET5_4 + // conversion X509Certificate to X509Certificate2 not supported + // https://github.com/dotnet/corefx/issues/4510 + X509Certificate2 certificate2 = null; + return false; +#else X509Certificate2 certificate2 = certificate as X509Certificate2 ?? new X509Certificate2(certificate); + +#endif if (_clientValidationCallback != null) { if (!_clientValidationCallback(certificate2, chain, sslPolicyErrors)) diff --git a/src/Microsoft.AspNet.Server.Kestrel/Filter/NoOpConnectionFilter.cs b/src/Microsoft.AspNet.Server.Kestrel/Filter/NoOpConnectionFilter.cs index c8196a3c6..aac76ec34 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Filter/NoOpConnectionFilter.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Filter/NoOpConnectionFilter.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Threading.Tasks; +using Microsoft.AspNet.Http.Features; using Microsoft.AspNet.Server.Kestrel.Infrastructure; namespace Microsoft.AspNet.Server.Kestrel.Filter diff --git a/test/Microsoft.AspNet.Server.KestrelTests/HttpsConnectionFilterTests.cs b/test/Microsoft.AspNet.Server.KestrelTests/HttpsConnectionFilterTests.cs index f6a2494a0..167b49841 100644 --- a/test/Microsoft.AspNet.Server.KestrelTests/HttpsConnectionFilterTests.cs +++ b/test/Microsoft.AspNet.Server.KestrelTests/HttpsConnectionFilterTests.cs @@ -1,7 +1,7 @@ // 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. -#if DNX451 +using System; using System.Collections.Generic; using System.IO; using System.Net; @@ -137,6 +137,9 @@ await Assert.ThrowsAnyAsync( } } + // https://github.com/dotnet/corefx/issues/4512 +#if DNX451 + // https://github.com/aspnet/KestrelHttpServer/issues/240 // This test currently fails on mono because of an issue with SslStream. [ConditionalFact] @@ -192,6 +195,10 @@ public async Task AllowCertificateContinuesWhenNoCertificate() #endif } } +#endif + + // https://github.com/dotnet/corefx/issues/4510 +#if DNX451 // https://github.com/aspnet/KestrelHttpServer/issues/240 // This test currently fails on mono because of an issue with SslStream. @@ -205,7 +212,9 @@ public async Task CertificatePassedToHttpContext() try { +#if DNX451 ServicePointManager.ServerCertificateValidationCallback += validationCallback; +#endif var serverAddress = "https://localhost:54321/"; var serviceContext = new TestServiceContext() @@ -253,8 +262,12 @@ public async Task CertificatePassedToHttpContext() } finally { +#if DNX451 ServicePointManager.ServerCertificateValidationCallback -= validationCallback; +#endif } } +#endif + } }