src/Microsoft.Azure.SignalR/ServiceOptionsSetup.cs (116 lines of code) (raw):
// 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.Linq;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Options;
using Microsoft.Extensions.Primitives;
namespace Microsoft.Azure.SignalR
{
internal class ServiceOptionsSetup : IConfigureOptions<ServiceOptions>, IOptionsChangeTokenSource<ServiceOptions>
{
private readonly IConfiguration _configuration;
private readonly string _connectionName;
public string Name => Options.DefaultName;
public ServiceOptionsSetup(IConfiguration configuration) : this(configuration, null)
{
}
public ServiceOptionsSetup(IConfiguration configuration, string connectionName)
{
_configuration = configuration;
_connectionName = connectionName;
}
public void Configure(ServiceOptions options)
{
var configuration = ParseConfiguration(_connectionName);
options.ConnectionString = configuration.ConnectionString;
options.Endpoints = configuration.Endpoints;
options.ApplicationName = configuration.AppName;
var configurableOption = configuration.configurableOptions;
if (configurableOption != null)
{
options.ServerStickyMode = GetConfiguredEnum(configurableOption.ServerStickyMode, options.ServerStickyMode);
options.InitialHubServerConnectionCount = configurableOption.InitialHubServerConnectionCount ?? options.InitialHubServerConnectionCount;
options.MaxHubServerConnectionCount = configurableOption.MaxHubServerConnectionCount ?? options.MaxHubServerConnectionCount;
options.AccessTokenAlgorithm = GetConfiguredEnum(configurableOption.AccessTokenAlgorithm, options.AccessTokenAlgorithm);
options.AccessTokenLifetime = GetConfiguredTimeSpanFromSeconds(configurableOption.AccessTokenLifetimeInSeconds, options.AccessTokenLifetime);
options.ServiceScaleTimeout = GetConfiguredTimeSpanFromSeconds(configurableOption.ServiceScaleTimeoutInSeconds, options.ServiceScaleTimeout);
options.MaxPollIntervalInSeconds = configurableOption.MaxPollIntervalInSeconds ?? options.MaxPollIntervalInSeconds;
if (configurableOption.GracefulShutdown != null)
{
var shutdownOptions = new GracefulShutdownOptions();
shutdownOptions.Mode = GetConfiguredEnum(configurableOption.GracefulShutdown.Mode, shutdownOptions.Mode);
shutdownOptions.Timeout = GetConfiguredTimeSpanFromSeconds(configurableOption.GracefulShutdown.TimeoutInSeconds, shutdownOptions.Timeout);
options.GracefulShutdown = shutdownOptions;
}
}
}
public IChangeToken GetChangeToken()
{
return _configuration.GetReloadToken();
}
private static TimeSpan GetConfiguredTimeSpanFromSeconds(int? seconds, TimeSpan defaultValue)
{
return seconds == null ? defaultValue : TimeSpan.FromSeconds(seconds.Value);
}
private static T GetConfiguredEnum<T>(string value, T defaultValue) where T : struct
{
if (Enum.TryParse<T>(value, true, out var result))
{
return result;
}
return defaultValue;
}
private (string AppName, string ConnectionString, ServiceEndpoint[] Endpoints, ConfigurableServiceOptions configurableOptions) ParseConfiguration(string connectionName)
{
var sectionKey = string.IsNullOrEmpty(connectionName) ? Constants.Keys.AzureSignalRSectionKey : $"{Constants.Keys.AzureSignalRSectionKey}:{connectionName}";
var options = _configuration.GetSection(sectionKey).Get<ConfigurableServiceOptions>();
var appName = GetApplicationName(sectionKey);
var connectionString = GetConnectionString(sectionKey, connectionName);
var endpoints = GetEndpoints(sectionKey);
return (appName, connectionString, endpoints, options);
}
private string GetApplicationName(string sectionKey)
{
// A known issue in previous version that the key ended with ":"
return _configuration[$"{sectionKey}:ApplicationName:"] ?? _configuration[$"{sectionKey}:ApplicationName"];
}
private string GetConnectionString(string sectionKey, string connectionName)
{
// ConnectionStrings_connectionName takes the highest priority
if (!string.IsNullOrEmpty(connectionName) && _configuration.GetConnectionString(connectionName) is string connectionString)
{
return connectionString;
}
var connectionStringKey = $"{sectionKey}:ConnectionString";
// Fallback to ConnectionStrings:Azure:SignalR:ConnectionString format when the default one is not available
return _configuration[connectionStringKey] ?? _configuration.GetConnectionString(connectionStringKey) ;
}
private ServiceEndpoint[] GetEndpoints(string sectionKey)
{
var endpointKey = $"{sectionKey}:ConnectionString";
var endpoints = _configuration.GetEndpoints(endpointKey).ToArray();
if (endpoints.Length == 0)
{
endpoints = _configuration.GetEndpoints($"ConnectionStrings:{endpointKey}").ToArray();
}
return endpoints;
}
private record class ConfigurableGracefulShutdownOptions(
string Mode,
int? TimeoutInSeconds
)
{
public ConfigurableGracefulShutdownOptions() : this(null, null) { }
}
private record class ConfigurableServiceOptions(
string ServerStickyMode,
int? InitialHubServerConnectionCount,
int? MaxHubServerConnectionCount,
string AccessTokenAlgorithm,
int? AccessTokenLifetimeInSeconds,
int? ServiceScaleTimeoutInSeconds,
int? MaxPollIntervalInSeconds,
ConfigurableGracefulShutdownOptions GracefulShutdown
)
{
public ConfigurableServiceOptions() : this(null, null, null, null, null, null, null, null) { }
}
}
}