public Proto2FrameWriter( PipeWriter outputPipeWriter, ConnectionContext connectionContext, Proto2Connection http2Connection, OutputFlowControl connectionOutputFlowControl, ITimeoutControl timeoutControl, MinDataRate minResponseDataRate, string connectionId, IKestrelTrace log) { _outputWriter = outputPipeWriter; _connectionContext = connectionContext; _http2Connection = http2Connection; _connectionOutputFlowControl = connectionOutputFlowControl; _connectionId = connectionId; _log = log; _timeoutControl = timeoutControl; _minResponseDataRate = minResponseDataRate; _flusher = new TimingPipeFlusher(_outputWriter, timeoutControl, log); _outgoingFrame = new Proto2Frame(); _headerEncodingBuffer = new byte[_maxFrameSize]; }
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(); } } }