public async Task AsyncUnaryCall_Http3ServerResetsCancelCodeAfterDeadline_DeadlineStatus() { // Arrange var services = new ServiceCollection(); services.AddNUnitLogger(); var serviceProvider = services.BuildServiceProvider(); var syncPoint = new SyncPoint(runContinuationsAsynchronously: true); var httpClient = ClientTestHelpers.CreateTestClient(async request => { await syncPoint.WaitToContinue(); throw new QuicStreamAbortedException("Stream aborted by peer (268)."); }); var testSystemClock = new TestSystemClock(DateTime.UtcNow); var invoker = HttpClientCallInvokerFactory.Create( httpClient, systemClock: testSystemClock, loggerFactory: serviceProvider.GetRequiredService <ILoggerFactory>()); var deadline = invoker.Channel.Clock.UtcNow.AddSeconds(1); // Act var responseTask = invoker.AsyncUnaryCall <HelloRequest, HelloReply>(ClientTestHelpers.ServiceMethod, string.Empty, new CallOptions(deadline: deadline), new HelloRequest()).ResponseAsync; await syncPoint.WaitForSyncPoint().DefaultTimeout(); testSystemClock.UtcNow = deadline; syncPoint.Continue(); // Assert var ex = await ExceptionAssert.ThrowsAsync <RpcException>(() => responseTask).DefaultTimeout(); Assert.AreEqual(StatusCode.DeadlineExceeded, ex.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_AuthInterceptorSuccess_ResponseHeadersPopulated() { // Arrange var httpClient = ClientTestHelpers.CreateTestClient(async request => { var reply = new HelloReply { Message = "Hello world" }; var streamContent = await ClientTestHelpers.CreateResponseContent(reply).DefaultTimeout(); var response = ResponseUtils.CreateResponse(HttpStatusCode.OK, streamContent); response.Headers.Add("custom", "ABC"); return(response); }); var credentialsSyncPoint = new SyncPoint(runContinuationsAsynchronously: true); var credentials = CallCredentials.FromInterceptor(async(context, metadata) => { await credentialsSyncPoint.WaitToContinue(); metadata.Add("Authorization", $"Bearer TEST"); }); var invoker = HttpClientCallInvokerFactory.Create(httpClient, configure: options => options.Credentials = ChannelCredentials.Create(new SslCredentials(), credentials)); // Act var call = invoker.AsyncUnaryCall <HelloRequest, HelloReply>(ClientTestHelpers.ServiceMethod, string.Empty, new CallOptions(), new HelloRequest()); var responseHeadersTask = call.ResponseHeadersAsync; await credentialsSyncPoint.WaitForSyncPoint().DefaultTimeout(); credentialsSyncPoint.Continue(); var responseHeaders = await responseHeadersTask.DefaultTimeout(); // Assert Assert.AreEqual("ABC", responseHeaders.GetValue("custom")); }
public async Task ServerStreaming_WriteAfterMethodComplete_Error(bool writeBeforeExit) { var tcs = new TaskCompletionSource <object?>(TaskCreationOptions.RunContinuationsAsynchronously); var writeTcs = new TaskCompletionSource <Task>(TaskCreationOptions.RunContinuationsAsynchronously); var syncPoint = new SyncPoint(runContinuationsAsynchronously: true); async Task ServerStreamingWithTrailers(DataMessage request, IServerStreamWriter <DataMessage> responseStream, ServerCallContext context) { var writeTask = Task.Run(async() => { if (writeBeforeExit) { await responseStream.WriteAsync(new DataMessage()); } await syncPoint.WaitToContinue(); await responseStream.WriteAsync(new DataMessage()); }); writeTcs.SetResult(writeTask); await tcs.Task; } // 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()); await syncPoint.WaitForSyncPoint().DefaultTimeout(); tcs.SetResult(null); // Assert if (writeBeforeExit) { Assert.IsTrue(await call.ResponseStream.MoveNext().DefaultTimeout()); } Assert.IsFalse(await call.ResponseStream.MoveNext().DefaultTimeout()); Assert.AreEqual(StatusCode.OK, call.GetStatus().StatusCode); syncPoint.Continue(); var writeTask = await writeTcs.Task.DefaultTimeout(); var ex = await ExceptionAssert.ThrowsAsync <InvalidOperationException>(() => writeTask).DefaultTimeout(); Assert.AreEqual("Can't write the message because the request is complete.", ex.Message); Assert.IsFalse(await call.ResponseStream.MoveNext()); }
public async Task <ReadOnlyMemory <byte> > WaitForDataAsync() { await _syncPoint.WaitForSyncPoint(); ResetSyncPointAndContinuePrevious(); //await _awaiter(); return(_currentWriteData); }
public async Task AsyncUnaryCall_AuthInteceptorDispose_Error() { // 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 reply = new HelloReply { Message = "Hello world" }; var streamContent = await ClientTestHelpers.CreateResponseContent(reply).DefaultTimeout(); return(ResponseUtils.CreateResponse(HttpStatusCode.OK, streamContent)); }); var serviceConfig = ServiceConfigHelpers.CreateRetryServiceConfig(); var credentialsSyncPoint = new SyncPoint(runContinuationsAsynchronously: true); var credentials = CallCredentials.FromInterceptor(async(context, metadata) => { await credentialsSyncPoint.WaitToContinue(); metadata.Add("Authorization", $"Bearer TEST"); }); var invoker = HttpClientCallInvokerFactory.Create(httpClient, loggerFactory: provider.GetRequiredService <ILoggerFactory>(), serviceConfig: serviceConfig, configure: options => options.Credentials = ChannelCredentials.Create(new SslCredentials(), credentials)); // Act var call = invoker.AsyncUnaryCall <HelloRequest, HelloReply>(ClientTestHelpers.ServiceMethod, string.Empty, new CallOptions(), new HelloRequest { Name = "World" }); var responseTask = call.ResponseAsync; var responseHeadersTask = call.ResponseHeadersAsync; await credentialsSyncPoint.WaitForSyncPoint().DefaultTimeout(); call.Dispose(); // Assert var ex = await ExceptionAssert.ThrowsAsync <RpcException>(() => responseTask).DefaultTimeout(); Assert.AreEqual(StatusCode.Cancelled, ex.StatusCode); ex = await ExceptionAssert.ThrowsAsync <RpcException>(() => responseHeadersTask).DefaultTimeout(); Assert.AreEqual(StatusCode.Cancelled, ex.StatusCode); var write = testSink.Writes.Single(w => w.EventId.Name == "CallCommited"); Assert.AreEqual("Call commited. Reason: Canceled", write.State.ToString()); }
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 GET_ServerAbort_ClientReceivesAbort(HttpProtocols protocol) { // Arrange var syncPoint = new SyncPoint(); var cancelledTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); var writeAsyncTask = new TaskCompletionSource <Task>(TaskCreationOptions.RunContinuationsAsynchronously); var builder = CreateHostBuilder(async context => { context.RequestAborted.Register(() => cancelledTcs.SetResult()); context.Abort(); // Sync with client await syncPoint.WaitToContinue(); writeAsyncTask.SetResult(context.Response.Body.WriteAsync(TestData).AsTask()); }, protocol: protocol); using (var host = builder.Build()) using (var client = CreateClient()) { await host.StartAsync().DefaultTimeout(); var request = new HttpRequestMessage(HttpMethod.Get, $"https://127.0.0.1:{host.GetPort()}/"); request.Version = GetProtocol(protocol); request.VersionPolicy = HttpVersionPolicy.RequestVersionExact; // Act var ex = await Assert.ThrowsAnyAsync <HttpRequestException>(() => client.SendAsync(request, CancellationToken.None)).DefaultTimeout(); // Assert if (protocol == HttpProtocols.Http3) { var innerEx = Assert.IsType <QuicStreamAbortedException>(ex.InnerException); Assert.Equal(258, innerEx.ErrorCode); } await cancelledTcs.Task.DefaultTimeout(); // Sync with server to ensure RequestDelegate is still running await syncPoint.WaitForSyncPoint().DefaultTimeout(); syncPoint.Continue(); var serverWriteTask = await writeAsyncTask.Task.DefaultTimeout(); await serverWriteTask.DefaultTimeout(); await host.StopAsync().DefaultTimeout(); } }
public async Task ClientStreaming_ServerAbort() { // Arrange var requestStreamSyncPoint = new SyncPoint(); var responseEndingSyncPoint = new SyncPoint(); RequestDelegate appDelegate = async ctx => { // Send headers await ctx.Response.BodyWriter.FlushAsync(); ctx.Abort(); await responseEndingSyncPoint.WaitToContinue(); }; Stream requestStream = null; var builder = new WebHostBuilder().Configure(app => app.Run(appDelegate)); var server = new TestServer(builder); var client = server.CreateClient(); var httpRequest = new HttpRequestMessage(HttpMethod.Post, "http://localhost:12345"); httpRequest.Version = new Version(2, 0); httpRequest.Content = new PushContent(async stream => { requestStream = stream; await requestStreamSyncPoint.WaitToContinue(); }); // Act var response = await client.SendAsync(httpRequest, HttpCompletionOption.ResponseHeadersRead).WithTimeout(); var responseContent = await response.Content.ReadAsStreamAsync().WithTimeout(); // Assert // Ensure server has aborted await responseEndingSyncPoint.WaitForSyncPoint(); // Ensure request stream has started await requestStreamSyncPoint.WaitForSyncPoint(); // Send content and finish request body await ExceptionAssert.ThrowsAsync <OperationCanceledException>( () => requestStream.WriteAsync(Encoding.UTF8.GetBytes("Hello world")).AsTask(), "Flush was canceled on underlying PipeWriter.").WithTimeout(); responseEndingSyncPoint.Continue(); requestStreamSyncPoint.Continue(); }
public async Task Unary_RetryThrottlingBecomesActive_HasDelay_Failure() { var callCount = 0; var syncPoint = new SyncPoint(runContinuationsAsynchronously: true); async Task <DataMessage> UnaryFailure(DataMessage request, ServerCallContext context) { Interlocked.Increment(ref callCount); await syncPoint.WaitToContinue(); return(request); } // Arrange var method = Fixture.DynamicGrpc.AddUnaryMethod <DataMessage, DataMessage>(UnaryFailure); var channel = CreateChannel(serviceConfig: ServiceConfigHelpers.CreateHedgingServiceConfig( hedgingDelay: TimeSpan.FromMilliseconds(100), retryThrottling: new RetryThrottlingPolicy { MaxTokens = 5, TokenRatio = 0.1 })); var client = TestClientFactory.Create(channel, method); // Act var call = client.UnaryCall(new DataMessage()); await syncPoint.WaitForSyncPoint().DefaultTimeout(); // Manually trigger retry throttling Debug.Assert(channel.RetryThrottling != null); channel.RetryThrottling.CallFailure(); channel.RetryThrottling.CallFailure(); channel.RetryThrottling.CallFailure(); Debug.Assert(channel.RetryThrottling.IsRetryThrottlingActive()); // Assert await TestHelpers.AssertIsTrueRetryAsync(() => HasLog(LogLevel.Debug, "AdditionalCallsBlockedByRetryThrottling", "Additional calls blocked by retry throttling."), "Check for expected log."); Assert.AreEqual(1, callCount); syncPoint.Continue(); await call.ResponseAsync.DefaultTimeout(); Assert.AreEqual(StatusCode.OK, call.GetStatus().StatusCode); AssertHasLog(LogLevel.Debug, "CallCommited", "Call commited. Reason: ResponseHeadersReceived"); }
public async Task HandleCallAsync_WriteMultipleMessages_Returned() { // Arrange var syncPoint = new SyncPoint(); ServerStreamingServerMethod <JsonTranscodingGreeterService, HelloRequest, HelloReply> invoker = async(s, r, w, c) => { await w.WriteAsync(new HelloReply { Message = $"Hello {r.Name} 1" }); await syncPoint.WaitToContinue(); await w.WriteAsync(new HelloReply { Message = $"Hello {r.Name} 2" }); }; var pipe = new Pipe(); var routeParameterDescriptors = new Dictionary <string, List <FieldDescriptor> > { ["name"] = new List <FieldDescriptor>(new[] { HelloRequest.Descriptor.FindFieldByNumber(HelloRequest.NameFieldNumber) }) }; var descriptorInfo = TestHelpers.CreateDescriptorInfo(routeParameterDescriptors: routeParameterDescriptors); var callHandler = CreateCallHandler(invoker, descriptorInfo: descriptorInfo); var httpContext = TestHelpers.CreateHttpContext(bodyStream: pipe.Writer.AsStream()); httpContext.Request.RouteValues["name"] = "TestName!"; // Act var callTask = callHandler.HandleCallAsync(httpContext); // Assert Assert.Equal(200, httpContext.Response.StatusCode); Assert.Equal("application/json; charset=utf-8", httpContext.Response.ContentType); var line1 = await ReadLineAsync(pipe.Reader).DefaultTimeout(); using var responseJson1 = JsonDocument.Parse(line1 !); Assert.Equal("Hello TestName! 1", responseJson1.RootElement.GetProperty("message").GetString()); await syncPoint.WaitForSyncPoint().DefaultTimeout(); syncPoint.Continue(); var line2 = await ReadLineAsync(pipe.Reader).DefaultTimeout(); using var responseJson2 = JsonDocument.Parse(line2 !); Assert.Equal("Hello TestName! 2", responseJson2.RootElement.GetProperty("message").GetString()); await callTask.DefaultTimeout(); }
public async Task Unary_MultipleLargeMessages_ExceedChannelMaxBufferSize() { // Arrange var sp1 = new SyncPoint(runContinuationsAsynchronously: true); var sp2 = new SyncPoint(runContinuationsAsynchronously: true); var sp3 = new SyncPoint(runContinuationsAsynchronously: true); var channel = CreateChannel( serviceConfig: ServiceConfigHelpers.CreateRetryServiceConfig(), maxRetryBufferSize: 200, maxRetryBufferPerCallSize: 100); var request = new DataMessage { Data = ByteString.CopyFrom(new byte[90]) }; // Act var call1Task = MakeCall(Fixture, channel, request, sp1); await sp1.WaitForSyncPoint(); var call2Task = MakeCall(Fixture, channel, request, sp2); await sp2.WaitForSyncPoint(); // Will exceed channel buffer limit and won't retry var call3Task = MakeCall(Fixture, channel, request, sp3); await sp3.WaitForSyncPoint(); // Assert Assert.AreEqual(194, channel.CurrentRetryBufferSize); sp1.Continue(); sp2.Continue(); sp3.Continue(); var response = await call1Task.DefaultTimeout(); Assert.AreEqual(90, response.Data.Length); response = await call2Task.DefaultTimeout(); Assert.AreEqual(90, response.Data.Length); // Can't retry because buffer size exceeded. var ex = await ExceptionAssert.ThrowsAsync <RpcException>(() => call3Task).DefaultTimeout(); Assert.AreEqual(StatusCode.Unavailable, ex.StatusCode); Assert.AreEqual(0, channel.CurrentRetryBufferSize);
public override async Task <int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) { // Still have leftover data? if (_currentData.Length > 0) { return(ReadInternalBuffer(buffer, offset, count)); } cancellationToken.Register(() => { _syncPoint.CancelWaitForSyncPoint(cancellationToken); }); // Wait until data is provided by AddDataAndWait await _syncPoint.WaitForSyncPoint(); return(ReadInternalBuffer(buffer, offset, count)); }
public async Task CanCancelStartingConnectionAfterNegotiate() { using (StartVerifiableLog()) { // Set up a SyncPoint within Negotiate, so we can verify // that the call has gotten that far var negotiateSyncPoint = new SyncPoint(); var testHttpHandler = new TestHttpMessageHandler(autoNegotiate: false); testHttpHandler.OnNegotiate(async(request, cancellationToken) => { // Wait here for the test code to cancel the "outer" token await negotiateSyncPoint.WaitToContinue().DefaultTimeout(); // Cancel cancellationToken.ThrowIfCancellationRequested(); return(ResponseUtils.CreateResponse(HttpStatusCode.OK)); }); await WithConnectionAsync( CreateConnection(testHttpHandler), async (connection) => { // Kick off StartAsync, but don't wait for it var cts = new CancellationTokenSource(); var startTask = connection.StartAsync(cts.Token); // Wait for the connection to get to the "WaitToContinue" call above, // which means it has gotten to Negotiate await negotiateSyncPoint.WaitForSyncPoint().DefaultTimeout(); // Assert that StartAsync has not yet been canceled Assert.False(startTask.IsCanceled); // Cancel StartAsync, then "release" the SyncPoint // so the negotiate handler can keep going cts.Cancel(); negotiateSyncPoint.Continue(); // Assert that StartAsync was canceled await Assert.ThrowsAsync <TaskCanceledException>(() => startTask).DefaultTimeout(); }); } }
public async Task UnaryMethod_CancelCall_PollingCountersUpdatedCorrectly() { // Loop to ensure test is resilent across multiple runs for (int i = 1; i < 3; i++) { var syncPoint = new SyncPoint(); var cts = new CancellationTokenSource(); // Ignore errors SetExpectedErrorsFilter(writeContext => { return(true); }); async Task <HelloReply> UnaryCancel(HelloRequest request, ServerCallContext context) { await syncPoint.WaitToContinue().DefaultTimeout(); return(new HelloReply()); } // Arrange var clock = new TestSystemClock(DateTime.UtcNow); 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>(UnaryCancel); var channel = CreateChannel(); var client = TestClientFactory.Create(channel, method); var call = client.UnaryCall(new HelloRequest(), new CallOptions(cancellationToken: cts.Token)); // Assert - Call in progress await AssertCounters("Server call in progress", serverEventListener, new Dictionary <string, long> { ["current-calls"] = 1, ["calls-failed"] = i - 1, ["calls-deadline-exceeded"] = 0, }).DefaultTimeout(); await AssertCounters("Client call in progress", clientEventListener, new Dictionary <string, long> { ["current-calls"] = 1, ["calls-failed"] = i - 1, ["calls-deadline-exceeded"] = 0, }).DefaultTimeout(); // Act - Wait for call to deadline on server await syncPoint.WaitForSyncPoint().DefaultTimeout(); cts.Cancel(); syncPoint.Continue(); var ex = await ExceptionAssert.ThrowsAsync <RpcException>(() => call.ResponseHeadersAsync).DefaultTimeout(); Assert.AreEqual(StatusCode.Cancelled, ex.StatusCode); // Assert - Call complete await AssertCounters("Server call in complete", serverEventListener, new Dictionary <string, long> { ["current-calls"] = 0, ["calls-failed"] = i, ["calls-deadline-exceeded"] = 0, }).DefaultTimeout(); await AssertCounters("Client call complete", clientEventListener, new Dictionary <string, long> { ["current-calls"] = 0, ["calls-failed"] = i, ["calls-deadline-exceeded"] = 0, }).DefaultTimeout(); } }
public async Task ClientStreaming_ReadAfterMethodCancelled_Error(bool readBeforeExit) { SetExpectedErrorsFilter(writeContext => { if (writeContext.LoggerName == "Grpc.Net.Client.Internal.GrpcCall" && (writeContext.Exception is TaskCanceledException || writeContext.Exception is HttpRequestException)) { return(true); } if (writeContext.LoggerName == "Grpc.Net.Client.Internal.HttpContentClientStreamWriter" && writeContext.Exception is InvalidOperationException) { return(true); } return(false); }); var tcs = new TaskCompletionSource <object?>(TaskCreationOptions.RunContinuationsAsynchronously); var readTcs = new TaskCompletionSource <Task>(TaskCreationOptions.RunContinuationsAsynchronously); var syncPoint = new SyncPoint(runContinuationsAsynchronously: true); async Task <DataMessage> ClientStreamingWithTrailers(IAsyncStreamReader <DataMessage> requestStream, ServerCallContext context) { var readTask = Task.Run(async() => { if (readBeforeExit) { Assert.IsTrue(await requestStream.MoveNext()); } await syncPoint.WaitToContinue(); context.GetHttpContext().Abort(); Assert.IsFalse(await requestStream.MoveNext()); }); readTcs.SetResult(readTask); await tcs.Task; return(new DataMessage()); } // Arrange var method = Fixture.DynamicGrpc.AddClientStreamingMethod <DataMessage, DataMessage>(ClientStreamingWithTrailers); var channel = CreateChannel(); var client = TestClientFactory.Create(channel, method); // Act var call = client.ClientStreamingCall(); // Assert if (readBeforeExit) { await call.RequestStream.WriteAsync(new DataMessage()).DefaultTimeout(); } await syncPoint.WaitForSyncPoint().DefaultTimeout(); syncPoint.Continue(); var readTask = await readTcs.Task.DefaultTimeout(); var serverException = await ExceptionAssert.ThrowsAsync <InvalidOperationException>(() => readTask).DefaultTimeout(); Assert.AreEqual("Can't read messages after the request is complete.", serverException.Message); // Ensure the server abort reaches the client await Task.Delay(100); var clientException = await ExceptionAssert.ThrowsAsync <RpcException>(() => call.RequestStream.WriteAsync(new DataMessage())).DefaultTimeout(); Assert.AreEqual(StatusCode.Internal, clientException.StatusCode); }
public async Task GET_RequestReturnsLargeData_GracefulShutdownDuringRequest_RequestGracefullyCompletes(bool hasTrailers) { // Enable client logging. // Test failure on CI could be from HttpClient bug. using var httpEventSource = new HttpEventSourceListener(LoggerFactory); // Arrange const int DataLength = 500_000; var randomBytes = Enumerable.Range(1, DataLength).Select(i => (byte)((i % 10) + 48)).ToArray(); var syncPoint = new SyncPoint(); ILogger logger = null; var builder = CreateHostBuilder( async c => { await syncPoint.WaitToContinue(); var memory = c.Response.BodyWriter.GetMemory(randomBytes.Length); logger.LogInformation($"Server writing {randomBytes.Length} bytes response"); randomBytes.CopyTo(memory); // It's important for this test that the large write is the last data written to // the response and it's not awaited by the request delegate. logger.LogInformation($"Server advancing {randomBytes.Length} bytes response"); c.Response.BodyWriter.Advance(randomBytes.Length); if (hasTrailers) { c.Response.AppendTrailer("test-trailer", "value!"); } }, protocol: HttpProtocols.Http2, plaintext: true); using var host = builder.Build(); logger = host.Services.GetRequiredService <ILoggerFactory>().CreateLogger("Test"); var client = HttpHelpers.CreateClient(); // Act await host.StartAsync().DefaultTimeout(); var longRunningTask = StartLongRunningRequestAsync(logger, host, client); logger.LogInformation("Waiting for request on server"); await syncPoint.WaitForSyncPoint().DefaultTimeout(); logger.LogInformation("Stopping server"); var stopTask = host.StopAsync(); syncPoint.Continue(); var(readData, trailers) = await longRunningTask.DefaultTimeout(); await stopTask.DefaultTimeout(); // Assert Assert.Equal(randomBytes, readData); if (hasTrailers) { Assert.Equal("value!", trailers.GetValues("test-trailer").Single()); } }
public async Task ClientStreaming_Cancellation() { // Arrange var responseStartedSyncPoint = new SyncPoint(); var responseReadSyncPoint = new SyncPoint(); var responseEndingSyncPoint = new SyncPoint(); var requestStreamSyncPoint = new SyncPoint(); var readCanceled = false; RequestDelegate appDelegate = async ctx => { // Send headers await ctx.Response.BodyWriter.FlushAsync(); // Ensure headers received by client await responseStartedSyncPoint.WaitToContinue(); var serverBuffer = new byte[1024]; var serverLength = await ctx.Request.Body.ReadAsync(serverBuffer); Assert.Equal("SENT", Encoding.UTF8.GetString(serverBuffer, 0, serverLength)); await responseReadSyncPoint.WaitToContinue(); try { await ctx.Request.Body.ReadAsync(serverBuffer); } catch (OperationCanceledException) { readCanceled = true; } await responseEndingSyncPoint.WaitToContinue(); }; Stream requestStream = null; var builder = new WebHostBuilder().Configure(app => app.Run(appDelegate)); var server = new TestServer(builder); var client = server.CreateClient(); var httpRequest = new HttpRequestMessage(HttpMethod.Post, "http://localhost:12345"); httpRequest.Version = new Version(2, 0); httpRequest.Content = new PushContent(async stream => { requestStream = stream; await requestStreamSyncPoint.WaitToContinue(); }); // Act var response = await client.SendAsync(httpRequest, HttpCompletionOption.ResponseHeadersRead).WithTimeout(); await responseStartedSyncPoint.WaitForSyncPoint().WithTimeout(); responseStartedSyncPoint.Continue(); var responseContent = await response.Content.ReadAsStreamAsync().WithTimeout(); // Assert // Ensure request stream has started await requestStreamSyncPoint.WaitForSyncPoint(); // Write to request await requestStream.WriteAsync(Encoding.UTF8.GetBytes("SENT")).AsTask().WithTimeout(); await requestStream.FlushAsync().WithTimeout(); await responseReadSyncPoint.WaitForSyncPoint().WithTimeout(); // Cancel request. Disposing response must be used because SendAsync has finished. response.Dispose(); responseReadSyncPoint.Continue(); await responseEndingSyncPoint.WaitForSyncPoint().WithTimeout(); responseEndingSyncPoint.Continue(); Assert.True(readCanceled); requestStreamSyncPoint.Continue(); }
public async Task ServerStreaming_CancellationOnClientWhileMoveNext_CancellationSentToServer() { var pauseServerTcs = new TaskCompletionSource <object?>(TaskCreationOptions.RunContinuationsAsynchronously); var callEndSyncPoint = new SyncPoint(); var serverCancellationRequested = false; async Task ServerStreamingCall(DataMessage request, IServerStreamWriter <DataMessage> streamWriter, ServerCallContext context) { await streamWriter.WriteAsync(new DataMessage()); await streamWriter.WriteAsync(new DataMessage()); await pauseServerTcs.Task.DefaultTimeout(); while (!context.CancellationToken.IsCancellationRequested) { await Task.Delay(10); } serverCancellationRequested = context.CancellationToken.IsCancellationRequested; await callEndSyncPoint.WaitToContinue(); } 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 should dispose HttpResponseMessage cts.CancelAfter(TimeSpan.FromSeconds(0.2)); // 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. pauseServerTcs.TrySetResult(null); await callEndSyncPoint.WaitForSyncPoint().DefaultTimeout(); callEndSyncPoint.Continue(); Assert.AreEqual(true, serverCancellationRequested); await TestHelpers.AssertIsTrueRetryAsync( () => HasLog(LogLevel.Information, "GrpcStatusError", "Call failed with gRPC error status. Status code: 'Cancelled', Message: 'Call canceled by the client.'."), "Missing client cancellation log.").DefaultTimeout(); }
public async Task HealthService_RemoveDefault_DefaultNotFound() { // Arrange var healthCheckResult = new HealthCheckResult(HealthStatus.Healthy); var services = new ServiceCollection(); services.AddLogging(); services .AddGrpcHealthChecks(o => { o.Services.Clear(); o.Services.MapService("new", result => true); }) .AddAsyncCheck("", () => Task.FromResult(healthCheckResult)); services.Configure <HealthCheckPublisherOptions>(o => { o.Delay = TimeSpan.FromSeconds(1); o.Period = TimeSpan.FromSeconds(1); }); HealthReport?report = null; var syncPoint = new SyncPoint(runContinuationsAsynchronously: true); var testPublisher = new TestHealthCheckPublisher(); testPublisher.OnHealthReport = async r => { report = r; await syncPoint.WaitToContinue(); }; services.AddSingleton <IHealthCheckPublisher>(testPublisher); var serviceProvider = services.BuildServiceProvider(); var healthService = serviceProvider.GetRequiredService <HealthServiceImpl>(); var hostedService = serviceProvider.GetServices <IHostedService>().Single(); async Task CheckForStatusAsync(string service, HealthCheckResponse.Types.ServingStatus status) { var context = new TestServerCallContext(DateTime.MaxValue, CancellationToken.None); var result = await healthService !.Check(new HealthCheckRequest() { Service = service }, context); Assert.AreEqual(status, result.Status); } // Act await hostedService.StartAsync(CancellationToken.None); // Assert try { await syncPoint.WaitForSyncPoint().DefaultTimeout(); Assert.AreEqual(HealthStatus.Healthy, report !.Status); syncPoint.Continue(); await ExceptionAssert.ThrowsAsync <RpcException>(() => CheckForStatusAsync(service : "", HealthCheckResponse.Types.ServingStatus.ServiceUnknown)); await CheckForStatusAsync(service : "new", HealthCheckResponse.Types.ServingStatus.Serving); } finally { await hostedService.StopAsync(CancellationToken.None); } }
public async Task HealthService_Watch_WriteResults() { // Arrange var healthCheckResult = new HealthCheckResult(HealthStatus.Healthy); var services = new ServiceCollection(); services.AddLogging(); services.AddGrpcHealthChecks().AddAsyncCheck( "", () => Task.FromResult(healthCheckResult), new string[] { "sample" }); services.Configure <HealthCheckPublisherOptions>(o => { o.Delay = TimeSpan.FromSeconds(1); o.Period = TimeSpan.FromSeconds(1); }); var serviceProvider = services.BuildServiceProvider(); var healthService = serviceProvider.GetRequiredService <HealthServiceImpl>(); var hostedService = serviceProvider.GetServices <IHostedService>().Single(); HealthCheckResponse?response = null; var syncPoint = new SyncPoint(runContinuationsAsynchronously: true); var testServerStreamWriter = new TestServerStreamWriter <HealthCheckResponse>(); testServerStreamWriter.OnWriteAsync = async message => { response = message; await syncPoint.WaitToContinue(); }; var cts = new CancellationTokenSource(); var callTask = healthService.Watch( new HealthCheckRequest(), testServerStreamWriter, new TestServerCallContext(DateTime.MaxValue, cts.Token)); // Act await hostedService.StartAsync(CancellationToken.None); // Assert try { await syncPoint.WaitForSyncPoint().DefaultTimeout(); Assert.AreEqual(HealthCheckResponse.Types.ServingStatus.ServiceUnknown, response !.Status); syncPoint.Continue(); healthCheckResult = new HealthCheckResult(HealthStatus.Unhealthy); syncPoint = new SyncPoint(runContinuationsAsynchronously: true); await syncPoint.WaitForSyncPoint().DefaultTimeout(); Assert.AreEqual(HealthCheckResponse.Types.ServingStatus.NotServing, response !.Status); syncPoint.Continue(); healthCheckResult = new HealthCheckResult(HealthStatus.Healthy); syncPoint = new SyncPoint(runContinuationsAsynchronously: true); await syncPoint.WaitForSyncPoint().DefaultTimeout(); Assert.AreEqual(HealthCheckResponse.Types.ServingStatus.Serving, response !.Status); syncPoint.Continue(); cts.Cancel(); await callTask.DefaultTimeout(); } finally { await hostedService.StopAsync(CancellationToken.None); } }
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 POST_ClientCancellationUpload_RequestAbortRaised(HttpProtocols protocol) { // Arrange var syncPoint = new SyncPoint(); var cancelledTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); var readAsyncTask = new TaskCompletionSource <Task>(TaskCreationOptions.RunContinuationsAsynchronously); var builder = CreateHostBuilder(async context => { context.RequestAborted.Register(() => { Logger.LogInformation("Server received cancellation"); cancelledTcs.SetResult(); }); var body = context.Request.Body; Logger.LogInformation("Server reading content"); await body.ReadAtLeastLengthAsync(TestData.Length).DefaultTimeout(); // Sync with client await syncPoint.WaitToContinue(); Logger.LogInformation("Server waiting for cancellation"); await cancelledTcs.Task; readAsyncTask.SetResult(body.ReadAsync(new byte[1024]).AsTask()); }, protocol: protocol); using (var host = builder.Build()) using (var client = CreateClient()) { await host.StartAsync().DefaultTimeout(); var cts = new CancellationTokenSource(); var requestContent = new StreamingHttpContext(); var request = new HttpRequestMessage(HttpMethod.Post, $"https://127.0.0.1:{host.GetPort()}/"); request.Content = requestContent; request.Version = GetProtocol(protocol); request.VersionPolicy = HttpVersionPolicy.RequestVersionExact; // Act var responseTask = client.SendAsync(request, cts.Token); var requestStream = await requestContent.GetStreamAsync().DefaultTimeout(); Logger.LogInformation("Client sending request headers"); await requestStream.FlushAsync().DefaultTimeout(); Logger.LogInformation("Client sending request content"); await requestStream.WriteAsync(TestData).DefaultTimeout(); await requestStream.FlushAsync().DefaultTimeout(); Logger.LogInformation("Client waiting until content is read on server"); await syncPoint.WaitForSyncPoint().DefaultTimeout(); Logger.LogInformation("Client cancelling"); cts.Cancel(); // Continue on server syncPoint.Continue(); // Assert await Assert.ThrowsAnyAsync <OperationCanceledException>(() => responseTask).DefaultTimeout(); await cancelledTcs.Task.DefaultTimeout(); var serverWriteTask = await readAsyncTask.Task.DefaultTimeout(); await Assert.ThrowsAnyAsync <Exception>(() => serverWriteTask).DefaultTimeout(); await host.StopAsync().DefaultTimeout(); } }
public async Task ServerStreaming_WriteAfterMethodCancelled_Error(bool writeBeforeExit) { SetExpectedErrorsFilter(writeContext => { if (writeContext.LoggerName == "Grpc.Net.Client.Internal.GrpcCall" && (writeContext.Exception is TaskCanceledException || writeContext.Exception is HttpRequestException)) { return(true); } if (writeContext.LoggerName == "Grpc.Net.Client.Internal.GrpcCall" && writeContext.EventId.Name == "ErrorReadingMessage") { return(true); } return(false); }); var tcs = new TaskCompletionSource <object?>(TaskCreationOptions.RunContinuationsAsynchronously); var syncPoint = new SyncPoint(runContinuationsAsynchronously: true); Task?writeTask = null; async Task ServerStreamingWithTrailers(DataMessage request, IServerStreamWriter <DataMessage> responseStream, ServerCallContext context) { writeTask = Task.Run(async() => { if (writeBeforeExit) { await responseStream.WriteAsync(new DataMessage()); } await syncPoint.WaitToContinue(); context.GetHttpContext().Abort(); await responseStream.WriteAsync(new DataMessage()); }); await tcs.Task; } // 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()); await syncPoint.WaitForSyncPoint().DefaultTimeout(); // Assert if (writeBeforeExit) { Assert.IsTrue(await call.ResponseStream.MoveNext().DefaultTimeout()); } syncPoint.Continue(); var serverException = await ExceptionAssert.ThrowsAsync <InvalidOperationException>(() => writeTask !).DefaultTimeout(); Assert.AreEqual("Can't write the message because the request is complete.", serverException.Message); // Ensure the server abort reaches the client await Task.Delay(100); var clientException = await ExceptionAssert.ThrowsAsync <RpcException>(() => call.ResponseStream.MoveNext()).DefaultTimeout(); Assert.AreEqual(StatusCode.Unavailable, clientException.StatusCode); }
public async Task ConnectAsync_ShiftThroughStates_CompleteOnReady() { // Arrange var syncPoint = new SyncPoint(runContinuationsAsynchronously: true); var currentConnectivityState = ConnectivityState.TransientFailure; var services = new ServiceCollection(); services.AddSingleton <ResolverFactory, ChannelTestResolverFactory>(); services.AddSingleton <ISubchannelTransportFactory>(new TestSubchannelTransportFactory(async(s, c) => { await syncPoint.WaitToContinue(); return(currentConnectivityState); })); var handler = new TestHttpMessageHandler(); var channelOptions = new GrpcChannelOptions { ServiceProvider = services.BuildServiceProvider(), HttpHandler = handler }; // Act var channel = GrpcChannel.ForAddress("https://localhost", channelOptions); var waitForStateTask = WaitForStateAsync(channel, ConnectivityState.Connecting); // Assert Assert.IsFalse(waitForStateTask.IsCompleted); var connectTask = channel.ConnectAsync(); Assert.IsFalse(connectTask.IsCompleted); await waitForStateTask.DefaultTimeout(); waitForStateTask = WaitForStateAsync(channel, ConnectivityState.TransientFailure); await syncPoint.WaitForSyncPoint().DefaultTimeout(); syncPoint.Continue(); syncPoint = new SyncPoint(runContinuationsAsynchronously: true); await waitForStateTask.DefaultTimeout(); waitForStateTask = WaitForStateAsync(channel, ConnectivityState.Ready); Assert.IsFalse(connectTask.IsCompleted); await syncPoint.WaitForSyncPoint().DefaultTimeout(); currentConnectivityState = ConnectivityState.Ready; syncPoint.Continue(); syncPoint = new SyncPoint(runContinuationsAsynchronously: true); await connectTask.DefaultTimeout(); await waitForStateTask.DefaultTimeout(); Assert.AreEqual(ConnectivityState.Ready, channel.State); waitForStateTask = WaitForStateAsync(channel, ConnectivityState.Ready); channel.Dispose(); await waitForStateTask.DefaultTimeout(); Assert.AreEqual(ConnectivityState.Shutdown, channel.State); }
public async Task UnaryMethod_DeadlineExceededCall_PollingCountersUpdatedCorrectly() { // Loop to ensure test is resilent across multiple runs for (var i = 1; i < 3; i++) { Logger.LogInformation($"Iteration {i}"); var syncPoint = new SyncPoint(); // Ignore errors SetExpectedErrorsFilter(writeContext => { return(true); }); async Task <HelloReply> UnaryDeadlineExceeded(HelloRequest request, ServerCallContext context) { Logger.LogInformation("On server."); await PollAssert(() => context.Status.StatusCode == StatusCode.DeadlineExceeded).DefaultTimeout(); await syncPoint.WaitToContinue().DefaultTimeout(); 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); using var channel = CreateChannel(); // Force client to handle deadline status from call channel.DisableClientDeadline = true; var client = TestClientFactory.Create(channel, method); // Need a high deadline to avoid flakiness. No way to disable server deadline timer. var deadline = DateTime.UtcNow.AddMilliseconds(500); var call = client.UnaryCall(new HelloRequest(), new CallOptions(deadline: deadline)); // Assert - Call in progress await AssertCounters("Server call in progress", serverEventListener, new Dictionary <string, long> { ["calls-failed"] = i - 1, ["calls-deadline-exceeded"] = i - 1, }).DefaultTimeout(); await AssertCounters("Client call in progress", clientEventListener, new Dictionary <string, long> { ["calls-failed"] = i - 1, ["calls-deadline-exceeded"] = i - 1, }).DefaultTimeout(); // Act - Wait for call to deadline on server await syncPoint.WaitForSyncPoint().DefaultTimeout(); syncPoint.Continue(); var ex = await ExceptionAssert.ThrowsAsync <RpcException>(() => call.ResponseAsync).DefaultTimeout(); Assert.AreEqual(StatusCode.DeadlineExceeded, ex.StatusCode); // Assert - Call complete await AssertCounters("Server call in complete", serverEventListener, new Dictionary <string, long> { ["current-calls"] = 0, ["calls-failed"] = i, ["calls-deadline-exceeded"] = i, }).DefaultTimeout(); await AssertCounters("Client call complete", clientEventListener, new Dictionary <string, long> { ["current-calls"] = 0, ["calls-failed"] = i, ["calls-deadline-exceeded"] = i, }).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(); }
public async Task SSETransportCancelsSendOnStop() { var eventStreamTcs = new TaskCompletionSource(); var readTcs = new TaskCompletionSource(); var sendSyncPoint = new SyncPoint(); var mockHttpHandler = new Mock <HttpMessageHandler>(); mockHttpHandler.Protected() .Setup <Task <HttpResponseMessage> >("SendAsync", ItExpr.IsAny <HttpRequestMessage>(), ItExpr.IsAny <CancellationToken>()) .Returns <HttpRequestMessage, CancellationToken>(async(request, cancellationToken) => { await Task.Yield(); if (request.Headers.Accept?.Contains(new MediaTypeWithQualityHeaderValue("text/event-stream")) == true) { // Receive loop started - allow stopping the transport eventStreamTcs.SetResult(); // returns unfinished task to block pipelines var mockStream = new Mock <Stream>(); mockStream .Setup(s => s.ReadAsync(It.IsAny <Memory <byte> >(), It.IsAny <CancellationToken>())) .Returns(async() => { await readTcs.Task; throw new TaskCanceledException(); }); mockStream.Setup(s => s.CanRead).Returns(true); return(new HttpResponseMessage { Content = new StreamContent(mockStream.Object) }); } // Throw TaskCanceledException from SSE send's SendAsync on stop cancellationToken.Register(s => ((SyncPoint)s).Continue(), sendSyncPoint); await sendSyncPoint.WaitToContinue(); throw new TaskCanceledException(); }); using (var httpClient = new HttpClient(mockHttpHandler.Object)) using (StartVerifiableLog()) { var sseTransport = new ServerSentEventsTransport(httpClient, loggerFactory: LoggerFactory); await sseTransport.StartAsync( new Uri("http://fakeuri.org"), TransferFormat.Text).DefaultTimeout(); await eventStreamTcs.Task; await sseTransport.Output.WriteAsync(new byte[] { 0x42 }); // For send request to be in progress await sendSyncPoint.WaitForSyncPoint(); var stopTask = sseTransport.StopAsync(); readTcs.SetResult(); sendSyncPoint.Continue(); await stopTask; } }
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 ClientStreamingWorks() { // Arrange var responseStartedSyncPoint = new SyncPoint(); var requestEndingSyncPoint = new SyncPoint(); var requestStreamSyncPoint = new SyncPoint(); RequestDelegate appDelegate = async ctx => { // Send headers await ctx.Response.BodyWriter.FlushAsync(); // Ensure headers received by client await responseStartedSyncPoint.WaitToContinue(); await ctx.Response.WriteAsync("STARTED"); // ReadToEndAsync will wait until request body is complete var requestString = await new StreamReader(ctx.Request.Body).ReadToEndAsync(); await ctx.Response.WriteAsync(requestString + " POST Response"); await requestEndingSyncPoint.WaitToContinue(); }; Stream requestStream = null; var builder = new WebHostBuilder().Configure(app => app.Run(appDelegate)); var server = new TestServer(builder); var client = server.CreateClient(); var httpRequest = new HttpRequestMessage(HttpMethod.Post, "http://localhost:12345"); httpRequest.Version = new Version(2, 0); httpRequest.Content = new PushContent(async stream => { requestStream = stream; await requestStreamSyncPoint.WaitToContinue(); }); // Act var response = await client.SendAsync(httpRequest, HttpCompletionOption.ResponseHeadersRead).WithTimeout(); await responseStartedSyncPoint.WaitForSyncPoint().WithTimeout(); responseStartedSyncPoint.Continue(); var responseContent = await response.Content.ReadAsStreamAsync().WithTimeout(); // Assert // Ensure request stream has started await requestStreamSyncPoint.WaitForSyncPoint(); byte[] buffer = new byte[1024]; var length = await responseContent.ReadAsync(buffer).AsTask().WithTimeout(); Assert.Equal("STARTED", Encoding.UTF8.GetString(buffer, 0, length)); // Send content and finish request body await requestStream.WriteAsync(Encoding.UTF8.GetBytes("Hello world")).AsTask().WithTimeout(); await requestStream.FlushAsync().WithTimeout(); requestStreamSyncPoint.Continue(); // Ensure content is received while request is in progress length = await responseContent.ReadAsync(buffer).AsTask().WithTimeout(); Assert.Equal("Hello world POST Response", Encoding.UTF8.GetString(buffer, 0, length)); // Request is ending await requestEndingSyncPoint.WaitForSyncPoint().WithTimeout(); requestEndingSyncPoint.Continue(); // No more response content length = await responseContent.ReadAsync(buffer).AsTask().WithTimeout(); Assert.Equal(0, length); }