Skip to content

Commit

Permalink
Merge branch 'dev' into graceful-shutdown
Browse files Browse the repository at this point in the history
  • Loading branch information
terencefan authored Nov 17, 2020
2 parents 406b874 + ab81341 commit 07910e0
Show file tree
Hide file tree
Showing 6 changed files with 138 additions and 96 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using Microsoft.Extensions.Configuration;

namespace Microsoft.Azure.SignalR
{
internal static class IConfigurationExtension
{
/// <summary>
/// Gets SignalR service endpoints configured in a section.
/// </summary>
/// <remarks>
/// The SignalR service endpoint whose key is exactly the section name is not extracted. Only children of the section are extracted.
/// </remarks>
public static ServiceEndpoint[] GetSignalRServiceEndpoints(this IConfiguration configuration, string sectionName)
{
var section = configuration.GetSection(sectionName);
return GetEndpoints(section).ToArray();
}

private static IEnumerable<ServiceEndpoint> GetEndpoints(IConfiguration section)
{
foreach (var entry in section.AsEnumerable(true))
{
if (!string.IsNullOrEmpty(entry.Value))
{
yield return new ServiceEndpoint(entry.Key, entry.Value);
}
}
}
}
}
41 changes: 9 additions & 32 deletions src/Microsoft.Azure.SignalR.Common/Endpoints/ServiceEndpoint.cs
Original file line number Diff line number Diff line change
Expand Up @@ -61,10 +61,7 @@ internal ServiceEndpoint(string endpoint, AuthOptions authOptions, int port = 44

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

public ServiceEndpoint(string connectionString, EndpointType type = EndpointType.Primary, string name = "")
Expand Down Expand Up @@ -140,44 +137,24 @@ public override bool Equals(object obj)

internal static (string, EndpointType) ParseKey(string key)
{
if (key == Constants.Keys.ConnectionStringDefaultKey || key == Constants.Keys.ConnectionStringSecondaryKey)
if (string.IsNullOrEmpty(key))
{
return (string.Empty, EndpointType.Primary);
}

if (key.StartsWith(Constants.Keys.ConnectionStringKeyPrefix))
{
// Azure:SignalR:ConnectionString:<name>:<type>
return ParseKeyWithPrefix(key, Constants.Keys.ConnectionStringKeyPrefix);
}

if (key.StartsWith(Constants.Keys.ConnectionStringSecondaryKey))
{
return ParseKeyWithPrefix(key, Constants.Keys.ConnectionStringSecondaryKey);
}

throw new ArgumentException($"Invalid format: {key}", nameof(key));
}

private static (string, EndpointType) ParseKeyWithPrefix(string key, string prefix)
{
var status = key.Substring(prefix.Length);
var parts = status.Split(':');
var parts = key.Split(':');
if (parts.Length == 1)
{
return (parts[0], EndpointType.Primary);
}
else if (Enum.TryParse<EndpointType>(parts[1], true, out var endpointStatus))
{
return (parts[0], endpointStatus);
}
else
{
if (Enum.TryParse<EndpointType>(parts[1], true, out var endpointStatus))
{
return (parts[0], endpointStatus);
}
else
{
return (status, EndpointType.Primary);
}
return (key, EndpointType.Primary);
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System.Runtime.CompilerServices;

[assembly: InternalsVisibleTo("Microsoft.Azure.SignalR, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
[assembly: InternalsVisibleTo("Microsoft.Azure.WebJobs.Extensions.SignalRService, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
[assembly: InternalsVisibleTo("Microsoft.Azure.SignalR.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
[assembly: InternalsVisibleTo("Microsoft.Azure.SignalR.Common.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
[assembly: InternalsVisibleTo("Microsoft.Azure.SignalR.AspNet, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
Expand Down
37 changes: 7 additions & 30 deletions src/Microsoft.Azure.SignalR/ServiceOptionsSetup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Options;
using Microsoft.Extensions.Primitives;
Expand Down Expand Up @@ -46,38 +44,17 @@ public IChangeToken GetChangeToken()
Enum.TryParse(mode, true, out stickyMode);
}

var (connectionString, endpoints) = GetEndpoint(_configuration, Constants.Keys.ConnectionStringDefaultKey);

// Fallback to ConnectionStrings:Azure:SignalR:ConnectionString format when the default one is not available
if (connectionString == null && endpoints.Length == 0)
{
(connectionString, endpoints) = GetEndpoint(_configuration, Constants.Keys.ConnectionStringSecondaryKey);
}

return (appName, connectionString, stickyMode, endpoints);
}
var connectionString = _configuration[Constants.Keys.ConnectionStringDefaultKey] ?? _configuration[Constants.Keys.ConnectionStringSecondaryKey];

private static (string, ServiceEndpoint[]) GetEndpoint(IConfiguration configuration, string key)
{
var section = configuration.GetSection(key);
var connectionString = section.Value;
var endpoints = GetEndpoints(section.GetChildren()).ToArray();

return (connectionString, endpoints);
}
var endpoints = _configuration.GetSignalRServiceEndpoints(Constants.Keys.ConnectionStringDefaultKey);

private static IEnumerable<ServiceEndpoint> GetEndpoints(IEnumerable<IConfigurationSection> sections)
{
foreach (var section in sections)
if (endpoints.Length == 0)
{
foreach (var entry in section.AsEnumerable())
{
if (!string.IsNullOrEmpty(entry.Value))
{
yield return new ServiceEndpoint(entry.Key, entry.Value);
}
}
endpoints = _configuration.GetSignalRServiceEndpoints(Constants.Keys.ConnectionStringSecondaryKey);
}

return (appName, connectionString, stickyMode, endpoints);
}
}
}
}
56 changes: 22 additions & 34 deletions test/Microsoft.Azure.SignalR.Common.Tests/ServiceEndpointFacts.cs
Original file line number Diff line number Diff line change
@@ -1,8 +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 Microsoft.AspNetCore.Http;
using System;
using System.Collections.Generic;
using Xunit;

Expand All @@ -11,13 +9,11 @@ namespace Microsoft.Azure.SignalR.Common.Tests
public class ServiceEndpointFacts
{
[Theory]
[InlineData("Azure:SignalR:ConnectionString", "", EndpointType.Primary)]
[InlineData("Azure:SignalR:ConnectionString:a", "a", EndpointType.Primary)]
[InlineData("Azure:SignalR:ConnectionString:a:primary", "a", EndpointType.Primary)]
[InlineData("Azure:SignalR:ConnectionString:a:Primary", "a", EndpointType.Primary)]
[InlineData("Azure:SignalR:ConnectionString:secondary", "secondary", EndpointType.Primary)]
[InlineData("Azure:SignalR:ConnectionString:a:secondary", "a", EndpointType.Secondary)]
[InlineData("Azure:SignalR:ConnectionString::secondary", "", EndpointType.Secondary)]
[InlineData("a", "a", EndpointType.Primary)]
[InlineData("a:primary", "a", EndpointType.Primary)]
[InlineData("secondary", "secondary", EndpointType.Primary)]
[InlineData("a:secondary", "a", EndpointType.Secondary)]
[InlineData(":secondary", "", EndpointType.Secondary)]
internal void TestParseKey(string key, string expectedName, EndpointType expectedType)
{
var (name, type) = ServiceEndpoint.ParseKey(key);
Expand All @@ -26,14 +22,6 @@ internal void TestParseKey(string key, string expectedName, EndpointType expecte
Assert.Equal(expectedType, type);
}

[Theory]
[InlineData("a")]
[InlineData("")]
internal void TestParseInvalidKey(string key)
{
Assert.Throws<ArgumentException>(() => ServiceEndpoint.ParseKey(key));
}

[Theory]
[MemberData(nameof(TestEndpointsEqualityInput))]
internal void TestEndpointsEquality(ServiceEndpoint first, ServiceEndpoint second, bool equal)
Expand All @@ -46,32 +34,32 @@ internal void TestEndpointsEquality(ServiceEndpoint first, ServiceEndpoint secon
{
new object[]
{
new ServiceEndpoint("Azure:SignalR:ConnectionString:a", "Endpoint=http://localhost;AccessKey=ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789;Port=8080;Version=1.0"),
new ServiceEndpoint("Azure:SignalR:ConnectionString", "Endpoint=http://localhost;AccessKey=ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789;Port=8080;Version=1.0"),
new ServiceEndpoint("a", "Endpoint=http://localhost;AccessKey=ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789;Port=8080;Version=1.0"),
new ServiceEndpoint("Endpoint=http://localhost;AccessKey=ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789;Port=8080;Version=1.0"),
false,
},
new object[]
{
new ServiceEndpoint("Azure:SignalR:ConnectionString:a", "Endpoint=http://localhost;AccessKey=ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789;Port=8080;Version=1.0"),
new ServiceEndpoint("Azure:SignalR:ConnectionString::primary", "Endpoint=http://localhost;AccessKey=ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789;Port=8080"),
new ServiceEndpoint("a", "Endpoint=http://localhost;AccessKey=ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789;Port=8080;Version=1.0"),
new ServiceEndpoint(":primary", "Endpoint=http://localhost;AccessKey=ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789;Port=8080"),
false,
},
new object[]
{
new ServiceEndpoint("Azure:SignalR:ConnectionString:a", "Endpoint=http://localhost;AccessKey=ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789;Port=8080;Version=1.0"),
new ServiceEndpoint("Azure:SignalR:ConnectionString:a:secondary", "Endpoint=http://localhost;AccessKey=ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"),
new ServiceEndpoint("a", "Endpoint=http://localhost;AccessKey=ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789;Port=8080;Version=1.0"),
new ServiceEndpoint("a:secondary", "Endpoint=http://localhost;AccessKey=ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"),
false,
},
new object[]
{
new ServiceEndpoint("Azure:SignalR:ConnectionString:a", "Endpoint=http://localhost;AccessKey=ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789;Port=8080;Version=1.0"),
new ServiceEndpoint("Azure:SignalR:ConnectionString:b", "Endpoint=http://localhost;AccessKey=ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456780"),
new ServiceEndpoint("a", "Endpoint=http://localhost;AccessKey=ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789;Port=8080;Version=1.0"),
new ServiceEndpoint("b", "Endpoint=http://localhost;AccessKey=ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456780"),
false,
},
new object[]
{
new ServiceEndpoint("Endpoint=http://localhost;AccessKey=ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789;Port=8080;Version=1.0"),
new ServiceEndpoint("Azure:SignalR:ConnectionString::secondary", "Endpoint=http://localhost;AccessKey=ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456780"),
new ServiceEndpoint(":secondary", "Endpoint=http://localhost;AccessKey=ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456780"),
false,
},
new object[]
Expand All @@ -82,28 +70,28 @@ internal void TestEndpointsEquality(ServiceEndpoint first, ServiceEndpoint secon
},
new object[]
{
new ServiceEndpoint("Azure:SignalR:ConnectionString", "Endpoint=http://localhost;AccessKey=ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789;Port=8080;Version=1.0"),
new ServiceEndpoint("Azure:SignalR:ConnectionString", "Endpoint=https://localhost;AccessKey=ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456780"),
new ServiceEndpoint("Endpoint=http://localhost;AccessKey=ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789;Port=8080;Version=1.0"),
new ServiceEndpoint("Endpoint=https://localhost;AccessKey=ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456780"),
false,
},
new object[]
{
new ServiceEndpoint("Azure:SignalR:ConnectionString", "Endpoint=http://localhost;AccessKey=ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789;Port=8080;Version=1.0"),
new ServiceEndpoint("Azure:SignalR:ConnectionString", "Endpoint=http://localhost;AccessKey=OPQRSTUVWXYZ0123456780ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456780"),
new ServiceEndpoint("Endpoint=http://localhost;AccessKey=ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789;Port=8080;Version=1.0"),
new ServiceEndpoint("Endpoint=http://localhost;AccessKey=OPQRSTUVWXYZ0123456780ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456780"),
true,
},
new object[]
{
new ServiceEndpoint("Azure:SignalR:ConnectionString", "Endpoint=http://localhost;AccessKey=ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789;Port=8080;Version=1.0"),
new ServiceEndpoint("Azure:SignalR:ConnectionString::primary", "Endpoint=http://localhost;AccessKey=OPQRSTUVWXYZ0123456780ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456780"),
new ServiceEndpoint("Endpoint=http://localhost;AccessKey=ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789;Port=8080;Version=1.0"),
new ServiceEndpoint(":primary", "Endpoint=http://localhost;AccessKey=OPQRSTUVWXYZ0123456780ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456780"),
true,
},
new object[]
{
new ServiceEndpoint("Azure:SignalR:ConnectionString::secondary", "Endpoint=http://localhost;AccessKey=ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789;Port=8080;Version=1.0"),
new ServiceEndpoint(":secondary", "Endpoint=http://localhost;AccessKey=ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789;Port=8080;Version=1.0"),
new ServiceEndpoint("Endpoint=http://localhost;AccessKey=OPQRSTUVWXYZ0123456780ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456780", EndpointType.Secondary),
true,
}
};
}
}
}
63 changes: 63 additions & 0 deletions test/Microsoft.Azure.SignalR.Tests/ServiceOptionsSetupFacts.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System.Collections.Generic;
using System.Linq;
using Microsoft.Extensions.Configuration;
using Xunit;

namespace Microsoft.Azure.SignalR.Tests
{
public class ServiceOptionsSetupFacts
{
public const string FakeConnectionString = "Endpoint=http://fake;AccessKey=ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789;Port=8080;Version=1.0";

private static readonly string[] ConnectionStringKeyPrefixs = new string[] { Constants.Keys.ConnectionStringKeyPrefix, Constants.Keys.ConnectionStringSecondaryKeyPrefix };

private static readonly Dictionary<string, (string, EndpointType)> EndpointDict = new Dictionary<string, (string, EndpointType)>
{
{"a",("a",EndpointType.Primary) },
{"secondary",("secondary",EndpointType.Primary) },
{"a:secondary",("a",EndpointType.Secondary) },
{":secondary",(string.Empty,EndpointType.Secondary) }
};

public static IEnumerable<object[]> ParseServiceEndpointData = from section in ConnectionStringKeyPrefixs
from tuple in EndpointDict
select new object[] { section + tuple.Key, tuple.Value.Item1, tuple.Value.Item2 };

[Theory]
[MemberData(nameof(ParseServiceEndpointData))]
public void ParseServiceEndpointTest(string key, string endpointName, EndpointType type)
{
IConfiguration configuration = new ConfigurationBuilder().AddInMemoryCollection().Build();
configuration[key] = FakeConnectionString;
var setup = new ServiceOptionsSetup(configuration);
var options = new ServiceOptions();
setup.Configure(options);

var resultEndpoint = options.Endpoints.Single();
Assert.Equal(endpointName, resultEndpoint.Name);
Assert.Equal(type, resultEndpoint.EndpointType);
}

[Fact]
public void ParseMultipleEndpointsTest()
{
IConfiguration configuration = new ConfigurationBuilder().AddInMemoryCollection().Build();
var defaultConnectionString = "Endpoint=http://default;AccessKey=ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789;Port=8080;Version=1.0";
configuration[$"{Constants.Keys.ConnectionStringDefaultKey}"] = defaultConnectionString;
foreach (var key in EndpointDict.Keys)
{
configuration[ConfigurationPath.Combine(Constants.Keys.ConnectionStringDefaultKey, key)] = FakeConnectionString;
}

var setup = new ServiceOptionsSetup(configuration);
var options = new ServiceOptions();
setup.Configure(options);

Assert.Equal(defaultConnectionString, options.ConnectionString);
Assert.Equal(EndpointDict.Count, options.Endpoints.Length);
}
}
}

0 comments on commit 07910e0

Please sign in to comment.