1. 程式人生 > >DotNetCore深入了解之二HttpContext類

DotNetCore深入了解之二HttpContext類

ec2 default 代碼 找到 host tsa acc 技術 bind

當KestrelServer啟動時,會綁定相應的IP地址,同時在綁定時將加入HttpConnectionMiddleware作為終端連接的中間件。

技術分享圖片
 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 }
View Code 技術分享圖片
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 }
View Code

當請求抵達此中間件時,在其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 }
View Code

ProcessRequestsAsync方法內部會根據HTTP協議的不同創建Http1Connection或者Http2Connection對象,一般為Http1Connection。

技術分享圖片
 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 }
View Code

Http1Connection父類HttpProtocol裏的ProcessRequests方法會創建一個Context對象,但這還不是最終要找到的HttpContext。

技術分享圖片
 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 }
View Code

在HostingApplication類中會看到HttpContext原來是由HttpContextFactory工廠類生成的。

技術分享圖片
 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 }
View Code

HttpContextFactory類才是最後的一站。

技術分享圖片
 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 }
View Code

技術分享圖片

生成的HttpContext對象最終傳遞到IHttpApplication的ProcessRequestAsync方法。之後的事情便是WebHost與HostingApplication的工作了。

請求(Request),響應(Response),會話(Session)這些與HTTP接觸時最常見到的名詞,都出現在HttpContext對象中。說明在處理HTTP請求時,若是需要獲取這些相關信息,完全可以通過調用其屬性而得到。

通過傳遞一個上下文環境參數,以協助獲取各環節處理過程中所需的信息,在各種框架中是十分常見的作法。ASP.NET Core裏的用法並無特別的創新,但其實用性還是毋庸置疑的。如果想要構建自己的框架時,不妨多參考下ASP.NET Core裏的代碼,畢竟它已是一個較成熟的產品,其中有許多值得借鑒的地方。

DotNetCore深入了解之二HttpContext類