Skip to content

Commit 8ad0574

Browse files
committed
Merge in 'release/8.0-rc1' changes
2 parents b70b7ef + e3eb451 commit 8ad0574

File tree

21 files changed

+721
-144
lines changed

21 files changed

+721
-144
lines changed

src/Servers/Connections.Abstractions/src/Features/IReconnectFeature.cs

Lines changed: 0 additions & 24 deletions
This file was deleted.
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System;
5+
using System.Collections.Generic;
6+
using System.IO.Pipelines;
7+
using System.Linq;
8+
#if NET8_0_OR_GREATER
9+
using System.Runtime.Versioning;
10+
#endif
11+
using System.Text;
12+
using System.Threading.Tasks;
13+
14+
namespace Microsoft.AspNetCore.Connections.Abstractions;
15+
16+
/// <summary>
17+
/// Provides access to connection reconnect operations.
18+
/// </summary>
19+
/// <remarks>This feature is experimental.</remarks>
20+
#if NET8_0_OR_GREATER
21+
[RequiresPreviewFeatures("IStatefulReconnectFeature is a preview interface")]
22+
#endif
23+
public interface IStatefulReconnectFeature
24+
{
25+
/// <summary>
26+
/// Called when a connection reconnects. The new <see cref="PipeWriter"/> that application code should write to is passed in.
27+
/// </summary>
28+
public void OnReconnected(Func<PipeWriter, Task> notifyOnReconnect);
29+
30+
/// <summary>
31+
/// Allows disabling the reconnect feature so a reconnecting connection will not be allowed anymore.
32+
/// </summary>
33+
void DisableReconnect();
34+
}

src/Servers/Connections.Abstractions/src/PublicAPI/net462/PublicAPI.Unshipped.txt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#nullable enable
2-
Microsoft.AspNetCore.Connections.Abstractions.IReconnectFeature
3-
Microsoft.AspNetCore.Connections.Abstractions.IReconnectFeature.NotifyOnReconnect.get -> System.Action!
4-
Microsoft.AspNetCore.Connections.Abstractions.IReconnectFeature.NotifyOnReconnect.set -> void
2+
Microsoft.AspNetCore.Connections.Abstractions.IStatefulReconnectFeature
3+
Microsoft.AspNetCore.Connections.Abstractions.IStatefulReconnectFeature.DisableReconnect() -> void
4+
Microsoft.AspNetCore.Connections.Abstractions.IStatefulReconnectFeature.OnReconnected(System.Func<System.IO.Pipelines.PipeWriter!, System.Threading.Tasks.Task!>! notifyOnReconnect) -> void
55
Microsoft.AspNetCore.Connections.Features.IConnectionMetricsTagsFeature
66
Microsoft.AspNetCore.Connections.Features.IConnectionMetricsTagsFeature.Tags.get -> System.Collections.Generic.ICollection<System.Collections.Generic.KeyValuePair<string!, object?>>!
77
Microsoft.AspNetCore.Connections.Features.IConnectionNamedPipeFeature

src/Servers/Connections.Abstractions/src/PublicAPI/net8.0/PublicAPI.Unshipped.txt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#nullable enable
2-
Microsoft.AspNetCore.Connections.Abstractions.IReconnectFeature
3-
Microsoft.AspNetCore.Connections.Abstractions.IReconnectFeature.NotifyOnReconnect.get -> System.Action!
4-
Microsoft.AspNetCore.Connections.Abstractions.IReconnectFeature.NotifyOnReconnect.set -> void
2+
Microsoft.AspNetCore.Connections.Abstractions.IStatefulReconnectFeature
3+
Microsoft.AspNetCore.Connections.Abstractions.IStatefulReconnectFeature.DisableReconnect() -> void
4+
Microsoft.AspNetCore.Connections.Abstractions.IStatefulReconnectFeature.OnReconnected(System.Func<System.IO.Pipelines.PipeWriter!, System.Threading.Tasks.Task!>! notifyOnReconnect) -> void
55
Microsoft.AspNetCore.Connections.Features.IConnectionMetricsTagsFeature
66
Microsoft.AspNetCore.Connections.Features.IConnectionMetricsTagsFeature.Tags.get -> System.Collections.Generic.ICollection<System.Collections.Generic.KeyValuePair<string!, object?>>!
77
Microsoft.AspNetCore.Connections.Features.IConnectionNamedPipeFeature

src/Servers/Connections.Abstractions/src/PublicAPI/netstandard2.0/PublicAPI.Unshipped.txt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#nullable enable
2-
Microsoft.AspNetCore.Connections.Abstractions.IReconnectFeature
3-
Microsoft.AspNetCore.Connections.Abstractions.IReconnectFeature.NotifyOnReconnect.get -> System.Action!
4-
Microsoft.AspNetCore.Connections.Abstractions.IReconnectFeature.NotifyOnReconnect.set -> void
2+
Microsoft.AspNetCore.Connections.Abstractions.IStatefulReconnectFeature
3+
Microsoft.AspNetCore.Connections.Abstractions.IStatefulReconnectFeature.DisableReconnect() -> void
4+
Microsoft.AspNetCore.Connections.Abstractions.IStatefulReconnectFeature.OnReconnected(System.Func<System.IO.Pipelines.PipeWriter!, System.Threading.Tasks.Task!>! notifyOnReconnect) -> void
55
Microsoft.AspNetCore.Connections.Features.IConnectionMetricsTagsFeature
66
Microsoft.AspNetCore.Connections.Features.IConnectionMetricsTagsFeature.Tags.get -> System.Collections.Generic.ICollection<System.Collections.Generic.KeyValuePair<string!, object?>>!
77
Microsoft.AspNetCore.Connections.Features.IConnectionNamedPipeFeature

src/Servers/Connections.Abstractions/src/PublicAPI/netstandard2.1/PublicAPI.Unshipped.txt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#nullable enable
2-
Microsoft.AspNetCore.Connections.Abstractions.IReconnectFeature
3-
Microsoft.AspNetCore.Connections.Abstractions.IReconnectFeature.NotifyOnReconnect.get -> System.Action!
4-
Microsoft.AspNetCore.Connections.Abstractions.IReconnectFeature.NotifyOnReconnect.set -> void
2+
Microsoft.AspNetCore.Connections.Abstractions.IStatefulReconnectFeature
3+
Microsoft.AspNetCore.Connections.Abstractions.IStatefulReconnectFeature.DisableReconnect() -> void
4+
Microsoft.AspNetCore.Connections.Abstractions.IStatefulReconnectFeature.OnReconnected(System.Func<System.IO.Pipelines.PipeWriter!, System.Threading.Tasks.Task!>! notifyOnReconnect) -> void
55
Microsoft.AspNetCore.Connections.Features.IConnectionMetricsTagsFeature
66
Microsoft.AspNetCore.Connections.Features.IConnectionMetricsTagsFeature.Tags.get -> System.Collections.Generic.ICollection<System.Collections.Generic.KeyValuePair<string!, object?>>!
77
Microsoft.AspNetCore.Connections.Features.IConnectionNamedPipeFeature

src/SignalR/clients/csharp/Client.Core/src/HubConnection.cs

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -579,6 +579,13 @@ private async Task StopAsyncCore(bool disposing)
579579
TaskContinuationOptions.ExecuteSynchronously | TaskContinuationOptions.OnlyOnFaulted,
580580
TaskScheduler.Default);
581581
}
582+
583+
#pragma warning disable CA2252 // This API requires opting into preview features
584+
if (connectionState.Connection.Features.Get<IStatefulReconnectFeature>() is IStatefulReconnectFeature feature)
585+
{
586+
feature.DisableReconnect();
587+
}
588+
#pragma warning restore CA2252 // This API requires opting into preview features
582589
}
583590
else
584591
{
@@ -1088,6 +1095,14 @@ private async Task SendWithLock(ConnectionState expectedConnectionState, HubMess
10881095
{
10891096
Log.ReceivedCloseWithError(_logger, close.Error);
10901097
}
1098+
1099+
#pragma warning disable CA2252 // This API requires opting into preview features
1100+
if (connectionState.Connection.Features.Get<IStatefulReconnectFeature>() is IStatefulReconnectFeature feature)
1101+
{
1102+
feature.DisableReconnect();
1103+
}
1104+
#pragma warning restore CA2252 // This API requires opting into preview features
1105+
10911106
return close;
10921107
case PingMessage _:
10931108
Log.ReceivedPing(_logger);
@@ -1900,14 +1915,16 @@ public ConnectionState(ConnectionContext connection, HubConnection hubConnection
19001915
_logger = _hubConnection._logger;
19011916
_hasInherentKeepAlive = connection.Features.Get<IConnectionInherentKeepAliveFeature>()?.HasInherentKeepAlive ?? false;
19021917

1903-
if (Connection.Features.Get<IReconnectFeature>() is IReconnectFeature feature)
1918+
#pragma warning disable CA2252 // This API requires opting into preview features
1919+
if (Connection.Features.Get<IStatefulReconnectFeature>() is IStatefulReconnectFeature feature)
19041920
{
19051921
_messageBuffer = new MessageBuffer(connection, hubConnection._protocol,
19061922
_hubConnection._serviceProvider.GetService<IOptions<HubConnectionOptions>>()?.Value.StatefulReconnectBufferSize
19071923
?? DefaultStatefulReconnectBufferSize);
19081924

1909-
feature.NotifyOnReconnect = _messageBuffer.Resend;
1925+
feature.OnReconnected(_messageBuffer.ResendAsync);
19101926
}
1927+
#pragma warning restore CA2252 // This API requires opting into preview features
19111928
}
19121929

19131930
public string GetNextId() => (++_nextInvocationId).ToString(CultureInfo.InvariantCulture);

src/SignalR/clients/csharp/Client/test/UnitTests/HubConnectionTests.cs

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
using System.Net.WebSockets;
88
using System.Threading.Channels;
99
using Microsoft.AspNetCore.Connections;
10+
using Microsoft.AspNetCore.Connections.Abstractions;
1011
using Microsoft.AspNetCore.Connections.Features;
1112
using Microsoft.AspNetCore.Http.Connections.Client;
1213
using Microsoft.AspNetCore.SignalR.Protocol;
@@ -878,6 +879,70 @@ public async Task HubConnectionIsMockable()
878879
mockConnection.Verify(c => c.StopAsync(It.IsAny<CancellationToken>()), Times.Once);
879880
}
880881

882+
[Fact]
883+
public async Task DisableReconnectCalledWhenCloseMessageReceived()
884+
{
885+
var builder = new HubConnectionBuilder().WithUrl("http://example.com");
886+
var innerConnection = new TestConnection();
887+
var reconnectFeature = new TestReconnectFeature();
888+
#pragma warning disable CA2252 // This API requires opting into preview features
889+
innerConnection.Features.Set<IStatefulReconnectFeature>(reconnectFeature);
890+
#pragma warning restore CA2252 // This API requires opting into preview features
891+
892+
var delegateConnectionFactory = new DelegateConnectionFactory(
893+
endPoint => innerConnection.StartAsync());
894+
builder.Services.AddSingleton<IConnectionFactory>(delegateConnectionFactory);
895+
896+
var hubConnection = builder.Build();
897+
var closedEventTcs = new TaskCompletionSource<Exception>();
898+
hubConnection.Closed += e =>
899+
{
900+
closedEventTcs.SetResult(e);
901+
return Task.CompletedTask;
902+
};
903+
904+
await hubConnection.StartAsync().DefaultTimeout();
905+
906+
await innerConnection.ReceiveJsonMessage(new { type = HubProtocolConstants.CloseMessageType });
907+
908+
var exception = await closedEventTcs.Task.DefaultTimeout();
909+
Assert.Null(exception);
910+
911+
await reconnectFeature.DisableReconnectCalled.DefaultTimeout();
912+
}
913+
914+
[Fact]
915+
public async Task DisableReconnectCalledWhenSendingCloseMessage()
916+
{
917+
var builder = new HubConnectionBuilder().WithUrl("http://example.com");
918+
var innerConnection = new TestConnection();
919+
var reconnectFeature = new TestReconnectFeature();
920+
#pragma warning disable CA2252 // This API requires opting into preview features
921+
innerConnection.Features.Set<IStatefulReconnectFeature>(reconnectFeature);
922+
#pragma warning restore CA2252 // This API requires opting into preview features
923+
924+
var delegateConnectionFactory = new DelegateConnectionFactory(
925+
endPoint => innerConnection.StartAsync());
926+
builder.Services.AddSingleton<IConnectionFactory>(delegateConnectionFactory);
927+
928+
var hubConnection = builder.Build();
929+
var closedEventTcs = new TaskCompletionSource<Exception>();
930+
hubConnection.Closed += e =>
931+
{
932+
closedEventTcs.SetResult(e);
933+
return Task.CompletedTask;
934+
};
935+
936+
await hubConnection.StartAsync().DefaultTimeout();
937+
938+
await hubConnection.StopAsync().DefaultTimeout();
939+
940+
var exception = await closedEventTcs.Task.DefaultTimeout();
941+
Assert.Null(exception);
942+
943+
await reconnectFeature.DisableReconnectCalled.DefaultTimeout();
944+
}
945+
881946
private class SampleObject
882947
{
883948
public SampleObject(string foo, int bar)
@@ -962,4 +1027,24 @@ public ReadOnlyMemory<byte> GetMessageBytes(HubMessage message)
9621027
return HubProtocolExtensions.GetMessageBytes(this, message);
9631028
}
9641029
}
1030+
1031+
#pragma warning disable CA2252 // This API requires opting into preview features
1032+
private sealed class TestReconnectFeature : IStatefulReconnectFeature
1033+
#pragma warning restore CA2252 // This API requires opting into preview features
1034+
{
1035+
private TaskCompletionSource _disableReconnect = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
1036+
1037+
public Task DisableReconnectCalled => _disableReconnect.Task;
1038+
1039+
#pragma warning disable CA2252 // This API requires opting into preview features
1040+
public void OnReconnected(Func<PipeWriter, Task> notifyOnReconnected) { }
1041+
#pragma warning restore CA2252 // This API requires opting into preview features
1042+
1043+
#pragma warning disable CA2252 // This API requires opting into preview features
1044+
public void DisableReconnect()
1045+
#pragma warning restore CA2252 // This API requires opting into preview features
1046+
{
1047+
_disableReconnect.TrySetResult();
1048+
}
1049+
}
9651050
}

src/SignalR/clients/csharp/Http.Connections.Client/src/HttpConnection.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -531,9 +531,11 @@ private async Task StartTransport(Uri connectUrl, HttpTransportType transportTyp
531531
// We successfully started, set the transport properties (we don't want to set these until the transport is definitely running).
532532
_transport = transport;
533533

534-
if (useAck && _transport is IReconnectFeature reconnectFeature)
534+
if (useAck && _transport is IStatefulReconnectFeature reconnectFeature)
535535
{
536+
#pragma warning disable CA2252 // This API requires opting into preview features
536537
Features.Set(reconnectFeature);
538+
#pragma warning restore CA2252 // This API requires opting into preview features
537539
}
538540

539541
Log.TransportStarted(_logger, transportType);

0 commit comments

Comments
 (0)