< Summary

Information
Class: IceRpc.Transports.Quic.Internal.QuicPipeReader
Assembly: IceRpc.Transports.Quic
File(s): /home/runner/work/icerpc-csharp/icerpc-csharp/src/IceRpc.Transports.Quic/Internal/QuicPipeReader.cs
Tag: 275_13775359185
Line coverage
56%
Covered lines: 42
Uncovered lines: 33
Coverable lines: 75
Total lines: 151
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;
 7
 8namespace IceRpc.Transports.Quic.Internal;
 9
 10/// <summary>Implements a PipeReader over a QuicStream.</summary>
 11internal class QuicPipeReader : PipeReader
 12{
 13    private bool _isCompleted;
 14    private readonly Action _completeCallback;
 15    private readonly Action _throwIfConnectionClosedOrDisposed;
 16    private readonly PipeReader _pipeReader;
 17    private readonly QuicStream _stream;
 18
 19    // StreamPipeReader.AdvanceTo does not call the underlying stream and as a result does not throw any QuicException.
 344820    public override void AdvanceTo(SequencePosition consumed) => AdvanceTo(consumed, consumed);
 21
 22    public override void AdvanceTo(SequencePosition consumed, SequencePosition examined) =>
 2685823        _pipeReader.AdvanceTo(consumed, examined);
 24
 425    public override void CancelPendingRead() => _pipeReader.CancelPendingRead();
 26
 27    public override void Complete(Exception? exception = null)
 104028    {
 104029        if (!_isCompleted)
 100230        {
 100231            _isCompleted = true;
 32
 33            // We don't use the application error code, it's irrelevant.
 100234            _stream.Abort(QuicAbortDirection.Read, errorCode: 0);
 35
 36            // This does not call _stream.Dispose since leaveOpen is set to true.
 100237            _pipeReader.Complete();
 38
 100239            _completeCallback();
 100240        }
 104041    }
 42
 43    public override async Task CopyToAsync(Stream destination, CancellationToken cancellationToken)
 044    {
 045        _throwIfConnectionClosedOrDisposed();
 46        try
 047        {
 048            await _pipeReader.CopyToAsync(destination, cancellationToken).ConfigureAwait(false);
 049        }
 050        catch (QuicException exception)
 051        {
 052            throw exception.ToIceRpcException();
 53        }
 054        catch (SocketException exception)
 055        {
 056            throw exception.ToIceRpcException();
 57        }
 58        // We don't catch and wrap other exceptions. It could be for example an InvalidOperationException when
 59        // attempting to read while another read is in progress.
 060    }
 61
 62    public override async Task CopyToAsync(PipeWriter writer, CancellationToken cancellationToken)
 063    {
 064        _throwIfConnectionClosedOrDisposed();
 65        try
 066        {
 067            await _pipeReader.CopyToAsync(writer, cancellationToken).ConfigureAwait(false);
 068        }
 069        catch (QuicException exception)
 070        {
 071            throw exception.ToIceRpcException();
 72        }
 073        catch (SocketException exception)
 074        {
 075            throw exception.ToIceRpcException();
 76        }
 77        // We don't catch and wrap other exceptions. It could be for example an InvalidOperationException when
 78        // attempting to read while another read is in progress.
 079    }
 80
 81    public override async ValueTask<ReadResult> ReadAsync(CancellationToken cancellationToken = default)
 2689382    {
 83        // First check if there's buffered data. If the connection is closed, we still want to return this data.
 2689384        if (TryRead(out ReadResult readResult))
 285        {
 286            return readResult;
 87        }
 88
 2689189        _throwIfConnectionClosedOrDisposed();
 90
 91        try
 2688792        {
 2688793            return await _pipeReader.ReadAsync(cancellationToken).ConfigureAwait(false);
 94        }
 1895        catch (QuicException exception)
 1896        {
 1897            throw exception.ToIceRpcException();
 98        }
 099        catch (SocketException exception)
 0100        {
 0101            throw exception.ToIceRpcException();
 102        }
 103        // We don't catch and wrap other exceptions. It could be for example an InvalidOperationException when
 104        // attempting to read while another read is in progress.
 26867105    }
 106
 107    // StreamPipeReader.TryRead does not call the underlying QuicStream and as a result does not throw any
 108    // QuicException.
 26893109    public override bool TryRead(out ReadResult result) => _pipeReader.TryRead(out result);
 110
 111    protected override async ValueTask<ReadResult> ReadAtLeastAsyncCore(
 112        int minimumSize,
 113        CancellationToken cancellationToken)
 2114    {
 115        try
 2116        {
 2117            return await _pipeReader.ReadAtLeastAsync(minimumSize, cancellationToken).ConfigureAwait(false);
 118        }
 0119        catch (QuicException exception)
 0120        {
 0121            throw exception.ToIceRpcException();
 122        }
 0123        catch (SocketException exception)
 0124        {
 0125            throw exception.ToIceRpcException();
 126        }
 127        // We don't catch and wrap other exceptions. It could be for example an InvalidOperationException when
 128        // attempting to read while another read is in progress.
 2129    }
 130
 1038131    internal QuicPipeReader(
 1038132        QuicStream stream,
 1038133        MemoryPool<byte> pool,
 1038134        int minimumSegmentSize,
 1038135        Action completeCallback,
 1038136        Action throwIfConnectionClosed)
 1038137    {
 1038138        _stream = stream;
 1038139        _completeCallback = completeCallback;
 140
 141        // This callback is used to check if the connection is closed or disposed before calling ReadAsync or TryRead on
 142        // the pipe reader. This check works around the use of the QuicError.OperationAborted error code for both
 143        // reporting the abortion of the in-progress read call and for reporting a closed connection before the
 144        // operation process starts. In this latter case, we want to report ConnectionAborted.
 1038145        _throwIfConnectionClosedOrDisposed = throwIfConnectionClosed;
 146
 1038147        _pipeReader = Create(
 1038148            _stream,
 1038149            new StreamPipeReaderOptions(pool, minimumSegmentSize, minimumReadSize: -1, leaveOpen: true));
 1038150    }
 151}