in src/Microsoft.Azure.SignalR.AspNet/HubHost/SignalRMessageParser.cs [54:169]
public IEnumerable<AppMessage> GetMessages(Message message)
{
if (message.IsCommand)
{
var command = _serializer.Parse<Command>(message.Value, message.Encoding);
switch (command.CommandType)
{
case CommandType.AddToGroup:
{
// name is hg-{HubName}.{GroupName}, consider the whole as the actual group name
// this message always goes through the appName-connection
var groupName = command.Value;
var connectionId = GetName(message.Key, PrefixHelper.ConnectionIdPrefix);
var joinGroupWithAckMessage = new JoinGroupWithAckMessage(connectionId, groupName).WithTracingId();
if (joinGroupWithAckMessage.TracingId != null)
{
MessageLog.StartToAddConnectionToGroup(_logger, joinGroupWithAckMessage);
}
// go through the app connection
// use groupName as the partitionkey so that commands towards the same group always goes into the same service connection
yield return new AppMessage(joinGroupWithAckMessage, message);
yield break;
}
case CommandType.RemoveFromGroup:
{
// this message always goes through the appName-connection
var groupName = command.Value;
var connectionId = GetName(message.Key, PrefixHelper.ConnectionIdPrefix);
var leaveGroupWithAckMessage = new LeaveGroupWithAckMessage(connectionId, groupName).WithTracingId();
if (leaveGroupWithAckMessage.TracingId != null)
{
MessageLog.StartToRemoveConnectionFromGroup(_logger, leaveGroupWithAckMessage);
}
// go through the app connection
// use groupName as the partitionkey so that commands towards the same group always goes into the same service connection
yield return new AppMessage(leaveGroupWithAckMessage, message);
yield break;
}
case CommandType.Initializing:
yield break;
case CommandType.Abort:
yield break;
}
}
var segment = GetPayload(message);
// broadcast case
if (TryGetName(message.Key, PrefixHelper.HubPrefix, out var hubName))
{
var broadcastDataMessage = new BroadcastDataMessage(excludedList: GetExcludedIds(message.Filter), payloads: GetPayloads(segment)).WithTracingId();
if (broadcastDataMessage.TracingId != null)
{
MessageLog.StartToBroadcastMessage(_logger, broadcastDataMessage);
}
yield return new HubMessage(hubName, broadcastDataMessage, message);
}
// echo case
else if (TryGetName(message.Key, PrefixHelper.HubConnectionIdPrefix, out _))
{
// naming: hc-{HubName}.{ConnectionId}
// ConnectionId can NEVER contain .
var index = message.Key.LastIndexOf('.');
if (index < 0 || index == message.Key.Length - 1)
{
throw new ArgumentException($"Key must contain '.' in between but it is not: {message.Key}");
}
var connectionId = message.Key.Substring(index + 1);
var connectionDataMessage = new ConnectionDataMessage(connectionId, segment).WithTracingId();
if (connectionDataMessage.TracingId != null)
{
MessageLog.StartToSendMessageToConnection(_logger, connectionDataMessage);
}
// Go through the app connection
yield return new AppMessage(connectionDataMessage, message);
}
// group broadcast case
else if (TryGetName(message.Key, PrefixHelper.HubGroupPrefix, out _))
{
// naming: hg-{HubName}.{GroupName}, it as a whole is the group name per the JoinGroup implementation
// go through the app connection
// use groupName as the partitionkey so that commands towards the same group always goes into the same service connection
var groupName = message.Key;
var groupBroadcastDataMessage = new GroupBroadcastDataMessage(groupName, excludedList: GetExcludedIds(message.Filter), payloads: GetPayloads(segment)).WithTracingId();
if (groupBroadcastDataMessage.TracingId != null)
{
MessageLog.StartToBroadcastMessageToGroup(_logger, groupBroadcastDataMessage);
}
yield return new AppMessage(groupBroadcastDataMessage, message);
}
// user case
else if (TryGetName(message.Key, PrefixHelper.HubUserPrefix, out var userWithHubPrefix))
{
// naming: hu-{HubName}.{UserName}, HubName can contain '.' and UserName can contain '.'
// Go through all the possibilities
foreach (var (hub, user) in GetPossibleNames(userWithHubPrefix))
{
var userDataMessage = new UserDataMessage(user, GetPayloads(segment)).WithTracingId();
if (userDataMessage.TracingId != null)
{
MessageLog.StartToSendMessageToUser(_logger, userDataMessage);
}
// For old protocol, it is always single user per message https://github.com/SignalR/SignalR/blob/dev/src/Microsoft.AspNet.SignalR.Core/Infrastructure/Connection.cs#L162
yield return new HubMessage(hub, userDataMessage, message);
}
}
else
{
throw new NotSupportedException($"Message {message.Key} is not supported.");
}
}