public async Task StreamFlushAsyncThrowingDoesReturnMemoryToPool() { using (var pool = new DisposeTrackingBufferPool()) { var stream = new CancelledWritesStream(); var options = new StreamPipeWriterOptions(pool); PipeWriter writer = PipeWriter.Create(stream, options); writer.WriteEmpty(10); Assert.Equal(1, pool.CurrentlyRentedBlocks); Assert.Equal(0, pool.DisposedBlocks); ValueTask <FlushResult> task = writer.FlushAsync(); stream.WaitForWriteTask.TrySetResult(null); stream.WaitForFlushTask.TrySetException(new Exception()); await Assert.ThrowsAsync <Exception>(async() => await task); Assert.Equal(0, pool.CurrentlyRentedBlocks); Assert.Equal(1, pool.DisposedBlocks); writer.Complete(); Assert.Equal(0, pool.CurrentlyRentedBlocks); Assert.Equal(1, pool.DisposedBlocks); } }
public async Task WriteAsyncThrowsDuringMultiSegmentWriteCompleteReturnsAllMemory() { using (var pool = new DisposeTrackingBufferPool()) { var stream = new ThrowAfterNWritesStream(1); var options = new StreamPipeWriterOptions(pool); PipeWriter writer = PipeWriter.Create(stream, options); writer.WriteEmpty(pool.MaxBufferSize); writer.WriteEmpty(pool.MaxBufferSize); writer.WriteEmpty(pool.MaxBufferSize); Assert.Equal(3, pool.CurrentlyRentedBlocks); Assert.Equal(0, pool.DisposedBlocks); await Assert.ThrowsAsync <InvalidOperationException>(async() => await writer.FlushAsync()); Assert.Equal(2, pool.CurrentlyRentedBlocks); Assert.Equal(1, pool.DisposedBlocks); writer.Complete(); Assert.Equal(0, writer.UnflushedBytes); Assert.Equal(0, pool.CurrentlyRentedBlocks); Assert.Equal(3, pool.DisposedBlocks); } }
public void StreamPipeWriterOptions_Ctor_Defaults() { var options = new StreamPipeWriterOptions(); Assert.Same(MemoryPool <byte> .Shared, options.Pool); Assert.Equal(4096, options.MinimumBufferSize); Assert.False(options.LeaveOpen); }
public SslStreamDuplexPipe( IDuplexPipe transport, StreamPipeReaderOptions readerOptions, StreamPipeWriterOptions writerOptions, Func <Stream, SslStream> factory) : base(transport, readerOptions, writerOptions, factory) { }
public void StreamPipeWriterOptions_Ctor_Roundtrip() { using (var pool = new TestMemoryPool()) { var options = new StreamPipeWriterOptions(pool: pool, minimumBufferSize: 1234, leaveOpen: true); Assert.Same(pool, options.Pool); Assert.Equal(1234, options.MinimumBufferSize); Assert.True(options.LeaveOpen); } }
public static StreamPipeWriterOptions WriterOptionsCreator(MemoryPool <byte> memoryPool) { var outputPipeOptions = new StreamPipeWriterOptions ( pool: memoryPool, leaveOpen: true ); return(outputPipeOptions); }
public SeatidStream(Stream inputStream, Stream outputStream) { var readerOpts = new StreamPipeReaderOptions(); _reader = PipeReader.Create(inputStream, readerOpts); var writerOptions = new StreamPipeWriterOptions(leaveOpen: true); _writer = PipeWriter.Create(outputStream, writerOptions); _outStream = outputStream; _inStream = inputStream; }
public async Task OnConnectionAsync(ConnectionContext context) { var inputStream = context.Transport.Input.AsStream(); var outputStream = context.Transport.Output.AsStream(); var protocol = new TlsServerProtocol(inputStream, outputStream, new Org.BouncyCastle.Security.SecureRandom()); protocol.Accept(new BlazeTlsServer(_options)); var sslStream = protocol.Stream; var memoryPool = context.Features.Get <IMemoryPoolFeature>()?.MemoryPool; var inputPipeOptions = new StreamPipeReaderOptions ( pool: memoryPool, bufferSize: memoryPool.GetMinimumSegmentSize(), minimumReadSize: memoryPool.GetMinimumAllocSize(), leaveOpen: true ); var outputPipeOptions = new StreamPipeWriterOptions ( pool: memoryPool, leaveOpen: true ); var sslDuplexPipe = new DuplexPipeStreamAdapter <Stream>(context.Transport, inputPipeOptions, outputPipeOptions, sslStream); var originalTransport = context.Transport; try { context.Transport = sslDuplexPipe; // Disposing the stream will dispose the sslDuplexPipe await using (sslStream) await using (sslDuplexPipe) { await _next(context).ConfigureAwait(false); // Dispose the inner stream (SslDuplexPipe) before disposing the SslStream // as the duplex pipe can hit an ODE as it still may be writing. } } finally { context.Transport = originalTransport; } }
public async Task WritesUsingGetMemoryWorks() { var bytes = Encoding.ASCII.GetBytes("abcdefghijklmnopqrstuvwzyz"); var stream = new MemoryStream(); var options = new StreamPipeWriterOptions(new HeapBufferPool(), minimumBufferSize: 1); PipeWriter writer = PipeWriter.Create(stream, options); for (int i = 0; i < bytes.Length; i++) { writer.GetMemory().Span[0] = bytes[i]; writer.Advance(1); } await writer.FlushAsync(); Assert.Equal(bytes, stream.ToArray()); writer.Complete(); }
private SslDuplexPipe CreateSslDuplexPipe(IDuplexPipe transport, MemoryPool <byte> memoryPool) { var inputPipeOptions = new StreamPipeReaderOptions ( pool: memoryPool, bufferSize: memoryPool.GetMinimumSegmentSize(), minimumReadSize: memoryPool.GetMinimumAllocSize(), leaveOpen: true ); var outputPipeOptions = new StreamPipeWriterOptions ( pool: memoryPool, leaveOpen: true ); return(new SslDuplexPipe(transport, inputPipeOptions, outputPipeOptions, _sslStreamFactory)); }
public void GetMemoryBiggerThanPoolSizeAllocatesArrayPoolArray() { using (var pool = new DisposeTrackingBufferPool()) { var stream = new MemoryStream(); var options = new StreamPipeWriterOptions(pool); PipeWriter writer = PipeWriter.Create(stream, options); Memory <byte> memory = writer.GetMemory(pool.MaxBufferSize + 1); Assert.True(memory.Length > pool.MaxBufferSize + 1); Assert.Equal(0, pool.CurrentlyRentedBlocks); Assert.Equal(0, pool.DisposedBlocks); writer.Complete(); Assert.Equal(0, pool.CurrentlyRentedBlocks); Assert.Equal(0, pool.DisposedBlocks); } }
public async Task WritesUsingGetSpanWorks() { byte[] bytes = "abcdefghijklmnopqrstuvwzyz" u8.ToArray(); var stream = new MemoryStream(); var options = new StreamPipeWriterOptions(new HeapBufferPool(), minimumBufferSize: 1); PipeWriter writer = PipeWriter.Create(stream, options); for (int i = 0; i < bytes.Length; i++) { writer.GetSpan()[0] = bytes[i]; writer.Advance(1); } await writer.FlushAsync(); Assert.Equal(bytes, stream.ToArray()); writer.Complete(); Assert.Equal(0, writer.UnflushedBytes); }
public void GetMemorySameAsTheMaxPoolSizeUsesThePool() { using (var pool = new DisposeTrackingBufferPool()) { byte[] bytes = Encoding.ASCII.GetBytes("Hello World"); var stream = new MemoryStream(); var options = new StreamPipeWriterOptions(pool); PipeWriter writer = PipeWriter.Create(stream, options); Memory <byte> memory = writer.GetMemory(pool.MaxBufferSize); Assert.Equal(pool.MaxBufferSize, memory.Length); Assert.Equal(1, pool.CurrentlyRentedBlocks); Assert.Equal(0, pool.DisposedBlocks); writer.Complete(); Assert.Equal(0, pool.CurrentlyRentedBlocks); Assert.Equal(1, pool.DisposedBlocks); } }
public void CompletingWithBufferedBytesStillReturnsMemoryToPool() { using (var pool = new DisposeTrackingBufferPool()) { byte[] bytes = Encoding.ASCII.GetBytes("Hello World"); var stream = new MemoryStream(); var options = new StreamPipeWriterOptions(pool); PipeWriter writer = PipeWriter.Create(stream, options); bytes.AsSpan().CopyTo(writer.GetSpan(bytes.Length)); writer.Advance(bytes.Length); Assert.Equal(1, pool.CurrentlyRentedBlocks); Assert.Equal(0, pool.DisposedBlocks); Assert.Equal(0, stream.Length); writer.Complete(); Assert.Equal(0, pool.CurrentlyRentedBlocks); Assert.Equal(1, pool.DisposedBlocks); } }
public DuplexPipeStreamAdapter(IDuplexPipe duplexPipe, StreamPipeReaderOptions readerOptions, StreamPipeWriterOptions writerOptions, Func <Stream, TStream> createStream) : base(duplexPipe.Input, duplexPipe.Output) { Stream = createStream(this); var outputOptions = new PipeOptions(pool: writerOptions.Pool, readerScheduler: PipeScheduler.Inline, writerScheduler: PipeScheduler.Inline, pauseWriterThreshold: 1, resumeWriterThreshold: 1, minimumSegmentSize: writerOptions.MinimumBufferSize, useSynchronizationContext: false); Input = PipeReader.Create(Stream, readerOptions); // We're using a pipe here because the HTTP/2 stack in Kestrel currently makes assumptions // about when it is ok to write to the PipeWriter. This should be reverted back to PipeWriter.Create once // those patterns are fixed. _output = new Pipe(outputOptions); }
private async Task InnerOnConnectionAsync(ConnectionContext context) { var feature = new TlsConnectionFeature(); context.Features.Set<ITlsConnectionFeature>(feature); context.Features.Set<ITlsHandshakeFeature>(feature); var memoryPool = context.Features.Get<IMemoryPoolFeature>()?.MemoryPool; var inputPipeOptions = new StreamPipeReaderOptions ( pool: memoryPool, bufferSize: memoryPool.GetMinimumSegmentSize(), minimumReadSize: memoryPool.GetMinimumAllocSize(), leaveOpen: true ); var outputPipeOptions = new StreamPipeWriterOptions ( pool: memoryPool, leaveOpen: true ); TlsDuplexPipe tlsDuplexPipe = null; if (_options.RemoteCertificateMode == RemoteCertificateMode.NoCertificate) { tlsDuplexPipe = new TlsDuplexPipe(context.Transport, inputPipeOptions, outputPipeOptions); } else { tlsDuplexPipe = new TlsDuplexPipe(context.Transport, inputPipeOptions, outputPipeOptions, s => new SslStream( s, leaveInnerStreamOpen: false, userCertificateValidationCallback: (sender, certificate, chain, sslPolicyErrors) => { if (certificate == null) { return _options.RemoteCertificateMode != RemoteCertificateMode.RequireCertificate; } if (_options.RemoteCertificateValidation == null) { if (sslPolicyErrors != SslPolicyErrors.None) { return false; } } var certificate2 = ConvertToX509Certificate2(certificate); if (certificate2 == null) { return false; } if (_options.RemoteCertificateValidation != null) { if (!_options.RemoteCertificateValidation(certificate2, chain, sslPolicyErrors)) { return false; } } return true; })); } var sslStream = tlsDuplexPipe.Stream; using (var cancellationTokeSource = new CancellationTokenSource(_options.HandshakeTimeout)) using (cancellationTokeSource.Token.UnsafeRegisterCancellation(state => ((ConnectionContext)state).Abort(), context)) { try { var sslOptions = new TlsClientAuthenticationOptions { ClientCertificates = new X509CertificateCollection(new[] { _certificate }), EnabledSslProtocols = _options.SslProtocols, }; _options.OnAuthenticateAsClient?.Invoke(context, sslOptions); #if NETCOREAPP await sslStream.AuthenticateAsClientAsync(sslOptions.Value, cancellationTokeSource.Token); #else await sslStream.AuthenticateAsClientAsync( sslOptions.TargetHost, sslOptions.ClientCertificates, sslOptions.EnabledSslProtocols, sslOptions.CertificateRevocationCheckMode == X509RevocationMode.Online); #endif } catch (OperationCanceledException) { _logger?.LogDebug(2, "Authentication timed out"); #if NETCOREAPP await sslStream.DisposeAsync(); #else sslStream.Dispose(); #endif return; } catch (Exception ex) when (ex is IOException || ex is AuthenticationException) { _logger?.LogDebug(1, ex, "Authentication failed"); #if NETCOREAPP await sslStream.DisposeAsync(); #else sslStream.Dispose(); #endif return; } } #if NETCOREAPP feature.ApplicationProtocol = sslStream.NegotiatedApplicationProtocol.Protocol; #endif context.Features.Set<ITlsApplicationProtocolFeature>(feature); feature.LocalCertificate = ConvertToX509Certificate2(sslStream.LocalCertificate); feature.RemoteCertificate = ConvertToX509Certificate2(sslStream.RemoteCertificate); feature.CipherAlgorithm = sslStream.CipherAlgorithm; feature.CipherStrength = sslStream.CipherStrength; feature.HashAlgorithm = sslStream.HashAlgorithm; feature.HashStrength = sslStream.HashStrength; feature.KeyExchangeAlgorithm = sslStream.KeyExchangeAlgorithm; feature.KeyExchangeStrength = sslStream.KeyExchangeStrength; feature.Protocol = sslStream.SslProtocol; var originalTransport = context.Transport; try { context.Transport = tlsDuplexPipe; // Disposing the stream will dispose the tlsDuplexPipe #if NETCOREAPP await using (sslStream) await using (tlsDuplexPipe) #else using (sslStream) using (tlsDuplexPipe) #endif { await _next(context); // Dispose the inner stream (tlsDuplexPipe) before disposing the SslStream // as the duplex pipe can hit an ODE as it still may be writing. } } finally { // Restore the original so that it gets closed appropriately context.Transport = originalTransport; } }
public DuplexPipeStreamAdapter(IDuplexPipe duplexPipe, StreamPipeReaderOptions readerOptions, StreamPipeWriterOptions writerOptions, Func <Stream, TStream> createStream) : base(duplexPipe.Input, duplexPipe.Output, throwOnCancelled : true) { Stream = createStream(this); var inputOptions = new PipeOptions(pool: readerOptions.Pool, readerScheduler: PipeScheduler.ThreadPool, writerScheduler: PipeScheduler.Inline, pauseWriterThreshold: 1, resumeWriterThreshold: 1, minimumSegmentSize: readerOptions.Pool.GetMinimumSegmentSize(), useSynchronizationContext: false); _minAllocBufferSize = writerOptions.MinimumBufferSize; _input = new Pipe(inputOptions); Output = PipeWriter.Create(Stream, writerOptions); }
public DuplexPipeStreamAdapter(IDuplexPipe duplexPipe, StreamPipeReaderOptions readerOptions, StreamPipeWriterOptions writerOptions, TStream stream) { Stream = stream; Input = PipeReader.Create(stream, readerOptions); Output = PipeWriter.Create(stream, writerOptions); }
/// <summary> /// Writes the object asynchronously as bencode to the specified <see cref="Stream"/> using a <see cref="PipeWriter"/>. /// </summary> /// <param name="stream">The stream to write to.</param> /// <param name="writerOptions">The options for the <see cref="PipeWriter"/>.</param> /// <param name="cancellationToken"></param> public ValueTask <FlushResult> EncodeToAsync(Stream stream, StreamPipeWriterOptions writerOptions = null, CancellationToken cancellationToken = default) { return(EncodeObjectAsync(PipeWriter.Create(stream, writerOptions), cancellationToken)); }
public DuplexPipeStreamAdapter(IDuplexPipe duplexPipe, StreamPipeReaderOptions readerOptions, StreamPipeWriterOptions writerOptions, Func <Stream, TStream> createStream) : base(duplexPipe.Input, duplexPipe.Output) { Stream = createStream(this); Input = PipeReader.Create(Stream, readerOptions); Output = PipeWriter.Create(Stream, writerOptions); }
public SslDuplexPipe(IDuplexPipe transport, StreamPipeReaderOptions readerOptions, StreamPipeWriterOptions writerOptions) : this(transport, readerOptions, writerOptions, s => new SslStream(s)) { }
public PipeAdapter(Stream stream, StreamPipeReaderOptions readerOptions, StreamPipeWriterOptions writerOptions) { _stream = stream; _reader = PipeReader.Create(_stream, readerOptions); _writer = PipeWriter.Create(_stream, writerOptions); }
public DuplexPipeStreamAdapter(IDuplexPipe duplexPipe, StreamPipeReaderOptions readerOptions, StreamPipeWriterOptions writerOptions, Func <Stream, TStream> createStream) : base(duplexPipe.Input, duplexPipe.Output) { var stream = createStream(this); Stream = stream; this.WrappedInput = new StreamPipelineReader(PipeReader.Create(stream, readerOptions)); this.WrappedOutput = new StreamPipelineWriter(PipeWriter.Create(stream, writerOptions)); }
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; } }
private async Task InnerOnConnectionAsync(ConnectionContext context) { bool certificateRequired; var feature = new Core.Internal.TlsConnectionFeature(); context.Features.Set <ITlsConnectionFeature>(feature); context.Features.Set <ITlsHandshakeFeature>(feature); var memoryPool = context.Features.Get <IMemoryPoolFeature>()?.MemoryPool; var inputPipeOptions = new StreamPipeReaderOptions ( pool: memoryPool, bufferSize: memoryPool.GetMinimumSegmentSize(), minimumReadSize: memoryPool.GetMinimumAllocSize(), leaveOpen: true ); var outputPipeOptions = new StreamPipeWriterOptions ( pool: memoryPool, leaveOpen: true ); SslDuplexPipe sslDuplexPipe = null; if (_options.ClientCertificateMode == ClientCertificateMode.NoCertificate) { sslDuplexPipe = new SslDuplexPipe(context.Transport, inputPipeOptions, outputPipeOptions); certificateRequired = false; } else { sslDuplexPipe = new SslDuplexPipe(context.Transport, inputPipeOptions, outputPipeOptions, s => new SslStream(s, leaveInnerStreamOpen: false, userCertificateValidationCallback: (sender, certificate, chain, sslPolicyErrors) => { if (certificate == null) { return(_options.ClientCertificateMode != ClientCertificateMode.RequireCertificate); } if (_options.ClientCertificateValidation == null) { if (sslPolicyErrors != SslPolicyErrors.None) { return(false); } } var certificate2 = ConvertToX509Certificate2(certificate); if (certificate2 == null) { return(false); } if (_options.ClientCertificateValidation != null) { if (!_options.ClientCertificateValidation(certificate2, chain, sslPolicyErrors)) { return(false); } } return(true); })); certificateRequired = true; } var sslStream = sslDuplexPipe.Stream; using (var cancellationTokeSource = new CancellationTokenSource(_options.HandshakeTimeout)) using (cancellationTokeSource.Token.UnsafeRegister(state => ((ConnectionContext)state).Abort(), context)) { try { // Adapt to the SslStream signature ServerCertificateSelectionCallback selector = null; if (_serverCertificateSelector != null) { selector = (sender, name) => { context.Features.Set(sslStream); var cert = _serverCertificateSelector(context, name); if (cert != null) { EnsureCertificateIsAllowedForServerAuth(cert); } return(cert); }; } var sslOptions = new SslServerAuthenticationOptions { ServerCertificate = _serverCertificate, ServerCertificateSelectionCallback = selector, ClientCertificateRequired = certificateRequired, EnabledSslProtocols = _options.SslProtocols, CertificateRevocationCheckMode = _options.CheckCertificateRevocation ? X509RevocationMode.Online : X509RevocationMode.NoCheck, ApplicationProtocols = new List <SslApplicationProtocol>() }; // This is order sensitive if ((_options.HttpProtocols & HttpProtocols.Http2) != 0) { sslOptions.ApplicationProtocols.Add(SslApplicationProtocol.Http2); // https://tools.ietf.org/html/rfc7540#section-9.2.1 sslOptions.AllowRenegotiation = false; } if ((_options.HttpProtocols & HttpProtocols.Http1) != 0) { sslOptions.ApplicationProtocols.Add(SslApplicationProtocol.Http11); } _options.OnAuthenticate?.Invoke(context, sslOptions); await sslStream.AuthenticateAsServerAsync(sslOptions, CancellationToken.None); } catch (OperationCanceledException) { _logger?.LogDebug(2, CoreStrings.AuthenticationTimedOut); await sslStream.DisposeAsync(); return; } catch (Exception ex) when(ex is IOException || ex is AuthenticationException) { _logger?.LogDebug(1, ex, CoreStrings.AuthenticationFailed); await sslStream.DisposeAsync(); return; } } feature.ApplicationProtocol = sslStream.NegotiatedApplicationProtocol.Protocol; context.Features.Set <ITlsApplicationProtocolFeature>(feature); feature.ClientCertificate = ConvertToX509Certificate2(sslStream.RemoteCertificate); feature.CipherAlgorithm = sslStream.CipherAlgorithm; feature.CipherStrength = sslStream.CipherStrength; feature.HashAlgorithm = sslStream.HashAlgorithm; feature.HashStrength = sslStream.HashStrength; feature.KeyExchangeAlgorithm = sslStream.KeyExchangeAlgorithm; feature.KeyExchangeStrength = sslStream.KeyExchangeStrength; feature.Protocol = sslStream.SslProtocol; var originalTransport = context.Transport; try { context.Transport = sslDuplexPipe; // Disposing the stream will dispose the sslDuplexPipe await using (sslStream) await using (sslDuplexPipe) { await _next(context); // Dispose the inner stream (SslDuplexPipe) before disposing the SslStream // as the duplex pipe can hit an ODE as it still may be writing. } } finally { // Restore the original so that it gets closed appropriately context.Transport = originalTransport; } }
private async Task RunAsync() { try { var address = BindingAddress.Parse(Options.Url); if (!IPAddress.TryParse(address.Host, out var ip)) { ip = Dns.GetHostEntry(address.Host).AddressList.First(); } var endpoint = new IPEndPoint(ip, address.Port); _logger.LogInformation($"Connecting to '{endpoint}'."); await using var context = await _connectionFactory.ConnectAsync(endpoint); _logger.LogInformation($"Connected to '{endpoint}'."); var originalTransport = context.Transport; IAsyncDisposable sslState = null; if (address.Scheme.Equals("https", StringComparison.OrdinalIgnoreCase)) { _logger.LogInformation("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); var sslDuplexPipe = new SslDuplexPipe(context.Transport, inputPipeOptions, outputPipeOptions); var sslStream = sslDuplexPipe.Stream; sslState = sslDuplexPipe; context.Transport = sslDuplexPipe; await sslStream.AuthenticateAsClientAsync(new SslClientAuthenticationOptions { TargetHost = address.Host, RemoteCertificateValidationCallback = (_, __, ___, ____) => true, ApplicationProtocols = new List <SslApplicationProtocol> { SslApplicationProtocol.Http2 }, EnabledSslProtocols = SslProtocols.Tls12, }, CancellationToken.None); _logger.LogInformation($"TLS handshake completed successfully."); } var http2Utilities = new Http2Utilities(context, _logger, _stopTokenSource.Token); try { await Options.Scenaro(http2Utilities); } catch (Exception ex) { _logger.LogError(ex, "App error"); throw; } finally { // Unwind Https for shutdown. This must happen before the context goes out of scope or else DisposeAsync will never complete context.Transport = originalTransport; if (sslState != null) { await sslState.DisposeAsync(); } } } finally { HostApplicationLifetime.StopApplication(); } }