public async Task RunAsync() { var endpoint = new IPEndPoint(IPAddress.Loopback, 5001); _logger.LogInformation($"Connecting to '{endpoint}'."); await using var context = await _connectionFactory.ConnectAsync(endpoint); _logger.LogInformation($"Connected to '{endpoint}'. Starting TLS handshake."); var memoryPool = context.Features.Get <IMemoryPoolFeature>()?.MemoryPool; var inputPipeOptions = new StreamPipeReaderOptions(memoryPool, memoryPool.GetMinimumSegmentSize(), memoryPool.GetMinimumAllocSize(), leaveOpen: true); var outputPipeOptions = new StreamPipeWriterOptions(pool: memoryPool, leaveOpen: true); await using var sslDuplexPipe = new SslDuplexPipe(context.Transport, inputPipeOptions, outputPipeOptions); await using var sslStream = sslDuplexPipe.Stream; var originalTransport = context.Transport; context.Transport = sslDuplexPipe; try { await sslStream.AuthenticateAsClientAsync(new SslClientAuthenticationOptions { TargetHost = "localhost", RemoteCertificateValidationCallback = (_, __, ___, ____) => true, ApplicationProtocols = new List <SslApplicationProtocol> { SslApplicationProtocol.Http2 }, EnabledSslProtocols = SslProtocols.Tls12, }, CancellationToken.None); _logger.LogInformation($"TLS handshake completed successfully."); var http2Utilities = new Http2Utilities(context); await http2Utilities.InitializeConnectionAsync(); _logger.LogInformation("Initialized http2 connection. Starting stream 1."); await http2Utilities.StartStreamAsync(1, Http2Utilities._browserRequestHeaders, endStream : true); var headersFrame = await http2Utilities.ReceiveFrameAsync(); Trace.Assert(headersFrame.Type == Http2FrameType.HEADERS); Trace.Assert((headersFrame.Flags & (byte)Http2HeadersFrameFlags.END_HEADERS) != 0); Trace.Assert((headersFrame.Flags & (byte)Http2HeadersFrameFlags.END_STREAM) == 0); _logger.LogInformation("Received headers in a single frame."); var decodedHeaders = http2Utilities.DecodeHeaders(headersFrame); foreach (var header in decodedHeaders) { _logger.LogInformation($"{header.Key}: {header.Value}"); } var dataFrame = await http2Utilities.ReceiveFrameAsync(); Trace.Assert(dataFrame.Type == Http2FrameType.DATA); Trace.Assert((dataFrame.Flags & (byte)Http2DataFrameFlags.END_STREAM) == 0); _logger.LogInformation("Received data in a single frame."); _logger.LogInformation(Encoding.UTF8.GetString(dataFrame.Payload.ToArray())); var trailersFrame = await http2Utilities.ReceiveFrameAsync(); Trace.Assert(trailersFrame.Type == Http2FrameType.HEADERS); Trace.Assert((trailersFrame.Flags & (byte)Http2DataFrameFlags.END_STREAM) == 1); _logger.LogInformation("Received trailers in a single frame."); http2Utilities._decodedHeaders.Clear(); var decodedTrailers = http2Utilities.DecodeHeaders(trailersFrame); foreach (var header in decodedHeaders) { _logger.LogInformation($"{header.Key}: {header.Value}"); } await http2Utilities.StopConnectionAsync(expectedLastStreamId : 1, ignoreNonGoAwayFrames : false); _logger.LogInformation("Connection stopped."); } finally { context.Transport = originalTransport; } }