Complete upload streams on connection close (#13065)
This commit is contained in:
parent
116e5bdd9a
commit
eb966eb605
|
|
@ -583,6 +583,12 @@ namespace Microsoft.AspNetCore.SignalR
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
_ = InnerAbortConnection(connection);
|
_ = InnerAbortConnection(connection);
|
||||||
|
|
||||||
|
// Use _streamTracker to avoid lazy init from StreamTracker getter if it doesn't exist
|
||||||
|
if (connection._streamTracker != null)
|
||||||
|
{
|
||||||
|
connection._streamTracker.CompleteAll(new OperationCanceledException("The underlying connection was closed."));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static async Task InnerAbortConnection(HubConnectionContext connection)
|
static async Task InnerAbortConnection(HubConnectionContext connection)
|
||||||
|
|
|
||||||
|
|
@ -77,6 +77,14 @@ namespace Microsoft.AspNetCore.SignalR
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void CompleteAll(Exception ex)
|
||||||
|
{
|
||||||
|
foreach (var converter in _lookup)
|
||||||
|
{
|
||||||
|
converter.Value.TryComplete(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static IStreamConverter BuildStream<T>(int streamBufferCapacity)
|
private static IStreamConverter BuildStream<T>(int streamBufferCapacity)
|
||||||
{
|
{
|
||||||
return new ChannelConverter<T>(streamBufferCapacity);
|
return new ChannelConverter<T>(streamBufferCapacity);
|
||||||
|
|
|
||||||
|
|
@ -298,6 +298,30 @@ namespace Microsoft.AspNetCore.SignalR.Tests
|
||||||
output.Complete();
|
output.Complete();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task UploadDoesWorkOnComplete(ChannelReader<string> source)
|
||||||
|
{
|
||||||
|
var tcs = new TaskCompletionSource<int>(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||||
|
Context.Items[nameof(UploadDoesWorkOnComplete)] = tcs.Task;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
while (await source.WaitToReadAsync())
|
||||||
|
{
|
||||||
|
while (source.TryRead(out var item))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
tcs.SetException(ex);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
tcs.TrySetResult(42);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract class TestHub : Hub
|
public abstract class TestHub : Hub
|
||||||
|
|
|
||||||
|
|
@ -3705,6 +3705,37 @@ namespace Microsoft.AspNetCore.SignalR.Tests
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ConnectionCloseCleansUploadStreams()
|
||||||
|
{
|
||||||
|
var serviceProvider = HubConnectionHandlerTestUtils.CreateServiceProvider();
|
||||||
|
var connectionHandler = serviceProvider.GetService<HubConnectionHandler<MethodHub>>();
|
||||||
|
|
||||||
|
using (StartVerifiableLog())
|
||||||
|
{
|
||||||
|
using var client = new TestClient();
|
||||||
|
|
||||||
|
var connectionHandlerTask = await client.ConnectAsync(connectionHandler);
|
||||||
|
|
||||||
|
// Wait for a connection, or for the endpoint to fail.
|
||||||
|
await client.Connected.OrThrowIfOtherFails(connectionHandlerTask).OrTimeout();
|
||||||
|
|
||||||
|
await client.BeginUploadStreamAsync("invocation", nameof(MethodHub.UploadDoesWorkOnComplete), streamIds: new[] { "id" }, args: Array.Empty<object>()).OrTimeout();
|
||||||
|
|
||||||
|
await client.SendHubMessageAsync(new StreamItemMessage("id", "hello")).OrTimeout();
|
||||||
|
|
||||||
|
await client.DisposeAsync().OrTimeout();
|
||||||
|
|
||||||
|
await connectionHandlerTask.OrTimeout();
|
||||||
|
|
||||||
|
// This task completes if the upload stream is completed, via closing the connection
|
||||||
|
var task = (Task<int>)client.Connection.Items[nameof(MethodHub.UploadDoesWorkOnComplete)];
|
||||||
|
|
||||||
|
var exception = await Assert.ThrowsAsync<OperationCanceledException>(() => task).OrTimeout();
|
||||||
|
Assert.Equal("The underlying connection was closed.", exception.Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private class CustomHubActivator<THub> : IHubActivator<THub> where THub : Hub
|
private class CustomHubActivator<THub> : IHubActivator<THub> where THub : Hub
|
||||||
{
|
{
|
||||||
public int ReleaseCount;
|
public int ReleaseCount;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue