| | 1 | | // Copyright (c) ZeroC, Inc. |
| | 2 | |
|
| | 3 | | using System.Diagnostics; |
| | 4 | | using System.Net; |
| | 5 | | using System.Net.Quic; |
| | 6 | | using System.Net.Security; |
| | 7 | | using System.Net.Sockets; |
| | 8 | |
|
| | 9 | | namespace IceRpc.Transports.Quic.Internal; |
| | 10 | |
|
| | 11 | | internal class QuicMultiplexedListener : IListener<IMultiplexedConnection> |
| | 12 | | { |
| 237 | 13 | | public ServerAddress ServerAddress { get; } |
| | 14 | |
|
| | 15 | | private readonly QuicListener _listener; |
| | 16 | | private readonly MultiplexedConnectionOptions _options; |
| | 17 | | private readonly QuicServerConnectionOptions _quicServerOptions; |
| | 18 | |
|
| | 19 | | public async Task<(IMultiplexedConnection, EndPoint)> AcceptAsync(CancellationToken cancellationToken) |
| 234 | 20 | | { |
| | 21 | | try |
| 234 | 22 | | { |
| 234 | 23 | | QuicConnection connection = await _listener.AcceptConnectionAsync(cancellationToken).ConfigureAwait(false); |
| 223 | 24 | | return (new QuicMultiplexedServerConnection(connection, _options), connection.RemoteEndPoint); |
| | 25 | | } |
| 0 | 26 | | catch (QuicException exception) |
| 0 | 27 | | { |
| 0 | 28 | | throw exception.ToIceRpcException(); |
| | 29 | | } |
| 0 | 30 | | catch (SocketException exception) |
| 0 | 31 | | { |
| 0 | 32 | | throw exception.ToIceRpcException(); |
| | 33 | | } |
| 223 | 34 | | } |
| | 35 | |
|
| 478 | 36 | | public ValueTask DisposeAsync() => _listener.DisposeAsync(); |
| | 37 | |
|
| 244 | 38 | | internal QuicMultiplexedListener( |
| 244 | 39 | | ServerAddress serverAddress, |
| 244 | 40 | | MultiplexedConnectionOptions options, |
| 244 | 41 | | QuicServerTransportOptions quicTransportOptions, |
| 244 | 42 | | SslServerAuthenticationOptions authenticationOptions) |
| 244 | 43 | | { |
| 244 | 44 | | if (!IPAddress.TryParse(serverAddress.Host, out IPAddress? ipAddress)) |
| 0 | 45 | | { |
| 0 | 46 | | throw new ArgumentException( |
| 0 | 47 | | $"Listening on the DNS name '{serverAddress.Host}' is not allowed; an IP address is required.", |
| 0 | 48 | | nameof(serverAddress)); |
| | 49 | | } |
| | 50 | |
|
| 244 | 51 | | _options = options; |
| | 52 | |
|
| 244 | 53 | | authenticationOptions = authenticationOptions.Clone(); |
| 244 | 54 | | authenticationOptions.ApplicationProtocols ??= new List<SslApplicationProtocol> // Mandatory with Quic |
| 244 | 55 | | { |
| 244 | 56 | | new SslApplicationProtocol(serverAddress.Protocol.Name) |
| 244 | 57 | | }; |
| | 58 | |
|
| 244 | 59 | | _quicServerOptions = new QuicServerConnectionOptions |
| 244 | 60 | | { |
| 244 | 61 | | DefaultCloseErrorCode = (int)MultiplexedConnectionCloseError.Aborted, |
| 244 | 62 | | DefaultStreamErrorCode = 0, |
| 244 | 63 | | IdleTimeout = quicTransportOptions.IdleTimeout, |
| 244 | 64 | | #if NET9_0_OR_GREATER |
| 244 | 65 | | KeepAliveInterval = Timeout.InfiniteTimeSpan, // the server doesn't send PING frames |
| 244 | 66 | | #endif |
| 244 | 67 | | ServerAuthenticationOptions = authenticationOptions, |
| 244 | 68 | | MaxInboundBidirectionalStreams = options.MaxBidirectionalStreams, |
| 244 | 69 | | MaxInboundUnidirectionalStreams = options.MaxUnidirectionalStreams |
| 244 | 70 | | }; |
| | 71 | |
|
| | 72 | | try |
| 244 | 73 | | { |
| | 74 | | // ListenAsync implementation is synchronous so it's safe to get the result synchronously. |
| 244 | 75 | | ValueTask<QuicListener> task = QuicListener.ListenAsync( |
| 244 | 76 | | new QuicListenerOptions |
| 244 | 77 | | { |
| 244 | 78 | | ListenEndPoint = new IPEndPoint(ipAddress, serverAddress.Port), |
| 244 | 79 | | ListenBacklog = quicTransportOptions.ListenBacklog, |
| 244 | 80 | | ApplicationProtocols = authenticationOptions.ApplicationProtocols, |
| 229 | 81 | | ConnectionOptionsCallback = (connection, sslInfo, cancellationToken) => new(_quicServerOptions) |
| 244 | 82 | | }, |
| 244 | 83 | | CancellationToken.None); |
| 242 | 84 | | Debug.Assert(task.IsCompleted); |
| 242 | 85 | | _listener = task.Result; |
| | 86 | |
|
| 242 | 87 | | ServerAddress = serverAddress with { Port = (ushort)_listener.LocalEndPoint.Port }; |
| 242 | 88 | | } |
| 2 | 89 | | catch (QuicException exception) |
| 2 | 90 | | { |
| 2 | 91 | | throw exception.ToIceRpcException(); |
| | 92 | | } |
| 0 | 93 | | catch (SocketException exception) |
| 0 | 94 | | { |
| 0 | 95 | | throw exception.ToIceRpcException(); |
| | 96 | | } |
| 242 | 97 | | } |
| | 98 | | } |