< Summary

Information
Class: IceRpc.Transports.Quic.Internal.QuicMultiplexedServerConnection
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
100%
Covered lines: 5
Uncovered lines: 0
Coverable lines: 5
Total lines: 182
Line coverage: 100%
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%11100%
.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;
 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
 18    private protected QuicMultiplexedConnection(MultiplexedConnectionOptions options)
 19    {
 20        _minSegmentSize = options.MinSegmentSize;
 21        _pool = options.Pool;
 22    }
 23
 24    public async ValueTask<IMultiplexedStream> AcceptStreamAsync(CancellationToken cancellationToken)
 25    {
 26        if (_connection is null)
 27        {
 28            throw new InvalidOperationException("The Quic connection is not connected.");
 29        }
 30
 31        try
 32        {
 33            QuicStream stream = await _connection.AcceptInboundStreamAsync(cancellationToken).ConfigureAwait(false);
 34            return new QuicMultiplexedStream(stream, isRemote: true, _pool, _minSegmentSize, ThrowIfClosedOrDisposed);
 35        }
 36        catch (QuicException exception)
 37        {
 38            throw exception.ToIceRpcException();
 39        }
 40        catch (SocketException exception)
 41        {
 42            throw exception.ToIceRpcException();
 43        }
 44    }
 45
 46    public abstract Task<TransportConnectionInformation> ConnectAsync(CancellationToken cancellationToken);
 47
 48    public async Task CloseAsync(MultiplexedConnectionCloseError closeError, CancellationToken cancellationToken)
 49    {
 50        try
 51        {
 52            _isClosed = true;
 53            if (_connection is not null)
 54            {
 55                await _connection.CloseAsync((long)closeError, cancellationToken).ConfigureAwait(false);
 56            }
 57        }
 58        catch (QuicException exception)
 59        {
 60            throw exception.ToIceRpcException();
 61        }
 62        catch (SocketException exception)
 63        {
 64            throw exception.ToIceRpcException();
 65        }
 66    }
 67
 68    public async ValueTask<IMultiplexedStream> CreateStreamAsync(
 69        bool bidirectional,
 70        CancellationToken cancellationToken)
 71    {
 72        if (_connection is null)
 73        {
 74            throw new InvalidOperationException("The Quic connection is not connected.");
 75        }
 76
 77        QuicStream stream;
 78        try
 79        {
 80            stream = await _connection.OpenOutboundStreamAsync(
 81                bidirectional ? QuicStreamType.Bidirectional : QuicStreamType.Unidirectional,
 82                cancellationToken).ConfigureAwait(false);
 83        }
 84        catch (QuicException exception)
 85        {
 86            throw exception.ToIceRpcException();
 87        }
 88        catch (SocketException exception)
 89        {
 90            throw exception.ToIceRpcException();
 91        }
 92        catch (ObjectDisposedException)
 93        {
 94            cancellationToken.ThrowIfCancellationRequested();
 95            throw;
 96        }
 97
 98        return new QuicMultiplexedStream(
 99            stream,
 100            isRemote: false,
 101            _pool,
 102            _minSegmentSize,
 103            ThrowIfClosedOrDisposed);
 104    }
 105
 106    public async ValueTask DisposeAsync()
 107    {
 108        _isDisposed = true;
 109
 110        if (_connection is not null)
 111        {
 112            try
 113            {
 114                await _connection.DisposeAsync().ConfigureAwait(false);
 115            }
 116            catch
 117            {
 118                // Workaround for https://github.com/dotnet/runtime/issues/78641.
 119            }
 120        }
 121    }
 122
 123    private void ThrowIfClosedOrDisposed()
 124    {
 125        if (_isClosed)
 126        {
 127            throw new IceRpcException(IceRpcError.ConnectionAborted, "The connection was closed.");
 128        }
 129        else if (_isDisposed)
 130        {
 131            throw new IceRpcException(IceRpcError.ConnectionAborted, "The connection was disposed.");
 132        }
 133    }
 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) =>
 223173        Task.FromResult(new TransportConnectionInformation(
 223174            localNetworkAddress: _connection!.LocalEndPoint,
 223175            remoteNetworkAddress: _connection.RemoteEndPoint,
 223176            _connection.RemoteCertificate));
 177
 178    internal QuicMultiplexedServerConnection(
 179        QuicConnection connection,
 180        MultiplexedConnectionOptions options)
 446181        : base(options) => _connection = connection;
 182}