The previous article describes the request processing from the ASP. NET Core module to Kestrelserver. Now it's time to talk about how to generate the HttpContext we're familiar with in ASP.
When Kestrelserver is started, the corresponding IP address is bound and the Httpconnectionmiddleware is added as a terminal-connected middleware at bind time.
Public async Task startasync<tcontext> (ihttpapplication<tcontext> application, CancellationToken CancellationToken) {try {... async Task onbind (listenoptions endpoint) {//ADD the HTTP Middleware as the terminal connection middleware endpoint. Usehttpserver (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.V Alue, 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); } ...}
public static IConnectionBuilder UseHttpServer<TContext>(this IConnectionBuilder builder, IList<IConnectionAdapter> adapters, ServiceContext serviceContext, IHttpApplication<TContext> application, HttpProtocols protocols){ var middleware = new HttpConnectionMiddleware<TContext>(adapters, serviceContext, application, protocols); return builder.Use(next => { return middleware.OnConnectionAsync; });}
When the request arrives at this middleware, the Httpconnection object is created in its Onconnectionasync method and the request is processed through the object.
public async Task OnConnectionAsync(ConnectionContext connectionContext){ ... var connection = new HttpConnection(httpConnectionContext); _serviceContext.ConnectionManager.AddConnection(httpConnectionId, connection); try { var processingTask = connection.ProcessRequestsAsync(_application); ... } ...}
The
Processrequestsasync method internally creates http1connection or Http2connection objects based on the HTTP protocol, typically http1connection.
Public async Task processrequestsasync<tcontext> (ihttpapplication<tcontext> HttpApplication) {try { ... lock (_protocolselectionlock) {//Ensure that the connection hasn ' t already been stopped. if (_protocolselectionstate = = protocolselectionstate.initializing) {switch (Select Protocol ()) {case HTTPPROTOCOLS.HTTP1://_http1connection must be initialized before adding the connection to the connection Manager Requestprocessor = _http1conn ection = Createhttp1connection (_adaptedtransport, application); _protocolselectionstate = protocolselectionstate.selected; Break Case HTTPPROTOCOLS.HTTP2://_http2connection must is initialized before yielding control to the TR Ansport thread,//To prevent a race condition wheRe _http2connection.abort () is called just AS//_http2connection are about to be initialized. Requestprocessor = Createhttp2connection (_adaptedtransport, application); _protocolselectionstate = protocolselectionstate.selected; Break Case Httpprotocols.none://An error is already logged in Selectprotocol (), but we should close th E connection. Abort (Ex:null); Break Default://Selectprotocol () only returns HTTP1, HTTP2 or None. throw new NotSupportedException ($ "{nameof (Selectprotocol)} returned something other than HTTP1, HTTP2 or None."); } _requestprocessor = Requestprocessor; }} if (requestprocessor! = null) {await requestprocessor.processrequestsasync (httpapplic AtiON); } await Adaptedpipelinetask; Await _socketclosedtcs.task; } ...}
Http1connection the Processrequests method in the parent class Httpprotocol creates a context object, but this is not the HttpContext that is ultimately to be found.
private async Task ProcessRequests<TContext>(IHttpApplication<TContext> application){ // Keep-alive is default for HTTP/1.1 and HTTP/2; parsing and errors will change its value _keepAlive = true; while (_keepAlive) { ... var httpContext = application.CreateContext(this); try { KestrelEventSource.Log.RequestStart(this); // Run the application code for this request await application.ProcessRequestAsync(httpContext); if (_ioCompleted == 0) { VerifyResponseContentLength(); } } ... }}
In the Hostingapplication class you will see that HttpContext was originally generated by the Httpcontextfactory factory class.
public Context CreateContext(IFeatureCollection contextFeatures){ var context = new Context(); var httpContext = _httpContextFactory.Create(contextFeatures); _diagnostics.BeginRequest(httpContext, ref context); context.HttpContext = httpContext; return context;}
The Httpcontextfactory class is the last stop.
public HttpContext Create(IFeatureCollection featureCollection){ if (featureCollection == null) { throw new ArgumentNullException(nameof(featureCollection)); } var httpContext = new DefaultHttpContext(featureCollection); if (_httpContextAccessor != null) { _httpContextAccessor.HttpContext = httpContext; } var formFeature = new FormFeature(httpContext.Request, _formOptions); featureCollection.Set<IFormFeature>(formFeature); return httpContext;}
A simple summary of the flowchart summarizes:
The resulting HttpContext object is eventually passed to the Ihttpapplication Processrequestasync method. The next thing was the work of Httphost and HttpApplication.
So what is the use of the generated HttpContext in so much effort?
First look at the definition of it on MSDN:
Encapsulates all http-specific information on an individual HTTP request.
It can be understood that for each individual HTTP request, the HttpContext object created in the meantime encapsulates all the required HTTP information.
Then look at the properties it contains:
public abstract class HttpContext{ public abstract IFeatureCollection Features { get; } public abstract HttpRequest Request { get; } public abstract HttpResponse Response { get; } public abstract ConnectionInfo Connection { get; } public abstract WebSocketManager WebSockets { get; } public abstract AuthenticationManager Authentication { get; } public abstract ClaimsPrincipal User { get; set; } public abstract IDictionary<object, object> Items { get; set; } public abstract IServiceProvider RequestServices { get; set; } public abstract CancellationToken RequestAborted { get; set; } public abstract string TraceIdentifier { get; set; } public abstract ISession Session { get; set; } public abstract void Abort();}
Requests, responses (Response), sessions (session) These nouns, which are most commonly used in contact with HTTP, appear in the HttpContext object. Note that when processing an HTTP request, it is entirely possible to call its properties if you need to obtain the relevant information.
It is very common in various frameworks to pass a context parameter to help get the information needed during the process of each link. There is no particular innovation in the usage of the ASP, but its usefulness is beyond doubt. If you want to build your own framework, you might as well refer to the code in ASP. NET core, after all, it is a more mature product, there are many worthy of reference in the place.
. NET Core Development Log--httpcontext