Configure enabled protocols per endpoint and add HTTP/2 w/ prior knowledge support (#2067).
This commit is contained in:
parent
eb9417b577
commit
551c1ebc0b
|
|
@ -351,4 +351,13 @@
|
|||
<data name="PositiveFiniteTimeSpanRequired" xml:space="preserve">
|
||||
<value>Timespan must be positive and finite.</value>
|
||||
</data>
|
||||
<data name="EndPointRequiresAtLeastOneProtocol" xml:space="preserve">
|
||||
<value>An endpoint must be configured to serve at least one protocol.</value>
|
||||
</data>
|
||||
<data name="EndPointRequiresTlsForHttp1AndHttp2" xml:space="preserve">
|
||||
<value>Using both HTTP/1.x and HTTP/2 on the same endpoint requires the use of TLS.</value>
|
||||
</data>
|
||||
<data name="EndPointHttp2NotNegotiated" xml:space="preserve">
|
||||
<value>HTTP/2 over TLS was not negotiated on an HTTP/2-only endpoint.</value>
|
||||
</data>
|
||||
</root>
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
// 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;
|
||||
|
||||
namespace Microsoft.AspNetCore.Server.Kestrel.Core
|
||||
{
|
||||
[Flags]
|
||||
public enum HttpProtocols
|
||||
{
|
||||
None = 0x0,
|
||||
Http1 = 0x1,
|
||||
Http2 = 0x2,
|
||||
Http1AndHttp2 = Http1 | Http2,
|
||||
}
|
||||
}
|
||||
|
|
@ -10,6 +10,7 @@ using System.Net;
|
|||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Hosting.Server;
|
||||
using Microsoft.AspNetCore.Http.Features;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Core.Adapter.Internal;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Core.Features;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http;
|
||||
|
|
@ -135,8 +136,15 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
|
|||
adaptedPipelineTask = adaptedPipeline.RunAsync(stream);
|
||||
}
|
||||
|
||||
if (_http1Connection.ConnectionFeatures.Get<ITlsApplicationProtocolFeature>()?.ApplicationProtocol == "h2" &&
|
||||
Interlocked.CompareExchange(ref _http2ConnectionState, Http2ConnectionStarted, Http2ConnectionNotStarted) == Http2ConnectionNotStarted)
|
||||
var protocol = SelectProtocol();
|
||||
|
||||
if (protocol == HttpProtocols.None)
|
||||
{
|
||||
Abort(ex: null);
|
||||
}
|
||||
|
||||
// One of these has to run even if no protocol was selected so the abort propagates and everything completes properly
|
||||
if (protocol == HttpProtocols.Http2 && Interlocked.CompareExchange(ref _http2ConnectionState, Http2ConnectionStarted, Http2ConnectionNotStarted) == Http2ConnectionNotStarted)
|
||||
{
|
||||
await _http2Connection.ProcessAsync(httpApplication);
|
||||
}
|
||||
|
|
@ -207,6 +215,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
|
|||
public Task StopProcessingNextRequestAsync()
|
||||
{
|
||||
Debug.Assert(_http1Connection != null, $"{nameof(_http1Connection)} is null");
|
||||
Debug.Assert(_http2Connection != null, $"{nameof(_http2Connection)} is null");
|
||||
|
||||
if (Interlocked.Exchange(ref _http2ConnectionState, Http2ConnectionClosed) == Http2ConnectionStarted)
|
||||
{
|
||||
|
|
@ -223,6 +232,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
|
|||
public void Abort(Exception ex)
|
||||
{
|
||||
Debug.Assert(_http1Connection != null, $"{nameof(_http1Connection)} is null");
|
||||
Debug.Assert(_http2Connection != null, $"{nameof(_http2Connection)} is null");
|
||||
|
||||
// Abort the connection (if not already aborted)
|
||||
if (Interlocked.Exchange(ref _http2ConnectionState, Http2ConnectionClosed) == Http2ConnectionStarted)
|
||||
|
|
@ -245,6 +255,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
|
|||
public void SendTimeoutResponse()
|
||||
{
|
||||
Debug.Assert(_http1Connection != null, $"{nameof(_http1Connection)} is null");
|
||||
Debug.Assert(_http2Connection != null, $"{nameof(_http2Connection)} is null");
|
||||
|
||||
RequestTimedOut = true;
|
||||
_http1Connection.SendTimeoutResponse();
|
||||
|
|
@ -253,6 +264,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
|
|||
public void StopProcessingNextRequest()
|
||||
{
|
||||
Debug.Assert(_http1Connection != null, $"{nameof(_http1Connection)} is null");
|
||||
Debug.Assert(_http2Connection != null, $"{nameof(_http2Connection)} is null");
|
||||
|
||||
_http1Connection.StopProcessingNextRequest();
|
||||
}
|
||||
|
|
@ -260,10 +272,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
|
|||
private async Task<Stream> ApplyConnectionAdaptersAsync()
|
||||
{
|
||||
Debug.Assert(_http1Connection != null, $"{nameof(_http1Connection)} is null");
|
||||
Debug.Assert(_http2Connection != null, $"{nameof(_http2Connection)} is null");
|
||||
|
||||
var connectionAdapters = _context.ConnectionAdapters;
|
||||
var stream = new RawStream(_context.Transport.Input, _context.Transport.Output);
|
||||
var adapterContext = new ConnectionAdapterContext(_http1Connection.ConnectionFeatures, stream);
|
||||
var adapterContext = new ConnectionAdapterContext(_context.ConnectionFeatures, stream);
|
||||
_adaptedConnections = new List<IAdaptedConnection>(connectionAdapters.Count);
|
||||
|
||||
try
|
||||
|
|
@ -272,7 +285,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
|
|||
{
|
||||
var adaptedConnection = await connectionAdapters[i].OnConnectionAsync(adapterContext);
|
||||
_adaptedConnections.Add(adaptedConnection);
|
||||
adapterContext = new ConnectionAdapterContext(_http1Connection.ConnectionFeatures, adaptedConnection.ConnectionStream);
|
||||
adapterContext = new ConnectionAdapterContext(_context.ConnectionFeatures, adaptedConnection.ConnectionStream);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
|
@ -290,16 +303,50 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
|
|||
var adaptedConnections = _adaptedConnections;
|
||||
if (adaptedConnections != null)
|
||||
{
|
||||
for (int i = adaptedConnections.Count - 1; i >= 0; i--)
|
||||
for (var i = adaptedConnections.Count - 1; i >= 0; i--)
|
||||
{
|
||||
adaptedConnections[i].Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private HttpProtocols SelectProtocol()
|
||||
{
|
||||
var hasTls = _context.ConnectionFeatures.Get<ITlsConnectionFeature>() != null;
|
||||
var applicationProtocol = _context.ConnectionFeatures.Get<ITlsApplicationProtocolFeature>()?.ApplicationProtocol;
|
||||
var http1Enabled = (_context.Protocols & HttpProtocols.Http1) == HttpProtocols.Http1;
|
||||
var http2Enabled = (_context.Protocols & HttpProtocols.Http2) == HttpProtocols.Http2;
|
||||
|
||||
string error = null;
|
||||
|
||||
if (_context.Protocols == HttpProtocols.None)
|
||||
{
|
||||
error = CoreStrings.EndPointRequiresAtLeastOneProtocol;
|
||||
}
|
||||
|
||||
if (!hasTls && http1Enabled && http2Enabled)
|
||||
{
|
||||
error = CoreStrings.EndPointRequiresTlsForHttp1AndHttp2;
|
||||
}
|
||||
|
||||
if (!http1Enabled && http2Enabled && hasTls && applicationProtocol != "h2")
|
||||
{
|
||||
error = CoreStrings.EndPointHttp2NotNegotiated;
|
||||
}
|
||||
|
||||
if (error != null)
|
||||
{
|
||||
Log.LogError(0, error);
|
||||
return HttpProtocols.None;
|
||||
}
|
||||
|
||||
return http2Enabled && (!hasTls || applicationProtocol == "h2") ? HttpProtocols.Http2 : HttpProtocols.Http1;
|
||||
}
|
||||
|
||||
public void Tick(DateTimeOffset now)
|
||||
{
|
||||
Debug.Assert(_http1Connection != null, $"{nameof(_http1Connection)} is null");
|
||||
Debug.Assert(_http2Connection != null, $"{nameof(_http2Connection)} is null");
|
||||
|
||||
var timestamp = now.Ticks;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
using System;
|
||||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using Microsoft.AspNetCore.Hosting.Server;
|
||||
using Microsoft.AspNetCore.Protocols;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Core.Adapter.Internal;
|
||||
|
|
@ -9,14 +11,14 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
|
|||
{
|
||||
public static class HttpConnectionBuilderExtensions
|
||||
{
|
||||
public static IConnectionBuilder UseHttpServer<TContext>(this IConnectionBuilder builder, ServiceContext serviceContext, IHttpApplication<TContext> application)
|
||||
public static IConnectionBuilder UseHttpServer<TContext>(this IConnectionBuilder builder, ServiceContext serviceContext, IHttpApplication<TContext> application, HttpProtocols protocols)
|
||||
{
|
||||
return builder.UseHttpServer(Array.Empty<IConnectionAdapter>(), serviceContext, application);
|
||||
return builder.UseHttpServer(Array.Empty<IConnectionAdapter>(), serviceContext, application, protocols);
|
||||
}
|
||||
|
||||
public static IConnectionBuilder UseHttpServer<TContext>(this IConnectionBuilder builder, IList<IConnectionAdapter> adapters, ServiceContext serviceContext, IHttpApplication<TContext> application)
|
||||
public static IConnectionBuilder UseHttpServer<TContext>(this IConnectionBuilder builder, IList<IConnectionAdapter> adapters, ServiceContext serviceContext, IHttpApplication<TContext> application, HttpProtocols protocols)
|
||||
{
|
||||
var middleware = new HttpConnectionMiddleware<TContext>(adapters, serviceContext, application);
|
||||
var middleware = new HttpConnectionMiddleware<TContext>(adapters, serviceContext, application, protocols);
|
||||
return builder.Use(next =>
|
||||
{
|
||||
return middleware.OnConnectionAsync;
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
|
|||
{
|
||||
public string ConnectionId { get; set; }
|
||||
public long HttpConnectionId { get; set; }
|
||||
public HttpProtocols Protocols { get; set; }
|
||||
public ServiceContext ServiceContext { get; set; }
|
||||
public IFeatureCollection ConnectionFeatures { get; set; }
|
||||
public IList<IConnectionAdapter> ConnectionAdapters { get; set; }
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO.Pipelines;
|
||||
using System.Net;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
|
@ -8,6 +9,8 @@ using Microsoft.AspNetCore.Http.Features;
|
|||
using Microsoft.AspNetCore.Protocols;
|
||||
using Microsoft.AspNetCore.Protocols.Features;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Core.Adapter.Internal;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2;
|
||||
|
||||
namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
|
||||
{
|
||||
|
|
@ -20,11 +23,13 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
|
|||
private readonly IList<IConnectionAdapter> _connectionAdapters;
|
||||
private readonly ServiceContext _serviceContext;
|
||||
private readonly IHttpApplication<TContext> _application;
|
||||
private readonly HttpProtocols _protocols;
|
||||
|
||||
public HttpConnectionMiddleware(IList<IConnectionAdapter> adapters, ServiceContext serviceContext, IHttpApplication<TContext> application)
|
||||
public HttpConnectionMiddleware(IList<IConnectionAdapter> adapters, ServiceContext serviceContext, IHttpApplication<TContext> application, HttpProtocols protocols)
|
||||
{
|
||||
_serviceContext = serviceContext;
|
||||
_application = application;
|
||||
_protocols = protocols;
|
||||
|
||||
// Keeping these around for now so progress can be made without updating tests
|
||||
_connectionAdapters = adapters;
|
||||
|
|
@ -42,6 +47,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
|
|||
{
|
||||
ConnectionId = connectionContext.ConnectionId,
|
||||
HttpConnectionId = httpConnectionId,
|
||||
Protocols = _protocols,
|
||||
ServiceContext = _serviceContext,
|
||||
ConnectionFeatures = connectionContext.Features,
|
||||
PipeFactory = connectionContext.PipeFactory,
|
||||
|
|
|
|||
|
|
@ -135,7 +135,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core
|
|||
async Task OnBind(ListenOptions endpoint)
|
||||
{
|
||||
// Add the HTTP middleware as the terminal connection middleware
|
||||
endpoint.UseHttpServer(endpoint.ConnectionAdapters, ServiceContext, application);
|
||||
endpoint.UseHttpServer(endpoint.ConnectionAdapters, ServiceContext, application, endpoint.Protocols);
|
||||
|
||||
var connectionDelegate = endpoint.Build();
|
||||
|
||||
|
|
|
|||
|
|
@ -118,6 +118,12 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core
|
|||
/// </remarks>
|
||||
public bool NoDelay { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// The protocols enabled on this endpoint.
|
||||
/// </summary>
|
||||
/// <remarks>Defaults to HTTP/1.x only.</remarks>
|
||||
public HttpProtocols Protocols { get; set; } = HttpProtocols.Http1;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="List{IConnectionAdapter}"/> that allows each connection <see cref="System.IO.Stream"/>
|
||||
/// to be intercepted and transformed.
|
||||
|
|
|
|||
|
|
@ -1102,6 +1102,48 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core
|
|||
internal static string FormatPositiveFiniteTimeSpanRequired()
|
||||
=> GetString("PositiveFiniteTimeSpanRequired");
|
||||
|
||||
/// <summary>
|
||||
/// An endpoint must be configured to serve at least one protocol.
|
||||
/// </summary>
|
||||
internal static string EndPointRequiresAtLeastOneProtocol
|
||||
{
|
||||
get => GetString("EndPointRequiresAtLeastOneProtocol");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// An endpoint must be configured to serve at least one protocol.
|
||||
/// </summary>
|
||||
internal static string FormatEndPointRequiresAtLeastOneProtocol()
|
||||
=> GetString("EndPointRequiresAtLeastOneProtocol");
|
||||
|
||||
/// <summary>
|
||||
/// Using both HTTP/1.x and HTTP/2 on the same endpoint requires the use of TLS.
|
||||
/// </summary>
|
||||
internal static string EndPointRequiresTlsForHttp1AndHttp2
|
||||
{
|
||||
get => GetString("EndPointRequiresTlsForHttp1AndHttp2");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Using both HTTP/1.x and HTTP/2 on the same endpoint requires the use of TLS.
|
||||
/// </summary>
|
||||
internal static string FormatEndPointRequiresTlsForHttp1AndHttp2()
|
||||
=> GetString("EndPointRequiresTlsForHttp1AndHttp2");
|
||||
|
||||
/// <summary>
|
||||
/// HTTP/2 over TLS was not negotiated on an HTTP/2-only endpoint.
|
||||
/// </summary>
|
||||
internal static string EndPointHttp2NotNegotiated
|
||||
{
|
||||
get => GetString("EndPointHttp2NotNegotiated");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// HTTP/2 over TLS was not negotiated on an HTTP/2-only endpoint.
|
||||
/// </summary>
|
||||
internal static string FormatEndPointHttp2NotNegotiated()
|
||||
=> GetString("EndPointHttp2NotNegotiated");
|
||||
|
||||
private static string GetString(string name, params string[] formatterNames)
|
||||
{
|
||||
var value = _resourceManager.GetString(name);
|
||||
|
|
|
|||
|
|
@ -15,7 +15,8 @@ namespace Microsoft.AspNetCore.Hosting
|
|||
return listenOptions.UseTls(new TlsConnectionAdapterOptions
|
||||
{
|
||||
CertificatePath = certificatePath,
|
||||
PrivateKeyPath = privateKeyPath
|
||||
PrivateKeyPath = privateKeyPath,
|
||||
Protocols = listenOptions.Protocols
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,9 +4,9 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http.Features;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Core;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Core.Adapter.Internal;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Core.Features;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
|
@ -16,7 +16,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Tls
|
|||
public class TlsConnectionAdapter : IConnectionAdapter
|
||||
{
|
||||
private static readonly ClosedAdaptedConnection _closedAdaptedConnection = new ClosedAdaptedConnection();
|
||||
private static readonly HashSet<string> _serverProtocols = new HashSet<string>(new[] { "h2", "http/1.1" });
|
||||
private static readonly List<string> _serverProtocols = new List<string>();
|
||||
|
||||
private readonly TlsConnectionAdapterOptions _options;
|
||||
private readonly ILogger _logger;
|
||||
|
|
@ -47,6 +47,17 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Tls
|
|||
|
||||
_options = options;
|
||||
_logger = loggerFactory?.CreateLogger(nameof(TlsConnectionAdapter));
|
||||
|
||||
// Order is important. If HTTP/2 is enabled, we prefer it over HTTP/1.1. So add it first.
|
||||
if ((options.Protocols & HttpProtocols.Http2) == HttpProtocols.Http2)
|
||||
{
|
||||
_serverProtocols.Add("h2");
|
||||
}
|
||||
|
||||
if ((options.Protocols & HttpProtocols.Http1) == HttpProtocols.Http1)
|
||||
{
|
||||
_serverProtocols.Add("http/1.1");
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsHttps => true;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
// 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.AspNetCore.Server.Kestrel.Core;
|
||||
|
||||
namespace Microsoft.AspNetCore.Server.Kestrel.Tls
|
||||
{
|
||||
public class TlsConnectionAdapterOptions
|
||||
|
|
@ -8,5 +10,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Tls
|
|||
public string CertificatePath { get; set; } = string.Empty;
|
||||
|
||||
public string PrivateKeyPath { get; set; } = string.Empty;
|
||||
|
||||
public HttpProtocols Protocols { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -56,6 +56,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
|
|||
mockDebugger.SetupGet(g => g.IsAttached).Returns(true);
|
||||
_httpConnection.Debugger = mockDebugger.Object;
|
||||
_httpConnection.CreateHttp1Connection(new DummyApplication(), _httpConnectionContext.Transport, _httpConnectionContext.Application);
|
||||
_httpConnection.CreateHttp2Connection(new DummyApplication(), _httpConnectionContext.Transport, _httpConnectionContext.Application);
|
||||
|
||||
var now = DateTimeOffset.Now;
|
||||
_httpConnection.Tick(now);
|
||||
|
|
@ -103,6 +104,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
|
|||
_httpConnectionContext.ServiceContext.Log = logger;
|
||||
|
||||
_httpConnection.CreateHttp1Connection(new DummyApplication(), _httpConnectionContext.Transport, _httpConnectionContext.Application);
|
||||
_httpConnection.CreateHttp2Connection(new DummyApplication(), _httpConnectionContext.Transport, _httpConnectionContext.Application);
|
||||
_httpConnection.Http1Connection.Reset();
|
||||
|
||||
// Initialize timestamp
|
||||
|
|
@ -130,6 +132,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
|
|||
_httpConnectionContext.ServiceContext.Log = mockLogger.Object;
|
||||
|
||||
_httpConnection.CreateHttp1Connection(new DummyApplication(), _httpConnectionContext.Transport, _httpConnectionContext.Application);
|
||||
_httpConnection.CreateHttp2Connection(new DummyApplication(), _httpConnectionContext.Transport, _httpConnectionContext.Application);
|
||||
_httpConnection.Http1Connection.Reset();
|
||||
|
||||
// Initialize timestamp
|
||||
|
|
@ -172,6 +175,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
|
|||
_httpConnectionContext.ServiceContext.Log = mockLogger.Object;
|
||||
|
||||
_httpConnection.CreateHttp1Connection(new DummyApplication(), _httpConnectionContext.Transport, _httpConnectionContext.Application);
|
||||
_httpConnection.CreateHttp2Connection(new DummyApplication(), _httpConnectionContext.Transport, _httpConnectionContext.Application);
|
||||
_httpConnection.Http1Connection.Reset();
|
||||
|
||||
// Initialize timestamp
|
||||
|
|
@ -249,6 +253,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
|
|||
_httpConnectionContext.ServiceContext.Log = mockLogger.Object;
|
||||
|
||||
_httpConnection.CreateHttp1Connection(new DummyApplication(), _httpConnectionContext.Transport, _httpConnectionContext.Application);
|
||||
_httpConnection.CreateHttp2Connection(new DummyApplication(), _httpConnectionContext.Transport, _httpConnectionContext.Application);
|
||||
_httpConnection.Http1Connection.Reset();
|
||||
|
||||
// Initialize timestamp
|
||||
|
|
@ -317,6 +322,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
|
|||
_httpConnectionContext.ServiceContext.Log = mockLogger.Object;
|
||||
|
||||
_httpConnection.CreateHttp1Connection(new DummyApplication(), _httpConnectionContext.Transport, _httpConnectionContext.Application);
|
||||
_httpConnection.CreateHttp2Connection(new DummyApplication(), _httpConnectionContext.Transport, _httpConnectionContext.Application);
|
||||
_httpConnection.Http1Connection.Reset();
|
||||
|
||||
// Initialize timestamp
|
||||
|
|
@ -379,6 +385,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
|
|||
_httpConnectionContext.ServiceContext.Log = mockLogger.Object;
|
||||
|
||||
_httpConnection.CreateHttp1Connection(new DummyApplication(), _httpConnectionContext.Transport, _httpConnectionContext.Application);
|
||||
_httpConnection.CreateHttp2Connection(new DummyApplication(), _httpConnectionContext.Transport, _httpConnectionContext.Application);
|
||||
_httpConnection.Http1Connection.Reset();
|
||||
|
||||
var startTime = systemClock.UtcNow;
|
||||
|
|
@ -420,6 +427,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
|
|||
_httpConnectionContext.ServiceContext.Log = mockLogger.Object;
|
||||
|
||||
_httpConnection.CreateHttp1Connection(new DummyApplication(), _httpConnectionContext.Transport, _httpConnectionContext.Application);
|
||||
_httpConnection.CreateHttp2Connection(new DummyApplication(), _httpConnectionContext.Transport, _httpConnectionContext.Application);
|
||||
_httpConnection.Http1Connection.Reset();
|
||||
_httpConnection.Http1Connection.RequestAborted.Register(() =>
|
||||
{
|
||||
|
|
@ -454,6 +462,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
|
|||
_httpConnectionContext.ServiceContext.Log = mockLogger.Object;
|
||||
|
||||
_httpConnection.CreateHttp1Connection(new DummyApplication(), _httpConnectionContext.Transport, _httpConnectionContext.Application);
|
||||
_httpConnection.CreateHttp2Connection(new DummyApplication(), _httpConnectionContext.Transport, _httpConnectionContext.Application);
|
||||
_httpConnection.Http1Connection.Reset();
|
||||
_httpConnection.Http1Connection.RequestAborted.Register(() =>
|
||||
{
|
||||
|
|
@ -496,6 +505,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
|
|||
_httpConnectionContext.ServiceContext.Log = mockLogger.Object;
|
||||
|
||||
_httpConnection.CreateHttp1Connection(new DummyApplication(), _httpConnectionContext.Transport, _httpConnectionContext.Application);
|
||||
_httpConnection.CreateHttp2Connection(new DummyApplication(), _httpConnectionContext.Transport, _httpConnectionContext.Application);
|
||||
_httpConnection.Http1Connection.Reset();
|
||||
_httpConnection.Http1Connection.RequestAborted.Register(() =>
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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.Net;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
|
||||
{
|
||||
public class ListenOptionsTests
|
||||
{
|
||||
[Fact]
|
||||
public void ProtocolsDefault()
|
||||
{
|
||||
var listenOptions = new ListenOptions(new IPEndPoint(IPAddress.Loopback, 0));
|
||||
Assert.Equal(HttpProtocols.Http1, listenOptions.Protocols);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,96 @@
|
|||
// 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.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Core;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2;
|
||||
using Microsoft.AspNetCore.Testing;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
|
||||
{
|
||||
public class HttpProtocolSelectionTests
|
||||
{
|
||||
[Fact]
|
||||
public Task Server_NoProtocols_Error()
|
||||
{
|
||||
return TestError<InvalidOperationException>(HttpProtocols.None, CoreStrings.EndPointRequiresAtLeastOneProtocol);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public Task Server_Http1AndHttp2_Cleartext_Error()
|
||||
{
|
||||
return TestError<InvalidOperationException>(HttpProtocols.Http1AndHttp2, CoreStrings.EndPointRequiresTlsForHttp1AndHttp2);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public Task Server_Http1Only_Cleartext_Success()
|
||||
{
|
||||
return TestSuccess(HttpProtocols.Http1, "GET / HTTP/1.1\r\nHost:\r\n\r\n", "HTTP/1.1 200 OK");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public Task Server_Http2Only_Cleartext_Success()
|
||||
{
|
||||
// Expect a SETTINGS frame (type 0x4) with no payload and no flags
|
||||
return TestSuccess(HttpProtocols.Http2, Encoding.ASCII.GetString(Http2Connection.ClientPreface), "\x00\x00\x00\x04\x00\x00\x00\x00\x00");
|
||||
}
|
||||
|
||||
private async Task TestSuccess(HttpProtocols serverProtocols, string request, string expectedResponse)
|
||||
{
|
||||
var builder = new WebHostBuilder()
|
||||
.UseKestrel(options =>
|
||||
{
|
||||
options.Listen(IPAddress.Loopback, 0, listenOptions => listenOptions.Protocols = serverProtocols);
|
||||
})
|
||||
.Configure(app => app.Run(context => Task.CompletedTask));
|
||||
|
||||
using (var host = builder.Build())
|
||||
{
|
||||
host.Start();
|
||||
|
||||
using (var connection = new TestConnection(host.GetPort()))
|
||||
{
|
||||
await connection.Send(request);
|
||||
await connection.Receive(expectedResponse);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task TestError<TException>(HttpProtocols serverProtocols, string expectedErrorMessage)
|
||||
where TException : Exception
|
||||
{
|
||||
var logger = new TestApplicationErrorLogger();
|
||||
var loggerProvider = new Mock<ILoggerProvider>();
|
||||
loggerProvider
|
||||
.Setup(provider => provider.CreateLogger(It.IsAny<string>()))
|
||||
.Returns(logger);
|
||||
|
||||
var builder = new WebHostBuilder()
|
||||
.ConfigureLogging(loggingBuilder => loggingBuilder.AddProvider(loggerProvider.Object))
|
||||
.UseKestrel(options => options.Listen(IPAddress.Loopback, 0, listenOptions => listenOptions.Protocols = serverProtocols))
|
||||
.Configure(app => app.Run(context => Task.CompletedTask));
|
||||
|
||||
using (var host = builder.Build())
|
||||
{
|
||||
host.Start();
|
||||
|
||||
using (var connection = new TestConnection(host.GetPort()))
|
||||
{
|
||||
await connection.WaitForConnectionClose().TimeoutAfter(TimeSpan.FromSeconds(30));
|
||||
}
|
||||
}
|
||||
|
||||
Assert.Single(logger.Messages, message => message.LogLevel == LogLevel.Error
|
||||
&& message.EventId.Id == 0
|
||||
&& message.Message == expectedErrorMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -54,7 +54,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Tests
|
|||
public async Task ConnectionCanReadAndWrite(ListenOptions listenOptions)
|
||||
{
|
||||
var serviceContext = new TestServiceContext();
|
||||
listenOptions.UseHttpServer(listenOptions.ConnectionAdapters, serviceContext, new DummyApplication(TestApp.EchoApp));
|
||||
listenOptions.UseHttpServer(listenOptions.ConnectionAdapters, serviceContext, new DummyApplication(TestApp.EchoApp), HttpProtocols.Http1);
|
||||
|
||||
var transportContext = new TestLibuvTransportContext()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -33,12 +33,12 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Tests
|
|||
var serviceContextPrimary = new TestServiceContext();
|
||||
var transportContextPrimary = new TestLibuvTransportContext();
|
||||
var builderPrimary = new ConnectionBuilder();
|
||||
builderPrimary.UseHttpServer(serviceContextPrimary, new DummyApplication(c => c.Response.WriteAsync("Primary")));
|
||||
builderPrimary.UseHttpServer(serviceContextPrimary, new DummyApplication(c => c.Response.WriteAsync("Primary")), HttpProtocols.Http1);
|
||||
transportContextPrimary.ConnectionHandler = new ConnectionHandler(serviceContextPrimary, builderPrimary.Build());
|
||||
|
||||
var serviceContextSecondary = new TestServiceContext();
|
||||
var builderSecondary = new ConnectionBuilder();
|
||||
builderSecondary.UseHttpServer(serviceContextSecondary, new DummyApplication(c => c.Response.WriteAsync("Secondary")));
|
||||
builderSecondary.UseHttpServer(serviceContextSecondary, new DummyApplication(c => c.Response.WriteAsync("Secondary")), HttpProtocols.Http1);
|
||||
var transportContextSecondary = new TestLibuvTransportContext();
|
||||
transportContextSecondary.ConnectionHandler = new ConnectionHandler(serviceContextSecondary, builderSecondary.Build());
|
||||
|
||||
|
|
@ -101,7 +101,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Tests
|
|||
|
||||
var serviceContextPrimary = new TestServiceContext();
|
||||
var builderPrimary = new ConnectionBuilder();
|
||||
builderPrimary.UseHttpServer(serviceContextPrimary, new DummyApplication(c => c.Response.WriteAsync("Primary")));
|
||||
builderPrimary.UseHttpServer(serviceContextPrimary, new DummyApplication(c => c.Response.WriteAsync("Primary")), HttpProtocols.Http1);
|
||||
var transportContextPrimary = new TestLibuvTransportContext() { Log = new LibuvTrace(logger) };
|
||||
transportContextPrimary.ConnectionHandler = new ConnectionHandler(serviceContextPrimary, builderPrimary.Build());
|
||||
|
||||
|
|
@ -113,7 +113,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Tests
|
|||
HttpParserFactory = serviceContextPrimary.HttpParserFactory,
|
||||
};
|
||||
var builderSecondary = new ConnectionBuilder();
|
||||
builderSecondary.UseHttpServer(serviceContextSecondary, new DummyApplication(c => c.Response.WriteAsync("Secondary")));
|
||||
builderSecondary.UseHttpServer(serviceContextSecondary, new DummyApplication(c => c.Response.WriteAsync("Secondary")), HttpProtocols.Http1);
|
||||
var transportContextSecondary = new TestLibuvTransportContext();
|
||||
transportContextSecondary.ConnectionHandler = new ConnectionHandler(serviceContextSecondary, builderSecondary.Build());
|
||||
|
||||
|
|
@ -212,7 +212,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Tests
|
|||
|
||||
var serviceContextPrimary = new TestServiceContext();
|
||||
var builderPrimary = new ConnectionBuilder();
|
||||
builderPrimary.UseHttpServer(serviceContextPrimary, new DummyApplication(c => c.Response.WriteAsync("Primary")));
|
||||
builderPrimary.UseHttpServer(serviceContextPrimary, new DummyApplication(c => c.Response.WriteAsync("Primary")), HttpProtocols.Http1);
|
||||
var transportContextPrimary = new TestLibuvTransportContext() { Log = new LibuvTrace(logger) };
|
||||
transportContextPrimary.ConnectionHandler = new ConnectionHandler(serviceContextPrimary, builderPrimary.Build());
|
||||
|
||||
|
|
@ -224,7 +224,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Tests
|
|||
HttpParserFactory = serviceContextPrimary.HttpParserFactory,
|
||||
};
|
||||
var builderSecondary = new ConnectionBuilder();
|
||||
builderSecondary.UseHttpServer(serviceContextSecondary, new DummyApplication(c => c.Response.WriteAsync("Secondary")));
|
||||
builderSecondary.UseHttpServer(serviceContextSecondary, new DummyApplication(c => c.Response.WriteAsync("Secondary")), HttpProtocols.Http1);
|
||||
var transportContextSecondary = new TestLibuvTransportContext();
|
||||
transportContextSecondary.ConnectionHandler = new ConnectionHandler(serviceContextSecondary, builderSecondary.Build());
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue