< 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: 592_20856082467
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
Total methods: 10
Method coverage: 80%

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%2.04278.57%
TryRead(...)100%11100%
ReadAtLeastAsyncCore()100%1.22140%
.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.
 172124    public override void AdvanceTo(SequencePosition consumed) => AdvanceTo(consumed, consumed);
 25
 26    public override void AdvanceTo(SequencePosition consumed, SequencePosition examined) =>
 1352127        _pipeReader.AdvanceTo(consumed, examined);
 28
 229    public override void CancelPendingRead() => _pipeReader.CancelPendingRead();
 30
 31    public override void Complete(Exception? exception = null)
 52132    {
 52133        if (!_isCompleted)
 50234        {
 50235            _isCompleted = true;
 36
 37            // We don't use the application error code, it's irrelevant.
 50238            _stream.Abort(QuicAbortDirection.Read, errorCode: 0);
 39
 40            // This does not call _stream.Dispose since leaveOpen is set to true.
 50241            _pipeReader.Complete();
 42
 50243            _completeCallback();
 50244        }
 52145    }
 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)
 1353986    {
 87        // First check if there's buffered data. If the connection is closed, we still want to return this data.
 1353988        if (TryRead(out ReadResult readResult))
 189        {
 190            return readResult;
 91        }
 92
 1353893        _throwIfConnectionClosedOrDisposed();
 94
 95        try
 1353696        {
 1353697            return await _pipeReader.ReadAsync(cancellationToken).ConfigureAwait(false);
 98        }
 999        catch (QuicException exception)
 9100        {
 9101            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.
 13539113    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
 520135    internal QuicPipeReader(
 520136        QuicStream stream,
 520137        MemoryPool<byte> pool,
 520138        int minimumSegmentSize,
 520139        Action completeCallback,
 520140        Action throwIfConnectionClosed)
 520141    {
 520142        _stream = stream;
 520143        _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.
 520149        _throwIfConnectionClosedOrDisposed = throwIfConnectionClosed;
 150
 520151        _pipeReader = Create(
 520152            _stream,
 520153            new StreamPipeReaderOptions(pool, minimumSegmentSize, minimumReadSize: -1, leaveOpen: true));
 520154    }
 155}