From b2670fe6cdfcadb84667f6feb2a999b65f89dfdd Mon Sep 17 00:00:00 2001 From: yzt Date: Mon, 7 Nov 2022 13:04:52 +0800 Subject: [PATCH] Fix proxy not applied to serverless transient mode (#1708) Fix https://github.com/Azure/azure-signalr/issues/1700 --- .../DependencyInjectionExtensions.cs | 3 +- .../DependencyInjectionExtensionFacts.cs | 46 +++++++++++++++++-- ...soft.Azure.SignalR.Management.Tests.csproj | 3 +- .../RestHealthCheckServiceFacts.cs | 6 +-- .../Serialization/SerailizerFacts.cs | 3 +- .../StronglyTypedServiceHubContextFacts.cs | 18 ++++---- 6 files changed, 60 insertions(+), 19 deletions(-) diff --git a/src/Microsoft.Azure.SignalR.Management/DependencyInjectionExtensions.cs b/src/Microsoft.Azure.SignalR.Management/DependencyInjectionExtensions.cs index e07c3bfc1..800e03dfd 100644 --- a/src/Microsoft.Azure.SignalR.Management/DependencyInjectionExtensions.cs +++ b/src/Microsoft.Azure.SignalR.Management/DependencyInjectionExtensions.cs @@ -123,7 +123,8 @@ private static IServiceCollection TrySetProductInfo(this IServiceCollection serv } private static IServiceCollection AddRestClientFactory(this IServiceCollection services) => services - .AddHttpClient() + .AddHttpClient(Options.DefaultName) + .ConfigurePrimaryHttpMessageHandler(sp => new HttpClientHandler() { Proxy = sp.GetRequiredService>().Value.Proxy }).Services .AddSingleton(sp => { var options = sp.GetRequiredService>().Value; diff --git a/test/Microsoft.Azure.SignalR.Management.Tests/DependencyInjectionExtensionFacts.cs b/test/Microsoft.Azure.SignalR.Management.Tests/DependencyInjectionExtensionFacts.cs index 6eaca3a4b..e226f6b0e 100644 --- a/test/Microsoft.Azure.SignalR.Management.Tests/DependencyInjectionExtensionFacts.cs +++ b/test/Microsoft.Azure.SignalR.Management.Tests/DependencyInjectionExtensionFacts.cs @@ -2,13 +2,20 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System; +using System.Collections.Generic; using System.IO; using System.Linq; +using System.Net; using System.Reflection; using System.Threading.Tasks; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting.Server; +using Microsoft.AspNetCore.Hosting.Server.Features; +using Microsoft.AspNetCore.Http; using Microsoft.Azure.SignalR.Tests.Common; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Newtonsoft.Json; using Xunit; @@ -33,7 +40,7 @@ public DependencyInjectionExtensionFacts(ITestOutputHelper outputHelper) public async Task FileConfigHotReloadTest() { // to avoid possible file name conflict with another FileConfigHotReloadTest - string configPath = nameof(DependencyInjectionExtensionFacts); + var configPath = nameof(DependencyInjectionExtensionFacts); var originUrl = "http://origin.url"; var newUrl = "http://new.url"; var configObj = new @@ -47,7 +54,7 @@ public async Task FileConfigHotReloadTest() } }; File.WriteAllText(configPath, JsonConvert.SerializeObject(configObj)); - ServiceCollection services = new ServiceCollection(); + var services = new ServiceCollection(); services.AddSignalRServiceManager(); services.AddSingleton(new ConfigurationBuilder().AddJsonFile(configPath, false, true).Build()); using var provider = services.BuildServiceProvider(); @@ -170,7 +177,7 @@ public void ConnectionStringNull_TransientMode_Throw() public async Task MultiServiceEndpoints_NotAppliedToTransientModeAsync() { // to avoid possible file name conflict with another FileConfigHotReloadTest - string configPath = nameof(MultiServiceEndpoints_NotAppliedToTransientModeAsync); + var configPath = nameof(MultiServiceEndpoints_NotAppliedToTransientModeAsync); var connStr = FakeEndpointUtils.GetFakeConnectionString(1).Single(); var configObj = new { @@ -208,5 +215,38 @@ public async Task MultiServiceEndpoints_NotAppliedToTransientModeAsync() await Task.Delay(5000); Assert.Equal(connStr, optionsMonitor.CurrentValue.ConnectionString);// as new config don't pass validation, it is not reloaded } + + [Fact] + public async Task ProxyApplyToTransientModeTestAsync() + { + var requestUrls = new Queue(); + + //create a simple proxy server + var appBuilder = WebApplication.CreateBuilder(); + appBuilder.Services.AddLogging(b => b.AddXunit(_outputHelper)); + using var app = appBuilder.Build(); + //randomly choose a free port, listen to all interfaces + app.Urls.Add("http://[::1]:0"); + app.Run(async context => + { + requestUrls.Enqueue(context.Request.Path); + await context.Response.WriteAsync(""); + }); + await app.StartAsync(); + + var serviceManager = new ServiceManagerBuilder().WithOptions(o => + { + // use http schema to avoid SSL handshake + o.ConnectionString = "Endpoint=http://abc;AccessKey=nOu3jXsHnsO5urMumc87M9skQbUWuQ+PE5IvSUEic8w=;Version=1.0;"; + o.Proxy = new WebProxy(app.Services.GetRequiredService().Features.Get().Addresses.First()); + }).BuildServiceManager(); + Assert.True(await serviceManager.IsServiceHealthy(default)); + Assert.Equal("/api/v1/health", requestUrls.Dequeue()); + + using var hubContext = await serviceManager.CreateHubContextAsync("hub", default); + Assert.True(await hubContext.ClientManager.UserExistsAsync("userId")); + Assert.Equal("/api/hubs/hub/users/userId", requestUrls.Dequeue()); + await app.StopAsync(); + } } } \ No newline at end of file diff --git a/test/Microsoft.Azure.SignalR.Management.Tests/Microsoft.Azure.SignalR.Management.Tests.csproj b/test/Microsoft.Azure.SignalR.Management.Tests/Microsoft.Azure.SignalR.Management.Tests.csproj index 5e70342ab..2a8a7c83f 100644 --- a/test/Microsoft.Azure.SignalR.Management.Tests/Microsoft.Azure.SignalR.Management.Tests.csproj +++ b/test/Microsoft.Azure.SignalR.Management.Tests/Microsoft.Azure.SignalR.Management.Tests.csproj @@ -1,8 +1,7 @@  - net5.0 - + net6.0 false diff --git a/test/Microsoft.Azure.SignalR.Management.Tests/RestHealthCheckServiceFacts.cs b/test/Microsoft.Azure.SignalR.Management.Tests/RestHealthCheckServiceFacts.cs index be5bd8aed..d4d03a016 100644 --- a/test/Microsoft.Azure.SignalR.Management.Tests/RestHealthCheckServiceFacts.cs +++ b/test/Microsoft.Azure.SignalR.Management.Tests/RestHealthCheckServiceFacts.cs @@ -64,13 +64,13 @@ public async Task TestRestHealthCheckServiceWithEndpointFromHealthyToUnhealthy() .ReturnsAsync(new HttpResponseMessage(HttpStatusCode.BadGateway)) .ReturnsAsync(new HttpResponseMessage(HttpStatusCode.BadGateway)) .ReturnsAsync(new HttpResponseMessage(HttpStatusCode.BadGateway)); - + var checkInterval = TimeSpan.FromSeconds(3); var retryInterval = TimeSpan.FromSeconds(0.5); using var _ = StartLog(out var loggerFactory); var services = new ServiceCollection() - .AddHttpClient(Options.DefaultName).ConfigurePrimaryHttpMessageHandler(() => handlerMock.Object).Services .AddSignalRServiceManager() + .AddHttpClient(Options.DefaultName).ConfigurePrimaryHttpMessageHandler(() => handlerMock.Object).Services .Configure(o => { o.CheckInterval = checkInterval; @@ -84,7 +84,7 @@ public async Task TestRestHealthCheckServiceWithEndpointFromHealthyToUnhealthy() .CreateHubContextAsync(HubName, default); var endpoint = (serviceHubContext as ServiceHubContextImpl).ServiceProvider.GetRequiredService().GetEndpoints(HubName).First(); - + //The first health check is OK Assert.True(endpoint.Online); diff --git a/test/Microsoft.Azure.SignalR.Management.Tests/Serialization/SerailizerFacts.cs b/test/Microsoft.Azure.SignalR.Management.Tests/Serialization/SerailizerFacts.cs index d53958c81..417c8f03b 100644 --- a/test/Microsoft.Azure.SignalR.Management.Tests/Serialization/SerailizerFacts.cs +++ b/test/Microsoft.Azure.SignalR.Management.Tests/Serialization/SerailizerFacts.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Linq; using System.Text; +using System.Text.Json.Serialization; using System.Threading.Tasks; using Azure.Core.Serialization; using Microsoft.AspNetCore.SignalR; @@ -26,7 +27,7 @@ public static IEnumerable IgnoreNullObjectSerializers { get { - yield return new object[] { new JsonObjectSerializer(new() { IgnoreNullValues = true }) }; + yield return new object[] { new JsonObjectSerializer(new() { DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull }) }; yield return new object[] { new NewtonsoftJsonObjectSerializer(new() { NullValueHandling = NullValueHandling.Ignore }) }; } } diff --git a/test/Microsoft.Azure.SignalR.Management.Tests/StronglyTypedServiceHubContextFacts.cs b/test/Microsoft.Azure.SignalR.Management.Tests/StronglyTypedServiceHubContextFacts.cs index effb4346b..47123b0bf 100644 --- a/test/Microsoft.Azure.SignalR.Management.Tests/StronglyTypedServiceHubContextFacts.cs +++ b/test/Microsoft.Azure.SignalR.Management.Tests/StronglyTypedServiceHubContextFacts.cs @@ -114,9 +114,9 @@ void assertion(HttpRequestMessage request, CancellationToken t) { Assert.EndsWith($"/groups/{groupName}/connections/{connectionId}?api-version=2022-06-01", request.RequestUri.AbsoluteUri); } - var services = new ServiceCollection().AddHttpClient(Options.DefaultName) - .ConfigurePrimaryHttpMessageHandler(() => new TestRootHandler(assertion)).Services - .AddSignalRServiceManager(); + var services = new ServiceCollection() + .AddSignalRServiceManager() + .AddHttpClient(Options.DefaultName).ConfigurePrimaryHttpMessageHandler(() => new TestRootHandler(assertion)).Services; await using var hubContext = await Create(ServiceTransportType.Transient, services); await hubContext.Groups.AddToGroupAsync(connectionId, groupName); @@ -156,9 +156,9 @@ void assertion(HttpRequestMessage request, CancellationToken t) { Assert.EndsWith($"/users/{userId}/groups/{groupName}?api-version=2022-06-01", request.RequestUri.AbsoluteUri); } - var services = new ServiceCollection().AddHttpClient(Options.DefaultName) - .ConfigurePrimaryHttpMessageHandler(() => new TestRootHandler(assertion)).Services - .AddSignalRServiceManager(); + var services = new ServiceCollection() + .AddSignalRServiceManager() + .AddHttpClient(Options.DefaultName).ConfigurePrimaryHttpMessageHandler(() => new TestRootHandler(assertion)).Services; await using var hubContext = await Create(ServiceTransportType.Transient, services); await hubContext.UserGroups.AddToGroupAsync(userId, groupName); @@ -198,9 +198,9 @@ void assertion(HttpRequestMessage request, CancellationToken t) Assert.EndsWith($"/connections/{connectionId}?api-version=2022-06-01", request.RequestUri.AbsoluteUri); Assert.Equal(HttpMethod.Delete, request.Method); } - var services = new ServiceCollection().AddHttpClient(Options.DefaultName) - .ConfigurePrimaryHttpMessageHandler(() => new TestRootHandler(assertion)).Services - .AddSignalRServiceManager(); + var services = new ServiceCollection() + .AddSignalRServiceManager() + .AddHttpClient(Options.DefaultName).ConfigurePrimaryHttpMessageHandler(() => new TestRootHandler(assertion)).Services; await using var hubContext = await Create(ServiceTransportType.Transient, services); await hubContext.ClientManager.CloseConnectionAsync(connectionId);