< Summary

Information
Class: IceRpc.Transports.Quic.Internal.QuicMultiplexedConnection
Assembly: IceRpc.Transports.Quic
File(s): /home/runner/work/icerpc-csharp/icerpc-csharp/src/IceRpc.Transports.Quic/Internal/QuicMultiplexedConnection.cs
Tag: 275_13775359185
Line coverage
78%
Covered lines: 63
Uncovered lines: 17
Coverable lines: 80
Total lines: 182
Line coverage: 78.7%
Branch coverage
92%
Covered branches: 13
Total branches: 14
Branch coverage: 92.8%
Method coverage
100%
Covered methods: 6
Total methods: 6
Method coverage: 100%

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
AcceptStreamAsync()50%2.18264.28%
CloseAsync()100%2.26260%
CreateStreamAsync()100%4.02488.46%
DisposeAsync()100%2.06275%
ThrowIfClosedOrDisposed()100%44100%

File(s)

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

#LineLine coverage
 1// Copyright (c) ZeroC, Inc.
 2
 3using System.Buffers;
 4using System.Net.Quic;
 5using System.Net.Sockets;
 6
 7namespace IceRpc.Transports.Quic.Internal;
 8
 9/// <summary>The Quic multiplexed connection implements an <see cref="IMultiplexedConnection" />.</summary>
 10internal abstract class QuicMultiplexedConnection : IMultiplexedConnection
 11{
 12    private protected QuicConnection? _connection;
 13    private volatile bool _isClosed;
 14    private volatile bool _isDisposed;
 15    private readonly int _minSegmentSize;
 16    private readonly MemoryPool<byte> _pool;
 17
 45618    private protected QuicMultiplexedConnection(MultiplexedConnectionOptions options)
 45619    {
 45620        _minSegmentSize = options.MinSegmentSize;
 45621        _pool = options.Pool;
 45622    }
 23
 24    public async ValueTask<IMultiplexedStream> AcceptStreamAsync(CancellationToken cancellationToken)
 59625    {
 59626        if (_connection is null)
 027        {
 028            throw new InvalidOperationException("The Quic connection is not connected.");
 29        }
 30
 31        try
 59632        {
 59633            QuicStream stream = await _connection.AcceptInboundStreamAsync(cancellationToken).ConfigureAwait(false);
 55934            return new QuicMultiplexedStream(stream, isRemote: true, _pool, _minSegmentSize, ThrowIfClosedOrDisposed);
 35        }
 3236        catch (QuicException exception)
 3237        {
 3238            throw exception.ToIceRpcException();
 39        }
 040        catch (SocketException exception)
 041        {
 042            throw exception.ToIceRpcException();
 43        }
 55944    }
 45
 46    public abstract Task<TransportConnectionInformation> ConnectAsync(CancellationToken cancellationToken);
 47
 48    public async Task CloseAsync(MultiplexedConnectionCloseError closeError, CancellationToken cancellationToken)
 6049    {
 50        try
 6051        {
 6052            _isClosed = true;
 6053            if (_connection is not null)
 6054            {
 6055                await _connection.CloseAsync((long)closeError, cancellationToken).ConfigureAwait(false);
 6056            }
 6057        }
 058        catch (QuicException exception)
 059        {
 060            throw exception.ToIceRpcException();
 61        }
 062        catch (SocketException exception)
 063        {
 064            throw exception.ToIceRpcException();
 65        }
 6066    }
 67
 68    public async ValueTask<IMultiplexedStream> CreateStreamAsync(
 69        bool bidirectional,
 70        CancellationToken cancellationToken)
 61871    {
 61872        if (_connection is null)
 273        {
 274            throw new InvalidOperationException("The Quic connection is not connected.");
 75        }
 76
 77        QuicStream stream;
 78        try
 61679        {
 61680            stream = await _connection.OpenOutboundStreamAsync(
 61681                bidirectional ? QuicStreamType.Bidirectional : QuicStreamType.Unidirectional,
 61682                cancellationToken).ConfigureAwait(false);
 58383        }
 2284        catch (QuicException exception)
 2285        {
 2286            throw exception.ToIceRpcException();
 87        }
 088        catch (SocketException exception)
 089        {
 090            throw exception.ToIceRpcException();
 91        }
 792        catch (ObjectDisposedException)
 793        {
 794            cancellationToken.ThrowIfCancellationRequested();
 795            throw;
 96        }
 97
 58398        return new QuicMultiplexedStream(
 58399            stream,
 583100            isRemote: false,
 583101            _pool,
 583102            _minSegmentSize,
 583103            ThrowIfClosedOrDisposed);
 583104    }
 105
 106    public async ValueTask DisposeAsync()
 469107    {
 469108        _isDisposed = true;
 109
 469110        if (_connection is not null)
 463111        {
 112            try
 463113            {
 463114                await _connection.DisposeAsync().ConfigureAwait(false);
 463115            }
 0116            catch
 0117            {
 118                // Workaround for https://github.com/dotnet/runtime/issues/78641.
 0119            }
 463120        }
 469121    }
 122
 123    private void ThrowIfClosedOrDisposed()
 38100124    {
 38100125        if (_isClosed)
 4126        {
 4127            throw new IceRpcException(IceRpcError.ConnectionAborted, "The connection was closed.");
 128        }
 38096129        else if (_isDisposed)
 4130        {
 4131            throw new IceRpcException(IceRpcError.ConnectionAborted, "The connection was disposed.");
 132        }
 38092133    }
 134}
 135
 136internal class QuicMultiplexedClientConnection : QuicMultiplexedConnection
 137{
 138    private readonly QuicClientConnectionOptions _quicClientConnectionOptions;
 139
 140    public override async Task<TransportConnectionInformation> ConnectAsync(CancellationToken cancellationToken)
 141    {
 142        // Establish the Quic connection.
 143        try
 144        {
 145            _connection = await QuicConnection.ConnectAsync(
 146                _quicClientConnectionOptions,
 147                cancellationToken).ConfigureAwait(false);
 148        }
 149        catch (QuicException exception)
 150        {
 151            throw exception.ToIceRpcException();
 152        }
 153        catch (SocketException exception)
 154        {
 155            throw exception.ToIceRpcException();
 156        }
 157
 158        return new TransportConnectionInformation(
 159            localNetworkAddress: _connection.LocalEndPoint,
 160            remoteNetworkAddress: _connection.RemoteEndPoint,
 161            _connection.RemoteCertificate);
 162    }
 163
 164    internal QuicMultiplexedClientConnection(
 165        MultiplexedConnectionOptions options,
 166        QuicClientConnectionOptions quicOptions)
 167        : base(options) => _quicClientConnectionOptions = quicOptions;
 168}
 169
 170internal class QuicMultiplexedServerConnection : QuicMultiplexedConnection
 171{
 172    public override Task<TransportConnectionInformation> ConnectAsync(CancellationToken cancellationToken) =>
 173        Task.FromResult(new TransportConnectionInformation(
 174            localNetworkAddress: _connection!.LocalEndPoint,
 175            remoteNetworkAddress: _connection.RemoteEndPoint,
 176            _connection.RemoteCertificate));
 177
 178    internal QuicMultiplexedServerConnection(
 179        QuicConnection connection,
 180        MultiplexedConnectionOptions options)
 181        : base(options) => _connection = connection;
 182}