< Summary - CoreWCF Coverage — PR #1766

Information
Class: CoreWCF.Channels.Framing.DuplexPipeStream
Assembly: CoreWCF.NetTcp
File(s): /home/runner/work/CoreWCF/CoreWCF/src/CoreWCF.NetTcp/src/CoreWCF/Channels/Framing/DuplexPipeStream.cs
Line coverage
0%
Covered lines: 0
Uncovered lines: 67
Coverable lines: 67
Total lines: 238
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 16
Branch coverage: 0%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Cyclomatic complexity NPath complexity Sequence coverage
.ctor(...)100%110%
Seek(...)100%110%
SetLength(...)100%110%
Read(...)0%220%
ReadAsync(...)100%110%
Write(...)100%110%
WriteAsync(...)100%110%
Flush()100%110%
FlushAsync(...)100%110%
ReadAsyncInternal()0%440%
BeginRead(...)100%110%
EndRead(...)100%110%
BeginWrite(...)100%110%
EndWrite(...)100%110%
Dispose(...)100%110%
DisposeAsync()100%110%
SetContentType(...)0%220%
LengthToConsume(...)0%880%
.cctor()100%110%
LengthToConsume(...)100%110%

File(s)

/home/runner/work/CoreWCF/CoreWCF/src/CoreWCF.NetTcp/src/CoreWCF/Channels/Framing/DuplexPipeStream.cs

#LineLine coverage
 1// Licensed to the .NET Foundation under one or more agreements.
 2// The .NET Foundation licenses this file to you under the MIT license.
 3
 4using System;
 5using System.Buffers;
 6using System.IO;
 7using System.IO.Pipelines;
 8using System.Threading;
 9using System.Threading.Tasks;
 10using CoreWCF.Runtime;
 11
 12namespace CoreWCF.Channels.Framing
 13{
 14    internal class DuplexPipeStream : Stream, IAsyncDisposable
 15    {
 16        private readonly PipeReader _input;
 17        private readonly PipeWriter _output;
 18        private IInputLengthDecider _inputLengthDecider;
 19
 020        public DuplexPipeStream(PipeReader input, PipeWriter output)
 21        {
 022            _input = input;
 023            _output = output;
 024            _inputLengthDecider = NoopInputLengthDecider.Instance;
 025        }
 26
 027        public override bool CanRead => true;
 28
 029        public override bool CanSeek => false;
 30
 031        public override bool CanWrite => true;
 32
 033        public override long Length => throw new NotSupportedException();
 34
 35        public override long Position
 36        {
 37            get
 38            {
 039                throw new NotSupportedException();
 40            }
 41            set
 42            {
 043                throw new NotSupportedException();
 44            }
 45        }
 46
 47        public override long Seek(long offset, SeekOrigin origin)
 48        {
 049            throw new NotSupportedException();
 50        }
 51
 52        public override void SetLength(long value)
 53        {
 054            throw new NotSupportedException();
 55        }
 56
 57        public override int Read(byte[] buffer, int offset, int count)
 58        {
 059            ValueTask<int> vt = ReadAsyncInternal(new Memory<byte>(buffer, offset, count), default);
 060            return vt.IsCompleted ?
 061                vt.Result :
 062                vt.AsTask().GetAwaiter().GetResult();
 63        }
 64
 65        public override Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken = 
 66        {
 067            return ReadAsyncInternal(new Memory<byte>(buffer, offset, count), cancellationToken).AsTask();
 68        }
 69
 70        // TODO: Enable code when moving to .NET 5
 71        //public override ValueTask<int> ReadAsync(Memory<byte> destination, CancellationToken cancellationToken = defau
 72        //{
 73        //    return ReadAsyncInternal(destination, cancellationToken);
 74        //}
 75
 76        public override void Write(byte[] buffer, int offset, int count)
 77        {
 078            WriteAsync(buffer, offset, count).GetAwaiter().GetResult();
 079        }
 80
 81        public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
 82        {
 083            return _output.WriteAsync(buffer.AsMemory(offset, count), cancellationToken).AsTask();
 84        }
 85
 86        // TODO: Enable code when moving to .NET 5
 87        //public override ValueTask WriteAsync(ReadOnlyMemory<byte> source, CancellationToken cancellationToken = defaul
 88        //{
 89        //    return _output.WriteAsync(source, cancellationToken).GetAsValueTask();
 90        //}
 91
 92        public override void Flush()
 93        {
 094            FlushAsync(CancellationToken.None).GetAwaiter().GetResult();
 095        }
 96
 97        public override Task FlushAsync(CancellationToken cancellationToken)
 98        {
 099            return _output.FlushAsync(cancellationToken).AsTask();
 100        }
 101
 102        private async ValueTask<int> ReadAsyncInternal(Memory<byte> destination, CancellationToken cancellationToken)
 103        {
 104            while (true)
 105            {
 0106                var result = await _input.ReadAsync(cancellationToken);
 0107                var readableBuffer = result.Buffer;
 108                try
 109                {
 0110                    if (!readableBuffer.IsEmpty)
 111                    {
 112                        // buffer.Count is int
 0113                        var count = _inputLengthDecider.LengthToConsume(readableBuffer, destination.Length);
 0114                        readableBuffer = readableBuffer.Slice(0, count);
 0115                        readableBuffer.CopyTo(destination.Span);
 0116                        return count;
 117                    }
 118
 0119                    if (result.IsCompleted)
 120                    {
 0121                        return 0;
 122                    }
 0123                }
 124                finally
 125                {
 0126                    _input.AdvanceTo(readableBuffer.End, readableBuffer.End);
 127                }
 128            }
 0129        }
 130
 131        public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, object stat
 132        {
 0133            return ReadAsync(buffer, offset, count).ToApm(callback, state);
 134        }
 135
 136        public override int EndRead(IAsyncResult asyncResult)
 137        {
 0138            return asyncResult.ToApmEnd<int>();
 139        }
 140
 141        public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object sta
 142        {
 0143            return WriteAsync(buffer, offset, count).ToApm(callback, state);
 144        }
 145
 146        public override void EndWrite(IAsyncResult asyncResult)
 147        {
 0148            asyncResult.ToApmEnd();
 0149        }
 150
 151        protected override void Dispose(bool disposing)
 152        {
 0153            _input.Complete();
 0154            _output.Complete();
 0155            base.Dispose(disposing);
 0156        }
 157
 158        public async ValueTask DisposeAsync()
 159        {
 0160            await _input.CompleteAsync();
 0161            await _output.CompleteAsync();
 0162            base.Dispose(true);
 0163        }
 164
 165        internal void SetContentType(string contentType)
 166        {
 0167            if (contentType == "application/ssl-tls" /*FramingUpgradeString.SslOrTls*/)
 0168                _inputLengthDecider = new TlsInputLengthDecider();
 0169            else _inputLengthDecider = NoopInputLengthDecider.Instance;
 0170        }
 171
 172        internal interface IInputLengthDecider
 173        {
 174            int LengthToConsume(ReadOnlySequence<byte> buffer, int destinationLength);
 175        }
 176
 177        internal class TlsInputLengthDecider : IInputLengthDecider
 178        {
 179            // buffer[0] is TLS Frame content type, eg 23 = ApplicationData
 180            // buffer[1] and buffer[2] are the TLS version, eg 0x0303 == TLS 1.2
 181            // buffer[3] and buffer[4] and big endian integer for length
 182
 183            private int _frameSize;
 184
 185            public int LengthToConsume(ReadOnlySequence<byte> buffer, int destinationLength)
 186            {
 187                // Maximum number of bytes that can be read from incoming buffer. This is either the entire buffer if th
 188                // space in the destination, or however much space is available in the destination buffer
 0189                int maxRead = (int)Math.Min(buffer.Length, destinationLength);
 190                int bytesToRead;
 191
 192                // If there's still bytes left to be read from the current frame, we don't need to read the frame header
 193                // read the frame size if we've finished reading the previous frame.
 0194                if (_frameSize == 0)
 195                {
 0196                    if (buffer.Length < 5)
 197                    {
 198                        // Need at least 5 bytes to read size from frame header so presuming this isn't a TLS frame
 199                        // so indicating to consume everything that's possible.
 0200                        return maxRead;
 201                    }
 202
 0203                    if (buffer.IsSingleSegment || buffer.First.Length >= 5)
 204                    {
 205                        // We have enough bytes to read the frame size from the first segment
 0206                        var span = buffer.First.Span;
 207                        Fx.Assert(span[1] == 3, "Invalid TLS header");
 208                        // The length in the TLS header doesn't include the header itself so that needs to be added
 0209                        _frameSize = ((span[3] << 8) | span[4]) + 5;
 210                    }
 211                    else
 212                    {
 213                        // We have at least 5 bytes, but the first 5 bytes aren't in the first segment so get the 5 byte
 0214                        var frameHeaderBytes = buffer.Slice(0, 5).ToArray();
 215                        // The length in the TLS header doesn't include the header itself so that needs to be added
 0216                        _frameSize = ((frameHeaderBytes[3] << 8) | frameHeaderBytes[4]) + 5;
 217                    }
 218                }
 219
 220                // If the frame size is smaller than the number of bytes read from the input, then only read frame size 
 0221                bytesToRead = Math.Min(maxRead, _frameSize);
 222                // Decrement _frameSize by bytesToRead to store number of bytes still pending to be read in future reads
 0223                _frameSize -= bytesToRead;
 0224                return bytesToRead;
 225            }
 226        }
 227
 228        internal class NoopInputLengthDecider : IInputLengthDecider
 229        {
 0230            internal static IInputLengthDecider Instance = new NoopInputLengthDecider();
 231            public int LengthToConsume(ReadOnlySequence<byte> buffer, int destinationLength)
 232            {
 233                // Can't read more than there's space in the destination buffer
 0234                return (int)Math.Min(buffer.Length, destinationLength);
 235            }
 236        }
 237    }
 238}