當KestrelServer啓動時,會綁定相應的IP地址,同時在綁定時將加入HttpConnectionMiddleware做爲終端鏈接的中間件。app
1 public async Task StartAsync<TContext>(IHttpApplication<TContext> application, CancellationToken cancellationToken) 2 { 3 try 4 { 5 ... 6 7 async Task OnBind(ListenOptions endpoint) 8 { 9 // Add the HTTP middleware as the terminal connection middleware 10 endpoint.UseHttpServer(endpoint.ConnectionAdapters, ServiceContext, application, endpoint.Protocols); 11 12 var connectionDelegate = endpoint.Build(); 13 14 // Add the connection limit middleware 15 if (Options.Limits.MaxConcurrentConnections.HasValue) 16 { 17 connectionDelegate = new ConnectionLimitMiddleware(connectionDelegate, Options.Limits.MaxConcurrentConnections.Value, Trace).OnConnectionAsync; 18 } 19 20 var connectionDispatcher = new ConnectionDispatcher(ServiceContext, connectionDelegate); 21 var transport = _transportFactory.Create(endpoint, connectionDispatcher); 22 _transports.Add(transport); 23 24 await transport.BindAsync().ConfigureAwait(false); 25 } 26 27 await AddressBinder.BindAsync(_serverAddresses, Options, Trace, OnBind).ConfigureAwait(false); 28 } 29 30 ... 31 }
1 public static IConnectionBuilder UseHttpServer<TContext>(this IConnectionBuilder builder, IList<IConnectionAdapter> adapters, ServiceContext serviceContext, IHttpApplication<TContext> application, HttpProtocols protocols) 2 { 3 var middleware = new HttpConnectionMiddleware<TContext>(adapters, serviceContext, application, protocols); 4 return builder.Use(next => 5 { 6 return middleware.OnConnectionAsync; 7 }); 8 }
當請求抵達此中間件時,在其OnConnectionAsync方法裏會建立HttpConnection對象,並經過該對象處理請求框架
1 public async Task OnConnectionAsync(ConnectionContext connectionContext) 2 { 3 ... 4 5 var connection = new HttpConnection(httpConnectionContext); 6 _serviceContext.ConnectionManager.AddConnection(httpConnectionId, connection); 7 8 try 9 { 10 var processingTask = connection.ProcessRequestsAsync(_application); 11 12 ... 13 } 14 ... 15 }
ProcessRequestsAsync方法內部會根據HTTP協議的不一樣建立Http1Connection或者Http2Connection對象,通常爲Http1Connection。socket
1 public async Task ProcessRequestsAsync<TContext>(IHttpApplication<TContext> httpApplication) 2 { 3 try 4 { 5 ... 6 7 lock (_protocolSelectionLock) 8 { 9 // Ensure that the connection hasn't already been stopped. 10 if (_protocolSelectionState == ProtocolSelectionState.Initializing) 11 { 12 switch (SelectProtocol()) 13 { 14 case HttpProtocols.Http1: 15 // _http1Connection must be initialized before adding the connection to the connection manager 16 requestProcessor = _http1Connection = CreateHttp1Connection(_adaptedTransport, application); 17 _protocolSelectionState = ProtocolSelectionState.Selected; 18 break; 19 case HttpProtocols.Http2: 20 // _http2Connection must be initialized before yielding control to the transport thread, 21 // to prevent a race condition where _http2Connection.Abort() is called just as 22 // _http2Connection is about to be initialized. 23 requestProcessor = CreateHttp2Connection(_adaptedTransport, application); 24 _protocolSelectionState = ProtocolSelectionState.Selected; 25 break; 26 case HttpProtocols.None: 27 // An error was already logged in SelectProtocol(), but we should close the connection. 28 Abort(ex: null); 29 break; 30 default: 31 // SelectProtocol() only returns Http1, Http2 or None. 32 throw new NotSupportedException($"{nameof(SelectProtocol)} returned something other than Http1, Http2 or None."); 33 } 34 35 _requestProcessor = requestProcessor; 36 } 37 } 38 39 if (requestProcessor != null) 40 { 41 await requestProcessor.ProcessRequestsAsync(httpApplication); 42 } 43 44 await adaptedPipelineTask; 45 await _socketClosedTcs.Task; 46 } 47 ... 48 }
Http1Connection父類HttpProtocol裏的ProcessRequests方法會建立一個Context對象,但這還不是最終要找到的HttpContext。async
1 private async Task ProcessRequests<TContext>(IHttpApplication<TContext> application) 2 { 3 // Keep-alive is default for HTTP/1.1 and HTTP/2; parsing and errors will change its value 4 _keepAlive = true; 5 6 while (_keepAlive) 7 { 8 ... 9 10 var httpContext = application.CreateContext(this); 11 12 try 13 { 14 KestrelEventSource.Log.RequestStart(this); 15 16 // Run the application code for this request 17 await application.ProcessRequestAsync(httpContext); 18 19 if (_ioCompleted == 0) 20 { 21 VerifyResponseContentLength(); 22 } 23 } 24 ... 25 } 26 }
在HostingApplication類中會看到HttpContext原來是由HttpContextFactory工廠類生成的。ide
1 public Context CreateContext(IFeatureCollection contextFeatures) 2 { 3 var context = new Context(); 4 var httpContext = _httpContextFactory.Create(contextFeatures); 5 6 _diagnostics.BeginRequest(httpContext, ref context); 7 8 context.HttpContext = httpContext; 9 return context; 10 }
HttpContextFactory類纔是最後的一站。ui
1 public HttpContext Create(IFeatureCollection featureCollection) 2 { 3 if (featureCollection == null) 4 { 5 throw new ArgumentNullException(nameof(featureCollection)); 6 } 7 8 var httpContext = new DefaultHttpContext(featureCollection); 9 if (_httpContextAccessor != null) 10 { 11 _httpContextAccessor.HttpContext = httpContext; 12 } 13 14 var formFeature = new FormFeature(httpContext.Request, _formOptions); 15 featureCollection.Set<IFormFeature>(formFeature); 16 17 return httpContext; 18 }
生成的HttpContext對象最終傳遞到IHttpApplication的ProcessRequestAsync方法。以後的事情即是WebHost與HostingApplication的工做了。this
請求(Request),響應(Response),會話(Session)這些與HTTP接觸時最多見到的名詞,都出如今HttpContext對象中。說明在處理HTTP請求時,如果須要獲取這些相關信息,徹底能夠經過調用其屬性而獲得。spa
經過傳遞一個上下文環境參數,以協助獲取各環節處理過程當中所需的信息,在各類框架中是十分常見的做法。ASP.NET Core裏的用法並沒有特別的創新,但其實用性仍是毋庸置疑的。若是想要構建本身的框架時,不妨多參考下ASP.NET Core裏的代碼,畢竟它已經是一個較成熟的產品,其中有許多值得借鑑的地方。3d