Skip to content
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

HTTP/1.1 connection upgraded to HTTP/2 has old cowboy_req:version/1 #1613

Open
okeuday opened this issue Jul 20, 2023 · 4 comments
Open

HTTP/1.1 connection upgraded to HTTP/2 has old cowboy_req:version/1 #1613

okeuday opened this issue Jul 20, 2023 · 4 comments

Comments

@okeuday
Copy link
Contributor

okeuday commented Jul 20, 2023

When using cowboy 2.10.0, a HTTP/1.1 connection that gets upgraded to HTTP/2 doesn't have its cowboy_req:version/1 data updated after the upgrade (i.e., 'HTTP/1.1' is returned instead of the expected 'HTTP/2'). Certain response headers are invalid for HTTP/2 use (based on curl use) like X-Content-Type-Options and X-XSS-Protection, so it is best to be aware of the current protocol used for the HTTP response headers. The version could be changed to 'HTTP/2' in cowboy_http:http2_upgrade/4 or cowboy_http2:init/12.

A way to replicate this problem is with CloudI/CloudI@f279724 using:

$ curl -v -v -v --http2 http://localhost:6464/cloudi/api/rpc/logging_status.erl
*   Trying ::1:6464...
* TCP_NODELAY set
* connect to ::1 port 6464 failed: Connection refused
*   Trying 127.0.0.1:6464...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 6464 (#0)
> GET /cloudi/api/rpc/logging_status.erl HTTP/1.1
> Host: localhost:6464
> User-Agent: curl/7.68.0
> Accept: */*
> Connection: Upgrade, HTTP2-Settings
> Upgrade: h2c
> HTTP2-Settings: AAMAAABkAARAAAAAAAIAAAAA
> 
* Mark bundle as not supporting multiuse
< HTTP/1.1 101 Switching Protocols
< connection: Upgrade
< upgrade: h2c
* Received 101
* Using HTTP2, server supports multi-use
* Connection state changed (HTTP/2 confirmed)
* Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0
* Connection state changed (MAX_CONCURRENT_STREAMS == 4294967295)!
* http2 error: Invalid HTTP header field was received: frame type: 1, stream: 1, name: [X-Content-Type-Options], value: [nosniff]
* HTTP/2 stream 0 was not closed cleanly: PROTOCOL_ERROR (err 1)
* stopped the pause stream!
* Connection #0 to host localhost left intact
curl: (92) HTTP/2 stream 0 was not closed cleanly: PROTOCOL_ERROR (err 1)

Using the curl argument --http2-prior-knowledge instead of --http2 doesn't cause this error, due to initiating the connection as HTTP/2 (no HTTP/1.1 upgrade occurs).

@essen
Copy link
Member

essen commented Jul 21, 2023

Good catch, but tough one. The request is HTTP/1.1 so I think we want to keep that around (for example for logs). Only the response is HTTP/2. Perhaps we need a token 'HTTP/1.1 -> HTTP/2' or something to properly reflect the HTTP/1.1 request HTTP/2 response.

@essen
Copy link
Member

essen commented Jul 21, 2023

As a workaround for the time being you can check the Connection/Upgrade/HTTP2-Settings header to identify that the upgrade was requested.

@okeuday
Copy link
Contributor Author

okeuday commented Jul 21, 2023

@essen I think 'HTTP/1.1 -> HTTP/2' is an idea separate from the cowboy_req:version/1 value that is closer to a state machine transition, so something that could be returned from a function that could trace the protocol's state machine as it progresses in the function call path. The reason I say that, is because if you want 'HTTP/1.1 -> HTTP/2' as a return value, you likely want a websocket related return value too and when you have both, you are really representing the protocol's state machine's execution path.

I interpret the cowboy_req:version/1 function as the request's current version and you could always have a function like cowboy_req:version_initial/1 (your naming may prefer cowboy_req:initial_version/1) to return 'HTTP/1.1' while cowboy_req:version/1 could return 'HTTP/2' after the upgrade. My view of:

GET /cloudi/api/rpc/logging_status.erl HTTP/1.1
...
Connection: Upgrade, HTTP2-Settings
Upgrade: h2c
HTTP2-Settings: AAMAAABkAARAAAAAAAIAAAAA

is that it is 'HTTP/1.1' which really isn't sure it wants 'HTTP/2' because it isn't started as a 'HTTP/2' request, until the server says its ok to be a 'HTTP/2' request based on its logic because it is able to support the newer protocol (a "'HTTP/1.1' XOR 'HTTP/2' request"). So, I still think it is better to have cowboy_req:version/1 return 'HTTP/2'.

What you decide is fine. I am not going to work-around it (don't like extra parsing of the Connection/Upgrade/HTTP2-Settings header stuff) because no one has complained about the problem yet. I will just wait for what exists in the future, unless someone complains.

@essen
Copy link
Member

essen commented Jul 21, 2023

Well the request's version is HTTP/1.1, it is only the response that is HTTP/2. The request's version is HTTP/1.1, it's not changing afterwards. So either we have a special token, or a second field identifying the protocol used in the response. A separate field likely makes more sense. But I need to think about it for a while.

The problem doesn't occur in Websocket because we don't provide the Req after the Websocket upgrade, only before when we are still HTTP/1.1.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants