Expose Tls details as a feature. #2661
Limit Http/2 to TLS 1.2 #2251 Bootstrap functional tests #2238
This commit is contained in:
parent
c18a27d1f1
commit
c498f03cb4
|
|
@ -5,47 +5,48 @@
|
|||
<!-- This files is typically managed by automation. Execute 'run.ps1 upgrade deps' to update these variables to the last-known-good versions. -->
|
||||
<PropertyGroup Label="Package Versions">
|
||||
<BenchmarkDotNetPackageVersion>0.10.13</BenchmarkDotNetPackageVersion>
|
||||
<InternalAspNetCoreAnalyzersPackageVersion>2.2.0-preview1-34411</InternalAspNetCoreAnalyzersPackageVersion>
|
||||
<InternalAspNetCoreSdkPackageVersion>2.2.0-preview1-17081</InternalAspNetCoreSdkPackageVersion>
|
||||
<InternalAspNetCoreAnalyzersPackageVersion>2.2.0-preview1-34484</InternalAspNetCoreAnalyzersPackageVersion>
|
||||
<InternalAspNetCoreSdkPackageVersion>2.2.0-preview1-17087</InternalAspNetCoreSdkPackageVersion>
|
||||
<LibuvPackageVersion>1.10.0</LibuvPackageVersion>
|
||||
<MicrosoftAspNetCoreAllPackageVersion>2.2.0-preview1-34411</MicrosoftAspNetCoreAllPackageVersion>
|
||||
<MicrosoftAspNetCoreBenchmarkRunnerSourcesPackageVersion>2.2.0-preview1-34411</MicrosoftAspNetCoreBenchmarkRunnerSourcesPackageVersion>
|
||||
<MicrosoftAspNetCoreCertificatesGenerationSourcesPackageVersion>2.2.0-preview1-34411</MicrosoftAspNetCoreCertificatesGenerationSourcesPackageVersion>
|
||||
<MicrosoftAspNetCoreHostingAbstractionsPackageVersion>2.2.0-preview1-34411</MicrosoftAspNetCoreHostingAbstractionsPackageVersion>
|
||||
<MicrosoftAspNetCoreHostingPackageVersion>2.2.0-preview1-34411</MicrosoftAspNetCoreHostingPackageVersion>
|
||||
<MicrosoftAspNetCoreHttpAbstractionsPackageVersion>2.2.0-preview1-34411</MicrosoftAspNetCoreHttpAbstractionsPackageVersion>
|
||||
<MicrosoftAspNetCoreHttpFeaturesPackageVersion>2.2.0-preview1-34411</MicrosoftAspNetCoreHttpFeaturesPackageVersion>
|
||||
<MicrosoftAspNetCoreHttpPackageVersion>2.2.0-preview1-34411</MicrosoftAspNetCoreHttpPackageVersion>
|
||||
<MicrosoftAspNetCoreTestingPackageVersion>2.2.0-preview1-34411</MicrosoftAspNetCoreTestingPackageVersion>
|
||||
<MicrosoftAspNetCoreWebUtilitiesPackageVersion>2.2.0-preview1-34411</MicrosoftAspNetCoreWebUtilitiesPackageVersion>
|
||||
<MicrosoftExtensionsActivatorUtilitiesSourcesPackageVersion>2.2.0-preview1-34411</MicrosoftExtensionsActivatorUtilitiesSourcesPackageVersion>
|
||||
<MicrosoftExtensionsBuffersMemoryPoolSourcesPackageVersion>2.2.0-preview1-34411</MicrosoftExtensionsBuffersMemoryPoolSourcesPackageVersion>
|
||||
<MicrosoftExtensionsBuffersSourcesPackageVersion>2.2.0-preview1-34411</MicrosoftExtensionsBuffersSourcesPackageVersion>
|
||||
<MicrosoftExtensionsBuffersTestingSourcesPackageVersion>2.2.0-preview1-34411</MicrosoftExtensionsBuffersTestingSourcesPackageVersion>
|
||||
<MicrosoftExtensionsConfigurationBinderPackageVersion>2.2.0-preview1-34411</MicrosoftExtensionsConfigurationBinderPackageVersion>
|
||||
<MicrosoftExtensionsConfigurationCommandLinePackageVersion>2.2.0-preview1-34411</MicrosoftExtensionsConfigurationCommandLinePackageVersion>
|
||||
<MicrosoftExtensionsConfigurationJsonPackageVersion>2.2.0-preview1-34411</MicrosoftExtensionsConfigurationJsonPackageVersion>
|
||||
<MicrosoftExtensionsDependencyInjectionPackageVersion>2.2.0-preview1-34411</MicrosoftExtensionsDependencyInjectionPackageVersion>
|
||||
<MicrosoftExtensionsLoggingAbstractionsPackageVersion>2.2.0-preview1-34411</MicrosoftExtensionsLoggingAbstractionsPackageVersion>
|
||||
<MicrosoftExtensionsLoggingConsolePackageVersion>2.2.0-preview1-34411</MicrosoftExtensionsLoggingConsolePackageVersion>
|
||||
<MicrosoftExtensionsLoggingPackageVersion>2.2.0-preview1-34411</MicrosoftExtensionsLoggingPackageVersion>
|
||||
<MicrosoftExtensionsLoggingTestingPackageVersion>2.2.0-preview1-34411</MicrosoftExtensionsLoggingTestingPackageVersion>
|
||||
<MicrosoftExtensionsOptionsPackageVersion>2.2.0-preview1-34411</MicrosoftExtensionsOptionsPackageVersion>
|
||||
<MicrosoftAspNetCoreAllPackageVersion>2.2.0-preview1-34484</MicrosoftAspNetCoreAllPackageVersion>
|
||||
<MicrosoftAspNetCoreBenchmarkRunnerSourcesPackageVersion>2.2.0-preview1-34484</MicrosoftAspNetCoreBenchmarkRunnerSourcesPackageVersion>
|
||||
<MicrosoftAspNetCoreCertificatesGenerationSourcesPackageVersion>2.2.0-preview1-34484</MicrosoftAspNetCoreCertificatesGenerationSourcesPackageVersion>
|
||||
<MicrosoftAspNetCoreHostingAbstractionsPackageVersion>2.2.0-preview1-34484</MicrosoftAspNetCoreHostingAbstractionsPackageVersion>
|
||||
<MicrosoftAspNetCoreHostingPackageVersion>2.2.0-preview1-34484</MicrosoftAspNetCoreHostingPackageVersion>
|
||||
<MicrosoftAspNetCoreHttpAbstractionsPackageVersion>2.2.0-preview1-34484</MicrosoftAspNetCoreHttpAbstractionsPackageVersion>
|
||||
<MicrosoftAspNetCoreHttpFeaturesPackageVersion>2.2.0-preview1-34484</MicrosoftAspNetCoreHttpFeaturesPackageVersion>
|
||||
<MicrosoftAspNetCoreHttpPackageVersion>2.2.0-preview1-34484</MicrosoftAspNetCoreHttpPackageVersion>
|
||||
<MicrosoftAspNetCoreTestingPackageVersion>2.2.0-preview1-34484</MicrosoftAspNetCoreTestingPackageVersion>
|
||||
<MicrosoftAspNetCoreWebUtilitiesPackageVersion>2.2.0-preview1-34484</MicrosoftAspNetCoreWebUtilitiesPackageVersion>
|
||||
<MicrosoftExtensionsActivatorUtilitiesSourcesPackageVersion>2.2.0-preview1-34484</MicrosoftExtensionsActivatorUtilitiesSourcesPackageVersion>
|
||||
<MicrosoftExtensionsBuffersMemoryPoolSourcesPackageVersion>2.2.0-preview1-34484</MicrosoftExtensionsBuffersMemoryPoolSourcesPackageVersion>
|
||||
<MicrosoftExtensionsBuffersSourcesPackageVersion>2.2.0-preview1-34484</MicrosoftExtensionsBuffersSourcesPackageVersion>
|
||||
<MicrosoftExtensionsBuffersTestingSourcesPackageVersion>2.2.0-preview1-34484</MicrosoftExtensionsBuffersTestingSourcesPackageVersion>
|
||||
<MicrosoftExtensionsConfigurationBinderPackageVersion>2.2.0-preview1-34484</MicrosoftExtensionsConfigurationBinderPackageVersion>
|
||||
<MicrosoftExtensionsConfigurationCommandLinePackageVersion>2.2.0-preview1-34484</MicrosoftExtensionsConfigurationCommandLinePackageVersion>
|
||||
<MicrosoftExtensionsConfigurationJsonPackageVersion>2.2.0-preview1-34484</MicrosoftExtensionsConfigurationJsonPackageVersion>
|
||||
<MicrosoftExtensionsDependencyInjectionPackageVersion>2.2.0-preview1-34484</MicrosoftExtensionsDependencyInjectionPackageVersion>
|
||||
<MicrosoftExtensionsLoggingAbstractionsPackageVersion>2.2.0-preview1-34484</MicrosoftExtensionsLoggingAbstractionsPackageVersion>
|
||||
<MicrosoftExtensionsLoggingConsolePackageVersion>2.2.0-preview1-34484</MicrosoftExtensionsLoggingConsolePackageVersion>
|
||||
<MicrosoftExtensionsLoggingPackageVersion>2.2.0-preview1-34484</MicrosoftExtensionsLoggingPackageVersion>
|
||||
<MicrosoftExtensionsLoggingTestingPackageVersion>2.2.0-preview1-34484</MicrosoftExtensionsLoggingTestingPackageVersion>
|
||||
<MicrosoftExtensionsOptionsPackageVersion>2.2.0-preview1-34484</MicrosoftExtensionsOptionsPackageVersion>
|
||||
<MicrosoftNETCoreApp20PackageVersion>2.0.0</MicrosoftNETCoreApp20PackageVersion>
|
||||
<MicrosoftNETCoreApp21PackageVersion>2.1.0</MicrosoftNETCoreApp21PackageVersion>
|
||||
<MicrosoftNETCoreApp22PackageVersion>2.2.0-preview1-26606-01</MicrosoftNETCoreApp22PackageVersion>
|
||||
<MicrosoftNetHttpHeadersPackageVersion>2.2.0-preview1-34411</MicrosoftNetHttpHeadersPackageVersion>
|
||||
<MicrosoftNETCoreApp22PackageVersion>2.2.0-preview1-26614-02</MicrosoftNETCoreApp22PackageVersion>
|
||||
<MicrosoftNetHttpHeadersPackageVersion>2.2.0-preview1-34484</MicrosoftNetHttpHeadersPackageVersion>
|
||||
<MicrosoftNETTestSdkPackageVersion>15.6.1</MicrosoftNETTestSdkPackageVersion>
|
||||
<MoqPackageVersion>4.7.49</MoqPackageVersion>
|
||||
<NETStandardLibrary20PackageVersion>2.0.3</NETStandardLibrary20PackageVersion>
|
||||
<NewtonsoftJsonPackageVersion>11.0.2</NewtonsoftJsonPackageVersion>
|
||||
<SystemBuffersPackageVersion>4.6.0-preview1-26605-01</SystemBuffersPackageVersion>
|
||||
<SystemIOPipelinesPackageVersion>4.6.0-preview1-26605-01</SystemIOPipelinesPackageVersion>
|
||||
<SystemMemoryPackageVersion>4.6.0-preview1-26605-01</SystemMemoryPackageVersion>
|
||||
<SystemNumericsVectorsPackageVersion>4.6.0-preview1-26605-01</SystemNumericsVectorsPackageVersion>
|
||||
<SystemRuntimeCompilerServicesUnsafePackageVersion>4.6.0-preview1-26605-01</SystemRuntimeCompilerServicesUnsafePackageVersion>
|
||||
<SystemSecurityCryptographyCngPackageVersion>4.6.0-preview1-26605-01</SystemSecurityCryptographyCngPackageVersion>
|
||||
<SystemThreadingTasksExtensionsPackageVersion>4.6.0-preview1-26605-01</SystemThreadingTasksExtensionsPackageVersion>
|
||||
<SystemBuffersPackageVersion>4.6.0-preview1-26613-07</SystemBuffersPackageVersion>
|
||||
<SystemIOPipelinesPackageVersion>4.6.0-preview1-26613-07</SystemIOPipelinesPackageVersion>
|
||||
<SystemMemoryPackageVersion>4.6.0-preview1-26613-07</SystemMemoryPackageVersion>
|
||||
<SystemNetHttpWinHttpHandlerPackageVersion>4.6.0-preview1-26613-07</SystemNetHttpWinHttpHandlerPackageVersion>
|
||||
<SystemNumericsVectorsPackageVersion>4.6.0-preview1-26613-07</SystemNumericsVectorsPackageVersion>
|
||||
<SystemRuntimeCompilerServicesUnsafePackageVersion>4.6.0-preview1-26613-07</SystemRuntimeCompilerServicesUnsafePackageVersion>
|
||||
<SystemSecurityCryptographyCngPackageVersion>4.6.0-preview1-26613-07</SystemSecurityCryptographyCngPackageVersion>
|
||||
<SystemThreadingTasksExtensionsPackageVersion>4.6.0-preview1-26613-07</SystemThreadingTasksExtensionsPackageVersion>
|
||||
<Utf8JsonPackageVersion>1.3.7</Utf8JsonPackageVersion>
|
||||
<XunitAnalyzersPackageVersion>0.8.0</XunitAnalyzersPackageVersion>
|
||||
<XunitPackageVersion>2.3.1</XunitPackageVersion>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,24 @@
|
|||
// 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.Authentication;
|
||||
|
||||
namespace Microsoft.AspNetCore.Connections.Features
|
||||
{
|
||||
public interface ITlsHandshakeFeature
|
||||
{
|
||||
SslProtocols Protocol { get; }
|
||||
|
||||
CipherAlgorithmType CipherAlgorithm { get; }
|
||||
|
||||
int CipherStrength { get; }
|
||||
|
||||
HashAlgorithmType HashAlgorithm { get; }
|
||||
|
||||
int HashStrength { get; }
|
||||
|
||||
ExchangeAlgorithmType KeyExchangeAlgorithm { get; }
|
||||
|
||||
int KeyExchangeStrength { get; }
|
||||
}
|
||||
}
|
||||
|
|
@ -518,4 +518,7 @@ For more information on configuring HTTPS see https://go.microsoft.com/fwlink/?l
|
|||
<data name="Http2ErrorFrameOverLimit" xml:space="preserve">
|
||||
<value>The received frame size of {size} exceeds the limit {limit}.</value>
|
||||
</data>
|
||||
<data name="Http2ErrorMinTlsVersion" xml:space="preserve">
|
||||
<value>Tls 1.2 or later must be used for HTTP/2. {protocol} was negotiated.</value>
|
||||
</data>
|
||||
</root>
|
||||
|
|
@ -3,14 +3,15 @@
|
|||
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.Collections;
|
||||
using System.Collections.Concurrent;
|
||||
using System.IO.Pipelines;
|
||||
using System.Security.Authentication;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Connections;
|
||||
using Microsoft.AspNetCore.Connections.Features;
|
||||
using Microsoft.AspNetCore.Hosting.Server;
|
||||
using Microsoft.AspNetCore.Http.Features;
|
||||
using Microsoft.AspNetCore.Connections;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2.HPack;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure;
|
||||
|
|
@ -115,31 +116,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
|
|||
|
||||
try
|
||||
{
|
||||
while (!_stopping)
|
||||
{
|
||||
var result = await Input.ReadAsync();
|
||||
var readableBuffer = result.Buffer;
|
||||
var consumed = readableBuffer.Start;
|
||||
var examined = readableBuffer.End;
|
||||
ValidateTlsRequirements();
|
||||
|
||||
try
|
||||
{
|
||||
if (!readableBuffer.IsEmpty)
|
||||
{
|
||||
if (ParsePreface(readableBuffer, out consumed, out examined))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (result.IsCompleted)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
Input.AdvanceTo(consumed, examined);
|
||||
}
|
||||
if (!await TryReadPrefaceAsync())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_stopping)
|
||||
|
|
@ -213,6 +194,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
|
|||
}
|
||||
|
||||
await _frameWriter.WriteGoAwayAsync(_highestOpenedStreamId, errorCode);
|
||||
_frameWriter.Complete();
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
|
@ -222,6 +204,55 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
|
|||
}
|
||||
}
|
||||
|
||||
// https://tools.ietf.org/html/rfc7540#section-9.2
|
||||
// Some of these could not be checked in advance. Fail before using the connection.
|
||||
private void ValidateTlsRequirements()
|
||||
{
|
||||
var tlsFeature = ConnectionFeatures.Get<ITlsHandshakeFeature>();
|
||||
if (tlsFeature == null)
|
||||
{
|
||||
// Not using TLS at all.
|
||||
return;
|
||||
}
|
||||
|
||||
if (tlsFeature.Protocol < SslProtocols.Tls12)
|
||||
{
|
||||
throw new Http2ConnectionErrorException(CoreStrings.FormatHttp2ErrorMinTlsVersion(tlsFeature.Protocol), Http2ErrorCode.INADEQUATE_SECURITY);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<bool> TryReadPrefaceAsync()
|
||||
{
|
||||
while (!_stopping)
|
||||
{
|
||||
var result = await Input.ReadAsync();
|
||||
var readableBuffer = result.Buffer;
|
||||
var consumed = readableBuffer.Start;
|
||||
var examined = readableBuffer.End;
|
||||
|
||||
try
|
||||
{
|
||||
if (!readableBuffer.IsEmpty)
|
||||
{
|
||||
if (ParsePreface(readableBuffer, out consumed, out examined))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (result.IsCompleted)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
Input.AdvanceTo(consumed, examined);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool ParsePreface(ReadOnlySequence<byte> readableBuffer, out SequencePosition consumed, out SequencePosition examined)
|
||||
{
|
||||
consumed = readableBuffer.Start;
|
||||
|
|
|
|||
|
|
@ -32,6 +32,20 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
|
|||
_outputReader = outputPipeReader;
|
||||
}
|
||||
|
||||
public void Complete()
|
||||
{
|
||||
lock (_writeLock)
|
||||
{
|
||||
if (_completed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_completed = true;
|
||||
_outputWriter.Complete();
|
||||
}
|
||||
}
|
||||
|
||||
public void Abort(Exception ex)
|
||||
{
|
||||
lock (_writeLock)
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ using System.Security.Cryptography.X509Certificates;
|
|||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Connections;
|
||||
using Microsoft.AspNetCore.Connections.Features;
|
||||
using Microsoft.AspNetCore.Http.Features;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Core;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Core.Adapter.Internal;
|
||||
|
|
@ -77,6 +78,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Https.Internal
|
|||
bool certificateRequired;
|
||||
var feature = new TlsConnectionFeature();
|
||||
context.Features.Set<ITlsConnectionFeature>(feature);
|
||||
context.Features.Set<ITlsHandshakeFeature>(feature);
|
||||
|
||||
if (_options.ClientCertificateMode == ClientCertificateMode.NoCertificate)
|
||||
{
|
||||
|
|
@ -210,6 +212,13 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Https.Internal
|
|||
#error TFMs need to be updated
|
||||
#endif
|
||||
feature.ClientCertificate = ConvertToX509Certificate2(sslStream.RemoteCertificate);
|
||||
feature.CipherAlgorithm = sslStream.CipherAlgorithm;
|
||||
feature.CipherStrength = sslStream.CipherStrength;
|
||||
feature.HashAlgorithm = sslStream.HashAlgorithm;
|
||||
feature.HashStrength = sslStream.HashStrength;
|
||||
feature.KeyExchangeAlgorithm = sslStream.KeyExchangeAlgorithm;
|
||||
feature.KeyExchangeStrength = sslStream.KeyExchangeStrength;
|
||||
feature.Protocol = sslStream.SslProtocol;
|
||||
|
||||
return new HttpsAdaptedConnection(sslStream);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,20 +2,36 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Security.Authentication;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Connections.Features;
|
||||
using Microsoft.AspNetCore.Http.Features;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Core.Features;
|
||||
|
||||
namespace Microsoft.AspNetCore.Server.Kestrel.Https.Internal
|
||||
{
|
||||
internal class TlsConnectionFeature : ITlsConnectionFeature, ITlsApplicationProtocolFeature
|
||||
internal class TlsConnectionFeature : ITlsConnectionFeature, ITlsApplicationProtocolFeature, ITlsHandshakeFeature
|
||||
{
|
||||
public X509Certificate2 ClientCertificate { get; set; }
|
||||
|
||||
public ReadOnlyMemory<byte> ApplicationProtocol { get; set; }
|
||||
|
||||
public SslProtocols Protocol { get; set; }
|
||||
|
||||
public CipherAlgorithmType CipherAlgorithm { get; set; }
|
||||
|
||||
public int CipherStrength { get; set; }
|
||||
|
||||
public HashAlgorithmType HashAlgorithm { get; set; }
|
||||
|
||||
public int HashStrength { get; set; }
|
||||
|
||||
public ExchangeAlgorithmType KeyExchangeAlgorithm { get; set; }
|
||||
|
||||
public int KeyExchangeStrength { get; set; }
|
||||
|
||||
public Task<X509Certificate2> GetClientCertificateAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
return Task.FromResult(ClientCertificate);
|
||||
|
|
|
|||
|
|
@ -1876,6 +1876,20 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core
|
|||
internal static string FormatHttp2ErrorFrameOverLimit(object size, object limit)
|
||||
=> string.Format(CultureInfo.CurrentCulture, GetString("Http2ErrorFrameOverLimit", "size", "limit"), size, limit);
|
||||
|
||||
/// <summary>
|
||||
/// Tls 1.2 or later must be used for HTTP/2. {protocol} was negotiated.
|
||||
/// </summary>
|
||||
internal static string Http2ErrorMinTlsVersion
|
||||
{
|
||||
get => GetString("Http2ErrorMinTlsVersion");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tls 1.2 or later must be used for HTTP/2. {protocol} was negotiated.
|
||||
/// </summary>
|
||||
internal static string FormatHttp2ErrorMinTlsVersion(object protocol)
|
||||
=> string.Format(CultureInfo.CurrentCulture, GetString("Http2ErrorMinTlsVersion", "protocol"), protocol);
|
||||
|
||||
private static string GetString(string name, params string[] formatterNames)
|
||||
{
|
||||
var value = _resourceManager.GetString(name);
|
||||
|
|
|
|||
|
|
@ -89,7 +89,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal
|
|||
writeReq = null;
|
||||
}
|
||||
}
|
||||
else if (result.IsCompleted)
|
||||
|
||||
if (result.IsCompleted)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -245,13 +245,13 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets.Internal
|
|||
{
|
||||
var result = await Output.ReadAsync();
|
||||
|
||||
var buffer = result.Buffer;
|
||||
|
||||
if (result.IsCanceled)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
var buffer = result.Buffer;
|
||||
|
||||
var end = buffer.End;
|
||||
var isCompleted = result.IsCompleted;
|
||||
if (!buffer.IsEmpty)
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal;
|
|||
using Microsoft.AspNetCore.Testing;
|
||||
using Microsoft.Net.Http.Headers;
|
||||
using Xunit;
|
||||
using Microsoft.AspNetCore.Http.Features;
|
||||
|
||||
namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
|
||||
{
|
||||
|
|
@ -274,6 +275,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
|
|||
|
||||
_connectionContext = new Http2ConnectionContext
|
||||
{
|
||||
ConnectionFeatures = new FeatureCollection(),
|
||||
ServiceContext = new TestServiceContext()
|
||||
{
|
||||
Log = new TestKestrelTrace(_logger)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,102 @@
|
|||
// 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 NETCOREAPP2_2
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Net.Security;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Core;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Core.Features;
|
||||
using Microsoft.AspNetCore.Testing;
|
||||
using Microsoft.AspNetCore.Testing.xunit;
|
||||
using Microsoft.Extensions.Logging.Testing;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests.Http2
|
||||
{
|
||||
[OSSkipCondition(OperatingSystems.MacOSX, SkipReason = "Missing SslStream ALPN support: https://github.com/dotnet/corefx/issues/30492")]
|
||||
[OSSkipCondition(OperatingSystems.Linux, SkipReason = "Curl requires a custom install to support HTTP/2, see https://askubuntu.com/questions/884899/how-do-i-install-curl-with-http2-support")]
|
||||
[MinimumOSVersion(OperatingSystems.Windows, WindowsVersions.Win10)]
|
||||
public class HandshakeTests : LoggedTest
|
||||
{
|
||||
private static X509Certificate2 _x509Certificate2 = TestResources.GetTestCertificate();
|
||||
|
||||
public HttpClient Client { get; set; }
|
||||
|
||||
public HandshakeTests()
|
||||
{
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||
{
|
||||
// We don't want the default SocketsHttpHandler, it doesn't support HTTP/2 yet.
|
||||
Client = new HttpClient(new WinHttpHandler()
|
||||
{
|
||||
ServerCertificateValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[ConditionalFact]
|
||||
public async Task TlsAlpnHandshakeSelectsHttp2From1and2()
|
||||
{
|
||||
using (var server = new TestServer(context =>
|
||||
{
|
||||
var tlsFeature = context.Features.Get<ITlsApplicationProtocolFeature>();
|
||||
Assert.NotNull(tlsFeature);
|
||||
Assert.True(SslApplicationProtocol.Http2.Protocol.Span.SequenceEqual(tlsFeature.ApplicationProtocol.Span),
|
||||
"ALPN: " + tlsFeature.ApplicationProtocol.Length);
|
||||
|
||||
return context.Response.WriteAsync("hello world " + context.Request.Protocol);
|
||||
}, new TestServiceContext(LoggerFactory),
|
||||
kestrelOptions =>
|
||||
{
|
||||
kestrelOptions.Listen(IPAddress.Loopback, 0, listenOptions =>
|
||||
{
|
||||
listenOptions.Protocols = HttpProtocols.Http1AndHttp2;
|
||||
listenOptions.UseHttps(_x509Certificate2);
|
||||
});
|
||||
}))
|
||||
{
|
||||
var result = await Client.GetStringAsync($"https://localhost:{server.Port}/");
|
||||
Assert.Equal("hello world HTTP/2", result);
|
||||
}
|
||||
}
|
||||
|
||||
[ConditionalFact]
|
||||
public async Task TlsAlpnHandshakeSelectsHttp2()
|
||||
{
|
||||
using (var server = new TestServer(context =>
|
||||
{
|
||||
var tlsFeature = context.Features.Get<ITlsApplicationProtocolFeature>();
|
||||
Assert.NotNull(tlsFeature);
|
||||
Assert.True(SslApplicationProtocol.Http2.Protocol.Span.SequenceEqual(tlsFeature.ApplicationProtocol.Span),
|
||||
"ALPN: " + tlsFeature.ApplicationProtocol.Length);
|
||||
|
||||
return context.Response.WriteAsync("hello world " + context.Request.Protocol);
|
||||
}, new TestServiceContext(LoggerFactory),
|
||||
kestrelOptions =>
|
||||
{
|
||||
kestrelOptions.Listen(IPAddress.Loopback, 0, listenOptions =>
|
||||
{
|
||||
listenOptions.Protocols = HttpProtocols.Http2;
|
||||
listenOptions.UseHttps(_x509Certificate2);
|
||||
});
|
||||
}))
|
||||
{
|
||||
var result = await Client.GetStringAsync($"https://localhost:{server.Port}/");
|
||||
Assert.Equal("hello world HTTP/2", result);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#elif NET461 // No ALPN support
|
||||
#else
|
||||
#error TFMs need updating
|
||||
#endif
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Core.Adapter.Internal;
|
||||
|
||||
namespace System.IO.Pipelines
|
||||
{
|
||||
internal class PipeReaderFactory
|
||||
{
|
||||
private static readonly Action<object> _cancelReader = state => ((PipeReader)state).CancelPendingRead();
|
||||
|
||||
public static PipeReader CreateFromStream(PipeOptions options, Stream stream, CancellationToken cancellationToken)
|
||||
{
|
||||
if (!stream.CanRead)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
var pipe = new Pipe(options);
|
||||
_ = CopyToAsync(stream, pipe, cancellationToken);
|
||||
|
||||
return pipe.Reader;
|
||||
}
|
||||
|
||||
private static async Task CopyToAsync(Stream stream, Pipe pipe, CancellationToken cancellationToken)
|
||||
{
|
||||
// We manually register for cancellation here in case the Stream implementation ignores it
|
||||
using (var registration = cancellationToken.Register(_cancelReader, pipe.Reader))
|
||||
{
|
||||
try
|
||||
{
|
||||
await stream.CopyToAsync(new RawStream(null, pipe.Writer), bufferSize: 4096, cancellationToken);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
// Ignore the cancellation signal (the pipe reader is already wired up for cancellation when the token trips)
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
pipe.Writer.Complete(ex);
|
||||
return;
|
||||
}
|
||||
pipe.Writer.Complete();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,126 @@
|
|||
// 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 NETCOREAPP2_2
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.IO.Pipelines;
|
||||
using System.Net;
|
||||
using System.Net.Security;
|
||||
using System.Security.Authentication;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Core;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Core.Features;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2;
|
||||
using Microsoft.AspNetCore.Testing;
|
||||
using Microsoft.AspNetCore.Testing.xunit;
|
||||
using Microsoft.Extensions.Logging.Testing;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests.Http2
|
||||
{
|
||||
[OSSkipCondition(OperatingSystems.MacOSX, SkipReason = "Missing SslStream ALPN support: https://github.com/dotnet/corefx/issues/30492")]
|
||||
public class TlsTests : LoggedTest
|
||||
{
|
||||
private static X509Certificate2 _x509Certificate2 = TestResources.GetTestCertificate();
|
||||
|
||||
[ConditionalFact]
|
||||
public async Task TlsHandshakeRejectsTlsLessThan12()
|
||||
{
|
||||
using (var server = new TestServer(context =>
|
||||
{
|
||||
var tlsFeature = context.Features.Get<ITlsApplicationProtocolFeature>();
|
||||
Assert.NotNull(tlsFeature);
|
||||
Assert.Equal(tlsFeature.ApplicationProtocol, SslApplicationProtocol.Http2.Protocol);
|
||||
|
||||
return context.Response.WriteAsync("hello world " + context.Request.Protocol);
|
||||
}, new TestServiceContext(LoggerFactory),
|
||||
kestrelOptions =>
|
||||
{
|
||||
kestrelOptions.Listen(IPAddress.Loopback, 0, listenOptions =>
|
||||
{
|
||||
listenOptions.Protocols = HttpProtocols.Http2;
|
||||
listenOptions.UseHttps(_x509Certificate2, httpsOptions =>
|
||||
{
|
||||
httpsOptions.SslProtocols = SslProtocols.Tls11 | SslProtocols.Tls12;
|
||||
});
|
||||
});
|
||||
}))
|
||||
{
|
||||
var connection = server.CreateConnection();
|
||||
var sslStream = new SslStream(connection.Stream);
|
||||
await sslStream.AuthenticateAsClientAsync(new SslClientAuthenticationOptions()
|
||||
{
|
||||
TargetHost = "localhost",
|
||||
RemoteCertificateValidationCallback = (_, __, ___, ____) => true,
|
||||
ApplicationProtocols = new List<SslApplicationProtocol>() { SslApplicationProtocol.Http2, SslApplicationProtocol.Http11 },
|
||||
EnabledSslProtocols = SslProtocols.Tls11, // Intentionally less than the required 1.2
|
||||
}, CancellationToken.None);
|
||||
|
||||
var reader = PipeReaderFactory.CreateFromStream(PipeOptions.Default, sslStream, CancellationToken.None);
|
||||
await WaitForConnectionErrorAsync(reader, ignoreNonGoAwayFrames: false, expectedLastStreamId: 0, expectedErrorCode: Http2ErrorCode.INADEQUATE_SECURITY);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task WaitForConnectionErrorAsync(PipeReader reader, bool ignoreNonGoAwayFrames, int expectedLastStreamId, Http2ErrorCode expectedErrorCode)
|
||||
{
|
||||
var frame = await ReceiveFrameAsync(reader);
|
||||
|
||||
if (ignoreNonGoAwayFrames)
|
||||
{
|
||||
while (frame.Type != Http2FrameType.GOAWAY)
|
||||
{
|
||||
frame = await ReceiveFrameAsync(reader);
|
||||
}
|
||||
}
|
||||
|
||||
Assert.Equal(Http2FrameType.GOAWAY, frame.Type);
|
||||
Assert.Equal(8, frame.Length);
|
||||
Assert.Equal(0, frame.Flags);
|
||||
Assert.Equal(0, frame.StreamId);
|
||||
Assert.Equal(expectedLastStreamId, frame.GoAwayLastStreamId);
|
||||
Assert.Equal(expectedErrorCode, frame.GoAwayErrorCode);
|
||||
}
|
||||
|
||||
private async Task<Http2Frame> ReceiveFrameAsync(PipeReader reader)
|
||||
{
|
||||
var frame = new Http2Frame();
|
||||
|
||||
while (true)
|
||||
{
|
||||
var result = await reader.ReadAsync();
|
||||
var buffer = result.Buffer;
|
||||
var consumed = buffer.Start;
|
||||
var examined = buffer.End;
|
||||
|
||||
if (buffer.IsEmpty && result.IsCompleted)
|
||||
{
|
||||
throw new IOException("The reader completed without returning a frame.");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// Assert.True(buffer.Length > 0);
|
||||
|
||||
if (Http2FrameReader.ReadFrame(buffer, frame, 16_384, out consumed, out examined))
|
||||
{
|
||||
return frame;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
reader.AdvanceTo(consumed, examined);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#elif NET461 // No ALPN support
|
||||
#else
|
||||
#error TFMs need updating
|
||||
#endif
|
||||
|
|
@ -14,6 +14,7 @@ using System.Security.Cryptography.X509Certificates;
|
|||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Connections.Features;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Http.Features;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Core;
|
||||
|
|
@ -30,8 +31,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
|
|||
private static X509Certificate2 _x509Certificate2 = TestResources.GetTestCertificate();
|
||||
private static X509Certificate2 _x509Certificate2NoExt = TestResources.GetTestCertificate("no_extensions.pfx");
|
||||
|
||||
// https://github.com/aspnet/KestrelHttpServer/issues/240
|
||||
// This test currently fails on mono because of an issue with SslStream.
|
||||
[Fact]
|
||||
public async Task CanReadAndWriteWithHttpsConnectionAdapter()
|
||||
{
|
||||
|
|
@ -55,6 +54,37 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
|
|||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task HandshakeDetailsAreAvailable()
|
||||
{
|
||||
var listenOptions = new ListenOptions(new IPEndPoint(IPAddress.Loopback, 0))
|
||||
{
|
||||
ConnectionAdapters =
|
||||
{
|
||||
new HttpsConnectionAdapter(new HttpsConnectionAdapterOptions { ServerCertificate = _x509Certificate2 })
|
||||
}
|
||||
};
|
||||
|
||||
using (var server = new TestServer(context =>
|
||||
{
|
||||
var tlsFeature = context.Features.Get<ITlsHandshakeFeature>();
|
||||
Assert.NotNull(tlsFeature);
|
||||
Assert.True(tlsFeature.Protocol > SslProtocols.None, "Protocol");
|
||||
Assert.True(tlsFeature.CipherAlgorithm > CipherAlgorithmType.Null, "Cipher");
|
||||
Assert.True(tlsFeature.CipherStrength > 0, "CipherStrength");
|
||||
Assert.True(tlsFeature.HashAlgorithm >= HashAlgorithmType.None, "HashAlgorithm"); // May be None on Linux.
|
||||
Assert.True(tlsFeature.HashStrength >= 0, "HashStrength"); // May be 0 for some algorithms
|
||||
Assert.True(tlsFeature.KeyExchangeAlgorithm > ExchangeAlgorithmType.None, "KeyExchangeAlgorithm");
|
||||
Assert.True(tlsFeature.KeyExchangeStrength >= 0, "KeyExchangeStrength"); // May be 0 on mac
|
||||
|
||||
return context.Response.WriteAsync("hello world");
|
||||
}, new TestServiceContext(LoggerFactory), listenOptions))
|
||||
{
|
||||
var result = await HttpClientSlim.GetStringAsync($"https://localhost:{server.Port}/", validateCertificate: false);
|
||||
Assert.Equal("hello world", result);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task RequireCertificateFailsWhenNoCertificate()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -45,15 +45,24 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
|
|||
}
|
||||
|
||||
public TestServer(RequestDelegate app, TestServiceContext context, ListenOptions listenOptions, Action<IServiceCollection> configureServices)
|
||||
: this(app, context, options => options.ListenOptions.Add(listenOptions), configureServices)
|
||||
{
|
||||
}
|
||||
public TestServer(RequestDelegate app, TestServiceContext context, Action<KestrelServerOptions> configureKestrel)
|
||||
: this(app, context, configureKestrel, _ => { })
|
||||
{
|
||||
}
|
||||
|
||||
public TestServer(RequestDelegate app, TestServiceContext context, Action<KestrelServerOptions> configureKestrel, Action<IServiceCollection> configureServices)
|
||||
{
|
||||
_app = app;
|
||||
_listenOptions = listenOptions;
|
||||
Context = context;
|
||||
|
||||
_host = TransportSelector.GetWebHostBuilder()
|
||||
.UseKestrel(o =>
|
||||
.UseKestrel(options =>
|
||||
{
|
||||
o.ListenOptions.Add(_listenOptions);
|
||||
configureKestrel(options);
|
||||
_listenOptions = options.ListenOptions.First();
|
||||
})
|
||||
.ConfigureServices(services =>
|
||||
{
|
||||
|
|
@ -70,7 +79,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
|
|||
}
|
||||
return new KestrelServer(sp.GetRequiredService<ITransportFactory>(), context);
|
||||
});
|
||||
RemoveDevCert(services);
|
||||
configureServices(services);
|
||||
})
|
||||
.UseSetting(WebHostDefaults.ApplicationKey, typeof(TestServer).GetTypeInfo().Assembly.FullName)
|
||||
|
|
@ -79,19 +87,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
|
|||
_host.Start();
|
||||
}
|
||||
|
||||
public static void RemoveDevCert(IServiceCollection services)
|
||||
{
|
||||
// KestrelServerOptionsSetup would scan all system certificates on every test server creation
|
||||
// making test runs very slow
|
||||
foreach (var descriptor in services.ToArray())
|
||||
{
|
||||
if (descriptor.ImplementationType == typeof(KestrelServerOptionsSetup))
|
||||
{
|
||||
services.Remove(descriptor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public IPEndPoint EndPoint => _listenOptions.IPEndPoint;
|
||||
public int Port => _listenOptions.IPEndPoint.Port;
|
||||
public AddressFamily AddressFamily => _listenOptions.IPEndPoint.AddressFamily;
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@
|
|||
<PackageReference Include="Microsoft.AspNetCore.Testing" Version="$(MicrosoftAspNetCoreTestingPackageVersion)" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Testing" Version="$(MicrosoftExtensionsLoggingTestingPackageVersion)" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="$(NewtonsoftJsonPackageVersion)" />
|
||||
<PackageReference Include="System.Net.Http.WinHttpHandler" Version="$(SystemNetHttpWinHttpHandlerPackageVersion)" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
|
|||
{
|
||||
public static IWebHostBuilder GetWebHostBuilder(Func<MemoryPool<byte>> memoryPoolFactory = null)
|
||||
{
|
||||
return new WebHostBuilder().UseLibuv(options => { options.MemoryPoolFactory = memoryPoolFactory ?? options.MemoryPoolFactory; }).ConfigureServices(TestServer.RemoveDevCert);
|
||||
return new WebHostBuilder().UseLibuv(options => { options.MemoryPoolFactory = memoryPoolFactory ?? options.MemoryPoolFactory; });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@
|
|||
<PackageReference Include="Microsoft.AspNetCore.Testing" Version="$(MicrosoftAspNetCoreTestingPackageVersion)" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Testing" Version="$(MicrosoftExtensionsLoggingTestingPackageVersion)" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="$(NewtonsoftJsonPackageVersion)" />
|
||||
<PackageReference Include="System.Net.Http.WinHttpHandler" Version="$(SystemNetHttpWinHttpHandlerPackageVersion)" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
|
|||
{
|
||||
public static IWebHostBuilder GetWebHostBuilder(Func<MemoryPool<byte>> memoryPoolFactory = null)
|
||||
{
|
||||
return new WebHostBuilder().UseSockets(options => { options.MemoryPoolFactory = memoryPoolFactory ?? options.MemoryPoolFactory; }).ConfigureServices(TestServer.RemoveDevCert);
|
||||
return new WebHostBuilder().UseSockets(options => { options.MemoryPoolFactory = memoryPoolFactory ?? options.MemoryPoolFactory; });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue