diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..630f5fa --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,22 @@ +name: Test + +on: [push, pull_request] + +jobs: + test: + strategy: + fail-fast: false + matrix: + ruby_version: ["2.7", "3.0", "3.1", "3.2", "3.3"] + os: ["ubuntu-latest","windows-latest","macos-latest"] + rack_version: ["2", "3"] + runs-on: ${{ matrix.os }} + env: + RACK_VERSION: ${{ matrix.rack_version }} + steps: + - uses: actions/checkout@v4 + - uses: ruby/setup-ruby@v1 + with: + ruby-version: ${{ matrix.ruby_version }} + - run: "bundle install" + - run: "bundle exec rake" \ No newline at end of file diff --git a/lib/rack_reverse_proxy/middleware.rb b/lib/rack_reverse_proxy/middleware.rb index 96faf9f..6e51a5e 100644 --- a/lib/rack_reverse_proxy/middleware.rb +++ b/lib/rack_reverse_proxy/middleware.rb @@ -17,7 +17,7 @@ class Middleware } def initialize(app = nil, &b) - @app = app || lambda { |_| [404, [], []] } + @app = app || lambda { |_| [404, rack_version_less_than_three ? [] : {}, []] } @rules = [] @global_options = DEFAULT_OPTIONS instance_eval(&b) if block_given? @@ -29,6 +29,10 @@ def call(env) private + def rack_version_less_than_three + Rack.release.split('.').first.to_i < 3 + end + def reverse_proxy_options(options) @global_options = @global_options.merge(options) end diff --git a/lib/rack_reverse_proxy/roundtrip.rb b/lib/rack_reverse_proxy/roundtrip.rb index 75cb350..eaced4f 100644 --- a/lib/rack_reverse_proxy/roundtrip.rb +++ b/lib/rack_reverse_proxy/roundtrip.rb @@ -152,7 +152,11 @@ def target_response end def response_headers - @_response_headers ||= build_response_headers + @_response_headers ||= begin + headers = build_response_headers + headers = headers.transform_keys(&:downcase) unless rack_version_less_than_three + headers + end end def build_response_headers @@ -163,25 +167,23 @@ def build_response_headers end def rack_response_headers - Rack::Utils::HeaderHash.new( - Rack::Proxy.normalize_headers( - format_headers(target_response.headers) - ) - ) + headers = Rack::Proxy.normalize_headers(format_headers(target_response.headers)) + rack_version_less_than_three ? Rack::Utils::HeaderHash.new(headers) : Rack::Headers.new.merge(headers) end + def replace_location_header return unless need_replace_location? rewrite_uri(response_location, source_request) - response_headers["Location"] = response_location.to_s + response_headers[set_rack_version_specific_location_header] = response_location.to_s end def response_location - @_response_location ||= URI(response_headers["Location"]) + @_response_location ||= URI(response_headers[set_rack_version_specific_location_header] || uri) end def need_replace_location? - response_headers["Location"] && options[:replace_response_host] && response_location.host + response_headers[set_rack_version_specific_location_header] && options[:replace_response_host] && response_location.host end def setup_request @@ -272,5 +274,13 @@ def non_ambiguous_match def ambiguous_match? matches.length > 1 && global_options[:matching] != :first end + + def rack_version_less_than_three + Rack.release.split('.').first.to_i < 3 + end + + def set_rack_version_specific_location_header + rack_version_less_than_three ? 'Location' : 'location' + end end end diff --git a/rack-reverse-proxy.gemspec b/rack-reverse-proxy.gemspec index 418acf2..3c1d260 100644 --- a/rack-reverse-proxy.gemspec +++ b/rack-reverse-proxy.gemspec @@ -35,10 +35,15 @@ eos spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) spec.require_paths = ["lib"] - spec.add_dependency "rack", ">= 1.0.0" + if ENV['RACK_VERSION'] == '2' + spec.add_dependency 'rack', ">= 1.0.0", '< 3.0' + else + spec.add_dependency 'rack', '>= 3.0', '< 4.0' + spec.add_dependency 'rackup', '~> 2.0' + end spec.add_dependency "rack-proxy", "~> 0.6", ">= 0.6.1" - spec.add_development_dependency "bundler", "~> 1.7" - spec.add_development_dependency "rake", "~> 10.3" + spec.add_development_dependency "bundler", ">= 1.7", "< 3.0" + spec.add_development_dependency "rake", ">= 10.3" end # rubocop:enable diff --git a/spec/rack/reverse_proxy_spec.rb b/spec/rack/reverse_proxy_spec.rb index 4f4cfbb..adbc290 100644 --- a/spec/rack/reverse_proxy_spec.rb +++ b/spec/rack/reverse_proxy_spec.rb @@ -24,7 +24,7 @@ def dummy_app ) end - describe "global options", focus: true do + describe "global options" do it "starts with default global options" do m = Rack::ReverseProxy.new(dummy_app) do reverse_proxy "/test", "http://example.com/" @@ -73,10 +73,11 @@ def app expect(last_response.body).to eq("Proxied App") end - it "produces a response header of type HeaderHash" do + it "produces a response header of type Headers" do stub_request(:get, "http://example.com/test") get "/test" - expect(last_response.headers).to be_an_instance_of(Rack::Utils::HeaderHash) + expected_class = rack_version_less_than_three ? Rack::Utils::HeaderHash : Rack::Headers + expect(last_response.headers).to be_an_instance_of(expected_class) end it "parses the headers as a Hash with values of type String" do @@ -154,7 +155,7 @@ def app expect(headers["Status"]).to be_nil end - it "formats the headers correctly to avoid duplicates" do + it "compares keys case-insensitive" do stub_request(:get, "http://example.com/2test").to_return( :headers => { :date => "Wed, 22 Jul 2015 11:27:21 GMT" } ) @@ -163,7 +164,7 @@ def app headers = last_response.headers.to_hash expect(headers["Date"]).to eq("Wed, 22 Jul 2015 11:27:21 GMT") - expect(headers["date"]).to be_nil + expect(headers["date"]).to rack_version_less_than_three ? be_nil : eq("Wed, 22 Jul 2015 11:27:21 GMT") end it "formats the headers with dashes correctly" do @@ -175,8 +176,7 @@ def app get "/2test" headers = last_response.headers.to_hash - expect(headers["X-Additional-Info"]).to eq("something") - expect(headers["x-additional-info"]).to be_nil + expect(headers["x-additional-info"]).to eq(rack_version_less_than_three ? nil : "something") end it "the response header includes content-length" do @@ -759,7 +759,7 @@ def app end end - describe "as a rack app" do + describe "as a rack app", skip: rack_version_less_than_three do it "responds with 404 when the path is not matched" do get "/" expect(last_response).to be_not_found diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 404277a..99a204d 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -103,4 +103,8 @@ # User-defined configuration WebMock.disable_net_connect! + + def rack_version_less_than_three + Rack.release.split('.').first.to_i < 3 + end end