public async Task DisconnectEndpoint_NoCallsMade_SubchannelStateUpdated() { // Ignore errors SetExpectedErrorsFilter(writeContext => { return(true); }); string?host = null; Task <HelloReply> UnaryMethod(HelloRequest request, ServerCallContext context) { host = context.Host; return(Task.FromResult(new HelloReply { Message = request.Name })); } // Arrange using var endpoint = BalancerHelpers.CreateGrpcEndpoint <HelloRequest, HelloReply>(50051, UnaryMethod, nameof(UnaryMethod)); var channel = await BalancerHelpers.CreateChannel(LoggerFactory, new RoundRobinConfig(), new[] { endpoint.Address }); await channel.ConnectAsync().DefaultTimeout(); var subchannel = await BalancerHelpers.WaitForSubchannelToBeReadyAsync(Logger, channel).DefaultTimeout(); // Act endpoint.Dispose(); // Assert await TestHelpers.AssertIsTrueRetryAsync( () => subchannel.State == ConnectivityState.TransientFailure, "Wait for subchannel to fail.").DefaultTimeout(); }
public async Task Resolver_SubchannelTransientFailure_ResolverRefreshed() { // Ignore errors SetExpectedErrorsFilter(writeContext => { return(true); }); string?host = null; Task <HelloReply> UnaryMethod(HelloRequest request, ServerCallContext context) { host = context.Host; return(Task.FromResult(new HelloReply { Message = request.Name })); } // Arrange using var endpoint1 = BalancerHelpers.CreateGrpcEndpoint <HelloRequest, HelloReply>(50051, UnaryMethod, nameof(UnaryMethod)); using var endpoint2 = BalancerHelpers.CreateGrpcEndpoint <HelloRequest, HelloReply>(50052, UnaryMethod, nameof(UnaryMethod)); SyncPoint?syncPoint = new SyncPoint(runContinuationsAsynchronously: true); syncPoint.Continue(); var resolver = new TestResolver(async() => { await syncPoint.WaitToContinue().DefaultTimeout(); syncPoint = new SyncPoint(runContinuationsAsynchronously: true); }); resolver.UpdateAddresses(new List <BalancerAddress> { new BalancerAddress(endpoint1.Address.Host, endpoint1.Address.Port), new BalancerAddress(endpoint2.Address.Host, endpoint2.Address.Port) }); var channel = await BalancerHelpers.CreateChannel(LoggerFactory, new RoundRobinConfig(), resolver, connect : true); await BalancerHelpers.WaitForSubChannelsToBeReadyAsync(Logger, channel, 2).DefaultTimeout(); var client = TestClientFactory.Create(channel, endpoint1.Method); var waitForRefreshTask = syncPoint.WaitForSyncPoint(); endpoint1.Dispose(); await waitForRefreshTask.DefaultTimeout(); resolver.UpdateAddresses(new List <BalancerAddress> { new BalancerAddress(endpoint2.Address.Host, endpoint2.Address.Port) }); syncPoint.Continue(); await BalancerHelpers.WaitForSubChannelsToBeReadyAsync(Logger, channel, 1).DefaultTimeout(); }
public async Task UnaryCall_MultipleCalls_RoundRobin() { // Ignore errors SetExpectedErrorsFilter(writeContext => { return(true); }); string?host = null; Task <HelloReply> UnaryMethod(HelloRequest request, ServerCallContext context) { host = context.Host; return(Task.FromResult(new HelloReply { Message = request.Name })); } // Arrange using var endpoint1 = BalancerHelpers.CreateGrpcEndpoint <HelloRequest, HelloReply>(50051, UnaryMethod, nameof(UnaryMethod)); using var endpoint2 = BalancerHelpers.CreateGrpcEndpoint <HelloRequest, HelloReply>(50052, UnaryMethod, nameof(UnaryMethod)); var channel = await BalancerHelpers.CreateChannel(LoggerFactory, new RoundRobinConfig(), new[] { endpoint1.Address, endpoint2.Address }, connect : true); await BalancerHelpers.WaitForSubchannelsToBeReadyAsync(Logger, channel, 2).DefaultTimeout(); var client = TestClientFactory.Create(channel, endpoint1.Method); // Act var reply = await client.UnaryCall(new HelloRequest { Name = "Balancer" }).ResponseAsync.DefaultTimeout(); // Assert Assert.AreEqual("Balancer", reply.Message); var nextHost = GetNextHost(host !); // Act reply = await client.UnaryCall(new HelloRequest { Name = "Balancer" }).ResponseAsync.DefaultTimeout(); // Assert Assert.AreEqual("Balancer", reply.Message); Assert.AreEqual(nextHost, host !); nextHost = GetNextHost(host !); // Act reply = await client.UnaryCall(new HelloRequest { Name = "Balancer" }).ResponseAsync.DefaultTimeout(); // Assert Assert.AreEqual("Balancer", reply.Message); Assert.AreEqual(nextHost, host); string GetNextHost(string host) { return(host == "127.0.0.1:50051" ? "127.0.0.1:50052" : "127.0.0.1:50051"); } }
public async Task Client_CallCredentials_WithLoadBalancing_RoundtripToken() { // Arrange string?authorization = null; Task <HelloReply> UnaryMethod(HelloRequest request, ServerCallContext context) { authorization = context.RequestHeaders.GetValue("authorization"); return(Task.FromResult(new HelloReply { Message = request.Name })); } var credentials = CallCredentials.FromInterceptor((context, metadata) => { metadata.Add("Authorization", $"Bearer TEST"); return(Task.CompletedTask); }); using var endpoint1 = BalancerHelpers.CreateGrpcEndpoint <HelloRequest, HelloReply>(50051, UnaryMethod, nameof(UnaryMethod), HttpProtocols.Http1AndHttp2, isHttps: true); using var endpoint2 = BalancerHelpers.CreateGrpcEndpoint <HelloRequest, HelloReply>(50052, UnaryMethod, nameof(UnaryMethod), HttpProtocols.Http1AndHttp2, isHttps: true); var services = new ServiceCollection(); services.AddSingleton <ResolverFactory>(new StaticResolverFactory(_ => new[] { new BalancerAddress(endpoint1.Address.Host, endpoint1.Address.Port), new BalancerAddress(endpoint2.Address.Host, endpoint2.Address.Port) })); var socketsHttpHandler = new SocketsHttpHandler { EnableMultipleHttp2Connections = true, SslOptions = new System.Net.Security.SslClientAuthenticationOptions { EnabledSslProtocols = SslProtocols.Tls | SslProtocols.Tls11 | SslProtocols.Tls12, RemoteCertificateValidationCallback = (_, __, ___, ____) => true } }; var channel = GrpcChannel.ForAddress("static:///localhost", new GrpcChannelOptions { LoggerFactory = LoggerFactory, ServiceProvider = services.BuildServiceProvider(), Credentials = ChannelCredentials.Create(new SslCredentials(), credentials), HttpHandler = socketsHttpHandler }); var client = TestClientFactory.Create(channel, endpoint1.Method); // Act var reply = await client.UnaryCall(new HelloRequest { Name = "Balancer" }).ResponseAsync.DefaultTimeout(); // Assert Assert.AreEqual("Bearer TEST", authorization); Assert.AreEqual("Balancer", reply.Message); }
public async Task UnaryCall_UnavailableAddress_FallbackToWorkingAddress() { // Ignore errors SetExpectedErrorsFilter(writeContext => { return(true); }); string?host = null; Task <HelloReply> UnaryMethod(HelloRequest request, ServerCallContext context) { host = context.Host; return(Task.FromResult(new HelloReply { Message = request.Name })); } // Arrange using var endpoint1 = BalancerHelpers.CreateGrpcEndpoint <HelloRequest, HelloReply>(50051, UnaryMethod, nameof(UnaryMethod)); using var endpoint2 = BalancerHelpers.CreateGrpcEndpoint <HelloRequest, HelloReply>(50052, UnaryMethod, nameof(UnaryMethod)); var channel = await BalancerHelpers.CreateChannel(LoggerFactory, new RoundRobinConfig(), new[] { endpoint1.Address, endpoint2.Address }, connect : true); await BalancerHelpers.WaitForSubchannelsToBeReadyAsync(Logger, channel, 2).DefaultTimeout(); var client = TestClientFactory.Create(channel, endpoint1.Method); var reply1 = await client.UnaryCall(new HelloRequest { Name = "Balancer1" }); Assert.AreEqual("Balancer1", reply1.Message); var host1 = host; var reply2 = await client.UnaryCall(new HelloRequest { Name = "Balancer2" }); Assert.AreEqual("Balancer2", reply2.Message); var host2 = host; Assert.Contains("127.0.0.1:50051", new[] { host1, host2 }); Assert.Contains("127.0.0.1:50052", new[] { host1, host2 }); endpoint1.Dispose(); var subChannel = await BalancerHelpers.WaitForSubchannelToBeReadyAsync(Logger, channel).DefaultTimeout(); Assert.AreEqual(50052, subChannel.CurrentAddress?.EndPoint.Port); reply1 = await client.UnaryCall(new HelloRequest { Name = "Balancer" }); Assert.AreEqual("Balancer", reply1.Message); Assert.AreEqual("127.0.0.1:50052", host); }
public async Task UnaryCall_CallAfterCancellation_Success() { // Ignore errors SetExpectedErrorsFilter(writeContext => { return(true); }); string?host = null; Task <HelloReply> UnaryMethod(HelloRequest request, ServerCallContext context) { host = context.Host; return(Task.FromResult(new HelloReply { Message = request.Name })); } // Arrange using var endpoint = BalancerHelpers.CreateGrpcEndpoint <HelloRequest, HelloReply>(50051, UnaryMethod, nameof(UnaryMethod)); var channel = await BalancerHelpers.CreateChannel(LoggerFactory, new PickFirstConfig(), new[] { endpoint.Address }).DefaultTimeout(); var client = TestClientFactory.Create(channel, endpoint.Method); // Kill endpoint so client can't connect. endpoint.Dispose(); var cts = new CancellationTokenSource(TimeSpan.FromSeconds(0.5)); cts.Token.Register(() => Logger.LogInformation("Cancellation token raised")); // Start call that is canceled while getting picker. await ExceptionAssert.ThrowsAsync <RpcException>(() => client.UnaryCall( new HelloRequest { Name = "Balancer" }, new CallOptions(cancellationToken: cts.Token)).ResponseAsync).DefaultTimeout(); // Restart endpoint. using var endpoint1 = BalancerHelpers.CreateGrpcEndpoint <HelloRequest, HelloReply>(50051, UnaryMethod, nameof(UnaryMethod)); // Act var reply = await client.UnaryCall(new HelloRequest { Name = "Balancer" }, new CallOptions().WithWaitForReady(true)).ResponseAsync.DefaultTimeout(); // Assert Assert.AreEqual("Balancer", reply.Message); Assert.AreEqual("127.0.0.1:50051", host); }
public async Task UnaryCall_ReconnectBetweenCalls_Success() { // Ignore errors SetExpectedErrorsFilter(writeContext => { return(true); }); string?host = null; Task <HelloReply> UnaryMethod(HelloRequest request, ServerCallContext context) { host = context.Host; return(Task.FromResult(new HelloReply { Message = request.Name })); } // Arrange using var endpoint = BalancerHelpers.CreateGrpcEndpoint <HelloRequest, HelloReply>(50051, UnaryMethod, nameof(UnaryMethod)); var channel = await BalancerHelpers.CreateChannel(LoggerFactory, new RoundRobinConfig(), new[] { endpoint.Address }); var client = TestClientFactory.Create(channel, endpoint.Method); // Act var reply = await client.UnaryCall(new HelloRequest { Name = "Balancer" }).ResponseAsync.DefaultTimeout(); // Assert Assert.AreEqual("Balancer", reply.Message); Assert.AreEqual("127.0.0.1:50051", host); Logger.LogInformation("Ending " + endpoint.Address); endpoint.Dispose(); Logger.LogInformation("Restarting"); using var endpointNew = BalancerHelpers.CreateGrpcEndpoint <HelloRequest, HelloReply>(50051, UnaryMethod, nameof(UnaryMethod)); // Act reply = await client.UnaryCall(new HelloRequest { Name = "Balancer" }, new CallOptions().WithWaitForReady()).ResponseAsync.DefaultTimeout(); // Assert Assert.AreEqual("Balancer", reply.Message); Assert.AreEqual("127.0.0.1:50051", host); }
public async Task Active_UnaryCall_ConnectTimeout_ErrorThrownWhenTimeoutExceeded() { // Ignore errors SetExpectedErrorsFilter(writeContext => { return(true); }); var tcs = new TaskCompletionSource <object?>(TaskCreationOptions.RunContinuationsAsynchronously); string?host = null; async Task <HelloReply> UnaryMethod(HelloRequest request, ServerCallContext context) { var protocol = context.GetHttpContext().Request.Protocol; Logger.LogInformation("Received protocol: " + protocol); await tcs.Task; host = context.Host; return(new HelloReply { Message = request.Name }); } // Arrange using var endpoint = BalancerHelpers.CreateGrpcEndpoint <HelloRequest, HelloReply>(50051, UnaryMethod, nameof(UnaryMethod)); var handler = new SocketsHttpHandler() { // ConnectTimeout is so small that CT will always be canceled before Socket is used. ConnectTimeout = TimeSpan.FromTicks(1), }; var channel = GrpcChannel.ForAddress(endpoint.Address, new GrpcChannelOptions() { HttpHandler = handler, LoggerFactory = LoggerFactory }); var client = TestClientFactory.Create(channel, endpoint.Method); var ex = await ExceptionAssert.ThrowsAsync <RpcException>(() => client.UnaryCall(new HelloRequest()).ResponseAsync).DefaultTimeout(); Assert.AreEqual("A connection could not be established within the configured ConnectTimeout.", ex.Status.DebugException !.Message); }
public async Task UnaryCall_SingleAddressFailure_RpcError() { // Ignore errors SetExpectedErrorsFilter(writeContext => { return(true); }); string?host = null; Task <HelloReply> UnaryMethod(HelloRequest request, ServerCallContext context) { host = context.Host; return(Task.FromResult(new HelloReply { Message = request.Name })); } // Arrange using var endpoint = BalancerHelpers.CreateGrpcEndpoint <HelloRequest, HelloReply>(50051, UnaryMethod, nameof(UnaryMethod)); var channel = await BalancerHelpers.CreateChannel(LoggerFactory, new PickFirstConfig(), new[] { endpoint.Address }).DefaultTimeout(); var client = TestClientFactory.Create(channel, endpoint.Method); // Act var reply = await client.UnaryCall(new HelloRequest { Name = "Balancer" }).ResponseAsync.DefaultTimeout(); // Assert Assert.AreEqual("Balancer", reply.Message); Assert.AreEqual("127.0.0.1:50051", host); Logger.LogInformation("Ending " + endpoint.Address); endpoint.Dispose(); var ex = await ExceptionAssert.ThrowsAsync <RpcException>( () => client.UnaryCall(new HelloRequest { Name = "Balancer" }).ResponseAsync).DefaultTimeout(); Assert.AreEqual(StatusCode.Unavailable, ex.StatusCode); // Sometimes SocketException is wrapped by HttpRequestException. Assert.IsTrue(HasExceptionInStack <SocketException>(ex.Status.DebugException), $"Unexpected exception: {ex.Status.DebugException}");
public async Task Subchannel_ResolveRemovesSubchannel_SubchannelCleanedUp() { // Ignore errors SetExpectedErrorsFilter(writeContext => { return(true); }); string?host = null; Task <HelloReply> UnaryMethod(HelloRequest request, ServerCallContext context) { host = context.Host; return(Task.FromResult(new HelloReply { Message = request.Name })); } // Arrange using var endpoint1 = BalancerHelpers.CreateGrpcEndpoint <HelloRequest, HelloReply>(50051, UnaryMethod, nameof(UnaryMethod)); using var endpoint2 = BalancerHelpers.CreateGrpcEndpoint <HelloRequest, HelloReply>(50052, UnaryMethod, nameof(UnaryMethod)); var resolver = new TestResolver(LoggerFactory); resolver.UpdateAddresses(new List <BalancerAddress> { new BalancerAddress(endpoint1.Address.Host, endpoint1.Address.Port) }); var channel = await BalancerHelpers.CreateChannel(LoggerFactory, new RoundRobinConfig(), resolver, connect : true); var disposedSubchannel = await BalancerHelpers.WaitForSubchannelToBeReadyAsync(Logger, channel).DefaultTimeout(); Assert.IsNotNull(((SocketConnectivitySubchannelTransport)disposedSubchannel.Transport)._initialSocket); resolver.UpdateAddresses(new List <BalancerAddress> { new BalancerAddress(endpoint2.Address.Host, endpoint2.Address.Port) }); Assert.IsNull(((SocketConnectivitySubchannelTransport)disposedSubchannel.Transport)._initialSocket); }
public async Task UnaryCall_MultipleCalls_RoundRobin() { // Ignore errors SetExpectedErrorsFilter(writeContext => { return(true); }); SyncPoint?syncPoint = null; string? host = null; async Task <HelloReply> UnaryMethod(HelloRequest request, ServerCallContext context) { host = context.Host; if (syncPoint != null) { await syncPoint.WaitToContinue(); } return(new HelloReply { Message = request.Name }); } // Arrange using var endpoint1 = BalancerHelpers.CreateGrpcEndpoint <HelloRequest, HelloReply>(50051, UnaryMethod, nameof(UnaryMethod)); using var endpoint2 = BalancerHelpers.CreateGrpcEndpoint <HelloRequest, HelloReply>(50052, UnaryMethod, nameof(UnaryMethod)); var channel = await BalancerHelpers.CreateChannel(LoggerFactory, new LoadBalancingConfig("least_used"), new[] { endpoint1.Address, endpoint2.Address }, connect : true); await BalancerHelpers.WaitForSubchannelsToBeReadyAsync( Logger, channel, expectedCount : 2, getPickerSubchannels : picker => ((LeastUsedPicker?)picker)?._subchannels.ToArray() ?? Array.Empty <Subchannel>()).DefaultTimeout(); var client = TestClientFactory.Create(channel, endpoint1.Method); // Act var reply = await client.UnaryCall(new HelloRequest { Name = "Balancer" }).ResponseAsync.DefaultTimeout(); // Assert Assert.AreEqual("Balancer", reply.Message); Assert.AreEqual("127.0.0.1:50051", host); // Act reply = await client.UnaryCall(new HelloRequest { Name = "Balancer" }).ResponseAsync.DefaultTimeout(); // Assert Assert.AreEqual("Balancer", reply.Message); Assert.AreEqual("127.0.0.1:50051", host); // Act var sp1 = syncPoint = new SyncPoint(runContinuationsAsynchronously: true); var pendingCall1 = client.UnaryCall(new HelloRequest { Name = "Balancer" }); // Assert await syncPoint.WaitForSyncPoint().DefaultTimeout(); Assert.AreEqual("127.0.0.1:50051", host); // Act var sp2 = syncPoint = new SyncPoint(runContinuationsAsynchronously: true); var pendingCall2 = client.UnaryCall(new HelloRequest { Name = "Balancer" }); // Assert await syncPoint.WaitForSyncPoint().DefaultTimeout(); Assert.AreEqual("127.0.0.1:50052", host); // Act var sp3 = syncPoint = new SyncPoint(runContinuationsAsynchronously: true); var pendingCall3 = client.UnaryCall(new HelloRequest { Name = "Balancer" }); // Assert await syncPoint.WaitForSyncPoint().DefaultTimeout(); Assert.AreEqual("127.0.0.1:50051", host); sp1.Continue(); sp2.Continue(); sp3.Continue(); }
public async Task Subchannel_ResolveRemovesSubchannelAfterRequest_SubchannelCleanedUp() { // Ignore errors SetExpectedErrorsFilter(writeContext => { return(true); }); string?host = null; Task <HelloReply> UnaryMethod(HelloRequest request, ServerCallContext context) { host = context.Host; return(Task.FromResult(new HelloReply { Message = request.Name })); } // Arrange using var endpoint1 = BalancerHelpers.CreateGrpcEndpoint <HelloRequest, HelloReply>(50051, UnaryMethod, nameof(UnaryMethod)); using var endpoint2 = BalancerHelpers.CreateGrpcEndpoint <HelloRequest, HelloReply>(50052, UnaryMethod, nameof(UnaryMethod)); var resolver = new TestResolver(LoggerFactory); resolver.UpdateAddresses(new List <BalancerAddress> { new BalancerAddress(endpoint1.Address.Host, endpoint1.Address.Port) }); var channel = await BalancerHelpers.CreateChannel(LoggerFactory, new RoundRobinConfig(), resolver, connect : true); var disposedSubchannel = await BalancerHelpers.WaitForSubchannelToBeReadyAsync(Logger, channel).DefaultTimeout(); var client = TestClientFactory.Create(channel, endpoint1.Method); var reply1 = await client.UnaryCall(new HelloRequest { Name = "Balancer1" }); Assert.AreEqual("Balancer1", reply1.Message); Assert.AreEqual("127.0.0.1:50051", host !); resolver.UpdateAddresses(new List <BalancerAddress> { new BalancerAddress(endpoint2.Address.Host, endpoint2.Address.Port) }); var activeStreams = ((SocketConnectivitySubchannelTransport)disposedSubchannel.Transport).GetActiveStreams(); Assert.AreEqual(1, activeStreams.Count); Assert.AreEqual("127.0.0.1", activeStreams[0].Address.EndPoint.Host); Assert.AreEqual(50051, activeStreams[0].Address.EndPoint.Port); // Wait until connected to new endpoint Subchannel?newSubchannel = null; while (true) { newSubchannel = await BalancerHelpers.WaitForSubchannelToBeReadyAsync(Logger, channel).DefaultTimeout(); if (newSubchannel.CurrentAddress?.EndPoint.Equals(endpoint2.EndPoint) ?? false) { break; } } // Subchannel has a socket until a request is made. Assert.IsNotNull(((SocketConnectivitySubchannelTransport)newSubchannel.Transport)._initialSocket); endpoint1.Dispose(); var reply2 = await client.UnaryCall(new HelloRequest { Name = "Balancer2" }); Assert.AreEqual("Balancer2", reply2.Message); Assert.AreEqual("127.0.0.1:50052", host !); // Disposed subchannel stream removed when endpoint disposed. activeStreams = ((SocketConnectivitySubchannelTransport)disposedSubchannel.Transport).GetActiveStreams(); Assert.AreEqual(0, activeStreams.Count); Assert.IsNull(((SocketConnectivitySubchannelTransport)disposedSubchannel.Transport)._initialSocket); // New subchannel stream created with request. activeStreams = ((SocketConnectivitySubchannelTransport)newSubchannel.Transport).GetActiveStreams(); Assert.AreEqual(1, activeStreams.Count); Assert.AreEqual("127.0.0.1", activeStreams[0].Address.EndPoint.Host); Assert.AreEqual(50052, activeStreams[0].Address.EndPoint.Port); Assert.IsNull(((SocketConnectivitySubchannelTransport)disposedSubchannel.Transport)._initialSocket); }
public async Task UnaryCall_MultipleChannelsShareHandler_ReconnectBetweenCalls_Success() { // Ignore errors SetExpectedErrorsFilter(writeContext => { return(true); }); string?host = null; Task <HelloReply> UnaryMethod(HelloRequest request, ServerCallContext context) { host = context.Host; return(Task.FromResult(new HelloReply { Message = request.Name })); } // Arrange using var endpoint = BalancerHelpers.CreateGrpcEndpoint <HelloRequest, HelloReply>(50051, UnaryMethod, nameof(UnaryMethod)); var socketsHttpHandler = new SocketsHttpHandler(); var channel1 = await BalancerHelpers.CreateChannel(LoggerFactory, new RoundRobinConfig(), new[] { endpoint.Address }, socketsHttpHandler); var channel2 = await BalancerHelpers.CreateChannel(LoggerFactory, new RoundRobinConfig(), new[] { endpoint.Address }, socketsHttpHandler); var client1 = TestClientFactory.Create(channel1, endpoint.Method); var client2 = TestClientFactory.Create(channel2, endpoint.Method); // Act var reply1Task = client1.UnaryCall(new HelloRequest { Name = "Balancer" }).ResponseAsync.DefaultTimeout(); var reply2Task = client2.UnaryCall(new HelloRequest { Name = "Balancer" }).ResponseAsync.DefaultTimeout(); // Assert Assert.AreEqual("Balancer", (await reply1Task).Message); Assert.AreEqual("Balancer", (await reply2Task).Message); Assert.AreEqual("127.0.0.1:50051", host); // Wait for connecting or failure. // Connecting is faster to wait for, but the status could change so quickly that wait for state change is not triggered. // Use failure as backup status. var expectedStates = new[] { ConnectivityState.Connecting, ConnectivityState.TransientFailure }; var waitForConnectingTask = Task.WhenAll( BalancerHelpers.WaitForChannelStatesAsync(Logger, channel1, expectedStates, channelId: 1), BalancerHelpers.WaitForChannelStatesAsync(Logger, channel2, expectedStates, channelId: 2)); Logger.LogInformation("Ending " + endpoint.Address); endpoint.Dispose(); await waitForConnectingTask.DefaultTimeout(); Logger.LogInformation("Restarting"); using var endpointNew = BalancerHelpers.CreateGrpcEndpoint <HelloRequest, HelloReply>(50051, UnaryMethod, nameof(UnaryMethod)); // Act reply1Task = client1.UnaryCall(new HelloRequest { Name = "Balancer" }, new CallOptions().WithWaitForReady()).ResponseAsync.DefaultTimeout(); reply2Task = client2.UnaryCall(new HelloRequest { Name = "Balancer" }, new CallOptions().WithWaitForReady()).ResponseAsync.DefaultTimeout(); // Assert Assert.AreEqual("Balancer", (await reply1Task).Message); Assert.AreEqual("Balancer", (await reply2Task).Message); Assert.AreEqual("127.0.0.1:50051", host); }
public async Task Active_UnaryCall_MultipleStreams_UnavailableAddress_FallbackToWorkingAddress() { // Ignore errors SetExpectedErrorsFilter(writeContext => { return(true); }); var tcs = new TaskCompletionSource <object?>(TaskCreationOptions.RunContinuationsAsynchronously); string?host = null; async Task <HelloReply> UnaryMethod(HelloRequest request, ServerCallContext context) { var protocol = context.GetHttpContext().Request.Protocol; Logger.LogInformation("Received protocol: " + protocol); await tcs.Task; host = context.Host; return(new HelloReply { Message = request.Name }); } // Arrange using var endpoint1 = BalancerHelpers.CreateGrpcEndpoint <HelloRequest, HelloReply>(50051, UnaryMethod, nameof(UnaryMethod), HttpProtocols.Http1AndHttp2, isHttps: true); using var endpoint2 = BalancerHelpers.CreateGrpcEndpoint <HelloRequest, HelloReply>(50052, UnaryMethod, nameof(UnaryMethod), HttpProtocols.Http1AndHttp2, isHttps: true); var services = new ServiceCollection(); services.AddSingleton <ResolverFactory>(new StaticResolverFactory(_ => new[] { new BalancerAddress(endpoint1.Address.Host, endpoint1.Address.Port), new BalancerAddress(endpoint2.Address.Host, endpoint2.Address.Port) })); var socketsHttpHandler = new SocketsHttpHandler { EnableMultipleHttp2Connections = true, SslOptions = new System.Net.Security.SslClientAuthenticationOptions() { RemoteCertificateValidationCallback = (_, __, ___, ____) => true } }; var grpcWebHandler = new GrpcWebHandler(GrpcWebMode.GrpcWeb, new RequestVersionHandler(socketsHttpHandler)); var channel = GrpcChannel.ForAddress("static:///localhost", new GrpcChannelOptions { LoggerFactory = LoggerFactory, HttpHandler = grpcWebHandler, ServiceProvider = services.BuildServiceProvider(), Credentials = new SslCredentials() }); var client = TestClientFactory.Create(channel, endpoint1.Method); // Act grpcWebHandler.HttpVersion = new Version(1, 1); var http11CallTasks = new List <Task <HelloReply> >(); for (int i = 0; i < 10; i++) { Logger.LogInformation($"Sending gRPC call {i}"); http11CallTasks.Add(client.UnaryCall(new HelloRequest { Name = "Balancer" }).ResponseAsync); } Logger.LogInformation($"Done sending gRPC calls"); var balancer = BalancerHelpers.GetInnerLoadBalancer <PickFirstBalancer>(channel) !; var subchannel = balancer._subchannel !; var transport = (SocketConnectivitySubchannelTransport)subchannel.Transport; var activeStreams = transport.GetActiveStreams(); // Assert Assert.AreEqual(HttpHandlerType.SocketsHttpHandler, channel.HttpHandlerType); await TestHelpers.AssertIsTrueRetryAsync(() => { activeStreams = transport.GetActiveStreams(); return(activeStreams.Count == 10); }, "Wait for connections to start."); foreach (var t in activeStreams) { Assert.AreEqual(new DnsEndPoint("127.0.0.1", 50051), t.Address.EndPoint); } // Act grpcWebHandler.HttpVersion = new Version(2, 0); var http2CallTasks = new List <Task <HelloReply> >(); for (int i = 0; i < 10; i++) { Logger.LogInformation($"Sending gRPC call {i}"); http2CallTasks.Add(client.UnaryCall(new HelloRequest { Name = "Balancer" }).ResponseAsync); } Logger.LogInformation($"Done sending gRPC calls"); // Assert await TestHelpers.AssertIsTrueRetryAsync(() => { activeStreams = transport.GetActiveStreams(); return(activeStreams.Count == 11); }, "Wait for connections to start."); Assert.AreEqual(new DnsEndPoint("127.0.0.1", 50051), activeStreams.Last().Address.EndPoint); tcs.SetResult(null); await Task.WhenAll(http11CallTasks).DefaultTimeout(); await Task.WhenAll(http2CallTasks).DefaultTimeout(); Assert.AreEqual(ConnectivityState.Ready, channel.State); Logger.LogInformation($"Closing {endpoint1}"); endpoint1.Dispose(); // There are still be 10 HTTP/1.1 connections because they aren't immediately removed // when the server is shutdown and connectivity is lost. await TestHelpers.AssertIsTrueRetryAsync(() => { activeStreams = transport.GetActiveStreams(); return(activeStreams.Count == 10); }, "Wait for HTTP/2 connection to end."); grpcWebHandler.HttpVersion = new Version(1, 1); await Task.Delay(1000); Logger.LogInformation($"Starting failed call"); var ex = await ExceptionAssert.ThrowsAsync <RpcException>(() => client.UnaryCall(new HelloRequest { Name = "Balancer" }).ResponseAsync).DefaultTimeout(); Assert.AreEqual(StatusCode.Unavailable, ex.StatusCode); // Removed by failed call. activeStreams = transport.GetActiveStreams(); Assert.AreEqual(0, activeStreams.Count); Assert.AreEqual(ConnectivityState.Idle, channel.State); Logger.LogInformation($"Next call goes to fallback address."); var reply = await client.UnaryCall(new HelloRequest { Name = "Balancer" }).ResponseAsync.TimeoutAfter(TimeSpan.FromSeconds(20)); Assert.AreEqual("Balancer", reply.Message); Assert.AreEqual("127.0.0.1:50052", host); activeStreams = transport.GetActiveStreams(); Assert.AreEqual(1, activeStreams.Count); Assert.AreEqual(new DnsEndPoint("127.0.0.1", 50052), activeStreams[0].Address.EndPoint); }