Example #1
0
        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);

            var outputOptions = new PipeOptions(pool: writerOptions.Pool,
                                                readerScheduler: PipeScheduler.Inline,
                                                writerScheduler: PipeScheduler.Inline,
                                                pauseWriterThreshold: 1,
                                                resumeWriterThreshold: 1,
                                                minimumSegmentSize: writerOptions.MinimumBufferSize,
                                                useSynchronizationContext: false);

            _minAllocBufferSize = writerOptions.MinimumBufferSize;

            _input = new Pipe(inputOptions);

            // 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);
        }
Example #2
0
        protected override async Task ExecuteAsync(CancellationToken cancellationToken)
        {
            _logger.LogInformation("Worker started");
            _payloadReadyEvent.Wait(cancellationToken);
            _payloadReadyEvent.Reset();

            while (!cancellationToken.IsCancellationRequested)
            {
                _logger.LogInformation("Worker running at: {time}", DateTimeOffset.UtcNow);
                if (!File.Exists(Constants.EXPECTED_ZIP_FILE_NAME))
                {
                    // The package hasn't arrived yet
                    _logger.LogDebug("Compressed payload not found {filename}", Constants.EXPECTED_ZIP_FILE_NAME);
                    await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken);

                    continue;
                }

                using ZipArchive zipArchive = ZipFile.OpenRead(Constants.EXPECTED_ZIP_FILE_NAME);
                ZipArchiveEntry dataEntry      = zipArchive.Entries.SingleOrDefault();
                Stream          zipEntryStream = dataEntry.Open();

                StreamPipeReaderOptions streamOptions =
                    new StreamPipeReaderOptions(bufferSize: MINIMUM_BUFFER_SIZE);
                PipeReader pipeReader = PipeReader.Create(zipEntryStream, streamOptions);

                while (true)
                {
                    _logger.LogDebug("Reading stream into pipe buffer");
                    ReadResult result = await pipeReader.ReadAsync(cancellationToken);

                    ReadOnlySequence <byte> buffer = result.Buffer;

                    // Notify the PipeReader how much buffer was used
                    pipeReader.AdvanceTo(buffer.Start, buffer.End);

                    if (!result.IsCompleted)
                    {
                        continue;
                    }

                    _logger.LogDebug("Payload read");
                    JsonDocument documentFromBuffer = InspectBuffer(buffer);
                    _ = await _payloadChannel.AddPayloadAsync(documentFromBuffer, cancellationToken);

                    break;
                }

                await pipeReader.CompleteAsync();

                await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken);

                return;
            }

            if (cancellationToken.IsCancellationRequested)
            {
                _logger.LogWarning("Operation cancelled");
            }
        }
        public async Task CompletingReturnsUnconsumedMemoryToPool()
        {
            using (var pool = new DisposeTrackingBufferPool())
            {
                var options = new StreamPipeReaderOptions(pool: pool, bufferSize: 4096, minimumReadSize: 1024);
                // 2 full segments
                var        stream = new MemoryStream(new byte[options.BufferSize * 3]);
                PipeReader reader = PipeReader.Create(stream, options);

                while (true)
                {
                    ReadResult readResult = await reader.ReadAsync();

                    reader.AdvanceTo(readResult.Buffer.Start, readResult.Buffer.End);

                    if (readResult.IsCompleted)
                    {
                        break;
                    }
                }

                Assert.Equal(4, pool.CurrentlyRentedBlocks);
                reader.Complete();
                Assert.Equal(0, pool.CurrentlyRentedBlocks);
                Assert.Equal(4, pool.DisposedBlocks);
            }
        }
Example #4
0
 public SslStreamDuplexPipe(
     IDuplexPipe transport,
     StreamPipeReaderOptions readerOptions,
     StreamPipeWriterOptions writerOptions,
     Func <Stream, SslStream> factory)
     : base(transport, readerOptions, writerOptions, factory)
 {
 }
Example #5
0
        public async Task CanReadMultipleTimes()
        {
            // This needs to run inline to synchronize the reader and writer
            TaskCompletionSource <object> waitForRead = null;
            var protocol = new TestProtocol();

            async Task DoAsyncRead(PipeReader reader, int[] bufferSizes)
            {
                var index = 0;

                while (true)
                {
                    var readResult = await reader.ReadAsync().ConfigureAwait(false);

                    if (readResult.IsCompleted)
                    {
                        break;
                    }

                    Assert.Equal(bufferSizes[index], readResult.Buffer.Length);
                    reader.AdvanceTo(readResult.Buffer.End);
                    index++;
                    waitForRead?.TrySetResult(null);
                }

                reader.Complete();
            }

            async Task DoAsyncWrites(PipeWriter writer, int[] bufferSizes)
            {
                for (var i = 0; i < bufferSizes.Length; i++)
                {
                    writer.WriteEmpty(protocol, bufferSizes[i]);
                    waitForRead = new TaskCompletionSource <object>();
                    await writer.FlushAsync().ConfigureAwait(false);

                    await waitForRead.Task;
                }

                writer.Complete();
            }

            // We're using the pipe here as a way to pump bytes into the reader asynchronously
            var pipe    = new Pipe();
            var options = new StreamPipeReaderOptions(bufferSize: 4096);
            var reader  = new MessagePipeReader(PipeReader.Create(pipe.Reader.AsStream(), options), protocol);

            var writes = new[] { 4096, 1024, 123, 4096, 100 };

            var readingTask = DoAsyncRead(reader, writes);
            var writingTask = DoAsyncWrites(pipe.Writer, writes);

            await readingTask;
            await writingTask;

            pipe.Reader.Complete();
        }
Example #6
0
        public DuplexPipeStreamAdapter(IDuplexPipe duplexPipe, StreamPipeReaderOptions readerOptions, StreamPipeWriterOptions writerOptions, Func <Stream, TStream> createStream) :
            base(duplexPipe.Input, duplexPipe.Output)
        {
            var stream = createStream(this);

            Stream = stream;
            Input  = PipeReader.Create(stream, readerOptions);
            Output = PipeWriter.Create(stream, writerOptions);
        }
        public void StreamPipeReaderOptions_Ctor_Defaults()
        {
            var options = new StreamPipeReaderOptions();

            Assert.Same(MemoryPool <byte> .Shared, options.Pool);
            Assert.Equal(4096, options.BufferSize);
            Assert.Equal(1024, options.MinimumReadSize);
            Assert.False(options.LeaveOpen);
        }
Example #8
0
        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 void StreamPipeReaderOptions_Ctor_Roundtrip()
 {
     using (var pool = new TestMemoryPool())
     {
         var options = new StreamPipeReaderOptions(pool: pool, bufferSize: 1234, minimumReadSize: 5678, leaveOpen: true);
         Assert.Same(pool, options.Pool);
         Assert.Equal(1234, options.BufferSize);
         Assert.Equal(5678, options.MinimumReadSize);
         Assert.True(options.LeaveOpen);
     }
 }
        public void Setup()
        {
            _midLevelParser = new GraphQlResponseParser();
            _lowLevelParser = new LowLevelGraphQlResponseParser();
            _stream         = JsonStreamFactory.CreateGraphQlResponseStream();
            _emptyStream    = JsonStreamFactory.CreateEmptyGraphQlResponseStream();

            // needed for benchmarking to avoid stream closure
            // passed in as argument since under normal use we won't need to allocate this and can allow the stream to close
            _streamPipeReaderOptions = new StreamPipeReaderOptions(leaveOpen: true);
        }
        public static StreamPipeReaderOptions ReaderOptionsCreator(MemoryPool <byte> memoryPool)
        {
            var inputPipeOptions = new StreamPipeReaderOptions
                                   (
                pool: memoryPool,
                bufferSize: memoryPool.MaxBufferSize,
                minimumReadSize: memoryPool.MaxBufferSize / 2,
                leaveOpen: true
                                   );

            return(inputPipeOptions);
        }
        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;
            }
        }
Example #13
0
        public async Task ConsumingSegmentsReturnsMemoryToPool()
        {
            using (var pool = new DisposeTrackingBufferPool())
            {
                var options = new StreamPipeReaderOptions(pool: pool, bufferSize: 4096, minimumReadSize: 1024);
                // 2 full segments
                var        stream = new MemoryStream(new byte[options.BufferSize * 2]);
                PipeReader reader = PipeReader.Create(stream, options);

                ReadResult readResult = await reader.ReadAsync();

                ReadOnlySequence <byte> buffer = readResult.Buffer;
                Assert.Equal(1, pool.CurrentlyRentedBlocks);
                Assert.Equal(options.BufferSize, buffer.Length);
                reader.AdvanceTo(buffer.Start, buffer.End);

                readResult = await reader.ReadAsync();

                buffer = readResult.Buffer;
                Assert.Equal(options.BufferSize * 2, buffer.Length);
                Assert.Equal(2, pool.CurrentlyRentedBlocks);
                reader.AdvanceTo(buffer.Start, buffer.End);

                readResult = await reader.ReadAsync();

                buffer = readResult.Buffer;
                Assert.Equal(options.BufferSize * 2, buffer.Length);
                // We end up allocating a 3rd block here since we don't know ahead of time that
                // it's the last one
                Assert.Equal(3, pool.CurrentlyRentedBlocks);

                reader.AdvanceTo(buffer.Slice(buffer.Start, 4096).End, buffer.End);

                Assert.Equal(2, pool.CurrentlyRentedBlocks);
                Assert.Equal(1, pool.DisposedBlocks);

                readResult = await reader.ReadAsync();

                buffer = readResult.Buffer;
                Assert.Equal(options.BufferSize, buffer.Length);
                reader.AdvanceTo(buffer.Slice(buffer.Start, 4096).End, buffer.End);

                // All of the blocks get returned here since we hit the first case of emptying the entire list
                Assert.Equal(0, pool.CurrentlyRentedBlocks);
                Assert.Equal(3, pool.DisposedBlocks);

                reader.Complete();
            }
        }
Example #14
0
        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);
        }
        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));
        }
Example #16
0
        public async Task ReadWithDifferentSettings(int bytesInBuffer, int bufferSize, int minimumReadSize, int[] readBufferSizes)
        {
            var        options = new StreamPipeReaderOptions(bufferSize: bufferSize, minimumReadSize: minimumReadSize, pool: new HeapBufferPool());
            var        stream  = new MemoryStream(Enumerable.Range(0, bytesInBuffer).Select(i => (byte)i).ToArray());
            PipeReader reader  = PipeReader.Create(stream, options);

            for (int i = 0; i < readBufferSizes.Length; i++)
            {
                ReadResult readResult = await reader.ReadAsync();

                long length = readResult.Buffer.Length;
                Assert.Equal(readBufferSizes[i], length);
                reader.AdvanceTo(readResult.Buffer.End);
                if (length == 0)
                {
                    Assert.True(readResult.IsCompleted);
                }
            }

            reader.Complete();
        }
        public async Task <UserDataModel> ParseResponseJsonAsync(Stream stream, StreamPipeReaderOptions options)
        {
            var pipeReader = PipeReader.Create(stream, options);

            var state = new JsonReaderState();

            while (true)
            {
                var result = await pipeReader.ReadAsync(); // read from the pipe

                var buffer = result.Buffer;

                var(userDataModel, arrayIsEmpty) = ParseJson(buffer, result.IsCompleted, ref state);  // read complete items from the current buffer

                if (result.IsCompleted || userDataModel != null || arrayIsEmpty)
                {
                    pipeReader.Complete(); // mark the PipeReader as complete
                    return(userDataModel);
                }

                pipeReader.AdvanceTo(buffer.Start, buffer.End); // only mark as examined until we have enough buffered data to read what we need
            }
        }
Example #18
0
        public async Task NewSegmentsAllocatedWhenBufferReachesMinimumReadSize()
        {
            // We're using the pipe here as a way to pump bytes into the reader asynchronously
            var        pipe    = new Pipe();
            var        options = new StreamPipeReaderOptions(pool: new HeapBufferPool(), bufferSize: 10, minimumReadSize: 5);
            PipeReader reader  = PipeReader.Create(pipe.Reader.AsStream(), options);

            pipe.Writer.WriteEmpty(6);
            await pipe.Writer.FlushAsync();

            ReadResult readResult = await reader.ReadAsync();

            Assert.Equal(6, readResult.Buffer.Length);
            Assert.True(readResult.Buffer.IsSingleSegment);
            reader.AdvanceTo(readResult.Buffer.Start, readResult.Buffer.End);

            pipe.Writer.WriteEmpty(4);
            await pipe.Writer.FlushAsync();

            readResult = await reader.ReadAsync();

            Assert.Equal(10, readResult.Buffer.Length);
            Assert.False(readResult.Buffer.IsSingleSegment);
            var segments = 0;

            foreach (ReadOnlyMemory <byte> segment in readResult.Buffer)
            {
                segments++;
            }
            Assert.Equal(2, segments);
            reader.AdvanceTo(readResult.Buffer.End);

            reader.Complete();

            pipe.Writer.Complete();
        }
        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;
            }
        }
 public SslDuplexPipe(IDuplexPipe transport, StreamPipeReaderOptions readerOptions, StreamPipeWriterOptions writerOptions)
     : this(transport, readerOptions, writerOptions, s => new SslStream(s))
 {
 }
Example #21
0
        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;
            }
        }
Example #22
0
 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, TStream stream)
 {
     Stream = stream;
     Input  = PipeReader.Create(stream, readerOptions);
     Output = PipeWriter.Create(stream, writerOptions);
 }
        /// <summary>
        /// Parses an <see cref="IBObject"/>  of type <typeparamref name="T"/> from the <see cref="Stream"/> asynchronously using a <see cref="PipeReader"/>.
        /// </summary>
        /// <typeparam name="T">The type of <see cref="IBObject"/> to parse as.</typeparam>
        public static ValueTask <T> ParseAsync <T>(this IBencodeParser parser, Stream stream, StreamPipeReaderOptions readerOptions = null, CancellationToken cancellationToken = default) where T : class, IBObject
        {
            var reader = PipeReader.Create(stream, readerOptions);

            return(parser.ParseAsync <T>(reader, cancellationToken));
        }
Example #25
0
            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 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();
        }
    }