Kestrel WebTransport rejects RFC 9297 clients: H3_DATAGRAM uses draft codepoint 0xffd277 instead of 0x33
Maintainers usually reply within 1 day
Nobody has claimed this yet.
Assessment
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Newbie friendliness
- 42/100
- Issue type
- Bug
- Clarity
- Mostly clear
- Activity status
- Active
- Tech stack
- csharp
- Domain
- backend-api-design
Research direction
Start with src/Servers/Kestrel/Core/src/Internal/Http3/Http3SettingType.cs, then trace how datagram settings and WebTransport request detection are handled in Http3Stream and WebTransportSession. Check the existing WebTransport follow-ups in #42788 for scope and tests. Done means RFC 9297 clients can negotiate datagrams and the accepted WebTransport version behavior is defined; the issue does not name specific tests.
Written by the indexing model from the issue text.
Description
Is there an existing issue for this?
- I have searched the existing issues
Describe the bug
Kestrel's WebTransport accepts only the pre-RFC HTTP/3 datagram setting, so standards-compliant WebTransport clients other than Chrome cannot open a session.
Http3SettingType.H3Datagram is 0xffd277, the codepoint from draft-ietf-masque-h3-datagram:
RFC 9297 (published 2022) assigns SETTINGS_H3_DATAGRAM = 0x33. A client that sends only 0x33, as RFC 9297 says, is read as having no datagram support. Http3Stream then fails the extended CONNECT:
HTTP/3 stream error (SettingsError): HTTP/3 datagrams negotiation mismatch. Client support: 'False'. Server support: 'True'.
Kestrel also advertises only 0xffd277, so RFC clients never see the server's datagram support either.
Chrome works only because it still sends both codepoints. The Rust wtransport crate (0.7.2) and other clients that follow RFC 9297 are rejected.
A related gate: a request is treated as WebTransport only when it carries sec-webtransport-http3-draft02: 1 (WebTransportSession.CurrentSupportedVersion). Later drafts of draft-ietf-webtrans-http3 removed that header in favour of SETTINGS_WT_MAX_SESSIONS and the WT-Available-Protocols / version negotiation. A client that targets a newer draft and omits the header gets IsWebTransportRequest == false, even when its settings match.
Expected Behavior
- Kestrel advertises
SETTINGS_H3_DATAGRAM = 0x33(it may keep sending0xffd277as well for older clients). - Kestrel treats a peer as datagram-capable when either
0x33or0xffd277is1. - Ideally, the
draft02version header stops being the only path toIsWebTransportRequest, or the version that's accepted is documented and configurable.
Steps To Reproduce
- Linux with
libmsquic2.6.2, .NET SDK11.0.100-rc.3.26522.148. - Run a Kestrel host:
AppContext.SetSwitch("Microsoft.AspNetCore.Server.Kestrel.Experimental.WebTransportAndH3Datagrams", true)WebApplication.CreateSlimBuilder()+builder.WebHost.UseQuic()- listen with
HttpProtocols.Http1AndHttp2AndHttp3on a fixed port with a self-signed ECDSA certificate (validity under 14 days) app.Run(async ctx => await ctx.Features.Get<IHttpWebTransportFeature>()!.AcceptAsync(ctx.RequestAborted))
- Connect with
wtransport0.7.2, pinning the certificate hash withClientConfig::builder().with_bind_default().with_server_certificate_hashes([...]), thenEndpoint::client(cfg)?.connect("https://127.0.0.1:<port>/wt"). - The client gets
ConnectionError(LocallyClosed). Kestrel trace logs show theH3_SETTINGS_ERRORabove on the CONNECT stream. - Patch the client to also send setting
0xffd277 = 1. The CONNECT now reaches the app butIsWebTransportRequestis false (the client getsSessionRejected). - Also add the header
sec-webtransport-http3-draft02: 1. The session is accepted, and bidirectional streams round-trip correctly.
So once the client imitates Chrome's legacy codepoints, the rest of Kestrel's WebTransport implementation interoperates fine. The only problems are the setting codepoint and the version header.
Exceptions (if any)
Microsoft.AspNetCore.Connections.ConnectionAbortedException: HTTP/3 stream error (SettingsError): HTTP/3 datagrams negotiation mismatch. Client support: 'False'. Server support: 'True'.
---> Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http3.Http3StreamErrorException: HTTP/3 stream error (SettingsError): HTTP/3 datagrams negotiation mismatch. Client support: 'False'. Server support: 'True'.
at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http3.Http3Stream.ProcessHeadersFrameAsync[TContext](...)
at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http3.Http3Stream.ProcessRequestAsync[TContext](...)
.NET Version
11.0.100-rc.3.26522.148
Anything else?
- Probably belongs with the WebTransport follow-ups in #42788.
- A smaller trap we hit while testing:
CreateSlimBuilder()does not register the QUIC transport. WithoutUseQuic(), anHttp1AndHttp2AndHttp3endpoint binds TCP only and logs no warning. A warning when HTTP/3 is requested but no multiplexed transport is registered would have saved us a lot of time.
- Dominant language
- C#
- Stars
- 38.5k
- Forks
- 11.6k
- Avg merge
- 3d 1h
- Merged PRs (30d)
- 289
Getting set up
Starts the project's dev container in your browser, under your own GitHub account.
- No Dockerfile or Docker Compose file
- Has a pull request template
- Read the contributing guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from dotnet/aspnetcore
-
area-networking
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
dotnet/aspnetcore#70545 · 1 comment ·
Maintainers usually reply within 1 day
-
area-infrastructure
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
dotnet/aspnetcore#70524 · 1 comment ·
Maintainers usually reply within 1 day
-
[OpenTelemetry] Hosting replaces an Activity.DisplayName that the app set, when a route matchesOpenarea-hosting Regression
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
dotnet/aspnetcore#70516 · 1 comment · 1 reaction ·
Maintainers usually reply within 1 day
-
[SSE] ServerSentEventsResult.WrapEvents is missing [EnumeratorCancellation], so RequestAborted never reaches the app's generator on the IAsyncEnumerable<T> overloadPossibly taken @bharatprajapati-coder claimed this 1 day ago. Openarea-minimal
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
dotnet/aspnetcore#70419 · 2 comments ·
Maintainers usually reply within 1 day
-
[Blazor] DefaultInMemoryCircuitPersistenceProvider never disposes the retention CancellationTokenSource when a persist is rejected at capacityPossibly taken @Vinoth2562000 claimed this 1 day ago. Openarea-blazor
Difficulty 2/5 1-3 hours Newbie friendliness 77/100
dotnet/aspnetcore#70418 · 1 comment · 1 assignee ·
Maintainers usually reply within 1 day
All issues in dotnet/aspnetcore
Similar issues
-
bug emitter:client:csharp
Difficulty 2/5 1-3 hours Newbie friendliness 84/100
microsoft/typespec#12572 · 1 comment · 1 reaction ·
Maintainers usually reply within 1 day
-
needs-area-label
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
Maintainers usually reply within 2 days
-
AWS Lambda: KeyNotFoundException drops HTTP-triggered transactions when allowAllHeaders is disabledOpenbug
Difficulty 2/5 1-3 hours Newbie friendliness 70/100
newrelic/newrelic-dotnet-agent#3949 · 1 comment ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 87/100
Maintainers usually reply within 1 day
-
priority:P2
Difficulty 2/5 1-3 hours Newbie friendliness 80/100