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

RACK_URL_SCHEME calculation caring about more headers #1491

Merged
merged 7 commits into from
Jul 17, 2019

Conversation

olleolleolle
Copy link
Contributor

@olleolleolle olleolleolle commented Dec 21, 2017

This PR is about #1179, as a way of pushing the discussion onwards.

  • add new constants to const.rb and use them
  • RACK_URL_SCHEME is now read from a series of HTTP headers (just like Rack does it)

@nateberkopec
Copy link
Member

This definitely looks like the right approach, just needs the things you already pointed out to get it over the line.

@evanphx
Copy link
Member

evanphx commented Feb 20, 2019

Can this be merged? Is anything waiting on it?

@olleolleolle
Copy link
Contributor Author

@evanphx I apologize, this had been sitting - I'll turn these TODO items into a checkbox list and then do them.

When visible and less obtuse, review becomes much easier.

@olleolleolle
Copy link
Contributor Author

olleolleolle commented Jul 9, 2019

In order to get this code mergeable, I offer it as-is to review. After re-reading this change, I think it's ready.

I had one question to my earlier self, who was borrowing that code.

Q: Why am I splitting X-Forwarded-Proto on first comma?

Would anyone add more content to that header? Are there any examples?

A: Example from the code author

The answer is: "yes there are examples!"

rack/rack@85ca454

Documentation about the X-Forwarded-Proto header, in general

None of these mention any of the practical examples of "multiple proxies after each other", but they can be nice sources of learning about the header, in general.

Mozilla MDN explains the X-Forwarded-Proto

Cloudflare also appends an X-Forwarded-Proto header, which can either be HTTP or HTTPS depending on the protocol the user used to visit the site, like this:

"X-Forwarded-Proto: https"

Cloudflare support docs on the header

Example: Cloudflare explains X-Forwarded-Proto

The X-Forwarded-Proto request header helps you identify the protocol (HTTP or HTTPS) that a client used to connect to your load balancer. Your server access logs contain only the protocol used between the server and the load balancer; they contain no information about the protocol used between the client and the load balancer. To determine the protocol used between the client and the load balancer, use the X-Forwarded-Proto request header. Elastic Load Balancing stores the protocol used between the client and the load balancer in the X-Forwarded-Proto request header and passes the header along to your server.

Your application or website can use the protocol stored in the X-Forwarded-Proto request header to render a response that redirects to the appropriate URL.

The X-Forwarded-Proto request header takes the following form:

X-Forwarded-Proto: originatingProtocol

The following example contains an X-Forwarded-Proto request header for a request that originated from the client as an HTTPS request:

X-Forwarded-Proto: https

AWS ELB documentation on the header

Example: AWS explains X-Forwarded-Proto

Addendum: TIL: the Forwarded header

There's a standardized header Forwarded (2014) and it has structure. We're not managing that in Puma yet.

lib/puma/server.rb Outdated Show resolved Hide resolved
lib/puma/server.rb Outdated Show resolved Hide resolved
Copy link
Member

@nateberkopec nateberkopec left a comment

Choose a reason for hiding this comment

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

Just a few stylistic changes. Also, needs tests.

@nateberkopec nateberkopec self-assigned this Jul 15, 2019
@olleolleolle
Copy link
Contributor Author

@nateberkopec I tried to interpret your comments as commits.

@olleolleolle
Copy link
Contributor Author

Note: To run one test file, saving yourself time, use TEST:

bundle exec rake test TEST=test/test_puma_server.rb

@olleolleolle
Copy link
Contributor Author

@nateberkopec Thanks for asking again, for tests. I sat down and had a go.

@@ -588,8 +588,11 @@ def normalize_env(env, client)
end

def default_server_port(env)
return PORT_443 if env[HTTPS_KEY] == 'on' || env[HTTPS_KEY] == 'https'
env['HTTP_X_FORWARDED_PROTO'] == 'https' ? PORT_443 : PORT_80
if ['on', HTTPS].include?(env[HTTPS_KEY]) || env[HTTP_X_FORWARDED_PROTO].to_s[0...5] == HTTPS || env[HTTP_X_FORWARDED_SCHEME] == HTTPS || env[HTTP_X_FORWARDED_SSL] == "on"
Copy link
Member

Choose a reason for hiding this comment

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

OK - now that we've refactored this out a bit, line 591 and the if/if/else in like 634 look really similar. Is there any possibility for re-use here?

@nateberkopec nateberkopec self-requested a review July 15, 2019 21:15
req['HOST'] = "example.com"
req['X_FORWARDED_SSL'] = "on"

res = Net::HTTP.start @host, @server.connected_port do |http|
Copy link
Member

Choose a reason for hiding this comment

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

Hmmm. This doesn't match our current style of just writing onto a TCP socket. Thoughts on that?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Eh, I only repeated test bodies which were there, tests that checked similar things, to have something to start with.

When I refactor this, I'll try to narrow down the tests to be more about the methods I changed.

end

def test_respect_x_forwarded_scheme
@server.app = proc do |env|
Copy link
Member

Choose a reason for hiding this comment

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

Once my other comment is resolved - the setup in all of these is so similar, we should extract to a helper method.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'd be glad to!

http.request(req)
end

assert_equal "80", res.body
Copy link
Member

Choose a reason for hiding this comment

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

I wonder - why not just test the actual methods modified rather than test res.body? This test is testing a lot more than it has to.

@olleolleolle
Copy link
Contributor Author

@nateberkopec I took a closer look, and made follow-up commits.

@nateberkopec
Copy link
Member

🚢 🚢 🚢

@nateberkopec nateberkopec merged commit 30fbd84 into puma:master Jul 17, 2019
@nateberkopec
Copy link
Member

Thanks for all your hard work here, @olleolleolle !

@olleolleolle olleolleolle deleted the feature/issue-1179 branch July 18, 2019 04:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants