public RedisInvocation ReadInvocation(byte[] data) { // See WriteInvocation for format. using (var stream = new MemoryStream(data)) using (var reader = new BinaryReaderWithVarInt(stream, _utf8NoBom)) { IReadOnlyList <string> excludedIds = null; var idCount = reader.ReadVarInt(); if (idCount > 0) { var ids = new string[idCount]; for (var i = 0; i < idCount; i++) { ids[i] = reader.ReadString(); } excludedIds = ids; } var message = SerializedHubMessage.ReadAllSerializedVersions(reader); return(new RedisInvocation(message, excludedIds)); } }
/// <inheritdoc /> public override Task SendGroupsAsync(IReadOnlyList <string> groupNames, string methodName, object[] args, CancellationToken cancellationToken = default) { // Each task represents the list of tasks for each of the writes within a group List <Task> tasks = null; SerializedHubMessage message = null; foreach (var groupName in groupNames) { if (string.IsNullOrEmpty(groupName)) { throw new InvalidOperationException("Cannot send to an empty group name."); } var group = _groups[groupName]; if (group != null) { SendToGroupConnections(methodName, args, group, null, ref tasks, ref message); } } if (tasks != null) { return(Task.WhenAll(tasks)); } return(Task.CompletedTask); }
/// <inheritdoc /> public override Task SendGroupAsync(string groupName, string methodName, object[] args, CancellationToken cancellationToken = default) { if (groupName == null) { throw new ArgumentNullException(nameof(groupName)); } var group = _groups[groupName]; if (group != null) { // Can't optimize for sending to a single connection in a group because // group might be modified inbetween checking and sending List <Task> tasks = null; SerializedHubMessage message = null; SendToGroupConnections(methodName, args, group, null, ref tasks, ref message); if (tasks != null) { return(Task.WhenAll(tasks)); } } return(Task.CompletedTask); }
public byte[] WriteInvocation(string methodName, object[] args, IReadOnlyList <string> excludedIds) { // Redis Invocation Format: // * Variable length integer: Number of excluded Ids // * For each excluded Id: // * Length prefixed string: ID // * SerializedHubMessage encoded by the format described by that type. using (var stream = new MemoryStream()) using (var writer = new BinaryWriterWithVarInt(stream, _utf8NoBom)) { if (excludedIds != null) { writer.WriteVarInt(excludedIds.Count); foreach (var id in excludedIds) { writer.Write(id); } } else { writer.WriteVarInt(0); } SerializedHubMessage.WriteAllSerializedVersions(writer, new InvocationMessage(methodName, argumentBindingException: null, args), _protocols); return(stream.ToArray()); } }
public RedisInvocation(SerializedHubMessage message, IReadOnlyList <string>?excludedConnectionIds, string?invocationId = null, string?returnChannel = null) { Message = message; ExcludedConnectionIds = excludedConnectionIds; ReturnChannel = returnChannel; InvocationId = invocationId; }
public void GetSerializedMessageSerializesUsingHubProtocolIfNoCacheAvailable() { var invocation = new InvocationMessage("Foo", new object[0]); var message = new SerializedHubMessage(invocation); var protocol = new DummyHubProtocol("p1"); var serialized = message.GetSerializedMessage(protocol); Assert.Equal(DummyHubProtocol.DummySerialization, serialized.ToArray()); Assert.Collection(protocol.GetWrittenMessages(), actualMessage => Assert.Same(invocation, actualMessage)); }
public MsmqInvocation( IReadOnlyList <string> connections, IReadOnlyList <string> groups, IReadOnlyList <string> users, IReadOnlyList <string> excludedConnectionIds, SerializedHubMessage message) { this.Connections = connections; this.Groups = groups; this.Users = users; this.ExcludedConnectionIds = excludedConnectionIds; this.Message = message; }
public static IReadOnlyDictionary <string, byte[]> ToProtocolDictionary(this IEnumerable <IHubProtocol> protocols, string methodName, object[] args) { var serializedMessageHub = new SerializedHubMessage(new InvocationMessage(methodName, args)); var messages = new Dictionary <string, byte[]>(); foreach (var protocol in protocols) { ReadOnlyMemory <byte> serialized = serializedMessageHub.GetSerializedMessage(protocol); messages.Add(protocol.Name, serialized.ToArray()); } return(messages); }
private static void WriteSerializedHubMessage(Stream stream, SerializedHubMessage message) { // Written as a MessagePack 'map' where the keys are the name of the protocol (as a MessagePack 'str') // and the values are the serialized blob (as a MessagePack 'bin'). var protocol = new JsonHubProtocol(); MessagePackBinary.WriteMapHeader(stream, 1); MessagePackBinary.WriteString(stream, protocol.Name); var serialized = message.GetSerializedMessage(protocol); MemoryMarshal.TryGetArray(serialized, out var array); MessagePackBinary.WriteBytes(stream, array.Array, array.Offset, array.Count); }
private void WriteSerializedHubMessage(Stream stream, SerializedHubMessage message) { // Written as a MessagePack 'map' where the keys are the name of the protocol (as a MessagePack 'str') // and the values are the serialized blob (as a MessagePack 'bin'). MessagePackBinary.WriteMapHeader(stream, this.protocols.Count); foreach (var protocol in this.protocols) { MessagePackBinary.WriteString(stream, protocol.Name); var serialized = message.GetSerializedMessage(protocol); var isArray = MemoryMarshal.TryGetArray(serialized, out var array); Debug.Assert(isArray, "should be array"); MessagePackBinary.WriteBytes(stream, array.Array, array.Offset, array.Count); } }
public void GetSerializedMessageReturnsCachedSerializationIfAvailable() { var invocation = new InvocationMessage("Foo", new object[0]); var message = new SerializedHubMessage(invocation); var protocol = new DummyHubProtocol("p1"); // This should cache it _ = message.GetSerializedMessage(protocol); // Get it again var serialized = message.GetSerializedMessage(protocol); Assert.Equal(DummyHubProtocol.DummySerialization, serialized.ToArray()); // We should still only have written one message Assert.Collection(protocol.GetWrittenMessages(), actualMessage => Assert.Same(invocation, actualMessage)); }
public Task SendToAllLocalConnection(string methodName, object[] args, string[] excludeConnectionIds = null) { List <Task> tasks = null; SerializedHubMessage message = null; // foreach over HubConnectionStore avoids allocating an enumerator foreach (var connection in _connections) { if (excludeConnectionIds != null && excludeConnectionIds.Contains(connection.ConnectionId)) { continue; } if (message == null) { message = CreateSerializedInvocationMessage(methodName, args); } var task = connection.WriteAsync(message); if (!task.IsCompletedSuccessfully) { if (tasks == null) { tasks = new List <Task>(); } tasks.Add(task.AsTask()); } } if (tasks == null) { return(Task.CompletedTask); } // Some connections are slow return(Task.WhenAll(tasks)); }
public async Task SerializingTwoMessagesFromTheSameProtocolSimultaneouslyResultsInOneCachedItemAsync(int numberOfSerializationsToPreCache) { var invocation = new InvocationMessage("Foo", new object[0]); var message = new SerializedHubMessage(invocation); // "Pre-cache" the requested number of serializations (so we can test scenarios involving each of the fields and the fallback list) for (var i = 0; i < numberOfSerializationsToPreCache; i++) { _ = message.GetSerializedMessage(new DummyHubProtocol($"p{i}")); } var onWrite = SyncPoint.Create(2, out var syncPoints); var protocol = new DummyHubProtocol("test", () => onWrite().Wait()); // Serialize once, but hold at the Hub Protocol var firstSerialization = Task.Run(() => message.GetSerializedMessage(protocol)); await syncPoints[0].WaitForSyncPoint(); // Serialize again, which should hit the lock var secondSerialization = Task.Run(() => message.GetSerializedMessage(protocol)); Assert.False(secondSerialization.IsCompleted); // Release both instances of the syncpoint syncPoints[0].Continue(); syncPoints[1].Continue(); // Everything should finish and only one serialization should be written await firstSerialization.DefaultTimeout(); await secondSerialization.DefaultTimeout(); Assert.Collection(message.GetAllSerializations().Skip(numberOfSerializationsToPreCache).ToArray(), serializedMessage => { Assert.Equal("test", serializedMessage.ProtocolName); Assert.Equal(DummyHubProtocol.DummySerialization, serializedMessage.Serialized.ToArray()); }); }
/// <inheritdoc /> public override Task SendGroupExceptAsync(string groupName, string methodName, object[] args, IReadOnlyList <string> excludedConnectionIds, CancellationToken cancellationToken = default) { if (groupName == null) { throw new ArgumentNullException(nameof(groupName)); } var group = _groups[groupName]; if (group != null) { List <Task> tasks = null; SerializedHubMessage message = null; SendToGroupConnections(methodName, args, group, connection => !excludedConnectionIds.Contains(connection.ConnectionId), ref tasks, ref message); if (tasks != null) { return(Task.WhenAll(tasks)); } } return(Task.CompletedTask); }
public RabbitMQInvocation(SerializedHubMessage message, IReadOnlyList <string> excludedConnectionIds) { this.Message = message; this.ExcludedConnectionIds = excludedConnectionIds; }
// Tasks and message are passed by ref so they can be lazily created inside the method post-filtering, // while still being re-usable when sending to multiple groups private void SendToGroupConnections(string methodName, object[] args, ConcurrentDictionary <string, HubConnectionContext> connections, Func <HubConnectionContext, bool> include, ref List <Task> tasks, ref SerializedHubMessage message) { // foreach over ConcurrentDictionary avoids allocating an enumerator foreach (var connection in connections) { if (include != null && !include(connection.Value)) { continue; } if (message == null) { message = CreateSerializedInvocationMessage(methodName, args); } var task = connection.Value.WriteAsync(message); if (!task.IsCompletedSuccessfully) { if (tasks == null) { tasks = new List <Task>(); } tasks.Add(task.AsTask()); } } }
public PostgreSqlInvocation(SerializedHubMessage message, IReadOnlyList <string> excludedConnectionIds) { Message = message; ExcludedConnectionIds = excludedConnectionIds; }
public RedisInvocation(SerializedHubMessage message, IReadOnlyList <string> excludedIds) { Message = message; ExcludedIds = excludedIds; }
public async Task SendMessage(SerializedHubMessage message) => await Clients.All.SendAsync("recieveMessage", message);