< 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: 1321_24790053727
Line coverage
56%
Covered lines: 42
Uncovered lines: 33
Coverable lines: 75
Total lines: 155
Line coverage: 56%
Branch coverage
100%
Covered branches: 4
Total branches: 4
Branch coverage: 100%
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()100%1140%
.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.
 176824    public override void AdvanceTo(SequencePosition consumed) => AdvanceTo(consumed, consumed);
 25
 26    public override void AdvanceTo(SequencePosition consumed, SequencePosition examined) =>
 1362227        _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)
 1364886    {
 87        // First check if there's buffered data. If the connection is closed, we still want to return this data.
 1364888        if (TryRead(out ReadResult readResult))
 1789        {
 1790            return readResult;
 91        }
 92
 1363193        _throwIfConnectionClosedOrDisposed();
 94
 95        try
 1362996        {
 1362997            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.
 13634109    }
 110
 111    // StreamPipeReader.TryRead does not call the underlying QuicStream and as a result does not throw any
 112    // QuicException.
 13648113    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        try
 1120        {
 1121            return await _pipeReader.ReadAtLeastAsync(minimumSize, cancellationToken).ConfigureAwait(false);
 122        }
 0123        catch (QuicException exception)
 0124        {
 0125            throw exception.ToIceRpcException();
 126        }
 0127        catch (SocketException exception)
 0128        {
 0129            throw exception.ToIceRpcException();
 130        }
 131        // We don't catch and wrap other exceptions. It could be for example an InvalidOperationException when
 132        // attempting to read while another read is in progress.
 1133    }
 134
 528135    internal QuicPipeReader(
 528136        QuicStream stream,
 528137        MemoryPool<byte> pool,
 528138        int minimumSegmentSize,
 528139        Action completeCallback,
 528140        Action throwIfConnectionClosed)
 528141    {
 528142        _stream = stream;
 528143        _completeCallback = completeCallback;
 144
 145        // This callback is used to check if the connection is closed or disposed before calling ReadAsync or TryRead on
 146        // the pipe reader. This check works around the use of the QuicError.OperationAborted error code for both
 147        // reporting the abortion of the in-progress read call and for reporting a closed connection before the
 148        // operation process starts. In this latter case, we want to report ConnectionAborted.
 528149        _throwIfConnectionClosedOrDisposed = throwIfConnectionClosed;
 150
 528151        _pipeReader = Create(
 528152            _stream,
 528153            new StreamPipeReaderOptions(pool, minimumSegmentSize, minimumReadSize: -1, leaveOpen: true));
 528154    }
 155}