Merge pull request #3243 from dotnet-maestro-bot/merge/release/2.2-to-master

[automated] Merge branch 'release/2.2' => 'master'
This commit is contained in:
BrennanConroy 2018-10-31 16:28:19 -07:00 committed by GitHub
commit e5e51ac6ca
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 389 additions and 302 deletions

View File

@ -7,27 +7,37 @@ using System.IO.Pipelines;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http.Connections.Internal; using Microsoft.AspNetCore.Http.Connections.Internal;
using Microsoft.AspNetCore.SignalR.Tests;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Xunit; using Xunit;
using Xunit.Abstractions;
namespace Microsoft.AspNetCore.Http.Connections.Tests namespace Microsoft.AspNetCore.Http.Connections.Tests
{ {
public class HttpConnectionManagerTests public class HttpConnectionManagerTests : VerifiableLoggedTest
{ {
public HttpConnectionManagerTests(ITestOutputHelper output)
: base(output)
{
}
[Fact] [Fact]
public void NewConnectionsHaveConnectionId() public void NewConnectionsHaveConnectionId()
{ {
var connectionManager = CreateConnectionManager(); using (StartVerifiableLog(out var loggerFactory))
var connection = connectionManager.CreateConnection(); {
var connectionManager = CreateConnectionManager(loggerFactory);
var connection = connectionManager.CreateConnection();
Assert.NotNull(connection.ConnectionId); Assert.NotNull(connection.ConnectionId);
Assert.Equal(HttpConnectionStatus.Inactive, connection.Status); Assert.Equal(HttpConnectionStatus.Inactive, connection.Status);
Assert.Null(connection.ApplicationTask); Assert.Null(connection.ApplicationTask);
Assert.Null(connection.TransportTask); Assert.Null(connection.TransportTask);
Assert.Null(connection.Cancellation); Assert.Null(connection.Cancellation);
Assert.NotEqual(default, connection.LastSeenUtc); Assert.NotEqual(default, connection.LastSeenUtc);
Assert.NotNull(connection.Transport); Assert.NotNull(connection.Transport);
Assert.NotNull(connection.Application); Assert.NotNull(connection.Application);
}
} }
[Theory] [Theory]
@ -42,305 +52,341 @@ namespace Microsoft.AspNetCore.Http.Connections.Tests
[InlineData(ConnectionStates.CloseGracefully | ConnectionStates.ApplicationFaulted | ConnectionStates.TransportNotFaulted)] [InlineData(ConnectionStates.CloseGracefully | ConnectionStates.ApplicationFaulted | ConnectionStates.TransportNotFaulted)]
public async Task DisposingConnectionsClosesBothSidesOfThePipe(ConnectionStates states) public async Task DisposingConnectionsClosesBothSidesOfThePipe(ConnectionStates states)
{ {
var closeGracefully = (states & ConnectionStates.CloseGracefully) != 0; using (StartVerifiableLog(out var loggerFactory))
var applicationFaulted = (states & ConnectionStates.ApplicationFaulted) != 0;
var transportFaulted = (states & ConnectionStates.TransportFaulted) != 0;
var connectionManager = CreateConnectionManager();
var connection = connectionManager.CreateConnection();
if (applicationFaulted)
{ {
// If the application is faulted then we want to make sure the transport task only completes after var closeGracefully = (states & ConnectionStates.CloseGracefully) != 0;
// the application completes var applicationFaulted = (states & ConnectionStates.ApplicationFaulted) != 0;
connection.ApplicationTask = Task.FromException(new Exception("Application failed")); var transportFaulted = (states & ConnectionStates.TransportFaulted) != 0;
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) 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 () =>
{ {
throw new Exception("Transport failed"); // Wait for the application to end
} var result = await connection.Application.Input.ReadAsync();
}); connection.Application.Input.AdvanceTo(result.Buffer.End);
} if (transportFaulted)
else if (transportFaulted) {
{ throw new Exception("Transport failed");
// If the transport is faulted then we want to make sure the transport task only completes after }
// the application completes });
connection.TransportTask = Task.FromException(new Exception("Application failed"));
connection.ApplicationTask = Task.Run(async () => }
else if (transportFaulted)
{ {
// Wait for the application to end // If the transport is faulted then we want to make sure the transport task only completes after
var result = await connection.Transport.Input.ReadAsync(); // the application completes
connection.Transport.Input.AdvanceTo(result.Buffer.End); connection.TransportTask = Task.FromException(new Exception("Application failed"));
}); connection.ApplicationTask = Task.Run(async () =>
} {
else // Wait for the application to end
{ var result = await connection.Transport.Input.ReadAsync();
connection.ApplicationTask = Task.CompletedTask; connection.Transport.Input.AdvanceTo(result.Buffer.End);
connection.TransportTask = Task.CompletedTask; });
} }
else
{
connection.ApplicationTask = Task.CompletedTask;
connection.TransportTask = Task.CompletedTask;
}
var applicationInputTcs = new TaskCompletionSource<object>(); var applicationInputTcs = new TaskCompletionSource<object>();
var applicationOutputTcs = new TaskCompletionSource<object>(); var applicationOutputTcs = new TaskCompletionSource<object>();
var transportInputTcs = new TaskCompletionSource<object>(); var transportInputTcs = new TaskCompletionSource<object>();
var transportOutputTcs = new TaskCompletionSource<object>(); var transportOutputTcs = new TaskCompletionSource<object>();
connection.Transport.Input.OnWriterCompleted((_, __) => transportInputTcs.TrySetResult(null), null); connection.Transport.Input.OnWriterCompleted((_, __) => transportInputTcs.TrySetResult(null), null);
connection.Transport.Output.OnReaderCompleted((_, __) => transportOutputTcs.TrySetResult(null), null); connection.Transport.Output.OnReaderCompleted((_, __) => transportOutputTcs.TrySetResult(null), null);
connection.Application.Input.OnWriterCompleted((_, __) => applicationInputTcs.TrySetResult(null), null); connection.Application.Input.OnWriterCompleted((_, __) => applicationInputTcs.TrySetResult(null), null);
connection.Application.Output.OnReaderCompleted((_, __) => applicationOutputTcs.TrySetResult(null), null); connection.Application.Output.OnReaderCompleted((_, __) => applicationOutputTcs.TrySetResult(null), null);
try try
{ {
await connection.DisposeAsync(closeGracefully).OrTimeout(); await connection.DisposeAsync(closeGracefully).OrTimeout();
}
catch (Exception ex) when (!(ex is TimeoutException))
{
// Ignore the exception that bubbles out of the failing task
}
await Task.WhenAll(applicationInputTcs.Task, applicationOutputTcs.Task, transportInputTcs.Task, transportOutputTcs.Task).OrTimeout();
} }
catch (Exception ex) when (!(ex is TimeoutException))
{
// Ignore the exception that bubbles out of the failing task
}
await Task.WhenAll(applicationInputTcs.Task, applicationOutputTcs.Task, transportInputTcs.Task, transportOutputTcs.Task).OrTimeout();
} }
[Fact] [Fact]
public void NewConnectionsCanBeRetrieved() public void NewConnectionsCanBeRetrieved()
{ {
var connectionManager = CreateConnectionManager(); using (StartVerifiableLog(out var loggerFactory))
var connection = connectionManager.CreateConnection(); {
var connectionManager = CreateConnectionManager(loggerFactory);
var connection = connectionManager.CreateConnection();
Assert.NotNull(connection.ConnectionId); Assert.NotNull(connection.ConnectionId);
Assert.True(connectionManager.TryGetConnection(connection.ConnectionId, out var newConnection)); Assert.True(connectionManager.TryGetConnection(connection.ConnectionId, out var newConnection));
Assert.Same(newConnection, connection); Assert.Same(newConnection, connection);
}
} }
[Fact] [Fact]
public void AddNewConnection() public void AddNewConnection()
{ {
var connectionManager = CreateConnectionManager(); using (StartVerifiableLog(out var loggerFactory))
var connection = connectionManager.CreateConnection(PipeOptions.Default, PipeOptions.Default); {
var connectionManager = CreateConnectionManager(loggerFactory);
var connection = connectionManager.CreateConnection(PipeOptions.Default, PipeOptions.Default);
var transport = connection.Transport; var transport = connection.Transport;
Assert.NotNull(connection.ConnectionId); Assert.NotNull(connection.ConnectionId);
Assert.NotNull(transport); Assert.NotNull(transport);
Assert.True(connectionManager.TryGetConnection(connection.ConnectionId, out var newConnection)); Assert.True(connectionManager.TryGetConnection(connection.ConnectionId, out var newConnection));
Assert.Same(newConnection, connection); Assert.Same(newConnection, connection);
Assert.Same(transport, newConnection.Transport); Assert.Same(transport, newConnection.Transport);
}
} }
[Fact] [Fact]
public void RemoveConnection() public void RemoveConnection()
{ {
var connectionManager = CreateConnectionManager(); using (StartVerifiableLog(out var loggerFactory))
var connection = connectionManager.CreateConnection(PipeOptions.Default, PipeOptions.Default); {
var connectionManager = CreateConnectionManager(loggerFactory);
var connection = connectionManager.CreateConnection(PipeOptions.Default, PipeOptions.Default);
var transport = connection.Transport; var transport = connection.Transport;
Assert.NotNull(connection.ConnectionId); Assert.NotNull(connection.ConnectionId);
Assert.NotNull(transport); Assert.NotNull(transport);
Assert.True(connectionManager.TryGetConnection(connection.ConnectionId, out var newConnection)); Assert.True(connectionManager.TryGetConnection(connection.ConnectionId, out var newConnection));
Assert.Same(newConnection, connection); Assert.Same(newConnection, connection);
Assert.Same(transport, newConnection.Transport); Assert.Same(transport, newConnection.Transport);
connectionManager.RemoveConnection(connection.ConnectionId); connectionManager.RemoveConnection(connection.ConnectionId);
Assert.False(connectionManager.TryGetConnection(connection.ConnectionId, out newConnection)); Assert.False(connectionManager.TryGetConnection(connection.ConnectionId, out newConnection));
}
} }
[Fact] [Fact]
public async Task CloseConnectionsEndsAllPendingConnections() public async Task CloseConnectionsEndsAllPendingConnections()
{ {
var connectionManager = CreateConnectionManager(); using (StartVerifiableLog(out var loggerFactory))
var connection = connectionManager.CreateConnection(PipeOptions.Default, PipeOptions.Default);
connection.ApplicationTask = Task.Run(async () =>
{ {
var result = await connection.Transport.Input.ReadAsync(); var connectionManager = CreateConnectionManager(loggerFactory);
var connection = connectionManager.CreateConnection(PipeOptions.Default, PipeOptions.Default);
try connection.ApplicationTask = Task.Run(async () =>
{ {
Assert.True(result.IsCompleted); var result = await connection.Transport.Input.ReadAsync();
// We should be able to write try
await connection.Transport.Output.WriteAsync(new byte[] { 1 }); {
} Assert.True(result.IsCompleted);
finally
{
connection.Transport.Input.AdvanceTo(result.Buffer.End);
}
});
connection.TransportTask = Task.Run(async () => // We should be able to write
{ await connection.Transport.Output.WriteAsync(new byte[] { 1 });
var result = await connection.Application.Input.ReadAsync(); }
Assert.Equal(new byte[] { 1 }, result.Buffer.ToArray()); finally
connection.Application.Input.AdvanceTo(result.Buffer.End); {
connection.Transport.Input.AdvanceTo(result.Buffer.End);
}
});
result = await connection.Application.Input.ReadAsync(); connection.TransportTask = Task.Run(async () =>
try
{
Assert.True(result.IsCompleted);
}
finally
{ {
var result = await connection.Application.Input.ReadAsync();
Assert.Equal(new byte[] { 1 }, result.Buffer.ToArray());
connection.Application.Input.AdvanceTo(result.Buffer.End); connection.Application.Input.AdvanceTo(result.Buffer.End);
}
});
connectionManager.CloseConnections(); result = await connection.Application.Input.ReadAsync();
try
{
Assert.True(result.IsCompleted);
}
finally
{
connection.Application.Input.AdvanceTo(result.Buffer.End);
}
});
await connection.DisposeAsync(); connectionManager.CloseConnections();
await connection.DisposeAsync();
}
} }
[Fact] [Fact]
public async Task DisposingConnectionMultipleTimesWaitsOnConnectionClose() public async Task DisposingConnectionMultipleTimesWaitsOnConnectionClose()
{ {
var connectionManager = CreateConnectionManager(); using (StartVerifiableLog(out var loggerFactory))
var connection = connectionManager.CreateConnection(PipeOptions.Default, PipeOptions.Default); {
var tcs = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously); var connectionManager = CreateConnectionManager(loggerFactory);
var connection = connectionManager.CreateConnection(PipeOptions.Default, PipeOptions.Default);
var tcs = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
connection.ApplicationTask = tcs.Task; connection.ApplicationTask = tcs.Task;
connection.TransportTask = tcs.Task; connection.TransportTask = tcs.Task;
var firstTask = connection.DisposeAsync(); var firstTask = connection.DisposeAsync();
var secondTask = connection.DisposeAsync(); var secondTask = connection.DisposeAsync();
Assert.False(firstTask.IsCompleted); Assert.False(firstTask.IsCompleted);
Assert.False(secondTask.IsCompleted); Assert.False(secondTask.IsCompleted);
tcs.TrySetResult(null); tcs.TrySetResult(null);
await Task.WhenAll(firstTask, secondTask).OrTimeout(); await Task.WhenAll(firstTask, secondTask).OrTimeout();
}
} }
[Fact] [Fact]
public async Task DisposingConnectionMultipleGetsExceptionFromTransportOrApp() public async Task DisposingConnectionMultipleGetsExceptionFromTransportOrApp()
{ {
var connectionManager = CreateConnectionManager(); using (StartVerifiableLog(out var loggerFactory))
var connection = connectionManager.CreateConnection(PipeOptions.Default, PipeOptions.Default); {
var tcs = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously); var connectionManager = CreateConnectionManager(loggerFactory);
var connection = connectionManager.CreateConnection(PipeOptions.Default, PipeOptions.Default);
var tcs = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
connection.ApplicationTask = tcs.Task; connection.ApplicationTask = tcs.Task;
connection.TransportTask = tcs.Task; connection.TransportTask = tcs.Task;
var firstTask = connection.DisposeAsync(); var firstTask = connection.DisposeAsync();
var secondTask = connection.DisposeAsync(); var secondTask = connection.DisposeAsync();
Assert.False(firstTask.IsCompleted); Assert.False(firstTask.IsCompleted);
Assert.False(secondTask.IsCompleted); Assert.False(secondTask.IsCompleted);
tcs.TrySetException(new InvalidOperationException("Error")); tcs.TrySetException(new InvalidOperationException("Error"));
var exception = await Assert.ThrowsAsync<InvalidOperationException>(async () => await firstTask.OrTimeout()); var exception = await Assert.ThrowsAsync<InvalidOperationException>(async () => await firstTask.OrTimeout());
Assert.Equal("Error", exception.Message); Assert.Equal("Error", exception.Message);
exception = await Assert.ThrowsAsync<InvalidOperationException>(async () => await secondTask.OrTimeout()); exception = await Assert.ThrowsAsync<InvalidOperationException>(async () => await secondTask.OrTimeout());
Assert.Equal("Error", exception.Message); Assert.Equal("Error", exception.Message);
}
} }
[Fact] [Fact]
public async Task DisposingConnectionMultipleGetsCancellation() public async Task DisposingConnectionMultipleGetsCancellation()
{ {
var connectionManager = CreateConnectionManager(); using (StartVerifiableLog(out var loggerFactory))
var connection = connectionManager.CreateConnection(PipeOptions.Default, PipeOptions.Default); {
var tcs = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously); var connectionManager = CreateConnectionManager(loggerFactory);
var connection = connectionManager.CreateConnection(PipeOptions.Default, PipeOptions.Default);
var tcs = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
connection.ApplicationTask = tcs.Task; connection.ApplicationTask = tcs.Task;
connection.TransportTask = tcs.Task; connection.TransportTask = tcs.Task;
var firstTask = connection.DisposeAsync(); var firstTask = connection.DisposeAsync();
var secondTask = connection.DisposeAsync(); var secondTask = connection.DisposeAsync();
Assert.False(firstTask.IsCompleted); Assert.False(firstTask.IsCompleted);
Assert.False(secondTask.IsCompleted); Assert.False(secondTask.IsCompleted);
tcs.TrySetCanceled(); tcs.TrySetCanceled();
await Assert.ThrowsAsync<TaskCanceledException>(async () => await firstTask.OrTimeout()); await Assert.ThrowsAsync<TaskCanceledException>(async () => await firstTask.OrTimeout());
await Assert.ThrowsAsync<TaskCanceledException>(async () => await secondTask.OrTimeout()); await Assert.ThrowsAsync<TaskCanceledException>(async () => await secondTask.OrTimeout());
}
} }
[Fact] [Fact]
public async Task DisposeInactiveConnection() public async Task DisposeInactiveConnection()
{ {
var connectionManager = CreateConnectionManager(); using (StartVerifiableLog(out var loggerFactory))
var connection = connectionManager.CreateConnection(PipeOptions.Default, PipeOptions.Default); {
var connectionManager = CreateConnectionManager(loggerFactory);
var connection = connectionManager.CreateConnection(PipeOptions.Default, PipeOptions.Default);
Assert.NotNull(connection.ConnectionId); Assert.NotNull(connection.ConnectionId);
Assert.NotNull(connection.Transport); Assert.NotNull(connection.Transport);
await connection.DisposeAsync(); await connection.DisposeAsync();
Assert.Equal(HttpConnectionStatus.Disposed, connection.Status); Assert.Equal(HttpConnectionStatus.Disposed, connection.Status);
}
} }
[Fact] [Fact]
public async Task DisposeInactiveConnectionWithNoPipes() public async Task DisposeInactiveConnectionWithNoPipes()
{ {
var connectionManager = CreateConnectionManager(); using (StartVerifiableLog(out var loggerFactory))
var connection = connectionManager.CreateConnection(); {
var connectionManager = CreateConnectionManager(loggerFactory);
var connection = connectionManager.CreateConnection();
Assert.NotNull(connection.ConnectionId); Assert.NotNull(connection.ConnectionId);
Assert.NotNull(connection.Transport); Assert.NotNull(connection.Transport);
Assert.NotNull(connection.Application); Assert.NotNull(connection.Application);
await connection.DisposeAsync(); await connection.DisposeAsync();
Assert.Equal(HttpConnectionStatus.Disposed, connection.Status); Assert.Equal(HttpConnectionStatus.Disposed, connection.Status);
}
} }
[Fact] [Fact]
public async Task ApplicationLifetimeIsHookedUp() public async Task ApplicationLifetimeIsHookedUp()
{ {
var appLifetime = new TestApplicationLifetime(); using (StartVerifiableLog(out var loggerFactory))
var connectionManager = CreateConnectionManager(appLifetime);
var tcs = new TaskCompletionSource<object>();
appLifetime.Start();
var connection = connectionManager.CreateConnection(PipeOptions.Default, PipeOptions.Default);
connection.Application.Output.OnReaderCompleted((error, state) =>
{ {
tcs.TrySetResult(null); var appLifetime = new TestApplicationLifetime();
}, var connectionManager = CreateConnectionManager(loggerFactory, appLifetime);
null); var tcs = new TaskCompletionSource<object>();
appLifetime.StopApplication(); appLifetime.Start();
// Connection should be disposed so this should complete immediately var connection = connectionManager.CreateConnection(PipeOptions.Default, PipeOptions.Default);
await tcs.Task.OrTimeout();
connection.Application.Output.OnReaderCompleted((error, state) =>
{
tcs.TrySetResult(null);
},
null);
appLifetime.StopApplication();
// Connection should be disposed so this should complete immediately
await tcs.Task.OrTimeout();
}
} }
[Fact] [Fact]
public async Task ApplicationLifetimeCanStartBeforeHttpConnectionManagerInitialized() public async Task ApplicationLifetimeCanStartBeforeHttpConnectionManagerInitialized()
{ {
var appLifetime = new TestApplicationLifetime(); using (StartVerifiableLog(out var loggerFactory))
appLifetime.Start();
var connectionManager = CreateConnectionManager(appLifetime);
var tcs = new TaskCompletionSource<object>();
var connection = connectionManager.CreateConnection(PipeOptions.Default, PipeOptions.Default);
connection.Application.Output.OnReaderCompleted((error, state) =>
{ {
tcs.TrySetResult(null); var appLifetime = new TestApplicationLifetime();
}, appLifetime.Start();
null);
appLifetime.StopApplication(); var connectionManager = CreateConnectionManager(loggerFactory, appLifetime);
var tcs = new TaskCompletionSource<object>();
// Connection should be disposed so this should complete immediately var connection = connectionManager.CreateConnection(PipeOptions.Default, PipeOptions.Default);
await tcs.Task.OrTimeout();
connection.Application.Output.OnReaderCompleted((error, state) =>
{
tcs.TrySetResult(null);
},
null);
appLifetime.StopApplication();
// Connection should be disposed so this should complete immediately
await tcs.Task.OrTimeout();
}
} }
private static HttpConnectionManager CreateConnectionManager(IApplicationLifetime lifetime = null) private static HttpConnectionManager CreateConnectionManager(ILoggerFactory loggerFactory, IApplicationLifetime lifetime = null)
{ {
lifetime = lifetime ?? new EmptyApplicationLifetime(); lifetime = lifetime ?? new EmptyApplicationLifetime();
return new HttpConnectionManager(new LoggerFactory(), lifetime); return new HttpConnectionManager(loggerFactory, lifetime);
} }
[Flags] [Flags]

View File

@ -9,92 +9,111 @@ using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Connections; using Microsoft.AspNetCore.Connections;
using Microsoft.AspNetCore.Http.Connections.Internal.Transports; using Microsoft.AspNetCore.Http.Connections.Internal.Transports;
using Microsoft.AspNetCore.SignalR.Tests;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Xunit; using Xunit;
using Xunit.Abstractions;
namespace Microsoft.AspNetCore.Http.Connections.Tests namespace Microsoft.AspNetCore.Http.Connections.Tests
{ {
public class LongPollingTests public class LongPollingTests : VerifiableLoggedTest
{ {
public LongPollingTests(ITestOutputHelper output)
: base(output)
{
}
[Fact] [Fact]
public async Task Set204StatusCodeWhenChannelComplete() public async Task Set204StatusCodeWhenChannelComplete()
{ {
var pair = DuplexPipe.CreateConnectionPair(PipeOptions.Default, PipeOptions.Default); using (StartVerifiableLog(out var loggerFactory))
var connection = new DefaultConnectionContext("foo", pair.Transport, pair.Application); {
var pair = DuplexPipe.CreateConnectionPair(PipeOptions.Default, PipeOptions.Default);
var connection = new DefaultConnectionContext("foo", pair.Transport, pair.Application);
var context = new DefaultHttpContext(); var context = new DefaultHttpContext();
var poll = new LongPollingTransport(CancellationToken.None, connection.Application.Input, loggerFactory: new LoggerFactory()); var poll = new LongPollingTransport(CancellationToken.None, connection.Application.Input, loggerFactory);
connection.Transport.Output.Complete(); connection.Transport.Output.Complete();
await poll.ProcessRequestAsync(context, context.RequestAborted).OrTimeout(); await poll.ProcessRequestAsync(context, context.RequestAborted).OrTimeout();
Assert.Equal(204, context.Response.StatusCode); Assert.Equal(204, context.Response.StatusCode);
}
} }
[Fact] [Fact]
public async Task Set200StatusCodeWhenTimeoutTokenFires() public async Task Set200StatusCodeWhenTimeoutTokenFires()
{ {
var pair = DuplexPipe.CreateConnectionPair(PipeOptions.Default, PipeOptions.Default); using (StartVerifiableLog(out var loggerFactory))
var connection = new DefaultConnectionContext("foo", pair.Transport, pair.Application);
var context = new DefaultHttpContext();
var timeoutToken = new CancellationToken(true);
var poll = new LongPollingTransport(timeoutToken, connection.Application.Input, loggerFactory: new LoggerFactory());
using (var cts = CancellationTokenSource.CreateLinkedTokenSource(timeoutToken, context.RequestAborted))
{ {
await poll.ProcessRequestAsync(context, cts.Token).OrTimeout(); var pair = DuplexPipe.CreateConnectionPair(PipeOptions.Default, PipeOptions.Default);
var connection = new DefaultConnectionContext("foo", pair.Transport, pair.Application);
var context = new DefaultHttpContext();
Assert.Equal(0, context.Response.ContentLength); var timeoutToken = new CancellationToken(true);
Assert.Equal(200, context.Response.StatusCode); var poll = new LongPollingTransport(timeoutToken, connection.Application.Input, loggerFactory);
using (var cts = CancellationTokenSource.CreateLinkedTokenSource(timeoutToken, context.RequestAborted))
{
await poll.ProcessRequestAsync(context, cts.Token).OrTimeout();
Assert.Equal(0, context.Response.ContentLength);
Assert.Equal(200, context.Response.StatusCode);
}
} }
} }
[Fact] [Fact]
public async Task FrameSentAsSingleResponse() public async Task FrameSentAsSingleResponse()
{ {
var pair = DuplexPipe.CreateConnectionPair(PipeOptions.Default, PipeOptions.Default); using (StartVerifiableLog(out var loggerFactory))
var connection = new DefaultConnectionContext("foo", pair.Transport, pair.Application); {
var context = new DefaultHttpContext(); var pair = DuplexPipe.CreateConnectionPair(PipeOptions.Default, PipeOptions.Default);
var connection = new DefaultConnectionContext("foo", pair.Transport, pair.Application);
var context = new DefaultHttpContext();
var poll = new LongPollingTransport(CancellationToken.None, connection.Application.Input, loggerFactory: new LoggerFactory()); var poll = new LongPollingTransport(CancellationToken.None, connection.Application.Input, loggerFactory);
var ms = new MemoryStream(); var ms = new MemoryStream();
context.Response.Body = ms; context.Response.Body = ms;
await connection.Transport.Output.WriteAsync(Encoding.UTF8.GetBytes("Hello World")); await connection.Transport.Output.WriteAsync(Encoding.UTF8.GetBytes("Hello World"));
connection.Transport.Output.Complete(); connection.Transport.Output.Complete();
await poll.ProcessRequestAsync(context, context.RequestAborted).OrTimeout(); await poll.ProcessRequestAsync(context, context.RequestAborted).OrTimeout();
Assert.Equal(200, context.Response.StatusCode); Assert.Equal(200, context.Response.StatusCode);
Assert.Equal("Hello World", Encoding.UTF8.GetString(ms.ToArray())); Assert.Equal("Hello World", Encoding.UTF8.GetString(ms.ToArray()));
}
} }
[Fact] [Fact]
public async Task MultipleFramesSentAsSingleResponse() public async Task MultipleFramesSentAsSingleResponse()
{ {
var pair = DuplexPipe.CreateConnectionPair(PipeOptions.Default, PipeOptions.Default); using (StartVerifiableLog(out var loggerFactory))
var connection = new DefaultConnectionContext("foo", pair.Transport, pair.Application); {
var context = new DefaultHttpContext(); var pair = DuplexPipe.CreateConnectionPair(PipeOptions.Default, PipeOptions.Default);
var connection = new DefaultConnectionContext("foo", pair.Transport, pair.Application);
var context = new DefaultHttpContext();
var poll = new LongPollingTransport(CancellationToken.None, connection.Application.Input, loggerFactory: new LoggerFactory()); var poll = new LongPollingTransport(CancellationToken.None, connection.Application.Input, loggerFactory);
var ms = new MemoryStream(); var ms = new MemoryStream();
context.Response.Body = ms; context.Response.Body = ms;
await connection.Transport.Output.WriteAsync(Encoding.UTF8.GetBytes("Hello")); await connection.Transport.Output.WriteAsync(Encoding.UTF8.GetBytes("Hello"));
await connection.Transport.Output.WriteAsync(Encoding.UTF8.GetBytes(" ")); await connection.Transport.Output.WriteAsync(Encoding.UTF8.GetBytes(" "));
await connection.Transport.Output.WriteAsync(Encoding.UTF8.GetBytes("World")); await connection.Transport.Output.WriteAsync(Encoding.UTF8.GetBytes("World"));
connection.Transport.Output.Complete(); connection.Transport.Output.Complete();
await poll.ProcessRequestAsync(context, context.RequestAborted).OrTimeout(); await poll.ProcessRequestAsync(context, context.RequestAborted).OrTimeout();
Assert.Equal(200, context.Response.StatusCode); Assert.Equal(200, context.Response.StatusCode);
var payload = ms.ToArray(); var payload = ms.ToArray();
Assert.Equal("Hello World", Encoding.UTF8.GetString(payload)); Assert.Equal("Hello World", Encoding.UTF8.GetString(payload));
}
} }
[Fact] [Fact]

View File

@ -11,85 +11,104 @@ using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Http.Connections.Internal.Transports; using Microsoft.AspNetCore.Http.Connections.Internal.Transports;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Xunit; using Xunit;
using Microsoft.AspNetCore.SignalR.Tests;
using Xunit.Abstractions;
namespace Microsoft.AspNetCore.Http.Connections.Tests namespace Microsoft.AspNetCore.Http.Connections.Tests
{ {
public class ServerSentEventsTests public class ServerSentEventsTests : VerifiableLoggedTest
{ {
public ServerSentEventsTests(ITestOutputHelper output)
: base(output)
{
}
[Fact] [Fact]
public async Task SSESetsContentType() public async Task SSESetsContentType()
{ {
var pair = DuplexPipe.CreateConnectionPair(PipeOptions.Default, PipeOptions.Default); using (StartVerifiableLog(out var loggerFactory))
var connection = new DefaultConnectionContext("foo", pair.Transport, pair.Application); {
var context = new DefaultHttpContext(); var pair = DuplexPipe.CreateConnectionPair(PipeOptions.Default, PipeOptions.Default);
var connection = new DefaultConnectionContext("foo", pair.Transport, pair.Application);
var context = new DefaultHttpContext();
var sse = new ServerSentEventsTransport(connection.Application.Input, connectionId: string.Empty, loggerFactory: new LoggerFactory()); var sse = new ServerSentEventsTransport(connection.Application.Input, connectionId: string.Empty, loggerFactory);
connection.Transport.Output.Complete(); connection.Transport.Output.Complete();
await sse.ProcessRequestAsync(context, context.RequestAborted); await sse.ProcessRequestAsync(context, context.RequestAborted);
Assert.Equal("text/event-stream", context.Response.ContentType); Assert.Equal("text/event-stream", context.Response.ContentType);
Assert.Equal("no-cache", context.Response.Headers["Cache-Control"]); Assert.Equal("no-cache", context.Response.Headers["Cache-Control"]);
}
} }
[Fact] [Fact]
public async Task SSETurnsResponseBufferingOff() public async Task SSETurnsResponseBufferingOff()
{ {
var pair = DuplexPipe.CreateConnectionPair(PipeOptions.Default, PipeOptions.Default); using (StartVerifiableLog(out var loggerFactory))
var connection = new DefaultConnectionContext("foo", pair.Transport, pair.Application); {
var context = new DefaultHttpContext(); var pair = DuplexPipe.CreateConnectionPair(PipeOptions.Default, PipeOptions.Default);
var connection = new DefaultConnectionContext("foo", pair.Transport, pair.Application);
var context = new DefaultHttpContext();
var feature = new HttpBufferingFeature(); var feature = new HttpBufferingFeature();
context.Features.Set<IHttpBufferingFeature>(feature); context.Features.Set<IHttpBufferingFeature>(feature);
var sse = new ServerSentEventsTransport(connection.Application.Input, connectionId: connection.ConnectionId, loggerFactory: new LoggerFactory()); var sse = new ServerSentEventsTransport(connection.Application.Input, connectionId: connection.ConnectionId, loggerFactory);
connection.Transport.Output.Complete(); connection.Transport.Output.Complete();
await sse.ProcessRequestAsync(context, context.RequestAborted); await sse.ProcessRequestAsync(context, context.RequestAborted);
Assert.True(feature.ResponseBufferingDisabled); Assert.True(feature.ResponseBufferingDisabled);
}
} }
[Fact] [Fact]
public async Task SSEWritesMessages() public async Task SSEWritesMessages()
{ {
var pair = DuplexPipe.CreateConnectionPair(PipeOptions.Default, new PipeOptions(readerScheduler: PipeScheduler.Inline)); using (StartVerifiableLog(out var loggerFactory))
var connection = new DefaultConnectionContext("foo", pair.Transport, pair.Application); {
var context = new DefaultHttpContext(); var pair = DuplexPipe.CreateConnectionPair(PipeOptions.Default, new PipeOptions(readerScheduler: PipeScheduler.Inline));
var connection = new DefaultConnectionContext("foo", pair.Transport, pair.Application);
var context = new DefaultHttpContext();
var ms = new MemoryStream(); var ms = new MemoryStream();
context.Response.Body = ms; context.Response.Body = ms;
var sse = new ServerSentEventsTransport(connection.Application.Input, connectionId: string.Empty, loggerFactory: new LoggerFactory()); var sse = new ServerSentEventsTransport(connection.Application.Input, connectionId: string.Empty, loggerFactory);
var task = sse.ProcessRequestAsync(context, context.RequestAborted); var task = sse.ProcessRequestAsync(context, context.RequestAborted);
await connection.Transport.Output.WriteAsync(Encoding.ASCII.GetBytes("Hello")); await connection.Transport.Output.WriteAsync(Encoding.ASCII.GetBytes("Hello"));
connection.Transport.Output.Complete(); connection.Transport.Output.Complete();
await task.OrTimeout(); await task.OrTimeout();
Assert.Equal(":\r\ndata: Hello\r\n\r\n", Encoding.ASCII.GetString(ms.ToArray())); Assert.Equal(":\r\ndata: Hello\r\n\r\n", Encoding.ASCII.GetString(ms.ToArray()));
}
} }
[Fact] [Fact]
public async Task SSEWritesVeryLargeMessages() public async Task SSEWritesVeryLargeMessages()
{ {
var pair = DuplexPipe.CreateConnectionPair(PipeOptions.Default, new PipeOptions(readerScheduler: PipeScheduler.Inline)); using (StartVerifiableLog(out var loggerFactory))
var connection = new DefaultConnectionContext("foo", pair.Transport, pair.Application); {
var context = new DefaultHttpContext(); var pair = DuplexPipe.CreateConnectionPair(PipeOptions.Default, new PipeOptions(readerScheduler: PipeScheduler.Inline));
var connection = new DefaultConnectionContext("foo", pair.Transport, pair.Application);
var context = new DefaultHttpContext();
var ms = new MemoryStream(); var ms = new MemoryStream();
context.Response.Body = ms; context.Response.Body = ms;
var sse = new ServerSentEventsTransport(connection.Application.Input, connectionId: string.Empty, loggerFactory: new LoggerFactory()); var sse = new ServerSentEventsTransport(connection.Application.Input, connectionId: string.Empty, loggerFactory);
var task = sse.ProcessRequestAsync(context, context.RequestAborted); var task = sse.ProcessRequestAsync(context, context.RequestAborted);
string hText = new string('H', 60000); string hText = new string('H', 60000);
string wText = new string('W', 60000); string wText = new string('W', 60000);
await connection.Transport.Output.WriteAsync(Encoding.ASCII.GetBytes(hText + wText)); await connection.Transport.Output.WriteAsync(Encoding.ASCII.GetBytes(hText + wText));
connection.Transport.Output.Complete(); connection.Transport.Output.Complete();
await task.OrTimeout(); await task.OrTimeout();
Assert.Equal(":\r\ndata: " + hText + wText + "\r\n\r\n", Encoding.ASCII.GetString(ms.ToArray())); Assert.Equal(":\r\ndata: " + hText + wText + "\r\n\r\n", Encoding.ASCII.GetString(ms.ToArray()));
}
} }
[Theory] [Theory]
@ -98,21 +117,24 @@ namespace Microsoft.AspNetCore.Http.Connections.Tests
[InlineData("Hello\r\nWorld", ":\r\ndata: Hello\r\ndata: World\r\n\r\n")] [InlineData("Hello\r\nWorld", ":\r\ndata: Hello\r\ndata: World\r\n\r\n")]
public async Task SSEAddsAppropriateFraming(string message, string expected) public async Task SSEAddsAppropriateFraming(string message, string expected)
{ {
var pair = DuplexPipe.CreateConnectionPair(PipeOptions.Default, PipeOptions.Default); using (StartVerifiableLog(out var loggerFactory))
var connection = new DefaultConnectionContext("foo", pair.Transport, pair.Application); {
var context = new DefaultHttpContext(); var pair = DuplexPipe.CreateConnectionPair(PipeOptions.Default, PipeOptions.Default);
var connection = new DefaultConnectionContext("foo", pair.Transport, pair.Application);
var context = new DefaultHttpContext();
var sse = new ServerSentEventsTransport(connection.Application.Input, connectionId: string.Empty, loggerFactory: new LoggerFactory()); var sse = new ServerSentEventsTransport(connection.Application.Input, connectionId: string.Empty, loggerFactory);
var ms = new MemoryStream(); var ms = new MemoryStream();
context.Response.Body = ms; context.Response.Body = ms;
await connection.Transport.Output.WriteAsync(Encoding.UTF8.GetBytes(message)); await connection.Transport.Output.WriteAsync(Encoding.UTF8.GetBytes(message));
connection.Transport.Output.Complete(); connection.Transport.Output.Complete();
await sse.ProcessRequestAsync(context, context.RequestAborted); await sse.ProcessRequestAsync(context, context.RequestAborted);
Assert.Equal(expected, Encoding.UTF8.GetString(ms.ToArray())); Assert.Equal(expected, Encoding.UTF8.GetString(ms.ToArray()));
}
} }
private class HttpBufferingFeature : IHttpBufferingFeature private class HttpBufferingFeature : IHttpBufferingFeature

View File

@ -38,11 +38,11 @@ namespace Microsoft.AspNetCore.Http.Connections.Tests
using (StartVerifiableLog(out var loggerFactory, LogLevel.Debug)) using (StartVerifiableLog(out var loggerFactory, LogLevel.Debug))
{ {
var pair = DuplexPipe.CreateConnectionPair(PipeOptions.Default, PipeOptions.Default); var pair = DuplexPipe.CreateConnectionPair(PipeOptions.Default, PipeOptions.Default);
var connection = new HttpConnectionContext("foo", pair.Transport, pair.Application); var connection = new HttpConnectionContext("foo", pair.Transport, pair.Application, loggerFactory.CreateLogger("HttpConnectionContext1"));
using (var feature = new TestWebSocketConnectionFeature()) using (var feature = new TestWebSocketConnectionFeature())
{ {
var connectionContext = new HttpConnectionContext(string.Empty, null, null); var connectionContext = new HttpConnectionContext(string.Empty, null, null, loggerFactory.CreateLogger("HttpConnectionContext2"));
var ws = new WebSocketsTransport(new WebSocketOptions(), connection.Application, connectionContext, loggerFactory); var ws = new WebSocketsTransport(new WebSocketOptions(), connection.Application, connectionContext, loggerFactory);
// Give the server socket to the transport and run it // Give the server socket to the transport and run it
@ -86,11 +86,11 @@ namespace Microsoft.AspNetCore.Http.Connections.Tests
using (StartVerifiableLog(out var loggerFactory, LogLevel.Debug)) using (StartVerifiableLog(out var loggerFactory, LogLevel.Debug))
{ {
var pair = DuplexPipe.CreateConnectionPair(PipeOptions.Default, PipeOptions.Default); var pair = DuplexPipe.CreateConnectionPair(PipeOptions.Default, PipeOptions.Default);
var connection = new HttpConnectionContext("foo", pair.Transport, pair.Application); var connection = new HttpConnectionContext("foo", pair.Transport, pair.Application, loggerFactory.CreateLogger("HttpConnectionContext1"));
using (var feature = new TestWebSocketConnectionFeature()) using (var feature = new TestWebSocketConnectionFeature())
{ {
var connectionContext = new HttpConnectionContext(string.Empty, null, null); var connectionContext = new HttpConnectionContext(string.Empty, null, null, loggerFactory.CreateLogger("HttpConnectionContext2"));
connectionContext.ActiveFormat = transferFormat; connectionContext.ActiveFormat = transferFormat;
var ws = new WebSocketsTransport(new WebSocketOptions(), connection.Application, connectionContext, loggerFactory); var ws = new WebSocketsTransport(new WebSocketOptions(), connection.Application, connectionContext, loggerFactory);
@ -123,7 +123,7 @@ namespace Microsoft.AspNetCore.Http.Connections.Tests
using (StartVerifiableLog(out var loggerFactory, LogLevel.Debug)) using (StartVerifiableLog(out var loggerFactory, LogLevel.Debug))
{ {
var pair = DuplexPipe.CreateConnectionPair(PipeOptions.Default, PipeOptions.Default); var pair = DuplexPipe.CreateConnectionPair(PipeOptions.Default, PipeOptions.Default);
var connection = new HttpConnectionContext("foo", pair.Transport, pair.Application); var connection = new HttpConnectionContext("foo", pair.Transport, pair.Application, loggerFactory.CreateLogger("HttpConnectionContext1"));
using (var feature = new TestWebSocketConnectionFeature()) using (var feature = new TestWebSocketConnectionFeature())
{ {
@ -146,7 +146,7 @@ namespace Microsoft.AspNetCore.Http.Connections.Tests
} }
} }
var connectionContext = new HttpConnectionContext(string.Empty, null, null); var connectionContext = new HttpConnectionContext(string.Empty, null, null, loggerFactory.CreateLogger("HttpConnectionContext2"));
var ws = new WebSocketsTransport(new WebSocketOptions(), connection.Application, connectionContext, loggerFactory); var ws = new WebSocketsTransport(new WebSocketOptions(), connection.Application, connectionContext, loggerFactory);
// Give the server socket to the transport and run it // Give the server socket to the transport and run it