< 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: 2300_35243572715
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
 24122    private protected QuicMultiplexedConnection(MultiplexedConnectionOptions options)
 24123    {
 24124        _minSegmentSize = options.MinSegmentSize;
 24125        _pool = options.Pool;
 24126    }
 27
 28    public async ValueTask<IMultiplexedStream> AcceptStreamAsync(CancellationToken cancellationToken)
 31729    {
 31730        if (_connection is null)
 031        {
 032            throw new InvalidOperationException("The QUIC connection is not connected.");
 33        }
 34
 35        try
 31736        {
 31737            QuicStream stream = await _connection.AcceptInboundStreamAsync(cancellationToken).ConfigureAwait(false);
 29038            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        }
 29048    }
 49
 50    public abstract Task<TransportConnectionInformation> ConnectAsync(CancellationToken cancellationToken);
 51
 52    public async Task CloseAsync(MultiplexedConnectionCloseError closeError, CancellationToken cancellationToken)
 3853    {
 54        try
 3855        {
 3856            _isClosed = true;
 3857            if (_connection is not null)
 3858            {
 3859                await _connection.CloseAsync((long)closeError, cancellationToken).ConfigureAwait(false);
 3860            }
 3861        }
 062        catch (QuicException exception)
 063        {
 064            throw exception.ToIceRpcException();
 65        }
 066        catch (SocketException exception)
 067        {
 068            throw exception.ToIceRpcException();
 69        }
 3870    }
 71
 72    public async ValueTask<IMultiplexedStream> CreateStreamAsync(
 73        bool bidirectional,
 74        CancellationToken cancellationToken)
 32275    {
 32276        if (_connection is null)
 177        {
 178            throw new InvalidOperationException("The QUIC connection is not connected.");
 79        }
 80
 81        QuicStream stream;
 82        try
 32183        {
 32184            stream = await _connection.OpenOutboundStreamAsync(
 32185                bidirectional ? QuicStreamType.Bidirectional : QuicStreamType.Unidirectional,
 32186                cancellationToken).ConfigureAwait(false);
 30287        }
 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
 302102        return new QuicMultiplexedStream(
 302103            stream,
 302104            isRemote: false,
 302105            _pool,
 302106            _minSegmentSize,
 302107            ThrowIfClosedOrDisposed);
 302108    }
 109
 110    public async ValueTask DisposeAsync()
 248111    {
 248112        _isDisposed = true;
 113
 248114        if (_connection is not null)
 245115        {
 245116            await _connection.DisposeAsync().ConfigureAwait(false);
 245117        }
 248118    }
 119
 120    private void ThrowIfClosedOrDisposed()
 19342121    {
 19342122        if (_isClosed)
 2123        {
 2124            throw new IceRpcException(IceRpcError.ConnectionAborted, "The connection was closed.");
 125        }
 19340126        else if (_isDisposed)
 2127        {
 2128            throw new IceRpcException(IceRpcError.ConnectionAborted, "The connection was disposed.");
 129        }
 19338130    }
 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}