public void SendMessageSendsAndReceivesAMessage() { var action = "DoSomething"; var request = new ServerRequest { ServerName = "TestServer" }; var url = "http://somewhere/"; var factory = new TestClientFactory((u, a, d) => { Assert.AreEqual(url + "server/TestServer/RawXmlMessage.aspx", u.AbsoluteUri); Assert.AreEqual("POST", a); Assert.AreEqual(action, d["action"]); Assert.AreEqual(request.ToString(), d["message"]); var theResponse = new Response { RequestIdentifier = request.Identifier }; return Encoding.UTF8.GetBytes(theResponse.ToString()); }); var connection = new HttpConnection(new Uri(url), factory); var response = connection.SendMessage(action, request); Assert.IsInstanceOf<Response>(response); Assert.AreEqual(request.Identifier, response.RequestIdentifier); }
public void SendMessageAsyncSendsMessage() { var action = "DoSomething"; var request = new ServerRequest { ServerName = "TestServer" }; var url = "http://somewhere/"; var factory = new TestClientFactory((u, a, d) => { Assert.AreEqual(url + "server/TestServer/RawXmlMessage.aspx", u.AbsoluteUri); Assert.AreEqual("POST", a); Assert.AreEqual(action, d["action"]); Assert.AreEqual(request.ToString(), d["message"]); var theResponse = new Response { RequestIdentifier = request.Identifier }; return Encoding.UTF8.GetBytes(theResponse.ToString()); }); var connection = new HttpConnection(new Uri(url), factory); var completed = false; connection.SendMessageCompleted += (o, e) => { completed = true; Assert.IsFalse(e.Cancelled); Assert.IsNull(e.Error); }; connection.SendMessageAsync(action, request); Assert.IsTrue(completed); }
public async void Should_Be_Able_To_Publish_Dynamic_Objects() { using (var client = TestClientFactory.CreateNormal()) { /* Setup */ var tcs = new TaskCompletionSource <DynamicMessage>(); client.SubscribeAsync <DynamicMessage>((message, context) => { tcs.TrySetResult(message); return(Task.FromResult(true)); }); /* Test */ client.PublishAsync(new DynamicMessage { Body = new { IsDynamic = true } }); await tcs.Task; /* Assert */ Assert.True(tcs.Task.Result.Body.IsDynamic); } }
public async Task Should_Not_Throw_If_Credentials_Are_Correct() { /* Setup */ var config = new RawRabbitConfiguration { Username = "******", Password = "******", Hostnames = new List <string> { "localhost" }, VirtualHost = "/", Port = 5672 }; /* Test */ var b = TestClientFactory.CreateNormal(ioc => ioc.AddSingleton(p => config)); b.Dispose(); /* Assert */ Assert.True(true, "Does not throw."); }
public async Task Unary_CanceledBeforeServerCall_Failure() { var callCount = 0; var tcs = new TaskCompletionSource <DataMessage>(TaskCreationOptions.RunContinuationsAsynchronously); Task <DataMessage> UnaryFailure(DataMessage request, ServerCallContext context) { callCount++; return(tcs.Task); } // Arrange var method = Fixture.DynamicGrpc.AddUnaryMethod <DataMessage, DataMessage>(UnaryFailure); var serviceConfig = ServiceConfigHelpers.CreateHedgingServiceConfig(nonFatalStatusCodes: new List <StatusCode> { StatusCode.DeadlineExceeded }); var channel = CreateChannel(serviceConfig: serviceConfig); var client = TestClientFactory.Create(channel, method); var cts = new CancellationTokenSource(); cts.Cancel(); // Act var call = client.UnaryCall(new DataMessage(), new CallOptions(cancellationToken: cts.Token)); // Assert var ex = await ExceptionAssert.ThrowsAsync <RpcException>(() => call.ResponseAsync).DefaultTimeout(); Assert.AreEqual(StatusCode.Cancelled, ex.StatusCode); Assert.AreEqual(StatusCode.Cancelled, call.GetStatus().StatusCode); Assert.AreEqual(0, callCount); AssertHasLog(LogLevel.Debug, "CallCommited", "Call commited. Reason: Canceled"); tcs.SetResult(new DataMessage()); }
public async Task Unary_DeadlineExceedAfterServerCall_Failure(int exceptedServerCallCount) { var callCount = 0; var tcs = new TaskCompletionSource <DataMessage>(TaskCreationOptions.RunContinuationsAsynchronously); Task <DataMessage> UnaryFailure(DataMessage request, ServerCallContext context) { callCount++; if (callCount < exceptedServerCallCount) { return(Task.FromException <DataMessage>(new RpcException(new Status(StatusCode.DeadlineExceeded, "")))); } return(tcs.Task); } // Arrange var method = Fixture.DynamicGrpc.AddUnaryMethod <DataMessage, DataMessage>(UnaryFailure); var serviceConfig = ServiceConfigHelpers.CreateHedgingServiceConfig(nonFatalStatusCodes: new List <StatusCode> { StatusCode.DeadlineExceeded }); var channel = CreateChannel(serviceConfig: serviceConfig); var client = TestClientFactory.Create(channel, method); // Act var call = client.UnaryCall(new DataMessage(), new CallOptions(deadline: DateTime.UtcNow.AddMilliseconds(200))); // Assert var ex = await ExceptionAssert.ThrowsAsync <RpcException>(() => call.ResponseAsync).DefaultTimeout(); Assert.AreEqual(StatusCode.DeadlineExceeded, ex.StatusCode); Assert.AreEqual(StatusCode.DeadlineExceeded, call.GetStatus().StatusCode); Assert.IsFalse(Logs.Any(l => l.EventId.Name == "DeadlineTimerRescheduled")); }
public async Task ReceivedMessageExceedsDefaultSize_ThrowError() { Task <HelloReply> UnaryDeadlineExceeded(HelloRequest request, ServerCallContext context) { // Return message is 4 MB + 1 B. Default receive size is 4 MB return(Task.FromResult(new HelloReply { Message = new string('!', (1024 * 1024 * 4) + 1) })); } // Arrange SetExpectedErrorsFilter(writeContext => { if (writeContext.LoggerName == "Grpc.Net.Client.Internal.GrpcCall" && writeContext.EventId.Name == "ErrorReadingMessage" && writeContext.State.ToString() == "Error reading message.") { return(true); } return(false); }); var method = Fixture.DynamicGrpc.AddUnaryMethod <HelloRequest, HelloReply>(UnaryDeadlineExceeded); var channel = CreateChannel(); channel.DisableClientDeadlineTimer = true; var client = TestClientFactory.Create(channel, method); // Act var ex = await ExceptionAssert.ThrowsAsync <RpcException>(() => client.UnaryCall(new HelloRequest()).ResponseAsync).DefaultTimeout(); // Assert Assert.AreEqual(StatusCode.ResourceExhausted, ex.StatusCode); Assert.AreEqual("Received message exceeds the maximum configured message size.", ex.Status.Detail); }
public async Task Should_Honor_Last_Configuration() { /* Setup */ using (var client = TestClientFactory.CreateExtendable()) { const string exchangeName = "topology"; TestChannel.ExchangeDelete(exchangeName); /* Test */ var result = await client.UpdateTopologyAsync(c => c .ForExchange(exchangeName) .UseConfiguration(e => e.WithType(ExchangeType.Headers)) .ForExchange(exchangeName) .UseConfiguration(e => e.WithType(ExchangeType.Topic)) .ForExchange(exchangeName) .UseConfiguration(e => e.WithType(ExchangeType.Direct)) .ForExchange(exchangeName) .UseConfiguration(e => e.WithType(ExchangeType.Fanout))); /* Assert */ Assert.Equal(result.Exchanges[0].Exchange.ExchangeType, ExchangeType.Fanout.ToString().ToLower()); } }
public async Task Should_Work_For_Pub_Sub() { /* Setup */ using (var client = TestClientFactory.CreateNormal(ioc => ioc .AddSingleton <IConfigurationEvaluator, AttributeConfigEvaluator>() )) { var tcs = new TaskCompletionSource <AttributedMessage>(); client.SubscribeAsync <AttributedMessage>((message, context) => { tcs.TrySetResult(message); return(Task.FromResult(true)); }); /* Test */ await client.PublishAsync(new AttributedMessage()); await tcs.Task; /* Assert */ Assert.True(true, "Routing successful."); } }
public async Task Should_Support_The_Hello_World_Tutorial() { /* Setup */ using (var sender = TestClientFactory.CreateNormal()) using (var reciever = TestClientFactory.CreateNormal()) { var sent = new BasicMessage { Prop = "Hello, world!" }; var recieved = new TaskCompletionSource <BasicMessage>(); reciever.SubscribeAsync <BasicMessage>((message, info) => { recieved.SetResult(message); return(Task.FromResult(true)); }, configuration => configuration .WithQueue(queue => queue .WithName("hello") .WithExclusivity(false) .WithAutoDelete(false) ) .WithRoutingKey("hello") ); /* Test */ await sender.PublishAsync(sent, configuration : builder => builder .WithRoutingKey("hello") ); await recieved.Task; /* Assert */ Assert.Equal(expected: sent.Prop, actual: recieved.Task.Result.Prop); } }
public async Task Should_Perform_Rpc_Without_Direct_Reply_To() { /* Setup */ using (var requester = TestClientFactory.CreateNormal()) using (var responder = TestClientFactory.CreateNormal()) { var response = new BasicResponse { Prop = "This is the response." }; responder.RespondAsync <BasicRequest, BasicResponse>((req, i) => { return(Task.FromResult(response)); }); /* Test */ var firstRecieved = await requester.RequestAsync <BasicRequest, BasicResponse>(new BasicRequest(), configuration : cfg => cfg .WithReplyQueue( q => q .WithName("special_reply_queue") .WithAutoDelete()) .WithNoAck(false)); var secondRecieved = await requester.RequestAsync <BasicRequest, BasicResponse>(new BasicRequest(), configuration : cfg => cfg .WithReplyQueue( q => q .WithName("another_special_reply_queue") .WithAutoDelete()) .WithNoAck(false) ); /* Assert */ Assert.Equal(expected: response.Prop, actual: firstRecieved.Prop); Assert.Equal(expected: response.Prop, actual: secondRecieved.Prop); } }
private async Task TestTelemetryHeaderIsSet(HttpMessageHandler?handler) { string?telemetryHeader = null; Task <HelloReply> UnaryTelemetryHeader(HelloRequest request, ServerCallContext context) { telemetryHeader = context.RequestHeaders.GetValue( #if NET5_0 "traceparent" #else "request-id" #endif ); return(Task.FromResult(new HelloReply())); } // Arrange var method = Fixture.DynamicGrpc.AddUnaryMethod <HelloRequest, HelloReply>(UnaryTelemetryHeader); var options = new GrpcChannelOptions { LoggerFactory = LoggerFactory, HttpHandler = handler }; // Want to test the behavior of the default, internally created handler. // Only supply the URL to a manually created GrpcChannel. var channel = GrpcChannel.ForAddress(Fixture.GetUrl(TestServerEndpointName.Http2), options); var client = TestClientFactory.Create(channel, method); // Act await client.UnaryCall(new HelloRequest()); // Assert Assert.IsNotNull(telemetryHeader); }
public async Task Should_Be_Able_To_Recieve_Different_Types_Of_Messages() { /* Setup */ using (var publisher = TestClientFactory.CreateNormal()) using (var subscriber = TestClientFactory.CreateNormal()) { var basicMsg = new BasicMessage { Prop = "Hello, world!" }; var simpleMsg = new SimpleMessage { IsSimple = true }; var basicMsgTcs = new TaskCompletionSource <BasicMessage>(); var simpleMsgTcs = new TaskCompletionSource <SimpleMessage>(); subscriber.SubscribeAsync <BasicMessage>((msg, i) => { basicMsgTcs.SetResult(msg); return(basicMsgTcs.Task); }); subscriber.SubscribeAsync <SimpleMessage>((msg, i) => { simpleMsgTcs.SetResult(msg); return(basicMsgTcs.Task); }); /* Test */ publisher.PublishAsync(basicMsg); publisher.PublishAsync(simpleMsg); Task.WaitAll(basicMsgTcs.Task, simpleMsgTcs.Task); /* Assert */ Assert.Equal(expected: basicMsg.Prop, actual: basicMsgTcs.Task.Result.Prop); Assert.Equal(expected: simpleMsg.IsSimple, actual: simpleMsgTcs.Task.Result.IsSimple); } }
public async Task Should_Call_Message_Handler_In_Correct_Order() { /* Setup */ using (var publisher = TestClientFactory.CreateExtendable()) { publisher.SubscribeAsync <FirstMessage>((message, context) => publisher.PublishAsync(new SecondMessage(), context.GlobalRequestId)); publisher.SubscribeAsync <SecondMessage>((message, context) => publisher.PublishAsync(new ThirdMessage(), context.GlobalRequestId)); publisher.SubscribeAsync <ThirdMessage>((message, context) => publisher.PublishAsync(new ForthMessage(), context.GlobalRequestId)); var recieveIndex = 0; var secondMsgDate = DateTime.MinValue; var thirdMsgDate = DateTime.MinValue; /* Test */ var chain = _client.ExecuteSequence(c => c .PublishAsync(new FirstMessage()) .When <SecondMessage>((message, context) => { secondMsgDate = DateTime.Now; return(Task.FromResult(true)); }) .When <ThirdMessage>((message, context) => { thirdMsgDate = DateTime.Now; return(Task.FromResult(true)); }) .Complete <ForthMessage>() ); await chain.Task; /* Assert */ Assert.True(secondMsgDate < thirdMsgDate); } }
public async Task Unary_DeadlineExceedBeforeServerCall_Failure() { var callCount = 0; var tcs = new TaskCompletionSource <DataMessage>(TaskCreationOptions.RunContinuationsAsynchronously); Task <DataMessage> UnaryFailure(DataMessage request, ServerCallContext context) { callCount++; return(tcs.Task); } // Arrange var method = Fixture.DynamicGrpc.AddUnaryMethod <DataMessage, DataMessage>(UnaryFailure); var serviceConfig = ServiceConfigHelpers.CreateRetryServiceConfig(retryableStatusCodes: new List <StatusCode> { StatusCode.DeadlineExceeded }); var channel = CreateChannel(serviceConfig: serviceConfig); var client = TestClientFactory.Create(channel, method); // Act var call = client.UnaryCall(new DataMessage(), new CallOptions(deadline: DateTime.UtcNow)); // Assert var ex = await ExceptionAssert.ThrowsAsync <RpcException>(() => call.ResponseAsync).DefaultTimeout(); Assert.AreEqual(StatusCode.DeadlineExceeded, ex.StatusCode); Assert.AreEqual(StatusCode.DeadlineExceeded, call.GetStatus().StatusCode); Assert.AreEqual(0, callCount); AssertHasLog(LogLevel.Debug, "RetryEvaluated", "Evaluated retry for failed gRPC call. Status code: 'DeadlineExceeded', Attempt: 1, Retry: False"); AssertHasLog(LogLevel.Debug, "CallCommited", "Call commited. Reason: DeadlineExceeded"); tcs.SetResult(new DataMessage()); }
public async Task Should_Retry_For_Publish_Subscribe_After_Given_Timespan() { /* Setup */ using (var subscriber = TestClientFactory.CreateNormal <AdvancedMessageContext>()) using (var publisher = TestClientFactory.CreateNormal <AdvancedMessageContext>()) { var subscribeTcs = new TaskCompletionSource <bool>(); var delay = TimeSpan.FromSeconds(1); var hasBeenDelayed = false; var firstRecieved = DateTime.MinValue; var secondRecieved = DateTime.MinValue; subscriber.SubscribeAsync <BasicMessage>((message, context) => { if (!hasBeenDelayed) { firstRecieved = DateTime.Now; context.RetryLater(delay); hasBeenDelayed = true; return(Task.FromResult(true)); } secondRecieved = DateTime.Now; return(Task.Delay(10).ContinueWith(t => subscribeTcs.SetResult(true))); }); /* Test */ await publisher.PublishAsync(new BasicMessage { Prop = "I'm about to be reborn!" }); await subscribeTcs.Task; var actualDelay = secondRecieved - firstRecieved; await Task.Delay(80); /* Assert */ Assert.Equal(expected: delay.Seconds, actual: actualDelay.Seconds); } }
public async Task Should_Update_Exhange_Type() { /* Setup */ const string exchangeName = "topology"; TestChannel.ExchangeDelete(exchangeName); TestChannel.ExchangeDeclare(exchangeName, RabbitMQ.Client.ExchangeType.Direct); using (var client = TestClientFactory.CreateExtendable()) { /* Test */ await client.UpdateTopologyAsync(t => t .ForExchange(exchangeName) .UseConfiguration(e => e .WithType(ExchangeType.Topic) .WithDurability(false)) ); /* Assert */ TestChannel.ExchangeDeclare(exchangeName, RabbitMQ.Client.ExchangeType.Topic); Assert.True(true, "Did not throw"); Assert.Throws <OperationInterruptedException>(() => TestChannel.ExchangeDeclare(exchangeName, RabbitMQ.Client.ExchangeType.Direct)); } }
public async Task ShareSocketsHttpHandler() { Task <HelloReply> Unary(HelloRequest request, ServerCallContext context) { return(Task.FromResult(new HelloReply { Message = request.Name })); } // Arrange var method = Fixture.DynamicGrpc.AddUnaryMethod <HelloRequest, HelloReply>(Unary); var http = Fixture.CreateHandler(TestServerEndpointName.Http2); var channel1 = GrpcChannel.ForAddress(http.address, new GrpcChannelOptions { LoggerFactory = LoggerFactory, HttpHandler = http.handler }); var channel2 = GrpcChannel.ForAddress(http.address, new GrpcChannelOptions { LoggerFactory = LoggerFactory, HttpHandler = http.handler }); var client2 = TestClientFactory.Create(channel2, method); // Act var reply = await client2.UnaryCall(new HelloRequest { Name = "World" }).ResponseAsync.DefaultTimeout(); // Assert Assert.AreEqual(HttpHandlerType.SocketsHttpHandler, channel1.HttpHandlerType); Assert.AreEqual(HttpHandlerType.SocketsHttpHandler, channel2.HttpHandlerType); Assert.AreEqual("World", reply.Message); }
public async Task Should_Keep_Consumer_Open_After_Publish_Exception() { /* Setup */ using (var client = TestClientFactory.CreateNormal()) { var hasThrownTcs = new TaskCompletionSource <bool>(); var hasRecievedTcs = new TaskCompletionSource <bool>(); client.SubscribeAsync <BasicMessage>((message, context) => { if (!hasThrownTcs.Task.IsCompleted) { Timer timer = null; timer = new Timer(state => { timer?.Dispose(); hasThrownTcs.SetResult(true); }, null, TimeSpan.FromMilliseconds(100), new TimeSpan(-1)); throw new Exception("Uh uh!"); } else { hasRecievedTcs.SetResult(true); } return(Task.FromResult(true)); }); /* Test */ client.PublishAsync(new BasicMessage()); await hasThrownTcs.Task; client.PublishAsync(new BasicMessage()); await hasRecievedTcs.Task; /* Assert */ Assert.True(true); } }
public async Task Should_Throw_Publish_Confirm_Exception_If_Server_Doesnt_Respond_Within_Time_Limit() { /* Setup */ var publisher = TestClientFactory.CreateNormal(ioc => ioc.AddSingleton(p => { var config = RawRabbitConfiguration.Local; config.PublishConfirmTimeout = TimeSpan.FromTicks(1); return(config); })); using (publisher) { /* Test */ /* Assert */ try { await publisher.PublishAsync <BasicMessage>(); } catch (PublishConfirmException) { Assert.True(true); } } }
public async Task Should_Forward_Context_On_Rpc() { /* Setup */ using (var requester = TestClientFactory.CreateNormal()) using (var firstResponder = TestClientFactory.CreateNormal()) using (var secondResponder = TestClientFactory.CreateNormal()) { var tcs = new TaskCompletionSource <bool>(); MessageContext firstContext = null; MessageContext secondContext = null; firstResponder.RespondAsync <FirstRequest, FirstResponse>(async(req, c) => { firstContext = c; var resp = await firstResponder.RequestAsync <SecondRequest, SecondResponse>(new SecondRequest(), c.GlobalRequestId); return(new FirstResponse { Infered = resp.Source }); }); secondResponder.RespondAsync <SecondRequest, SecondResponse>((req, c) => { secondContext = c; tcs.SetResult(true); return(Task.FromResult(new SecondResponse { Source = Guid.NewGuid() })); }); /* Test */ requester.RequestAsync <FirstRequest, FirstResponse>(); await tcs.Task; /* Assert */ Assert.Equal(firstContext.GlobalRequestId, secondContext.GlobalRequestId); } }
public async Task UnaryMethod_DeadlineExceededCall_PollingCountersUpdatedCorrectly() { var tcs = new TaskCompletionSource <bool>(TaskCreationOptions.RunContinuationsAsynchronously); // Ignore errors SetExpectedErrorsFilter(writeContext => { return(true); }); async Task <HelloReply> UnaryDeadlineExceeded(HelloRequest request, ServerCallContext context) { await PollAssert(() => context.Status.StatusCode == StatusCode.DeadlineExceeded).DefaultTimeout(); tcs.TrySetResult(true); return(new HelloReply()); } // Arrange var clientEventListener = CreateEnableListener(Grpc.Net.Client.Internal.GrpcEventSource.Log); var serverEventListener = CreateEnableListener(Grpc.AspNetCore.Server.Internal.GrpcEventSource.Log); // Act - Start call var method = Fixture.DynamicGrpc.AddUnaryMethod <HelloRequest, HelloReply>(UnaryDeadlineExceeded); var channel = CreateChannel(); channel.DisableClientDeadlineTimer = true; var client = TestClientFactory.Create(Channel, method); var call = client.UnaryCall(new HelloRequest(), new CallOptions(deadline: DateTime.UtcNow.AddMilliseconds(500))); // Assert - Call in progress await AssertCounters(serverEventListener, new Dictionary <string, long> { ["current-calls"] = 1, ["calls-failed"] = 0, ["calls-deadline-exceeded"] = 0, }).DefaultTimeout(); await AssertCounters(clientEventListener, new Dictionary <string, long> { ["current-calls"] = 1, ["calls-failed"] = 0, ["calls-deadline-exceeded"] = 0, }).DefaultTimeout(); // Act - Wait for call to deadline on server await tcs.Task.DefaultTimeout(); // Assert - Call complete await AssertCounters(serverEventListener, new Dictionary <string, long> { ["current-calls"] = 0, ["calls-failed"] = 1, ["calls-deadline-exceeded"] = 1, }).DefaultTimeout(); await AssertCounters(clientEventListener, new Dictionary <string, long> { ["current-calls"] = 0, ["calls-failed"] = 1, ["calls-deadline-exceeded"] = 1, }).DefaultTimeout(); }
public async Task DuplexStreamingMethod_Success_PollingCountersUpdatedCorrectly() { async Task DuplexStreamingMethod(IAsyncStreamReader <HelloRequest> requestStream, IServerStreamWriter <HelloReply> responseStream, ServerCallContext context) { while (await requestStream.MoveNext().DefaultTimeout()) { } await responseStream.WriteAsync(new HelloReply { Message = "Message 1" }).DefaultTimeout(); await responseStream.WriteAsync(new HelloReply { Message = "Message 2" }).DefaultTimeout(); } // Arrange var clientEventListener = CreateEnableListener(Grpc.Net.Client.Internal.GrpcEventSource.Log); var serverEventListener = CreateEnableListener(Grpc.AspNetCore.Server.Internal.GrpcEventSource.Log); // Act - Start call var method = Fixture.DynamicGrpc.AddDuplexStreamingMethod <HelloRequest, HelloReply>(DuplexStreamingMethod); var client = TestClientFactory.Create(Channel, method); var call = client.DuplexStreamingCall(); // Assert - Call in progress await AssertCounters(serverEventListener, new Dictionary <string, long> { ["current-calls"] = 1, ["messages-sent"] = 0, ["messages-received"] = 0, }).DefaultTimeout(); await AssertCounters(clientEventListener, new Dictionary <string, long> { ["current-calls"] = 1, ["messages-sent"] = 0, ["messages-received"] = 0, }).DefaultTimeout(); await call.RequestStream.WriteAsync(new HelloRequest { Name = "Name 1" }).DefaultTimeout(); await call.RequestStream.WriteAsync(new HelloRequest { Name = "Name 2" }).DefaultTimeout(); await call.RequestStream.CompleteAsync().DefaultTimeout(); while (await call.ResponseStream.MoveNext().DefaultTimeout()) { } // Assert - Call complete await AssertCounters(serverEventListener, new Dictionary <string, long> { ["current-calls"] = 0, ["messages-sent"] = 2, ["messages-received"] = 2, }).DefaultTimeout(); await AssertCounters(clientEventListener, new Dictionary <string, long> { ["current-calls"] = 0, ["messages-sent"] = 2, ["messages-received"] = 2, }).DefaultTimeout(); }
public async Task Duplex_ManyParallelRequests_MessageRoundTripped() { const string ImportantMessage = @" _____ _____ _____ | __ \| __ \ / ____| __ _| |__) | |__) | | / _` | _ /| ___/| | | (_| | | \ \| | | |____ \__, |_| \_\_| \_____| __/ | |___/ _ (_) _ ___ | / __| | \__ \ _ |_|___/ | | ___ ___ ___ | | / __/ _ \ / _ \| | | (_| (_) | (_) | | \___\___/ \___/|_| "; var attempts = 100; var allUploads = new List <string>(); var allCompletedTasks = new List <Task>(); var tcs = new TaskCompletionSource <object?>(TaskCreationOptions.RunContinuationsAsynchronously); async Task MessageUpload( IAsyncStreamReader <StringValue> requestStream, IServerStreamWriter <StringValue> responseStream, ServerCallContext context) { // Receive chunks var chunks = new List <string>(); await foreach (var chunk in requestStream.ReadAllAsync()) { chunks.Add(chunk.Value); } Task completeTask; lock (allUploads) { allUploads.Add(string.Join(Environment.NewLine, chunks)); if (allUploads.Count < attempts) { // Check that unused calls are canceled. completeTask = Task.Run(async() => { await tcs.Task; var cancellationTcs = new TaskCompletionSource <bool>(); context.CancellationToken.Register(s => ((TaskCompletionSource <bool>)s !).SetResult(true), cancellationTcs); await cancellationTcs.Task; }); } else { // Write response in used call. completeTask = Task.Run(async() => { // Write chunks foreach (var chunk in chunks) { await responseStream.WriteAsync(new StringValue { Value = chunk }); } }); } } await completeTask; } var method = Fixture.DynamicGrpc.AddDuplexStreamingMethod <StringValue, StringValue>(MessageUpload); var channel = CreateChannel(serviceConfig: ServiceConfigHelpers.CreateHedgingServiceConfig(maxAttempts: 100, hedgingDelay: TimeSpan.Zero), maxRetryAttempts: 100); var client = TestClientFactory.Create(channel, method); using var call = client.DuplexStreamingCall(); var lines = ImportantMessage.Split(Environment.NewLine); for (var i = 0; i < lines.Length; i++) { await call.RequestStream.WriteAsync(new StringValue { Value = lines[i] }).DefaultTimeout(); await Task.Delay(TimeSpan.FromSeconds(0.01)).DefaultTimeout(); } await call.RequestStream.CompleteAsync().DefaultTimeout(); await TestHelpers.AssertIsTrueRetryAsync(() => allUploads.Count == 100, "Wait for all calls to reach server.").DefaultTimeout(); tcs.SetResult(null); var receivedLines = new List <string>(); await foreach (var line in call.ResponseStream.ReadAllAsync().DefaultTimeout()) { receivedLines.Add(line.Value); } Assert.AreEqual(ImportantMessage, string.Join(Environment.NewLine, receivedLines)); foreach (var upload in allUploads) { Assert.AreEqual(ImportantMessage, upload); } await Task.WhenAll(allCompletedTasks).DefaultTimeout(); }
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(); }
private async Task RunConcurrentStreams(bool writeResponseHeaders) { SetExpectedErrorsFilter(writeContext => { return(true); }); var streamCount = 210; var count = 0; var tcs = new TaskCompletionSource <object?>(TaskCreationOptions.RunContinuationsAsynchronously); var connectionIds = new List <string>(); var l = new object(); async Task <HelloReply> UnaryThrowError(HelloRequest request, ServerCallContext context) { lock (l) { count++; var connectionId = context.GetHttpContext().Connection.Id; if (!connectionIds.Contains(connectionId)) { connectionIds.Add(connectionId); } } Logger.LogInformation($"Received message '{request.Name}'"); if (writeResponseHeaders) { await context.WriteResponseHeadersAsync(new Metadata()); } if (count == streamCount) { tcs.SetResult(null); } await tcs.Task; return(new HelloReply { Message = request.Name }); } // Arrange var method = Fixture.DynamicGrpc.AddUnaryMethod <HelloRequest, HelloReply>(UnaryThrowError); var channel = GrpcChannel.ForAddress(Fixture.GetUrl(TestServerEndpointName.Http2), new GrpcChannelOptions { LoggerFactory = LoggerFactory }); var client = TestClientFactory.Create(channel, method); var calls = new AsyncUnaryCall <HelloReply> [streamCount]; try { // Act for (var i = 0; i < calls.Length; i++) { var call = client.UnaryCall(new HelloRequest { Name = (i + 1).ToString() }); calls[i] = call; if (writeResponseHeaders) { await call.ResponseHeadersAsync.DefaultTimeout(); } } await Task.WhenAll(calls.Select(c => c.ResponseHeadersAsync)).DefaultTimeout(); // Assert Assert.AreEqual(3, connectionIds.Count); } catch (Exception ex) { throw new Exception($"Received {count} of {streamCount} on the server. Connection Ids: {string.Join(", ", connectionIds)}", ex); } finally { for (var i = 0; i < calls.Length; i++) { calls[i].Dispose(); } } }
public async Task ServerStreaming_CancellationOnClientWithoutResponseHeaders_CancellationSentToServer() { var syncPoint = new SyncPoint(); var serverCompleteTcs = new TaskCompletionSource <object?>(TaskCreationOptions.RunContinuationsAsynchronously); async Task ServerStreamingCall(DataMessage request, IServerStreamWriter <DataMessage> streamWriter, ServerCallContext context) { await syncPoint.WaitToContinue().DefaultTimeout(); // Wait until the client cancels while (!context.CancellationToken.IsCancellationRequested) { await Task.Delay(TimeSpan.FromMilliseconds(10)); } serverCompleteTcs.TrySetResult(null); } // Arrange SetExpectedErrorsFilter(writeContext => { // Kestrel cancellation error message if (writeContext.Exception is IOException && writeContext.Exception.Message == "The client reset the request stream.") { return(true); } if (writeContext.LoggerName == "Grpc.Net.Client.Internal.GrpcCall" && writeContext.EventId.Name == "GrpcStatusError" && writeContext.Message == "Call failed with gRPC error status. Status code: 'Cancelled', Message: 'Call canceled by the client.'.") { return(true); } if (writeContext.LoggerName == "Grpc.Net.Client.Internal.GrpcCall" && writeContext.EventId.Name == "ErrorStartingCall" && writeContext.Message == "Error starting gRPC call.") { return(true); } // Ignore all logging related errors for now return(false); }); var method = Fixture.DynamicGrpc.AddServerStreamingMethod <DataMessage, DataMessage>(ServerStreamingCall); var channel = CreateChannel(); var cts = new CancellationTokenSource(); var client = TestClientFactory.Create(channel, method); // Act var call = client.ServerStreamingCall(new DataMessage(), new CallOptions(cancellationToken: cts.Token)); await syncPoint.WaitForSyncPoint(); syncPoint.Continue(); // Assert var moveNextTask = call.ResponseStream.MoveNext(CancellationToken.None); cts.Cancel(); var ex = await ExceptionAssert.ThrowsAsync <RpcException>(() => moveNextTask).DefaultTimeout(); Assert.AreEqual(StatusCode.Cancelled, ex.StatusCode); await serverCompleteTcs.Task.DefaultTimeout(); }
public async Task SendValidRequest_ClientAbort_ClientThrowsCancelledException() { var serverAbortedTcs = new TaskCompletionSource <object?>(TaskCreationOptions.RunContinuationsAsynchronously); async Task ServerStreamingEcho(ServerStreamingEchoRequest request, IServerStreamWriter <ServerStreamingEchoResponse> responseStream, ServerCallContext context) { Logger.LogInformation("Server call started"); var httpContext = context.GetHttpContext(); httpContext.RequestAborted.Register(() => serverAbortedTcs.SetResult(null)); for (var i = 0; i < request.MessageCount; i++) { try { Logger.LogInformation($"Server writing message {i}"); await responseStream.WriteAsync(new ServerStreamingEchoResponse { Message = request.Message }); await Task.Delay(request.MessageInterval.ToTimeSpan(), context.CancellationToken); } catch (OperationCanceledException) { return; } } } // Arrage var method = Fixture.DynamicGrpc.AddServerStreamingMethod <ServerStreamingEchoRequest, ServerStreamingEchoResponse>(ServerStreamingEcho); var httpClient = CreateGrpcWebClient(); var channel = GrpcChannel.ForAddress(httpClient.BaseAddress !, new GrpcChannelOptions { HttpClient = httpClient, LoggerFactory = LoggerFactory }); var cts = new CancellationTokenSource(); var client = TestClientFactory.Create(channel, method); // Act var call = client.ServerStreamingCall(new ServerStreamingEchoRequest { Message = "test", MessageCount = 2, MessageInterval = TimeSpan.FromMilliseconds(100).ToDuration() }, new CallOptions(cancellationToken: cts.Token)); // Assert Assert.IsTrue(await call.ResponseStream.MoveNext(CancellationToken.None).DefaultTimeout()); Assert.AreEqual("test", call.ResponseStream.Current.Message); cts.Cancel(); var ex = await ExceptionAssert.ThrowsAsync <RpcException>(() => call.ResponseStream.MoveNext(CancellationToken.None)).DefaultTimeout(); Assert.AreEqual(StatusCode.Cancelled, ex.StatusCode); Assert.AreEqual("Call canceled by the client.", ex.Status.Detail); Assert.AreEqual(StatusCode.Cancelled, call.GetStatus().StatusCode); // It is possible get into a situation where the response stream finishes slightly before the call. // Small delay to ensure call logging is complete. await Task.Delay(50); AssertHasLog(LogLevel.Information, "GrpcStatusError", "Call failed with gRPC error status. Status code: 'Cancelled', Message: 'Call canceled by the client.'."); // HTTP/1.1 doesn't appear to send the cancellation to the server. // This might be because the server has finished reading the response body // and doesn't have a way to get the notification. if (EndpointName != TestServerEndpointName.Http1) { // Verify the abort reached the server. await serverAbortedTcs.Task.DefaultTimeout(); } }
public void SendMessageAsyncPassesOnRemoteException() { var action = "DoSomething"; var request = new ServerRequest { ServerName = "TestServer" }; var url = "http://somewhere/"; var errorMessage = "Oops, an error happened"; var factory = new TestClientFactory((u, a, d) => { throw new CruiseControlException(errorMessage); }); var connection = new HttpConnection(new Uri(url), factory); var completed = false; connection.SendMessageCompleted += (o, e) => { completed = true; Assert.IsFalse(e.Cancelled); Assert.IsNotNull(e.Error); Assert.AreEqual(errorMessage, e.Error.Message); Assert.IsNull(e.Response); }; connection.SendMessageAsync(action, request); Assert.IsTrue(completed); }
public void SendMessageAsyncCanBeCancelled() { var action = "DoSomething"; var request = new ServerRequest { ServerName = "TestServer" }; var url = "http://nowhere"; HttpConnection connection = null; var factory = new TestClientFactory((u, a, d) => { connection.CancelAsync(); return new byte[0]; }); connection = new HttpConnection(new Uri(url), factory); var completed = false; connection.SendMessageCompleted += (o, e) => { completed = true; Assert.IsTrue(e.Cancelled); Assert.IsNull(e.Error); Assert.IsNull(e.Response); }; connection.SendMessageAsync(action, request); Assert.IsTrue(completed); }
public void InvokeApplicationBeforeItsConnection() { RunWith10SecTimeout(async() => { Task <GreetingResponse> HandleAsync(GreetingRequest request, MethodCallContext context) { return(Task.FromResult(new GreetingResponse { Greeting = request.Name + "1" })); } var brokerInstance = _testBrokerFixture.SharedInstance; var createdServersCount = 0; var createdServerClient = new TaskCompletionSource <IClient>(); var echoServerFactory = new TestClientFactory( (broker, id) => { var optionsBuilder = new ClientOptionsBuilder() .WithBrokerWorkingDir(brokerInstance.WorkingDir) .WithAppInstanceId(id) .WithApplicationId(EchoServerClient.Id) .WithDefaultConfiguration() .WithProvidedService( GreetingService.Id, x => x.WithUnaryMethod <GreetingRequest, GreetingResponse>(GreetingService.HelloMethodId, HandleAsync)); var serverClient = ClientFactory.Instance.Create(optionsBuilder.Build()); createdServerClient.SetResult(serverClient); createdServersCount++; return(Task.FromResult(serverClient)); }); var appLauncher = RegisterDisposable( new TestAppLauncher( brokerInstance, new Dictionary <string, TestClientFactory> { { EchoServerClient.Id, echoServerFactory } }, false ) ); await appLauncher.StartAsync(); var client1 = CreateClient <EchoClient>(); await client1.ConnectAsync(); var client2 = CreateClient <EchoClient>(); await client2.ConnectAsync(); var helloTask = client1.GreetingService.Hello(new GreetingRequest { Name = "Test1" }).ResponseAsync; var callUnconnectedServerTask = createdServerClient.Task.ContinueWith(async task => { var serverClient = task.Result; await Task.Delay(TimeSpan.FromSeconds(1)); var providedMethodReference = ProvidedMethodReference.CreateWithAppInstanceId(GreetingService.Id, GreetingService.HelloMethodId, EchoServerClient.Id, serverClient.ApplicationInstanceId); var methodCallDescriptor = new MethodCallDescriptor(providedMethodReference); await client2.CallInvoker.CallUnary <GreetingRequest, GreetingResponse>(methodCallDescriptor, new GreetingRequest() { Name = "Test2" }); }).Unwrap(); var connectedServerAfterDelayTask = createdServerClient.Task.ContinueWith(async task => { var serverClient = task.Result; await Task.Delay(TimeSpan.FromSeconds(3)); serverClient.ConnectionId.ShouldBe(UniqueId.Empty); var onlineConnectionsResponse = await client1.AppLifecycleService.GetConnections(new GetConnectionsRequest { AppInstanceId = serverClient.ApplicationInstanceId }); onlineConnectionsResponse.Connections.Count.ShouldBe(0); await serverClient.ConnectAsync(); }).Unwrap(); await Task.WhenAll(helloTask, callUnconnectedServerTask, connectedServerAfterDelayTask); createdServersCount.ShouldBe(1); }); }
public async Task ServerStreaming_ThrowErrorWithTrailers_TrailersReturnedToClient() { async Task ServerStreamingWithTrailers(DataMessage request, IServerStreamWriter <DataMessage> responseStream, ServerCallContext context) { await context.WriteResponseHeadersAsync(new Metadata { { "Key", "Value1" }, { "Key", "Value2" }, }); await responseStream.WriteAsync(new DataMessage()); await responseStream.WriteAsync(new DataMessage()); await responseStream.WriteAsync(new DataMessage()); await responseStream.WriteAsync(new DataMessage()); context.ResponseTrailers.Add("Key", "ResponseTrailers"); throw new RpcException(new Status(StatusCode.Aborted, "Message"), new Metadata { { "Key", "RpcException" } }); } // Arrange var method = Fixture.DynamicGrpc.AddServerStreamingMethod <DataMessage, DataMessage>(ServerStreamingWithTrailers); var channel = CreateChannel(); var client = TestClientFactory.Create(channel, method); // Act var call = client.ServerStreamingCall(new DataMessage()); // Assert var headers = await call.ResponseHeadersAsync.DefaultTimeout(); var keyHeaders = headers.GetAll("key").ToList(); Assert.AreEqual("key", keyHeaders[0].Key); Assert.AreEqual("Value1", keyHeaders[0].Value); Assert.AreEqual("key", keyHeaders[1].Key); Assert.AreEqual("Value2", keyHeaders[1].Value); Assert.IsTrue(await call.ResponseStream.MoveNext().DefaultTimeout()); Assert.IsTrue(await call.ResponseStream.MoveNext().DefaultTimeout()); Assert.IsTrue(await call.ResponseStream.MoveNext().DefaultTimeout()); Assert.IsTrue(await call.ResponseStream.MoveNext().DefaultTimeout()); var ex = await ExceptionAssert.ThrowsAsync <RpcException>(() => call.ResponseStream.MoveNext()).DefaultTimeout(); var trailers = call.GetTrailers(); Assert.AreEqual(2, trailers.Count); Assert.AreEqual("key", trailers[0].Key); Assert.AreEqual("ResponseTrailers", trailers[0].Value); Assert.AreEqual("key", trailers[1].Key); Assert.AreEqual("RpcException", trailers[1].Value); Assert.AreEqual(StatusCode.Aborted, call.GetStatus().StatusCode); Assert.AreEqual("Message", call.GetStatus().Detail); }
public async Task ServerStreaming_CancellationOnClientAfterResponseHeadersReceived_CancellationSentToServer() { var serverCompleteTcs = new TaskCompletionSource <object?>(TaskCreationOptions.RunContinuationsAsynchronously); async Task ServerStreamingCall(DataMessage request, IServerStreamWriter <DataMessage> streamWriter, ServerCallContext context) { // Write until the client cancels while (!context.CancellationToken.IsCancellationRequested) { await streamWriter.WriteAsync(new DataMessage()); await Task.Delay(TimeSpan.FromMilliseconds(10)); } serverCompleteTcs.TrySetResult(null); } // Arrange SetExpectedErrorsFilter(writeContext => { // Kestrel cancellation error message if (writeContext.Exception is IOException && writeContext.Exception.Message == "The client reset the request stream.") { return(true); } // Cancellation happened after checking token but before writing message if (writeContext.LoggerName == "Grpc.AspNetCore.Server.ServerCallHandler" && writeContext.EventId.Name == "ErrorExecutingServiceMethod" && writeContext.Exception is InvalidOperationException && writeContext.Exception.Message == "Cannot write message after request is complete.") { return(true); } if (writeContext.LoggerName == "Grpc.Net.Client.Internal.GrpcCall" && writeContext.EventId.Name == "GrpcStatusError" && writeContext.Message == "Call failed with gRPC error status. Status code: 'Cancelled', Message: 'Call canceled by the client.'.") { return(true); } // Ignore all logging related errors for now return(false); }); var method = Fixture.DynamicGrpc.AddServerStreamingMethod <DataMessage, DataMessage>(ServerStreamingCall); var channel = CreateChannel(); var cts = new CancellationTokenSource(); var client = TestClientFactory.Create(channel, method); // Act var call = client.ServerStreamingCall(new DataMessage(), new CallOptions(cancellationToken: cts.Token)); // Assert // 1. Lets read some messages Assert.IsTrue(await call.ResponseStream.MoveNext(CancellationToken.None).DefaultTimeout()); Assert.IsTrue(await call.ResponseStream.MoveNext(CancellationToken.None).DefaultTimeout()); // 2. Cancel the token that was passed to the gRPC call. This was given to HttpClient.SendAsync cts.Cancel(); // 3. Read from the response stream. This will throw a cancellation exception locally var ex = await ExceptionAssert.ThrowsAsync <RpcException>(() => call.ResponseStream.MoveNext(CancellationToken.None)).DefaultTimeout(); Assert.AreEqual(StatusCode.Cancelled, ex.StatusCode); // 4. Check that the cancellation was sent to the server. This will await serverCompleteTcs.Task.DefaultTimeout(); }
private async Task RunConcurrentStreams(bool writeResponseHeaders) { var streamCount = 201; var count = 0; var tcs = new TaskCompletionSource <object?>(TaskCreationOptions.RunContinuationsAsynchronously); async Task WaitForAllStreams(IAsyncStreamReader <DataMessage> requestStream, IServerStreamWriter <DataMessage> responseStream, ServerCallContext context) { Interlocked.Increment(ref count); if (writeResponseHeaders) { await context.WriteResponseHeadersAsync(new Metadata()); } if (count == streamCount) { tcs.SetResult(null); } await tcs.Task; } // Arrange var method = Fixture.DynamicGrpc.AddDuplexStreamingMethod <DataMessage, DataMessage>(WaitForAllStreams); var channel = GrpcChannel.ForAddress(Fixture.GetUrl(TestServerEndpointName.Http2)); var client = TestClientFactory.Create(channel, method); var calls = new AsyncDuplexStreamingCall <DataMessage, DataMessage> [streamCount]; try { // Act for (var i = 0; i < calls.Length; i++) { var call = client.DuplexStreamingCall(); calls[i] = call; if (writeResponseHeaders) { await call.ResponseHeadersAsync.DefaultTimeout(); } } // Assert await Task.WhenAll(calls.Select(c => c.ResponseHeadersAsync)).DefaultTimeout(); } catch (Exception ex) { throw new Exception($"Received {count} of {streamCount} on the server.", ex); } finally { for (var i = 0; i < calls.Length; i++) { calls[i].Dispose(); } } }