示例#1
0
    public QuicStreamContext(QuicConnectionContext connection, QuicTransportContext context)
    {
        _connection = connection;
        _context    = context;
        _log        = context.Log;
        MemoryPool  = connection.MemoryPool;
        MultiplexedConnectionFeatures = connection.Features;

        RemoteEndPoint = connection.RemoteEndPoint;
        LocalEndPoint  = connection.LocalEndPoint;

        var maxReadBufferSize  = context.Options.MaxReadBufferSize ?? 0;
        var maxWriteBufferSize = context.Options.MaxWriteBufferSize ?? 0;

        // TODO should we allow these PipeScheduler to be configurable here?
        var inputOptions  = new PipeOptions(MemoryPool, PipeScheduler.ThreadPool, PipeScheduler.Inline, maxReadBufferSize, maxReadBufferSize / 2, useSynchronizationContext: false);
        var outputOptions = new PipeOptions(MemoryPool, PipeScheduler.Inline, PipeScheduler.ThreadPool, maxWriteBufferSize, maxWriteBufferSize / 2, useSynchronizationContext: false);

        _inputPipe  = new Pipe(inputOptions);
        _outputPipe = new Pipe(outputOptions);

        _transportPipeReader = new CompletionPipeReader(_inputPipe.Reader);
        _transportPipeWriter = new CompletionPipeWriter(_outputPipe.Writer);

        _originalApplication = new DuplexPipe(_outputPipe.Reader, _inputPipe.Writer);
        _originalTransport   = new DuplexPipe(_transportPipeReader, _transportPipeWriter);
    }
    public async ValueTask <MultiplexedConnectionContext?> AcceptAsync(IFeatureCollection?features = null, CancellationToken cancellationToken = default)
    {
        try
        {
            var quicConnection = await _listener.AcceptConnectionAsync(cancellationToken);

            var connectionContext = new QuicConnectionContext(quicConnection, _context);

            QuicLog.AcceptedConnection(_log, connectionContext);

            return(connectionContext);
        }
        catch (QuicOperationAbortedException ex)
        {
            _log.LogDebug($"Listener has aborted with exception: {ex.Message}");
        }
        return(null);
    }
    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;
    }