You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
net/http: add support for SETTINGS_ENABLE_CONNECT_PROTOCOL #53208
I propose extending the x/net/http2 and net/http packages to support the extended CONNECT protocol described in RFC 8441. Support for this will be always-on in the http server, and the http client will automatically detect the presence of support based on the settings flag and adjust the checks it applies to outgoing requests to allow the for the extended CONNECT protocol. http.Request will be extended with a new Protocol member to allow users to set the :protocol psudoheader.
Public Interface Changes
http.Request will gain a new member, Protocol of type string.
All other functionality can be achieved with existing interfaces.
Implementation
An example implementation of SETTINGS_ENABLE_CONNECT_PROTOCOL support for both the client and the server can be found on gerrit. This is just meant to be a proof of concept, not a fully production ready implementation. Rather than using a field on http.Request it uses a magic header called HACK-HTTP2-Protocol, but the intention is that this would be switched out for req.Protocol for real code.
The implementation is pretty non-invasive. It consists of extending the code to allow the use of a new :protocol psudo header, sending the new settings flag during the initial handshake, and loosening some checks when the extended CONNECT protocol is in use.
Probably the most complex thing about the implementation I was able to come up with is the fact that a CONNECT request coming from the client can now trigger a ping HTTP/2 frame if the server has not yet sent headers. This is required for an extended CONNECT to work if is the very first request on a given http2.Transport object.
Related Issues
#49918 contains some discussion about some of my initial implementation work and links to further context.
Reacted by Sam Batschelet, Armin Becher, Akshay Shah, Sven Greb, Kyle Larose, Nicolas HENRY and Ajeet D'Souza
How do we handle a request sent on an HTTP/1 connection with a Protocol field set? Return an error? Set Upgrade: $PROTOCOL and Connection: upgrade?
My inclination is to return an error since my understanding of go's philosophy is that it is a one-way-to-do-things language and you can already set the Upgrade and Connection headers in the Request.Header map. I can definitely sympathize with the alternative view that it is less surprising for Protocol = "websocket" to work regardless of the underlying transport though so I'd be happy to do it either way.
An alternative to adding a new field might be to set a :protocol key in the Request.Header map.
I like this approach because it feels a little weird for Protocol to be a top level field on such a common type when it will be so rarely used. I think I actually tried this when implementing support but ran into an error. I decided to propose a new field because psudoheaders are not really supposed to be set by the user, but I'm not so sure how good a justification this is. On reflection, maybe allowing people to set ":protocol" in the headers map by relaxing some checks that forbid user defined psudoheaders is the best way to go since it doesn't shove the feature in users face quite so much. If you're happy with that approach, I would be as well (really I don't have particularly strong feelings in any direction so I'm happy to do whatever, but this design seems good).
I don't have a strong feeling on a Protocol field vs. setting a :protocol header. Perhaps @bradfitz has an opinion.
An argument for a Protocol field might be if allows websockets implementations to be agnostic of HTTP/1 or HTTP/2 with net/http handling translation between the Protocol field and the underlying request (pseudo-)headers. I don't know if that's feasible.
We do have existing cases where you can set either a field in the Request or a header (e.g., Content-Length), so doing that for Upgrade and Connection wouldn't necessarily be horrible.
An argument for a Protocol field might be if allows websockets implementations to be agnostic of HTTP/1 or HTTP/2 with net/http handling translation between the Protocol field and the underlying request (pseudo-)headers. I don't know if that's feasible.
The websocket handshake for HTTP/1.1 and HTTP/2 are sufficiently different that attempting this is probably not a good idea. There is a bunch of Sec-* header stuff in HTTP/1.1 that isn't needed in HTTP/2 because psudoheaders are not user-settable from within a browser. The HTTP/2 handshake doesn't need to muck about with nonces for example. Since they are different enough any websocket library will need to explicitly support both protocols unless we move most of the handshake logic into the http and http2 packages themselves, which doesn't seem ideal.
We do have existing cases where you can set either a field in the Request or a header (e.g., Content-Length), so doing that for Upgrade and Connection wouldn't necessarily be horrible.
Gotcha, that is good context and weakens my one-way-to-do-things argument.
I think I'm still a little inclined towards allowing a :protocol header, but not that strongly.
Should I clean up the gerrit patch to use a :protocol pseudo-header in Request.Header and request another review, or is there another step required for the proposal to be accepted?
Activity
ianlancetaylor commentedon Jun 8, 2022
CC @neild @bradfitz
neild commentedon Jun 8, 2022
How do we handle a request sent on an HTTP/1 connection with a
Protocolfield set? Return an error? SetUpgrade: $PROTOCOLandConnection: upgrade?An alternative to adding a new field might be to set a
:protocolkey in theRequest.Headermap.ethanpailes commentedon Jun 8, 2022
My inclination is to return an error since my understanding of go's philosophy is that it is a one-way-to-do-things language and you can already set the
UpgradeandConnectionheaders in theRequest.Headermap. I can definitely sympathize with the alternative view that it is less surprising forProtocol = "websocket"to work regardless of the underlying transport though so I'd be happy to do it either way.I like this approach because it feels a little weird for Protocol to be a top level field on such a common type when it will be so rarely used. I think I actually tried this when implementing support but ran into an error. I decided to propose a new field because psudoheaders are not really supposed to be set by the user, but I'm not so sure how good a justification this is. On reflection, maybe allowing people to set
":protocol"in the headers map by relaxing some checks that forbid user defined psudoheaders is the best way to go since it doesn't shove the feature in users face quite so much. If you're happy with that approach, I would be as well (really I don't have particularly strong feelings in any direction so I'm happy to do whatever, but this design seems good).neild commentedon Jun 8, 2022
I don't have a strong feeling on a
Protocolfield vs. setting a:protocolheader. Perhaps @bradfitz has an opinion.An argument for a
Protocolfield might be if allows websockets implementations to be agnostic of HTTP/1 or HTTP/2 withnet/httphandling translation between theProtocolfield and the underlying request (pseudo-)headers. I don't know if that's feasible.We do have existing cases where you can set either a field in the
Requestor a header (e.g.,Content-Length), so doing that forUpgradeandConnectionwouldn't necessarily be horrible.ethanpailes commentedon Jun 8, 2022
The websocket handshake for HTTP/1.1 and HTTP/2 are sufficiently different that attempting this is probably not a good idea. There is a bunch of
Sec-*header stuff in HTTP/1.1 that isn't needed in HTTP/2 because psudoheaders are not user-settable from within a browser. The HTTP/2 handshake doesn't need to muck about with nonces for example. Since they are different enough any websocket library will need to explicitly support both protocols unless we move most of the handshake logic into the http and http2 packages themselves, which doesn't seem ideal.Gotcha, that is good context and weakens my one-way-to-do-things argument.
I think I'm still a little inclined towards allowing a
:protocolheader, but not that strongly.bradfitz commentedon Oct 21, 2022
I'd rather put
:protocolin Request.Header (akin to http.TrailerPrefix) over adding a newRequest.Protocol stringfield.neild commentedon Oct 21, 2022
Proposal SGTM with a
:protocolpseudo-header in Request.Header.ethanpailes commentedon Oct 21, 2022
Should I clean up the gerrit patch to use a
:protocolpseudo-header in Request.Header and request another review, or is there another step required for the proposal to be accepted?mdwn commentedon Feb 11, 2023
becheran commentedon May 26, 2023
42 remaining items
Add HTTP/1.1 support for CONNECT-UDP
Add HTTP/1.1 support for CONNECT-UDP
Fix WebSocket over HTTP/2 by disabling extended CONNECT
net/http: add HTTP2Config.EnableConnectProtocol
net/http: add HTTP2Config.EnableConnectProtocol
net/http: add HTTP2Config.EnableConnectProtocol
net/http: add HTTP2Config.EnableConnectProtocol
net/http: add HTTP2Config.EnableConnectProtocol
gopherbot commentedon May 9, 2026
Change https://go.dev/cl/776300 mentions this issue:
net/http: add HTTP2Config.EnableConnectProtocolnet/http: add HTTP2Config.EnableConnectProtocol
net/http: add HTTP2Config.EnableConnectProtocol
implement CONNECT-IP over HTTP/2
implement CONNECT-IP over HTTP/2
net/http: add HTTP2Config.EnableConnectProtocol