From 2e213eab39087c06379af47ec49700b4102fc66c Mon Sep 17 00:00:00 2001 From: Olle Jonsson Date: Mon, 10 Jul 2023 20:47:21 +0200 Subject: [PATCH 01/20] Refer to github, not Rubyforge [ci skip] (#185) --- lib/rack/contrib/printout.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rack/contrib/printout.rb b/lib/rack/contrib/printout.rb index 54f84f72..d252040f 100644 --- a/lib/rack/contrib/printout.rb +++ b/lib/rack/contrib/printout.rb @@ -8,7 +8,7 @@ def initialize(app) end def call(env) - # See http://rack.rubyforge.org/doc/SPEC.html for details + # See https://github.com/rack/rack/blob/main/SPEC.rdoc for details puts "**********\n Environment\n **************" puts env.inspect From 5234ccc3081910f7f119f4f5170a214fef602246 Mon Sep 17 00:00:00 2001 From: Patrik Ragnarsson Date: Thu, 14 Sep 2023 18:13:45 +0200 Subject: [PATCH 02/20] Use GitHub Actions Travis CI not used anymore? Or at least shouldn't be, easier to use GitHub Actions. --- .github/workflows/ci.yml | 27 +++++++++++++++++++++++++++ .travis.yml | 14 -------------- rack-contrib.gemspec | 3 --- test/gemfiles/minimum_versions | 20 -------------------- 4 files changed, 27 insertions(+), 37 deletions(-) create mode 100644 .github/workflows/ci.yml delete mode 100644 .travis.yml delete mode 100644 test/gemfiles/minimum_versions diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000..82297dac --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,27 @@ +name: CI + +on: [push, pull_request] + +permissions: + contents: read + +jobs: + test: + strategy: + fail-fast: false + matrix: + os: [ ubuntu-20.04 ] + ruby: [ 2.2, 2.3, 2.4, 2.5, 2.6, 2.7, '3.0', 3.1, 3.2 ] + gemfile: [ Gemfile ] + runs-on: ${{ matrix.os }} + env: + BUNDLE_GEMFILE: ${{ github.workspace }}/${{ matrix.gemfile }} + steps: + - uses: actions/checkout@v4 + + - uses: ruby/setup-ruby@v1 + with: + ruby-version: ${{ matrix.ruby }} + bundler-cache: true + + - run: bundle exec rake diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index e167d271..00000000 --- a/.travis.yml +++ /dev/null @@ -1,14 +0,0 @@ -language: ruby - -cache: bundler - -rvm: - - 2.6.3 - - 2.5.5 - - 2.4.6 - - 2.3.8 - - 2.2.10 - -gemfile: - - Gemfile - - test/gemfiles/minimum_versions diff --git a/rack-contrib.gemspec b/rack-contrib.gemspec index b515925b..a517218f 100644 --- a/rack-contrib.gemspec +++ b/rack-contrib.gemspec @@ -35,9 +35,6 @@ Gem::Specification.new do |s| s.required_ruby_version = '>= 2.2.2' - # REMINDER: If you modify any dependencies, please ensure you - # update `test/gemfiles/minimum_versions`! - # s.add_runtime_dependency 'rack', '~> 2.0' s.add_development_dependency 'bundler', '>= 1.0', '< 3' diff --git a/test/gemfiles/minimum_versions b/test/gemfiles/minimum_versions deleted file mode 100644 index bae46295..00000000 --- a/test/gemfiles/minimum_versions +++ /dev/null @@ -1,20 +0,0 @@ -source 'https://rubygems.org' - -# These are the minimum available versions of all the gems we need, based on -# the version specs in the gemspec. We test against all of these to ensure -# we haven't accidentally used a feature of a gem that isn't in the oldest -# version we say we need. -# -gem 'rack', '= 2.0.1' -gem 'git-version-bump', '= 0.15.0' -gem 'i18n', '= 0.6.8' -gem 'json', '= 2.0.0' -gem 'mime-types', '= 3.0' -gem 'minitest', '= 5.6.0' -gem 'minitest-hooks', '= 1.0.0' -gem 'mail', '= 2.6.4' -gem 'nbio-csshttprequest', '= 1.0.2' -gem 'rdoc', '= 5.0.0' -gem 'rake', '= 10.4.2' -gem 'ruby-prof', '= 0.17.0' -gem 'timecop', '= 0.4.1' From 204366aa60ac010a709f07087c9cf9fd9491aa27 Mon Sep 17 00:00:00 2001 From: Patrik Ragnarsson Date: Thu, 14 Sep 2023 18:18:01 +0200 Subject: [PATCH 03/20] Move dev deps to Gemfile It is just easier to manage this way. --- Gemfile | 14 ++++++++++++++ rack-contrib.gemspec | 15 --------------- 2 files changed, 14 insertions(+), 15 deletions(-) diff --git a/Gemfile b/Gemfile index 7f4f5e95..5eabdf44 100644 --- a/Gemfile +++ b/Gemfile @@ -3,3 +3,17 @@ source 'https://rubygems.org' gemspec + +gem 'git-version-bump', '~> 0.15' +gem 'github-release', '~> 0.1' +gem 'i18n', '~> 0.6', '>= 0.6.8' +gem 'json', '~> 2.0' +gem 'mime-types', '~> 3.0' +gem 'minitest', '~> 5.6' +gem 'minitest-hooks', '~> 1.0' +gem 'mail', '~> 2.3', '>= 2.6.4' +gem 'nbio-csshttprequest', '~> 1.0' +gem 'rake', '~> 10.4', '>= 10.4.2' +gem 'rdoc', '~> 5.0' +gem 'ruby-prof', '~> 0.17' +gem 'timecop', '~> 0.9' diff --git a/rack-contrib.gemspec b/rack-contrib.gemspec index a517218f..279557de 100644 --- a/rack-contrib.gemspec +++ b/rack-contrib.gemspec @@ -37,21 +37,6 @@ Gem::Specification.new do |s| s.add_runtime_dependency 'rack', '~> 2.0' - s.add_development_dependency 'bundler', '>= 1.0', '< 3' - s.add_development_dependency 'git-version-bump', '~> 0.15' - s.add_development_dependency 'github-release', '~> 0.1' - s.add_development_dependency 'i18n', '~> 0.6', '>= 0.6.8' - s.add_development_dependency 'json', '~> 2.0' - s.add_development_dependency 'mime-types', '~> 3.0' - s.add_development_dependency 'minitest', '~> 5.6' - s.add_development_dependency 'minitest-hooks', '~> 1.0' - s.add_development_dependency 'mail', '~> 2.3', '>= 2.6.4' - s.add_development_dependency 'nbio-csshttprequest', '~> 1.0' - s.add_development_dependency 'rake', '~> 10.4', '>= 10.4.2' - s.add_development_dependency 'rdoc', '~> 5.0' - s.add_development_dependency 'ruby-prof', '~> 0.17' - s.add_development_dependency 'timecop', '~> 0.9' - s.homepage = "https://github.com/rack/rack-contrib/" s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "rack-contrib", "--main", "README"] s.require_paths = %w[lib] From 239aaeb55b98956c2e3631341715dc35aa44f0a7 Mon Sep 17 00:00:00 2001 From: Patrik Ragnarsson Date: Thu, 14 Sep 2023 18:19:03 +0200 Subject: [PATCH 04/20] Use latest `rake` Older rake does not work on modern Ruby (3.2): $ bundle exec rake rake aborted! NoMethodError: undefined method `=~' for # /Users/dentarg/.rubies/3.2.2/bin/bundle:25:in `load' /Users/dentarg/.rubies/3.2.2/bin/bundle:25:in `
' (See full trace by running task with --trace) --- Gemfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile b/Gemfile index 5eabdf44..7da1cbfb 100644 --- a/Gemfile +++ b/Gemfile @@ -13,7 +13,7 @@ gem 'minitest', '~> 5.6' gem 'minitest-hooks', '~> 1.0' gem 'mail', '~> 2.3', '>= 2.6.4' gem 'nbio-csshttprequest', '~> 1.0' -gem 'rake', '~> 10.4', '>= 10.4.2' +gem 'rake' gem 'rdoc', '~> 5.0' gem 'ruby-prof', '~> 0.17' gem 'timecop', '~> 0.9' From c753ac114cdf6df74408a7683fa19b773e5db7e2 Mon Sep 17 00:00:00 2001 From: Patrik Ragnarsson Date: Thu, 14 Sep 2023 18:20:50 +0200 Subject: [PATCH 05/20] Fix removed `exists?` method --- lib/rack/contrib/backstage.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rack/contrib/backstage.rb b/lib/rack/contrib/backstage.rb index 86d61cde..a7588e2b 100644 --- a/lib/rack/contrib/backstage.rb +++ b/lib/rack/contrib/backstage.rb @@ -10,7 +10,7 @@ def initialize(app, path) end def call(env) - if File.exists?(@file) + if File.exist?(@file) content = File.read(@file) length = content.bytesize.to_s [503, {'Content-Type' => 'text/html', 'Content-Length' => length}, [content]] From 2a5a5054a21d64bac7b218f1dfece15a10de2f1b Mon Sep 17 00:00:00 2001 From: Patrik Ragnarsson Date: Thu, 14 Sep 2023 20:10:30 +0200 Subject: [PATCH 06/20] Fix `Regexp` warning Fixes this warning: 3rd argument to Regexp.new is deprecated and will be removed in Ruby 3.3; use 2nd argument instead --- lib/rack/contrib/access.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rack/contrib/access.rb b/lib/rack/contrib/access.rb index bd1e48ae..8fe3674c 100644 --- a/lib/rack/contrib/access.rb +++ b/lib/rack/contrib/access.rb @@ -40,7 +40,7 @@ def remap(mapping) raise ArgumentError, "paths need to start with /" end location = location.chomp('/') - match = Regexp.new("^#{Regexp.quote(location).gsub('/', '/+')}(.*)", nil, 'n') + match = Regexp.new("^#{Regexp.quote(location).gsub('/', '/+')}(.*)", Regexp::NOENCODING) ipmasks.collect! do |ipmask| ipmask.is_a?(IPAddr) ? ipmask : IPAddr.new(ipmask) From 1f075f0be15e437ea78dc2065964f53747146dd8 Mon Sep 17 00:00:00 2001 From: Patrik Ragnarsson Date: Thu, 14 Sep 2023 20:39:54 +0200 Subject: [PATCH 07/20] Ruby 2.7, 3.0 needs `cgi` >= 0.3.6 --- Gemfile | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Gemfile b/Gemfile index 7da1cbfb..b77b17f3 100644 --- a/Gemfile +++ b/Gemfile @@ -17,3 +17,7 @@ gem 'rake' gem 'rdoc', '~> 5.0' gem 'ruby-prof', '~> 0.17' gem 'timecop', '~> 0.9' + +# See https://github.com/ruby/cgi/pull/29 +# Needed to have passing tests on Ruby 2.7, Ruby 3.0 +gem 'cgi', '>= 0.3.6' if RUBY_VERSION >= '2.7.0' && RUBY_VERSION <= '3.1.0' From a13c4acbcc0f826ba754b468b3b600b7a036bd99 Mon Sep 17 00:00:00 2001 From: Patrik Ragnarsson Date: Thu, 14 Sep 2023 22:33:14 +0200 Subject: [PATCH 08/20] Can not use `git-version-bump` on Ruby 2.2 Due to invalid syntax: https://github.com/mpalmer/git-version-bump/commit/640fedcd8f9934bef36cf5675bb9b7366c18e099#diff-88985aff4dfd862090e947417b1b4e60694e6028380ac827cae988331a3bb456R291 --- Gemfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile b/Gemfile index b77b17f3..7d9bb0b8 100644 --- a/Gemfile +++ b/Gemfile @@ -4,7 +4,7 @@ source 'https://rubygems.org' gemspec -gem 'git-version-bump', '~> 0.15' +gem 'git-version-bump', '~> 0.15' if RUBY_VERSION >= '2.3.0' gem 'github-release', '~> 0.1' gem 'i18n', '~> 0.6', '>= 0.6.8' gem 'json', '~> 2.0' From 1c4a9926c93a09f7112a70c46829fa99dd3fa15b Mon Sep 17 00:00:00 2001 From: Patrik Ragnarsson Date: Thu, 14 Sep 2023 23:13:42 +0200 Subject: [PATCH 09/20] Make `Rack::Profiler` test more robust We can't be that specific with matching on the output from ruby-prof (CallStackPrinter), it can vary depending on the Ruby version, as seen in https://github.com/rack/rack-contrib/issues/182 --- test/spec_rack_profiler.rb | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/test/spec_rack_profiler.rb b/test/spec_rack_profiler.rb index ee631730..4499435a 100644 --- a/test/spec_rack_profiler.rb +++ b/test/spec_rack_profiler.rb @@ -21,9 +21,10 @@ def profiler(app, options = {}) end specify 'called multiple times via query params' do - req = Rack::MockRequest.env_for("/", :params => "profile=process_time&profiler_runs=4") + runs = 4 + req = Rack::MockRequest.env_for("/", :params => "profile=process_time&profiler_runs=#{runs}") body = profiler(app).call(req)[2] - _(body.to_enum.to_a.join).must_match(/Time#initialize \[4 calls, 4 total\]/) + _(body.to_enum.to_a.join).must_match(/\[#{runs} calls, #{runs} total\]/) end specify 'CallStackPrinter has Content-Type test/html' do From 59b856e814a3dd4cdf0bff304c34afb9210d74a6 Mon Sep 17 00:00:00 2001 From: Patrik Ragnarsson Date: Thu, 14 Sep 2023 23:20:00 +0200 Subject: [PATCH 10/20] Allow `ruby-prof` >= 1.x --- Gemfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile b/Gemfile index 7d9bb0b8..041d5200 100644 --- a/Gemfile +++ b/Gemfile @@ -15,7 +15,7 @@ gem 'mail', '~> 2.3', '>= 2.6.4' gem 'nbio-csshttprequest', '~> 1.0' gem 'rake' gem 'rdoc', '~> 5.0' -gem 'ruby-prof', '~> 0.17' +gem 'ruby-prof' gem 'timecop', '~> 0.9' # See https://github.com/ruby/cgi/pull/29 From f21541c73605c61cd5e63c818631ebc0639fc523 Mon Sep 17 00:00:00 2001 From: Patrik Ragnarsson Date: Thu, 14 Sep 2023 23:42:32 +0200 Subject: [PATCH 11/20] Address deprecation warnings from `ruby-prof` 1.x These RubyProf.running? called from /Users/dentarg/src/rack-contrib/lib/rack/contrib/profiler.rb:49. NOTE: RubyProf.measure_mode= is deprecated; use Profile#measure_mode= instead. It will be removed on or after 2023-06. Comes from https://github.com/ruby-prof/ruby-prof/blob/fd3a5236a459586c5ca7ce4de506c1835129516a/lib/ruby-prof/compatibility.rb#L100-L112 However, they seem to lie, there is no Profile#measure_mode= method. --- lib/rack/contrib/profiler.rb | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/lib/rack/contrib/profiler.rb b/lib/rack/contrib/profiler.rb index 2dac848d..776d5169 100644 --- a/lib/rack/contrib/profiler.rb +++ b/lib/rack/contrib/profiler.rb @@ -32,6 +32,7 @@ class Profiler # option defaulting to :call_stack. def initialize(app, options = {}) @app = app + @profile = nil @printer = parse_printer(options[:printer] || DEFAULT_PRINTER) @times = (options[:times] || 1).to_i end @@ -46,27 +47,27 @@ def call(env) private def profiling?(env) - unless ::RubyProf.running? - request = Rack::Request.new(env.clone) - if mode = request.params.delete('profile') - if ::RubyProf.const_defined?(mode.upcase) - mode - else - env['rack.errors'].write "Invalid RubyProf measure_mode: " + - "#{mode}. Use one of #{MODES.to_a.join(', ')}" - false - end + return if @profile && @profile.running? + + request = Rack::Request.new(env.clone) + if mode = request.params.delete('profile') + if ::RubyProf.const_defined?(mode.upcase) + mode + else + env['rack.errors'].write "Invalid RubyProf measure_mode: " + + "#{mode}. Use one of #{MODES.to_a.join(', ')}" + false end end end def profile(env, mode) - ::RubyProf.measure_mode = ::RubyProf.const_get(mode.upcase) + @profile = ::RubyProf::Profile.new(measure_mode: ::RubyProf.const_get(mode.upcase)) GC.enable_stats if GC.respond_to?(:enable_stats) request = Rack::Request.new(env.clone) runs = (request.params['profiler_runs'] || @times).to_i - result = ::RubyProf.profile do + result = @profile.profile do runs.times { @app.call(env) } end GC.disable_stats if GC.respond_to?(:disable_stats) From 8d4d32f66a7c7aa6b50b0f8d7b7fabc2c4c1aa01 Mon Sep 17 00:00:00 2001 From: Patrik Ragnarsson Date: Thu, 14 Sep 2023 23:55:39 +0200 Subject: [PATCH 12/20] Drop support for Ruby 2.2 git-version-bump can't be used on Ruby 2.2 due to the use of &. https://github.com/mpalmer/git-version-bump/commit/640fedcd8f9934bef36cf5675bb9b7366c18e099#diff-88985aff4dfd862090e947417b1b4e60694e6028380ac827cae988331a3bb456R291 and the fact that the required ruby version hasn't been updated for the gem. https://github.com/mpalmer/git-version-bump/blob/c1af65cd82c131cb541fa717b3d24a9247973049/git-version-bump.gemspec#L12 Instead of fighting that battle, let's drop 2.2, it is very old (soon 10 years). Also, there is no need to rescue the LoadError when GVB is used in the gemspec, we must be able to load it. --- .github/workflows/ci.yml | 2 +- rack-contrib.gemspec | 8 ++------ 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 82297dac..d87b1e60 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,7 +11,7 @@ jobs: fail-fast: false matrix: os: [ ubuntu-20.04 ] - ruby: [ 2.2, 2.3, 2.4, 2.5, 2.6, 2.7, '3.0', 3.1, 3.2 ] + ruby: [ 2.3, 2.4, 2.5, 2.6, 2.7, '3.0', 3.1, 3.2 ] gemfile: [ Gemfile ] runs-on: ${{ matrix.os }} env: diff --git a/rack-contrib.gemspec b/rack-contrib.gemspec index 279557de..87589a1b 100644 --- a/rack-contrib.gemspec +++ b/rack-contrib.gemspec @@ -1,10 +1,6 @@ # frozen_string_literal: true -begin - require 'git-version-bump' -rescue LoadError - nil -end +require 'git-version-bump' Gem::Specification.new do |s| s.specification_version = 2 if s.respond_to? :specification_version= @@ -33,7 +29,7 @@ Gem::Specification.new do |s| s.extra_rdoc_files = %w[README.md COPYING] - s.required_ruby_version = '>= 2.2.2' + s.required_ruby_version = '>= 2.3.8' s.add_runtime_dependency 'rack', '~> 2.0' From 161eb9d190848254c331810e06feeaad25ec16aa Mon Sep 17 00:00:00 2001 From: Patrik Ragnarsson Date: Fri, 15 Sep 2023 00:12:18 +0200 Subject: [PATCH 13/20] Remove (crazy?) `PostBodyContentTypeParser` test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Running TESTOPTS=--seed=26040 bundle e rake (on any Ruby version it seems) made this test fail: 1) Failure: Rack::PostBodyContentTypeParser#test_0005_should not create additions [/Users/dentarg/src/rack-contrib/test/spec_rack_post_body_content_type_parser.rb:35]: Expected [:SPLIT_PATTERN, :hsh, :strip_doublequotes] to be empty. Maybe these come from Rack? ~/src/rack tags/v2.2.8 $ ag SPLIT_PATTERN lib/rack/media_type.rb 7: SPLIT_PATTERN = %r{\s*[;,]\s*} 18: content_type.split(SPLIT_PATTERN, 2).first.tap &:downcase! 29: content_type.split(SPLIT_PATTERN)[1..-1].each_with_object({}) do |s, hsh| ~/src/rack tags/v2.2.8 $ ag strip_doublequotes lib/rack/media_type.rb 32: hsh[k.tap(&:downcase!)] = strip_doublequotes(v) 38: def strip_doublequotes(str) Anyway, I don't think we can have a test like this. We don't seem to be in control of this. https://ruby-doc.org/3.2.2/Symbol.html#method-c-all_symbols says Symbol.all_symbols > Returns an array of all symbols currently in Ruby’s symbol table --- test/spec_rack_post_body_content_type_parser.rb | 7 ------- 1 file changed, 7 deletions(-) diff --git a/test/spec_rack_post_body_content_type_parser.rb b/test/spec_rack_post_body_content_type_parser.rb index 2cdf2a75..651876cc 100644 --- a/test/spec_rack_post_body_content_type_parser.rb +++ b/test/spec_rack_post_body_content_type_parser.rb @@ -28,13 +28,6 @@ _(params['key']).must_equal "value" end - specify "should not create additions" do - before = Symbol.all_symbols - params_for_request %{{"json_class":"this_should_not_be_added"}}, "application/json" rescue nil - result = Symbol.all_symbols - before - _(result).must_be_empty - end - specify "should apply given block to body" do params = params_for_request '{"key":"value"}', "application/json" do |body| { 'payload' => JSON.parse(body) } From 59c8921d924603d902259fb036d2f8b64dab47b3 Mon Sep 17 00:00:00 2001 From: Patrik Ragnarsson Date: Fri, 15 Sep 2023 00:17:59 +0200 Subject: [PATCH 14/20] Try reading gemspec last Looks like the LoadError rescue I removed in 4c9328100f958ff317d0054d4218a5ebb8aa33ec was useful after all? This change is trying to avoid this (which all jobs failed with): [!] There was an error parsing `Gemfile`: [!] There was an error while loading `rack-contrib.gemspec`: cannot load such file -- git-version-bump. Bundler cannot continue. # from /home/runner/work/rack-contrib/rack-contrib/rack-contrib.gemspec:3 # ------------------------------------------- # > require 'git-version-bump' # # ------------------------------------------- . Bundler cannot continue. # from /home/runner/work/rack-contrib/rack-contrib/Gemfile:5 # ------------------------------------------- # > gemspec # # ------------------------------------------- --- Gemfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Gemfile b/Gemfile index 041d5200..06e80639 100644 --- a/Gemfile +++ b/Gemfile @@ -2,8 +2,6 @@ source 'https://rubygems.org' -gemspec - gem 'git-version-bump', '~> 0.15' if RUBY_VERSION >= '2.3.0' gem 'github-release', '~> 0.1' gem 'i18n', '~> 0.6', '>= 0.6.8' @@ -21,3 +19,5 @@ gem 'timecop', '~> 0.9' # See https://github.com/ruby/cgi/pull/29 # Needed to have passing tests on Ruby 2.7, Ruby 3.0 gem 'cgi', '>= 0.3.6' if RUBY_VERSION >= '2.7.0' && RUBY_VERSION <= '3.1.0' + +gemspec From eae94fe453dc81ba72af497c5be509ac0412263d Mon Sep 17 00:00:00 2001 From: Patrik Ragnarsson Date: Fri, 15 Sep 2023 00:22:39 +0200 Subject: [PATCH 15/20] Add back gemspec `LoadError` rescue --- rack-contrib.gemspec | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/rack-contrib.gemspec b/rack-contrib.gemspec index 87589a1b..a833618e 100644 --- a/rack-contrib.gemspec +++ b/rack-contrib.gemspec @@ -1,6 +1,10 @@ # frozen_string_literal: true -require 'git-version-bump' +begin + require 'git-version-bump' +rescue LoadError + nil +end Gem::Specification.new do |s| s.specification_version = 2 if s.respond_to? :specification_version= From da54e10d52a9a238775a93f3711206f16a0e78a7 Mon Sep 17 00:00:00 2001 From: Patrik Ragnarsson Date: Fri, 15 Sep 2023 18:03:52 +0200 Subject: [PATCH 16/20] `ENOGVB` is not a thing, use `ENOTAG` This is what the git-version-bump README says to use: https://github.com/mpalmer/git-version-bump/tree/c1af65cd82c131cb541fa717b3d24a9247973049#in-your-ruby-code I guess 64fd18677a7a57fa2a80806d76000cd85e74422d got it wrong despite Matt being the author of both that commit and git-version-bump :-) --- rack-contrib.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rack-contrib.gemspec b/rack-contrib.gemspec index a833618e..5b5d5af0 100644 --- a/rack-contrib.gemspec +++ b/rack-contrib.gemspec @@ -11,7 +11,7 @@ Gem::Specification.new do |s| s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= s.name = 'rack-contrib' - s.version = GVB.version rescue "0.0.0.1.ENOGVB" + s.version = GVB.version rescue "0.0.0.1.ENOTAG" s.date = GVB.date rescue Time.now.strftime("%F") s.licenses = ['MIT'] From fe9528222487b31e8132b299bbc66a8c65abf570 Mon Sep 17 00:00:00 2001 From: Patrik Ragnarsson Date: Fri, 15 Sep 2023 19:06:38 +0200 Subject: [PATCH 17/20] No need to restrict GVB Co-authored-by: Robin Wallin --- Gemfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile b/Gemfile index 06e80639..55d03a4a 100644 --- a/Gemfile +++ b/Gemfile @@ -2,7 +2,7 @@ source 'https://rubygems.org' -gem 'git-version-bump', '~> 0.15' if RUBY_VERSION >= '2.3.0' +gem 'git-version-bump', '~> 0.15' gem 'github-release', '~> 0.1' gem 'i18n', '~> 0.6', '>= 0.6.8' gem 'json', '~> 2.0' From 4101575038784ce3dd6c3b4849279274a4edf14d Mon Sep 17 00:00:00 2001 From: Patrik Ragnarsson Date: Mon, 25 Sep 2023 20:14:07 +0200 Subject: [PATCH 18/20] Revert "Drop support for Ruby 2.2" This reverts commit 4c9328100f958ff317d0054d4218a5ebb8aa33ec. git-version-bump 0.19.1 should restore Ruby 2.2 compatibility. --- .github/workflows/ci.yml | 2 +- rack-contrib.gemspec | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d87b1e60..82297dac 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,7 +11,7 @@ jobs: fail-fast: false matrix: os: [ ubuntu-20.04 ] - ruby: [ 2.3, 2.4, 2.5, 2.6, 2.7, '3.0', 3.1, 3.2 ] + ruby: [ 2.2, 2.3, 2.4, 2.5, 2.6, 2.7, '3.0', 3.1, 3.2 ] gemfile: [ Gemfile ] runs-on: ${{ matrix.os }} env: diff --git a/rack-contrib.gemspec b/rack-contrib.gemspec index 5b5d5af0..d554b3c7 100644 --- a/rack-contrib.gemspec +++ b/rack-contrib.gemspec @@ -33,7 +33,7 @@ Gem::Specification.new do |s| s.extra_rdoc_files = %w[README.md COPYING] - s.required_ruby_version = '>= 2.3.8' + s.required_ruby_version = '>= 2.2.2' s.add_runtime_dependency 'rack', '~> 2.0' From b97c5e7378f9329c95fd244f6b63fab94d4e4052 Mon Sep 17 00:00:00 2001 From: Patrik Ragnarsson Date: Fri, 29 Sep 2023 10:32:12 +0200 Subject: [PATCH 19/20] Remove another crazy spec Triggered by TESTOPTS=--seed=8264 b e rake Seen in CI: https://github.com/rack/rack-contrib/actions/runs/6319320196/job/17160038534#step:4:88 I don't think we should be checking this. See https://github.com/rack/rack-contrib/commit/161eb9d190848254c331810e06feeaad25ec16aa --- test/spec_rack_json_body_parser_spec.rb | 8 -------- 1 file changed, 8 deletions(-) diff --git a/test/spec_rack_json_body_parser_spec.rb b/test/spec_rack_json_body_parser_spec.rb index 665e7e08..7e2a317f 100644 --- a/test/spec_rack_json_body_parser_spec.rb +++ b/test/spec_rack_json_body_parser_spec.rb @@ -58,14 +58,6 @@ def create_parser(app, **args, &block) _(res[2].to_enum.to_a.join).must_equal %Q({"{\\"key\\": \\"value\\"}"=>nil}) end - specify "should not create additions" do - before = Symbol.all_symbols - env = mock_env(input: %{{"json_class":"this_should_not_be_added"}}) - create_parser(app).call(env) - result = Symbol.all_symbols - before - _(result).must_be_empty - end - specify "should not rescue JSON:ParserError raised by the app" do env = mock_env app = ->(env) { raise JSON::ParserError } From a9cd089514b6410d34d09865509effa61f555d4f Mon Sep 17 00:00:00 2001 From: Patrik Ragnarsson Date: Sat, 7 Oct 2023 23:30:11 +0200 Subject: [PATCH 20/20] Support Rack 3 (#187) --- .github/workflows/ci.yml | 7 ++++ Gemfile | 1 + lib/rack/contrib/access.rb | 2 +- lib/rack/contrib/backstage.rb | 2 +- lib/rack/contrib/bounce_favicon.rb | 2 +- lib/rack/contrib/common_cookies.rb | 5 ++- lib/rack/contrib/csshttprequest.rb | 4 ++- lib/rack/contrib/deflect.rb | 2 +- lib/rack/contrib/enforce_valid_encoding.rb | 2 +- lib/rack/contrib/expectation_cascade.rb | 4 +-- lib/rack/contrib/host_meta.rb | 2 +- lib/rack/contrib/json_body_parser.rb | 8 ++--- lib/rack/contrib/jsonp.rb | 7 ++-- lib/rack/contrib/lazy_conditional_get.rb | 10 +++--- lib/rack/contrib/locale.rb | 5 ++- lib/rack/contrib/mailexceptions.rb | 2 +- lib/rack/contrib/not_found.rb | 2 +- .../contrib/post_body_content_type_parser.rb | 4 +-- lib/rack/contrib/profiler.rb | 4 +-- lib/rack/contrib/relative_redirect.rb | 3 +- lib/rack/contrib/response_cache.rb | 3 +- lib/rack/contrib/response_headers.rb | 8 +++-- lib/rack/contrib/static_cache.rb | 6 ++-- rack-contrib.gemspec | 2 +- test/spec_rack_access.rb | 2 +- test/spec_rack_backstage.rb | 4 +-- test/spec_rack_bounce_favicon.rb | 2 +- test/spec_rack_common_cookies.rb | 7 +++- test/spec_rack_config.rb | 2 +- test/spec_rack_cookies.rb | 6 +++- test/spec_rack_csshttprequest.rb | 21 +++++++++--- test/spec_rack_deflect.rb | 20 ++++++++--- test/spec_rack_enforce_valid_encoding.rb | 2 +- test/spec_rack_evil.rb | 6 ++-- test/spec_rack_expectation_cascade.rb | 14 ++++---- test/spec_rack_jsonp.rb | 34 +++++++++---------- test/spec_rack_lazy_conditional_get.rb | 29 ++++++++-------- test/spec_rack_lighttpd_script_name_fix.rb | 2 +- test/spec_rack_nested_params.rb | 9 ++--- ...spec_rack_post_body_content_type_parser.rb | 4 +-- test/spec_rack_proctitle.rb | 2 +- test/spec_rack_profiler.rb | 14 ++++---- test/spec_rack_response_cache.rb | 6 +++- test/spec_rack_response_headers.rb | 23 ++++++++++--- test/spec_rack_runtime.rb | 6 +++- 45 files changed, 197 insertions(+), 115 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 82297dac..686ce19d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,14 +7,21 @@ permissions: jobs: test: + name: Ruby ${{ matrix.ruby }} Rack ${{ matrix.rack }} strategy: fail-fast: false matrix: os: [ ubuntu-20.04 ] + rack: [ '~> 2.0', '~> 3.0' ] ruby: [ 2.2, 2.3, 2.4, 2.5, 2.6, 2.7, '3.0', 3.1, 3.2 ] gemfile: [ Gemfile ] + exclude: + # Rack 3 needs >= Ruby 2.4 + - { ruby: 2.2, rack: '~> 3.0' } + - { ruby: 2.3, rack: '~> 3.0' } runs-on: ${{ matrix.os }} env: + RACK_VERSION: ${{ matrix.rack }} BUNDLE_GEMFILE: ${{ github.workspace }}/${{ matrix.gemfile }} steps: - uses: actions/checkout@v4 diff --git a/Gemfile b/Gemfile index 55d03a4a..60c7f37c 100644 --- a/Gemfile +++ b/Gemfile @@ -11,6 +11,7 @@ gem 'minitest', '~> 5.6' gem 'minitest-hooks', '~> 1.0' gem 'mail', '~> 2.3', '>= 2.6.4' gem 'nbio-csshttprequest', '~> 1.0' +gem 'rack', ENV['RACK_VERSION'] gem 'rake' gem 'rdoc', '~> 5.0' gem 'ruby-prof' diff --git a/lib/rack/contrib/access.rb b/lib/rack/contrib/access.rb index 8fe3674c..4d33939d 100644 --- a/lib/rack/contrib/access.rb +++ b/lib/rack/contrib/access.rb @@ -72,7 +72,7 @@ def ipmasks_for_path(env) end def forbidden! - [403, { 'Content-Type' => 'text/html', 'Content-Length' => '0' }, []] + [403, { 'content-type' => 'text/html', 'content-length' => '0' }, []] end def ip_authorized?(request, ipmasks) diff --git a/lib/rack/contrib/backstage.rb b/lib/rack/contrib/backstage.rb index a7588e2b..c170c89a 100644 --- a/lib/rack/contrib/backstage.rb +++ b/lib/rack/contrib/backstage.rb @@ -13,7 +13,7 @@ def call(env) if File.exist?(@file) content = File.read(@file) length = content.bytesize.to_s - [503, {'Content-Type' => 'text/html', 'Content-Length' => length}, [content]] + [503, {'content-type' => 'text/html', 'content-length' => length}, [content]] else @app.call(env) end diff --git a/lib/rack/contrib/bounce_favicon.rb b/lib/rack/contrib/bounce_favicon.rb index 7319c2c7..fd23d0d1 100644 --- a/lib/rack/contrib/bounce_favicon.rb +++ b/lib/rack/contrib/bounce_favicon.rb @@ -9,7 +9,7 @@ def initialize(app) def call(env) if env["PATH_INFO"] == "/favicon.ico" - [404, {"Content-Type" => "text/html", "Content-Length" => "0"}, []] + [404, {"content-type" => "text/html", "content-length" => "0"}, []] else @app.call(env) end diff --git a/lib/rack/contrib/common_cookies.rb b/lib/rack/contrib/common_cookies.rb index 2223b0af..8ccc5c2f 100644 --- a/lib/rack/contrib/common_cookies.rb +++ b/lib/rack/contrib/common_cookies.rb @@ -7,13 +7,16 @@ class CommonCookies LOCALHOST_OR_IP_REGEXP = /^([\d.]+|localhost)$/ PORT = /:\d+$/ + HEADERS_KLASS = Rack.release < "3" ? Utils::HeaderHash : Headers + private_constant :HEADERS_KLASS + def initialize(app) @app = app end def call(env) status, headers, body = @app.call(env) - headers = Utils::HeaderHash.new(headers) + headers = HEADERS_KLASS.new.merge(headers) host = env['HTTP_HOST'].sub PORT, '' share_cookie(headers, host) diff --git a/lib/rack/contrib/csshttprequest.rb b/lib/rack/contrib/csshttprequest.rb index 1d76f05d..06c50b57 100644 --- a/lib/rack/contrib/csshttprequest.rb +++ b/lib/rack/contrib/csshttprequest.rb @@ -6,6 +6,8 @@ module Rack # A Rack middleware for providing CSSHTTPRequest responses. class CSSHTTPRequest + HEADERS_KLASS = Rack.release < "3" ? Utils::HeaderHash : Headers + private_constant :HEADERS_KLASS def initialize(app) @app = app @@ -15,7 +17,7 @@ def initialize(app) # the CSSHTTPRequest encoder def call(env) status, headers, response = @app.call(env) - headers = Utils::HeaderHash.new(headers) + headers = HEADERS_KLASS.new.merge(headers) if chr_request?(env) encoded_response = encode(response) diff --git a/lib/rack/contrib/deflect.rb b/lib/rack/contrib/deflect.rb index 3786ec25..4d9b5e11 100644 --- a/lib/rack/contrib/deflect.rb +++ b/lib/rack/contrib/deflect.rb @@ -65,7 +65,7 @@ def call env end def deflect! - [403, { 'Content-Type' => 'text/html', 'Content-Length' => '0' }, []] + [403, { 'content-type' => 'text/html', 'content-length' => '0' }, []] end def deflect? env diff --git a/lib/rack/contrib/enforce_valid_encoding.rb b/lib/rack/contrib/enforce_valid_encoding.rb index 19e54bcf..6380275c 100644 --- a/lib/rack/contrib/enforce_valid_encoding.rb +++ b/lib/rack/contrib/enforce_valid_encoding.rb @@ -18,7 +18,7 @@ def call env Rack::Utils.unescape(full_path).valid_encoding? @app.call env else - [400, {'Content-Type'=>'text/plain'}, ['Bad Request']] + [400, {'content-type'=>'text/plain'}, ['Bad Request']] end end end diff --git a/lib/rack/contrib/expectation_cascade.rb b/lib/rack/contrib/expectation_cascade.rb index 8f678bf2..9896ee78 100644 --- a/lib/rack/contrib/expectation_cascade.rb +++ b/lib/rack/contrib/expectation_cascade.rb @@ -5,8 +5,8 @@ class ExpectationCascade Expect = "HTTP_EXPECT".freeze ContinueExpectation = "100-continue".freeze - ExpectationFailed = [417, {"Content-Type" => "text/html"}, []].freeze - NotFound = [404, {"Content-Type" => "text/html"}, []].freeze + ExpectationFailed = [417, {"content-type" => "text/html"}, []] + NotFound = [404, {"content-type" => "text/html"}, []] attr_reader :apps diff --git a/lib/rack/contrib/host_meta.rb b/lib/rack/contrib/host_meta.rb index 5a1cbbe0..26df2476 100644 --- a/lib/rack/contrib/host_meta.rb +++ b/lib/rack/contrib/host_meta.rb @@ -30,7 +30,7 @@ def initialize(app, &block) def call(env) if env['PATH_INFO'] == '/host-meta' - [200, {'Content-Type' => 'application/host-meta'}, [@response]] + [200, {'content-type' => 'application/host-meta'}, [@response]] else @app.call(env) end diff --git a/lib/rack/contrib/json_body_parser.rb b/lib/rack/contrib/json_body_parser.rb index 77023a2b..9ee29ef6 100644 --- a/lib/rack/contrib/json_body_parser.rb +++ b/lib/rack/contrib/json_body_parser.rb @@ -14,10 +14,10 @@ module Rack # === Parse POST and GET requests only # use Rack::JSONBodyParser, verbs: ['POST', 'GET'] # - # === Parse POST|PATCH|PUT requests whose Content-Type matches 'json' + # === Parse POST|PATCH|PUT requests whose content-type matches 'json' # use Rack::JSONBodyParser, media: /json/ # - # === Parse POST requests whose Content-Type is 'application/json' or 'application/vnd+json' + # === Parse POST requests whose content-type is 'application/json' or 'application/vnd+json' # use Rack::JSONBodyParser, verbs: ['POST'], media: ['application/json', 'application/vnd.api+json'] # class JSONBodyParser @@ -63,7 +63,7 @@ def call(env) end rescue JSON::ParserError body = { error: 'Failed to parse body as JSON' }.to_json - header = { 'Content-Type' => 'application/json' } + header = { 'content-type' => 'application/json' } return Rack::Response.new(body, 400, header).finish end @app.call(env) @@ -75,7 +75,7 @@ def update_form_hash_with_json_body(env) body = env[Rack::RACK_INPUT] return unless (body_content = body.read) && !body_content.empty? - body.rewind # somebody might try to read this stream + body.rewind if body.respond_to?(:rewind) # somebody might try to read this stream env.update( Rack::RACK_REQUEST_FORM_HASH => @parser.call(body_content), Rack::RACK_REQUEST_FORM_INPUT => body diff --git a/lib/rack/contrib/jsonp.rb b/lib/rack/contrib/jsonp.rb index f0800972..9c75942c 100644 --- a/lib/rack/contrib/jsonp.rb +++ b/lib/rack/contrib/jsonp.rb @@ -23,6 +23,9 @@ class JSONP # "\342\200\251" # => "\u2029" U2028, U2029 = ("\u2028" == 'u2028') ? ["\342\200\250", "\342\200\251"] : ["\u2028", "\u2029"] + HEADERS_KLASS = Rack.release < "3" ? Utils::HeaderHash : Headers + private_constant :HEADERS_KLASS + def initialize(app) @app = app end @@ -42,7 +45,7 @@ def call(env) return status, headers, response end - headers = HeaderHash.new(headers) + headers = HEADERS_KLASS.new.merge(headers) if is_json?(headers) && has_callback?(request) callback = request.params['callback'] @@ -108,7 +111,7 @@ def pad(callback, response) end def bad_request(body = "Bad Request") - [ 400, { 'Content-Type' => 'text/plain', 'Content-Length' => body.bytesize.to_s }, [body] ] + [ 400, { 'content-type' => 'text/plain', 'content-length' => body.bytesize.to_s }, [body] ] end end diff --git a/lib/rack/contrib/lazy_conditional_get.rb b/lib/rack/contrib/lazy_conditional_get.rb index d7c6a90a..0472f0f6 100644 --- a/lib/rack/contrib/lazy_conditional_get.rb +++ b/lib/rack/contrib/lazy_conditional_get.rb @@ -74,14 +74,14 @@ def initialize app, cache={} def call env if reading? env and fresh? env - return [304, {'Last-Modified' => env['HTTP_IF_MODIFIED_SINCE']}, []] + return [304, {'last-modified' => env['HTTP_IF_MODIFIED_SINCE']}, []] end status, headers, body = @app.call env - headers = Utils::HeaderHash.new(headers) + headers = Rack.release < "3" ? Utils::HeaderHash.new(headers) : headers update_cache unless (reading?(env) or skipping?(headers)) - headers['Last-Modified'] = cached_value if stampable? headers + headers['last-modified'] = cached_value if stampable? headers [status, headers, body] end @@ -96,11 +96,11 @@ def reading? env end def skipping? headers - headers['Rack-Lazy-Conditional-Get'] == 'skip' + headers['rack-lazy-conditional-get'] == 'skip' end def stampable? headers - !headers.has_key?('Last-Modified') and headers['Rack-Lazy-Conditional-Get'] == 'yes' + !headers.has_key?('last-modified') and headers['rack-lazy-conditional-get'] == 'yes' end def update_cache diff --git a/lib/rack/contrib/locale.rb b/lib/rack/contrib/locale.rb index ea1344e4..167bf907 100644 --- a/lib/rack/contrib/locale.rb +++ b/lib/rack/contrib/locale.rb @@ -4,6 +4,9 @@ module Rack class Locale + HEADERS_KLASS = Rack.release < "3" ? Utils::HeaderHash : Headers + private_constant :HEADERS_KLASS + def initialize(app) @app = app end @@ -16,7 +19,7 @@ def call(env) env['rack.locale'] = I18n.locale = locale.to_s status, headers, body = @app.call(env) - headers = Utils::HeaderHash.new(headers) + headers = HEADERS_KLASS.new.merge(headers) unless headers['Content-Language'] headers['Content-Language'] = locale.to_s diff --git a/lib/rack/contrib/mailexceptions.rb b/lib/rack/contrib/mailexceptions.rb index b72e7bfc..69bb5417 100644 --- a/lib/rack/contrib/mailexceptions.rb +++ b/lib/rack/contrib/mailexceptions.rb @@ -106,7 +106,7 @@ def send_notification(exception, env) def extract_body(env) if io = env['rack.input'] - io.rewind + io.rewind if io.respond_to?(:rewind) io.read end end diff --git a/lib/rack/contrib/not_found.rb b/lib/rack/contrib/not_found.rb index 11d24cd4..64ae293c 100644 --- a/lib/rack/contrib/not_found.rb +++ b/lib/rack/contrib/not_found.rb @@ -27,7 +27,7 @@ def initialize(path = nil, content_type = 'text/html') end def call(env) - [404, {'Content-Type' => @content_type, 'Content-Length' => @length}, [@content]] + [404, {'content-type' => @content_type, 'content-length' => @length}, [@content]] end end end diff --git a/lib/rack/contrib/post_body_content_type_parser.rb b/lib/rack/contrib/post_body_content_type_parser.rb index 6a8b3c3e..0ce71a90 100644 --- a/lib/rack/contrib/post_body_content_type_parser.rb +++ b/lib/rack/contrib/post_body_content_type_parser.rb @@ -75,7 +75,7 @@ def initialize(app, &block) def call(env) if Rack::Request.new(env).media_type == APPLICATION_JSON && (body = env[POST_BODY].read).length != 0 - env[POST_BODY].rewind # somebody might try to read this stream + env[POST_BODY].rewind if env[POST_BODY].respond_to?(:rewind) # somebody might try to read this stream env.update(FORM_HASH => @block.call(body), FORM_INPUT => env[POST_BODY]) end @app.call(env) @@ -84,7 +84,7 @@ def call(env) end def bad_request(body = 'Bad Request') - [ 400, { 'Content-Type' => 'text/plain', 'Content-Length' => body.bytesize.to_s }, [body] ] + [ 400, { 'content-type' => 'text/plain', 'content-length' => body.bytesize.to_s }, [body] ] end end end diff --git a/lib/rack/contrib/profiler.rb b/lib/rack/contrib/profiler.rb index 776d5169..9367720d 100644 --- a/lib/rack/contrib/profiler.rb +++ b/lib/rack/contrib/profiler.rb @@ -83,10 +83,10 @@ def print(printer, result) end def headers(printer, env, mode) - headers = { 'Content-Type' => CONTENT_TYPES[printer.name] } + headers = { 'content-type' => CONTENT_TYPES[printer.name] } if printer == ::RubyProf::CallTreePrinter filename = ::File.basename(env['PATH_INFO']) - headers['Content-Disposition'] = + headers['content-disposition'] = %(attachment; filename="#{filename}.#{mode}.tree") end headers diff --git a/lib/rack/contrib/relative_redirect.rb b/lib/rack/contrib/relative_redirect.rb index 67c564b9..12d86abc 100644 --- a/lib/rack/contrib/relative_redirect.rb +++ b/lib/rack/contrib/relative_redirect.rb @@ -33,7 +33,8 @@ def initialize(app, &block) # does not start with a slash, make location relative to the path requested. def call(env) status, headers, body = @app.call(env) - headers = Rack::Utils::HeaderHash.new(headers) + headers_klass = Rack.release < "3" ? Rack::Utils::HeaderHash : Rack::Headers + headers = headers_klass.new.merge(headers) if [301,302,303, 307,308].include?(status) and loc = headers['Location'] and !%r{\Ahttps?://}o.match(loc) absolute = @absolute_proc.call(env, [status, headers, body]) diff --git a/lib/rack/contrib/response_cache.rb b/lib/rack/contrib/response_cache.rb index 2b966e76..1923b64d 100644 --- a/lib/rack/contrib/response_cache.rb +++ b/lib/rack/contrib/response_cache.rb @@ -53,7 +53,8 @@ def initialize(app, cache, &block) # subdirectory of cache. Otherwise, cache the body of the response as the value with the path as the key. def call(env) status, headers, body = @app.call(env) - headers = Rack::Utils::HeaderHash.new(headers) + headers_klass = Rack.release < "3" ? Rack::Utils::HeaderHash : Rack::Headers + headers = headers_klass.new.merge(headers) if env['REQUEST_METHOD'] == 'GET' and env['QUERY_STRING'] == '' and status == 200 and path = @path_proc.call(env, [status, headers, body]) if @cache.is_a?(String) diff --git a/lib/rack/contrib/response_headers.rb b/lib/rack/contrib/response_headers.rb index e634c4ba..4706a127 100644 --- a/lib/rack/contrib/response_headers.rb +++ b/lib/rack/contrib/response_headers.rb @@ -2,7 +2,8 @@ module Rack # Allows you to tap into the response headers. Yields a Rack::Utils::HeaderHash - # of current response headers to the block. Example: + # (Rack 2) or a Rack::Headers (Rack 3) of current response headers to the block. + # Example: # # use Rack::ResponseHeaders do |headers| # headers['X-Foo'] = 'bar' @@ -10,6 +11,9 @@ module Rack # end # class ResponseHeaders + HEADERS_KLASS = Rack.release < "3" ? Utils::HeaderHash : Headers + private_constant :HEADERS_KLASS + def initialize(app, &block) @app = app @block = block @@ -17,7 +21,7 @@ def initialize(app, &block) def call(env) response = @app.call(env) - headers = Utils::HeaderHash.new(response[1]) + headers = HEADERS_KLASS.new.merge(response[1]) @block.call(headers) response[1] = headers response diff --git a/lib/rack/contrib/static_cache.rb b/lib/rack/contrib/static_cache.rb index 027af4f5..beddd01a 100644 --- a/lib/rack/contrib/static_cache.rb +++ b/lib/rack/contrib/static_cache.rb @@ -52,6 +52,8 @@ module Rack class StaticCache + HEADERS_KLASS = Rack.release < "3" ? Utils::HeaderHash : Headers + private_constant :HEADERS_KLASS def initialize(app, options={}) @app = app @@ -67,7 +69,7 @@ def initialize(app, options={}) end end root = options[:root] || Dir.pwd - @file_server = Rack::File.new(root) + @file_server = Rack::Files.new(root) @cache_duration = options[:duration] || 1 @versioning_enabled = options.fetch(:versioning, true) if @versioning_enabled @@ -87,7 +89,7 @@ def call(env) end status, headers, body = @file_server.call(env) - headers = Utils::HeaderHash.new(headers) + headers = HEADERS_KLASS.new.merge(headers) if @no_cache[url].nil? headers['Cache-Control'] ="max-age=#{@duration_in_seconds}, public" diff --git a/rack-contrib.gemspec b/rack-contrib.gemspec index d554b3c7..0fa048b6 100644 --- a/rack-contrib.gemspec +++ b/rack-contrib.gemspec @@ -35,7 +35,7 @@ Gem::Specification.new do |s| s.required_ruby_version = '>= 2.2.2' - s.add_runtime_dependency 'rack', '~> 2.0' + s.add_runtime_dependency 'rack', '< 4' s.homepage = "https://github.com/rack/rack-contrib/" s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "rack-contrib", "--main", "README"] diff --git a/test/spec_rack_access.rb b/test/spec_rack_access.rb index 698e831e..3c33e35a 100644 --- a/test/spec_rack_access.rb +++ b/test/spec_rack_access.rb @@ -7,7 +7,7 @@ describe "Rack::Access" do before do - @app = lambda { |env| [200, { 'Content-Type' => 'text/plain' }, ['hello']] } + @app = lambda { |env| [200, { 'content-type' => 'text/plain' }, ['hello']] } @mock_addr_1 = '111.111.111.111' @mock_addr_2 = '192.168.1.222' @mock_addr_localhost = '127.0.0.1' diff --git a/test/spec_rack_backstage.rb b/test/spec_rack_backstage.rb index 7ca137e2..c741b8a0 100644 --- a/test/spec_rack_backstage.rb +++ b/test/spec_rack_backstage.rb @@ -10,7 +10,7 @@ app = Rack::Lint.new( Rack::Builder.new do use Rack::Backstage, 'test/Maintenance.html' - run lambda { |env| [200, {'Content-Type' => 'text/plain'}, ["Hello, World!"]] } + run lambda { |env| [200, {'content-type' => 'text/plain'}, ["Hello, World!"]] } end ) response = Rack::MockRequest.new(app).get('/') @@ -22,7 +22,7 @@ app = Rack::Lint.new( Rack::Builder.new do use Rack::Backstage, 'test/Nonsense.html' - run lambda { |env| [200, {'Content-Type' => 'text/plain'}, ["Hello, World!"]] } + run lambda { |env| [200, {'content-type' => 'text/plain'}, ["Hello, World!"]] } end ) response = Rack::MockRequest.new(app).get('/') diff --git a/test/spec_rack_bounce_favicon.rb b/test/spec_rack_bounce_favicon.rb index f792caa4..7c1cdc34 100644 --- a/test/spec_rack_bounce_favicon.rb +++ b/test/spec_rack_bounce_favicon.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true require 'minitest/autorun' -require 'rack/mock' +require 'rack' require 'rack/contrib/bounce_favicon' describe Rack::BounceFavicon do diff --git a/test/spec_rack_common_cookies.rb b/test/spec_rack_common_cookies.rb index df7b89cd..5d080876 100644 --- a/test/spec_rack_common_cookies.rb +++ b/test/spec_rack_common_cookies.rb @@ -87,7 +87,12 @@ def make_request(domain, cookies='key=value') specify 'should work with multiple cookies' do response = make_request 'sub.domain.bz', "key=value\nkey1=value2" _(response.headers['Set-Cookie']).must_equal "key=value; domain=.domain.bz\nkey1=value2; domain=.domain.bz" - end + end if Rack.release < "3" + + specify 'should not work with multiple cookies' do + error = _ { make_request 'sub.domain.bz', "key=value\nkey1=value2" }.must_raise(Rack::Lint::LintError) + _(error.message).must_match(/invalid header value/) + end if Rack.release > "3" specify 'should work with cookies which have explicit domain' do response = make_request 'sub.domain.bz', "key=value; domain=domain.bz" diff --git a/test/spec_rack_config.rb b/test/spec_rack_config.rb index b19d0090..71723f93 100644 --- a/test/spec_rack_config.rb +++ b/test/spec_rack_config.rb @@ -14,7 +14,7 @@ env['greeting'] = 'hello' end run lambda { |env| - [200, {'Content-Type' => 'text/plain'}, [env['greeting'] || '']] + [200, {'content-type' => 'text/plain'}, [env['greeting'] || '']] } end response = Rack::MockRequest.new(app).get('/') diff --git a/test/spec_rack_cookies.rb b/test/spec_rack_cookies.rb index afa41bb0..b3369647 100644 --- a/test/spec_rack_cookies.rb +++ b/test/spec_rack_cookies.rb @@ -33,7 +33,11 @@ def cookies(app) ) response = Rack::MockRequest.new(app).get('/') - _(response.headers['Set-Cookie'].split("\n").sort).must_equal(["foo=bar; path=/","quux=h%26m; path=/"]) + if Rack.release < "3" + _(response.headers['Set-Cookie'].split("\n").sort).must_equal(["foo=bar; path=/","quux=h%26m; path=/"]) + else + _(response.headers['Set-Cookie'].sort).must_equal(["foo=bar; path=/","quux=h%26m; path=/"]) + end end specify "should be able to set cookie with options" do diff --git a/test/spec_rack_csshttprequest.rb b/test/spec_rack_csshttprequest.rb index 3f9f742f..c3f78271 100644 --- a/test/spec_rack_csshttprequest.rb +++ b/test/spec_rack_csshttprequest.rb @@ -14,7 +14,11 @@ def css_httl_request(app) before(:each) do @test_body = '{"bar":"foo"}' - @test_headers = {'Content-Type' => 'text/plain'} + @test_headers = if Rack.release < "3" + {'Content-Type' => 'text/plain'} + else + {'content-type' => 'text/plain'} + end @encoded_body = CSSHTTPRequest.encode(@test_body) @app = lambda { |env| [200, @test_headers, [@test_body]] } end @@ -63,10 +67,17 @@ def css_httl_request(app) specify "should not modify any other headers" do headers = css_httl_request(@app).call(@request)[1] - _(headers).must_equal @test_headers.merge({ - 'Content-Type' => 'text/css', - 'Content-Length' => @encoded_body.length.to_s - }) + if Rack.release < "3" + _(headers).must_equal @test_headers.merge({ + 'Content-Type' => 'text/css', + 'Content-Length' => @encoded_body.length.to_s + }) + else + _(headers).must_equal @test_headers.merge({ + 'content-type' => 'text/css', + 'content-length' => @encoded_body.length.to_s + }) + end end end diff --git a/test/spec_rack_deflect.rb b/test/spec_rack_deflect.rb index 2c40530b..fb70da34 100644 --- a/test/spec_rack_deflect.rb +++ b/test/spec_rack_deflect.rb @@ -8,7 +8,7 @@ describe "Rack::Deflect" do before do - @app = lambda { |env| [200, { 'Content-Type' => 'text/plain' }, ['cookies']] } + @app = lambda { |env| [200, { 'content-type' => 'text/plain' }, ['cookies']] } @mock_addr_1 = '111.111.111.111' @mock_addr_2 = '222.222.222.222' @mock_addr_3 = '333.333.333.333' @@ -38,7 +38,11 @@ def mock_deflect options = {} 5.times do status, headers, body = app.call env _(status).must_equal 200 - _(body.to_enum.to_a).must_equal ['cookies'] + if Rack.release < "3" + _(body.to_enum.to_a).must_equal ['cookies'] + else + _(body.to_ary).must_equal ['cookies'] + end end # Remaining requests should fail for 10 seconds @@ -61,7 +65,11 @@ def mock_deflect options = {} 5.times do status, headers, body = app.call env _(status).must_equal 200 - _(body.to_enum.to_a).must_equal ['cookies'] + if Rack.release < "3" + _(body.to_enum.to_a).must_equal ['cookies'] + else + _(body.to_ary).must_equal ['cookies'] + end end # Exceeds request threshold @@ -91,7 +99,11 @@ def mock_deflect options = {} 10.times do status, headers, body = app.call env _(status).must_equal 200 - _(body.to_enum.to_a).must_equal ['cookies'] + if Rack.release < "3" + _(body.to_enum.to_a).must_equal ['cookies'] + else + _(body.to_ary).must_equal ['cookies'] + end end end diff --git a/test/spec_rack_enforce_valid_encoding.rb b/test/spec_rack_enforce_valid_encoding.rb index e2e83643..d25fad96 100644 --- a/test/spec_rack_enforce_valid_encoding.rb +++ b/test/spec_rack_enforce_valid_encoding.rb @@ -16,7 +16,7 @@ @app = Rack::Lint.new( Rack::EnforceValidEncoding.new( lambda do |env| - [200, {'Content-Type'=>'text/plain'}, ['Hello World']] + [200, {'content-type'=>'text/plain'}, ['Hello World']] end ) ) diff --git a/test/spec_rack_evil.rb b/test/spec_rack_evil.rb index cc3e2161..c8194ec8 100644 --- a/test/spec_rack_evil.rb +++ b/test/spec_rack_evil.rb @@ -7,8 +7,8 @@ describe "Rack::Evil" do app = lambda do |env| - template = ERB.new("<%= throw :response, [404, {'Content-Type' => 'text/html'}, ['Never know where it comes from']] %>") - [200, {'Content-Type' => 'text/plain'}, template.result(binding)] + template = ERB.new("<%= throw :response, [404, {'content-type' => 'text/html'}, ['Never know where it comes from']] %>") + [200, {'content-type' => 'text/plain'}, template.result(binding)] end env = Rack::MockRequest.env_for('', {}) @@ -17,7 +17,7 @@ status, headers, body = Rack::Lint.new(Rack::Evil.new(app)).call(env) _(status).must_equal 404 - _(headers['Content-Type']).must_equal 'text/html' + _(headers['content-type']).must_equal 'text/html' _(body.to_enum.to_a).must_equal ['Never know where it comes from'] end end diff --git a/test/spec_rack_expectation_cascade.rb b/test/spec_rack_expectation_cascade.rb index fc6e3041..9c13b54d 100644 --- a/test/spec_rack_expectation_cascade.rb +++ b/test/spec_rack_expectation_cascade.rb @@ -27,8 +27,8 @@ def expectation_cascade(&block) specify "returns first successful response" do app = expectation_cascade do |cascade| - cascade << lambda { |env| [417, {"Content-Type" => "text/plain"}, []] } - cascade << lambda { |env| [200, {"Content-Type" => "text/plain"}, ["OK"]] } + cascade << lambda { |env| [417, {"content-type" => "text/plain"}, []] } + cascade << lambda { |env| [200, {"content-type" => "text/plain"}, ["OK"]] } end env = Rack::MockRequest.env_for response = app.call(env) @@ -38,7 +38,7 @@ def expectation_cascade(&block) specify "expectation is set if it has not been already" do app = expectation_cascade do |cascade| - cascade << lambda { |env| [200, {"Content-Type" => "text/plain"}, ["Expect: #{env["HTTP_EXPECT"]}"]] } + cascade << lambda { |env| [200, {"content-type" => "text/plain"}, ["Expect: #{env["HTTP_EXPECT"]}"]] } end env = Rack::MockRequest.env_for response = app.call(env) @@ -48,7 +48,7 @@ def expectation_cascade(&block) specify "returns a 404 if no apps where matched and no expectation header was set" do app = expectation_cascade do |cascade| - cascade << lambda { |env| [417, {"Content-Type" => "text/plain"}, []] } + cascade << lambda { |env| [417, {"content-type" => "text/plain"}, []] } end env = Rack::MockRequest.env_for response = app.call(env) @@ -58,7 +58,7 @@ def expectation_cascade(&block) specify "returns a 417 if no apps where matched and a expectation header was set" do app = expectation_cascade do |cascade| - cascade << lambda { |env| [417, {"Content-Type" => "text/plain"}, []] } + cascade << lambda { |env| [417, {"content-type" => "text/plain"}, []] } end env = Rack::MockRequest.env_for('', "HTTP_EXPECT" => "100-continue") response = app.call(env) @@ -69,10 +69,10 @@ def expectation_cascade(&block) specify "nests expectation cascades" do app = expectation_cascade do |c1| c1 << expectation_cascade do |c2| - c2 << lambda { |env| [417, {"Content-Type" => "text/plain"}, []] } + c2 << lambda { |env| [417, {"content-type" => "text/plain"}, []] } end c1 << expectation_cascade do |c2| - c2 << lambda { |env| [200, {"Content-Type" => "text/plain"}, ["OK"]] } + c2 << lambda { |env| [200, {"content-type" => "text/plain"}, ["OK"]] } end end env = Rack::MockRequest.env_for diff --git a/test/spec_rack_jsonp.rb b/test/spec_rack_jsonp.rb index 3924d417..b8218cc3 100644 --- a/test/spec_rack_jsonp.rb +++ b/test/spec_rack_jsonp.rb @@ -19,7 +19,7 @@ def normalize_response(response) specify "should wrap the response body in the Javascript callback if JSON" do test_body = '{"bar":"foo"}' callback = 'foo' - app = lambda { |env| [200, {'Content-Type' => 'application/json'}, [test_body]] } + app = lambda { |env| [200, {'content-type' => 'application/json'}, [test_body]] } request = Rack::MockRequest.env_for("/", :params => "foo=bar&callback=#{callback}") body = jsonp(app).call(request).last _(body.to_enum.to_a).must_equal ["/**/#{callback}(#{test_body})"] @@ -28,7 +28,7 @@ def normalize_response(response) specify "should not wrap the response body in a callback if body is not JSON" do test_body = '{"bar":"foo"}' callback = 'foo' - app = lambda { |env| [200, {'Content-Type' => 'text/plain'}, [test_body]] } + app = lambda { |env| [200, {'content-type' => 'text/plain'}, [test_body]] } request = Rack::MockRequest.env_for("/", :params => "foo=bar&callback=#{callback}") body = jsonp(app).call(request).last _(body.to_enum.to_a).must_equal ['{"bar":"foo"}'] @@ -37,7 +37,7 @@ def normalize_response(response) specify "should update content length if it was set" do test_body = '{"bar":"foo"}' callback = 'foo' - app = lambda { |env| [200, {'Content-Type' => 'application/json', 'Content-Length' => test_body.length}, [test_body]] } + app = lambda { |env| [200, {'content-type' => 'application/json', 'Content-Length' => test_body.length}, [test_body]] } request = Rack::MockRequest.env_for("/", :params => "foo=bar&callback=#{callback}") headers = jsonp(app).call(request)[1] @@ -48,7 +48,7 @@ def normalize_response(response) specify "should not touch content length if not set" do test_body = '{"bar":"foo"}' callback = 'foo' - app = lambda { |env| [200, {'Content-Type' => 'application/json'}, [test_body]] } + app = lambda { |env| [200, {'content-type' => 'application/json'}, [test_body]] } request = Rack::MockRequest.env_for("/", :params => "foo=bar&callback=#{callback}") headers = jsonp(app).call(request)[1] _(headers['Content-Length']).must_be_nil @@ -57,10 +57,10 @@ def normalize_response(response) specify "should modify the content type to application/javascript" do test_body = '{"bar":"foo"}' callback = 'foo' - app = lambda { |env| [200, {'Content-Type' => 'application/json'}, [test_body]] } + app = lambda { |env| [200, {'content-type' => 'application/json'}, [test_body]] } request = Rack::MockRequest.env_for("/", :params => "foo=bar&callback=#{callback}") headers = jsonp(app).call(request)[1] - _(headers['Content-Type']).must_equal('application/javascript') + _(headers['content-type']).must_equal('application/javascript') end specify "should not allow literal U+2028 or U+2029" do @@ -70,7 +70,7 @@ def normalize_response(response) "{\"bar\":\"\342\200\250 and \342\200\251\"}" end callback = 'foo' - app = lambda { |env| [200, {'Content-Type' => 'application/json'}, [test_body]] } + app = lambda { |env| [200, {'content-type' => 'application/json'}, [test_body]] } request = Rack::MockRequest.env_for("/", :params => "foo=bar&callback=#{callback}") body = jsonp(app).call(request).last unless "\u2028" == 'u2028' @@ -84,7 +84,7 @@ def normalize_response(response) specify "with assignment" do test_body = '{"bar":"foo"}' callback = '' - app = lambda { |env| [200, {'Content-Type' => 'application/json'}, [test_body]] } + app = lambda { |env| [200, {'content-type' => 'application/json'}, [test_body]] } request = Rack::MockRequest.env_for("/", :params => "foo=bar&callback=#{callback}") body = jsonp(app).call(request).last _(body.to_enum.to_a).must_equal ['{"bar":"foo"}'] @@ -92,7 +92,7 @@ def normalize_response(response) specify "without assignment" do test_body = '{"bar":"foo"}' - app = lambda { |env| [200, {'Content-Type' => 'application/json'}, [test_body]] } + app = lambda { |env| [200, {'content-type' => 'application/json'}, [test_body]] } request = Rack::MockRequest.env_for("/", :params => "foo=bar&callback") body = jsonp(app).call(request).last _(body.to_enum.to_a).must_equal ['{"bar":"foo"}'] @@ -105,7 +105,7 @@ def normalize_response(response) test_body = '{"bar":"foo"}' callback = '*' content_type = 'application/json' - app = lambda { |env| [200, {'Content-Type' => content_type}, [test_body]] } + app = lambda { |env| [200, {'content-type' => content_type}, [test_body]] } request = Rack::MockRequest.env_for("/", :params => "foo=bar&callback=#{callback}") body = jsonp(app).call(request).last _(body.to_enum.to_a).must_equal ['Bad Request'] @@ -115,7 +115,7 @@ def normalize_response(response) test_body = '{"bar":"foo"}' callback = '*' content_type = 'application/json' - app = lambda { |env| [200, {'Content-Type' => content_type}, [test_body]] } + app = lambda { |env| [200, {'content-type' => content_type}, [test_body]] } request = Rack::MockRequest.env_for("/", :params => "foo=bar&callback=#{callback}") response_code = jsonp(app).call(request).first _(response_code).must_equal 400 @@ -127,7 +127,7 @@ def normalize_response(response) test_body = 'Good Request' callback = '*' content_type = 'text/plain' - app = lambda { |env| [200, {'Content-Type' => content_type}, [test_body]] } + app = lambda { |env| [200, {'content-type' => content_type}, [test_body]] } request = Rack::MockRequest.env_for("/", :params => "foo=bar&callback=#{callback}") body = jsonp(app).call(request).last _(body.to_enum.to_a).must_equal ['Good Request'] @@ -137,7 +137,7 @@ def normalize_response(response) test_body = '{"bar":"foo"}' callback = '*' content_type = 'text/plain' - app = lambda { |env| [200, {'Content-Type' => content_type}, [test_body]] } + app = lambda { |env| [200, {'content-type' => content_type}, [test_body]] } request = Rack::MockRequest.env_for("/", :params => "foo=bar&callback=#{callback}") response_code = jsonp(app).call(request).first _(response_code).must_equal 200 @@ -147,7 +147,7 @@ def normalize_response(response) describe "with XSS vulnerability attempts" do def request(callback, body = '{"bar":"foo"}') - app = lambda { |env| [200, {'Content-Type' => 'application/json'}, [body]] } + app = lambda { |env| [200, {'content-type' => 'application/json'}, [body]] } request = Rack::MockRequest.env_for("/", :params => "foo=bar&callback=#{callback}") jsonp(app).call(request) end @@ -182,7 +182,7 @@ def assert_bad_request(response) specify "should not change anything if no callback param is provided" do test_body = ['{"bar":"foo"}'] - app = lambda { |env| [200, {'Content-Type' => 'application/json'}, test_body] } + app = lambda { |env| [200, {'content-type' => 'application/json'}, test_body] } request = Rack::MockRequest.env_for("/", :params => "foo=bar") body = jsonp(app).call(request).last _(body.to_enum.to_a).must_equal test_body @@ -190,13 +190,13 @@ def assert_bad_request(response) specify "should not change anything if it's not a json response" do test_body = '404 Not Found' - app = lambda { |env| [404, {'Content-Type' => 'text/html'}, [test_body]] } + app = lambda { |env| [404, {'content-type' => 'text/html'}, [test_body]] } request = Rack::MockRequest.env_for("/", :params => "callback=foo", 'HTTP_ACCEPT' => 'application/json') body = jsonp(app).call(request).last _(body.to_enum.to_a).must_equal [test_body] end - specify "should not change anything if there is no Content-Type header" do + specify "should not change anything if there is no content-type header" do test_body = '404 Not Found' app = lambda { |env| [404, {}, [test_body]] } request = Rack::MockRequest.env_for("/", :params => "callback=foo", 'HTTP_ACCEPT' => 'application/json') diff --git a/test/spec_rack_lazy_conditional_get.rb b/test/spec_rack_lazy_conditional_get.rb index 852866f8..6f97af97 100644 --- a/test/spec_rack_lazy_conditional_get.rb +++ b/test/spec_rack_lazy_conditional_get.rb @@ -22,11 +22,11 @@ def set_global_last_modified val let(:response_headers) { headers = { - 'Content-Type' => 'text/plain', - 'Rack-Lazy-Conditional-Get' => rack_lazy_conditional_get + 'content-type' => 'text/plain', + 'rack-lazy-conditional-get' => rack_lazy_conditional_get } if response_with_last_modified - headers.merge!({'Last-Modified' => (Time.now-3600).httpdate}) + headers.merge!({'last-modified' => (Time.now-3600).httpdate}) end headers } @@ -57,24 +57,24 @@ def set_global_last_modified val before { @myapp = app } - describe 'When the resource has Rack-Lazy-Conditional-Get' do + describe 'When the resource has rack-lazy-conditional-get' do it 'Should set right headers' do status, headers, body = @myapp.call(env) value(status).must_equal 200 - value(headers['Rack-Lazy-Conditional-Get']).must_equal 'yes' - value(headers['Last-Modified']).must_equal global_last_modified + value(headers['rack-lazy-conditional-get']).must_equal 'yes' + value(headers['last-modified']).must_equal global_last_modified end - describe 'When the resource already has a Last-Modified header' do + describe 'When the resource already has a last-modified header' do let(:response_with_last_modified) { true } - it 'Does not update Last-Modified with the global one' do + it 'Does not update last-modified with the global one' do status, headers, body = @myapp.call(env) value(status).must_equal 200 - value(headers['Rack-Lazy-Conditional-Get']).must_equal 'yes' - value(headers['Last-Modified']).wont_equal global_last_modified + value(headers['rack-lazy-conditional-get']).must_equal 'yes' + value(headers['last-modified']).wont_equal global_last_modified end end @@ -112,7 +112,7 @@ def set_global_last_modified val set_global_last_modified (Time.now-3600).httpdate stamp = global_last_modified status, headers, body = @myapp.call(env) - value(headers['Rack-Lazy-Conditional-Get']).must_equal 'skip' + value(headers['rack-lazy-conditional-get']).must_equal 'skip' value(global_last_modified).must_equal stamp end @@ -120,15 +120,16 @@ def set_global_last_modified val end - describe 'When the ressource does not have Rack-Lazy-Conditional-Get' do + describe 'When the ressource does not have rack-lazy-conditional-get' do let(:rack_lazy_conditional_get) { 'no' } it 'Should set right headers' do status, headers, body = @myapp.call(env) + value(status).must_equal 200 - value(headers['Rack-Lazy-Conditional-Get']).must_equal 'no' - value(headers['Last-Modified']).must_be :nil? + value(headers['rack-lazy-conditional-get']).must_equal 'no' + value(headers['last-modified']).must_be :nil? end end diff --git a/test/spec_rack_lighttpd_script_name_fix.rb b/test/spec_rack_lighttpd_script_name_fix.rb index 400d47b0..7f1af97b 100644 --- a/test/spec_rack_lighttpd_script_name_fix.rb +++ b/test/spec_rack_lighttpd_script_name_fix.rb @@ -13,7 +13,7 @@ "SCRIPT_NAME" => "/hello" } ) - app = lambda { |_| [200, {'Content-Type' => 'text/plain'}, ["Hello, World!"]] } + app = lambda { |_| [200, {'content-type' => 'text/plain'}, ["Hello, World!"]] } response = Rack::Lint.new(Rack::LighttpdScriptNameFix.new(app)).call(env) _(env['SCRIPT_NAME'].empty?).must_equal(true) _(env['PATH_INFO']).must_equal '/hello/foo/bar/baz' diff --git a/test/spec_rack_nested_params.rb b/test/spec_rack_nested_params.rb index 53a022ef..7a6365f7 100644 --- a/test/spec_rack_nested_params.rb +++ b/test/spec_rack_nested_params.rb @@ -8,7 +8,7 @@ describe Rack::NestedParams do request_object = nil - App = lambda { |env| request_object = Rack::Request.new(env); [200, {'Content-Type' => 'text/plain'}, []] } + App = lambda { |env| request_object = Rack::Request.new(env); [200, {'content-type' => 'text/plain'}, []] } def env_for_post_with_headers(path, headers, body) Rack::MockRequest.env_for(path, {:method => "POST", :input => body}.merge(headers)) @@ -20,15 +20,16 @@ def form_post(params, content_type = 'application/x-www-form-urlencoded') end def middleware - Rack::Lint.new(Rack::NestedParams.new(App)) + # Rack::Lint can't be used because it does not rewind the body + Rack::NestedParams.new(App) end - specify "should handle requests with POST body Content-Type of application/x-www-form-urlencoded" do + specify "should handle requests with POST body content-type of application/x-www-form-urlencoded" do req = middleware.call(form_post({'foo[bar][baz]' => 'nested'})).last _(request_object.POST).must_equal({"foo" => { "bar" => { "baz" => "nested" }}}) end - specify "should not parse requests with other Content-Type" do + specify "should not parse requests with other content-type" do req = middleware.call(form_post({'foo[bar][baz]' => 'nested'}, 'text/plain')).last _(request_object.POST).must_equal({}) end diff --git a/test/spec_rack_post_body_content_type_parser.rb b/test/spec_rack_post_body_content_type_parser.rb index 651876cc..14fb9109 100644 --- a/test/spec_rack_post_body_content_type_parser.rb +++ b/test/spec_rack_post_body_content_type_parser.rb @@ -47,7 +47,7 @@ def assert_failed_to_parse_as_json(response) specify "should return bad request with invalid JSON" do test_body = '"bar":"foo"}' env = Rack::MockRequest.env_for "/", {:method => "POST", :input => test_body, "CONTENT_TYPE" => 'application/json'} - app = lambda { |env| [200, {'Content-Type' => 'text/plain'}, []] } + app = lambda { |env| [200, {'content-type' => 'text/plain'}, []] } response = Rack::Lint.new(Rack::PostBodyContentTypeParser.new(app)).call(env) assert_failed_to_parse_as_json(response) @@ -58,7 +58,7 @@ def assert_failed_to_parse_as_json(response) def params_for_request(body, content_type, &block) params = nil env = Rack::MockRequest.env_for "/", {:method => "POST", :input => body, "CONTENT_TYPE" => content_type} - app = lambda { |env| params = Rack::Request.new(env).POST; [200, {'Content-Type' => 'text/plain'}, []] } + app = lambda { |env| params = Rack::Request.new(env).POST; [200, {'content-type' => 'text/plain'}, []] } Rack::Lint.new(Rack::PostBodyContentTypeParser.new(app, &block)).call(env) params end diff --git a/test/spec_rack_proctitle.rb b/test/spec_rack_proctitle.rb index 96bc30f3..accfd3f0 100644 --- a/test/spec_rack_proctitle.rb +++ b/test/spec_rack_proctitle.rb @@ -13,7 +13,7 @@ def proc_title(app) end def simple_app(body=['Hello World!']) - lambda { |env| [200, {'Content-Type' => 'text/plain'}, body] } + lambda { |env| [200, {'content-type' => 'text/plain'}, body] } end specify "should set the process title when created" do diff --git a/test/spec_rack_profiler.rb b/test/spec_rack_profiler.rb index 4499435a..d64853d8 100644 --- a/test/spec_rack_profiler.rb +++ b/test/spec_rack_profiler.rb @@ -7,7 +7,7 @@ require 'rack/contrib/profiler' describe 'Rack::Profiler' do - app = lambda { |env| Time.new; [200, {'Content-Type' => 'text/plain'}, ['Oh hai der']] } + app = lambda { |env| Time.new; [200, {'content-type' => 'text/plain'}, ['Oh hai der']] } request = Rack::MockRequest.env_for("/", :params => "profile=process_time") def profiler(app, options = {}) @@ -27,21 +27,21 @@ def profiler(app, options = {}) _(body.to_enum.to_a.join).must_match(/\[#{runs} calls, #{runs} total\]/) end - specify 'CallStackPrinter has Content-Type test/html' do + specify 'CallStackPrinter has content-type test/html' do headers = profiler(app, :printer => :call_stack).call(request)[1] - _(headers).must_equal "Content-Type"=>"text/html" + _(headers).must_equal "content-type"=>"text/html" end - specify 'FlatPrinter and GraphPrinter has Content-Type text/plain' do + specify 'FlatPrinter and GraphPrinter has content-type text/plain' do %w(flat graph).each do |printer| headers = profiler(app, :printer => printer.to_sym).call(request)[1] - _(headers).must_equal "Content-Type"=>"text/plain" + _(headers).must_equal "content-type"=>"text/plain" end end - specify 'GraphHtmlPrinter has Content-Type text/html' do + specify 'GraphHtmlPrinter has content-type text/html' do headers = profiler(app, :printer => :graph_html).call(request)[1] - _(headers).must_equal "Content-Type"=>"text/html" + _(headers).must_equal "content-type"=>"text/html" end end diff --git a/test/spec_rack_response_cache.rb b/test/spec_rack_response_cache.rb index 3c3cb354..df9d32de 100644 --- a/test/spec_rack_response_cache.rb +++ b/test/spec_rack_response_cache.rb @@ -95,7 +95,11 @@ def request(opts={}, &block) _(e['PATH_INFO']).must_equal @def_path _(e['REQUEST_METHOD']).must_equal 'GET' _(e['QUERY_STRING']).must_equal '' - _(r).must_equal([200, {"Content-Type"=>"text/html"}, ["rack-response-cache"]]) + if Rack.release < "3" + _(r).must_equal([200, {"Content-Type"=>"text/html"}, ["rack-response-cache"]]) + else + _(r).must_equal([200, {"content-type"=>"text/html"}, ["rack-response-cache"]]) + end end specify "should unescape the path by default" do diff --git a/test/spec_rack_response_headers.rb b/test/spec_rack_response_headers.rb index 7a1613bd..5a717cdc 100644 --- a/test/spec_rack_response_headers.rb +++ b/test/spec_rack_response_headers.rb @@ -13,12 +13,17 @@ def env Rack::MockRequest.env_for('', {}) end - specify "yields a HeaderHash of response headers" do + specify "yields a HeaderHash (rack 2) or Headers (rack 3) of response headers" do orig_headers = {'X-Foo' => 'foo', 'X-Bar' => 'bar'} app = Proc.new {[200, orig_headers, []]} + headers_klass = Rack.release < "3" ? Rack::Utils::HeaderHash : Rack::Headers middleware = response_header(app) do |headers| - assert_instance_of Rack::Utils::HeaderHash, headers - _(orig_headers).must_equal headers + assert_instance_of headers_klass, headers + if Rack.release < "3" + _(orig_headers).must_equal headers + else + _(orig_headers).must_equal({'X-Foo' => 'foo', 'X-Bar' => 'bar'}) + end end middleware.call(env) end @@ -29,7 +34,11 @@ def env headers['X-Bar'] = 'bar' end r = middleware.call(env) - _(r[1]).must_equal('X-Foo' => 'foo', 'X-Bar' => 'bar') + if Rack.release < "3" + _(r[1]).must_equal('X-Foo' => 'foo', 'X-Bar' => 'bar') + else + _(r[1]).must_equal('x-foo' => 'foo', 'x-bar' => 'bar') + end end specify "allows deleting headers" do @@ -38,7 +47,11 @@ def env headers.delete('X-Bar') end r = middleware.call(env) - _(r[1]).must_equal('X-Foo' => 'foo') + if Rack.release < "3" + _(r[1]).must_equal('X-Foo' => 'foo') + else + _(r[1]).must_equal('x-foo' => 'foo') + end end end diff --git a/test/spec_rack_runtime.rb b/test/spec_rack_runtime.rb index 7c144711..45927329 100644 --- a/test/spec_rack_runtime.rb +++ b/test/spec_rack_runtime.rb @@ -7,6 +7,10 @@ specify "exists and sets X-Runtime header" do app = lambda { |env| [200, {'Content-Type' => 'text/plain'}, "Hello, World!"] } response = Rack::Runtime.new(app).call({}) - _(response[1]['X-Runtime']).must_match /[\d\.]+/ + if Rack.release < "3" + _(response[1]['X-Runtime']).must_match /[\d\.]+/ + else + _(response[1]['x-runtime']).must_match /[\d\.]+/ + end end end