Configure enabled protocols per endpoint and add HTTP/2 w/ prior knowledge support (#2067).

This commit is contained in:
Cesar Blum Silveira 2017-09-27 10:14:39 -07:00 committed by GitHub
parent eb9417b577
commit 551c1ebc0b
17 changed files with 292 additions and 23 deletions

View File

@ -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>

View File

@ -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,
}
}

View File

@ -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;

View File

@ -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;

View File

@ -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; }

View File

@ -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,

View File

@ -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();

View File

@ -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.

View File

@ -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);

View File

@ -15,7 +15,8 @@ namespace Microsoft.AspNetCore.Hosting
return listenOptions.UseTls(new TlsConnectionAdapterOptions
{
CertificatePath = certificatePath,
PrivateKeyPath = privateKeyPath
PrivateKeyPath = privateKeyPath,
Protocols = listenOptions.Protocols
});
}

View File

@ -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;

View File

@ -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; }
}
}

View File

@ -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(() =>
{

View File

@ -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);
}
}
}

View File

@ -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);
}
}
}

View File

@ -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()
{

View File

@ -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());