Beispiel #1
0
        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);
        }
Beispiel #2
0
        public async Task ClientStreaming_ResponseCompletesWithoutReadingRequest()
        {
            // Arrange
            var requestStreamTcs = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
            var responseEndingSyncPoint = new SyncPoint();

            RequestDelegate appDelegate = async ctx =>
            {
                await ctx.Response.WriteAsync("POST Response");
                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 requestStreamTcs.Task;
            });

            // Act
            var response = await client.SendAsync(httpRequest, HttpCompletionOption.ResponseHeadersRead).DefaultTimeout();

            var responseContent = await response.Content.ReadAsStreamAsync().DefaultTimeout();

            // Assert

            // Read response
            byte[] buffer = new byte[1024];
            var length = await responseContent.ReadAsync(buffer).AsTask().DefaultTimeout();
            Assert.Equal("POST Response", Encoding.UTF8.GetString(buffer, 0, length));

            // Send large content and block on back pressure
            var writeTask = Task.Run(async () =>
            {
                try
                {
                    await requestStream.WriteAsync(Encoding.UTF8.GetBytes(new string('!', 1024 * 1024 * 50))).AsTask().DefaultTimeout();
                    requestStreamTcs.SetResult(null);
                }
                catch (Exception ex)
                {
                    requestStreamTcs.SetException(ex);
                }
            });

            responseEndingSyncPoint.Continue();

            // No more response content
            length = await responseContent.ReadAsync(buffer).AsTask().DefaultTimeout();
            Assert.Equal(0, length);

            await writeTask;
        }
        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"));
        }
Beispiel #4
0
            static Task <DataMessage> MakeCall(GrpcTestFixture <FunctionalTestsWebsite.Startup> fixture, GrpcChannel channel, DataMessage request, SyncPoint syncPoint)
            {
                var callCount = 0;

                async Task <DataMessage> UnaryFailure(DataMessage request, ServerCallContext context)
                {
                    Interlocked.Increment(ref callCount);
                    if (callCount == 1)
                    {
                        await syncPoint.WaitToContinue();

                        throw new RpcException(new Status(StatusCode.Unavailable, ""));
                    }
                    else
                    {
                        return(request);
                    }
                }

                // Arrange
                var method = fixture.DynamicGrpc.AddUnaryMethod <DataMessage, DataMessage>(UnaryFailure);

                var client = TestClientFactory.Create(channel, method);

                var call = client.UnaryCall(request);

                return(call.ResponseAsync);
            }
Beispiel #5
0
        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"));
        }
Beispiel #6
0
        public async Task QueuedRequestsContinueWhenSpaceBecomesAvailible()
        {
            var blocker      = new SyncPoint();
            var firstRequest = true;

            var middleware = TestUtils.CreateTestMiddleware(
                maxConcurrentRequests: 1,
                next: httpContext =>
            {
                if (firstRequest)
                {
                    firstRequest = false;
                    return(blocker.WaitToContinue());
                }
                return(Task.CompletedTask);
            });

            // t1 (as the first request) is blocked by the tcs blocker
            var t1 = middleware.Invoke(new DefaultHttpContext());

            Assert.Equal(1, middleware.ActiveRequestCount);

            // t2 is blocked from entering the server since t1 already exists there
            // note: increasing MaxConcurrentRequests would allow t2 through while t1 is blocked
            var t2 = middleware.Invoke(new DefaultHttpContext());

            Assert.Equal(2, middleware.ActiveRequestCount);

            // unblock the first task, and the second should follow
            blocker.Continue();
            await t1.OrTimeout();

            await t2.OrTimeout();
        }
Beispiel #7
0
        public async Task Resolver_SubchannelTransientFailure_ResolverRefreshed()
        {
            // Ignore errors
            SetExpectedErrorsFilter(writeContext =>
            {
                return(true);
            });

            string?host = null;

            Task <HelloReply> UnaryMethod(HelloRequest request, ServerCallContext context)
            {
                host = context.Host;
                return(Task.FromResult(new HelloReply {
                    Message = request.Name
                }));
            }

            // Arrange
            using var endpoint1 = BalancerHelpers.CreateGrpcEndpoint <HelloRequest, HelloReply>(50051, UnaryMethod, nameof(UnaryMethod));
            using var endpoint2 = BalancerHelpers.CreateGrpcEndpoint <HelloRequest, HelloReply>(50052, UnaryMethod, nameof(UnaryMethod));

            SyncPoint?syncPoint = new SyncPoint(runContinuationsAsynchronously: true);

            syncPoint.Continue();

            var resolver = new TestResolver(async() =>
            {
                await syncPoint.WaitToContinue().DefaultTimeout();
                syncPoint = new SyncPoint(runContinuationsAsynchronously: true);
            });

            resolver.UpdateAddresses(new List <BalancerAddress>
            {
                new BalancerAddress(endpoint1.Address.Host, endpoint1.Address.Port),
                new BalancerAddress(endpoint2.Address.Host, endpoint2.Address.Port)
            });

            var channel = await BalancerHelpers.CreateChannel(LoggerFactory, new RoundRobinConfig(), resolver, connect : true);

            await BalancerHelpers.WaitForSubChannelsToBeReadyAsync(Logger, channel, 2).DefaultTimeout();

            var client = TestClientFactory.Create(channel, endpoint1.Method);

            var waitForRefreshTask = syncPoint.WaitForSyncPoint();

            endpoint1.Dispose();

            await waitForRefreshTask.DefaultTimeout();

            resolver.UpdateAddresses(new List <BalancerAddress>
            {
                new BalancerAddress(endpoint2.Address.Host, endpoint2.Address.Port)
            });

            syncPoint.Continue();

            await BalancerHelpers.WaitForSubChannelsToBeReadyAsync(Logger, channel, 1).DefaultTimeout();
        }
Beispiel #8
0
        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());
        }
Beispiel #9
0
        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);
        }
Beispiel #10
0
        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());
        }
Beispiel #11
0
        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();
        }
Beispiel #13
0
        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();
    }
Beispiel #15
0
            public async Task SayHellosAsync(HelloRequest request, IServerStreamWriter <HelloReply> responseStream)
            {
                for (var i = 0; i < 3; i++)
                {
                    await SyncPoint.WaitToContinue();

                    var message = $"How are you {request.Name}? {i}";

                    Logger.LogInformation("Sending message");
                    await responseStream.WriteAsync(new HelloReply { Message = message });
                }

                await SyncPoint.WaitToContinue();

                Logger.LogInformation("Sending message");
                await responseStream.WriteAsync(new HelloReply { Message = $"Goodbye {request.Name}!" });
            }
Beispiel #16
0
        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();
                });
            }
        }
Beispiel #17
0
            public override async Task SendAsync(ArraySegment <byte> buffer, WebSocketMessageType messageType, bool endOfMessage, CancellationToken cancellationToken)
            {
                if (_sync != null)
                {
                    await _sync.WaitToContinue();
                }
                cancellationToken.ThrowIfCancellationRequested();

                var copy = new byte[buffer.Count];

                Buffer.BlockCopy(buffer.Array, buffer.Offset, copy, 0, buffer.Count);
                await SendMessageAsync(new WebSocketMessage
                {
                    Buffer       = copy,
                    MessageType  = messageType,
                    EndOfMessage = endOfMessage
                },
                                       cancellationToken);
            }
        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);
        }
Beispiel #19
0
        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);
        }
Beispiel #20
0
        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);
        }
Beispiel #21
0
        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 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();
        }
Beispiel #23
0
        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();
                }
        }
Beispiel #24
0
        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();
            }
        }
Beispiel #25
0
        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);
        }
Beispiel #26
0
    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;
            }
    }
Beispiel #27
0
        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 UpdateAddresses_ConnectIsInProgress_InProgressConnectIsCanceledAndRestarted()
        {
            // Arrange
            var services = new ServiceCollection();

            services.AddNUnitLogger();
            var serviceProvider = services.BuildServiceProvider();
            var loggerFactory   = serviceProvider.GetRequiredService <ILoggerFactory>();
            var testLogger      = loggerFactory.CreateLogger(GetType());

            var resolver = new TestResolver(loggerFactory);

            resolver.UpdateAddresses(new List <BalancerAddress>
            {
                new BalancerAddress("localhost", 80)
            });

            var connectAddressesChannel = System.Threading.Channels.Channel.CreateUnbounded <DnsEndPoint>();

            var syncPoint = new SyncPoint(runContinuationsAsynchronously: true);

            var transportFactory = new TestSubchannelTransportFactory(async(s, c) =>
            {
                c.Register(state => ((SyncPoint)state !).Continue(), syncPoint);

                var connectAddress = s.GetAddresses().Single();
                testLogger.LogInformation("Writing connect address " + connectAddress);

                await connectAddressesChannel.Writer.WriteAsync(connectAddress.EndPoint, c);
                await syncPoint.WaitToContinue();

                c.ThrowIfCancellationRequested();
                return(ConnectivityState.Ready);
            });
            var clientChannel = new ConnectionManager(resolver, disableResolverServiceConfig: false, loggerFactory, transportFactory, Array.Empty <LoadBalancerFactory>());

            clientChannel.ConfigureBalancer(c => new PickFirstBalancer(c, loggerFactory));

            // Act
            _ = clientChannel.ConnectAsync(waitForReady: true, CancellationToken.None).ConfigureAwait(false);

            var connectAddress1 = await connectAddressesChannel.Reader.ReadAsync().AsTask().DefaultTimeout();

            Assert.AreEqual(80, connectAddress1.Port);

            // Endpoints are unchanged so continue connecting...
            resolver.UpdateAddresses(new List <BalancerAddress>
            {
                new BalancerAddress("localhost", 80)
            });
            Assert.IsFalse(syncPoint.WaitToContinue().IsCompleted);

            // Endpoints change so cancellation + reconnect triggered
            resolver.UpdateAddresses(new List <BalancerAddress>
            {
                new BalancerAddress("localhost", 81)
            });

            await syncPoint.WaitToContinue().DefaultTimeout();

            var connectAddress2 = await connectAddressesChannel.Reader.ReadAsync().AsTask().DefaultTimeout();

            Assert.AreEqual(81, connectAddress2.Port);
        }
Beispiel #29
0
        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 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();
        }