< Summary

Information
Class: IceRpc.Transports.Quic.Internal.QuicPipeReader
Assembly: IceRpc
File(s): /home/runner/work/icerpc-csharp/icerpc-csharp/src/IceRpc/Transports/Quic/Internal/QuicPipeReader.cs
Tag: 1986_28452893481
Line coverage
51%
Covered lines: 44
Uncovered lines: 41
Coverable lines: 85
Total lines: 172
Line coverage: 51.7%
Branch coverage
41%
Covered branches: 5
Total branches: 12
Branch coverage: 41.6%
Method coverage
80%
Covered methods: 8
Fully covered methods: 6
Total methods: 10
Method coverage: 80%
Full method coverage: 60%

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
AdvanceTo(...)100%11100%
AdvanceTo(...)100%11100%
CancelPendingRead()100%11100%
Complete(...)100%22100%
CopyToAsync()100%210%
CopyToAsync()100%210%
ReadAsync()100%2278.57%
TryRead(...)100%11100%
ReadAtLeastAsyncCore()12.5%30830%
.ctor(...)100%11100%

File(s)

/home/runner/work/icerpc-csharp/icerpc-csharp/src/IceRpc/Transports/Quic/Internal/QuicPipeReader.cs

#LineLine coverage
 1// Copyright (c) ZeroC, Inc.
 2
 3using System.Buffers;
 4using System.IO.Pipelines;
 5using System.Net.Quic;
 6using System.Net.Sockets;
 7using System.Runtime.Versioning;
 8
 9namespace IceRpc.Transports.Quic.Internal;
 10
 11/// <summary>Implements a PipeReader over a QuicStream.</summary>
 12[SupportedOSPlatform("linux")]
 13[SupportedOSPlatform("macos")]
 14[SupportedOSPlatform("windows")]
 15internal class QuicPipeReader : PipeReader
 16{
 17    private bool _isCompleted;
 18    private readonly Action _completeCallback;
 19    private readonly Action _throwIfConnectionClosedOrDisposed;
 20    private readonly PipeReader _pipeReader;
 21    private readonly QuicStream _stream;
 22
 23    // StreamPipeReader.AdvanceTo does not call the underlying stream and as a result does not throw any QuicException.
 175024    public override void AdvanceTo(SequencePosition consumed) => AdvanceTo(consumed, consumed);
 25
 26    public override void AdvanceTo(SequencePosition consumed, SequencePosition examined) =>
 1351427        _pipeReader.AdvanceTo(consumed, examined);
 28
 229    public override void CancelPendingRead() => _pipeReader.CancelPendingRead();
 30
 31    public override void Complete(Exception? exception = null)
 52932    {
 52933        if (!_isCompleted)
 51034        {
 51035            _isCompleted = true;
 36
 37            // We don't use the application error code, it's irrelevant.
 51038            _stream.Abort(QuicAbortDirection.Read, errorCode: 0);
 39
 40            // This does not call _stream.Dispose since leaveOpen is set to true.
 51041            _pipeReader.Complete();
 42
 51043            _completeCallback();
 51044        }
 52945    }
 46
 47    public override async Task CopyToAsync(Stream destination, CancellationToken cancellationToken)
 048    {
 049        _throwIfConnectionClosedOrDisposed();
 50        try
 051        {
 052            await _pipeReader.CopyToAsync(destination, cancellationToken).ConfigureAwait(false);
 053        }
 054        catch (QuicException exception)
 055        {
 056            throw exception.ToIceRpcException();
 57        }
 058        catch (SocketException exception)
 059        {
 060            throw exception.ToIceRpcException();
 61        }
 62        // We don't catch and wrap other exceptions. It could be for example an InvalidOperationException when
 63        // attempting to read while another read is in progress.
 064    }
 65
 66    public override async Task CopyToAsync(PipeWriter writer, CancellationToken cancellationToken)
 067    {
 068        _throwIfConnectionClosedOrDisposed();
 69        try
 070        {
 071            await _pipeReader.CopyToAsync(writer, cancellationToken).ConfigureAwait(false);
 072        }
 073        catch (QuicException exception)
 074        {
 075            throw exception.ToIceRpcException();
 76        }
 077        catch (SocketException exception)
 078        {
 079            throw exception.ToIceRpcException();
 80        }
 81        // We don't catch and wrap other exceptions. It could be for example an InvalidOperationException when
 82        // attempting to read while another read is in progress.
 083    }
 84
 85    public override async ValueTask<ReadResult> ReadAsync(CancellationToken cancellationToken = default)
 1354086    {
 87        // First check if there's buffered data. If the connection is closed, we still want to return this data.
 1354088        if (TryRead(out ReadResult readResult))
 1789        {
 1790            return readResult;
 91        }
 92
 1352393        _throwIfConnectionClosedOrDisposed();
 94
 95        try
 1352196        {
 1352197            return await _pipeReader.ReadAsync(cancellationToken).ConfigureAwait(false);
 98        }
 1099        catch (QuicException exception)
 10100        {
 10101            throw exception.ToIceRpcException();
 102        }
 0103        catch (SocketException exception)
 0104        {
 0105            throw exception.ToIceRpcException();
 106        }
 107        // We don't catch and wrap other exceptions. It could be for example an InvalidOperationException when
 108        // attempting to read while another read is in progress.
 13526109    }
 110
 111    // StreamPipeReader.TryRead does not call the underlying QuicStream and as a result does not throw any
 112    // QuicException.
 13541113    public override bool TryRead(out ReadResult result) => _pipeReader.TryRead(out result);
 114
 115    protected override async ValueTask<ReadResult> ReadAtLeastAsyncCore(
 116        int minimumSize,
 117        CancellationToken cancellationToken)
 1118    {
 119        // First check if there's sufficient buffered data. If the connection is closed, we still want to return this
 120        // data.
 1121        if (TryRead(out ReadResult readResult))
 0122        {
 0123            if (readResult.Buffer.Length >= minimumSize || readResult.IsCompleted || readResult.IsCanceled)
 0124            {
 0125                return readResult;
 126            }
 127            else
 0128            {
 129                // We need more data, keep going.
 0130                _pipeReader.AdvanceTo(readResult.Buffer.Start);
 0131            }
 0132        }
 133
 1134        _throwIfConnectionClosedOrDisposed();
 135
 136        try
 1137        {
 1138            return await _pipeReader.ReadAtLeastAsync(minimumSize, cancellationToken).ConfigureAwait(false);
 139        }
 0140        catch (QuicException exception)
 0141        {
 0142            throw exception.ToIceRpcException();
 143        }
 0144        catch (SocketException exception)
 0145        {
 0146            throw exception.ToIceRpcException();
 147        }
 148        // We don't catch and wrap other exceptions. It could be for example an InvalidOperationException when
 149        // attempting to read while another read is in progress.
 1150    }
 151
 528152    internal QuicPipeReader(
 528153        QuicStream stream,
 528154        MemoryPool<byte> pool,
 528155        int minimumSegmentSize,
 528156        Action completeCallback,
 528157        Action throwIfConnectionClosed)
 528158    {
 528159        _stream = stream;
 528160        _completeCallback = completeCallback;
 161
 162        // This callback is used to check if the connection is closed or disposed before calling ReadAsync or TryRead on
 163        // the pipe reader. This check works around the use of the QuicError.OperationAborted error code for both
 164        // reporting the abortion of the in-progress read call and for reporting a closed connection before the
 165        // operation process starts. In this latter case, we want to report ConnectionAborted.
 528166        _throwIfConnectionClosedOrDisposed = throwIfConnectionClosed;
 167
 528168        _pipeReader = Create(
 528169            _stream,
 528170            new StreamPipeReaderOptions(pool, minimumSegmentSize, minimumReadSize: -1, leaveOpen: true));
 528171    }
 172}