Skip to content

Commit 425c2be

Browse files
committed
implement a force_ssl option
This works similarly to Rails force_ssl. Why add this to a proxy gem when it's stuff that generally lives in the webserver configuration? The most common use case for using a proxy gem in the first place is not having full control of the webserver (such as on heroku). In those environments the redirect is generally configured to be done on Rails, but requests handled by rack reverse are not handled by a Rails controller. Ideally one would create another Rack middleware to do this, but I think the convenience is worth it (especially in a Rails environment).
1 parent a30497d commit 425c2be

File tree

3 files changed

+38
-5
lines changed

3 files changed

+38
-5
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ run app
5151
* `:password` password for basic auth
5252
* `:matching` is a global only option, if set to :first the first matched url will be requested (no ambigous error). Default: :all.
5353
* `:timeout` seconds to timout the requests
54+
* `:force_ssl` redirects to ssl version, if not already using it (requires `:replace_response_host`). Default: false.
5455

5556
### Sample usage in a Ruby on Rails app
5657

lib/rack_reverse_proxy/middleware.rb

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,13 @@ def proxy(env, source_request, rule)
6060

6161
options = @global_options.dup.merge(rule.options)
6262

63+
if options[:force_ssl] && options[:replace_response_host] &&
64+
source_request.scheme == 'http'
65+
rewrite_uri(uri, source_request)
66+
uri.scheme = 'https'
67+
return [301, {'Location' => uri.to_s}, '']
68+
end
69+
6370
# Initialize request
6471
target_request = Net::HTTP.const_get(
6572
source_request.request_method.capitalize
@@ -115,11 +122,7 @@ def proxy(env, source_request, rule)
115122
# Replace the location header with the proxy domain
116123
if response_headers["Location"] && options[:replace_response_host]
117124
response_location = URI(response_headers["location"])
118-
response_location.scheme = source_request.scheme
119-
response_location.host = source_request.host
120-
unless default_port_for_scheme?(source_request.scheme, source_request.port)
121-
response_location.port = source_request.port
122-
end
125+
rewrite_uri(response_location, source_request)
123126
response_headers["Location"] = response_location.to_s
124127
end
125128

@@ -162,5 +165,13 @@ def format_headers(headers)
162165
def default_port_for_scheme?(scheme, port)
163166
scheme == 'http' && port == 80 || scheme == 'https' && port == 443
164167
end
168+
169+
def rewrite_uri(rewritten, original)
170+
rewritten.scheme = original.scheme
171+
rewritten.host = original.host
172+
unless default_port_for_scheme?(original.scheme, original.port)
173+
rewritten.port = original.port
174+
end
175+
end
165176
end
166177
end

spec/rack/reverse_proxy_spec.rb

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,27 @@ def app
298298
end
299299
end
300300

301+
describe "with force ssl turned on" do
302+
def app
303+
Rack::ReverseProxy.new(dummy_app) do
304+
reverse_proxy "/test", "http://example1.com/",
305+
:force_ssl => true, :replace_response_host => true
306+
end
307+
end
308+
309+
it "redirects to the ssl version when requesting non-ssl" do
310+
stub_request(:get, "http://example1.com/test/stuff").to_return(:body => "proxied")
311+
get "http://example.com/test/stuff"
312+
expect(last_response.headers["Location"]).to eq("https://example.com/test/stuff")
313+
end
314+
315+
it "does nothing when already ssl" do
316+
stub_request(:get, "http://example1.com/test/stuff").to_return(:body => "proxied")
317+
get "https://example.com/test/stuff"
318+
expect(last_response.body).to eq("proxied")
319+
end
320+
end
321+
301322
describe "with a route as a regular expression" do
302323
def app
303324
Rack::ReverseProxy.new(dummy_app) do

0 commit comments

Comments
 (0)