Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ApplicationName to options and use it to prefix the HubName #449

Merged
merged 15 commits into from
Apr 8, 2019
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ internal class ServiceEndpointProvider : IServiceEndpointProvider

private readonly string _endpoint;
private readonly string _accessKey;
private readonly string _appName;
private readonly int? _port;
private readonly TimeSpan _accessTokenLifetime;

Expand All @@ -37,6 +38,7 @@ public ServiceEndpointProvider(ServiceEndpoint endpoint, TimeSpan? ttl = null)
// Version is ignored for aspnet signalr case
_endpoint = endpoint.Endpoint;
_accessKey = endpoint.AccessKey;
_appName = endpoint.ApplicationName;
_port = endpoint.Port;
}

Expand All @@ -57,7 +59,8 @@ public string GenerateServerAccessToken(string hubName, string userId, TimeSpan?
};
}

var audience = $"{_endpoint}/{ServerPath}/?hub={hubName.ToLower()}";
var prefixedHubName = string.IsNullOrEmpty(_appName) ? hubName.ToLower() : $"{_appName.ToLower()}_{hubName.ToLower()}";
var audience = $"{_endpoint}/{ServerPath}/?hub={prefixedHubName.ToLower()}";

return AuthenticationHelper.GenerateAccessToken(_accessKey, audience, claims, lifetime ?? _accessTokenLifetime, requestId);
}
Expand Down Expand Up @@ -95,9 +98,10 @@ public string GetClientEndpoint(string hubName = null, string originalPath = nul

public string GetServerEndpoint(string hubName)
{
var prefixedHubName = string.IsNullOrEmpty(_appName) ? hubName.ToLower() : $"{_appName.ToLower()}_{hubName.ToLower()}";
return _port.HasValue ?
$"{_endpoint}:{_port}/{ServerPath}/?hub={hubName.ToLower()}" :
$"{_endpoint}/{ServerPath}/?hub={hubName.ToLower()}";
$"{_endpoint}:{_port}/{ServerPath}/?hub={prefixedHubName.ToLower()}" :
$"{_endpoint}/{ServerPath}/?hub={prefixedHubName.ToLower()}";
}
}
}
9 changes: 9 additions & 0 deletions src/Microsoft.Azure.SignalR.AspNet/OwinExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,15 @@ private static void RunAzureSignalRCore(IAppBuilder builder, string applicationN
throw new ArgumentException(nameof(applicationName), "Empty application name is not allowed.");
}

if (options.UseHubNamePrefix)
{
options.ApplicationName = applicationName;
}
else
{
options.ApplicationName = "";
}

if (configuration == null)
{
// Keep the same as SignalR's exception
Expand Down
20 changes: 15 additions & 5 deletions src/Microsoft.Azure.SignalR.AspNet/ServiceOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,16 @@ public class ServiceOptions : IServiceEndpointOptions
/// </summary>
public int ConnectionCount { get; set; } = 5;

/// <summary>
/// Gets applicationName, which will be used as a prefix to apply to each hub name
/// </summary>
public string ApplicationName{ get; internal set; }

/// <summary>
/// Gets or sets whether the hub name wiull be prefixed with the ApplicationName
/// </summary>
public bool UseHubNamePrefix { get; set; }

/// <summary>
/// Gets or sets the func to generate claims from <see cref="IOwinContext" />.
/// The claims will be included in the auto-generated token for clients.
Expand All @@ -47,7 +57,7 @@ public ServiceOptions()
for (int i = 0; i < count; i++)
{
var setting = ConfigurationManager.ConnectionStrings[i];
var (isDefault, endpoint) = GetEndpoint(setting.Name, () => setting.ConnectionString);
var (isDefault, endpoint) = GetEndpoint(setting.Name, this.ApplicationName, () => setting.ConnectionString);
if (endpoint != null)
{
if (isDefault)
Expand All @@ -64,7 +74,7 @@ public ServiceOptions()
// Fallback to use AppSettings
foreach(var key in ConfigurationManager.AppSettings.AllKeys)
{
var (isDefault, endpoint) = GetEndpoint(key, () => ConfigurationManager.AppSettings[key]);
var (isDefault, endpoint) = GetEndpoint(key, this.ApplicationName, () => ConfigurationManager.AppSettings[key]);
if (endpoint != null)
{
if (isDefault)
Expand All @@ -81,16 +91,16 @@ public ServiceOptions()
Endpoints = endpoints.ToArray();
}

private static (bool isDefault, ServiceEndpoint endpoint) GetEndpoint(string key, Func<string> valueGetter)
private static (bool isDefault, ServiceEndpoint endpoint) GetEndpoint(string key, string appName, Func<string> valueGetter)
{
if (key == Constants.ConnectionStringDefaultKey && !string.IsNullOrEmpty(valueGetter()))
{
return (true, new ServiceEndpoint(valueGetter()));
return (true, new ServiceEndpoint(valueGetter(), applicationName: appName));
}

if (key.StartsWith(Constants.ConnectionStringKeyPrefix) && !string.IsNullOrEmpty(valueGetter()))
{
return (false, new ServiceEndpoint(key, valueGetter()));
return (false, new ServiceEndpoint(key, valueGetter(), appName));
}

return (false, null);
Expand Down
3 changes: 3 additions & 0 deletions src/Microsoft.Azure.SignalR.Common/Constants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,15 @@ namespace Microsoft.Azure.SignalR
internal static class Constants
{
public const string ConnectionStringDefaultKey = "Azure:SignalR:ConnectionString";
public const string ApplicationNameDefaultKey = "Azure:SignalR:ApplicationName";

public static readonly string ConnectionStringSecondaryKey =
$"ConnectionStrings:{ConnectionStringDefaultKey}";

public static readonly string ConnectionStringKeyPrefix = $"{ConnectionStringDefaultKey}:";

public static readonly string ApplicationNameDefaultKeyPrefix = $"{ApplicationNameDefaultKey}:";

public static readonly string ConnectionStringSecondaryKeyPrefix = $"{ConnectionStringSecondaryKey}:";

// Default access token lifetime
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ namespace Microsoft.Azure.SignalR
internal interface IServiceEndpointOptions
{
ServiceEndpoint[] Endpoints { get; }
string ApplicationName { get; }
string ConnectionString { get; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,17 +28,19 @@ public class ServiceEndpoint

internal int? Port { get; }

internal string ApplicationName { get; set; }

internal IServiceConnectionContainer Connection { get; set; }

public ServiceEndpoint(string key, string connectionString) : this(connectionString)
public ServiceEndpoint(string key, string connectionString, string applicationName = "") : this(connectionString, applicationName: applicationName)
{
if (!string.IsNullOrEmpty(key))
{
(Name, EndpointType) = ParseKey(key);
}
}

public ServiceEndpoint(string connectionString, EndpointType type = EndpointType.Primary, string name = "")
public ServiceEndpoint(string connectionString, EndpointType type = EndpointType.Primary, string name = "", string applicationName = "")
{
// The provider is responsible to check if the connection string is empty and throw correct error message
if (!string.IsNullOrEmpty(connectionString))
Expand All @@ -49,6 +51,7 @@ public ServiceEndpoint(string connectionString, EndpointType type = EndpointType
EndpointType = type;
ConnectionString = connectionString;
Name = name;
ApplicationName = applicationName;
}

public override string ToString()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ private static IEnumerable<ServiceEndpoint> GetEndpoints(IServiceEndpointOptions
{
foreach (var endpoint in endpoints)
{
endpoint.ApplicationName = options.ApplicationName;
if (endpoint.ConnectionString == connectionString)
{
connectionStringIncluded = true;
Expand All @@ -89,7 +90,7 @@ private static IEnumerable<ServiceEndpoint> GetEndpoints(IServiceEndpointOptions

if (!string.IsNullOrEmpty(connectionString) && !connectionStringIncluded)
{
yield return new ServiceEndpoint(options.ConnectionString);
yield return new ServiceEndpoint(options.ConnectionString, applicationName: options.ApplicationName);
}
}

Expand Down
16 changes: 13 additions & 3 deletions src/Microsoft.Azure.SignalR.Management/RestApiProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,28 @@ internal class RestApiProvider
private readonly RestApiAccessTokenGenerator _restApiAccessTokenGenerator;
private readonly string _baseEndpoint;
private readonly string _hubName;
private readonly string _appName;
private readonly string _requestPrefix;
private readonly string _audiencePrefix;
private readonly int? _port;

public RestApiProvider(string connectionString, string hubName)
public RestApiProvider(string connectionString, string hubName, string appName)
{
string accessKey;
(_baseEndpoint, accessKey, _, _port) = ConnectionStringParser.Parse(connectionString);
_hubName = hubName;
_appName = appName.ToLower();
_restApiAccessTokenGenerator = new RestApiAccessTokenGenerator(accessKey);
_requestPrefix = _port == null ? $"{_baseEndpoint}/api/v1/hubs/{_hubName}" : $"{_baseEndpoint}:{_port}/api/v1/hubs/{_hubName}";
_audiencePrefix = $"{_baseEndpoint}/api/v1/hubs/{_hubName}";
if (string.IsNullOrEmpty(_appName))
{
_requestPrefix = _port == null ? $"{_baseEndpoint}/api/v1/hubs/{_hubName}" : $"{_baseEndpoint}:{_port}/api/v1/hubs/{_hubName}";
_audiencePrefix = $"{_baseEndpoint}/api/v1/hubs/{_hubName}";
}
else
{
_requestPrefix = _port == null ? $"{_baseEndpoint}/api/v1/hubs/{_appName}_{_hubName}" : $"{_baseEndpoint}:{_port}/api/v1/hubs/{_appName}_{_hubName}";
_audiencePrefix = $"{_baseEndpoint}/api/v1/hubs/{_appName}_{_hubName}";
}
}

public RestApiEndpoint GetBroadcastEndpoint(TimeSpan? lifetime = null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ internal class RestHubLifetimeManager : HubLifetimeManager<Hub>, IHubLifetimeMan

public RestHubLifetimeManager(ServiceManagerOptions serviceManagerOptions, string hubName)
{
_restApiProvider = new RestApiProvider(serviceManagerOptions.ConnectionString, hubName);
_restApiProvider = new RestApiProvider(serviceManagerOptions.ConnectionString, serviceManagerOptions.ApplicationName, hubName);
}

public override Task AddToGroupAsync(string connectionId, string groupName, CancellationToken cancellationToken = default)
Expand Down
2 changes: 1 addition & 1 deletion src/Microsoft.Azure.SignalR.Management/ServiceManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ internal class ServiceManager : IServiceManager
internal ServiceManager(ServiceManagerOptions serviceManagerOptions)
{
_serviceManagerOptions = serviceManagerOptions;
_endpoint = new ServiceEndpoint(_serviceManagerOptions.ConnectionString, EndpointType.Secondary);
_endpoint = new ServiceEndpoint(_serviceManagerOptions.ConnectionString, EndpointType.Secondary, applicationName: _serviceManagerOptions.ApplicationName);
_endpointProvider = new ServiceEndpointProvider(_endpoint);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ public class ServiceManagerOptions
/// </summary>
public string ConnectionString { get; set; } = null;

/// <summary>
/// Gets or sets the ApplicationName which will be prefixed to each hub name
/// </summary>
public string ApplicationName { get; set; }

internal void ValidateOptions()
{
ValidateConnectionString();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,22 @@ internal sealed class DefaultServiceEndpointGenerator : IServiceEndpointGenerato

public string Version { get; }

public string ApplicationName { get; }

public int? Port { get; }

public DefaultServiceEndpointGenerator(string endpoint, string accessKey, string version, int? port)
public DefaultServiceEndpointGenerator(string endpoint, string accessKey, string version, int? port, string applicationName)
{
Endpoint = endpoint;
AccessKey = accessKey;
Version = version;
Port = port;
ApplicationName = applicationName;
}

public string GetClientAudience(string hubName) =>
InternalGetAudience(ClientPath, hubName);
InternalGetAudience(ClientPath, hubName, ApplicationName);


public string GetClientEndpoint(string hubName, string originalPath, string queryString)
{
Expand All @@ -46,21 +50,27 @@ public string GetClientEndpoint(string hubName, string originalPath, string quer
queryBuilder.Append("&").Append(queryString);
}

return $"{InternalGetEndpoint(ClientPath, hubName)}{queryBuilder}";
return $"{InternalGetEndpoint(ClientPath, hubName, ApplicationName)}{queryBuilder}";
}

public string GetServerAudience(string hubName) =>
InternalGetAudience(ServerPath, hubName);
InternalGetAudience(ServerPath, hubName, ApplicationName);

public string GetServerEndpoint(string hubName) =>
InternalGetEndpoint(ServerPath, hubName);
InternalGetEndpoint(ServerPath, hubName, ApplicationName);

private string InternalGetEndpoint(string path, string hubName) =>
Port.HasValue ?
$"{Endpoint}:{Port}/{path}/?hub={hubName.ToLower()}" :
$"{Endpoint}/{path}/?hub={hubName.ToLower()}";
private string InternalGetEndpoint(string path, string hubName, string applicationName)
{
var prefixedHubName = string.IsNullOrEmpty(applicationName) ? hubName.ToLower() : $"{applicationName.ToLower()}_{hubName.ToLower()}";
return Port.HasValue ?
$"{Endpoint}:{Port}/{path}/?hub={prefixedHubName}" :
$"{Endpoint}/{path}/?hub={prefixedHubName}";
}

private string InternalGetAudience(string path, string hubName) =>
$"{Endpoint}/{path}/?hub={hubName.ToLower()}";
private string InternalGetAudience(string path, string hubName, string applicationName)
{
var prefixedHubName = string.IsNullOrEmpty(applicationName) ? hubName.ToLower() : $"{applicationName.ToLower()}_{hubName.ToLower()}";
return $"{Endpoint}/{path}/?hub={prefixedHubName}";
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public ServiceEndpointProvider(ServiceEndpoint endpoint, TimeSpan? ttl = null)
var port = endpoint.Port;
var version = endpoint.Version;

_generator = new DefaultServiceEndpointGenerator(_endpoint, _accessKey, version, port);
_generator = new DefaultServiceEndpointGenerator(_endpoint, _accessKey, version, port, endpoint.ApplicationName);
}

public string GenerateClientAccessToken(string hubName, IEnumerable<Claim> claims = null, TimeSpan? lifetime = null, string requestId = null)
Expand Down
5 changes: 5 additions & 0 deletions src/Microsoft.Azure.SignalR/ServiceOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ public class ServiceOptions : IServiceEndpointOptions
/// </summary>
public int ConnectionCount { get; set; } = 5;

/// <summary>
/// Gets or sets the prefix to apply to each hub name
/// </summary>
public string ApplicationName { get; set; }

/// <summary>
/// Gets or sets the func to generate claims from <see cref="HttpContext" />.
/// The claims will be included in the auto-generated token for clients.
Expand Down
32 changes: 27 additions & 5 deletions src/Microsoft.Azure.SignalR/ServiceOptionsSetup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,31 +10,53 @@ namespace Microsoft.Azure.SignalR
{
internal class ServiceOptionsSetup : IConfigureOptions<ServiceOptions>
{
private readonly string _appName;
private readonly string _connectionString;
private readonly ServiceEndpoint[] _endpoints;

public ServiceOptionsSetup(IConfiguration configuration)
{
var (connectionString, endpoints) = GetEndpoint(configuration, Constants.ConnectionStringDefaultKey, Constants.ConnectionStringKeyPrefix);
_appName = GetAppName(configuration);

var (connectionString, endpoints) = GetEndpoint(configuration, Constants.ConnectionStringDefaultKey, Constants.ConnectionStringKeyPrefix, _appName);

// Fallback to ConnectionStrings:Azure:SignalR:ConnectionString format when the default one is not available
if (endpoints.Count == 0)
{
(connectionString, endpoints) = GetEndpoint(configuration, Constants.ConnectionStringSecondaryKey, Constants.ConnectionStringSecondaryKeyPrefix);
(connectionString, endpoints) = GetEndpoint(configuration, Constants.ConnectionStringSecondaryKey, Constants.ConnectionStringSecondaryKeyPrefix, _appName);
}

_connectionString = connectionString;
_endpoints = endpoints.ToArray();
}

private string GetAppName(IConfiguration configuration)
{
foreach (var pair in configuration.AsEnumerable())
{
var key = pair.Key;
if (key == Constants.ApplicationNameDefaultKey && !string.IsNullOrEmpty(pair.Value))
{
return pair.Value;
}

if (key.StartsWith(Constants.ApplicationNameDefaultKeyPrefix) && !string.IsNullOrEmpty(pair.Value))
{
return pair.Value;
}
}
return string.Empty;
}

public void Configure(ServiceOptions options)
{
// The default setup of ServiceOptions
options.ConnectionString = _connectionString;
options.Endpoints = _endpoints;
options.ApplicationName = _appName;
}

private static (string, List<ServiceEndpoint>) GetEndpoint(IConfiguration configuration, string defaultKey, string keyPrefix)
private static (string, List<ServiceEndpoint>) GetEndpoint(IConfiguration configuration, string defaultKey, string keyPrefix, string applicationName)
{
var endpoints = new List<ServiceEndpoint>();
string connectionString = null;
Expand All @@ -44,12 +66,12 @@ private static (string, List<ServiceEndpoint>) GetEndpoint(IConfiguration config
if (key == defaultKey && !string.IsNullOrEmpty(pair.Value))
{
connectionString = pair.Value;
endpoints.Add(new ServiceEndpoint(pair.Value));
endpoints.Add(new ServiceEndpoint(pair.Value, applicationName: applicationName));
}

if (key.StartsWith(keyPrefix) && !string.IsNullOrEmpty(pair.Value))
{
endpoints.Add(new ServiceEndpoint(key, pair.Value));
endpoints.Add(new ServiceEndpoint(key, pair.Value, applicationName));
}
}

Expand Down
Loading