diff --git a/lib/rack/reverse_proxy.rb b/lib/rack/reverse_proxy.rb index 4d63623..e731c20 100644 --- a/lib/rack/reverse_proxy.rb +++ b/lib/rack/reverse_proxy.rb @@ -27,6 +27,16 @@ def call(env) headers['HOST'] = uri.host if all_opts[:preserve_host] headers['X-Forwarded-Host'] = rackreq.host if all_opts[:x_forwarded_host] puts "Proxying #{rackreq.url} => #{uri} (Headers: #{headers.inspect})" if all_opts[:debug] + + if headers_opt = all_opts[:headers] + if headers_opt.is_a?(Proc) + headers = headers_opt.call(headers) + elsif headers_opt.is_a?(Hash) + headers.merge!(headers_opt) + else + $stderr.puts "Warning: :headers option provided with unsupported type '#{headers_opt.class}'. Expected a Hash or Proc" + end + end session = Net::HTTP.new(uri.host, uri.port) session.read_timeout=all_opts[:timeout] if all_opts[:timeout] @@ -37,7 +47,8 @@ def call(env) else # DO NOT DO THIS IN PRODUCTION !!! session.verify_mode = OpenSSL::SSL::VERIFY_NONE - end + end + session.start { |http| m = rackreq.request_method case m diff --git a/spec/rack/reverse_proxy_spec.rb b/spec/rack/reverse_proxy_spec.rb index 4296927..0b6db7d 100644 --- a/spec/rack/reverse_proxy_spec.rb +++ b/spec/rack/reverse_proxy_spec.rb @@ -120,6 +120,57 @@ def app get '/test/stuff' end end + + describe "with headers dictionary provided" do + context 'when the header does not exist in the source request' do + def app + Rack::ReverseProxy.new(dummy_app) do + reverse_proxy '/test', 'http://example.com/', {:headers => {'X-EXAMPLE-HEADER' => 'A value'}} + end + end + + it "should add the provided headers" do + stub_request(:any, 'example.com/test/stuff') + get '/test/stuff' + a_request(:get, 'http://example.com/test/stuff').with(:headers => {"X-EXAMPLE-HEADER" => "A value"}).should have_been_made + end + end + + context 'when the header already appears in the source request' do + def app + Rack::ReverseProxy.new(dummy_app) do + reverse_proxy '/test', 'http://example.com/', {:headers => {'X-EXAMPLE-HEADER' => 'New value'}} + end + end + + it "should replace the existing Headers value" do + headers = {'Accept'=>'*/*', 'Cookie'=>'', 'Host'=>'example.com', 'User-Agent'=>'Ruby', + 'X-Example-Header'=>'New value', 'X-Forwarded-Host'=>'example.org'} + stub_request(:any, 'example.com/test/stuff').with(:headers => headers) + get '/test/stuff', {}, {"X-EXAMPLE-HEADER" => "Original value"} + a_request(:get, 'http://example.com/test/stuff').with(:headers => {"X-EXAMPLE-HEADER" => "New value"}).should have_been_made + end + end + end + + describe 'with headers proc provided' do + def app + Rack::ReverseProxy.new(dummy_app) do + reverse_proxy('/test', 'http://example.com/', :headers => lambda do |headers| + headers['X-FORWARDED-FOR'].gsub!(', 127.0.0.1', '') if headers['X-FORWARDED-FOR'] + headers + end) + end + end + + it "should yield the headers to the proc for manipulation" do + headers = {'Accept'=>'*/*', 'Cookie'=>'', 'Host'=>'example.com', 'User-Agent'=>'Ruby', + 'X-Forwarded_For'=>'174.254.197.191', 'X-Forwarded-Host'=>'example.org'} + stub_request(:any, 'example.com/test/stuff').with(:headers => headers) + get '/test/stuff', {}, {'HTTP_X_Forwarded_For'=>'174.254.197.191, 127.0.0.1'} + a_request(:get, 'http://example.com/test/stuff').with(:headers => {"X-Forwarded-For" => "174.254.197.191"}).should have_been_made + end + end describe "with ambiguous routes and all matching" do def app