< Summary

Information
Class: IceRpc.Transports.Quic.Internal.QuicMultiplexedClientConnection
Assembly: IceRpc
File(s): /home/runner/work/icerpc-csharp/icerpc-csharp/src/IceRpc/Transports/Quic/Internal/QuicMultiplexedConnection.cs
Tag: 592_20856082467
Line coverage
83%
Covered lines: 15
Uncovered lines: 3
Coverable lines: 18
Total lines: 192
Line coverage: 83.3%
Branch coverage
N/A
Covered branches: 0
Total branches: 0
Branch coverage: N/A
Method coverage
100%
Covered methods: 2
Total methods: 2
Method coverage: 100%

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
ConnectAsync()100%1.01182.35%
.ctor(...)100%11100%

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;
 6using System.Runtime.Versioning;
 7
 8namespace IceRpc.Transports.Quic.Internal;
 9
 10/// <summary>The QUIC multiplexed connection implements an <see cref="IMultiplexedConnection" />.</summary>
 11[SupportedOSPlatform("linux")]
 12[SupportedOSPlatform("macos")]
 13[SupportedOSPlatform("windows")]
 14internal abstract class QuicMultiplexedConnection : IMultiplexedConnection
 15{
 16    private protected QuicConnection? _connection;
 17    private volatile bool _isClosed;
 18    private volatile bool _isDisposed;
 19    private readonly int _minSegmentSize;
 20    private readonly MemoryPool<byte> _pool;
 21
 22    private protected QuicMultiplexedConnection(MultiplexedConnectionOptions options)
 23    {
 24        _minSegmentSize = options.MinSegmentSize;
 25        _pool = options.Pool;
 26    }
 27
 28    public async ValueTask<IMultiplexedStream> AcceptStreamAsync(CancellationToken cancellationToken)
 29    {
 30        if (_connection is null)
 31        {
 32            throw new InvalidOperationException("The QUIC connection is not connected.");
 33        }
 34
 35        try
 36        {
 37            QuicStream stream = await _connection.AcceptInboundStreamAsync(cancellationToken).ConfigureAwait(false);
 38            return new QuicMultiplexedStream(stream, isRemote: true, _pool, _minSegmentSize, ThrowIfClosedOrDisposed);
 39        }
 40        catch (QuicException exception)
 41        {
 42            throw exception.ToIceRpcException();
 43        }
 44        catch (SocketException exception)
 45        {
 46            throw exception.ToIceRpcException();
 47        }
 48    }
 49
 50    public abstract Task<TransportConnectionInformation> ConnectAsync(CancellationToken cancellationToken);
 51
 52    public async Task CloseAsync(MultiplexedConnectionCloseError closeError, CancellationToken cancellationToken)
 53    {
 54        try
 55        {
 56            _isClosed = true;
 57            if (_connection is not null)
 58            {
 59                await _connection.CloseAsync((long)closeError, cancellationToken).ConfigureAwait(false);
 60            }
 61        }
 62        catch (QuicException exception)
 63        {
 64            throw exception.ToIceRpcException();
 65        }
 66        catch (SocketException exception)
 67        {
 68            throw exception.ToIceRpcException();
 69        }
 70    }
 71
 72    public async ValueTask<IMultiplexedStream> CreateStreamAsync(
 73        bool bidirectional,
 74        CancellationToken cancellationToken)
 75    {
 76        if (_connection is null)
 77        {
 78            throw new InvalidOperationException("The QUIC connection is not connected.");
 79        }
 80
 81        QuicStream stream;
 82        try
 83        {
 84            stream = await _connection.OpenOutboundStreamAsync(
 85                bidirectional ? QuicStreamType.Bidirectional : QuicStreamType.Unidirectional,
 86                cancellationToken).ConfigureAwait(false);
 87        }
 88        catch (QuicException exception)
 89        {
 90            throw exception.ToIceRpcException();
 91        }
 92        catch (SocketException exception)
 93        {
 94            throw exception.ToIceRpcException();
 95        }
 96        catch (ObjectDisposedException)
 97        {
 98            cancellationToken.ThrowIfCancellationRequested();
 99            throw;
 100        }
 101
 102        return new QuicMultiplexedStream(
 103            stream,
 104            isRemote: false,
 105            _pool,
 106            _minSegmentSize,
 107            ThrowIfClosedOrDisposed);
 108    }
 109
 110    public async ValueTask DisposeAsync()
 111    {
 112        _isDisposed = true;
 113
 114        if (_connection is not null)
 115        {
 116            try
 117            {
 118                await _connection.DisposeAsync().ConfigureAwait(false);
 119            }
 120            catch
 121            {
 122                // Workaround for https://github.com/dotnet/runtime/issues/78641.
 123            }
 124        }
 125    }
 126
 127    private void ThrowIfClosedOrDisposed()
 128    {
 129        if (_isClosed)
 130        {
 131            throw new IceRpcException(IceRpcError.ConnectionAborted, "The connection was closed.");
 132        }
 133        else if (_isDisposed)
 134        {
 135            throw new IceRpcException(IceRpcError.ConnectionAborted, "The connection was disposed.");
 136        }
 137    }
 138}
 139
 140[SupportedOSPlatform("linux")]
 141[SupportedOSPlatform("macos")]
 142[SupportedOSPlatform("windows")]
 143internal class QuicMultiplexedClientConnection : QuicMultiplexedConnection
 144{
 145    private readonly QuicClientConnectionOptions _quicClientConnectionOptions;
 146
 147    public override async Task<TransportConnectionInformation> ConnectAsync(CancellationToken cancellationToken)
 116148    {
 149        // Establish the QUIC connection.
 150        try
 116151        {
 116152            _connection = await QuicConnection.ConnectAsync(
 116153                _quicClientConnectionOptions,
 116154                cancellationToken).ConfigureAwait(false);
 113155        }
 0156        catch (QuicException exception)
 0157        {
 0158            throw exception.ToIceRpcException();
 159        }
 1160        catch (SocketException exception)
 1161        {
 1162            throw exception.ToIceRpcException();
 163        }
 164
 113165        return new TransportConnectionInformation(
 113166            localNetworkAddress: _connection.LocalEndPoint,
 113167            remoteNetworkAddress: _connection.RemoteEndPoint,
 113168            _connection.RemoteCertificate);
 113169    }
 170
 171    internal QuicMultiplexedClientConnection(
 172        MultiplexedConnectionOptions options,
 173        QuicClientConnectionOptions quicOptions)
 234174        : base(options) => _quicClientConnectionOptions = quicOptions;
 175}
 176
 177[SupportedOSPlatform("linux")]
 178[SupportedOSPlatform("macos")]
 179[SupportedOSPlatform("windows")]
 180internal class QuicMultiplexedServerConnection : QuicMultiplexedConnection
 181{
 182    public override Task<TransportConnectionInformation> ConnectAsync(CancellationToken cancellationToken) =>
 183        Task.FromResult(new TransportConnectionInformation(
 184            localNetworkAddress: _connection!.LocalEndPoint,
 185            remoteNetworkAddress: _connection.RemoteEndPoint,
 186            _connection.RemoteCertificate));
 187
 188    internal QuicMultiplexedServerConnection(
 189        QuicConnection connection,
 190        MultiplexedConnectionOptions options)
 191        : base(options) => _connection = connection;
 192}