예제 #1
0
        public async Task Unary_AttemptsGreaterThanDefaultClientLimit_LimitedAttemptsMade(int hedgingDelay)
        {
            var callCount = 0;

            Task <DataMessage> UnaryFailure(DataMessage request, ServerCallContext context)
            {
                Interlocked.Increment(ref callCount);
                return(Task.FromException <DataMessage>(new RpcException(new Status(StatusCode.Unavailable, ""))));
            }

            using var httpEventListener = new HttpEventSourceListener(LoggerFactory);

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

            var channel = CreateChannel(serviceConfig: ServiceConfigHelpers.CreateRetryServiceConfig(maxAttempts: 10, initialBackoff: TimeSpan.FromMilliseconds(hedgingDelay)));

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

            // Act
            var call = client.UnaryCall(new DataMessage());

            // Assert
            var ex = await ExceptionAssert.ThrowsAsync <RpcException>(() => call.ResponseAsync).DefaultTimeout();

            Assert.AreEqual(StatusCode.Unavailable, ex.StatusCode);
            Assert.AreEqual(StatusCode.Unavailable, call.GetStatus().StatusCode);

            Assert.AreEqual(5, callCount);

            AssertHasLog(LogLevel.Debug, "MaxAttemptsLimited", "The method has 10 attempts specified in the service config. The number of attempts has been limited by channel configuration to 5.");
            AssertHasLog(LogLevel.Debug, "CallCommited", "Call commited. Reason: ExceededAttemptCount");
        }
예제 #2
0
        public async Task SendUnauthenticatedRequest_UnauthenticatedErrorResponse()
        {
            using var httpEventListener = new HttpEventSourceListener(LoggerFactory);

            SetExpectedErrorsFilter(writeContext =>
            {
                // This error can happen if the server returns an unauthorized response
                // before the client has finished sending the request content.
                if (writeContext.LoggerName == "Grpc.Net.Client.Internal.GrpcCall" &&
                    writeContext.EventId.Name == "ErrorSendingMessage" &&
                    writeContext.Exception is OperationCanceledException)
                {
                    return(true);
                }

                return(false);
            });

            // Arrage
            var channel = CreateGrpcWebChannel();

            var client = new AuthorizedGreeter.AuthorizedGreeterClient(channel);

            // Act
            var ex = await ExceptionAssert.ThrowsAsync <RpcException>(() => client.SayHelloAsync(new HelloRequest {
                Name = "test"
            }).ResponseAsync).DefaultTimeout();

            // Assert
            Assert.AreEqual(StatusCode.Unauthenticated, ex.StatusCode);

            AssertHasLog(LogLevel.Information, "GrpcStatusError", "Call failed with gRPC error status. Status code: 'Unauthenticated', Message: 'Bad gRPC response. HTTP status code: 401'.");
        }
예제 #3
0
        public static async Task <int> Main(string[] args)
        {
            var rootCommand = new RootCommand();

            rootCommand.AddOption(new Option <string>(new string[] { "--client_type", nameof(ClientOptions.ClientType) }, () => "httpclient"));
            rootCommand.AddOption(new Option <string>(new string[] { "--server_host", nameof(ClientOptions.ServerHost) })
            {
                Required = true
            });
            rootCommand.AddOption(new Option <string>(new string[] { "--server_host_override", nameof(ClientOptions.ServerHostOverride) }));
            rootCommand.AddOption(new Option <int>(new string[] { "--server_port", nameof(ClientOptions.ServerPort) })
            {
                Required = true
            });
            rootCommand.AddOption(new Option <string>(new string[] { "--test_case", nameof(ClientOptions.TestCase) })
            {
                Required = true
            });
            rootCommand.AddOption(new Option <bool>(new string[] { "--use_tls", nameof(ClientOptions.UseTls) }));
            rootCommand.AddOption(new Option <bool>(new string[] { "--use_test_ca", nameof(ClientOptions.UseTestCa) }));
            rootCommand.AddOption(new Option <string>(new string[] { "--default_service_account", nameof(ClientOptions.DefaultServiceAccount) }));
            rootCommand.AddOption(new Option <string>(new string[] { "--oauth_scope", nameof(ClientOptions.OAuthScope) }));
            rootCommand.AddOption(new Option <string>(new string[] { "--service_account_key_file", nameof(ClientOptions.ServiceAccountKeyFile) }));
            rootCommand.AddOption(new Option <string>(new string[] { "--grpc_web_mode", nameof(ClientOptions.GrpcWebMode) }));
            rootCommand.AddOption(new Option <bool>(new string[] { "--use_winhttp", nameof(ClientOptions.UseWinHttp) }));
            rootCommand.AddOption(new Option <bool>(new string[] { "--use_http3", nameof(ClientOptions.UseHttp3) }));

            rootCommand.Handler = CommandHandler.Create <ClientOptions>(async(options) =>
            {
                var runtimeVersion = typeof(object).Assembly.GetCustomAttribute <AssemblyInformationalVersionAttribute>()?.InformationalVersion ?? "Unknown";

                Console.WriteLine("Runtime: " + runtimeVersion);
                Console.WriteLine("Use TLS: " + options.UseTls);
                Console.WriteLine("Use WinHttp: " + options.UseWinHttp);
                Console.WriteLine("Use HTTP/3: " + options.UseHttp3);
                Console.WriteLine("Use GrpcWebMode: " + options.GrpcWebMode);
                Console.WriteLine("Use Test CA: " + options.UseTestCa);
                Console.WriteLine("Client type: " + options.ClientType);
                Console.WriteLine("Server host: " + options.ServerHost);
                Console.WriteLine("Server port: " + options.ServerPort);

                var services = new ServiceCollection();
                services.AddLogging(configure =>
                {
                    configure.SetMinimumLevel(LogLevel.Trace);
                    configure.AddConsole(loggerOptions => loggerOptions.IncludeScopes = true);
                });

                using var serviceProvider = services.BuildServiceProvider();
                var loggerFactory         = serviceProvider.GetRequiredService <ILoggerFactory>();

                using var httpEventListener = new HttpEventSourceListener(loggerFactory);

                var interopClient = new InteropClient(options, loggerFactory);
                await interopClient.Run();
            });

            return(await rootCommand.InvokeAsync(args));
        }
예제 #4
0
    public async Task UseUrls_HelloWorld_ClientSuccess()
    {
        // Arrange
        using var httpEventSource = new HttpEventSourceListener(LoggerFactory);

        var builder = new HostBuilder()
                      .ConfigureWebHost(webHostBuilder =>
        {
            webHostBuilder
            .UseKestrel(o =>
            {
                o.ConfigureEndpointDefaults(listenOptions =>
                {
                    listenOptions.Protocols = Core.HttpProtocols.Http3;
                });
            })
            .UseUrls("https://127.0.0.1:0")
            .Configure(app =>
            {
                app.Run(async context =>
                {
                    await context.Response.WriteAsync("hello, world");
                });
            });
        })
                      .ConfigureServices(AddTestLogging);

        using (var host = builder.Build())
            using (var client = CreateClient())
            {
                await host.StartAsync();

                var request = new HttpRequestMessage(HttpMethod.Get, $"https://127.0.0.1:{host.GetPort()}/");
                request.Version       = HttpVersion.Version30;
                request.VersionPolicy = HttpVersionPolicy.RequestVersionExact;

                // Act
                var response = await client.SendAsync(request);

                // Assert
                response.EnsureSuccessStatusCode();
                Assert.Equal(HttpVersion.Version30, response.Version);
                var responseText = await response.Content.ReadAsStringAsync();

                Assert.Equal("hello, world", responseText);

                await host.StopAsync();
            }
    }
예제 #5
0
    private InteropClient(ClientOptions options)
    {
        this.options = options;

        var services = new ServiceCollection();

        services.AddLogging(configure =>
        {
            configure.SetMinimumLevel(LogLevel.Trace);
            configure.AddConsole(loggerOptions =>
            {
#pragma warning disable CS0618 // Type or member is obsolete
                loggerOptions.IncludeScopes = true;
                loggerOptions.DisableColors = true;
#pragma warning restore CS0618 // Type or member is obsolete
            });
        });

        serviceProvider = services.BuildServiceProvider();

        loggerFactory   = serviceProvider.GetRequiredService <ILoggerFactory>();
        httpEventSource = new HttpEventSourceListener(loggerFactory);
    }
예제 #6
0
        public async Task ClientStream_HttpClientWithTimeout_Success()
        {
            SetExpectedErrorsFilter(writeContext =>
            {
                if (writeContext.LoggerName == "Grpc.Net.Client.Internal.GrpcCall" &&
                    writeContext.Exception is TaskCanceledException)
                {
                    return(true);
                }

                if (writeContext.LoggerName == "Grpc.Net.Client.Internal.GrpcCall" &&
                    writeContext.EventId.Name == "WriteMessageError" &&
                    writeContext.Exception is InvalidOperationException &&
                    writeContext.Exception.Message == "Can't write the message because the call is complete.")
                {
                    return(true);
                }

                if (writeContext.LoggerName == TestConstants.ServerCallHandlerTestName)
                {
                    return(true);
                }

                return(false);
            });

            using var httpEventListener = new HttpEventSourceListener(LoggerFactory);

            var tcs = new TaskCompletionSource <object?>(TaskCreationOptions.RunContinuationsAsynchronously);

            async Task <DataComplete> ClientStreamedData(IAsyncStreamReader <DataMessage> requestStream, ServerCallContext context)
            {
                Logger.LogInformation("Server started");
                context.CancellationToken.Register(() =>
                {
                    Logger.LogInformation("Server completed TCS");
                    tcs.SetResult(null);
                });

                var total = 0L;

                await foreach (var message in requestStream.ReadAllAsync())
                {
                    total += message.Data.Length;

                    if (message.ServerDelayMilliseconds > 0)
                    {
                        await Task.Delay(message.ServerDelayMilliseconds);
                    }
                }

                return(new DataComplete
                {
                    Size = total
                });
            }

            // Arrange
            var data = CreateTestData(1024); // 1 KB

            var method = Fixture.DynamicGrpc.AddClientStreamingMethod <DataMessage, DataComplete>(ClientStreamedData, "ClientStreamedDataTimeout");

            var httpClient = Fixture.CreateClient();

            httpClient.Timeout = TimeSpan.FromSeconds(0.5);

            var channel = GrpcChannel.ForAddress(httpClient.BaseAddress !, new GrpcChannelOptions
            {
                HttpClient    = httpClient,
                LoggerFactory = LoggerFactory
            });

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

            var dataMessage = new DataMessage
            {
                Data = ByteString.CopyFrom(data)
            };

            // Act
            var call = client.ClientStreamingCall();

            Logger.LogInformation("Client writing message");
            await call.RequestStream.WriteAsync(dataMessage).DefaultTimeout();

            Logger.LogInformation("Client waiting for TCS to complete");
            await tcs.Task.DefaultTimeout();

            var ex = await ExceptionAssert.ThrowsAsync <RpcException>(() => call.RequestStream.WriteAsync(dataMessage)).DefaultTimeout();

            // Assert
            Assert.AreEqual(StatusCode.Cancelled, ex.StatusCode);
            Assert.AreEqual(StatusCode.Cancelled, call.GetStatus().StatusCode);

            AssertHasLog(LogLevel.Information, "GrpcStatusError", "Call failed with gRPC error status. Status code: 'Cancelled', Message: ''.");

            await TestHelpers.AssertIsTrueRetryAsync(
                () => HasLog(LogLevel.Error, "ErrorExecutingServiceMethod", "Error when executing service method 'ClientStreamedDataTimeout'."),
                "Wait for server error so it doesn't impact other tests.").DefaultTimeout();
        }
예제 #7
0
        public async Task MultipleMessagesFromOneClient_SuccessResponses()
        {
            // Arrange
            using var httpEventSource = new HttpEventSourceListener(LoggerFactory);

            var ms = new MemoryStream();

            MessageHelpers.WriteMessage(ms, new ChatMessage
            {
                Name    = "John",
                Message = "Hello Jill"
            });

            var streamingContent = new StreamingContent();
            var httpRequest      = GrpcHttpHelper.Create("Chat.Chatter/Chat");

            httpRequest.Content = streamingContent;

            // Act
            var responseTask = Fixture.Client.SendAsync(httpRequest, HttpCompletionOption.ResponseHeadersRead);

            // Assert
            Assert.IsFalse(responseTask.IsCompleted, "Server should wait for first message from client");

            var requestStream = await streamingContent.GetRequestStreamAsync().DefaultTimeout();

            Logger.LogInformation("Client sending message");
            await requestStream.WriteAsync(ms.ToArray()).AsTask().DefaultTimeout();

            await requestStream.FlushAsync().DefaultTimeout();

            Logger.LogInformation("Client waiting for response");
            var response = await responseTask.DefaultTimeout();

            response.AssertIsSuccessfulGrpcRequest();

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

            var pipeReader = PipeReader.Create(responseStream);

            Logger.LogInformation("Client reading message");
            var message1Task = MessageHelpers.AssertReadStreamMessageAsync <ChatMessage>(pipeReader);
            var message1     = await message1Task.DefaultTimeout();

            Assert.AreEqual("John", message1 !.Name);
            Assert.AreEqual("Hello Jill", message1.Message);

            Logger.LogInformation("Client starting reading message");
            var message2Task = MessageHelpers.AssertReadStreamMessageAsync <ChatMessage>(pipeReader);

            Assert.IsFalse(message2Task.IsCompleted, "Server is waiting for messages from client");

            ms = new MemoryStream();
            MessageHelpers.WriteMessage(ms, new ChatMessage
            {
                Name    = "Jill",
                Message = "Hello John"
            });

            Logger.LogInformation("Client sending message");
            await requestStream.WriteAsync(ms.ToArray()).AsTask().DefaultTimeout();

            await requestStream.FlushAsync().DefaultTimeout();

            Logger.LogInformation("Client waiting for reading message");
            var message2 = await message2Task.DefaultTimeout();

            Assert.AreEqual("Jill", message2 !.Name);
            Assert.AreEqual("Hello John", message2.Message);

            var finishedTask = MessageHelpers.AssertReadStreamMessageAsync <ChatMessage>(pipeReader);

            Assert.IsFalse(finishedTask.IsCompleted, "Server is waiting for client to end streaming");

            // Complete request stream
            streamingContent.Complete();

            await finishedTask.DefaultTimeout();

            response.AssertTrailerStatus();
        }
예제 #8
0
        public async Task UnaryMethod_SuccessfulCall_PollingCountersUpdatedCorrectly()
        {
            using var httpEventSource = new HttpEventSourceListener(LoggerFactory);

            var tcs = new TaskCompletionSource <bool>(TaskCreationOptions.RunContinuationsAsynchronously);

            async Task <HelloReply> UnarySuccess(HelloRequest request, ServerCallContext context)
            {
                await tcs.Task.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>(UnarySuccess);

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

            var call = client.UnaryCall(new HelloRequest());

            // Assert - Call in progress
            await AssertCounters("Server call in progress", serverEventListener, new Dictionary <string, long>
            {
                ["total-calls"]       = 1,
                ["current-calls"]     = 1,
                ["messages-sent"]     = 0,
                ["messages-received"] = 1,
            }).DefaultTimeout();
            await AssertCounters("Client call in progress", clientEventListener, new Dictionary <string, long>
            {
                ["total-calls"]       = 1,
                ["current-calls"]     = 1,
                ["messages-sent"]     = 1,
                ["messages-received"] = 0,
            }).DefaultTimeout();

            // Act - Complete call
            tcs.SetResult(true);

            await call.ResponseAsync.DefaultTimeout();

            // Assert - Call complete
            await AssertCounters("Server call in complete", serverEventListener, new Dictionary <string, long>
            {
                ["total-calls"]       = 1,
                ["current-calls"]     = 0,
                ["messages-sent"]     = 1,
                ["messages-received"] = 1,
            }).DefaultTimeout();
            await AssertCounters("Client call complete", clientEventListener, new Dictionary <string, long>
            {
                ["total-calls"]       = 1,
                ["current-calls"]     = 0,
                ["messages-sent"]     = 1,
                ["messages-received"] = 1,
            }).DefaultTimeout();
        }
예제 #9
0
        public async Task ClientStreaming_MultipleWritesAndRetries_Failure()
        {
            var nextFailure = 1;

            async Task <DataMessage> ClientStreamingWithReadFailures(IAsyncStreamReader <DataMessage> requestStream, ServerCallContext context)
            {
                List <byte> bytes = new List <byte>();

                await foreach (var message in requestStream.ReadAllAsync())
                {
                    if (bytes.Count >= nextFailure)
                    {
                        nextFailure = nextFailure * 2;
                        Logger.LogInformation($"Server failing at {bytes.Count}. Next failure at {nextFailure}.");
                        throw new RpcException(new Status(StatusCode.Unavailable, ""));
                    }

                    Logger.LogInformation($"Server received {bytes.Count}.");
                    bytes.Add(message.Data[0]);
                }

                Logger.LogInformation("Server returning response.");
                return(new DataMessage
                {
                    Data = ByteString.CopyFrom(bytes.ToArray())
                });
            }

            SetExpectedErrorsFilter(writeContext =>
            {
                return(true);
            });

            using var httpEventListener = new HttpEventSourceListener(LoggerFactory);

            // Arrange
            var method   = Fixture.DynamicGrpc.AddClientStreamingMethod <DataMessage, DataMessage>(ClientStreamingWithReadFailures);
            var channel  = CreateChannel(serviceConfig: ServiceConfigHelpers.CreateRetryServiceConfig(maxAttempts: 10), maxRetryAttempts: 10);
            var client   = TestClientFactory.Create(channel, method);
            var sentData = new List <byte>();

            // Act
            var call = client.ClientStreamingCall();

            for (var i = 0; i < 15; i++)
            {
                sentData.Add((byte)i);

                Logger.LogInformation($"Client writing message {i}.");
                await call.RequestStream.WriteAsync(new DataMessage { Data = ByteString.CopyFrom(new byte[] { (byte)i }) }).DefaultTimeout();

                await Task.Delay(1);
            }

            Logger.LogInformation("Client completing request stream.");
            await call.RequestStream.CompleteAsync().DefaultTimeout();

            Logger.LogInformation("Client waiting for response.");
            var result = await call.ResponseAsync.DefaultTimeout();

            // Assert
            Assert.IsTrue(result.Data.Span.SequenceEqual(sentData.ToArray()));
        }
예제 #10
0
    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());
        }
    }
예제 #11
0
        public void Configure(IApplicationBuilder app, IHostApplicationLifetime applicationLifetime)
        {
            // Required to notify performance infrastructure that it can begin benchmarks
            applicationLifetime.ApplicationStarted.Register(() => Console.WriteLine("Application started."));

            var loggerFactory = app.ApplicationServices.GetRequiredService <ILoggerFactory>();

            if (loggerFactory.CreateLogger <Startup>().IsEnabled(LogLevel.Trace))
            {
                _ = new HttpEventSourceListener(loggerFactory);
            }

            app.UseRouting();

#if NET5_0_OR_GREATER
            bool.TryParse(_config["enableCertAuth"], out var enableCertAuth);
            if (enableCertAuth)
            {
                app.UseAuthentication();
                app.UseAuthorization();
            }
#endif

#if GRPC_WEB
            bool.TryParse(_config["enableGrpcWeb"], out var enableGrpcWeb);

            if (enableGrpcWeb)
            {
                app.UseGrpcWeb(new GrpcWebOptions {
                    DefaultEnabled = true
                });
            }
#endif

            app.UseMiddleware <ServiceProvidersMiddleware>();

            app.UseEndpoints(endpoints =>
            {
                ConfigureAuthorization(endpoints.MapGrpcService <BenchmarkServiceImpl>());

                ConfigureAuthorization(endpoints.MapControllers());

                ConfigureAuthorization(endpoints.MapGet("/", context =>
                {
                    return(context.Response.WriteAsync("Benchmark Server"));
                }));

                ConfigureAuthorization(endpoints.MapPost("/unary", async context =>
                {
                    MemoryStream ms = new MemoryStream();
                    await context.Request.Body.CopyToAsync(ms);
                    ms.Seek(0, SeekOrigin.Begin);

                    JsonSerializer serializer = new JsonSerializer();
                    var message = serializer.Deserialize <SimpleRequest>(new JsonTextReader(new StreamReader(ms))) !;

                    ms.Seek(0, SeekOrigin.Begin);
                    using (var writer = new JsonTextWriter(new StreamWriter(ms, Encoding.UTF8, 1024, true)))
                    {
                        serializer.Serialize(writer, BenchmarkServiceImpl.CreateResponse(message));
                    }

                    context.Response.StatusCode = StatusCodes.Status200OK;

                    ms.Seek(0, SeekOrigin.Begin);
                    await ms.CopyToAsync(context.Response.Body);
                }));
            });
        }
예제 #12
0
    public static void Main(string[] args)
    {
        var hostBuilder = new HostBuilder()
                          .ConfigureLogging((_, factory) =>
        {
            factory.SetMinimumLevel(LogLevel.Trace);
            factory.AddSimpleConsole(o => o.TimestampFormat = "[HH:mm:ss.fff] ");
        })
                          .ConfigureWebHost(webHost =>
        {
            webHost.UseKestrel()
            .ConfigureKestrel((context, options) =>
            {
                var cert = CertificateLoader.LoadFromStoreCert("localhost", StoreName.My.ToString(), StoreLocation.CurrentUser, false);

                options.ConfigureHttpsDefaults(httpsOptions =>
                {
                    httpsOptions.ServerCertificate = cert;
                    // httpsOptions.ClientCertificateMode = ClientCertificateMode.AllowCertificate;
                    // httpsOptions.AllowAnyClientCertificate();
                });

                options.ListenAnyIP(5000, listenOptions =>
                {
                    listenOptions.UseConnectionLogging();
                    listenOptions.Protocols = HttpProtocols.Http1AndHttp2;
                });

                options.ListenAnyIP(5001, listenOptions =>
                {
                    listenOptions.UseHttps();
                    listenOptions.UseConnectionLogging();
                    listenOptions.Protocols = HttpProtocols.Http1AndHttp2AndHttp3;
                });

                options.ListenAnyIP(5002, listenOptions =>
                {
                    listenOptions.UseHttps(StoreName.My, "localhost");
                    listenOptions.UseConnectionLogging();
                    listenOptions.Protocols = HttpProtocols.Http3;
                });

                options.ListenAnyIP(5003, listenOptions =>
                {
                    listenOptions.UseHttps(httpsOptions =>
                    {
                        // ConnectionContext is null
                        httpsOptions.ServerCertificateSelector = (context, host) => cert;
                    });
                    listenOptions.UseConnectionLogging();
                    listenOptions.Protocols = HttpProtocols.Http1AndHttp2AndHttp3;
                });

                // No SslServerAuthenticationOptions callback is currently supported by QuicListener
                options.ListenAnyIP(5004, listenOptions =>
                {
                    listenOptions.UseHttps(httpsOptions =>
                    {
                        httpsOptions.OnAuthenticate = (_, sslOptions) => sslOptions.ServerCertificate = cert;
                    });
                    listenOptions.UseConnectionLogging();
                    listenOptions.Protocols = HttpProtocols.Http1AndHttp2;
                });

                // ServerOptionsSelectionCallback isn't currently supported by QuicListener
                options.ListenAnyIP(5005, listenOptions =>
                {
                    ServerOptionsSelectionCallback callback = (SslStream stream, SslClientHelloInfo clientHelloInfo, object state, CancellationToken cancellationToken) =>
                    {
                        var options = new SslServerAuthenticationOptions()
                        {
                            ServerCertificate = cert,
                        };
                        return(new ValueTask <SslServerAuthenticationOptions>(options));
                    };
                    listenOptions.UseHttps(callback, state: null);
                    listenOptions.Protocols = HttpProtocols.Http1AndHttp2;
                });

                // TlsHandshakeCallbackOptions (ServerOptionsSelectionCallback) isn't currently supported by QuicListener
                options.ListenAnyIP(5006, listenOptions =>
                {
                    listenOptions.UseHttps(new TlsHandshakeCallbackOptions()
                    {
                        OnConnection = context =>
                        {
                            var options = new SslServerAuthenticationOptions()
                            {
                                ServerCertificate = cert,
                            };
                            return(new ValueTask <SslServerAuthenticationOptions>(options));
                        },
                    });
                    listenOptions.UseConnectionLogging();
                    listenOptions.Protocols = HttpProtocols.Http1AndHttp2;
                });
            })
            .UseStartup <Startup>();
        });

        var host = hostBuilder.Build();

        // Listener needs to be configured before host (and HTTP/3 endpoints) start up.
        using var httpEventSource = new HttpEventSourceListener(host.Services.GetRequiredService <ILoggerFactory>());

        host.Run();
    }
예제 #13
0
    public async Task StreamPool_Heartbeat_ExpiredStreamRemoved()
    {
        // Arrange
        using var httpEventSource = new HttpEventSourceListener(LoggerFactory);

        var now             = new DateTimeOffset(2021, 7, 6, 12, 0, 0, TimeSpan.Zero);
        var testSystemClock = new TestSystemClock {
            UtcNow = now
        };

        await using var connectionListener = await QuicTestHelpers.CreateConnectionListenerFactory(LoggerFactory, testSystemClock);

        var options = QuicTestHelpers.CreateClientConnectionOptions(connectionListener.EndPoint);

        await using var clientConnection = await QuicConnection.ConnectAsync(options);

        await using var serverConnection = await connectionListener.AcceptAndAddFeatureAsync().DefaultTimeout();

        var testHeartbeatFeature = new TestHeartbeatFeature();

        serverConnection.Features.Set <IConnectionHeartbeatFeature>(testHeartbeatFeature);

        // Act & Assert
        var quicConnectionContext = Assert.IsType <QuicConnectionContext>(serverConnection);

        Assert.Equal(0, quicConnectionContext.StreamPool.Count);

        var stream1 = await QuicTestHelpers.CreateAndCompleteBidirectionalStreamGracefully(clientConnection, serverConnection, Logger);

        Assert.Equal(1, quicConnectionContext.StreamPool.Count);
        QuicStreamContext pooledStream = quicConnectionContext.StreamPool._array[0];

        Assert.Same(stream1, pooledStream);
        Assert.Equal(now.Ticks + QuicConnectionContext.StreamPoolExpiryTicks, pooledStream.PoolExpirationTicks);

        now = now.AddMilliseconds(100);
        testSystemClock.UtcNow = now;
        testHeartbeatFeature.RaiseHeartbeat();
        // Not removed.
        Assert.Equal(1, quicConnectionContext.StreamPool.Count);

        var stream2 = await QuicTestHelpers.CreateAndCompleteBidirectionalStreamGracefully(clientConnection, serverConnection, Logger);

        Assert.Equal(1, quicConnectionContext.StreamPool.Count);
        pooledStream = quicConnectionContext.StreamPool._array[0];
        Assert.Same(stream1, pooledStream);
        Assert.Equal(now.Ticks + QuicConnectionContext.StreamPoolExpiryTicks, pooledStream.PoolExpirationTicks);

        Assert.Same(stream1, stream2);

        now = now.AddTicks(QuicConnectionContext.StreamPoolExpiryTicks);
        testSystemClock.UtcNow = now;
        testHeartbeatFeature.RaiseHeartbeat();
        // Not removed.
        Assert.Equal(1, quicConnectionContext.StreamPool.Count);

        now = now.AddTicks(1);
        testSystemClock.UtcNow = now;
        testHeartbeatFeature.RaiseHeartbeat();
        // Removed.
        Assert.Equal(0, quicConnectionContext.StreamPool.Count);
    }
예제 #14
0
    public async Task BidirectionalStream_ServerWritesDataAndDisposes_ClientReadsData(int dataLength)
    {
        // Arrange
        using var httpEventSource = new HttpEventSourceListener(LoggerFactory);

        var testData = new byte[dataLength];

        for (int i = 0; i < dataLength; i++)
        {
            testData[i] = (byte)i;
        }

        await using var connectionListener = await QuicTestHelpers.CreateConnectionListenerFactory(LoggerFactory);

        var options = QuicTestHelpers.CreateClientConnectionOptions(connectionListener.EndPoint);

        await using var clientConnection = await QuicConnection.ConnectAsync(options);

        await using var serverConnection = await connectionListener.AcceptAndAddFeatureAsync().DefaultTimeout();

        // Act
        Logger.LogInformation("Client starting stream.");
        var clientStream = await clientConnection.OpenOutboundStreamAsync(QuicStreamType.Bidirectional);

        await clientStream.WriteAsync(TestData, completeWrites : true).DefaultTimeout();

        var serverStream = await serverConnection.AcceptAsync().DefaultTimeout();

        Logger.LogInformation("Server accepted stream.");
        var readResult = await serverStream.Transport.Input.ReadAtLeastAsync(TestData.Length).DefaultTimeout();

        serverStream.Transport.Input.AdvanceTo(readResult.Buffer.End);

        // Input should be completed.
        readResult = await serverStream.Transport.Input.ReadAsync().DefaultTimeout();

        Assert.True(readResult.IsCompleted);

        Logger.LogInformation("Client starting to read.");
        var readingTask = clientStream.ReadUntilEndAsync();

        Logger.LogInformation("Server sending data.");
        await serverStream.Transport.Output.WriteAsync(testData).DefaultTimeout();

        Logger.LogInformation("Server completing pipes.");
        await serverStream.Transport.Input.CompleteAsync().DefaultTimeout();

        await serverStream.Transport.Output.CompleteAsync().DefaultTimeout();

        Logger.LogInformation("Client reading until end of stream.");
        var data = await readingTask.DefaultTimeout();

        Assert.Equal(testData.Length, data.Length);
        Assert.Equal(testData, data);

        var quicStreamContext = Assert.IsType <QuicStreamContext>(serverStream);

        Logger.LogInformation("Server waiting for send and receiving loops to complete.");
        await quicStreamContext._processingTask.DefaultTimeout();

        Assert.True(quicStreamContext.CanWrite);
        Assert.True(quicStreamContext.CanRead);

        Logger.LogInformation("Server disposing stream.");
        await quicStreamContext.DisposeAsync().DefaultTimeout();

        quicStreamContext.Dispose();

        var quicConnectionContext = Assert.IsType <QuicConnectionContext>(serverConnection);

        Assert.Equal(1, quicConnectionContext.StreamPool.Count);
    }
예제 #15
0
    public async Task BidirectionalStream_ClientAbortedAfterDisposeCalled_NotPooled()
    {
        // Arrange
        using var httpEventSource = new HttpEventSourceListener(LoggerFactory);

        await using var connectionListener = await QuicTestHelpers.CreateConnectionListenerFactory(LoggerFactory);

        var options = QuicTestHelpers.CreateClientConnectionOptions(connectionListener.EndPoint);

        await using var clientConnection = await QuicConnection.ConnectAsync(options);

        await using var serverConnection = await connectionListener.AcceptAndAddFeatureAsync().DefaultTimeout();

        // Act
        Logger.LogInformation("Client starting stream.");
        var clientStream = await clientConnection.OpenOutboundStreamAsync(QuicStreamType.Bidirectional);

        await clientStream.WriteAsync(TestData).DefaultTimeout();

        var readTask = clientStream.ReadUntilEndAsync();

        Logger.LogInformation("Server accepted stream.");
        var serverStream = await serverConnection.AcceptAsync().DefaultTimeout();

        var readResult = await serverStream.Transport.Input.ReadAtLeastAsync(TestData.Length).DefaultTimeout();

        serverStream.Transport.Input.AdvanceTo(readResult.Buffer.End);

        // Server sends a large response that will make it wait to complete sends.
        Logger.LogInformation("Server writing a large response.");
        await serverStream.Transport.Output.WriteAsync(new byte[1024 * 1024 * 32]).DefaultTimeout();

        // Complete reading and writing.
        Logger.LogInformation("Server complete reading and writing.");
        await serverStream.Transport.Input.CompleteAsync();

        await serverStream.Transport.Output.CompleteAsync();

        Logger.LogInformation("Client wait to finish reading.");
        await readTask.DefaultTimeout();

        var quicStreamContext = Assert.IsType <QuicStreamContext>(serverStream);

        // Server starts disposing
        Logger.LogInformation("Server starts disposing.");
        var disposeTask = quicStreamContext.DisposeAsync();

        // Client aborts while server is draining
        clientStream.Abort(QuicAbortDirection.Read, (long)Http3ErrorCode.RequestCancelled);
        clientStream.Abort(QuicAbortDirection.Write, (long)Http3ErrorCode.RequestCancelled);

        // Server finishes disposing
        Logger.LogInformation("Wait for server finish disposing.");
        await disposeTask.DefaultTimeout();

        quicStreamContext.Dispose();

        var quicConnectionContext = Assert.IsType <QuicConnectionContext>(serverConnection);

        // Assert
        Assert.Equal(0, quicConnectionContext.StreamPool.Count);
    }
예제 #16
0
        public async Task NoBuffering_SuccessResponsesStreamed()
        {
            using var httpEventSource = new HttpEventSourceListener(LoggerFactory);

            var methodWrapper = new MethodWrapper
            {
                Logger    = Logger,
                SyncPoint = new SyncPoint(runContinuationsAsynchronously: true)
            };

            async Task SayHellos(HelloRequest request, IServerStreamWriter <HelloReply> responseStream, ServerCallContext context)
            {
                // Explicitly send the response headers before any streamed content
                Metadata responseHeaders = new Metadata();

                responseHeaders.Add("test-response-header", "value");
                await context.WriteResponseHeadersAsync(responseHeaders);

                await methodWrapper.SayHellosAsync(request, responseStream);
            }

            // Arrange
            var method = Fixture.DynamicGrpc.AddServerStreamingMethod <HelloRequest, HelloReply>(SayHellos);

            var requestMessage = new HelloRequest
            {
                Name = "World"
            };

            var requestStream = new MemoryStream();

            MessageHelpers.WriteMessage(requestStream, requestMessage);

            var httpRequest = GrpcHttpHelper.Create(method.FullName);

            httpRequest.Content = new GrpcStreamContent(requestStream);

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

            // Assert
            response.AssertIsSuccessfulGrpcRequest();

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

            var pipeReader = PipeReader.Create(responseStream);

            for (var i = 0; i < 3; i++)
            {
                var greetingTask = MessageHelpers.AssertReadStreamMessageAsync <HelloReply>(pipeReader);

                Assert.IsFalse(greetingTask.IsCompleted);

                await methodWrapper.SyncPoint.WaitForSyncPoint().DefaultTimeout();

                var currentSyncPoint = methodWrapper.SyncPoint;
                methodWrapper.SyncPoint = new SyncPoint(runContinuationsAsynchronously: true);
                currentSyncPoint.Continue();

                var greeting = (await greetingTask.DefaultTimeout()) !;

                Assert.AreEqual($"How are you World? {i}", greeting.Message);
            }

            var goodbyeTask = MessageHelpers.AssertReadStreamMessageAsync <HelloReply>(pipeReader);

            Assert.False(goodbyeTask.IsCompleted);

            await methodWrapper.SyncPoint.WaitForSyncPoint().DefaultTimeout();

            methodWrapper.SyncPoint.Continue();

            Assert.AreEqual("Goodbye World!", (await goodbyeTask.DefaultTimeout()) !.Message);

            var finishedTask = MessageHelpers.AssertReadStreamMessageAsync <HelloReply>(pipeReader);

            Assert.IsNull(await finishedTask.DefaultTimeout());
        }