File: System\Net\Quic\QuicConnectionOptions.cs
Web Access
Project: src\src\libraries\System.Net.Quic\src\System.Net.Quic.csproj (System.Net.Quic)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.Net.Security;
using System.Runtime.CompilerServices;
using System.Threading;
using static System.Net.Quic.ThrowHelper;
 
namespace System.Net.Quic;
 
/// <summary>
/// Collection of receive window sizes for <see cref="QuicConnection"/> as a whole and for individual <see cref="QuicStream"/> types.
/// </summary>
public sealed class QuicReceiveWindowSizes
{
    /// <summary>
    /// The initial flow-control window size for the connection.
    /// </summary>
    public int Connection { get; set; } = QuicDefaults.DefaultConnectionMaxData;
 
    /// <summary>
    /// The initial flow-control window size for locally initiated bidirectional streams.
    /// </summary>
    public int LocallyInitiatedBidirectionalStream { get; set; } = QuicDefaults.DefaultStreamMaxData;
 
    /// <summary>
    /// The initial flow-control window size for remotely initiated bidirectional streams.
    /// </summary>
    public int RemotelyInitiatedBidirectionalStream { get; set; } = QuicDefaults.DefaultStreamMaxData;
 
    /// <summary>
    /// The initial flow-control window size for (remotely initiated) unidirectional streams.
    /// </summary>
    public int UnidirectionalStream { get; set; } = QuicDefaults.DefaultStreamMaxData;
 
    internal void Validate(string argumentName)
    {
        ValidatePowerOf2(argumentName, Connection);
        ValidatePowerOf2(argumentName, LocallyInitiatedBidirectionalStream);
        ValidatePowerOf2(argumentName, RemotelyInitiatedBidirectionalStream);
        ValidatePowerOf2(argumentName, UnidirectionalStream);
 
        static void ValidatePowerOf2(string argumentName, int value, [CallerArgumentExpression(nameof(value))] string? propertyName = null)
        {
            if (value <= 0 || ((value - 1) & value) != 0)
            {
                throw new ArgumentOutOfRangeException(argumentName, value, SR.Format(SR.net_quic_power_of_2, $"{nameof(QuicConnectionOptions.InitialReceiveWindowSizes)}.{propertyName}"));
            }
        }
    }
}
 
/// <summary>
/// Arguments for <see cref="QuicConnectionOptions.StreamCapacityCallback"/>.
/// </summary>
public readonly struct QuicStreamCapacityChangedArgs
{
    /// <summary>
    /// The increment saying how many additional bidirectional streams can be opened on the connection, increased via the latest MAX_STREAMS frame.
    /// </summary>
    public int BidirectionalIncrement { get; init; }
    /// <summary>
    /// The increment saying how many additional unidirectional streams can be opened on the connection, increased via the latest MAX_STREAMS frame.
    /// </summary>
    public int UnidirectionalIncrement { get; init; }
}
 
/// <summary>
/// Shared options for both client (outbound) and server (inbound) <see cref="QuicConnection" />.
/// </summary>
public abstract class QuicConnectionOptions
{
    /// <summary>
    /// Prevent sub-classing by code outside of this assembly.
    /// </summary>
    internal QuicConnectionOptions()
    { }
 
    /// <summary>
    /// The maximum number of concurrent bidirectional streams that the remote peer connection can create on an open connection.
    /// Default 0 for client and 100 for server connection.
    /// </summary>
    public int MaxInboundBidirectionalStreams { get; set; }
 
    /// <summary>
    /// The maximum number of concurrent unidirectional streams that the remote peer connection can create on an open connection.
    /// Default 0 for client and 10 for server connection.
    /// </summary>
    public int MaxInboundUnidirectionalStreams { get; set; }
 
    /// <summary>
    /// Idle timeout for connections, after which the connection will be closed.
    /// Default <see cref="TimeSpan.Zero"/> means underlying implementation default idle timeout.
    /// </summary>
    public TimeSpan IdleTimeout { get; set; } = TimeSpan.Zero;
 
    /// <summary>
    /// Error code used when the stream needs to abort read or write side of the stream internally.
    /// This property is mandatory and not setting it will result in validation error when establishing a connection.
    /// </summary>
    // QUIC doesn't allow negative value: https://www.rfc-editor.org/rfc/rfc9000.html#integer-encoding
    // We can safely use this to distinguish if user provided value during validation.
    public long DefaultStreamErrorCode { get; set; } = -1;
 
    /// <summary>
    /// Error code used for <see cref="QuicConnection.CloseAsync(long, Threading.CancellationToken)"/> when the connection gets disposed.
    /// This property is mandatory and not setting it will result in validation error when establishing a connection.
    /// </summary>
    /// <remarks>
    /// To use different close error code, call  <see cref="QuicConnection.CloseAsync(long, Threading.CancellationToken)"/> explicitly before disposing.
    /// </remarks>
    // QUIC doesn't allow negative value: https://www.rfc-editor.org/rfc/rfc9000.html#integer-encoding
    // We can safely use this to distinguish if user provided value during validation.
    public long DefaultCloseErrorCode { get; set; } = -1;
 
    internal QuicReceiveWindowSizes? _initialReceiveWindowSizes;
 
    /// <summary>
    /// The initial receive window sizes for the connection and individual stream types.
    /// </summary>
    public QuicReceiveWindowSizes InitialReceiveWindowSizes
    {
        get => _initialReceiveWindowSizes ??= new QuicReceiveWindowSizes();
        set => _initialReceiveWindowSizes = value;
    }
 
    /// <summary>
    /// The interval at which keep alive packets are sent on the connection.
    /// Value <see cref="TimeSpan.Zero"/> means underlying implementation default timeout.
    /// Default <see cref="Timeout.InfiniteTimeSpan"/> means never sending keep alive packets.
    /// </summary>
    public TimeSpan KeepAliveInterval { get; set; } = Timeout.InfiniteTimeSpan;
 
    /// <summary>
    /// The upper bound on time when the handshake must complete. If the handshake does not
    /// complete in this time, the connection is aborted.
    /// Value <see cref="TimeSpan.Zero"/> means underlying implementation default timeout.
    /// Default timeout is 10 seconds.
    /// </summary>
    public TimeSpan HandshakeTimeout { get; set; } = QuicDefaults.HandshakeTimeout;
 
    /// <summary>
    /// Optional callback that is invoked when new stream limit is released by the peer. Corresponds to receiving a MAX_STREAMS frame.
    /// The callback values represent increments of stream limits, e.g.: current limit is 10 bidirectional streams, callback arguments notify 5 more additional bidirectional streams => 15 bidirectional streams can be opened in total at the moment.
    /// The initial capacity is reported with the first invocation of the callback that might happen before the <see cref="QuicConnection"/> instance is handed out via either
    /// <see cref="QuicConnection.ConnectAsync(QuicClientConnectionOptions, CancellationToken)"/> or <see cref="QuicListener.AcceptConnectionAsync(CancellationToken)"/>.
    /// </summary>
    public Action<QuicConnection, QuicStreamCapacityChangedArgs>? StreamCapacityCallback { get; set; }
 
    /// <summary>
    /// Validates the options and potentially sets platform specific defaults.
    /// </summary>
    /// <param name="argumentName">Name of the from the caller.</param>
    internal virtual void Validate(string argumentName)
    {
        ValidateInRange(argumentName, MaxInboundBidirectionalStreams, ushort.MaxValue);
        ValidateInRange(argumentName, MaxInboundUnidirectionalStreams, ushort.MaxValue);
        ValidateTimeSpan(argumentName, IdleTimeout);
        ValidateTimeSpan(argumentName, KeepAliveInterval);
        ValidateErrorCode(argumentName, DefaultCloseErrorCode);
        ValidateErrorCode(argumentName, DefaultStreamErrorCode);
        ValidateTimeSpan(argumentName, HandshakeTimeout);
 
        _initialReceiveWindowSizes?.Validate(argumentName);
    }
}
 
/// <summary>
/// Options for client (outbound) <see cref="QuicConnection" />.
/// </summary>
public sealed class QuicClientConnectionOptions : QuicConnectionOptions
{
    /// <summary>
    /// Initializes a new instance of the <see cref="QuicClientConnectionOptions"/> class.
    /// </summary>
    public QuicClientConnectionOptions()
    {
        MaxInboundBidirectionalStreams = QuicDefaults.DefaultClientMaxInboundBidirectionalStreams;
        MaxInboundUnidirectionalStreams = QuicDefaults.DefaultClientMaxInboundUnidirectionalStreams;
    }
 
    /// <summary>
    /// Client authentication options to use when establishing a new connection.
    /// This property is mandatory and not setting it will result in validation error when establishing a connection.
    /// </summary>
    public SslClientAuthenticationOptions ClientAuthenticationOptions { get; set; } = null!;
 
    /// <summary>
    /// The remote endpoint to connect to. May be both <see cref="DnsEndPoint"/>, which will get resolved to an IP before connecting, or directly <see cref="IPEndPoint"/>.
    /// This property is mandatory and not setting it will result in validation error when establishing a connection.
    /// </summary>
    public EndPoint RemoteEndPoint { get; set; } = null!;
 
    /// <summary>
    /// The optional local endpoint that will be bound to.
    /// </summary>
    public IPEndPoint? LocalEndPoint { get; set; }
 
    /// <summary>
    /// Validates the options and potentially sets platform specific defaults.
    /// </summary>
    /// <param name="argumentName">Name of the from the caller.</param>
    internal override void Validate(string argumentName)
    {
        base.Validate(argumentName);
 
        // The content of ClientAuthenticationOptions gets validate in MsQuicConfiguration.Create.
        ValidateNotNull(argumentName, SR.net_quic_not_null_open_connection, ClientAuthenticationOptions);
        ValidateNotNull(argumentName, SR.net_quic_not_null_open_connection, RemoteEndPoint);
    }
}
 
/// <summary>
/// Options for server (inbound) <see cref="QuicConnection" />. Provided by <see cref="QuicListenerOptions.ConnectionOptionsCallback"/>.
/// </summary>
public sealed class QuicServerConnectionOptions : QuicConnectionOptions
{
    /// <summary>
    /// Initializes a new instance of the <see cref="QuicClientConnectionOptions"/> class.
    /// </summary>
    public QuicServerConnectionOptions()
    {
        MaxInboundBidirectionalStreams = QuicDefaults.DefaultServerMaxInboundBidirectionalStreams;
        MaxInboundUnidirectionalStreams = QuicDefaults.DefaultServerMaxInboundUnidirectionalStreams;
    }
 
    /// <summary>
    /// Server authentication options to use when accepting a new connection.
    /// This property is mandatory and not setting it will result in validation error when establishing a connection.
    /// </summary>
    public SslServerAuthenticationOptions ServerAuthenticationOptions { get; set; } = null!;
 
    /// <summary>
    /// Validates the options and potentially sets platform specific defaults.
    /// </summary>
    /// <param name="argumentName">Name of the from the caller.</param>
    internal override void Validate(string argumentName)
    {
        base.Validate(argumentName);
 
        // The content of ServerAuthenticationOptions gets validate in MsQuicConfiguration.Create.
        ValidateNotNull(argumentName, SR.net_quic_not_null_accept_connection, ServerAuthenticationOptions);
    }
}