Skip to content
3 changes: 1 addition & 2 deletions src/Microsoft.Azure.SignalR.Emulator/AppBuilderExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication.JwtBearer;
Expand Down Expand Up @@ -77,7 +76,7 @@ public static IServiceCollection AddSignalREmulator(this IServiceCollection serv
services.AddSingleton<IUserIdProvider, AzureSignalRCustomUserIdProvider>();
services.AddSingleton(typeof(HttpServerlessMessageHandler<>));
services.AddSingleton<IHttpUpstreamTrigger, HttpUpstreamTrigger>();
services.AddSingleton<DynamicHubContextStore>();
services.AddSingleton<IDynamicHubContextStore, DynamicHubContextStore>();
services.AddSignalR().AddMessagePackProtocol();
return services;
}
Expand Down
45 changes: 45 additions & 0 deletions src/Microsoft.Azure.SignalR.Emulator/Common/Constants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,5 +37,50 @@ public static class Headers
public const string AsrsClientCertThumbprint = AsrsHeaderPrefix + "Client-Cert-Thumbprint";
public const string AsrsConnectionGroups = AsrsHeaderPrefix + "Connection-Group";
}

public enum ErrorCodeLevel
{
Warning,
Info,
Error
}

public enum ErrorCodeScope
{
Connection,
User,
Group
}

public enum ErrorCodeKind
{
NotExisted,
NotInGroup
}

public static class ErrorCodes
{
public static string BuildErrorCode(ErrorCodeLevel level, ErrorCodeScope scope, ErrorCodeKind kind)
{
return $"{level}.{scope}.{kind}";
}

public static class Warning
{
public static string ConnectionNotExisted => BuildErrorCode(ErrorCodeLevel.Warning, ErrorCodeScope.Connection, ErrorCodeKind.NotExisted);
public static string UserNotExisted => BuildErrorCode(ErrorCodeLevel.Warning, ErrorCodeScope.User, ErrorCodeKind.NotExisted);
public static string GroupNotExisted => BuildErrorCode(ErrorCodeLevel.Warning, ErrorCodeScope.Group, ErrorCodeKind.NotExisted);
}

public static class Info
{
public static string UserNotInGroup => BuildErrorCode(ErrorCodeLevel.Info, ErrorCodeScope.User, ErrorCodeKind.NotInGroup);
}

public static class Error
{
public static string ConnectionNotExisted => BuildErrorCode(ErrorCodeLevel.Error, ErrorCodeScope.Connection, ErrorCodeKind.NotExisted);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,17 @@
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.IO.Pipelines;
using System.Linq;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.SignalR;
using Microsoft.AspNetCore.SignalR.Protocol;
using Microsoft.Azure.SignalR.Common;
using Microsoft.Azure.SignalR.Controllers.Common;
using Microsoft.Azure.SignalR.Emulator.HubEmulator;
using Microsoft.Extensions.Logging;
using static System.Net.Mime.MediaTypeNames;

namespace Microsoft.Azure.SignalR.Emulator.Controllers
{
Expand All @@ -26,10 +24,10 @@ internal class SignalRServiceEmulatorWebApi : SignalRServiceWebApiDefinition
{
private const string HubPattern = "^[A-Za-z][A-Za-z0-9_`,.[\\]]{0,127}$";
private const string GroupPattern = "^\\S{1,1024}$";
private readonly DynamicHubContextStore _store;
private readonly IDynamicHubContextStore _store;
private readonly ILogger<SignalRServiceEmulatorWebApi> _logger;

public SignalRServiceEmulatorWebApi(DynamicHubContextStore store, ILogger<SignalRServiceEmulatorWebApi> _logger) : base()
public SignalRServiceEmulatorWebApi(IDynamicHubContextStore store, ILogger<SignalRServiceEmulatorWebApi> _logger) : base()
{
_store = store;
this._logger = _logger;
Expand Down Expand Up @@ -107,6 +105,8 @@ public override IActionResult CheckConnectionExistence(
}
}

Response.SetMsErrorCodeHeader(Constants.ErrorCodes.Warning.ConnectionNotExisted);

return NotFound();
}

Expand All @@ -132,6 +132,8 @@ public override IActionResult CheckGroupExistence(
}
}

Response.SetMsErrorCodeHeader(Constants.ErrorCodes.Warning.GroupNotExisted);

return NotFound();
}

Expand All @@ -157,6 +159,8 @@ public override IActionResult CheckUserExistence(
}
}

Response.SetMsErrorCodeHeader(Constants.ErrorCodes.Warning.UserNotExisted);

return NotFound();
}

Expand Down Expand Up @@ -206,6 +210,8 @@ public override IActionResult RemoveConnectionFromGroup(
}
}

Response.SetMsErrorCodeHeader(Constants.ErrorCodes.Error.ConnectionNotExisted);

return NotFound();
}

Expand All @@ -220,7 +226,7 @@ public override IActionResult RemoveConnectionFromAllGroups(
}

if (_store.TryGetLifetimeContext(GetInternalHubName(application, hub), out var c))
{
{
c.UserGroupManager.RemoveConnectionFromAllGroups(connectionId);
return Ok();
}
Expand Down Expand Up @@ -253,6 +259,8 @@ public override IActionResult AddConnectionToGroup(
}
}

Response.SetMsErrorCodeHeader(Constants.ErrorCodes.Error.ConnectionNotExisted);

return NotFound();
}

Expand Down Expand Up @@ -337,6 +345,8 @@ public override IActionResult CheckUserExistenceInGroup(
}
}

Response.SetMsErrorCodeHeader(Constants.ErrorCodes.Info.UserNotInGroup);

return NotFound();
}

Expand Down Expand Up @@ -455,7 +465,7 @@ public override async Task<IActionResult> CloseGroupConnections(

if (_store.TryGetLifetimeContext(GetInternalHubName(application, hub), out var c))
{
foreach(var cc in c.UserGroupManager.GetConnectionsForGroup(group).Value)
foreach (var cc in c.UserGroupManager.GetConnectionsForGroup(group).Value)
{
var lifetime = c.LifetimeManager;
var connection = lifetime.Connections[cc];
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Primitives;

namespace Microsoft.Azure.SignalR;

#nullable enable

internal static class HttpResponseExtensions
{
public static bool IsSuccessStatusCode(this HttpResponse response) =>
response.StatusCode >= 200 && response.StatusCode <= 299;

public static void SetMsErrorCodeHeader(this HttpResponse response, string code)
{
if (!string.IsNullOrEmpty(code))
{
const string MSErrorCodeKey = "x-ms-error-code";
if (!response.Headers.ContainsKey(MSErrorCodeKey))
{
response.Headers[MSErrorCodeKey] = new StringValues(code);
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
using System.Threading.Tasks;
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System.Threading.Tasks;
using Microsoft.AspNetCore.SignalR;
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
Expand All @@ -9,11 +12,11 @@ namespace Microsoft.Azure.SignalR.Emulator.HubEmulator
{
internal class CachedHubLifetimeManager<THub> : DefaultHubLifetimeManager<THub>, IHubLifetimeManager where THub : Hub
{
private readonly DynamicHubContextStore _store;
private readonly IDynamicHubContextStore _store;
private readonly string _hub;
public HubConnectionStore Connections { get; } = new HubConnectionStore();

public CachedHubLifetimeManager(DynamicHubContextStore store, ILogger<CachedHubLifetimeManager<THub>> logger) : base(logger)
public CachedHubLifetimeManager(IDynamicHubContextStore store, ILogger<CachedHubLifetimeManager<THub>> logger) : base(logger)
{
_store = store;
_hub = typeof(THub).Name;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ namespace Microsoft.Azure.SignalR.Emulator.HubEmulator
{
internal class DynamicConnectionHandler : ConnectionHandler
{
private readonly DynamicHubContextStore _store;
private readonly IDynamicHubContextStore _store;

public DynamicConnectionHandler(DynamicHubContextStore store)
public DynamicConnectionHandler(IDynamicHubContextStore store)
{
_store = store;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,14 @@ public DynamicHubContext(
ConnectionHandler = connectionHandler;
}

protected DynamicHubContext()
{
}

public Type HubType { get; }
public IHubClients ClientManager { get; }
public IHubLifetimeManager LifetimeManager { get; }
public ConnectionHandler ConnectionHandler { get; }
public GroupManager UserGroupManager { get; } = new GroupManager();
public virtual GroupManager UserGroupManager { get; } = new GroupManager();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

namespace Microsoft.Azure.SignalR.Emulator.HubEmulator
{
internal class DynamicHubContextStore
internal class DynamicHubContextStore : IDynamicHubContextStore
{
private readonly ModuleBuilder _hubModule =
AssemblyBuilder
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

namespace Microsoft.Azure.SignalR.Emulator.HubEmulator
{
internal interface IDynamicHubContextStore
{
bool TryGetLifetimeContext(string hub, out DynamicHubContext context);

public DynamicHubContext GetOrAdd(string hub);
}
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System.Runtime.CompilerServices;

[assembly:InternalsVisibleTo("Microsoft.Azure.SignalR.Emulator.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
[assembly: InternalsVisibleTo("Microsoft.Azure.SignalR.Emulator.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7")]
Loading