< Summary

Information
Class: IceRpc.Transports.Quic.Internal.QuicMultiplexedConnection
Assembly: IceRpc
File(s): /home/runner/work/icerpc-csharp/icerpc-csharp/src/IceRpc/Transports/Quic/Internal/QuicMultiplexedConnection.cs
Tag: 592_20856082467
Line coverage
78%
Covered lines: 63
Uncovered lines: 17
Coverable lines: 80
Total lines: 192
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;
 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
 22922    private protected QuicMultiplexedConnection(MultiplexedConnectionOptions options)
 22923    {
 22924        _minSegmentSize = options.MinSegmentSize;
 22925        _pool = options.Pool;
 22926    }
 27
 28    public async ValueTask<IMultiplexedStream> AcceptStreamAsync(CancellationToken cancellationToken)
 29929    {
 29930        if (_connection is null)
 031        {
 032            throw new InvalidOperationException("The QUIC connection is not connected.");
 33        }
 34
 35        try
 29936        {
 29937            QuicStream stream = await _connection.AcceptInboundStreamAsync(cancellationToken).ConfigureAwait(false);
 28038            return new QuicMultiplexedStream(stream, isRemote: true, _pool, _minSegmentSize, ThrowIfClosedOrDisposed);
 39        }
 1640        catch (QuicException exception)
 1641        {
 1642            throw exception.ToIceRpcException();
 43        }
 044        catch (SocketException exception)
 045        {
 046            throw exception.ToIceRpcException();
 47        }
 28048    }
 49
 50    public abstract Task<TransportConnectionInformation> ConnectAsync(CancellationToken cancellationToken);
 51
 52    public async Task CloseAsync(MultiplexedConnectionCloseError closeError, CancellationToken cancellationToken)
 3053    {
 54        try
 3055        {
 3056            _isClosed = true;
 3057            if (_connection is not null)
 3058            {
 3059                await _connection.CloseAsync((long)closeError, cancellationToken).ConfigureAwait(false);
 3060            }
 3061        }
 062        catch (QuicException exception)
 063        {
 064            throw exception.ToIceRpcException();
 65        }
 066        catch (SocketException exception)
 067        {
 068            throw exception.ToIceRpcException();
 69        }
 3070    }
 71
 72    public async ValueTask<IMultiplexedStream> CreateStreamAsync(
 73        bool bidirectional,
 74        CancellationToken cancellationToken)
 31075    {
 31076        if (_connection is null)
 177        {
 178            throw new InvalidOperationException("The QUIC connection is not connected.");
 79        }
 80
 81        QuicStream stream;
 82        try
 30983        {
 30984            stream = await _connection.OpenOutboundStreamAsync(
 30985                bidirectional ? QuicStreamType.Bidirectional : QuicStreamType.Unidirectional,
 30986                cancellationToken).ConfigureAwait(false);
 29287        }
 1188        catch (QuicException exception)
 1189        {
 1190            throw exception.ToIceRpcException();
 91        }
 092        catch (SocketException exception)
 093        {
 094            throw exception.ToIceRpcException();
 95        }
 496        catch (ObjectDisposedException)
 497        {
 498            cancellationToken.ThrowIfCancellationRequested();
 499            throw;
 100        }
 101
 292102        return new QuicMultiplexedStream(
 292103            stream,
 292104            isRemote: false,
 292105            _pool,
 292106            _minSegmentSize,
 292107            ThrowIfClosedOrDisposed);
 292108    }
 109
 110    public async ValueTask DisposeAsync()
 236111    {
 236112        _isDisposed = true;
 113
 236114        if (_connection is not null)
 233115        {
 116            try
 233117            {
 233118                await _connection.DisposeAsync().ConfigureAwait(false);
 233119            }
 0120            catch
 0121            {
 122                // Workaround for https://github.com/dotnet/runtime/issues/78641.
 0123            }
 233124        }
 236125    }
 126
 127    private void ThrowIfClosedOrDisposed()
 19143128    {
 19143129        if (_isClosed)
 2130        {
 2131            throw new IceRpcException(IceRpcError.ConnectionAborted, "The connection was closed.");
 132        }
 19141133        else if (_isDisposed)
 2134        {
 2135            throw new IceRpcException(IceRpcError.ConnectionAborted, "The connection was disposed.");
 136        }
 19139137    }
 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)
 148    {
 149        // Establish the QUIC connection.
 150        try
 151        {
 152            _connection = await QuicConnection.ConnectAsync(
 153                _quicClientConnectionOptions,
 154                cancellationToken).ConfigureAwait(false);
 155        }
 156        catch (QuicException exception)
 157        {
 158            throw exception.ToIceRpcException();
 159        }
 160        catch (SocketException exception)
 161        {
 162            throw exception.ToIceRpcException();
 163        }
 164
 165        return new TransportConnectionInformation(
 166            localNetworkAddress: _connection.LocalEndPoint,
 167            remoteNetworkAddress: _connection.RemoteEndPoint,
 168            _connection.RemoteCertificate);
 169    }
 170
 171    internal QuicMultiplexedClientConnection(
 172        MultiplexedConnectionOptions options,
 173        QuicClientConnectionOptions quicOptions)
 174        : 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}