-
Notifications
You must be signed in to change notification settings - Fork 5.3k
Accept any 2xx status code for CONNECT responses per RFC 9110/9112 #122408
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
…onses - Updated HttpConnection.cs to use IsSuccessStatusCode for CONNECT responses - Updated HttpConnectionPool.cs to accept any 2xx status code for proxy tunnel - Updated Http2Stream.cs to accept 2xx status codes for Extended CONNECT - Added theory tests to verify multiple 2xx status codes work correctly Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com>
|
Tagging subscribers to this area: @dotnet/ncl |
Fixed bug where 204 NoContent was checked before CONNECT, preventing CONNECT with 204 status from working properly. Moved CONNECT check before NoContent check. Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com>
src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnection.cs
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR fixes RFC 9110/9112 compliance by accepting any 2xx status code for CONNECT tunnel establishment, not just 200 OK. The previous implementation incorrectly rejected valid 201/202/204 responses.
Key changes:
- Updated HTTP/1.1 CONNECT handling to use
IsSuccessStatusCodeand reordered conditions to properly handle 204 NoContent - Updated proxy tunnel establishment to accept any 2xx response
- Updated HTTP/2 Extended CONNECT to accept status codes in the 200-299 range
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| HttpConnection.cs | Uses IsSuccessStatusCode instead of exact == HttpStatusCode.OK check; reorders conditions to check CONNECT before NoContent to correctly handle 204 responses |
| HttpConnectionPool.cs | Changes proxy tunnel establishment to accept IsSuccessStatusCode instead of checking for exact 200 status |
| Http2Stream.cs | Updates Extended CONNECT to accept statusCode >= 200 && statusCode <= 299 instead of exact 200 check |
| HttpClientHandlerTest.Connect.cs | Converts test from [Fact] to [Theory] with inline data testing 200/201/202/204 status codes for HTTP/1.1 CONNECT |
| SocketsHttpHandlerTest.Http2ExtendedConnect.cs | Adds new theory test validating 200/201/202/204 status codes for HTTP/2 Extended CONNECT |
wfurt
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
MihaZupan
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was thinking about whether supporting 204 is more or less correct here given that in https://www.rfc-editor.org/rfc/rfc9112.html#name-message-body-length it notes that the rules are ordered, with CONNECT coming after 204.
The length of a message body is determined by one of the following (in order of precedence):
- Any response to a HEAD request and any response with a 1xx (Informational), 204 (No Content), or 304 (Not Modified) status code is always terminated by the first empty line after the header fields, regardless of the header fields present in the message, and thus cannot contain a message body or trailer section.
- Any 2xx (Successful) response to a CONNECT request implies that the connection will become a tunnel immediately after the empty line that concludes the header fields. A client MUST ignore any Content-Length or Transfer-Encoding header fields received in such a message.
On the other hand, curl made a similar change where it allows any 2xx response, with an explicit test for 204. curl/curl#1859
The scenarios where it'd be problematic seem unrealistic enough that it likely doesn't matter though (e.g. reverse proxy that also proxied CONNECT requests, and a backend that accepted CONNECT requests and responded with a 204 but interpreted it as rejecting the tunnel).
Per RFC 9110 §9.3.6 and RFC 9112 §6.3, any 2xx status code indicates successful CONNECT tunnel establishment, not just 200 OK. The current implementation rejects valid 201/202/204 responses.
Changes:
IsSuccessStatusCodeinstead of exact== HttpStatusCode.OKcheck; reorder conditions to check CONNECT before NoContent to fix 204 handlingIsSuccessStatusCodefor proxy tunnel establishmentstatusCode >= 200 && statusCode <= 299for Extended CONNECTBefore:
After:
Tests: Added theory tests validating 200/201/202/204 status codes for both HTTP/1.1 CONNECT and HTTP/2 Extended CONNECT.
Warning
Firewall rules blocked me from connecting to one or more addresses (expand for details)
I tried to connect to the following addresses, but was blocked by firewall rules:
badhost/home/REDACTED/work/runtime/runtime/artifacts/bin/testhost/net11.0-linux-Debug-x64/dotnet /home/REDACTED/work/runtime/runtime/artifacts/bin/testhost/net11.0-linux-Debug-x64/dotnet exec --runtimeconfig /home/REDACTED/work/runtime/runtime/artifacts/bin/System.Net.Http.Functional.Tests/Debug/net11.0-linux/System.Net.Http.Functional.Tests.runtimeconfig.json --depsfile /home/REDACTED/work/runtime/runtime/artifacts/bin/System.Net.Http.Functional.Tests/Debug/net11.0-linux/System.Net.Http.Functional.Tests.deps.json /home/REDACTED/work/runtime/runtime/artifacts/bin/System.Net.Http.Functional.Tests/Debug/net11.0-linux/testhost.dll --port 40175 --endpoint 127.0.0.1:040175 --role client --parentprocessid 42021 --datacollectionport 38705 --telemetryoptedin false grep(dns block)does.not.exist.sorry/home/REDACTED/work/runtime/runtime/artifacts/bin/testhost/net11.0-linux-Debug-x64/dotnet /home/REDACTED/work/runtime/runtime/artifacts/bin/testhost/net11.0-linux-Debug-x64/dotnet exec --runtimeconfig /home/REDACTED/work/runtime/runtime/artifacts/bin/System.Net.Http.Functional.Tests/Debug/net11.0-linux/System.Net.Http.Functional.Tests.runtimeconfig.json --depsfile /home/REDACTED/work/runtime/runtime/artifacts/bin/System.Net.Http.Functional.Tests/Debug/net11.0-linux/System.Net.Http.Functional.Tests.deps.json /home/REDACTED/work/runtime/runtime/artifacts/bin/System.Net.Http.Functional.Tests/Debug/net11.0-linux/Microsoft.DotNet.RemoteExecutor.dll System.Net.Http.Functional.Tests, Version=11.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51 System.Net.Http.Functional.Tests.DiagnosticsTest <SendAsync_ConnectionFailure_RecordsActivitiesWithCorrectErrorInfo>g__RunTest|18_0 /tmp/4nnrhrc0.0rq 2.0 True dns sts/MultiInterfaceStreamContent.cs k/runtime/runtime/.dotnet/grep unnel.*proxy stParserTest.cs niStressTest.cs grep(dns block)/home/REDACTED/work/runtime/runtime/artifacts/bin/testhost/net11.0-linux-Debug-x64/dotnet /home/REDACTED/work/runtime/runtime/artifacts/bin/testhost/net11.0-linux-Debug-x64/dotnet exec --runtimeconfig /home/REDACTED/work/runtime/runtime/artifacts/bin/System.Net.Http.Functional.Tests/Debug/net11.0-linux/System.Net.Http.Functional.Tests.runtimeconfig.json --depsfile /home/REDACTED/work/runtime/runtime/artifacts/bin/System.Net.Http.Functional.Tests/Debug/net11.0-linux/System.Net.Http.Functional.Tests.deps.json /home/REDACTED/work/runtime/runtime/artifacts/bin/System.Net.Http.Functional.Tests/Debug/net11.0-linux/Microsoft.DotNet.RemoteExecutor.dll System.Net.Http.Functional.Tests, Version=11.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51 System.Net.Http.Functional.Tests.DiagnosticsTest <SendAsync_ConnectionFailure_RecordsActivitiesWithCorrectErrorInfo>g__RunTest|18_0 /tmp/50n1p3nb.fhg 1.1 False dns sts/HttpClientHa-d /usr/bin/grep unnel.*proxy ngeParserTest.csdiff sts/MultiInterfa--name-only grep(dns block)/home/REDACTED/work/runtime/runtime/artifacts/bin/testhost/net11.0-linux-Debug-x64/dotnet /home/REDACTED/work/runtime/runtime/artifacts/bin/testhost/net11.0-linux-Debug-x64/dotnet exec --runtimeconfig /home/REDACTED/work/runtime/runtime/artifacts/bin/System.Net.Http.Functional.Tests/Debug/net11.0-linux/System.Net.Http.Functional.Tests.runtimeconfig.json --depsfile /home/REDACTED/work/runtime/runtime/artifacts/bin/System.Net.Http.Functional.Tests/Debug/net11.0-linux/System.Net.Http.Functional.Tests.deps.json /home/REDACTED/work/runtime/runtime/artifacts/bin/System.Net.Http.Functional.Tests/Debug/net11.0-linux/Microsoft.DotNet.RemoteExecutor.dll System.Net.Http.Functional.Tests, Version=11.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51 System.Net.Http.Functional.Tests.DiagnosticsTest <SendAsync_ConnectionFailure_RecordsActivitiesWithCorrectErrorInfo>g__RunTest|18_0 /tmp/aiatuzah.jy5 1.1 True dns sts/HttpClientHa-d /home/REDACTED/wor168.63.129.16 unnel.*proxy ngeParserTest.csdiff sts/MultiInterfa--name-only grep(dns block)nosuchhost.invalid/home/REDACTED/work/runtime/runtime/artifacts/bin/testhost/net11.0-linux-Debug-x64/dotnet /home/REDACTED/work/runtime/runtime/artifacts/bin/testhost/net11.0-linux-Debug-x64/dotnet exec --runtimeconfig /home/REDACTED/work/runtime/runtime/artifacts/bin/System.Net.Http.Functional.Tests/Debug/net11.0-linux/System.Net.Http.Functional.Tests.runtimeconfig.json --depsfile /home/REDACTED/work/runtime/runtime/artifacts/bin/System.Net.Http.Functional.Tests/Debug/net11.0-linux/System.Net.Http.Functional.Tests.deps.json /home/REDACTED/work/runtime/runtime/artifacts/bin/System.Net.Http.Functional.Tests/Debug/net11.0-linux/testhost.dll --port 41445 --endpoint 127.0.0.1:041445 --role client --parentprocessid 38979 --datacollectionport 33849 --telemetryoptedin false grep(dns block)/home/REDACTED/work/runtime/runtime/artifacts/bin/testhost/net11.0-linux-Debug-x64/dotnet /home/REDACTED/work/runtime/runtime/artifacts/bin/testhost/net11.0-linux-Debug-x64/dotnet exec --runtimeconfig /home/REDACTED/work/runtime/runtime/artifacts/bin/System.Net.Http.Functional.Tests/Debug/net11.0-linux/System.Net.Http.Functional.Tests.runtimeconfig.json --depsfile /home/REDACTED/work/runtime/runtime/artifacts/bin/System.Net.Http.Functional.Tests/Debug/net11.0-linux/System.Net.Http.Functional.Tests.deps.json /home/REDACTED/work/runtime/runtime/artifacts/bin/System.Net.Http.Functional.Tests/Debug/net11.0-linux/testhost.dll --port 40175 --endpoint 127.0.0.1:040175 --role client --parentprocessid 42021 --datacollectionport 38705 --telemetryoptedin false grep(dns block)/home/REDACTED/work/runtime/runtime/artifacts/bin/testhost/net11.0-linux-Debug-x64/dotnet /home/REDACTED/work/runtime/runtime/artifacts/bin/testhost/net11.0-linux-Debug-x64/dotnet exec --runtimeconfig /home/REDACTED/work/runtime/runtime/artifacts/bin/System.Net.Http.Functional.Tests/Debug/net11.0-linux/System.Net.Http.Functional.Tests.runtimeconfig.json --depsfile /home/REDACTED/work/runtime/runtime/artifacts/bin/System.Net.Http.Functional.Tests/Debug/net11.0-linux/System.Net.Http.Functional.Tests.deps.json /home/REDACTED/work/runtime/runtime/artifacts/bin/System.Net.Http.Functional.Tests/Debug/net11.0-linux/Microsoft.DotNet.RemoteExecutor.dll System.Net.Http.Functional.Tests, Version=11.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51 System.Net.Http.Functional.Tests.DiagnosticsTest+<>c <SendAsync_ExpectedDiagnosticExceptionLogging>b__9_0 /tmp/fflb0jko.0nv 1.1 True |:protocol sts/FunctionalTe/low:false /usr/bin/grep unnel.*proxy derValueTest.cs sts/HttpClientHa--exclude-standard grep(dns block)www.some.example/home/REDACTED/work/runtime/runtime/artifacts/bin/testhost/net11.0-linux-Debug-x64/dotnet /home/REDACTED/work/runtime/runtime/artifacts/bin/testhost/net11.0-linux-Debug-x64/dotnet exec --runtimeconfig /home/REDACTED/work/runtime/runtime/artifacts/bin/System.Net.Http.Functional.Tests/Debug/net11.0-linux/System.Net.Http.Functional.Tests.runtimeconfig.json --depsfile /home/REDACTED/work/runtime/runtime/artifacts/bin/System.Net.Http.Functional.Tests/Debug/net11.0-linux/System.Net.Http.Functional.Tests.deps.json /home/REDACTED/work/runtime/runtime/artifacts/bin/System.Net.Http.Functional.Tests/Debug/net11.0-linux/testhost.dll --port 40175 --endpoint 127.0.0.1:040175 --role client --parentprocessid 42021 --datacollectionport 38705 --telemetryoptedin false grep(dns block)If you need me to access, download, or install something from one of these locations, you can either:
Original prompt
💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.