| | | 1 | | // Copyright (c) ZeroC, Inc. |
| | | 2 | | |
| | | 3 | | using IceRpc.Internal; |
| | | 4 | | using IceRpc.Transports; |
| | | 5 | | using IceRpc.Transports.Internal; |
| | | 6 | | using Microsoft.Extensions.Logging; |
| | | 7 | | using Microsoft.Extensions.Logging.Abstractions; |
| | | 8 | | using System.Net.Security; |
| | | 9 | | |
| | | 10 | | namespace IceRpc; |
| | | 11 | | |
| | | 12 | | /// <summary>Default implementation of <see cref="IClientProtocolConnectionFactory" />.</summary> |
| | | 13 | | public sealed class ClientProtocolConnectionFactory : IClientProtocolConnectionFactory |
| | | 14 | | { |
| | 2 | 15 | | private static readonly string[] _iceParams = ["t", "z"]; |
| | | 16 | | |
| | | 17 | | private readonly ConnectionOptions _connectionOptions; |
| | | 18 | | private readonly IDuplexClientTransport _duplexClientTransport; |
| | | 19 | | private readonly DuplexConnectionOptions _duplexConnectionOptions; |
| | | 20 | | private readonly SslClientAuthenticationOptions? _iceClientAuthenticationOptions; |
| | | 21 | | private readonly SslClientAuthenticationOptions? _iceRpcClientAuthenticationOptions; |
| | | 22 | | private readonly ILogger _logger; |
| | | 23 | | private readonly IMultiplexedClientTransport _multiplexedClientTransport; |
| | | 24 | | private readonly MultiplexedConnectionOptions _multiplexedConnectionOptions; |
| | | 25 | | |
| | | 26 | | /// <summary>Constructs a client protocol connection factory.</summary> |
| | | 27 | | /// <param name="connectionOptions">The connection options.</param> |
| | | 28 | | /// <param name="connectTimeout">The connect timeout.</param> |
| | | 29 | | /// <param name="clientAuthenticationOptions">The client authentication options.</param> |
| | | 30 | | /// <param name="duplexClientTransport">The duplex client transport. <see langword="null" /> is equivalent to <see |
| | | 31 | | /// cref="IDuplexClientTransport.Default" />.</param> |
| | | 32 | | /// <param name="multiplexedClientTransport">The multiplexed client transport. <see langword="null" /> is equivalent |
| | | 33 | | /// to <see cref="IMultiplexedClientTransport.Default" />.</param> |
| | | 34 | | /// <param name="logger">The logger. <see langword="null" /> is equivalent to <see cref="NullLogger.Instance" />. |
| | | 35 | | /// </param> |
| | 98 | 36 | | public ClientProtocolConnectionFactory( |
| | 98 | 37 | | ConnectionOptions connectionOptions, |
| | 98 | 38 | | TimeSpan connectTimeout, |
| | 98 | 39 | | SslClientAuthenticationOptions? clientAuthenticationOptions = null, |
| | 98 | 40 | | IDuplexClientTransport? duplexClientTransport = null, |
| | 98 | 41 | | IMultiplexedClientTransport? multiplexedClientTransport = null, |
| | 98 | 42 | | ILogger? logger = null) |
| | 98 | 43 | | { |
| | 98 | 44 | | if (clientAuthenticationOptions?.ApplicationProtocols is not null) |
| | 0 | 45 | | { |
| | 0 | 46 | | throw new ArgumentException( |
| | 0 | 47 | | "The ApplicationProtocols property of the SSL client authentication options must be null. The ALPN is se |
| | 0 | 48 | | nameof(clientAuthenticationOptions)); |
| | | 49 | | } |
| | | 50 | | |
| | 98 | 51 | | _connectionOptions = connectionOptions; |
| | | 52 | | |
| | 98 | 53 | | _duplexClientTransport = duplexClientTransport ?? IDuplexClientTransport.Default; |
| | 98 | 54 | | _duplexConnectionOptions = new DuplexConnectionOptions |
| | 98 | 55 | | { |
| | 98 | 56 | | Pool = connectionOptions.Pool, |
| | 98 | 57 | | MinSegmentSize = connectionOptions.MinSegmentSize, |
| | 98 | 58 | | }; |
| | | 59 | | |
| | 98 | 60 | | _multiplexedClientTransport = multiplexedClientTransport ?? IMultiplexedClientTransport.Default; |
| | | 61 | | |
| | | 62 | | // If the dispatcher is null, we don't allow the peer to open streams for incoming requests. The only stream |
| | | 63 | | // which is accepted locally is the control stream created by the peer. |
| | 98 | 64 | | _multiplexedConnectionOptions = new MultiplexedConnectionOptions |
| | 98 | 65 | | { |
| | 98 | 66 | | HandshakeTimeout = connectTimeout, |
| | 98 | 67 | | |
| | 98 | 68 | | MaxBidirectionalStreams = connectionOptions.Dispatcher is null ? 0 : |
| | 98 | 69 | | connectionOptions.MaxIceRpcBidirectionalStreams, |
| | 98 | 70 | | |
| | 98 | 71 | | // Add an additional stream for the icerpc protocol control stream. |
| | 98 | 72 | | MaxUnidirectionalStreams = connectionOptions.Dispatcher is null ? 1 : |
| | 98 | 73 | | connectionOptions.MaxIceRpcUnidirectionalStreams + 1, |
| | 98 | 74 | | |
| | 98 | 75 | | Pool = connectionOptions.Pool, |
| | 98 | 76 | | MinSegmentSize = connectionOptions.MinSegmentSize, |
| | 98 | 77 | | }; |
| | | 78 | | |
| | | 79 | | // Clone and set ALPN for each protocol. |
| | 98 | 80 | | if (clientAuthenticationOptions is not null) |
| | 5 | 81 | | { |
| | 5 | 82 | | _iceClientAuthenticationOptions = clientAuthenticationOptions.ShallowClone(); |
| | 5 | 83 | | _iceClientAuthenticationOptions.ApplicationProtocols = [Protocol.Ice.AlpnProtocol]; |
| | | 84 | | |
| | 5 | 85 | | _iceRpcClientAuthenticationOptions = clientAuthenticationOptions.ShallowClone(); |
| | 5 | 86 | | _iceRpcClientAuthenticationOptions.ApplicationProtocols = [Protocol.IceRpc.AlpnProtocol]; |
| | 5 | 87 | | } |
| | | 88 | | |
| | 98 | 89 | | _logger = logger ?? NullLogger.Instance; |
| | 98 | 90 | | } |
| | | 91 | | |
| | | 92 | | /// <summary>Creates a protocol connection to the specified server address.</summary> |
| | | 93 | | /// <param name="serverAddress">The address of the server.</param> |
| | | 94 | | /// <returns>The new protocol connection.</returns> |
| | | 95 | | /// <remarks>The protocol connection returned by this factory method is not connected. The caller must call |
| | | 96 | | /// <see cref="IProtocolConnection.ConnectAsync" /> exactly once on this connection before calling |
| | | 97 | | /// <see cref="IInvoker.InvokeAsync" />.</remarks> |
| | | 98 | | public IProtocolConnection CreateConnection(ServerAddress serverAddress) |
| | 99 | 99 | | { |
| | 99 | 100 | | var transportAddress = new TransportAddress |
| | 99 | 101 | | { |
| | 99 | 102 | | Host = serverAddress.Host, |
| | 99 | 103 | | Port = serverAddress.Port, |
| | 99 | 104 | | TransportName = serverAddress.Transport, |
| | 99 | 105 | | Params = serverAddress.Params |
| | 99 | 106 | | }; |
| | | 107 | | |
| | | 108 | | IProtocolConnection connection; |
| | 99 | 109 | | if (serverAddress.Protocol == Protocol.Ice) |
| | 27 | 110 | | { |
| | 27 | 111 | | SslClientAuthenticationOptions? clientAuthenticationOptions = _iceClientAuthenticationOptions; |
| | 27 | 112 | | if (clientAuthenticationOptions is null && _duplexClientTransport.IsSslRequired(serverAddress.Transport)) |
| | 0 | 113 | | { |
| | 0 | 114 | | clientAuthenticationOptions = new SslClientAuthenticationOptions |
| | 0 | 115 | | { |
| | 0 | 116 | | ApplicationProtocols = [Protocol.Ice.AlpnProtocol] |
| | 0 | 117 | | }; |
| | 0 | 118 | | } |
| | | 119 | | |
| | | 120 | | // Strip Ice-specific params ("t", "z") before passing to transport. These params have no |
| | | 121 | | // effect with IceRPC transports. |
| | 27 | 122 | | transportAddress = transportAddress with |
| | 27 | 123 | | { |
| | 27 | 124 | | Params = transportAddress.Params.RemoveRange(_iceParams) |
| | 27 | 125 | | }; |
| | | 126 | | |
| | 27 | 127 | | connection = new IceProtocolConnection( |
| | 27 | 128 | | _duplexClientTransport.CreateConnection(transportAddress, _duplexConnectionOptions, clientAuthentication |
| | 27 | 129 | | transportConnectionInformation: null, |
| | 27 | 130 | | _connectionOptions); |
| | 27 | 131 | | } |
| | | 132 | | else |
| | 72 | 133 | | { |
| | 72 | 134 | | SslClientAuthenticationOptions? clientAuthenticationOptions = _iceRpcClientAuthenticationOptions; |
| | 72 | 135 | | if (clientAuthenticationOptions is null && _multiplexedClientTransport.IsSslRequired(serverAddress.Transport |
| | 0 | 136 | | { |
| | 0 | 137 | | clientAuthenticationOptions = new SslClientAuthenticationOptions |
| | 0 | 138 | | { |
| | 0 | 139 | | ApplicationProtocols = [Protocol.IceRpc.AlpnProtocol] |
| | 0 | 140 | | }; |
| | 0 | 141 | | } |
| | | 142 | | |
| | 72 | 143 | | connection = new IceRpcProtocolConnection( |
| | 72 | 144 | | _multiplexedClientTransport.CreateConnection(transportAddress, _multiplexedConnectionOptions, clientAuth |
| | 72 | 145 | | transportConnectionInformation: null, |
| | 72 | 146 | | _connectionOptions, |
| | 72 | 147 | | taskExceptionObserver: _logger == NullLogger.Instance ? null : |
| | 72 | 148 | | new LogTaskExceptionObserver(_logger)); |
| | 72 | 149 | | } |
| | | 150 | | |
| | 99 | 151 | | connection = new MetricsProtocolConnectionDecorator(connection, Metrics.ClientMetrics, connectStarted: false); |
| | | 152 | | |
| | 99 | 153 | | return _logger == NullLogger.Instance ? connection : |
| | 99 | 154 | | new LogProtocolConnectionDecorator(connection, serverAddress, remoteNetworkAddress: null, _logger); |
| | 99 | 155 | | } |
| | | 156 | | } |