public ProtoConnectionMiddleware(IList <IConnectionAdapter> adapters, ServiceContext serviceContext, IProtoApplication <TContext> application, ProtoProtocols protocols)
        {
            _serviceContext = serviceContext;
            _application    = application;
            _protocols      = protocols;

            // Keeping these around for now so progress can be made without updating tests
            _connectionAdapters = adapters;
        }
Esempio n. 2
0
        /// <summary>
        /// Create a new handler.
        /// </summary>
        /// <param name="pathBase">The base path.</param>
        /// <param name="application">The <see cref="IProtoApplication{TContext}"/>.</param>
        public ClientHandler(PathString pathBase, IProtoApplication <Context> application)
        {
            _application = application ?? throw new ArgumentNullException(nameof(application));

            // PathString.StartsWithSegments that we use below requires the base path to not end in a slash.
            if (pathBase.HasValue && pathBase.Value.EndsWith("/"))
            {
                pathBase = new PathString(pathBase.Value.Substring(0, pathBase.Value.Length - 1));
            }
            _pathBase = pathBase;
        }
Esempio n. 3
0
        public async Task StartAsync <TContext>(IProtoApplication <TContext> application, CancellationToken cancellationToken)
        {
            try
            {
                if (!BitConverter.IsLittleEndian)
                {
                    throw new PlatformNotSupportedException(CoreStrings.BigEndianNotSupported);
                }

                ValidateOptions();

                if (_hasStarted)
                {
                    // The server has already started and/or has not been cleaned up yet
                    throw new InvalidOperationException(CoreStrings.ServerAlreadyStarted);
                }
                _hasStarted = true;

                ServiceContext.Heartbeat?.Start();

                async Task OnBind(ListenOptions endpoint)
                {
                    // Add the HTTP middleware as the terminal connection middleware
                    endpoint.UseProtoServer(endpoint.ConnectionAdapters, ServiceContext, application, endpoint.Protocols);

                    var connectionDelegate = endpoint.Build();

                    // Add the connection limit middleware
                    if (Options.Limits.MaxConcurrentConnections.HasValue)
                    {
                        connectionDelegate = new ConnectionLimitMiddleware(connectionDelegate, Options.Limits.MaxConcurrentConnections.Value, Trace).OnConnectionAsync;
                    }

                    var connectionDispatcher = new ConnectionDispatcher(ServiceContext, connectionDelegate);
                    var transport            = _transportFactory.Create(endpoint, connectionDispatcher);

                    _transports.Add(transport);

                    await transport.BindAsync().ConfigureAwait(false);
                }

                await AddressBinder.BindAsync(_serverAddresses, Options, Trace, OnBind).ConfigureAwait(false);
            }
            catch (Exception ex)
            {
                Trace.LogCritical(0, ex, "Unable to start Simple.");
                Dispose();
                throw;
            }
        }
Esempio n. 4
0
        internal ProtoContextBuilder(IProtoApplication <Context> application, bool allowSynchronousIO)
        {
            _application       = application ?? throw new ArgumentNullException(nameof(application));
            AllowSynchronousIO = allowSynchronousIO;
            _httpContext       = new DefaultProtoContext();

            var request = _httpContext.Request;

            request.Protocol = "HTTP/1.1";
            request.Method   = ProtoMethods.Get;

            _httpContext.Features.Set <IProtoBodyControlFeature>(this);
            _httpContext.Features.Set <IProtoResponseFeature>(_responseFeature);
            var requestLifetimeFeature = new ProtoRequestLifetimeFeature();

            requestLifetimeFeature.RequestAborted = _requestAbortedSource.Token;
            _httpContext.Features.Set <IProtoRequestLifetimeFeature>(requestLifetimeFeature);

            _responseStream       = new ResponseStream(ReturnResponseMessageAsync, AbortRequest, () => AllowSynchronousIO);
            _responseFeature.Body = _responseStream;
        }
Esempio n. 5
0
        public async Task StartAsync <TContext>(IProtoApplication <TContext> application, CancellationToken cancellationToken)
        {
            try
            {
                if (!BitConverter.IsLittleEndian)
                {
                    throw new PlatformNotSupportedException("CoreStrings.BigEndianNotSupported");
                }
                ValidateOptions();

                if (_hasStarted)
                {
                    // The server has already started and/or has not been cleaned up yet
                    throw new InvalidOperationException("CoreStrings.ClientAlreadyStarted");
                }
                _hasStarted = true;
            }
            catch (Exception ex)
            {
                Trace.LogCritical(0, ex, "Unable to start Standard.");
                Dispose();
                throw;
            }
        }
        public static IConnectionBuilder UseProtoServer <TContext>(this IConnectionBuilder builder, IList <IConnectionAdapter> adapters, ServiceContext serviceContext, IProtoApplication <TContext> application, ProtoProtocols protocols)
        {
            var middleware = new ProtoConnectionMiddleware <TContext>(adapters, serviceContext, application, protocols);

            return(builder.Use(next =>
            {
                return middleware.OnConnectionAsync;
            }));
        }
 public static IConnectionBuilder UseProtoServer <TContext>(this IConnectionBuilder builder, ServiceContext serviceContext, IProtoApplication <TContext> application, ProtoProtocols protocols)
 {
     return(builder.UseProtoServer(Array.Empty <IConnectionAdapter>(), serviceContext, application, protocols));
 }
Esempio n. 8
0
        public async Task ProcessRequestsAsync <TContext>(IProtoApplication <TContext> httpApplication)
        {
            try
            {
                AdaptedPipeline adaptedPipeline     = null;
                var             adaptedPipelineTask = Task.CompletedTask;

                // _adaptedTransport must be set prior to wiring up callbacks
                // to allow the connection to be aborted prior to protocol selection.
                _adaptedTransport = _context.Transport;

                if (_context.ConnectionAdapters.Count > 0)
                {
                    adaptedPipeline = new AdaptedPipeline(_adaptedTransport,
                                                          new Pipe(AdaptedInputPipeOptions),
                                                          new Pipe(AdaptedOutputPipeOptions),
                                                          Log);

                    _adaptedTransport = adaptedPipeline;
                }

                // This feature should never be null in Kestrel
                var connectionHeartbeatFeature = _context.ConnectionFeatures.Get <IConnectionHeartbeatFeature>();

                Debug.Assert(connectionHeartbeatFeature != null, nameof(IConnectionHeartbeatFeature) + " is missing!");

                connectionHeartbeatFeature?.OnHeartbeat(state => ((ProtoConnection)state).Tick(), this);

                var connectionLifetimeNotificationFeature = _context.ConnectionFeatures.Get <IConnectionLifetimeNotificationFeature>();

                Debug.Assert(connectionLifetimeNotificationFeature != null, nameof(IConnectionLifetimeNotificationFeature) + " is missing!");

                using (connectionLifetimeNotificationFeature?.ConnectionClosedRequested.Register(state => ((ProtoConnection)state).StopProcessingNextRequest(), this))
                {
                    // Ensure TimeoutControl._lastTimestamp is initialized before anything that could set timeouts runs.
                    _timeoutControl.Initialize(_systemClock.UtcNowTicks);

                    _context.ConnectionFeatures.Set <IConnectionTimeoutFeature>(_timeoutControl);

                    if (adaptedPipeline != null)
                    {
                        // Stream can be null here and run async will close the connection in that case
                        var stream = await ApplyConnectionAdaptersAsync();

                        adaptedPipelineTask = adaptedPipeline.RunAsync(stream);
                    }

                    IRequestProcessor requestProcessor = null;

                    lock (_protocolSelectionLock)
                    {
                        // Ensure that the connection hasn't already been stopped.
                        if (_protocolSelectionState == ProtocolSelectionState.Initializing)
                        {
                            var derivedContext = CreateDerivedContext(_adaptedTransport);

                            switch (SelectProtocol())
                            {
                            case ProtoProtocols.Proto1:
                                // _http1Connection must be initialized before adding the connection to the connection manager
                                requestProcessor        = _http1Connection = new Proto1Connection(derivedContext);
                                _protocolSelectionState = ProtocolSelectionState.Selected;
                                break;

                            case ProtoProtocols.Proto2:
                                // _http2Connection must be initialized before yielding control to the transport thread,
                                // to prevent a race condition where _http2Connection.Abort() is called just as
                                // _http2Connection is about to be initialized.
                                requestProcessor        = new Proto2Connection(derivedContext);
                                _protocolSelectionState = ProtocolSelectionState.Selected;
                                break;

                            case ProtoProtocols.None:
                                // An error was already logged in SelectProtocol(), but we should close the connection.
                                Abort(new ConnectionAbortedException(CoreStrings.ProtocolSelectionFailed));
                                break;

                            default:
                                // SelectProtocol() only returns Proto1, Proto2 or None.
                                throw new NotSupportedException($"{nameof(SelectProtocol)} returned something other than Proto1, Proto2 or None.");
                            }

                            _requestProcessor = requestProcessor;
                        }
                    }

                    _context.Transport.Input.OnWriterCompleted(
                        (_, state) => ((ProtoConnection)state).OnInputOrOutputCompleted(),
                        this);

                    _context.Transport.Output.OnReaderCompleted(
                        (_, state) => ((ProtoConnection)state).OnInputOrOutputCompleted(),
                        this);

                    if (requestProcessor != null)
                    {
                        await requestProcessor.ProcessRequestsAsync(httpApplication);
                    }

                    await adaptedPipelineTask;
                }
            }
            catch (Exception ex)
            {
                Log.LogCritical(0, ex, $"Unexpected exception in {nameof(ProtoConnection)}.{nameof(ProcessRequestsAsync)}.");
            }
            finally
            {
                DisposeAdaptedConnections();

                if (_http1Connection?.IsUpgraded == true)
                {
                    _context.ServiceContext.ConnectionManager.UpgradedConnectionCount.ReleaseOne();
                }
            }
        }
Esempio n. 9
0
 public Proto2Stream(IProtoApplication <TContext> application, Proto2StreamContext context) : base(context)
 {
     _application = application;
 }