ASP.Net Core and Brotli Compression

When looking at performance often the size of the payload will come up. In the past I have looked at different messaging formats instead of JSON such as MSGPack and Protobuff but recently instead of adding this level of complexity I looked at compression. The standard everyone has used has been GZIP and has been as far back as .Net framework 2.0. However this time I decided to explore Brotli.

Enabling Brotli in ASP.Net Core

Luckily for us ASP.Net Core has the ability to compress the payload thanks to the package Microsoft.AspNetCore.ResponseCompression so once you have added this to your project you just need to do the following in your Startup.cs

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddResponseCompression(options =>
            {
                options.EnableForHttps = true; 
                options.Providers.Add<BrotliCompressionProvider>();
            });
    }

    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        app.UseResponseCompression();
    }
}Code language: C# (cs)

By default if I now request data from my service, I will still receive it in an uncompressed format. This is good because sometimes I don’t want it compressed as the payload maybe so small that I don’t want the overhead of compressing it.

So to invoke it I need to add the following to the Request Header

"Accept-Encoding":"br"Code language: JSON / JSON with Comments (json)

This should send back a brotli compressed payload and a Response Header

"Content-Type" : "br"Code language: JSON / JSON with Comments (json)

Using HttpClient with Brotli Compression

Sometimes the need arises where your service needs to speak to another service using the HttpClient (Gateway pattern for example). If you want to leverage the Brotli Compression then we need to create our own HttpHandler.

Firstly install Brotli.NET package from nuget.

Then create a HttpHandler. This HttpHandler will add the header Accept-Encoding to request header. It will then read the response header Content-Type. If it is Brotli then it will use Brotli.NET to decompress it and hand the contents onto the next Handler.

  public class BrotliCompressionHandler : DelegatingHandler
    {
        
        protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
        {
            request.Headers.AcceptEncoding.Add(new StringWithQualityHeaderValue("br"));
        
            var response = await base.SendAsync(request, cancellationToken);
            if (response.Content.Headers.TryGetValues("Content-Encoding", out var ce) && ce.First() == "br")
            {
                var stream = new BrotliStream(await response.Content.ReadAsStreamAsync(), CompressionMode.Decompress,leaveOpen: true);
            
                response.Content = new StreamContent(stream);
            }
            return response;
        }
    }Code language: C# (cs)

We can now add this to our HttpClientFactory to enable it.

public void ConfigureServices(IServiceCollection services)
{
    services.AddHttpClient()
    .AddHttpMessageHandler<BrotliCompressionHandler>();
}Code language: HTML, XML (xml)