dotnet / dotnet/aspnetcore

`Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpResponseHeaders` does not validate headers the same way as `Microsoft.AspNetCore.Http.HeaderDictionary` does

Open
#56,543 0 comments 2 reactions 0 assignees View on GitHub
area-networking
Dominant language
C#
Stars
38.4k
Forks
10.9k
Avg merge
2d 5h
Merged PRs (30d)
276

Description

### Is there an existing issue for this?

- [X] I have searched the existing issues

### Describe the bug

If you assign an invalid value to Kestrel's HttpResponseHeaders, it will throw `InvalidOperationException`. If do you the same with the default `HeaderDictionary` it will pass and you can work with the header just fine.

Why exactly it this is an issue? In our unit tests we initialize `DefaultHttpContext` and pass it to our controllers so we can unittest them without initializing the entire http pipeline.

### Expected Behavior

I expect both header dictionaries to validate headers the same way, so my unit tests behaves as close to production as possible.

### Steps To Reproduce

https://github.com/zlepper/HttpHeaderEncodingIssue

If you run the web application and hit `http://localhost:5291/api/my` then you will get a 500 with the error `System.InvalidOperationException: Invalid non-ASCII or control character in header: 0x000D`.

If you run the unit tests they will pass.

The way i got the invalid header was by doing something like:
```

var cd = new ContentDisposition()
{
FileName = @"jhff-90°_asdfgh-qwer-qwer_zxcvb asdfgh.jpg"
};

Response.Headers.ContentDisposition = cd.ToString();

```
And yes, i know now that i should use `ContentDispositionHeaderValue` instead, that is not with this ticket is about.

### Exceptions (if any)

```
System.InvalidOperationException: Invalid non-ASCII or control character in header: 0x000D
at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpHeaders.ThrowInvalidHeaderCharacter(Char ch)
at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpHeaders.ValidateHeaderValueCharacters(String headerCharacters, Boolean requireAscii)
at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpHeaders.ValidateHeaderValueCharacters(String headerName, StringValues headerValues, Func`2 encodingSelector)
at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpResponseHeaders.Microsoft.AspNetCore.Http.IHeaderDictionary.set_ContentDisposition(StringValues value)
at HttpHeaderEncodingIssue.MyController.Get() in /home/rasmus/projects/HttpHeaderEncodingIssue/HttpHeaderEncodingIssue/MyController.cs:line 17
at lambda_method2(Closure, Object, Object[])
at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.SyncActionResultExecutor.Execute(ActionContext actionContext, IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.g__Logged|12_1(ControllerActionInvoker invoker)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.g__Awaited|10_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Rethrow(ActionExecutedContextSealed context)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.InvokeInnerFilterAsync()
--- End of stack trace from previous location ---
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.g__Awaited|20_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.g__Logged|17_1(ResourceInvoker invoker)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.g__Logged|17_1(ResourceInvoker invoker)
at Swashbuckle.AspNetCore.SwaggerUI.SwaggerUIMiddleware.Invoke(HttpContext httpContext)
at Swashbuckle.AspNetCore.Swagger.SwaggerMiddleware.Invoke(HttpContext httpContext, ISwaggerProvider swaggerProvider)
at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddlewareImpl.Invoke(HttpContext context)

HEADERS
=======
Accept: */*
Connection: keep-alive
Host: localhost:5291
User-Agent: PostmanRuntime/7.39.0
Accept-Encoding: gzip, deflate, br
Postman-Token: 628d6b3a-585a-488e-86e4-48f3bfb20c90

```

### .NET Version

8.0.106

### Anything else?

ASP.NET core version: 8.0.6 (I think)

IDE:
```
JetBrains Rider 2024.1.3
Build #RD-241.17011.166, built on June 7, 2024
Licensed to
Subscription is active until September 19, 2024.
Runtime version: 21.0.3+35-nixos amd64
VM: OpenJDK 64-Bit Server VM by JetBrains s.r.o.
Linux 6.6.34
.NET Core v7.0.20 x64
GC: G1 Young Generation, G1 Concurrent GC, G1 Old Generation
Memory: 16000M
Cores: 64
Registry:
ide.experimental.ui=true
eslint.additional.file.extensions=svelte
ide.new.project.model.index.case.sensitivity=true
Non-Bundled Plugins:
com.intellij.resharper.HeapAllocationsViewer (2024.1.0)
verify-rider (2024.1.2)
org.jetbrains.plugins.go-template (241.14494.150)
com.intellij.mermaid (0.0.22+IJ.232)
dev.blachut.svelte.lang (241.17011.2)
com.github.copilot (1.5.10.5840)
nix-idea (0.4.0.14)
Current Desktop: KDE

```

.NET SDK:
Version: 8.0.106
Commit: ab2d260803
Workload version: 8.0.100-manifests.98636946

Runtime Environment:
OS Name: nixos
OS Version: 24.05
OS Platform: Linux
RID: linux-x64
Base Path: /nix/store/dd73a86fnls6qw9ikvyg0bh5dkyaa9vr-dotnet-sdk-8.0.106/sdk/8.0.106/

.NET workloads installed:
Workload version: 8.0.100-manifests.98636946
There are no installed workloads to display.

Host:
Version: 8.0.6
Architecture: x64
Commit: 3b8b000a0e

.NET SDKs installed:
7.0.410 [/nix/store/887hfg4l8zmlh53gga44lpm8y9wgnys4-dotnet-core-combined/sdk]
8.0.106 [/nix/store/887hfg4l8zmlh53gga44lpm8y9wgnys4-dotnet-core-combined/sdk]

.NET runtimes installed:
Microsoft.AspNetCore.App 7.0.20 [/nix/store/887hfg4l8zmlh53gga44lpm8y9wgnys4-dotnet-core-combined/shared/Microsoft.AspNetCore.App]
Microsoft.AspNetCore.App 8.0.6 [/nix/store/887hfg4l8zmlh53gga44lpm8y9wgnys4-dotnet-core-combined/shared/Microsoft.AspNetCore.App]
Microsoft.NETCore.App 7.0.20 [/nix/store/887hfg4l8zmlh53gga44lpm8y9wgnys4-dotnet-core-combined/shared/Microsoft.NETCore.App]
Microsoft.NETCore.App 8.0.6 [/nix/store/887hfg4l8zmlh53gga44lpm8y9wgnys4-dotnet-core-combined/shared/Microsoft.NETCore.App]

Other architectures found:
None

Environment variables:
Not set

global.json file:
Not found

Learn more:
https://aka.ms/dotnet/info

Download .NET:
https://aka.ms/dotnet/download

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.