< 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: 1321_24790053727
Line coverage
81%
Covered lines: 61
Uncovered lines: 14
Coverable lines: 75
Total lines: 185
Line coverage: 81.3%
Branch coverage
92%
Covered branches: 13
Total branches: 14
Branch coverage: 92.8%
Method coverage
100%
Covered methods: 6
Fully covered methods: 3
Total methods: 6
Method coverage: 100%
Full method coverage: 50%

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
AcceptStreamAsync()50%2264.28%
CloseAsync()100%2260%
CreateStreamAsync()100%4488.46%
DisposeAsync()100%22100%
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
 23722    private protected QuicMultiplexedConnection(MultiplexedConnectionOptions options)
 23723    {
 23724        _minSegmentSize = options.MinSegmentSize;
 23725        _pool = options.Pool;
 23726    }
 27
 28    public async ValueTask<IMultiplexedStream> AcceptStreamAsync(CancellationToken cancellationToken)
 31529    {
 31530        if (_connection is null)
 031        {
 032            throw new InvalidOperationException("The QUIC connection is not connected.");
 33        }
 34
 35        try
 31536        {
 31537            QuicStream stream = await _connection.AcceptInboundStreamAsync(cancellationToken).ConfigureAwait(false);
 28838            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        }
 28848    }
 49
 50    public abstract Task<TransportConnectionInformation> ConnectAsync(CancellationToken cancellationToken);
 51
 52    public async Task CloseAsync(MultiplexedConnectionCloseError closeError, CancellationToken cancellationToken)
 3753    {
 54        try
 3755        {
 3756            _isClosed = true;
 3757            if (_connection is not null)
 3758            {
 3759                await _connection.CloseAsync((long)closeError, cancellationToken).ConfigureAwait(false);
 3760            }
 3761        }
 062        catch (QuicException exception)
 063        {
 064            throw exception.ToIceRpcException();
 65        }
 066        catch (SocketException exception)
 067        {
 068            throw exception.ToIceRpcException();
 69        }
 3770    }
 71
 72    public async ValueTask<IMultiplexedStream> CreateStreamAsync(
 73        bool bidirectional,
 74        CancellationToken cancellationToken)
 31875    {
 31876        if (_connection is null)
 177        {
 178            throw new InvalidOperationException("The QUIC connection is not connected.");
 79        }
 80
 81        QuicStream stream;
 82        try
 31783        {
 31784            stream = await _connection.OpenOutboundStreamAsync(
 31785                bidirectional ? QuicStreamType.Bidirectional : QuicStreamType.Unidirectional,
 31786                cancellationToken).ConfigureAwait(false);
 30087        }
 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
 300102        return new QuicMultiplexedStream(
 300103            stream,
 300104            isRemote: false,
 300105            _pool,
 300106            _minSegmentSize,
 300107            ThrowIfClosedOrDisposed);
 300108    }
 109
 110    public async ValueTask DisposeAsync()
 244111    {
 244112        _isDisposed = true;
 113
 244114        if (_connection is not null)
 241115        {
 241116            await _connection.DisposeAsync().ConfigureAwait(false);
 241117        }
 244118    }
 119
 120    private void ThrowIfClosedOrDisposed()
 19252121    {
 19252122        if (_isClosed)
 2123        {
 2124            throw new IceRpcException(IceRpcError.ConnectionAborted, "The connection was closed.");
 125        }
 19250126        else if (_isDisposed)
 2127        {
 2128            throw new IceRpcException(IceRpcError.ConnectionAborted, "The connection was disposed.");
 129        }
 19248130    }
 131}
 132
 133[SupportedOSPlatform("linux")]
 134[SupportedOSPlatform("macos")]
 135[SupportedOSPlatform("windows")]
 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
 170[SupportedOSPlatform("linux")]
 171[SupportedOSPlatform("macos")]
 172[SupportedOSPlatform("windows")]
 173internal class QuicMultiplexedServerConnection : QuicMultiplexedConnection
 174{
 175    public override Task<TransportConnectionInformation> ConnectAsync(CancellationToken cancellationToken) =>
 176        Task.FromResult(new TransportConnectionInformation(
 177            localNetworkAddress: _connection!.LocalEndPoint,
 178            remoteNetworkAddress: _connection.RemoteEndPoint,
 179            _connection.RemoteCertificate));
 180
 181    internal QuicMultiplexedServerConnection(
 182        QuicConnection connection,
 183        MultiplexedConnectionOptions options)
 184        : base(options) => _connection = connection;
 185}