/
githubmirror
/
aspnetcore
Обзор
Документация
Войти
/
githubmirror
/
aspnetcore
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/SignalR/common/Http.Connections/test/HttpConnectionManagerTests.cs
444 строки
17 KB
William Godbe
Rename Microsoft.Aspnetcore.Testing to Microsoft.AspNetCore.InternalTesting (#51713)
02 ноя 2023, 03:15
Не верифицирован
02 ноя 2023, 03:15
fc8deca
Код
Авторство
О чём код?
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System; using System.Diagnostics.Metrics; using System.IO.Pipelines; using System.Net; using System.Threading.Tasks; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http.Connections.Internal; using Microsoft.AspNetCore.Server.Kestrel.Core; using Microsoft.AspNetCore.SignalR.Tests; using Microsoft.AspNetCore.InternalTesting; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Xunit; namespace Microsoft.AspNetCore.Http.Connections.Tests; public class HttpConnectionManagerTests : VerifiableLoggedTest { [Fact] public void HttpConnectionDispatcherOptionsDefaults() { var options = new HttpConnectionDispatcherOptions(); Assert.Equal(TimeSpan.FromSeconds(10), options.TransportSendTimeout); Assert.Equal(65536, options.TransportMaxBufferSize); Assert.Equal(65536, options.ApplicationMaxBufferSize); Assert.Equal(HttpTransports.All, options.Transports); Assert.False(options.CloseOnAuthenticationExpiration); } [Fact] public void HttpConnectionDispatcherOptionsNegativeBufferSizeThrows() { var httpOptions = new HttpConnectionDispatcherOptions(); Assert.Throws<ArgumentOutOfRangeException>(() => httpOptions.TransportMaxBufferSize = -1); Assert.Throws<ArgumentOutOfRangeException>(() => httpOptions.ApplicationMaxBufferSize = -1); } [Fact] public void NewConnectionsHaveConnectionId() { using (StartVerifiableLog()) { var connectionManager = CreateConnectionManager(LoggerFactory); var connection = connectionManager.CreateConnection(); Assert.NotNull(connection.ConnectionId); Assert.Equal(HttpConnectionStatus.Inactive, connection.Status); Assert.Null(connection.ApplicationTask); Assert.Null(connection.TransportTask); Assert.Null(connection.Cancellation); Assert.NotEqual(default, connection.LastSeenTicks); Assert.NotNull(connection.Transport); Assert.NotNull(connection.Application); } } [Theory] [InlineData(ConnectionStates.ClosedUngracefully | ConnectionStates.ApplicationNotFaulted | ConnectionStates.TransportNotFaulted)] [InlineData(ConnectionStates.ClosedUngracefully | ConnectionStates.ApplicationNotFaulted | ConnectionStates.TransportFaulted)] [InlineData(ConnectionStates.ClosedUngracefully | ConnectionStates.ApplicationFaulted | ConnectionStates.TransportFaulted)] [InlineData(ConnectionStates.ClosedUngracefully | ConnectionStates.ApplicationFaulted | ConnectionStates.TransportNotFaulted)] [InlineData(ConnectionStates.CloseGracefully | ConnectionStates.ApplicationNotFaulted | ConnectionStates.TransportNotFaulted)] [InlineData(ConnectionStates.CloseGracefully | ConnectionStates.ApplicationNotFaulted | ConnectionStates.TransportFaulted)] [InlineData(ConnectionStates.CloseGracefully | ConnectionStates.ApplicationFaulted | ConnectionStates.TransportFaulted)] [InlineData(ConnectionStates.CloseGracefully | ConnectionStates.ApplicationFaulted | ConnectionStates.TransportNotFaulted)] public async Task DisposingConnectionsClosesBothSidesOfThePipe(ConnectionStates states) { using (StartVerifiableLog()) { var closeGracefully = (states & ConnectionStates.CloseGracefully) != 0; var applicationFaulted = (states & ConnectionStates.ApplicationFaulted) != 0; var transportFaulted = (states & ConnectionStates.TransportFaulted) != 0; var connectionManager = CreateConnectionManager(LoggerFactory); var connection = connectionManager.CreateConnection(); if (applicationFaulted) { // If the application is faulted then we want to make sure the transport task only completes after // the application completes connection.ApplicationTask = Task.FromException(new Exception("Application failed")); connection.TransportTask = Task.Run(async () => { // Wait for the application to end var result = await connection.Application.Input.ReadAsync(); connection.Application.Input.AdvanceTo(result.Buffer.End); if (transportFaulted) { throw new Exception("Transport failed"); } return false; }); } else if (transportFaulted) { // If the transport is faulted then we want to make sure the transport task only completes after // the application completes connection.TransportTask = Task.FromException<bool>(new Exception("Application failed")); connection.ApplicationTask = Task.Run(async () => { // Wait for the application to end var result = await connection.Transport.Input.ReadAsync(); connection.Transport.Input.AdvanceTo(result.Buffer.End); }); } else { connection.ApplicationTask = Task.CompletedTask; connection.TransportTask = Task.FromResult(true); } try { await connection.DisposeAsync(closeGracefully).DefaultTimeout(); } catch (Exception ex) when (!(ex is TimeoutException)) { // Ignore the exception that bubbles out of the failing task } var result = await connection.Transport.Output.FlushAsync(); Assert.True(result.IsCompleted); result = await connection.Application.Output.FlushAsync(); Assert.True(result.IsCompleted); var exception = await Assert.ThrowsAsync<InvalidOperationException>(async () => await connection.Transport.Input.ReadAsync()); Assert.Equal("Reading is not allowed after reader was completed.", exception.Message); exception = await Assert.ThrowsAsync<InvalidOperationException>(async () => await connection.Application.Input.ReadAsync()); Assert.Equal("Reading is not allowed after reader was completed.", exception.Message); } } [Fact] public void NewConnectionsCanBeRetrieved() { using (StartVerifiableLog()) { var connectionManager = CreateConnectionManager(LoggerFactory); var connection = connectionManager.CreateConnection(); Assert.NotNull(connection.ConnectionId); Assert.True(connectionManager.TryGetConnection(connection.ConnectionToken, out var newConnection)); Assert.Same(newConnection, connection); } } [Fact] public void AddNewConnection() { using (StartVerifiableLog()) { var connectionManager = CreateConnectionManager(LoggerFactory); var connection = connectionManager.CreateConnection(); var transport = connection.Transport; Assert.NotNull(connection.ConnectionId); Assert.NotNull(connection.ConnectionToken); Assert.NotNull(transport); Assert.True(connectionManager.TryGetConnection(connection.ConnectionToken, out var newConnection)); Assert.Same(newConnection, connection); Assert.Same(transport, newConnection.Transport); } } [Fact] public void RemoveConnection() { using (StartVerifiableLog()) { var connectionManager = CreateConnectionManager(LoggerFactory); var connection = connectionManager.CreateConnection(); var transport = connection.Transport; Assert.NotNull(connection.ConnectionId); Assert.NotNull(transport); Assert.True(connectionManager.TryGetConnection(connection.ConnectionToken, out var newConnection)); Assert.Same(newConnection, connection); Assert.Same(transport, newConnection.Transport); connectionManager.RemoveConnection(connection.ConnectionToken, connection.TransportType, HttpConnectionStopStatus.Timeout); Assert.False(connectionManager.TryGetConnection(connection.ConnectionToken, out newConnection)); } } [Fact] public void ConnectionIdAndConnectionTokenAreTheSameForNegotiateVersionZero() { using (StartVerifiableLog()) { var connectionManager = CreateConnectionManager(LoggerFactory); var connection = connectionManager.CreateConnection(new(), negotiateVersion: 0); var transport = connection.Transport; Assert.NotNull(connection.ConnectionId); Assert.NotNull(transport); Assert.True(connectionManager.TryGetConnection(connection.ConnectionToken, out var newConnection)); Assert.Same(newConnection, connection); Assert.Same(transport, newConnection.Transport); Assert.Equal(connection.ConnectionId, connection.ConnectionToken); } } [Fact] public void ConnectionIdAndConnectionTokenAreDifferentForNegotiateVersionOne() { using (StartVerifiableLog()) { var connectionManager = CreateConnectionManager(LoggerFactory); var connection = connectionManager.CreateConnection(new(), negotiateVersion: 1); var transport = connection.Transport; Assert.NotNull(connection.ConnectionId); Assert.NotNull(transport); Assert.True(connectionManager.TryGetConnection(connection.ConnectionToken, out var newConnection)); Assert.False(connectionManager.TryGetConnection(connection.ConnectionId, out var _)); Assert.Same(newConnection, connection); Assert.Same(transport, newConnection.Transport); Assert.NotEqual(connection.ConnectionId, connection.ConnectionToken); } } [Fact] public async Task CloseConnectionsEndsAllPendingConnections() { using (StartVerifiableLog()) { var connectionManager = CreateConnectionManager(LoggerFactory); var connection = connectionManager.CreateConnection(); connection.ApplicationTask = Task.Run(async () => { var result = await connection.Transport.Input.ReadAsync(); try { Assert.True(result.IsCompleted); } finally { connection.Transport.Input.AdvanceTo(result.Buffer.End); } }); connection.TransportTask = Task.Run(async () => { var result = await connection.Application.Input.ReadAsync(); try { Assert.True(result.IsCanceled); } finally { connection.Application.Input.AdvanceTo(result.Buffer.End); } return true; }); connectionManager.CloseConnections(); await connection.DisposeAsync(); } } [Fact] public async Task DisposingConnectionMultipleTimesWaitsOnConnectionClose() { using (StartVerifiableLog()) { var connectionManager = CreateConnectionManager(LoggerFactory); var connection = connectionManager.CreateConnection(); var tcs = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously); connection.ApplicationTask = tcs.Task; connection.TransportTask = tcs.Task; var firstTask = connection.DisposeAsync(); var secondTask = connection.DisposeAsync(); Assert.False(firstTask.IsCompleted); Assert.False(secondTask.IsCompleted); tcs.TrySetResult(true); await Task.WhenAll(firstTask, secondTask).DefaultTimeout(); } } [Fact] public async Task DisposingConnectionMultipleGetsExceptionFromTransportOrApp() { using (StartVerifiableLog()) { var connectionManager = CreateConnectionManager(LoggerFactory); var connection = connectionManager.CreateConnection(); var tcs = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously); connection.ApplicationTask = tcs.Task; connection.TransportTask = tcs.Task; var firstTask = connection.DisposeAsync(); var secondTask = connection.DisposeAsync(); Assert.False(firstTask.IsCompleted); Assert.False(secondTask.IsCompleted); tcs.TrySetException(new InvalidOperationException("Error")); var exception = await Assert.ThrowsAsync<InvalidOperationException>(async () => await firstTask.DefaultTimeout()); Assert.Equal("Error", exception.Message); exception = await Assert.ThrowsAsync<InvalidOperationException>(async () => await secondTask.DefaultTimeout()); Assert.Equal("Error", exception.Message); } } [Fact] public async Task DisposingConnectionMultipleGetsCancellation() { using (StartVerifiableLog()) { var connectionManager = CreateConnectionManager(LoggerFactory); var connection = connectionManager.CreateConnection(); var tcs = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously); connection.ApplicationTask = tcs.Task; connection.TransportTask = tcs.Task; var firstTask = connection.DisposeAsync(); var secondTask = connection.DisposeAsync(); Assert.False(firstTask.IsCompleted); Assert.False(secondTask.IsCompleted); tcs.TrySetCanceled(); await Assert.ThrowsAsync<TaskCanceledException>(async () => await firstTask.DefaultTimeout()); await Assert.ThrowsAsync<TaskCanceledException>(async () => await secondTask.DefaultTimeout()); } } [Fact] public async Task DisposeInactiveConnection() { using (StartVerifiableLog()) { var connectionManager = CreateConnectionManager(LoggerFactory); var connection = connectionManager.CreateConnection(); Assert.NotNull(connection.ConnectionId); Assert.NotNull(connection.Transport); await connection.DisposeAsync(); Assert.Equal(HttpConnectionStatus.Disposed, connection.Status); } } [Fact] public async Task DisposeInactiveConnectionWithNoPipes() { using (StartVerifiableLog()) { var connectionManager = CreateConnectionManager(LoggerFactory); var connection = connectionManager.CreateConnection(); Assert.NotNull(connection.ConnectionId); Assert.NotNull(connection.Transport); Assert.NotNull(connection.Application); await connection.DisposeAsync(); Assert.Equal(HttpConnectionStatus.Disposed, connection.Status); } } [Fact] public async Task ApplicationLifetimeIsHookedUp() { using (StartVerifiableLog()) { var appLifetime = new TestApplicationLifetime(); var connectionManager = CreateConnectionManager(LoggerFactory, appLifetime); appLifetime.Start(); var connection = connectionManager.CreateConnection(); appLifetime.StopApplication(); var result = await connection.Application.Output.FlushAsync(); Assert.True(result.IsCompleted); } } [Fact] public async Task ApplicationLifetimeCanStartBeforeHttpConnectionManagerInitialized() { using (StartVerifiableLog()) { var appLifetime = new TestApplicationLifetime(); appLifetime.Start(); var connectionManager = CreateConnectionManager(LoggerFactory, appLifetime); var connection = connectionManager.CreateConnection(); appLifetime.StopApplication(); var result = await connection.Application.Output.FlushAsync(); Assert.True(result.IsCompleted); } } private static HttpConnectionManager CreateConnectionManager(ILoggerFactory loggerFactory, IHostApplicationLifetime lifetime = null, HttpConnectionsMetrics metrics = null) { lifetime ??= new EmptyApplicationLifetime(); return new HttpConnectionManager(loggerFactory, lifetime, Options.Create(new ConnectionOptions()), metrics ?? new HttpConnectionsMetrics(new TestMeterFactory())); } [Flags] public enum ConnectionStates { ClosedUngracefully = 1, ApplicationNotFaulted = 2, TransportNotFaulted = 4, ApplicationFaulted = 8, TransportFaulted = 16, CloseGracefully = 32 } }