Search code examples
asp.net-core

How to handle extra header which was failed to read in ASP.NET Core?


I'm working on our ASP.NET MVC project and trying to convert it to ASP.NET Core MVC project by creating a new project targeting .NET 6 side by side. It's almost done until we see 500 was returned to specific requests from our customers. And the original ASP.NET MVC project works well to handle them. And we cannot tell which requests are different on the original site.

Both the original and new ASP.NET Core project are deployed on IIS on Windows Server 2022 Datacenter.

I've added app.UseExceptionHandler() and app.Use(async (context, next) =>{}) code to the beginning of the Configure(WebApplication app) method and still cannot catch the exception.

What I can see is the IIS log is this:

#Fields: date time s-ip cs-uri-stem cs-uri-query c-ip sc-status sc-bytes time-taken tag1
2024-02-07 04:05:16 192.168.1.2 /home/CheckThreadPool - 192.168.0.1 200 252 1 tag1
2024-02-07 04:08:06 192.168.1.2 /api/search - 192.168.0.1 500 145 47 -

And my Configure method looks like this:

public static void Configure(WebApplication app)
{
    app.UseExceptionHandler(errorApp =>
    {
        errorApp.Run(async context =>
        {
            context.Response.Headers["tag1"] = "tag1";
            
            var error = context.Features.Get<IExceptionHandlerFeature>();
            if (error != null)
            {
                var ex = error.Error;
                await context.Response.WriteAsync(ex.Message, Encoding.UTF8);
            }
        });
    });

    app.Use(async (context, next) =>
    {
        try
        {
            context.Response.Headers["tag1"] = "tag1";
            await next.Invoke();
        }
        catch (Exception ex)
        {
        }
    });
    // other code
}

At the same time, I got the error in the Event Viewer.

Category: Microsoft.AspNetCore.Server.IIS.Core.IISHttpServer EventId: 2

Connection ID "18158513698363146269", Request ID "4000001e-0000-fc00-b63f-84710c7967bb": An unhandled exception was thrown by the application.

Exception: System.InvalidOperationException: Operation is not valid due to the current state of the object.

at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure.StringUtilities.GetAsciiOrUTF8StringNonNullCharacters(ReadOnlySpan1 span, Encoding defaultEncoding) at Microsoft.AspNetCore.HttpSys.Internal.HeaderEncoding.GetString(Byte* pBytes, Int32 byteCount, Boolean useLatin1) at Microsoft.AspNetCore.HttpSys.Internal.NativeRequestContext.GetUnknownHeadersHelper(IDictionary2 unknownHeaders, Int64 fixup, HTTP_REQUEST* request)
at Microsoft.AspNetCore.HttpSys.Internal.NativeRequestContext.GetUnknownHeaders(IDictionary2 unknownHeaders) at Microsoft.AspNetCore.HttpSys.Internal.RequestHeaders.get_Extra() at Microsoft.AspNetCore.HttpSys.Internal.RequestHeaders.get_Item(String key) at Microsoft.AspNetCore.Hosting.HostingApplicationDiagnostics.<>c.<StartActivity>b__21_0(Object carrier, String fieldName, String& fieldValue, IEnumerable1& fieldValues) at System.Diagnostics.LegacyPropagator.ExtractTraceIdAndState(Object carrier, PropagatorGetterCallback getter, String& traceId, String& traceState) at Microsoft.AspNetCore.Hosting.HostingApplicationDiagnostics.StartActivity(HttpContext httpContext, Boolean loggingEnabled, Boolean diagnosticListenerActivityCreationEnabled, Boolean& hasDiagnosticListener) at Microsoft.AspNetCore.Hosting.HostingApplication.CreateContext(IFeatureCollection contextFeatures) at Microsoft.AspNetCore.Server.IIS.Core.IISHttpContextOfT`1.ProcessRequestAsync()

From the source code we can see that the header contains null or was failed to be convert to string by UTF8 encoding.

public static unsafe string GetAsciiOrUTF8StringNonNullCharacters(this ReadOnlySpan<byte> span, Encoding defaultEncoding)
{
    if (span.IsEmpty)
    {
        return string.Empty;
    }

    fixed (byte* source = &MemoryMarshal.GetReference(span))
    {
        var resultString = string.Create(span.Length, (IntPtr)source, s_getAsciiOrUTF8StringNonNullCharacters);

        // If resultString is marked, perform UTF-8 encoding
        if (resultString[0] == '\0')
        {
            // null characters are considered invalid
            if (span.IndexOf((byte)0) != -1)
            {
                throw new InvalidOperationException();
            }

            try
            {
                resultString = defaultEncoding.GetString(span);
            }
            catch (DecoderFallbackException)
            {
                throw new InvalidOperationException();
            }
        }

        return resultString;
    }
}

Now I'd like to know if there is anything I can do to handle such request by ignoring extra headers or else?

Update: Thanks Jason for the help to analyze and that help to narrow down the problem. Finally we found that it is cause by a module we added long ago and it only works for .Net Framework. We delete the module and the problem is fixed. I will pick Jason's answer since it points to the destination.


Solution

  • The error message is obvious, the problem is happening in HttpSys, so the UseExceptionHandler can't catch this error.

    And we should enable the Failed Request Tracing Rules to capture the information about the HTTP.sys.

    By the way, if you know how to reproduce the issue, then we can capture the http request information in browser.