ASP.NET Core hosting (revisited) – Part II

Last year I did a series of posts following ASP.NET Core execution in depth. At that time the framework was still named ASP.NET 5, I was using betas 5/6 and DNX (.NET Execution Environment) was still around. A lot has changed since then, namely the usage of .NET CLI instead of DNX. Today I’ll continue my previous post on hosting and somehow revisit last year’s post about ASP.NET request processing.

The last post ended up right before the WebHost is initialized. This is where ASP.NET configures the application services and middlewares in order to build the final RequestDelegate. Finally, when started, the host fires up the registered server (IServer), linking the incoming requests to the RequestDelegate. Lets go through these steps.

The initialization of WebHost is where the Startup class plays its role. Internally, the startup logic is represented by the IStartup interface, depicted below:

public interface IStartup
{
    IServiceProvider ConfigureServices(IServiceCollection services);
    void Configure(IApplicationBuilder app);
}

This interface mimics the two application startup actions: 1) configure services on DI and 2) configure the middlewares. On the Main method, we invoked UseStartup<T>; if you take a look at the method’s source code you’ll see that it actually registers an implementation of IStartup that resolves the two actions by convention, by looking up methods on the supplied startup class.

Anyway, the first actions on the WebHost initialization are all about gathering the required services and configuring the application services (the following lines are a bit scattered on the source; I condensed the most relevant parts here):

_startup = _hostingServiceProvider.GetRequiredService<IStartup>();
_applicationServices = _startup.ConfigureServices(_applicationServiceCollection);
Server = _applicationServices.GetRequiredService<IServer>();
var builderFactory = _applicationServices.GetRequiredService<IApplicationBuilderFactory>();
IApplicationBuilder builder = builderFactory.CreateBuilder(Server.Features);

Notice how the host gets required services that were previously configured by the host builder and our code on the Main method.

At this point the application services are configured on the DI infrastructure and the host has an application builder ready to start configuring middlewares. There are three main players on that stage:

  • IApplicationBuilder – used to register middlewares. The interface is very simplistic:
    public interface IApplicationBuilder
    {
        // ...
        RequestDelegate Build();
        IApplicationBuilder Use(Func<RequestDelegate, RequestDelegate> middleware);
    }
  • The Startup.Configure method – where the application configures its middlewares. To that end, it gets an IApplicationBuilder as an argument, as shown below:
    public class Startup
    {
        public void Configure(IApplicationBuilder app) { ... }
    }
  • Startup filters – classes that implement IStartupFilter, allowing configurations to be done on the IApplicationBuilder before the Startup.Configure method is invoked. The interface is as follows:
    public interface IStartupFilter
    {
        Action<IApplicationBuilder> Configure(Action<IApplicationBuilder> next);
    }

The only usage of these filters I found on ASP.NET source is to register a middleware that handles the per-request lifetime on the DI infrastructure. It’s important to ensure that this middleware executes before application middlwares so that the latter can use per-request services.

The construction of the aplication RequestDelegate starts exactly by applying startup filters. The bootstrap Action<IApplicationBuilder> is the Startup.Configure method and filters successively wrap around the previous action. Note that filters are applied in reverse order of registration, so that the first to be registered is actually the outermost wrapper (the first to execute).

var startupFilters = _applicationServices.GetService<IEnumerable<IStartupFilter>>();
Action<IApplicationBuilder> configure = _startup.Configure;
foreach (var filter in startupFilters.Reverse())
{
    configure = filter.Configure(configure);
}

Having this composition of middleware configuration actions, it’s time to actually execute them on the IApplicationBuilder and then build the RequestDelegate.

configure(builder);
return builder.Build();

After the configure delegate is executed, the application builder contains and ordered list of middlewares, as shown below. For this example I have two middlewares on my Startup class; the other is the aforementioned middleware that handles per-request DI.

middlewares

Recall that a middleware is a function that composes a RequestDelegate around another (the next one in the chain). Therefore, the final RequestDelegate must be built from the last middleware to the first, as illustrated below. This ensures that the first middleware to be registered is actually the outermost wrapper (the first to execute).

Untitled.png

The application builder’s Build method does exactly that: reverse the middlewares list and invoke them one by one, passing the current RequestDelegate. To bootstrap the configuration, it uses a default RequestDelegate that returns an HTTP 404 status code, ensuring a default response when no other middleware handles the request.

public RequestDelegate Build()
{
    RequestDelegate app = context =>
    {
        context.Response.StatusCode = 404;
        return Task.FromResult(0);
    };
    foreach (var component in _components.Reverse())
    {
        app = component(app);
    }
    return app;
}

At this point the WebHost is initialized and we have a RequestDelegate that represents the whole application. When the host is started (its Run method is invoked on the console application) it’s time to bring the bring the server implementation in and have it deliver requests to the RequestDelegate. To that end, one last component is used: IHttpApplication.

public interface IHttpApplication<TContext>
{
    TContext CreateContext(IFeatureCollection contextFeatures);
    Task ProcessRequestAsync(TContext context);
    void DisposeContext(TContext context, Exception exception);
}

This interface is the glue between the server and our application. This is visible on the IServer interface:

public interface IServer : IDisposable
{
    void Start<TContext>(IHttpApplication<TContext> application);
    // ...
}

Just by analyzing these two interfaces we can imagine a skeleton of what’s happening under the covers, probably with some actions in between:

  1. When the server is started, it is given an IHttpApplication.
  2. When a new request arrives on the server, it asks the application  to create a context for the request.
  3. The context is supplied to the application for request processing. This means that the context must contain everything the application needs to handle the corresponding request.
  4. After the request is done, the server asks the application to dispose of the previously created context.

So, what’s that IFeatureCollection thing on CreateContext? Features 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. You can take a look at all the features on the HttpAbstractions repository.

If you recall, 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 is done via the built-in implementation of IHttpApplication:

private readonly RequestDelegate _application;
private readonly IHttpContextFactory _httpContextFactory;

public Context CreateContext(IFeatureCollection contextFeatures)
{
    var httpContext = _httpContextFactory.Create(contextFeatures);
    // ...
    return new Context
    {
        HttpContext = httpContext,
        // ...
    };
}

public Task ProcessRequestAsync(Context context)
{
    return _application(context.HttpContext);
}

Going back a bit, when we start a WebHost, it actually starts the underlying server passing in the IHttpApplication and everything is ready for request processing. When a request comes in and the server invokes IHttpApplication.ProcessRequestAsync, the HttpContext is passed into the application RequestDelegate and we’re immediately executing middlewares’ code. There’s literally nothing else on the way of the request! Pretty slim, right?

execution

As a final note – and in case you’re wondering – it’s worth mentioning that 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.

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.

abstractions.PNG

And that’s it for this series. I revisited the execution of an ASP.NET Core application, starting from configuration via WebHostBuilder up to the moment where a request is handled to the application RequestDelegate. Hope this helps!

Advertisements

ASP.NET Core hosting (revisited) – Part I

Last year I did a series of posts following ASP.NET Core execution in depth. At that time the framework was still named ASP.NET 5, I was using betas 5/6 and DNX (.NET Execution Environment) was still around. A lot has changed since then, namely the usage of .NET CLI instead of DNX. Today I’ll revisit last year’s post about ASP.NET hosting.

Prior to RC2, an ASP.NET Core application was a class library. DNX – the execution environment at that time – was responsible for providing support for project.json, loading assemblies and invoking the ASP.NET hosting libraries, which in turn would find a Startup class to configure and start the application. Just for completeness, we used to have something like this on the commands section of project.json:

"web": "Microsoft.AspNet.Hosting --server Microsoft.AspNet.Server.WebListener --server.urls http://localhost:5000"

With the release of RC2, DNX and the related tools were replaced by the .NET CLI and, as such, ASP.NET Core applications became regular console applications. Instead of using the hosting libraries as the entry point and have them discover our application, we now call into ASP.NET libraries from our Main method (the common entry point) to configure the web application and fire up ASP.NET. Here’s an example of such a Main method:

public static void Main(string[] args)
        {
            var host = new WebHostBuilder()
                .UseKestrel()
                .UseStartup<Startup>()
                .Build();
            host.Run();
        }

The code is straightforward: we configure the server (Kestrel, in this case) and the Startup class (this now has to be done explicitly) to build the web host. Then we run it, blocking the main thread until the application shuts down. For the remainder of this post (and the following) I’ll go through the ASP.NET code to see what’s happening under the covers when we configure and run the web host.

Before moving further it’s worth doing a quick recap on some terminology:

  • RequestDelegate – represents the handling of a request. It’s a function that gets a context and returns a Task that completes when the request has been processed. This simple delegate actually represents the whole application.
    public delegate Task RequestDelegate(HttpContext context);
  • Middleware – a component that participates on request processing by inspecting, routing, or modifying request and/or response messages. A simplistic view of a middleware is a Func<RequestDelegate, RequestDelegate>, i.e. a means of composing around a given request delegate. This composition approach replaces the event-driven model found on previous versions of ASP.NET.
  • Startup class – the class where you configure the services and middlewares of your application. The configuration methods are discovered by convention, as described on ASP.NET documentation. You can also check this post for a more detailed explanation.

The interface of WebHostBuilder is rather simple, as illustrated below. Configuring the builder is mostly about configuring services for the DI infrastructure.

    public interface IWebHostBuilder
    {
        IWebHost Build();
        IWebHostBuilder ConfigureServices(Action<IServiceCollection> configureServices);
        IWebHostBuilder UseSetting(string key, string value);
        // ...
    }
    public class WebHostBuilder : IWebHostBuilder 
    {
        private readonly List<Action<IServiceCollection>> _configureServicesDelegates;

        public IWebHostBuilder ConfigureServices(Action<IServiceCollection> configureServices)
        {
          _configureServicesDelegates.Add(configureServices);
          return this;
        }
        // ...
    }

Most of the builder configurations done on the Main method are exposed via extension methods on the corresponding packages. For instance, the UseKestrel method is found on the Kestrel server package and looks like this:

public static IWebHostBuilder UseKestrel(this IWebHostBuilder hostBuilder)
{
    return hostBuilder.ConfigureServices(services =>
    {
        services.AddTransient<IConfigureOptions<KestrelServerOptions>, KestrelServerOptionsSetup>();
        services.AddSingleton<IServer, KestrelServer>();
    });
}

Notice the registration of IServer. This is (as expected) a required service that represents the server and will be used later on to link incoming requests to the application. If we were to use another server implementation, it would likely register its own IServer class via a similar extension method.

The WebHostBuilder.Build method is where everything is assembled. It has two main responsibilities:

  1. Configure and register the services needed for hosting.
  2. Create and initialize the WebHost.
public IWebHost Build()
{
    var hostingServices = BuildHostingServices();
    var hostingContainer = hostingServices.BuildServiceProvider();
    var host = new WebHost(hostingServices, hostingContainer, _options, _config);
    host.Initialize();
    return host;
}

The BuildHostingServices method is where hosting-related configuration takes place. First, the IHostingEnvironment is configured. This service can be used from this moment on to get information about the physical environment where the application is hosted, such as the root path for static content.

_options = new WebHostOptions(_config);

var appEnvironment = PlatformServices.Default.Application;
var contentRootPath = ResolveContentRootPath(_options.ContentRootPath, appEnvironment.ApplicationBasePath);
var applicationName = _options.ApplicationName ?? appEnvironment.ApplicationName;

_hostingEnvironment.Initialize(applicationName, contentRootPath, _options);

var services = new ServiceCollection(); // (1)
services.AddSingleton(_hostingEnvironment);

Note the creation of the service collection (1) that is used to create the base DI service provider for the WebHost (on the Build method shown before).

The next relevant action is the registration of some important built-in services that will be used for application configuration and request processing. We’ll go through the usage of these services later on.

services.AddLogging();
services.AddTransient<IApplicationBuilderFactory, ApplicationBuilderFactory>();
services.AddTransient<IHttpContextFactory, HttpContextFactory>();
services.AddOptions();
  • IApplicationBuilderFactory – allows create of the application builder that is used by the Startup class to configure middlewares.
  • IHttpContextFactory – create the HttpContext for an incoming request.
  • AddOptions – adds support for the options configuration pattern.

The last step is to execute the service configuration delegates that were externally registered in the WebHostBuilder. This is the case of the delegates registered by UseKestrel and UseStartup methods, as shown before.

private readonly List<Action<IServiceCollection>> _configureServicesDelegates;
// ...
foreach (var configureServices in _configureServicesDelegates)
{
    configureServices(services);
}

At this point all the base configurations and services required by the host are in place. The Build method can now create a WebHost instance and initialize it. This where all the application-specific configurations are triggered and it will be the subject of my next post. Stay tuned!

OAuth 2.0 library for .NET native apps

A few months ago I wrote about OAuth 2.0 for native apps, mentioning App Auth, an OAuth 2.0 client library for native Android and iOS applications. Recently I discovered that a similar library has been release for .NET! It is a PCL (Xamarin included) and it features the same security concerns as App Auth, following the IETF recommendations for native OAuth 2.0 clients. In addition, it also supports OpenId Connect features, such as validating identity  tokens. Very nice!

OAuth 2.0 mind map

Since the publication of The OAuth 2.0 Authorization Framework (RFC 6749) in 2012, both the IETF OAuth work group and the OpenID Foundation have released many other RFCs that extend/complement the base framework and the OAuth 2.0 ecosystem. I found this variety a bit confusing, so I figured I’d do a mind map and short summary of the specs for future reference. Here it goes.

oauth-mindmap

RFC 6749 – The OAuth 2.0 Authorization Framework

This is the core OAuth 2.0 standard. It defines the different roles (resource owner, client, authorization server, etc.) and the main authorization endpoints, flows and artifacts. Extensibility rules are also defined in this document. The “framework” on the RFC title is important: there are multiple authorization scenarios and a lot of aspects are left unspecified, which somehow justifies all the satellite specs. This was also the reason for heavy criticism on the early days of OAuth 2.0.

Tokens

RFC 6750 – Bearer Token Usage

The main spec doesn’t define the format of access tokens nor how they should be used to access protected resources. The most commonly deployed tokens, however, are bearer tokens. As the name implies, the possession of such tokens is sufficient to access the associated resources. RFC 6750 defines how bearer tokens should be sent to resource servers.

RFC 7009 – Token Revocation

Authorization servers usually allow resource owners to revoke previously granted authorizations. This RFC proposes an additional endpoint for authorization servers that clients can use to invalidate tokens they no longer need.

RFC 7662 – Token Introspection

The main spec doesn’t cover the actual usage of access tokens nor the procedure that resource servers should apply to verify them. More than verifying if a token is valid, resource servers are likely to need the associated metadata (e.g. the granted scopes) for authorization decisions. In a common deployment scenario, both authorization and resource servers are under the same domain, hence being able to somehow share the token validation logic. For more loose scenarios, RFC 7662 defines a token introspection endpoint that resource servers may use to query the authorization server to obtain the status and metadata of a token that is presented by an OAuth 2.0 client.

RFC 7519 – JSON Web Token (JWT)

JSON Web Token (JWT) is a compact, URL-safe means of representing claims to be transferred between two parties. The claims in a JWT are encoded as a JSON object.” JWTs can be used with JSON Web Signature (JWS) and JSON Web Encryption (JWE), enabling the claims to be integrity protected and/or encrypted. The spec includes a set of predefined claims – such as the issuer, subject and audience identifiers – as well as processing rules. JWTs can be used as self-contained access/refresh tokens.

More on obtaining authorization grants

RFC 7636 – Proof Key for Code Exchange by OAuth Public Clients (PKCE)

Clients that can’t keep their credentials private are called public clients. This is the case of most native applications. As such, the authorization code grant cannot be securely used by this type of clients. This spec defines a proof of possession extension that makes the code grant usable by these clients. Read this post for more details on PKCE and the underlying vulnerability.

Device Flow (draft)

Defines an authorization flow for clients executing on devices with limited input and/or browsing capabilities (e.g. TVs, STBs). It is based on out-of-band codes that the user provides to the authorization server using another device with better user-agent capabilities (e.g. a tablet).

Multiple Response Type Encoding Practices

Defines rules to encode authorization response parameters when multiple response types are used. Also defines response mode, a new authorization request parameter that allows the client to specify how the response should be delivered (query-string, fragment, etc.).

Form Post Response Mode

Based on the previous spec, this document defines a new response mode on which the authorization response is returned to the client via a auto-submit HTML form using an HTTP POST. This is different from the redirects (HTTP GET) defined on the core OAuth spec, with the advantage of reducing the likelihood of codes and tokens being logged as part of URLs.

Management

RFC 7591 – Dynamic Client Registration Protocol

Clients are usually registered by their developers in the intended authorization servers. This spec defines a means of dynamically registering clients Registration requests include items such as redirect URIs, intended grant types and web page URLs, and the response contains the assigned client ID and secret. There’s also a complementary experimental spec for subsequent changes to the client metadata.

Authorization Server Discovery Metadata (draft)

This draft defines the format of a JSON document that a client can use to obtain the different endpoints and capabilities of an authorization server. This metadata includes items such as the URL of the token endpoint and the supported response types. Besides this draft there’s also the OpenID Connect version, which is final and actually appeared first. The draft is compatible with the later.

Other types of authorization grants

RFC 7521 – Assertion Framework for Client Authentication and Authorization Grants

Defines mechanisms for OAuth 2.0 to work with other identity systems using assertions. The spec defines processing rules and message flows for using assertion-based client authentication and authorization grants when interacting with the token endpoint. This means that authorization grants are obtained by other means, instead of using the common OAuth flows. There’s a profile using SAML 2.0 assertions and another using JWT assertions.

Informational & best practices

RFC 6819 – Threat Model and Security Considerations

Defines a set of threats to the authorization flows and the corresponding countermeasures. Since OAuth 2.0 doesn’t include any “message security” on the base flows, being aware of those security aspects is very important to ensure the protocols’ security.

OAuth 2.0 for Native Apps (draft)

Native clients – such as mobile applications – can’t keep their credentials private. This spec defines a set of best practices for implementing such clients, namely the usage of the device’s native browser for authorization flows. I’ve discussed this subject in detail on a previous post.

Authentication

OpenID Connect Core 1.0

OpenID Connect 1.0 is a simple identity layer on top of the OAuth 2.0 protocol“. Actually it’s not that simple; it’s a whole new subject, but this post wouldn’t be complete without mentioning it. The spec defines extensions to OAuth 2.0 that enable clients to verify the identity of the user that authenticated on the authorization server.

Cake build example

Recently I’ve been experimenting with Cake for .NET builds. It is a build automation tool with a C# DSL, which per se is pretty nice! Besides that, I found the following nice aspects:

  • Expressive and comprehensive API which supports most of the common tasks on this type of builds.
  • Lots of add-ins for additional tasks.
  • Cross-platform.
  • Seems easy to extend.

I’ve put together an example project using Cake, which includes compilation via MSBuild, StyleCop analysis, test execution, creation of a NuGet package and publication of a web application to the file system,

Google’s Site Reliability Engineering

DevOps & Co

Here’s How Google Makes Sure It (Almost) Never Goes Down