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

Add SERVER_PROTOCOL to SPEC #1883

Merged
merged 1 commit into from Apr 29, 2022
Merged

Conversation

jeremyevans
Copy link
Contributor

SPEC currently does not currently specify a way to get the HTTP
version in use. However, both Chunked and CommonLogger need access
to the http version for correct functioning, and other users in
the rack ecosystem need it as well (Roda needs it, and I've just
identified a need for it in rack-test).

Unicorn, Webrick, and Puma all currently set SERVER_PROTOCOL.
However, Puma currently sets SERVER_PROTOCOL statically to
HTTP/1.1, unlike Unicorn and Webrick, which set it to the
protocol used by the client. Unicorn and Puma set HTTP_VERSION
to the protocol used by the client.

This specifies that SERVER_PROTOCOL should match the protocol
used by the client, that it should be a valid protocol matching
HTTP/\d(.\d)?, and that if HTTP_VERSION is provided, it must
match SERVER_PROTOCOL. This will require minor changes to Puma
to be compliant with the new SPEC.

Set SERVER_PROTOCOL to HTTP/1.1 by default in Rack::MockRequest,
allowing it to be set by the :http_version option. Update
CommonLogger specs to include the version.

This removes a spec in Chunked for usage without SERVER_PROTOCOL.
A comment in the removed lines indicate unicorn will not set
SERVER_PROTOCOL for HTTP/0.9 requests, but that is incorrect, as
unicorn has set SERVER_PROTOCOL to HTTP/0.9 since 2009 (see unicorn
commit bd0599c4ac91d95cae1f34df3ae99c92f3225391). The related
comment was correct when added in 2009 (rack commit
895beec), but has been incorrect
since the code was changed from HTTP_VERSION to SERVER_PROTOCOL in
2015 (rack commit e702d31).

SPEC currently does not currently specify a way to get the HTTP
version in use.  However, both Chunked and CommonLogger need access
to the http version for correct functioning, and other users in
the rack ecosystem need it as well (Roda needs it, and I've just
identified a need for it in rack-test).

Unicorn, Webrick, and Puma all currently set SERVER_PROTOCOL.
However, Puma currently sets SERVER_PROTOCOL statically to
HTTP/1.1, unlike Unicorn and Webrick, which set it to the
protocol used by the client.  Unicorn and Puma set HTTP_VERSION
to the protocol used by the client.

This specifies that SERVER_PROTOCOL should match the protocol
used by the client, that it should be a valid protocol matching
HTTP/\d(\.\d)?, and that if HTTP_VERSION is provided, it must
match SERVER_PROTOCOL.  This will require minor changes to Puma
to be compliant with the new SPEC.

Set SERVER_PROTOCOL to HTTP/1.1 by default in Rack::MockRequest,
allowing it to be set by the :http_version option. Update
CommonLogger specs to include the version.

This removes a spec in Chunked for usage without SERVER_PROTOCOL.
A comment in the removed lines indicate unicorn will not set
SERVER_PROTOCOL for HTTP/0.9 requests, but that is incorrect, as
unicorn has set SERVER_PROTOCOL to HTTP/0.9 since 2009 (see unicorn
commit bd0599c4ac91d95cae1f34df3ae99c92f3225391).  The related
comment was correct when added in 2009 (rack commit
895beec), but has been incorrect
since the code was changed from HTTP_VERSION to SERVER_PROTOCOL in
2015 (rack commit e702d31).
@ioquatix ioquatix enabled auto-merge (squash) April 29, 2022 23:00
Copy link
Member

@ioquatix ioquatix left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great work.

@ioquatix ioquatix merged commit caed808 into rack:main Apr 29, 2022
@MSP-Greg
Copy link
Contributor

MSP-Greg commented May 5, 2022

@jeremyevans

This specifies that SERVER_PROTOCOL should match the protocol
used by the client, that it should be a valid protocol matching
HTTP/\d(.\d)?, and that if HTTP_VERSION is provided, it must
match SERVER_PROTOCOL. This will require minor changes to Puma
to be compliant with the new SPEC.

Question about 'if HTTP_VERSION is provided, it must match SERVER_PROTOCOL'

Not sure about how to handle an edge case here. If the client sets a VERSION header (or has more than one), HTTP_VERSION cannot may not match SERVER_PROTOCOL?

Also, see puma/puma#2870

@ioquatix
Copy link
Member

ioquatix commented May 5, 2022

That's actually a good question, since at least in theory HTTP_VERSION is user provided.

@MSP-Greg
Copy link
Contributor

MSP-Greg commented May 5, 2022

I edited my message, see the Puma issue. When that was posted, I looked around for mention of a VERSION/HTTP_VERSION header, and found essentially nothing...

in theory HTTP_VERSION is user provided

I might say 'may be' rather than 'is', which is what's causing the whole issue...

@jeremyevans
Copy link
Contributor Author

I posted comments regarding SERVER_PROTOCOL/HTTP_VERSION in the Puma issue: puma/puma#2870 (comment)

@MSP-Greg
Copy link
Contributor

MSP-Greg commented May 5, 2022

@jeremyevans

Thank you for the response. In your summary of some current Ruby app servers, you don't mention how they handle a client provided VERSION header, which is the issue here.

Possibly, the rack spec could be changed to something similar to 'if HTTP_VERSION is provided, its first value must match SERVER_PROTOCOL'?

EDIT: mentioned in Puma comment -

Possibly we can drop the HTTP_VERSION requirement in Rack 4

I think dropping any HTTP_VERSION requirements (ignoring it) in Rack4 is a good idea.

@jeremyevans
Copy link
Contributor Author

Thank you for the response. In your summary of some current Ruby app servers, you don't mention how they handle a client provided VERSION header, which is the issue here.

Rack app:

[200, {}, [env.values_at('SERVER_PROTOCOL', 'HTTP_VERSION').inspect+"\n"]]

HTTP/1.0 requests without version header (curl -0):

  • Unicorn: ["HTTP/1.0", "HTTP/1.0"]
  • Puma: ["HTTP/1.1", "HTTP/1.0"]
  • Webrick: ["HTTP/1.1", "HTTP/1.1"]

HTTP/1.0 requests with version header (curl -0 -H "version:foo"):

  • Unicorn: ["HTTP/1.0", "HTTP/1.0"]
  • Puma: ["HTTP/1.1", "HTTP/1.0, foo"]
  • Webrick: ["HTTP/1.1", "foo"]

Possibly, the rack spec could be changed to something similar to 'if HTTP_VERSION is provided, its first value must match SERVER_PROTOCOL'?

I think the problem with that is that many applications already rely on HTTP_VERSION as the version of HTTP in use. Older versions of Rack used HTTP_VERSION, if you wanted to differentiate between HTTP/1.0 and HTTP/1.1 when using Puma (the most popular Ruby web server), you have to use HTTP_VERSION.

If we are going to change the rack 3 SPEC regarding this, we should specify that HTTP_VERSION must be the value of the version header, or nil if no version header is given. I think the first value must match SERVER_PROTOCOL is the wrong way to go, as it is the worst of both worlds. For one, I think most code that checks HTTP_VERSION is using == 'HTTP/1.0' or similar (https://github.com/rails/rails/blob/75a9e1be75769ae633a938d81d51e06852a69ea3/actionpack/lib/action_controller/metal/streaming.rb#L201, which uses HTTP_VERSION, dynamically defined near https://github.com/rails/rails/blob/main/actionpack/lib/action_dispatch/http/request.rb#L43). Either we try to get backwards compatibility for existing applications, by specifying HTTP_VERSION should be the same as SERVER_PROTOCOL if given, or we drop it completely and treat HTTP_VERSION just like any HTTP_* entry.

@MSP-Greg
Copy link
Contributor

MSP-Greg commented May 5, 2022

I think most code that checks HTTP_VERSION

Thank you for the data, and I had yet to look at Rails, thanks for that info. From the perspective of current compatibility, I think Unicorn seems to provide the best implementation, even though it ignores a client's value for VERSION.

Puma is easily patched to fix this, not sure about whether it would be considered a breaking change. Pinging @nateberkopec

userlea referenced this pull request May 8, 2022
…test-sprint

Add rack-session gem when testing rack-attack
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

Successfully merging this pull request may close these issues.

None yet

3 participants