Simplified HttpConnection (#11488)

- Don't register callbacks until we have a request processor
- Remove the IConnectionTimeoutFeature from the feature collection, it was used by connection adapters but isn't needed anymore.
- Rename OnInputOrOutputCompleted to OnConnectionClosed
This commit is contained in:
David Fowler 2019-06-24 11:42:27 -07:00 committed by GitHub
parent 7d4258b186
commit c63cd0c3ad
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 57 additions and 78 deletions

View File

@ -3,8 +3,6 @@
using System;
using System.Diagnostics;
using System.IO.Pipelines;
using System.Net;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Connections;
using Microsoft.AspNetCore.Connections.Features;
@ -39,55 +37,42 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
_timeoutControl = new TimeoutControl(this);
}
public string ConnectionId => _context.ConnectionId;
public IPEndPoint LocalEndPoint => _context.LocalEndPoint;
public IPEndPoint RemoteEndPoint => _context.RemoteEndPoint;
private IKestrelTrace Log => _context.ServiceContext.Log;
public async Task ProcessRequestsAsync<TContext>(IHttpApplication<TContext> httpApplication)
{
try
{
// 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 => ((HttpConnection)state).Tick(), this);
var connectionLifetimeNotificationFeature = _context.ConnectionFeatures.Get<IConnectionLifetimeNotificationFeature>();
Debug.Assert(connectionLifetimeNotificationFeature != null, nameof(IConnectionLifetimeNotificationFeature) + " is missing!");
using (connectionLifetimeNotificationFeature?.ConnectionClosedRequested.Register(state => ((HttpConnection)state).StopProcessingNextRequest(), this))
{
// Ensure TimeoutControl._lastTimestamp is initialized before anything that could set timeouts runs.
_timeoutControl.Initialize(_systemClock.UtcNowTicks);
_context.ConnectionFeatures.Set<IConnectionTimeoutFeature>(_timeoutControl);
IRequestProcessor requestProcessor = null;
lock (_protocolSelectionLock)
var httpConnectionContext = new HttpConnectionContext
{
// Ensure that the connection hasn't already been stopped.
if (_protocolSelectionState == ProtocolSelectionState.Initializing)
{
var derivedContext = CreateDerivedContext(_context.Transport);
ConnectionId = _context.ConnectionId,
ConnectionFeatures = _context.ConnectionFeatures,
MemoryPool = _context.MemoryPool,
LocalEndPoint = _context.LocalEndPoint,
RemoteEndPoint = _context.RemoteEndPoint,
ServiceContext = _context.ServiceContext,
ConnectionContext = _context.ConnectionContext,
TimeoutControl = _timeoutControl,
Transport = _context.Transport
};
switch (SelectProtocol())
{
case HttpProtocols.Http1:
// _http1Connection must be initialized before adding the connection to the connection manager
requestProcessor = _http1Connection = new Http1Connection(derivedContext);
requestProcessor = _http1Connection = new Http1Connection(httpConnectionContext);
_protocolSelectionState = ProtocolSelectionState.Selected;
break;
case HttpProtocols.Http2:
// _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 Http2Connection(derivedContext);
requestProcessor = new Http2Connection(httpConnectionContext);
_protocolSelectionState = ProtocolSelectionState.Selected;
break;
case HttpProtocols.None:
@ -100,21 +85,31 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
}
_requestProcessor = requestProcessor;
}
}
var closedRegistration = _context.ConnectionContext.ConnectionClosed.Register(state => ((HttpConnection)state).OnInputOrOutputCompleted(), this);
// We don't care about callbacks once all requests are processed
using (closedRegistration)
{
if (requestProcessor != null)
{
var connectionHeartbeatFeature = _context.ConnectionFeatures.Get<IConnectionHeartbeatFeature>();
var connectionLifetimeNotificationFeature = _context.ConnectionFeatures.Get<IConnectionLifetimeNotificationFeature>();
// These features should never be null in Kestrel itself, if this middleware is ever refactored to run outside of kestrel,
// we'll need to handle these missing.
Debug.Assert(connectionHeartbeatFeature != null, nameof(IConnectionHeartbeatFeature) + " is missing!");
Debug.Assert(connectionLifetimeNotificationFeature != null, nameof(IConnectionLifetimeNotificationFeature) + " is missing!");
// Register the various callbacks once we're going to start processing requests
// The heart beat for various timeouts
connectionHeartbeatFeature?.OnHeartbeat(state => ((HttpConnection)state).Tick(), this);
// Register for graceful shutdown of the server
using var shutdownRegistration = connectionLifetimeNotificationFeature?.ConnectionClosedRequested.Register(state => ((HttpConnection)state).StopProcessingNextRequest(), this);
// Register for connection close
using var closedRegistration = _context.ConnectionContext.ConnectionClosed.Register(state => ((HttpConnection)state).OnConnectionClosed(), this);
await requestProcessor.ProcessRequestsAsync(httpApplication);
}
}
}
}
catch (Exception ex)
{
Log.LogCritical(0, ex, $"Unexpected exception in {nameof(HttpConnection)}.{nameof(ProcessRequestsAsync)}.");
@ -136,22 +131,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
_protocolSelectionState = ProtocolSelectionState.Selected;
}
private HttpConnectionContext CreateDerivedContext(IDuplexPipe transport)
{
return new HttpConnectionContext
{
ConnectionId = _context.ConnectionId,
ConnectionFeatures = _context.ConnectionFeatures,
MemoryPool = _context.MemoryPool,
LocalEndPoint = _context.LocalEndPoint,
RemoteEndPoint = _context.RemoteEndPoint,
ServiceContext = _context.ServiceContext,
ConnectionContext = _context.ConnectionContext,
TimeoutControl = _timeoutControl,
Transport = transport
};
}
private void StopProcessingNextRequest()
{
ProtocolSelectionState previousState;
@ -183,7 +162,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
}
}
private void OnInputOrOutputCompleted()
private void OnConnectionClosed()
{
ProtocolSelectionState previousState;
lock (_protocolSelectionLock)
@ -287,7 +266,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
// It's safe to use UtcNowUnsynchronized since Tick is called by the Heartbeat.
var now = _systemClock.UtcNowUnsynchronized;
_timeoutControl.Tick(now);
_requestProcessor?.Tick(now);
_requestProcessor.Tick(now);
}
public void OnTimeout(TimeoutReason reason)