Skip to content

Commit 606be6a

Browse files
committed
start
1 parent 5440cd3 commit 606be6a

16 files changed

+198
-57
lines changed

src/Components/Server/src/DependencyInjection/ComponentServiceCollectionExtensions.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,11 +45,11 @@ public static IServerSideBlazorBuilder AddServerSideBlazor(this IServiceCollecti
4545
services.AddSignalR().AddHubOptions<ComponentHub>(options =>
4646
{
4747
options.SupportedProtocols.Clear();
48-
options.SupportedProtocols.Add(BlazorPackHubProtocol.ProtocolName);
49-
});
5048

51-
// Register the Blazor specific hub protocol
52-
services.TryAddEnumerable(ServiceDescriptor.Singleton<IHubProtocol, BlazorPackHubProtocol>());
49+
// Add Blazor specific hub protocol here instead of in DI
50+
// Because adding to DI would add this protocol to all Hubs by default
51+
options.AdditionalHubProtocols.Add(new BlazorPackHubProtocol());
52+
});
5353

5454
// Here we add a bunch of services that don't vary in any way based on the
5555
// user's configuration. So even if the user has multiple independent server-side

src/Components/Server/test/DependencyInjection/ComponentServiceCollectionExtensionsTest.cs

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,9 @@ public void AddServerSideSignalR_RegistersBlazorPack()
2323
var options = services.BuildServiceProvider().GetRequiredService<IOptions<HubOptions<ComponentHub>>>();
2424

2525
// Assert
26-
var protocol = Assert.Single(options.Value.SupportedProtocols);
27-
Assert.Equal(BlazorPackHubProtocol.ProtocolName, protocol);
26+
Assert.Empty(options.Value.SupportedProtocols);
27+
var protocol = Assert.Single(options.Value.AdditionalHubProtocols);
28+
Assert.Equal(BlazorPackHubProtocol.ProtocolName, protocol.Name);
2829
}
2930

3031
[Fact]
@@ -44,8 +45,9 @@ public void AddServerSideSignalR_RespectsGlobalHubOptions()
4445
var options = services.BuildServiceProvider().GetRequiredService<IOptions<HubOptions<ComponentHub>>>();
4546

4647
// Assert
47-
var protocol = Assert.Single(options.Value.SupportedProtocols);
48-
Assert.Equal(BlazorPackHubProtocol.ProtocolName, protocol);
48+
Assert.Empty(options.Value.SupportedProtocols);
49+
var protocol = Assert.Single(options.Value.AdditionalHubProtocols);
50+
Assert.Equal(BlazorPackHubProtocol.ProtocolName, protocol.Name);
4951
Assert.Equal(TimeSpan.FromMinutes(10), options.Value.HandshakeTimeout);
5052
}
5153

@@ -71,8 +73,9 @@ public void AddServerSideSignalR_ConfiguresGlobalOptionsBeforePerHubOptions()
7173
var globalOptions = services.BuildServiceProvider().GetRequiredService<IOptions<HubOptions>>();
7274

7375
// Assert
74-
var protocol = Assert.Single(options.Value.SupportedProtocols);
75-
Assert.Equal(BlazorPackHubProtocol.ProtocolName, protocol);
76+
Assert.Empty(options.Value.SupportedProtocols);
77+
var protocol = Assert.Single(options.Value.AdditionalHubProtocols);
78+
Assert.Equal(BlazorPackHubProtocol.ProtocolName, protocol.Name);
7679
Assert.Equal(TimeSpan.FromMinutes(5), options.Value.HandshakeTimeout);
7780

7881
// Configuring Blazor options is kept separate from the global options.

src/SignalR/perf/Microbenchmarks/HubConnectionContextBenchmark.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ public async Task SuccessHandshakeAsync()
6161
_pipe.AddReadResult(new ValueTask<ReadResult>(_handshakeRequestResult));
6262

6363
await _hubConnectionContext.HandshakeAsync(TimeSpan.FromSeconds(5), _supportedProtocols, _successHubProtocolResolver,
64-
_userIdProvider, enableDetailedErrors: true);
64+
_userIdProvider, enableDetailedErrors: true, Array.Empty<IHubProtocol>());
6565
}
6666

6767
[Benchmark]
@@ -70,7 +70,7 @@ public async Task ErrorHandshakeAsync()
7070
_pipe.AddReadResult(new ValueTask<ReadResult>(_handshakeRequestResult));
7171

7272
await _hubConnectionContext.HandshakeAsync(TimeSpan.FromSeconds(5), _supportedProtocols, _failureHubProtocolResolver,
73-
_userIdProvider, enableDetailedErrors: true);
73+
_userIdProvider, enableDetailedErrors: true, Array.Empty<IHubProtocol>());
7474
}
7575
}
7676

src/SignalR/perf/Microbenchmarks/RedisProtocolBenchmark.cs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ namespace Microsoft.AspNetCore.SignalR.Microbenchmarks
1313
{
1414
public class RedisProtocolBenchmark
1515
{
16-
private RedisProtocol _protocol;
16+
private RedisProtocol<TestHub> _protocol;
1717
private RedisGroupCommand _groupCommand;
1818
private object[] _args;
1919
private string _methodName;
@@ -28,7 +28,7 @@ public class RedisProtocolBenchmark
2828
[GlobalSetup]
2929
public void GlobalSetup()
3030
{
31-
_protocol = new RedisProtocol(new [] {
31+
_protocol = new RedisProtocol<TestHub>(new [] {
3232
new DummyProtocol("protocol1"),
3333
new DummyProtocol("protocol2")
3434
});
@@ -119,7 +119,7 @@ private static IReadOnlyList<string> GenerateIds(int count)
119119
return ids;
120120
}
121121

122-
private class DummyProtocol: IHubProtocol
122+
private class DummyProtocol : IHubProtocol
123123
{
124124
private static readonly byte[] _fixedOutput = new byte[] { 0x68, 0x68, 0x6C, 0x6C, 0x6F };
125125

@@ -152,5 +152,8 @@ public ReadOnlyMemory<byte> GetMessageBytes(HubMessage message)
152152
return HubProtocolExtensions.GetMessageBytes(this, message);
153153
}
154154
}
155+
156+
private class TestHub : Hub
157+
{ }
155158
}
156159
}

src/SignalR/server/Core/src/HubConnectionContext.cs

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -385,7 +385,7 @@ private void AbortAllowReconnect()
385385
}
386386

387387
internal async Task<bool> HandshakeAsync(TimeSpan timeout, IReadOnlyList<string> supportedProtocols, IHubProtocolResolver protocolResolver,
388-
IUserIdProvider userIdProvider, bool enableDetailedErrors)
388+
IUserIdProvider userIdProvider, bool enableDetailedErrors, IList<IHubProtocol> additionalProtocols)
389389
{
390390
try
391391
{
@@ -437,10 +437,22 @@ internal async Task<bool> HandshakeAsync(TimeSpan timeout, IReadOnlyList<string>
437437
Protocol = protocolResolver.GetProtocol(handshakeRequestMessage.Protocol, supportedProtocols);
438438
if (Protocol == null)
439439
{
440-
Log.HandshakeFailed(_logger, null);
440+
foreach (var protocol in additionalProtocols)
441+
{
442+
if (string.Equals(handshakeRequestMessage.Protocol, protocol.Name, StringComparison.OrdinalIgnoreCase))
443+
{
444+
Protocol = protocol;
445+
break;
446+
}
447+
}
441448

442-
await WriteHandshakeResponseAsync(new HandshakeResponseMessage($"The protocol '{handshakeRequestMessage.Protocol}' is not supported."));
443-
return false;
449+
if (Protocol == null)
450+
{
451+
Log.HandshakeFailed(_logger, null);
452+
453+
await WriteHandshakeResponseAsync(new HandshakeResponseMessage($"The protocol '{handshakeRequestMessage.Protocol}' is not supported."));
454+
return false;
455+
}
444456
}
445457

446458
if (!Protocol.IsVersionSupported(handshakeRequestMessage.Version))

src/SignalR/server/Core/src/HubConnectionHandler.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,8 @@ public override async Task OnConnectedAsync(ConnectionContext connection)
7777
// Then set the keepAlive and handshakeTimeout values to the defaults in HubOptionsSetup when they were explicitly set to null.
7878

7979
var supportedProtocols = _hubOptions.SupportedProtocols ?? _globalHubOptions.SupportedProtocols;
80-
if (supportedProtocols == null || supportedProtocols.Count == 0)
80+
var additionalProtocols = _hubOptions.AdditionalHubProtocols ?? _globalHubOptions.AdditionalHubProtocols ?? Array.Empty<IHubProtocol>();
81+
if ((supportedProtocols == null || supportedProtocols.Count == 0) && additionalProtocols.Count == 0)
8182
{
8283
throw new InvalidOperationException("There are no supported protocols");
8384
}
@@ -97,7 +98,7 @@ public override async Task OnConnectedAsync(ConnectionContext connection)
9798
var connectionContext = new HubConnectionContext(connection, contextOptions, _loggerFactory);
9899

99100
var resolvedSupportedProtocols = (supportedProtocols as IReadOnlyList<string>) ?? supportedProtocols.ToList();
100-
if (!await connectionContext.HandshakeAsync(handshakeTimeout, resolvedSupportedProtocols, _protocolResolver, _userIdProvider, _enableDetailedErrors))
101+
if (!await connectionContext.HandshakeAsync(handshakeTimeout, resolvedSupportedProtocols, _protocolResolver, _userIdProvider, _enableDetailedErrors, additionalProtocols))
101102
{
102103
return;
103104
}

src/SignalR/server/Core/src/HubOptions.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
using System;
55
using System.Collections.Generic;
6+
using Microsoft.AspNetCore.SignalR.Protocol;
67

78
namespace Microsoft.AspNetCore.SignalR
89
{
@@ -51,5 +52,11 @@ public class HubOptions
5152
/// Gets or sets the max buffer size for client upload streams. The default size is 10.
5253
/// </summary>
5354
public int? StreamBufferCapacity { get; set; } = null;
55+
56+
/// <summary>
57+
/// Add protocols specific to this Hub so other Hubs do not get these protocols by default.
58+
/// When using this you do not need to add the IHubProtocol to DI.
59+
/// </summary>
60+
public IList<IHubProtocol> AdditionalHubProtocols { get; set; } = null;
5461
}
5562
}

src/SignalR/server/Core/src/HubOptionsSetup.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,11 @@ public void Configure(HubOptions options)
5454
options.SupportedProtocols = new List<string>();
5555
}
5656

57+
if (options.AdditionalHubProtocols == null)
58+
{
59+
options.AdditionalHubProtocols = new List<IHubProtocol>();
60+
}
61+
5762
if (options.StreamBufferCapacity == null)
5863
{
5964
options.StreamBufferCapacity = DefaultStreamBufferCapacity;

src/SignalR/server/Core/src/HubOptionsSetup`T.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ public void Configure(HubOptions<THub> options)
2323
}
2424
options.KeepAliveInterval = _hubOptions.KeepAliveInterval;
2525
options.HandshakeTimeout = _hubOptions.HandshakeTimeout;
26+
27+
options.AdditionalHubProtocols = _hubOptions.AdditionalHubProtocols;
2628
}
2729
}
2830
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using Microsoft.AspNetCore.SignalR.Protocol;
5+
6+
namespace Microsoft.AspNetCore.SignalR
7+
{
8+
/// <summary>
9+
/// An abstraction that provides serialized <see cref="HubMessage"/>s for relevant <see cref="IHubProtocol"/>s.
10+
/// </summary>
11+
public interface IHubMessageSerializer<THub> where THub : Hub
12+
{
13+
/// <summary>
14+
/// Serializes the provided <see cref="HubMessage"/> for all <see cref="IHubProtocol"/>s registered for this Hub.
15+
/// </summary>
16+
/// <param name="message">The <see cref="HubMessage"/> to serialize.</param>
17+
/// <returns>Contains the serialized <see cref="HubMessage"/> for all protocols registered for this Hub.</returns>
18+
SerializedHubMessage SerializeMessage(HubMessage message);
19+
}
20+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using System;
5+
using System.Collections.Generic;
6+
using System.Linq;
7+
using Microsoft.AspNetCore.SignalR.Protocol;
8+
using Microsoft.Extensions.Options;
9+
10+
namespace Microsoft.AspNetCore.SignalR.Internal
11+
{
12+
internal class DefaultHubMessageSerializer<THub> : IHubMessageSerializer<THub> where THub : Hub
13+
{
14+
private List<IHubProtocol> _hubProtocols = new List<IHubProtocol>();
15+
16+
public DefaultHubMessageSerializer(IHubProtocolResolver hubProtocolResolver, IOptions<HubOptions> globalHubOptions, IOptions<HubOptions<THub>> hubOptions)
17+
{
18+
var supportedProtocols = hubOptions.Value.SupportedProtocols ?? globalHubOptions.Value.SupportedProtocols ?? Array.Empty<string>();
19+
foreach (var protocolName in supportedProtocols)
20+
{
21+
var protocol = hubProtocolResolver.GetProtocol(protocolName, (supportedProtocols as IReadOnlyList<string>) ?? supportedProtocols.ToList());
22+
if (protocol != null)
23+
{
24+
_hubProtocols.Add(protocol);
25+
}
26+
}
27+
28+
var additionalProtocols = hubOptions.Value.AdditionalHubProtocols ?? globalHubOptions.Value.AdditionalHubProtocols ?? Array.Empty<IHubProtocol>();
29+
foreach (var protocol in additionalProtocols)
30+
{
31+
_hubProtocols.Add(protocol);
32+
}
33+
}
34+
35+
public SerializedHubMessage SerializeMessage(HubMessage message)
36+
{
37+
var list = new List<SerializedMessage>(_hubProtocols.Count);
38+
foreach (var protocol in _hubProtocols)
39+
{
40+
list.Add(new SerializedMessage(protocol.Name, protocol.GetMessageBytes(message)));
41+
}
42+
43+
return new SerializedHubMessage(list);
44+
}
45+
}
46+
}

src/SignalR/server/Core/src/SerializedHubMessage.cs

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ public class SerializedHubMessage
1414
{
1515
private SerializedMessage _cachedItem1;
1616
private SerializedMessage _cachedItem2;
17-
private IList<SerializedMessage> _cachedItems;
17+
private List<SerializedMessage> _cachedItems;
1818
private readonly object _lock = new object();
1919

2020
/// <summary>
@@ -32,7 +32,7 @@ public SerializedHubMessage(IReadOnlyList<SerializedMessage> messages)
3232
for (var i = 0; i < messages.Count; i++)
3333
{
3434
var message = messages[i];
35-
SetCache(message.ProtocolName, message.Serialized);
35+
SetCacheUnsynchronized(message.ProtocolName, message.Serialized);
3636
}
3737
}
3838

@@ -54,7 +54,7 @@ public ReadOnlyMemory<byte> GetSerializedMessage(IHubProtocol protocol)
5454
{
5555
lock (_lock)
5656
{
57-
if (!TryGetCached(protocol.Name, out var serialized))
57+
if (!TryGetCachedUnsynchronized(protocol.Name, out var serialized))
5858
{
5959
if (Message == null)
6060
{
@@ -63,42 +63,45 @@ public ReadOnlyMemory<byte> GetSerializedMessage(IHubProtocol protocol)
6363
}
6464

6565
serialized = protocol.GetMessageBytes(Message);
66-
SetCache(protocol.Name, serialized);
66+
SetCacheUnsynchronized(protocol.Name, serialized);
6767
}
6868

6969
return serialized;
7070
}
7171
}
7272

73-
// Used for unit testing.
74-
internal IReadOnlyList<SerializedMessage> GetAllSerializations()
73+
/// <summary>
74+
/// Gets all serialized hub messages for all protocols used on this <see cref="SerializedHubMessage"/> instance.
75+
/// </summary>
76+
/// <returns>An <see cref="IEnumerable{T}"/> of already serialized hub messages for each protocol.</returns>
77+
public IEnumerable<SerializedMessage> GetAllSerializations()
7578
{
7679
// Even if this is only used in tests, let's do it right.
7780
lock (_lock)
7881
{
7982
if (_cachedItem1.ProtocolName == null)
8083
{
81-
return Array.Empty<SerializedMessage>();
84+
yield break;
8285
}
8386

84-
var list = new List<SerializedMessage>(2);
85-
list.Add(_cachedItem1);
87+
yield return _cachedItem1;
8688

8789
if (_cachedItem2.ProtocolName != null)
8890
{
89-
list.Add(_cachedItem2);
91+
yield return _cachedItem2;
9092

9193
if (_cachedItems != null)
9294
{
93-
list.AddRange(_cachedItems);
95+
foreach (var item in _cachedItems)
96+
{
97+
yield return item;
98+
}
9499
}
95100
}
96-
97-
return list;
98101
}
99102
}
100103

101-
private void SetCache(string protocolName, ReadOnlyMemory<byte> serialized)
104+
private void SetCacheUnsynchronized(string protocolName, ReadOnlyMemory<byte> serialized)
102105
{
103106
// We set the fields before moving on to the list, if we need it to hold more than 2 items.
104107
// We have to read/write these fields under the lock because the structs might tear and another
@@ -132,7 +135,7 @@ private void SetCache(string protocolName, ReadOnlyMemory<byte> serialized)
132135
}
133136
}
134137

135-
private bool TryGetCached(string protocolName, out ReadOnlyMemory<byte> result)
138+
private bool TryGetCachedUnsynchronized(string protocolName, out ReadOnlyMemory<byte> result)
136139
{
137140
if (string.Equals(_cachedItem1.ProtocolName, protocolName, StringComparison.Ordinal))
138141
{

src/SignalR/server/Core/src/SignalRDependencyInjectionExtensions.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ public static ISignalRServerBuilder AddSignalRCore(this IServiceCollection servi
2323
services.TryAddSingleton<SignalRCoreMarkerService>();
2424
services.TryAddSingleton(typeof(HubLifetimeManager<>), typeof(DefaultHubLifetimeManager<>));
2525
services.TryAddSingleton(typeof(IHubProtocolResolver), typeof(DefaultHubProtocolResolver));
26+
services.TryAddSingleton(typeof(IHubMessageSerializer<>), typeof(DefaultHubMessageSerializer<>));
2627
services.TryAddSingleton(typeof(IHubContext<>), typeof(HubContext<>));
2728
services.TryAddSingleton(typeof(IHubContext<,>), typeof(HubContext<,>));
2829
services.TryAddSingleton(typeof(HubConnectionHandler<>), typeof(HubConnectionHandler<>));

0 commit comments

Comments
 (0)