public QuicConnectionListener(QuicTransportOptions options, IQuicTrace log, EndPoint endpoint, SslServerAuthenticationOptions sslServerAuthenticationOptions)
        {
            if (options.Alpn == null)
            {
                throw new InvalidOperationException("QuicTransportOptions.Alpn must be configured with a value.");
            }

            _log     = log;
            _context = new QuicTransportContext(_log, options);
            var quicListenerOptions = new QuicListenerOptions();

            // TODO Should HTTP/3 specific ALPN still be global? Revisit whether it can be statically set once HTTP/3 is finalized.
            sslServerAuthenticationOptions.ApplicationProtocols = new List <SslApplicationProtocol>()
            {
                new SslApplicationProtocol(options.Alpn)
            };

            quicListenerOptions.ServerAuthenticationOptions = sslServerAuthenticationOptions;
            quicListenerOptions.ListenEndPoint = endpoint as IPEndPoint;
            quicListenerOptions.IdleTimeout    = options.IdleTimeout;

            _listener = new QuicListener(QuicImplementationProviders.MsQuic, quicListenerOptions);

            // Listener endpoint will resolve an ephemeral port, e.g. 127.0.0.1:0, into the actual port.
            EndPoint = _listener.ListenEndPoint;
        }
    public QuicConnectionListener(QuicTransportOptions options, ILogger log, EndPoint endpoint, SslServerAuthenticationOptions sslServerAuthenticationOptions)
    {
        if (!QuicImplementationProviders.Default.IsSupported)
        {
            throw new NotSupportedException("QUIC is not supported or enabled on this platform. See https://aka.ms/aspnet/kestrel/http3reqs for details.");
        }

        _log     = log;
        _context = new QuicTransportContext(_log, options);
        var quicListenerOptions = new QuicListenerOptions();

        var listenEndPoint = endpoint as IPEndPoint;

        if (listenEndPoint == null)
        {
            throw new InvalidOperationException($"QUIC doesn't support listening on the configured endpoint type. Expected {nameof(IPEndPoint)} but got {endpoint.GetType().Name}.");
        }

        quicListenerOptions.ServerAuthenticationOptions = sslServerAuthenticationOptions;
        quicListenerOptions.ListenEndPoint           = listenEndPoint;
        quicListenerOptions.IdleTimeout              = options.IdleTimeout;
        quicListenerOptions.MaxBidirectionalStreams  = options.MaxBidirectionalStreamCount;
        quicListenerOptions.MaxUnidirectionalStreams = options.MaxUnidirectionalStreamCount;
        quicListenerOptions.ListenBacklog            = options.Backlog;

        _listener = new QuicListener(quicListenerOptions);

        // Listener endpoint will resolve an ephemeral port, e.g. 127.0.0.1:0, into the actual port.
        EndPoint = _listener.ListenEndPoint;
    }
        public QuicConnectionListener(QuicTransportOptions options, IQuicTrace log, EndPoint endpoint)
        {
            if (options.Alpn == null)
            {
                throw new InvalidOperationException("QuicTransportOptions.Alpn must be configured with a value.");
            }

            _log     = log;
            _context = new QuicTransportContext(_log, options);
            EndPoint = endpoint;

            var quicListenerOptions = new QuicListenerOptions();
            var sslConfig           = new SslServerAuthenticationOptions();

            sslConfig.ServerCertificate    = options.Certificate;
            sslConfig.ApplicationProtocols = new List <SslApplicationProtocol>()
            {
                new SslApplicationProtocol(options.Alpn)
            };

            quicListenerOptions.ServerAuthenticationOptions = sslConfig;
            quicListenerOptions.CertificateFilePath         = options.CertificateFilePath;
            quicListenerOptions.PrivateKeyFilePath          = options.PrivateKeyFilePath;
            quicListenerOptions.ListenEndPoint = endpoint as IPEndPoint;
            quicListenerOptions.IdleTimeout    = TimeSpan.FromMinutes(2);

            _listener = new QuicListener(QuicImplementationProviders.MsQuic, quicListenerOptions);
            _listener.Start();
        }
        public static QuicTransportFactory CreateTransportFactory(ILoggerFactory loggerFactory = null)
        {
            var quicTransportOptions = new QuicTransportOptions();

            quicTransportOptions.Alpn        = Alpn;
            quicTransportOptions.IdleTimeout = TimeSpan.FromMinutes(1);

            return(new QuicTransportFactory(loggerFactory ?? NullLoggerFactory.Instance, Options.Create(quicTransportOptions)));
        }
    public async Task BindAsync_NoFeatures_Error()
    {
        // Arrange
        var quicTransportOptions = new QuicTransportOptions();
        var quicTransportFactory = new QuicTransportFactory(NullLoggerFactory.Instance, Options.Create(quicTransportOptions));

        // Act
        var ex = await Assert.ThrowsAsync <InvalidOperationException>(() => quicTransportFactory.BindAsync(new IPEndPoint(0, 0), features : null, cancellationToken : CancellationToken.None).AsTask()).DefaultTimeout();

        // Assert
        Assert.Equal("Couldn't find HTTPS configuration for QUIC transport.", ex.Message);
    }
    public static QuicTransportFactory CreateTransportFactory(ILoggerFactory loggerFactory = null, ISystemClock systemClock = null)
    {
        var quicTransportOptions = new QuicTransportOptions();

        quicTransportOptions.IdleTimeout = TimeSpan.FromMinutes(1);
        quicTransportOptions.MaxBidirectionalStreamCount  = 200;
        quicTransportOptions.MaxUnidirectionalStreamCount = 200;
        if (systemClock != null)
        {
            quicTransportOptions.SystemClock = systemClock;
        }

        return(new QuicTransportFactory(loggerFactory ?? NullLoggerFactory.Instance, Options.Create(quicTransportOptions)));
    }
    public async Task BindAsync_NoApplicationProtocols_Error()
    {
        // Arrange
        var quicTransportOptions = new QuicTransportOptions();
        var quicTransportFactory = new QuicTransportFactory(NullLoggerFactory.Instance, Options.Create(quicTransportOptions));
        var features             = new FeatureCollection();

        features.Set(new TlsConnectionCallbackOptions());

        // Act
        var ex = await Assert.ThrowsAsync <InvalidOperationException>(() => quicTransportFactory.BindAsync(new IPEndPoint(0, 0), features : features, cancellationToken : CancellationToken.None).AsTask()).DefaultTimeout();

        // Assert
        Assert.Equal("No application protocols specified for QUIC transport.", ex.Message);
    }
Exemple #8
0
    public async Task BindAsync_NoServerCertificate_Error()
    {
        // Arrange
        var quicTransportOptions = new QuicTransportOptions();
        var quicTransportFactory = new QuicTransportFactory(NullLoggerFactory.Instance, Options.Create(quicTransportOptions));
        var features             = new FeatureCollection();

        features.Set(new SslServerAuthenticationOptions());

        // Act
        var ex = await Assert.ThrowsAsync <InvalidOperationException>(() => quicTransportFactory.BindAsync(new IPEndPoint(0, 0), features : features, cancellationToken : CancellationToken.None).AsTask()).DefaultTimeout();

        // Assert
        Assert.Equal("SslServerAuthenticationOptions must provide a server certificate using ServerCertificate, ServerCertificateContext, or ServerCertificateSelectionCallback.", ex.Message);
    }
        public QuicConnectionListener(QuicTransportOptions options, IQuicTrace log, EndPoint endpoint)
        {
            _log     = log;
            _context = new QuicTransportContext(_log, options);
            EndPoint = endpoint;
            var sslConfig = new SslServerAuthenticationOptions();

            sslConfig.ServerCertificate    = options.Certificate;
            sslConfig.ApplicationProtocols = new List <SslApplicationProtocol>()
            {
                new SslApplicationProtocol(options.Alpn)
            };
            _listener = new QuicListener(QuicImplementationProviders.MsQuic, endpoint as IPEndPoint, sslConfig);
            _listener.Start();
        }
Exemple #10
0
    public QuicTransportFactory(ILoggerFactory loggerFactory, IOptions <QuicTransportOptions> options)
    {
        if (options == null)
        {
            throw new ArgumentNullException(nameof(options));
        }

        if (loggerFactory == null)
        {
            throw new ArgumentNullException(nameof(loggerFactory));
        }

        var logger = loggerFactory.CreateLogger("Microsoft.AspNetCore.Server.Kestrel.Transport.Quic");

        _log     = logger;
        _options = options.Value;
    }
Exemple #11
0
    public static QuicTransportFactory CreateTransportFactory(
        ILoggerFactory loggerFactory = null,
        ISystemClock systemClock     = null,
        long defaultCloseErrorCode   = 0)
    {
        var quicTransportOptions = new QuicTransportOptions();

        quicTransportOptions.MaxBidirectionalStreamCount  = 200;
        quicTransportOptions.MaxUnidirectionalStreamCount = 200;
        quicTransportOptions.DefaultCloseErrorCode        = defaultCloseErrorCode;
        if (systemClock != null)
        {
            quicTransportOptions.SystemClock = systemClock;
        }

        return(new QuicTransportFactory(loggerFactory ?? NullLoggerFactory.Instance, Options.Create(quicTransportOptions)));
    }
    public async Task BindAsync_SslServerAuthenticationOptions_Success()
    {
        // Arrange
        var quicTransportOptions = new QuicTransportOptions();
        var quicTransportFactory = new QuicTransportFactory(NullLoggerFactory.Instance, Options.Create(quicTransportOptions));
        var features             = new FeatureCollection();

        features.Set(new TlsConnectionCallbackOptions
        {
            ApplicationProtocols = new List <SslApplicationProtocol>
            {
                SslApplicationProtocol.Http3
            }
        });

        // Act & Assert
        await quicTransportFactory.BindAsync(new IPEndPoint(0, 0), features : features, cancellationToken : CancellationToken.None).AsTask().DefaultTimeout();
    }
Exemple #13
0
        public QuicConnectionListener(QuicTransportOptions options, IQuicTrace log, EndPoint endpoint, SslServerAuthenticationOptions sslServerAuthenticationOptions)
        {
            if (!QuicImplementationProviders.Default.IsSupported)
            {
                throw new NotSupportedException("QUIC is not supported or enabled on this platform. See https://aka.ms/aspnet/kestrel/http3reqs for details.");
            }

            _log     = log;
            _context = new QuicTransportContext(_log, options);
            var quicListenerOptions = new QuicListenerOptions();

            quicListenerOptions.ServerAuthenticationOptions = sslServerAuthenticationOptions;
            quicListenerOptions.ListenEndPoint           = endpoint as IPEndPoint;
            quicListenerOptions.IdleTimeout              = options.IdleTimeout;
            quicListenerOptions.MaxBidirectionalStreams  = options.MaxBidirectionalStreamCount;
            quicListenerOptions.MaxUnidirectionalStreams = options.MaxUnidirectionalStreamCount;

            _listener = new QuicListener(quicListenerOptions);

            // Listener endpoint will resolve an ephemeral port, e.g. 127.0.0.1:0, into the actual port.
            EndPoint = _listener.ListenEndPoint;
        }
Exemple #14
0
 public QuicTransportContext(IQuicTrace log, QuicTransportOptions options)
 {
     Log     = log;
     Options = options;
 }
    public QuicConnectionListener(
        QuicTransportOptions options,
        ILogger log,
        EndPoint endpoint,
        TlsConnectionCallbackOptions tlsConnectionCallbackOptions)
    {
        if (!QuicListener.IsSupported)
        {
            throw new NotSupportedException("QUIC is not supported or enabled on this platform. See https://aka.ms/aspnet/kestrel/http3reqs for details.");
        }

        if (endpoint is not IPEndPoint listenEndPoint)
        {
            throw new InvalidOperationException($"QUIC doesn't support listening on the configured endpoint type. Expected {nameof(IPEndPoint)} but got {endpoint.GetType().Name}.");
        }

        if (tlsConnectionCallbackOptions.ApplicationProtocols.Count == 0)
        {
            throw new InvalidOperationException("No application protocols specified.");
        }

        _pendingConnections = new ConditionalWeakTable <QuicConnection, QuicConnectionContext>();
        _log = log;
        _tlsConnectionCallbackOptions = tlsConnectionCallbackOptions;
        _context             = new QuicTransportContext(_log, options);
        _quicListenerOptions = new QuicListenerOptions
        {
            ApplicationProtocols      = _tlsConnectionCallbackOptions.ApplicationProtocols,
            ListenEndPoint            = listenEndPoint,
            ListenBacklog             = options.Backlog,
            ConnectionOptionsCallback = async(connection, helloInfo, cancellationToken) =>
            {
                // Create the connection context inside the callback because it's passed
                // to the connection callback. The field is then read once AcceptConnectionAsync
                // finishes awaiting.
                var currentAcceptingConnection = new QuicConnectionContext(connection, _context);
                _pendingConnections.Add(connection, currentAcceptingConnection);

                var context = new TlsConnectionCallbackContext
                {
                    ClientHelloInfo = helloInfo,
                    State           = _tlsConnectionCallbackOptions.OnConnectionState,
                    Connection      = currentAcceptingConnection,
                };
                var serverAuthenticationOptions = await _tlsConnectionCallbackOptions.OnConnection(context, cancellationToken);

                // If the callback didn't set protocols then use the listener's list of protocols.
                if (serverAuthenticationOptions.ApplicationProtocols == null)
                {
                    serverAuthenticationOptions.ApplicationProtocols = _tlsConnectionCallbackOptions.ApplicationProtocols;
                }

                // If the SslServerAuthenticationOptions doesn't have a cert or protocols then the
                // QUIC connection will fail and the client receives an unhelpful message.
                // Validate the options on the server and log issues to improve debugging.
                ValidateServerAuthenticationOptions(serverAuthenticationOptions);

                var connectionOptions = new QuicServerConnectionOptions
                {
                    ServerAuthenticationOptions = serverAuthenticationOptions,
                    IdleTimeout = Timeout.InfiniteTimeSpan, // Kestrel manages connection lifetimes itself so it can send GoAway's.
                    MaxInboundBidirectionalStreams  = options.MaxBidirectionalStreamCount,
                    MaxInboundUnidirectionalStreams = options.MaxUnidirectionalStreamCount,
                    DefaultCloseErrorCode           = options.DefaultCloseErrorCode,
                    DefaultStreamErrorCode          = options.DefaultStreamErrorCode,
                };
                return(connectionOptions);
            }
        };

        // Setting to listenEndPoint to prevent the property from being null.
        // This will be initialized when CreateListenerAsync() is invoked.
        EndPoint = listenEndPoint;
    }