public async Task AsyncUnaryCall_DisposeDuringBackoff_CanceledStatus() { // Arrange var callCount = 0; var httpClient = ClientTestHelpers.CreateTestClient(async request => { callCount++; await request.Content !.CopyToAsync(new MemoryStream()); return(ResponseUtils.CreateHeadersOnlyResponse(HttpStatusCode.OK, StatusCode.Unavailable, retryPushbackHeader: TimeSpan.FromSeconds(10).TotalMilliseconds.ToString())); }); var serviceConfig = ServiceConfigHelpers.CreateRetryServiceConfig(); var invoker = HttpClientCallInvokerFactory.Create(httpClient, serviceConfig: serviceConfig); var cts = new CancellationTokenSource(); // Act var call = invoker.AsyncUnaryCall <HelloRequest, HelloReply>(ClientTestHelpers.ServiceMethod, string.Empty, new CallOptions(cancellationToken: cts.Token), new HelloRequest { Name = "World" }); var delayTask = Task.Delay(100); var completedTask = await Task.WhenAny(call.ResponseAsync, delayTask); // Assert Assert.AreEqual(delayTask, completedTask); // Ensure that we're waiting for retry call.Dispose(); var ex = await ExceptionAssert.ThrowsAsync <RpcException>(() => call.ResponseAsync).DefaultTimeout(); Assert.AreEqual(StatusCode.Cancelled, ex.StatusCode); Assert.AreEqual("gRPC call disposed.", ex.Status.Detail); }
public async Task AsyncUnaryCall_FatalStatusCode_HedgeDelay_Failure() { // Arrange var callCount = 0; var httpClient = ClientTestHelpers.CreateTestClient(async request => { Interlocked.Increment(ref callCount); await request.Content !.CopyToAsync(new MemoryStream()); return(ResponseUtils.CreateHeadersOnlyResponse(HttpStatusCode.OK, (callCount == 1) ? StatusCode.Unavailable : StatusCode.InvalidArgument)); }); var serviceConfig = ServiceConfigHelpers.CreateHedgingServiceConfig(hedgingDelay: TimeSpan.FromMilliseconds(50)); var invoker = HttpClientCallInvokerFactory.Create(httpClient, serviceConfig: serviceConfig); // Act var call = invoker.AsyncUnaryCall <HelloRequest, HelloReply>(ClientTestHelpers.ServiceMethod, string.Empty, new CallOptions(), new HelloRequest { Name = "World" }); // Assert var ex = await ExceptionAssert.ThrowsAsync <RpcException>(() => call.ResponseAsync).DefaultTimeout(); Assert.AreEqual(StatusCode.InvalidArgument, ex.StatusCode); Assert.AreEqual(StatusCode.InvalidArgument, call.GetStatus().StatusCode); Assert.AreEqual(2, callCount); }
public async Task AsyncUnaryCall_FailureWithLongDelay_Dispose_CallImmediatelyDisposed() { // Arrange var callCount = 0; var httpClient = ClientTestHelpers.CreateTestClient(async request => { callCount++; await request.Content !.CopyToAsync(new MemoryStream()); return(ResponseUtils.CreateHeadersOnlyResponse(HttpStatusCode.OK, StatusCode.Unavailable)); }); // Very long delay var serviceConfig = ServiceConfigHelpers.CreateRetryServiceConfig(initialBackoff: TimeSpan.FromSeconds(30), maxBackoff: TimeSpan.FromSeconds(30)); var invoker = HttpClientCallInvokerFactory.Create(httpClient, serviceConfig: serviceConfig); // Act var call = invoker.AsyncUnaryCall <HelloRequest, HelloReply>(ClientTestHelpers.ServiceMethod, string.Empty, new CallOptions(), new HelloRequest { Name = "World" }); var resultTask = call.ResponseAsync; // Test will timeout if dispose doesn't kill the timer. call.Dispose(); // Assert var ex = await ExceptionAssert.ThrowsAsync <RpcException>(() => resultTask).DefaultTimeout(); Assert.AreEqual(StatusCode.Cancelled, ex.StatusCode); Assert.AreEqual("gRPC call disposed.", ex.Status.Detail); }
public async Task AsyncUnaryCall_ExceedRetryAttempts_Failure() { // Arrange var callCount = 0; var httpClient = ClientTestHelpers.CreateTestClient(async request => { callCount++; await request.Content !.CopyToAsync(new MemoryStream()); return(ResponseUtils.CreateHeadersOnlyResponse(HttpStatusCode.OK, StatusCode.Unavailable)); }); var serviceConfig = ServiceConfigHelpers.CreateRetryServiceConfig(maxAttempts: 3); var invoker = HttpClientCallInvokerFactory.Create(httpClient, serviceConfig: serviceConfig); // Act var call = invoker.AsyncUnaryCall <HelloRequest, HelloReply>(ClientTestHelpers.ServiceMethod, string.Empty, new CallOptions(), new HelloRequest { Name = "World" }); // Assert Assert.AreEqual(3, callCount); var ex = await ExceptionAssert.ThrowsAsync <RpcException>(() => call.ResponseAsync).DefaultTimeout(); Assert.AreEqual(StatusCode.Unavailable, ex.StatusCode); Assert.AreEqual(StatusCode.Unavailable, call.GetStatus().StatusCode); }
public async Task AsyncUnaryCall_SuccessAfterRetry_AccessResponseHeaders_SuccessfullyResponseHeadersReturned() { // Arrange HttpContent?content = null; var syncPoint = new SyncPoint(runContinuationsAsynchronously: true); var callCount = 0; var httpClient = ClientTestHelpers.CreateTestClient(async request => { callCount++; content = request.Content !; if (callCount == 1) { await content.CopyToAsync(new MemoryStream()); await syncPoint.WaitForSyncPoint(); return(ResponseUtils.CreateHeadersOnlyResponse( HttpStatusCode.OK, StatusCode.Unavailable, customHeaders: new Dictionary <string, string> { ["call-count"] = callCount.ToString() })); } syncPoint.Continue(); var reply = new HelloReply { Message = "Hello world" }; var streamContent = await ClientTestHelpers.CreateResponseContent(reply).DefaultTimeout(); return(ResponseUtils.CreateResponse( HttpStatusCode.OK, streamContent, customHeaders: new Dictionary <string, string> { ["call-count"] = callCount.ToString() })); }); var serviceConfig = ServiceConfigHelpers.CreateRetryServiceConfig(); var invoker = HttpClientCallInvokerFactory.Create(httpClient, serviceConfig: serviceConfig); // Act var call = invoker.AsyncUnaryCall <HelloRequest, HelloReply>(ClientTestHelpers.ServiceMethod, string.Empty, new CallOptions(), new HelloRequest { Name = "World" }); var headersTask = call.ResponseHeadersAsync; // Wait until the first call has failed and the second is on the server await syncPoint.WaitToContinue().DefaultTimeout(); // Assert Assert.AreEqual(2, callCount); Assert.AreEqual("Hello world", (await call.ResponseAsync.DefaultTimeout()).Message); var headers = await headersTask.DefaultTimeout(); Assert.AreEqual("2", headers.GetValue("call-count")); }
public async Task AsyncUnaryCall_SuccessTrailersOnly_ThrowNoMessageError() { // Arrange HttpResponseMessage?responseMessage = null; var httpClient = ClientTestHelpers.CreateTestClient(request => { responseMessage = ResponseUtils.CreateHeadersOnlyResponse(HttpStatusCode.OK, StatusCode.OK, customHeaders: new Dictionary <string, string> { [GrpcProtocolConstants.MessageTrailer] = "Detail!" }); return(Task.FromResult(responseMessage)); }); var invoker = HttpClientCallInvokerFactory.Create(httpClient); // Act var call = invoker.AsyncUnaryCall <HelloRequest, HelloReply>(ClientTestHelpers.ServiceMethod, string.Empty, new CallOptions(), new HelloRequest()); var headers = await call.ResponseHeadersAsync.DefaultTimeout(); var ex = await ExceptionAssert.ThrowsAsync <RpcException>(() => call.ResponseAsync).DefaultTimeout(); // Assert Assert.NotNull(responseMessage); Assert.IsFalse(responseMessage !.TrailingHeaders().Any()); // sanity check that there are no trailers Assert.AreEqual(StatusCode.Internal, ex.Status.StatusCode); Assert.AreEqual("Failed to deserialize response message.", ex.Status.Detail); Assert.AreEqual(StatusCode.Internal, call.GetStatus().StatusCode); Assert.AreEqual("Failed to deserialize response message.", call.GetStatus().Detail); Assert.AreEqual(0, headers.Count); Assert.AreEqual(0, call.GetTrailers().Count); }
public async Task AsyncServerStreamingCall_SuccessAfterRetry_RequestContentSent() { // Arrange var syncPoint = new SyncPoint(runContinuationsAsynchronously: true); MemoryStream?requestContent = null; var callCount = 0; var httpClient = ClientTestHelpers.CreateTestClient(async request => { Interlocked.Increment(ref callCount); var s = await request.Content !.ReadAsStreamAsync(); var ms = new MemoryStream(); await s.CopyToAsync(ms); if (callCount == 1) { await syncPoint.WaitForSyncPoint(); await request.Content !.CopyToAsync(new MemoryStream()); return(ResponseUtils.CreateHeadersOnlyResponse(HttpStatusCode.OK, StatusCode.Unavailable)); } syncPoint.Continue(); requestContent = ms; var reply = new HelloReply { Message = "Hello world" }; var streamContent = await ClientTestHelpers.CreateResponseContent(reply).DefaultTimeout(); return(ResponseUtils.CreateResponse(HttpStatusCode.OK, streamContent)); }); var serviceConfig = ServiceConfigHelpers.CreateHedgingServiceConfig(maxAttempts: 2, hedgingDelay: TimeSpan.FromMilliseconds(50)); var invoker = HttpClientCallInvokerFactory.Create(httpClient, serviceConfig: serviceConfig); // Act var call = invoker.AsyncServerStreamingCall <HelloRequest, HelloReply>(ClientTestHelpers.GetServiceMethod(MethodType.ServerStreaming), string.Empty, new CallOptions(), new HelloRequest { Name = "World" }); var moveNextTask = call.ResponseStream.MoveNext(CancellationToken.None); // Wait until the first call has failed and the second is on the server await syncPoint.WaitToContinue().DefaultTimeout(); // Assert Assert.IsTrue(await moveNextTask); Assert.AreEqual("Hello world", call.ResponseStream.Current.Message); requestContent !.Seek(0, SeekOrigin.Begin); var requestMessage = await ReadRequestMessage(requestContent).DefaultTimeout(); Assert.AreEqual("World", requestMessage !.Name); }
public async Task AsyncUnaryCall_ExceedAttempts_Failure() { // Arrange var tcs = new TaskCompletionSource <object?>(TaskCreationOptions.RunContinuationsAsynchronously); var requestMessages = new List <HelloRequest>(); var callCount = 0; var httpClient = ClientTestHelpers.CreateTestClient(async request => { // All calls are in-progress at once. Interlocked.Increment(ref callCount); if (callCount == 5) { tcs.TrySetResult(null); } await tcs.Task; var requestContent = await request.Content !.ReadAsStreamAsync(); var requestMessage = await ReadRequestMessage(requestContent); lock (requestMessages) { requestMessages.Add(requestMessage !); } return(ResponseUtils.CreateHeadersOnlyResponse(HttpStatusCode.OK, StatusCode.Unavailable)); }); var serviceConfig = ServiceConfigHelpers.CreateHedgingServiceConfig(); var invoker = HttpClientCallInvokerFactory.Create(httpClient, serviceConfig: serviceConfig); // Act var call = invoker.AsyncUnaryCall <HelloRequest, HelloReply>(ClientTestHelpers.ServiceMethod, string.Empty, new CallOptions(), new HelloRequest { Name = "World" }); // Assert Assert.AreEqual(5, callCount); var ex = await ExceptionAssert.ThrowsAsync <RpcException>(() => call.ResponseAsync).DefaultTimeout(); Assert.AreEqual(StatusCode.Unavailable, ex.StatusCode); Assert.AreEqual(StatusCode.Unavailable, call.GetStatus().StatusCode); Assert.AreEqual(5, requestMessages.Count); foreach (var requestMessage in requestMessages) { Assert.AreEqual("World", requestMessage.Name); } }
public async Task AsyncUnaryCall_NonOkStatusTrailer_AccessHeaders_ReturnHeaders() { // Arrange var httpClient = ClientTestHelpers.CreateTestClient(request => { var response = ResponseUtils.CreateHeadersOnlyResponse(HttpStatusCode.OK, StatusCode.Unimplemented, customHeaders: new Dictionary <string, string> { ["custom"] = "true" }); return(Task.FromResult(response)); }); var invoker = HttpClientCallInvokerFactory.Create(httpClient); // Act var headers = await invoker.AsyncUnaryCall <HelloRequest, HelloReply>(ClientTestHelpers.ServiceMethod, string.Empty, new CallOptions(), new HelloRequest()).ResponseHeadersAsync.DefaultTimeout(); // Assert Assert.AreEqual("true", headers.GetValue("custom")); }
private static async Task StatusReturnedCore(StatusInterceptor interceptor, StatusCode statusCode) { // Arrange var httpClient = ClientTestHelpers.CreateTestClient(async request => { if (statusCode == StatusCode.OK) { var streamContent = await ClientTestHelpers.CreateResponseContent(new HelloReply { Message = "PASS" }).DefaultTimeout(); return(ResponseUtils.CreateResponse(HttpStatusCode.OK, streamContent)); } else { return(ResponseUtils.CreateHeadersOnlyResponse(HttpStatusCode.OK, statusCode)); } }); var invoker = HttpClientCallInvokerFactory.Create(httpClient); // Act var callInvoker = invoker.Intercept(interceptor); var call = callInvoker.AsyncUnaryCall(ClientTestHelpers.ServiceMethod, Host, new CallOptions(), new HelloRequest()); // Assert if (statusCode == StatusCode.OK) { var result = await call.ResponseAsync.DefaultTimeout(); Assert.AreEqual("PASS", result.Message); } else { var ex = await ExceptionAssert.ThrowsAsync <RpcException>(() => call.ResponseAsync).DefaultTimeout(); Assert.AreEqual(statusCode, ex.StatusCode); } var interceptorStatusCode = await interceptor.GetStatusCodeAsync().DefaultTimeout(); Assert.AreEqual(statusCode, interceptorStatusCode); }
public async Task AsyncUnaryCall_ExceedAttempts_PusbackDelay_Failure() { // Arrange var stopwatch = new Stopwatch(); var callIntervals = new List <long>(); var hedgeDelay = TimeSpan.FromMilliseconds(100); const int timerResolutionMs = 15 * 2; // Timer has a precision of about 15ms. Double it, just to be safe var callCount = 0; var httpClient = ClientTestHelpers.CreateTestClient(async request => { callIntervals.Add(stopwatch.ElapsedMilliseconds); stopwatch.Restart(); Interlocked.Increment(ref callCount); await request.Content !.CopyToAsync(new MemoryStream()); return(ResponseUtils.CreateHeadersOnlyResponse(HttpStatusCode.OK, StatusCode.Unavailable, retryPushbackHeader: hedgeDelay.TotalMilliseconds.ToString(CultureInfo.InvariantCulture))); }); var serviceConfig = ServiceConfigHelpers.CreateHedgingServiceConfig(maxAttempts: 2, hedgingDelay: hedgeDelay); var invoker = HttpClientCallInvokerFactory.Create(httpClient, serviceConfig: serviceConfig); // Act stopwatch.Start(); var call = invoker.AsyncUnaryCall <HelloRequest, HelloReply>(ClientTestHelpers.ServiceMethod, string.Empty, new CallOptions(), new HelloRequest { Name = "World" }); // Assert var ex = await ExceptionAssert.ThrowsAsync <RpcException>(() => call.ResponseAsync).DefaultTimeout(); Assert.AreEqual(2, callCount); Assert.AreEqual(StatusCode.Unavailable, ex.StatusCode); Assert.AreEqual(StatusCode.Unavailable, call.GetStatus().StatusCode); // First call should happen immediately Assert.LessOrEqual(callIntervals[0], hedgeDelay.TotalMilliseconds); // Second call should happen after delay Assert.GreaterOrEqual(callIntervals[1], hedgeDelay.TotalMilliseconds - timerResolutionMs); }
public async Task AsyncUnaryCall_PushbackDelay_PushbackDelayUpdatesNextCallDelay() { // Arrange var stopwatch = new Stopwatch(); var callIntervals = new List <long>(); var hedgingDelay = TimeSpan.FromSeconds(10); var callCount = 0; var httpClient = ClientTestHelpers.CreateTestClient(async request => { callIntervals.Add(stopwatch.ElapsedMilliseconds); stopwatch.Restart(); Interlocked.Increment(ref callCount); await request.Content !.CopyToAsync(new MemoryStream()); var hedgingPushback = hedgingDelay.TotalMilliseconds.ToString(CultureInfo.InvariantCulture); if (callCount == 1) { hedgingPushback = "0"; } return(ResponseUtils.CreateHeadersOnlyResponse(HttpStatusCode.OK, StatusCode.Unavailable, retryPushbackHeader: hedgingPushback)); }); var serviceConfig = ServiceConfigHelpers.CreateHedgingServiceConfig(maxAttempts: 5, hedgingDelay: hedgingDelay); var invoker = HttpClientCallInvokerFactory.Create(httpClient, serviceConfig: serviceConfig); // Act stopwatch.Start(); var call = invoker.AsyncUnaryCall <HelloRequest, HelloReply>(ClientTestHelpers.ServiceMethod, string.Empty, new CallOptions(), new HelloRequest { Name = "World" }); // Assert await TestHelpers.AssertIsTrueRetryAsync(() => callIntervals.Count == 2, "Only two calls should be made.").DefaultTimeout(); // First call should happen immediately Assert.LessOrEqual(callIntervals[0], 100); // Second call should happen after delay Assert.LessOrEqual(callIntervals[1], hedgingDelay.TotalMilliseconds); }
public async Task AsyncUnaryCall_PushbackExpicitDelay_DelayForSpecifiedDuration() { // Arrange Task?delayTask = null; var callCount = 0; var httpClient = ClientTestHelpers.CreateTestClient(async request => { callCount++; if (callCount == 1) { await request.Content !.CopyToAsync(new MemoryStream()); delayTask = Task.Delay(100); return(ResponseUtils.CreateHeadersOnlyResponse(HttpStatusCode.OK, StatusCode.Unavailable, retryPushbackHeader: "200")); } var reply = new HelloReply { Message = "Hello world" }; var streamContent = await ClientTestHelpers.CreateResponseContent(reply).DefaultTimeout(); return(ResponseUtils.CreateResponse(HttpStatusCode.OK, streamContent)); }); var serviceConfig = ServiceConfigHelpers.CreateRetryServiceConfig(backoffMultiplier: 1); var invoker = HttpClientCallInvokerFactory.Create(httpClient, serviceConfig: serviceConfig); // Act var call = invoker.AsyncUnaryCall <HelloRequest, HelloReply>(ClientTestHelpers.ServiceMethod, string.Empty, new CallOptions(), new HelloRequest { Name = "World" }); // Delay of 100ms will finish before second record which has a pushback delay of 200ms var completedTask = await Task.WhenAny(call.ResponseAsync, delayTask !).DefaultTimeout(); var rs = await call.ResponseAsync.DefaultTimeout(); // Assert Assert.AreEqual(delayTask, completedTask); // Response task should finish after Assert.AreEqual(2, callCount); Assert.AreEqual("Hello world", rs.Message); }
public async Task AsyncUnaryCall_ManyAttemptsNoDelay_MarshallerCalledOnce() { // Arrange var callCount = 0; var httpClient = ClientTestHelpers.CreateTestClient(async request => { Interlocked.Increment(ref callCount); await request.Content !.CopyToAsync(new MemoryStream()); return(ResponseUtils.CreateHeadersOnlyResponse(HttpStatusCode.OK, StatusCode.Unavailable)); }); var serviceConfig = ServiceConfigHelpers.CreateHedgingServiceConfig(); var invoker = HttpClientCallInvokerFactory.Create(httpClient, serviceConfig: serviceConfig); var marshallerCount = 0; var requestMarshaller = Marshallers.Create <HelloRequest>( r => { Interlocked.Increment(ref marshallerCount); return(r.ToByteArray()); }, data => HelloRequest.Parser.ParseFrom(data)); var method = ClientTestHelpers.GetServiceMethod(requestMarshaller: requestMarshaller); // Act var call = invoker.AsyncUnaryCall <HelloRequest, HelloReply>(method, string.Empty, new CallOptions(), new HelloRequest { Name = "World" }); // Assert var ex = await ExceptionAssert.ThrowsAsync <RpcException>(() => call.ResponseAsync).DefaultTimeout(); Assert.AreEqual(StatusCode.Unavailable, ex.StatusCode); Assert.AreEqual(StatusCode.Unavailable, call.GetStatus().StatusCode); Assert.AreEqual(5, callCount); Assert.AreEqual(1, marshallerCount); }
public async Task AsyncUnaryCall_NoMessagesSuccess_Failure() { // Arrange var testSink = new TestSink(); var services = new ServiceCollection(); services.AddLogging(b => { b.AddProvider(new TestLoggerProvider(testSink)); }); services.AddNUnitLogger(); var provider = services.BuildServiceProvider(); var httpClient = ClientTestHelpers.CreateTestClient(async request => { var content = request.Content !; await content.CopyToAsync(new MemoryStream()); return(ResponseUtils.CreateHeadersOnlyResponse(HttpStatusCode.OK, StatusCode.OK)); }); var serviceConfig = ServiceConfigHelpers.CreateRetryServiceConfig(); var invoker = HttpClientCallInvokerFactory.Create(httpClient, loggerFactory: provider.GetRequiredService <ILoggerFactory>(), serviceConfig: serviceConfig); // Act var call = invoker.AsyncUnaryCall <HelloRequest, HelloReply>(ClientTestHelpers.GetServiceMethod(MethodType.Unary), string.Empty, new CallOptions(), new HelloRequest { Name = "World" }); var ex = await ExceptionAssert.ThrowsAsync <RpcException>(() => call.ResponseAsync).DefaultTimeout(); // Assert Assert.AreEqual("Failed to deserialize response message.", ex.Status.Detail); Assert.AreEqual(StatusCode.Internal, ex.StatusCode); var log = testSink.Writes.Single(w => w.EventId.Name == "CallCommited"); Assert.AreEqual("Call commited. Reason: FatalStatusCode", log.State.ToString()); }
public async Task AsyncServerStreamingCall_NoMessagesSuccess_SuccussCommitLogged() { // Arrange var testSink = new TestSink(); var services = new ServiceCollection(); services.AddLogging(b => { b.AddProvider(new TestLoggerProvider(testSink)); }); services.AddNUnitLogger(); var provider = services.BuildServiceProvider(); var httpClient = ClientTestHelpers.CreateTestClient(async request => { var content = request.Content !; await content.CopyToAsync(new MemoryStream()); return(ResponseUtils.CreateHeadersOnlyResponse(HttpStatusCode.OK, StatusCode.OK)); }); var serviceConfig = ServiceConfigHelpers.CreateRetryServiceConfig(); var invoker = HttpClientCallInvokerFactory.Create(httpClient, loggerFactory: provider.GetRequiredService <ILoggerFactory>(), serviceConfig: serviceConfig); // Act var call = invoker.AsyncServerStreamingCall <HelloRequest, HelloReply>(ClientTestHelpers.GetServiceMethod(MethodType.ServerStreaming), string.Empty, new CallOptions(), new HelloRequest { Name = "World" }); var moveNextTask = call.ResponseStream.MoveNext(CancellationToken.None); // Assert Assert.IsFalse(await moveNextTask); var log = testSink.Writes.Single(w => w.EventId.Name == "CallCommited"); Assert.AreEqual("Call commited. Reason: ResponseHeadersReceived", log.State.ToString()); }
public async Task AsyncDuplexStreamingCall_SuccessAfterRetry_RequestContentSent() { // Arrange var requestContent = new MemoryStream(); var syncPoint = new SyncPoint(runContinuationsAsynchronously: true); var callCount = 0; var httpClient = ClientTestHelpers.CreateTestClient(async request => { callCount++; var content = (PushStreamContent <HelloRequest, HelloReply>)request.Content !; if (callCount == 1) { _ = content.CopyToAsync(new MemoryStream()); await syncPoint.WaitForSyncPoint(); return(ResponseUtils.CreateHeadersOnlyResponse(HttpStatusCode.OK, StatusCode.Unavailable)); } syncPoint.Continue(); await content.PushComplete.DefaultTimeout(); await content.CopyToAsync(requestContent); requestContent.Seek(0, SeekOrigin.Begin); var reply = new HelloReply { Message = "Hello world" }; var streamContent = await ClientTestHelpers.CreateResponseContent(reply).DefaultTimeout(); return(ResponseUtils.CreateResponse(HttpStatusCode.OK, streamContent)); }); var serviceConfig = ServiceConfigHelpers.CreateRetryServiceConfig(); var invoker = HttpClientCallInvokerFactory.Create(httpClient, serviceConfig: serviceConfig); // Act var call = invoker.AsyncDuplexStreamingCall <HelloRequest, HelloReply>(ClientTestHelpers.GetServiceMethod(MethodType.DuplexStreaming), string.Empty, new CallOptions()); var moveNextTask = call.ResponseStream.MoveNext(CancellationToken.None); await call.RequestStream.WriteAsync(new HelloRequest { Name = "1" }).DefaultTimeout(); // Wait until the first call has failed and the second is on the server await syncPoint.WaitToContinue().DefaultTimeout(); await call.RequestStream.WriteAsync(new HelloRequest { Name = "2" }).DefaultTimeout(); await call.RequestStream.CompleteAsync().DefaultTimeout(); // Assert Assert.IsTrue(await moveNextTask.DefaultTimeout()); Assert.AreEqual("Hello world", call.ResponseStream.Current.Message); var requestMessage = await ReadRequestMessage(requestContent).DefaultTimeout(); Assert.AreEqual("1", requestMessage !.Name); requestMessage = await ReadRequestMessage(requestContent).DefaultTimeout(); Assert.AreEqual("2", requestMessage !.Name); requestMessage = await ReadRequestMessage(requestContent).DefaultTimeout(); Assert.IsNull(requestMessage); }
public async Task AsyncUnaryCall_SuccessAfterRetry_RequestContentSent() { // Arrange HttpContent?content = null; bool? firstRequestPreviousAttemptsHeader = null; string?secondRequestPreviousAttemptsHeaderValue = null; var requestContent = new MemoryStream(); var callCount = 0; var httpClient = ClientTestHelpers.CreateTestClient(async request => { callCount++; content = request.Content !; await content.CopyToAsync(requestContent); requestContent.Seek(0, SeekOrigin.Begin); if (callCount == 1) { firstRequestPreviousAttemptsHeader = request.Headers.TryGetValues(GrpcProtocolConstants.RetryPreviousAttemptsHeader, out _); return(ResponseUtils.CreateHeadersOnlyResponse(HttpStatusCode.OK, StatusCode.Unavailable)); } if (request.Headers.TryGetValues(GrpcProtocolConstants.RetryPreviousAttemptsHeader, out var retryAttemptCountValue)) { secondRequestPreviousAttemptsHeaderValue = retryAttemptCountValue.Single(); } var reply = new HelloReply { Message = "Hello world" }; var streamContent = await ClientTestHelpers.CreateResponseContent(reply).DefaultTimeout(); return(ResponseUtils.CreateResponse(HttpStatusCode.OK, streamContent, customTrailers: new Dictionary <string, string> { ["custom-trailer"] = "Value!" })); }); var serviceConfig = ServiceConfigHelpers.CreateRetryServiceConfig(); var invoker = HttpClientCallInvokerFactory.Create(httpClient, serviceConfig: serviceConfig); // Act var call = invoker.AsyncUnaryCall <HelloRequest, HelloReply>(ClientTestHelpers.ServiceMethod, string.Empty, new CallOptions(), new HelloRequest { Name = "World" }); // Assert Assert.AreEqual(2, callCount); Assert.AreEqual("Hello world", (await call.ResponseAsync.DefaultTimeout()).Message); Assert.AreEqual("1", (await call.ResponseHeadersAsync.DefaultTimeout()).GetValue(GrpcProtocolConstants.RetryPreviousAttemptsHeader)); Assert.IsNotNull(content); var requestMessage = await ReadRequestMessage(requestContent).DefaultTimeout(); Assert.AreEqual("World", requestMessage !.Name); Assert.IsFalse(firstRequestPreviousAttemptsHeader); Assert.AreEqual("1", secondRequestPreviousAttemptsHeaderValue); var trailers = call.GetTrailers(); Assert.AreEqual("Value!", trailers.GetValue("custom-trailer")); }
public async Task AsyncClientStreamingCall_SuccessAfterRetry_RequestContentSent(int hedgingDelayMS) { // Arrange var callLock = new object(); var requestContent = new MemoryStream(); var callCount = 0; var httpClient = ClientTestHelpers.CreateTestClient(async request => { var firstCall = false; lock (callLock) { callCount++; if (callCount == 1) { firstCall = true; } } if (firstCall) { await request.Content !.CopyToAsync(new MemoryStream()); return(ResponseUtils.CreateHeadersOnlyResponse(HttpStatusCode.OK, StatusCode.Unavailable)); } var content = (PushStreamContent <HelloRequest, HelloReply>)request.Content !; await content.PushComplete.DefaultTimeout(); await request.Content !.CopyToAsync(requestContent); var reply = new HelloReply { Message = "Hello world" }; var streamContent = await ClientTestHelpers.CreateResponseContent(reply).DefaultTimeout(); return(ResponseUtils.CreateResponse(HttpStatusCode.OK, streamContent)); }); var serviceConfig = ServiceConfigHelpers.CreateHedgingServiceConfig(maxAttempts: 2, hedgingDelay: TimeSpan.FromMilliseconds(hedgingDelayMS)); var invoker = HttpClientCallInvokerFactory.Create(httpClient, serviceConfig: serviceConfig); // Act var call = invoker.AsyncClientStreamingCall <HelloRequest, HelloReply>(ClientTestHelpers.GetServiceMethod(MethodType.ClientStreaming), string.Empty, new CallOptions()); // Assert Assert.IsNotNull(call); var responseTask = call.ResponseAsync; Assert.IsFalse(responseTask.IsCompleted, "Response not returned until client stream is complete."); await call.RequestStream.WriteAsync(new HelloRequest { Name = "1" }).DefaultTimeout(); await call.RequestStream.WriteAsync(new HelloRequest { Name = "2" }).DefaultTimeout(); await call.RequestStream.CompleteAsync().DefaultTimeout(); var responseMessage = await responseTask.DefaultTimeout(); Assert.AreEqual("Hello world", responseMessage.Message); requestContent.Seek(0, SeekOrigin.Begin); var requests = new List <HelloRequest>(); while (true) { var requestMessage = await ReadRequestMessage(requestContent).DefaultTimeout(); if (requestMessage == null) { break; } requests.Add(requestMessage); } Assert.AreEqual(2, requests.Count); Assert.AreEqual("1", requests[0].Name); Assert.AreEqual("2", requests[1].Name); }
public async Task AsyncClientStreamingCall_SuccessAfterRetry_RequestContentSent() { // Arrange var requestContent = new MemoryStream(); var callCount = 0; var httpClient = ClientTestHelpers.CreateTestClient(async request => { Interlocked.Increment(ref callCount); var currentContent = new MemoryStream(); await request.Content !.CopyToAsync(currentContent); if (callCount == 1) { return(ResponseUtils.CreateHeadersOnlyResponse(HttpStatusCode.OK, StatusCode.Unavailable)); } currentContent.Seek(0, SeekOrigin.Begin); await currentContent.CopyToAsync(requestContent); var reply = new HelloReply { Message = "Hello world" }; var streamContent = await ClientTestHelpers.CreateResponseContent(reply).DefaultTimeout(); return(ResponseUtils.CreateResponse(HttpStatusCode.OK, streamContent)); }); var serviceConfig = ServiceConfigHelpers.CreateRetryServiceConfig(); var invoker = HttpClientCallInvokerFactory.Create(httpClient, serviceConfig: serviceConfig); // Act var call = invoker.AsyncClientStreamingCall <HelloRequest, HelloReply>(ClientTestHelpers.GetServiceMethod(MethodType.ClientStreaming), string.Empty, new CallOptions()); // Assert Assert.IsNotNull(call); var responseTask = call.ResponseAsync; Assert.IsFalse(responseTask.IsCompleted, "Response not returned until client stream is complete."); await call.RequestStream.WriteAsync(new HelloRequest { Name = "1" }).DefaultTimeout(); await call.RequestStream.WriteAsync(new HelloRequest { Name = "2" }).DefaultTimeout(); await call.RequestStream.CompleteAsync().DefaultTimeout(); var responseMessage = await responseTask.DefaultTimeout(); Assert.AreEqual("Hello world", responseMessage.Message); requestContent.Seek(0, SeekOrigin.Begin); var requests = new List <HelloRequest>(); while (true) { var requestMessage = await ReadRequestMessage(requestContent).DefaultTimeout(); if (requestMessage == null) { break; } requests.Add(requestMessage); } Assert.AreEqual(2, requests.Count); Assert.AreEqual("1", requests[0].Name); Assert.AreEqual("2", requests[1].Name); call.Dispose(); }