< Summary - CoreWCF Coverage — PR #1766

Information
Class: CoreWCF.Channels.Framing.DuplexPipeStream
Assembly: CoreWCF.NetFramingBase
File(s): /home/runner/work/CoreWCF/CoreWCF/src/CoreWCF.NetFramingBase/src/CoreWCF/Channels/Framing/DuplexPipeStream.cs
Line coverage
50%
Covered lines: 34
Uncovered lines: 33
Coverable lines: 67
Total lines: 239
Line coverage: 50.7%
Branch coverage
56%
Covered branches: 9
Total branches: 16
Branch coverage: 56.2%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

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

File(s)

/home/runner/work/CoreWCF/CoreWCF/src/CoreWCF.NetFramingBase/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.Diagnostics;
 7using System.IO;
 8using System.IO.Pipelines;
 9using System.Threading;
 10using System.Threading.Tasks;
 11using CoreWCF.Runtime;
 12
 13namespace CoreWCF.Channels.Framing
 14{
 15    internal class DuplexPipeStream : Stream, IAsyncDisposable
 16    {
 17        private readonly PipeReader _input;
 18        private readonly PipeWriter _output;
 19        private IInputLengthDecider _inputLengthDecider;
 20
 821        public DuplexPipeStream(PipeReader input, PipeWriter output)
 22        {
 823            _input = input;
 824            _output = output;
 825            _inputLengthDecider = NoopInputLengthDecider.Instance;
 826        }
 27
 728        public override bool CanRead => true;
 29
 030        public override bool CanSeek => false;
 31
 732        public override bool CanWrite => true;
 33
 034        public override long Length => throw new NotSupportedException();
 35
 36        public override long Position
 37        {
 38            get
 39            {
 040                throw new NotSupportedException();
 41            }
 42            set
 43            {
 044                throw new NotSupportedException();
 45            }
 46        }
 47
 48        public override long Seek(long offset, SeekOrigin origin)
 49        {
 050            throw new NotSupportedException();
 51        }
 52
 53        public override void SetLength(long value)
 54        {
 055            throw new NotSupportedException();
 56        }
 57
 58        public override int Read(byte[] buffer, int offset, int count)
 59        {
 060            ValueTask<int> vt = ReadAsyncInternal(new Memory<byte>(buffer, offset, count), default);
 061            return vt.IsCompleted ?
 062                vt.Result :
 063                vt.AsTask().GetAwaiter().GetResult();
 64        }
 65
 66        public override Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken = 
 67        {
 14868            return ReadAsyncInternal(new Memory<byte>(buffer, offset, count), cancellationToken).AsTask();
 69        }
 70
 71        // TODO: Enable code when moving to .NET 5
 72        //public override ValueTask<int> ReadAsync(Memory<byte> destination, CancellationToken cancellationToken = defau
 73        //{
 74        //    return ReadAsyncInternal(destination, cancellationToken);
 75        //}
 76
 77        public override void Write(byte[] buffer, int offset, int count)
 78        {
 079            WriteAsync(buffer, offset, count).GetAwaiter().GetResult();
 080        }
 81
 82        public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
 83        {
 4184            return _output.WriteAsync(buffer.AsMemory(offset, count), cancellationToken).AsTask();
 85        }
 86
 87        // TODO: Enable code when moving to .NET 5
 88        //public override ValueTask WriteAsync(ReadOnlyMemory<byte> source, CancellationToken cancellationToken = defaul
 89        //{
 90        //    return _output.WriteAsync(source, cancellationToken).GetAsValueTask();
 91        //}
 92
 93        public override void Flush()
 94        {
 095            FlushAsync(CancellationToken.None).GetAwaiter().GetResult();
 096        }
 97
 98        public override Task FlushAsync(CancellationToken cancellationToken)
 99        {
 41100            return _output.FlushAsync(cancellationToken).AsTask();
 101        }
 102
 103        private async ValueTask<int> ReadAsyncInternal(Memory<byte> destination, CancellationToken cancellationToken)
 104        {
 105            while (true)
 106            {
 148107                var result = await _input.ReadAsync(cancellationToken);
 147108                var readableBuffer = result.Buffer;
 109                try
 110                {
 147111                    if (!readableBuffer.IsEmpty)
 112                    {
 113                        // buffer.Count is int
 147114                        var count = _inputLengthDecider.LengthToConsume(readableBuffer, destination.Length);
 147115                        readableBuffer = readableBuffer.Slice(0, count);
 147116                        readableBuffer.CopyTo(destination.Span);
 147117                        return count;
 118                    }
 119
 0120                    if (result.IsCompleted)
 121                    {
 0122                        return 0;
 123                    }
 0124                }
 125                finally
 126                {
 147127                    _input.AdvanceTo(readableBuffer.End, readableBuffer.End);
 128                }
 129            }
 147130        }
 131
 132        public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, object stat
 133        {
 0134            return ReadAsync(buffer, offset, count).ToApm(callback, state);
 135        }
 136
 137        public override int EndRead(IAsyncResult asyncResult)
 138        {
 0139            return asyncResult.ToApmEnd<int>();
 140        }
 141
 142        public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object sta
 143        {
 0144            return WriteAsync(buffer, offset, count).ToApm(callback, state);
 145        }
 146
 147        public override void EndWrite(IAsyncResult asyncResult)
 148        {
 0149            asyncResult.ToApmEnd();
 0150        }
 151
 152        protected override void Dispose(bool disposing)
 153        {
 0154            _input.Complete();
 0155            _output.Complete();
 0156            base.Dispose(disposing);
 0157        }
 158
 159        public async ValueTask DisposeAsync()
 160        {
 0161            await _input.CompleteAsync();
 0162            await _output.CompleteAsync();
 0163            base.Dispose(true);
 0164        }
 165
 166        internal void SetContentType(string contentType)
 167        {
 8168            if (contentType == FramingUpgradeString.SslOrTls)
 7169                _inputLengthDecider = new TlsInputLengthDecider();
 1170            else _inputLengthDecider = NoopInputLengthDecider.Instance;
 1171        }
 172
 173        internal interface IInputLengthDecider
 174        {
 175            int LengthToConsume(ReadOnlySequence<byte> buffer, int destinationLength);
 176        }
 177
 178        internal class TlsInputLengthDecider : IInputLengthDecider
 179        {
 180            // buffer[0] is TLS Frame content type, eg 23 = ApplicationData
 181            // buffer[1] and buffer[2] are the TLS version, eg 0x0303 == TLS 1.2
 182            // buffer[3] and buffer[4] and big endian integer for length
 183
 184            private int _frameSize;
 185
 186            public int LengthToConsume(ReadOnlySequence<byte> buffer, int destinationLength)
 187            {
 188                // Maximum number of bytes that can be read from incoming buffer. This is either the entire buffer if th
 189                // space in the destination, or however much space is available in the destination buffer
 116190                int maxRead = (int)Math.Min(buffer.Length, destinationLength);
 191                int bytesToRead;
 192
 193                // If there's still bytes left to be read from the current frame, we don't need to read the frame header
 194                // read the frame size if we've finished reading the previous frame.
 116195                if (_frameSize == 0)
 196                {
 58197                    if (buffer.Length < 5)
 198                    {
 199                        // Need at least 5 bytes to read size from frame header so presuming this isn't a TLS frame
 200                        // so indicating to consume everything that's possible.
 0201                        return maxRead;
 202                    }
 203
 58204                    if (buffer.IsSingleSegment || buffer.First.Length >= 5)
 205                    {
 206                        // We have enough bytes to read the frame size from the first segment
 58207                        var span = buffer.First.Span;
 208                        Fx.Assert(span[1] == 3 && span[2] <= 3, "Invalid TLS header");
 209                        // The length in the Negotiate header doesn't include the header itself so that needs to be adde
 58210                        _frameSize = ((span[3] << 8) | span[4]) + 5;
 211                    }
 212                    else
 213                    {
 214                        // We have at least 5 bytes, but the first 5 bytes aren't in the first segment so get the 5 byte
 0215                        var frameHeaderBytes = buffer.Slice(0, 5).ToArray();
 216                        // The length in the TLS header doesn't include the header itself so that needs to be added
 0217                        _frameSize = ((frameHeaderBytes[3] << 8) | frameHeaderBytes[4]) + 5;
 218                    }
 219                }
 220
 221                // If the frame size is smaller than the number of bytes read from the input, then only read frame size 
 116222                bytesToRead = Math.Min(maxRead, _frameSize);
 223                // Decrement _frameSize by bytesToRead to store number of bytes still pending to be read in future reads
 116224                _frameSize -= bytesToRead;
 116225                return bytesToRead;
 226            }
 227        }
 228
 229        internal class NoopInputLengthDecider : IInputLengthDecider
 230        {
 2231            internal static IInputLengthDecider Instance = new NoopInputLengthDecider();
 232            public int LengthToConsume(ReadOnlySequence<byte> buffer, int destinationLength)
 233            {
 234                // Can't read more than there's space in the destination buffer
 31235                return (int)Math.Min(buffer.Length, destinationLength);
 236            }
 237        }
 238    }
 239}