An Inside Look at ASP.NET 5 Execution – Part V – Request processing

On the previous post I covered the application bootstrap on ASP.NET Hosting, including the Startup class and middleware configuration. The last major task on the hosting layer was to start the server using the IServerFactory located during the bootstrap. Its Start method is defined as follows:

IDisposable Start(
    IServerInformation serverInformation,
    Func<IFeatureCollection, Task> application
);

I’d like to focus on the second parameter, which is function that represents the application, i.e. a callback that handles incoming requests. This function gets an IFeatureCollection and returns a Task that completes when the request processing is done. So what is this feature collection?

After the server is started, it invokes the application function each time a new request comes in. The request surfaces the application as a set of feature interfaces, which are fine-grained representations of the capabilities exposed by the server for the current request. There are feature interfaces to represent the HTTP connection, the request, the response, and so on. For instance, the interface for the HTTP response is defined as follows:

public interface IHttpResponseFeature
{
    int StatusCode { get; set; }
    string ReasonPhrase { get; set; }
    IDictionary<string, string[]> Headers { get; set; }
    Stream Body { get; set; }
    bool HeadersSent { get; }
    void OnSendingHeaders(Action callback, object state);
    void OnResponseCompleted(Action callback, object state);
}

If you recall from the previous post, the RequestDelegate gets an HttpContext and returns a Task. It is the hosting layer’s responsibility to wrap the low-level feature interfaces exposed by the server in a higher-level, strongly-typed HttpContext. This becomes clear if we take a look at the callback supplied to the server by the hosting layer:

RequestDelegate application = // ...
var contextFactory = _applicationServices.GetRequiredService();
var contextAccessor = _applicationServices.GetRequiredService();
var server = ServerFactory.Start(_serverInstance,
  async features =>
  {
      var httpContext = contextFactory.CreateHttpContext(features);
      // ...
      contextAccessor.HttpContext = httpContext;
      await application(httpContext);
  });

The HttpContext itself is an abstraction for applications to build on. The IHttpContextFactory implementation creates instances of DefaultHttpContext, which in turn uses DefaultHttpRequest and DefaultHttpResponse. These classes contain the logic of mapping feature interfaces into the high-level APIs. For example, the User property of DefaultHttpContext actually uses an underlying authentication feature:

public override ClaimsPrincipal User
{
    get
    {
        var user = this.HttpAuthenticationFeature.User;
        if (user == null)
        {
            user = new ClaimsPrincipal(new ClaimsIdentity());
            this.HttpAuthenticationFeature.User = user;
        }
        return user;
    }
    set { this.HttpAuthenticationFeature.User = value; }
}

The two sets of abstractions – feature interfaces and HTTP abstractions – allow applications to be hosted on different servers and different servers to be used without being tied to one specific hosting model, as illustrated below.

Untitled3

As an example, here’s the set of feature interfaces that reach the hosting layer in my hello world example. You can see that all the implementations are on the WebListener namespace and that there is no ITlsConnectionFeature, for example.

Untitled

Finally, when the request delegate is invoked, we’re immediately executing middlewares’ code. There’s literally nothing else on the way of the request! Pretty slim, right?

Untitled2

And that’s it for this series. I covered the execution of a self-hosted ASP.NET application, from the runtime native hosts up to the ASP.NET hosting layer and request processing. The framework code seems well organized and reduced to the essential. One can tell it was written from scratch 🙂 The one thing that seems a bit worse is how they (ab)used the service locator (despite mostly limited to the internal code) and how the conforming container (IServiceProvider interface) surfaces to the application developer.

I’ll probably dive into some details of MVC 6 on a future series. Stay tuned!


					
Advertisements

One thought on “An Inside Look at ASP.NET 5 Execution – Part V – Request processing

  1. Pingback: ASP.NET Core hosting (revisited) – Part II | Luís Gonçalves

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s