Asp.net core - Log request/response headers

Recently we deployed a new application to our clients production environment. Their hosting partner uses a API gateway in front of the application that allows them to rate limit calls, load balance the application and stuff like that. We were told that the gateway would be "100% non intrusive" and "just work". Yeah right.

We noticed right away that our application simply did not work in the production environment. Everything worked perfectly in our own test environments but as soon as the application was deployed to production, it stopped working...
One thing I noticed was that NO headers at all were forwarded to our application...well that sucks.

So I told the hosting company "Hey, please forward all headers to and from our application and everything will start working right away".

Hah. They responded with "Sorry we can't allow all headers, you need to explicitly specify ALL the headers that you want to passthrough, so please send us a list of all the headers asap"

facepalm

Anyhow, our application is an asp.net core application, we are using the authorization middleware and also a couple of other middlewares, my point is, I don't KNOW from the top of my head which headers our application use/needs, sure, I can make an educated guess and probably come close, but I will most certainly forget one or two.

Time to start coding. Since we are using asp.net core I created a simple middleware that logs all unique request/response headers that I can turn on/off in appsettings.

Actually, I created two middlewares, one responsible for saving the unique headers and one that could display the headers.
Here's the first one responsible for the "logging" part, it just saves the headers in two lists, one for the request headers and one for the response headers.

public class LogHeadersMiddleware  
{
    private readonly RequestDelegate next;
    public static readonly List<string> RequestHeaders = new List<string>();
    public static readonly List<string> ResponseHeaders = new List<string>();

    public LogHeadersMiddleware(RequestDelegate next)
    {
        this.next = next;
    }

    public async Task Invoke(HttpContext context)
    {
        var uniqueRequestHeaders = context.Request.Headers
            .Where(x => RequestHeaders.All(r => r != x.Key))
            .Select(x => x.Key);
        RequestHeaders.AddRange(uniqueRequestHeaders);

        await this.next.Invoke(context);
        var uniqueResponseHeaders = context.Response.Headers
            .Where(x => ResponseHeaders.All(r => r != x.Key))
            .Select(x => x.Key);
        ResponseHeaders.AddRange(uniqueResponseHeaders);
    }
}

Since I made the lists public I can then access them in my other middleware responsible for displaying the headers.

In my startup.cs file I register the logging middleware like this, I also map the url /show-headers to the ShowHeadersMiddleware

public void Configure(  
            IApplicationBuilder app, 
            IHostingEnvironment env, 
            ILoggerFactory loggerFactory,
            IOptions<ApplicationSettings> applicationOptions) 
{
    ...
    // Appsetting that toggles the logheaders middleware.
    if(applicationOptions.Value.LogHeaders)
    {
        // Extension method that enables the logging middleware
        app.UseLogHeadersMiddleware();
        app.Map("/show-headers", ShowHeaders);
    }
    ...
    app.UseMvc();
}

private static void ShowHeaders(IApplicationBuilder app)  
{
    app.Run(async context => {
        var requestHeaders = string.Join("<br/>", LogHeadersMiddleware.RequestHeaders.OrderBy(x => x));
        var responseHeaders = string.Join("<br />", LogHeadersMiddleware.ResponseHeaders.OrderBy(x => x));
        var output = $"<h2>Unique request headers</h2>{requestHeaders} <h2>Unique response headers</h2> {responseHeaders}";
        context.Response.ContentType = "text/html";
        await context.Response.WriteAsync(output);
    });
}

I then enabled the logging in our test environment and had our QA-people browse around in one of our other applications that uses the API so I could get a list of all the unique headers. I then sent the list to the hosting provider and sure enough...it started working right away.

Firefox adds charset=UTF-8 when doing POST requests with ajax.

One of our customers reported some issues yesterday, all of a sudden they could not upload images in our custom built admin. They sent along a screenshot and I could see that our API responded with HTTP status code 415. Our API follows the JSON API standard and the specification says

Clients MUST send all JSON API data in request documents with the header Content-Type: application/vnd.api+json without any media type parameters.

So, if someone makes a request to our API with an incorrect Content-Type header, we return 415 with a nice error message.

I checked the logs and this is what I found:

{
    "errors": [{
        "status": "415",
        "title": "Unsupported Media Type",
        "detail": "You must use the 'application/vnd.api+json' Content-Type in your request. You had the following Content-Type: 'application/vnd.api+json; charset=UTF-8'"
    }]
}

Well, that's weird because I know that we explicitly set the Content-Type header to be application/vnd.api+json without any charset information.

Time to start digging.

After some googling I found the following bug report(it's over 10 years old!).
I did some more reading and it seems like Mozilla fixed the bug in version 44 of Firefox.

Version 44 came out in the beginning of year 2016...and I know that our customer is a big organization and they are always late on updating all of their stuff...so I asked our client and it turns out that they are running...Firefox 39. I rest my case.

So, the solution is to update Firefox to get rid of this annoying bug.