diff --git a/trunk/PKGBUILD b/trunk/PKGBUILD index 473cb11..2e4447f 100644 --- a/trunk/PKGBUILD +++ b/trunk/PKGBUILD @@ -2,8 +2,8 @@ # Maintainer: Andreas 'Segaja' Schleifer pkgname=(ruby-sinatra ruby-sinatra-contrib ruby-rack-protection) -pkgver=2.2.2 -pkgrel=2 +pkgver=3.0.5 +pkgrel=1 arch=('any') url='https://sinatrarb.com/' license=('MIT') @@ -20,6 +20,8 @@ checkdepends=( 'ruby-rack' 'ruby-rack-protection' 'ruby-rack-test' + 'ruby-rack-session' + 'ruby-rackup' 'ruby-rake' 'ruby-rdiscount' 'ruby-rspec' @@ -32,11 +34,14 @@ checkdepends=( #'ruby-webrick' ) options=('!emptydirs') -source=("https://github.com/sinatra/sinatra/archive/v${pkgver}/sinatra-v${pkgver}.tar.gz") -sha512sums=('e69a36738a7c1095968a343113ddba414ff685e941a8b55575e4b2ffa5a8e7394d874966d5532643cdfec997ac4c825c293185f6688c715006e37159eedd2be8') +source=("https://github.com/sinatra/sinatra/archive/v${pkgver}/sinatra-v${pkgver}.tar.gz" + ruby-sinatra-3.0.5-ruby-rack-3.0.7-compat.patch) +sha512sums=('9ec41c3f0c4e4faaa181fddb2fe3262796a29e93fc07349422ca20dc845632c127c3e82e67f16ca19d2b2bcd43ad48fdca918d70b6747d8a2088ce1c84fd8eb2' + '63cf32cb34ed7deade07eb5e8b15f0946d62243cfb0477c4fabfcd6c01cf3143003e7f2321b98eeb227ab18a23f67555b90e53d56482084dc73154fdb3e5594b') prepare() { cd "sinatra-${pkgver}" + patch -Np1 -i ../ruby-sinatra-3.0.5-ruby-rack-3.0.7-compat.patch # update gemspec to allow newer version of the dependencies sed --in-place --regexp-extended 's|~>|>=|g' sinatra.gemspec */*.gemspec @@ -59,31 +64,26 @@ build() { check() { cd "sinatra-${pkgver}" - # cannot load such file -- coffee-script: skipping coffee tests - # cannot load such file -- creole: skipping creole tests - # cannot load such file -- erubis: skipping erubis tests - # cannot load such file -- haml: skipping haml tests - # rainbows is not installed, skipping integration tests # rainbows is not installed, skipping integration tests - # reel is not installed, skipping integration tests + # puma is not installed, skipping integration tests + # cannot load such file -- liquid + # Did you mean? liquid_test: skipping liquid tests + # cannot load such file -- rabl: skipping rabl tests + # cannot load such file -- slim: skipping slim tests + # thin is not installed, skipping integration tests + # falcon is not installed, skipping integration tests + # puma is not installed, skipping integration tests # HTTP is not installed, skipping integration tests - # cannot load such file -- less: skipping less tests - # cannot load such file -- liquid: skipping liquid tests - # cannot load such file -- markaby: skipping markaby tests # cannot load such file -- pandoc-ruby: skipping markdown tests with Tilt::PandocTemplate # cannot load such file -- commonmarker: skipping markdown tests with Tilt::CommonMarkerTemplate # cannot load such file -- redcarpet: skipping markdown tests with Tilt::RedcarpetTemplate - # cannot load such file -- bluecloth: skipping markdown tests with Tilt::BlueClothTemplate - # cannot load such file -- wikicloth: skipping mediawiki tests - # cannot load such file -- rabl: skipping rabl tests - # cannot load such file -- radius: skipping radius tests - # cannot load such file -- slim: skipping slim tests - # cannot load such file -- stylus: skipping stylus tests - # cannot load such file -- redcloth: skipping textile tests - # cannot load such file -- wlang: skipping wlang tests - # cannot load such file -- yajl: skipping yajl tests + # cannot load such file -- yajl + # Did you mean? yaml: skipping yajl tests + # cannot load such file -- markaby + # Did you mean? markaby_test: skipping markaby tests + # cannot load such file -- haml: skipping haml tests echo "sinatra" - rake spec + rake test # disabled as it requires hamlit #(cd sinatra-contrib @@ -93,13 +93,14 @@ check() { (cd rack-protection echo "rack-protection" - rake spec + rake test ) } package_ruby-sinatra() { pkgdesc='Classy web-development dressed in a DSL' - depends=('ruby' 'ruby-mustermann' 'ruby-rack' 'ruby-rack-protection' 'ruby-tilt') + depends=('ruby' 'ruby-mustermann' 'ruby-rack' 'ruby-rack-protection' + 'ruby-rack-session' 'ruby-rackup' 'ruby-tilt') _package "sinatra" "LICENSE" } diff --git a/trunk/ruby-sinatra-3.0.5-ruby-rack-3.0.7-compat.patch b/trunk/ruby-sinatra-3.0.5-ruby-rack-3.0.7-compat.patch new file mode 100644 index 0000000..4f296f6 --- /dev/null +++ b/trunk/ruby-sinatra-3.0.5-ruby-rack-3.0.7-compat.patch @@ -0,0 +1,3400 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Samuel Williams +Date: Fri, 23 Dec 2022 11:58:28 -0800 +Subject: [PATCH 01/41] Allow running with Rack 3. (#1811) + +* Allow running with Rack 3. + +* Pull in `rack-session` gem if needed. + +* Try again. + +* Use separate gemfiles for testing. + +* Better test titles. + +* Fix dependency on rack-test. + +* Add rackup gem. + +* Fix server registration. + +* Update rack version constraints + +Co-authored-by: Jordan Owens +--- + .github/workflows/test.yml | 15 ++++++++------- + Gemfile | 1 - + gems/rack-v3.rb | 4 ++++ + rack-protection/Gemfile | 2 -- + sinatra-contrib/Gemfile | 4 +--- + sinatra.gemspec | 2 +- + test/server_test.rb | 2 +- + 7 files changed, 15 insertions(+), 15 deletions(-) + create mode 100644 gems/rack-v3.rb + +diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml +index 847c7381..4c58cd5e 100644 +--- a/.github/workflows/test.yml ++++ b/.github/workflows/test.yml +@@ -11,29 +11,30 @@ permissions: + + jobs: + test: ++ name: ${{ matrix.ruby }} (Rack ${{ matrix.rack }}, Puma ${{ matrix.puma }}) + permissions: + contents: read # to fetch code (actions/checkout) + actions: read # to list jobs for workflow run (8398a7/action-slack) +- +- name: ${{ matrix.ruby }} (Rack ${{ matrix.rack }}, Puma ${{ matrix.puma }}) + runs-on: ubuntu-latest + timeout-minutes: 15 + strategy: + fail-fast: false + matrix: + puma: +- - '~> 5' # Due to https://github.com/puma/puma/issues/3000 +- rack: + - stable ++ rack: ++ - '~> 2' + # Due to https://github.com/actions/runner/issues/849, we have to use quotes for '3.0' + ruby: [2.6, 2.7, '3.0', 3.1, 'jruby-9.3', truffleruby, truffleruby-head] + include: +- - { ruby: 3.1, rack: stable, puma: latest, allow-failure: true } +- - { ruby: 3.1, rack: latest, allow-failure: true } +- - { ruby: jruby-head, rack: stable, allow-failure: true } ++ - { ruby: 3.1, rack: stable, puma: stable, gemfile: gems/rack-v3.rb, allow-failure: true } ++ - { ruby: 3.1, rack: latest, puma: latest, gemfile: gems/rack-v3.rb, allow-failure: true } ++ - { ruby: jruby-head, rack: stable, puma: stable, gemfile: gems/rack-v3.rb, allow-failure: true } + env: + rack: ${{ matrix.rack }} + puma: ${{ matrix.puma }} ++ BUNDLE_GEMFILE: ${{ matrix.gemfile }} ++ + steps: + - name: Install dependencies + run: | +diff --git a/Gemfile b/Gemfile +index 6884186d..4868e11a 100644 +--- a/Gemfile ++++ b/Gemfile +@@ -24,7 +24,6 @@ puma_version = { github: 'puma/puma' } if puma_version == 'latest' + gem 'puma', puma_version + + gem 'minitest', '~> 5.0' +-gem 'rack-test', github: 'rack/rack-test' + gem 'rubocop', '~> 1.32.0', require: false + gem 'yard' + +diff --git a/gems/rack-v3.rb b/gems/rack-v3.rb +new file mode 100644 +index 00000000..0650c0c4 +--- /dev/null ++++ b/gems/rack-v3.rb +@@ -0,0 +1,4 @@ ++eval_gemfile("../Gemfile") ++ ++gem "rackup" ++gem "rack-session" +diff --git a/rack-protection/Gemfile b/rack-protection/Gemfile +index cc444308..520dee07 100644 +--- a/rack-protection/Gemfile ++++ b/rack-protection/Gemfile +@@ -13,5 +13,3 @@ gem 'rack', rack_version + gem 'sinatra', path: '..' + + gemspec +- +-gem 'rack-test', github: 'rack/rack-test' +diff --git a/sinatra-contrib/Gemfile b/sinatra-contrib/Gemfile +index f0ae875c..6e26b939 100644 +--- a/sinatra-contrib/Gemfile ++++ b/sinatra-contrib/Gemfile +@@ -6,8 +6,6 @@ gemspec + gem 'rack-protection', path: '../rack-protection' + gem 'sinatra', path: '..' + +-gem 'rack-test', github: 'rack/rack-test' +- + group :development, :test do + platform :jruby do + gem 'json' +@@ -38,6 +36,6 @@ repos = { 'tilt' => 'rtomayko/tilt', 'rack' => 'rack/rack' } + %w[tilt rack].each do |lib| + dep = (ENV[lib] || 'stable').sub "#{lib}-", '' + dep = nil if dep == 'stable' +- dep = { github: repos[lib], branch: dep } if dep && dep !~ (/(\d+\.)+\d+/) ++ dep = { github: repos[lib], branch: dep } if dep && dep !~ (/(\d+\.?)+(\d+)?/) + gem lib, dep if dep + end +diff --git a/sinatra.gemspec b/sinatra.gemspec +index 8f9cf599..c74d4e78 100644 +--- a/sinatra.gemspec ++++ b/sinatra.gemspec +@@ -46,7 +46,7 @@ RubyGems 2.0 or newer is required to protect against public gem pushes. You can + s.required_ruby_version = '>= 2.6.0' + + s.add_dependency 'mustermann', '~> 3.0' +- s.add_dependency 'rack', '~> 2.2', '>= 2.2.4' ++ s.add_dependency 'rack', '>= 2.2.4', '< 4' + s.add_dependency 'rack-protection', version + s.add_dependency 'tilt', '~> 2.0' + +diff --git a/test/server_test.rb b/test/server_test.rb +index efacddb1..c4ae62f7 100644 +--- a/test/server_test.rb ++++ b/test/server_test.rb +@@ -24,7 +24,7 @@ module Rack::Handler + end + end + +- register 'mock', 'Rack::Handler::Mock' ++ register :mock, Mock + end + + class ServerTest < Minitest::Test +-- +2.39.2 + + +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Antonio Terceiro +Date: Fri, 23 Dec 2022 22:57:59 -0300 +Subject: [PATCH 02/41] Tests against Haml 6 + +In Haml 6, = escapes the results of the Ruby expression by default. If +you want that to be a nested template it needs to be called with !=. + +Also, attr_wrapper is no longer a valid option. +--- + Gemfile | 2 +- + test/haml_test.rb | 10 +++++----- + 2 files changed, 6 insertions(+), 6 deletions(-) + +diff --git a/Gemfile b/Gemfile +index 4868e11a..45229ef7 100644 +--- a/Gemfile ++++ b/Gemfile +@@ -38,7 +38,7 @@ gem 'commonmarker', '~> 0.23.4', platforms: [:ruby] + gem 'erubi' + gem 'eventmachine' + gem 'falcon', '~> 0.40', platforms: [:ruby] +-gem 'haml', '~> 5' ++gem 'haml', '~> 6' + gem 'kramdown' + gem 'liquid' + gem 'markaby' +diff --git a/test/haml_test.rb b/test/haml_test.rb +index bb7b09c6..ae13ae10 100644 +--- a/test/haml_test.rb ++++ b/test/haml_test.rb +@@ -26,7 +26,7 @@ class HAMLTest < Minitest::Test + + it "renders with inline layouts" do + mock_app do +- layout { %q(%h1= 'THIS. IS. ' + yield.upcase) } ++ layout { %q(%h1!= 'THIS. IS. ' + yield.upcase) } + get('/') { haml '%em Sparta' } + end + get '/' +@@ -66,7 +66,7 @@ class HAMLTest < Minitest::Test + + it "merges the default HAML options with the overrides and passes them to the Haml engine" do + mock_app do +- set :haml, {:format => :html5, :attr_wrapper => '"'} # default HAML attr are ++ set :haml, {:format => :html5} + get('/') { haml "!!!\n%h1{:class => :header} Hello World" } + get('/html4') { + haml "!!!\n%h1{:class => 'header'} Hello World", :format => :html4 +@@ -74,7 +74,7 @@ class HAMLTest < Minitest::Test + end + get '/' + assert ok? +- assert_equal "\n

Hello World

\n", body ++ assert_equal "\n

Hello World

\n", body + get '/html4' + assert ok? + assert_match(/^ false do +-- +2.39.2 + + +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Andrew Konchin +Date: Mon, 26 Dec 2022 13:23:00 +0200 +Subject: [PATCH 03/41] Skip falcon only for TruffleRuby + +--- + test/integration_helper.rb | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/test/integration_helper.rb b/test/integration_helper.rb +index 7dfbcbdd..f7edfa55 100644 +--- a/test/integration_helper.rb ++++ b/test/integration_helper.rb +@@ -125,7 +125,7 @@ module IntegrationHelper + servers = Sinatra::Base.server.dup + + # TruffleRuby doesn't support `Fiber.set_scheduler` yet +- unless Fiber.respond_to?(:set_scheduler) ++ if RUBY_ENGINE == "truffleruby" && !Fiber.respond_to?(:set_scheduler) + warn "skip falcon server" + servers.delete('falcon') + end +-- +2.39.2 + + +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Patrik Ragnarsson +Date: Wed, 28 Dec 2022 11:25:48 +0100 +Subject: [PATCH 04/41] CI: add jobs for Rack 2, Puma 5 and Ruby 3.2 + +- Include gemfile in job name + To be able to tell the difference without opening each job. + +- Avoid exact versions in sinatra-contrib/Gemfile + Introduced in a1722ca + Nokogiri 1.13.6 can't be installed on Ruby 3.2: + https://github.com/sinatra/sinatra/actions/runs/3793184348/jobs/6450054080#step:6:14 +--- + .github/workflows/test.yml | 6 ++++-- + sinatra-contrib/Gemfile | 6 +++--- + 2 files changed, 7 insertions(+), 5 deletions(-) + +diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml +index 4c58cd5e..964fbb70 100644 +--- a/.github/workflows/test.yml ++++ b/.github/workflows/test.yml +@@ -11,7 +11,7 @@ permissions: + + jobs: + test: +- name: ${{ matrix.ruby }} (Rack ${{ matrix.rack }}, Puma ${{ matrix.puma }}) ++ name: ${{ matrix.ruby }} (Rack ${{ matrix.rack }}, Puma ${{ matrix.puma }}) ${{ matrix.gemfile }} + permissions: + contents: read # to fetch code (actions/checkout) + actions: read # to list jobs for workflow run (8398a7/action-slack) +@@ -25,8 +25,10 @@ jobs: + rack: + - '~> 2' + # Due to https://github.com/actions/runner/issues/849, we have to use quotes for '3.0' +- ruby: [2.6, 2.7, '3.0', 3.1, 'jruby-9.3', truffleruby, truffleruby-head] ++ ruby: [2.6, 2.7, '3.0', 3.1, 3.2, 'jruby-9.3', truffleruby, truffleruby-head] + include: ++ - { ruby: 2.6, rack: '~> 2', puma: '~> 5' } ++ - { ruby: 3.2, rack: '~> 2', puma: '~> 5' } + - { ruby: 3.1, rack: stable, puma: stable, gemfile: gems/rack-v3.rb, allow-failure: true } + - { ruby: 3.1, rack: latest, puma: latest, gemfile: gems/rack-v3.rb, allow-failure: true } + - { ruby: jruby-head, rack: stable, puma: stable, gemfile: gems/rack-v3.rb, allow-failure: true } +diff --git a/sinatra-contrib/Gemfile b/sinatra-contrib/Gemfile +index 6e26b939..9d766170 100644 +--- a/sinatra-contrib/Gemfile ++++ b/sinatra-contrib/Gemfile +@@ -21,9 +21,9 @@ group :development, :test do + end + + platform :ruby do +- gem 'execjs', '2.0.0' +- gem 'nokogiri', '1.13.6' +- gem 'redcarpet', '3.5.1' ++ gem 'execjs', '>= 2.0.0' ++ gem 'nokogiri', '>= 1.13.6' ++ gem 'redcarpet', '>= 3.5.1' + gem 'yajl-ruby' + end + +-- +2.39.2 + + +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Patrik Ragnarsson +Date: Wed, 28 Dec 2022 11:03:49 +0100 +Subject: [PATCH 05/41] Revert "Allow running with Rack 3. (#1811)" + +This reverts commit 2ccd0dc29dc699c40a71ef6c1c8702668cc73d1b. + +It doesn't make sense to have this in master when it is not working. CI +will install Rack 3 and fail jobs. We don't know if we break something +for Rack 2 now. +--- + .github/workflows/test.yml | 9 +++------ + Gemfile | 1 + + gems/rack-v3.rb | 4 ---- + rack-protection/Gemfile | 2 ++ + sinatra-contrib/Gemfile | 2 ++ + sinatra.gemspec | 2 +- + test/server_test.rb | 2 +- + 7 files changed, 10 insertions(+), 12 deletions(-) + delete mode 100644 gems/rack-v3.rb + +diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml +index 964fbb70..926b69e7 100644 +--- a/.github/workflows/test.yml ++++ b/.github/workflows/test.yml +@@ -11,7 +11,7 @@ permissions: + + jobs: + test: +- name: ${{ matrix.ruby }} (Rack ${{ matrix.rack }}, Puma ${{ matrix.puma }}) ${{ matrix.gemfile }} ++ name: ${{ matrix.ruby }} (Rack ${{ matrix.rack }}, Puma ${{ matrix.puma }}) + permissions: + contents: read # to fetch code (actions/checkout) + actions: read # to list jobs for workflow run (8398a7/action-slack) +@@ -29,14 +29,11 @@ jobs: + include: + - { ruby: 2.6, rack: '~> 2', puma: '~> 5' } + - { ruby: 3.2, rack: '~> 2', puma: '~> 5' } +- - { ruby: 3.1, rack: stable, puma: stable, gemfile: gems/rack-v3.rb, allow-failure: true } +- - { ruby: 3.1, rack: latest, puma: latest, gemfile: gems/rack-v3.rb, allow-failure: true } +- - { ruby: jruby-head, rack: stable, puma: stable, gemfile: gems/rack-v3.rb, allow-failure: true } ++ - { ruby: 3.2, rack: '~> 2', puma: latest } ++ - { ruby: jruby-head, rack: '~> 2', puma: stable, allow-failure: true } + env: + rack: ${{ matrix.rack }} + puma: ${{ matrix.puma }} +- BUNDLE_GEMFILE: ${{ matrix.gemfile }} +- + steps: + - name: Install dependencies + run: | +diff --git a/Gemfile b/Gemfile +index 45229ef7..e29eab78 100644 +--- a/Gemfile ++++ b/Gemfile +@@ -24,6 +24,7 @@ puma_version = { github: 'puma/puma' } if puma_version == 'latest' + gem 'puma', puma_version + + gem 'minitest', '~> 5.0' ++gem 'rack-test', github: 'rack/rack-test' + gem 'rubocop', '~> 1.32.0', require: false + gem 'yard' + +diff --git a/gems/rack-v3.rb b/gems/rack-v3.rb +deleted file mode 100644 +index 0650c0c4..00000000 +--- a/gems/rack-v3.rb ++++ /dev/null +@@ -1,4 +0,0 @@ +-eval_gemfile("../Gemfile") +- +-gem "rackup" +-gem "rack-session" +diff --git a/rack-protection/Gemfile b/rack-protection/Gemfile +index 520dee07..cc444308 100644 +--- a/rack-protection/Gemfile ++++ b/rack-protection/Gemfile +@@ -13,3 +13,5 @@ gem 'rack', rack_version + gem 'sinatra', path: '..' + + gemspec ++ ++gem 'rack-test', github: 'rack/rack-test' +diff --git a/sinatra-contrib/Gemfile b/sinatra-contrib/Gemfile +index 9d766170..0e8fa6de 100644 +--- a/sinatra-contrib/Gemfile ++++ b/sinatra-contrib/Gemfile +@@ -6,6 +6,8 @@ gemspec + gem 'rack-protection', path: '../rack-protection' + gem 'sinatra', path: '..' + ++gem 'rack-test', github: 'rack/rack-test' ++ + group :development, :test do + platform :jruby do + gem 'json' +diff --git a/sinatra.gemspec b/sinatra.gemspec +index c74d4e78..8f9cf599 100644 +--- a/sinatra.gemspec ++++ b/sinatra.gemspec +@@ -46,7 +46,7 @@ RubyGems 2.0 or newer is required to protect against public gem pushes. You can + s.required_ruby_version = '>= 2.6.0' + + s.add_dependency 'mustermann', '~> 3.0' +- s.add_dependency 'rack', '>= 2.2.4', '< 4' ++ s.add_dependency 'rack', '~> 2.2', '>= 2.2.4' + s.add_dependency 'rack-protection', version + s.add_dependency 'tilt', '~> 2.0' + +diff --git a/test/server_test.rb b/test/server_test.rb +index c4ae62f7..efacddb1 100644 +--- a/test/server_test.rb ++++ b/test/server_test.rb +@@ -24,7 +24,7 @@ module Rack::Handler + end + end + +- register :mock, Mock ++ register 'mock', 'Rack::Handler::Mock' + end + + class ServerTest < Minitest::Test +-- +2.39.2 + + +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jordan Owens +Date: Sun, 22 Jan 2023 19:28:40 -0500 +Subject: [PATCH 06/41] Fix broken spec + +HTTP ranges with non decimal characters is treated as range 0..0 as of Rack 2.2.6.2. +--- + test/static_test.rb | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/test/static_test.rb b/test/static_test.rb +index 232cd21c..0022f088 100644 +--- a/test/static_test.rb ++++ b/test/static_test.rb +@@ -164,7 +164,7 @@ class StaticTest < Minitest::Test + end + + it 'correctly ignores syntactically invalid range requests' do +- ["bytes=45-40", "bytes=IV-LXVI", "octets=10-20", "bytes=", "bytes=3-1,4-5"].each do |http_range| ++ ["bytes=45-40", "octets=10-20", "bytes=", "bytes=3-1,4-5"].each do |http_range| + request = Rack::MockRequest.new(@app) + response = request.get("/#{File.basename(__FILE__)}", 'HTTP_RANGE' => http_range) + +-- +2.39.2 + + +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Patrik Ragnarsson +Date: Fri, 10 Feb 2023 17:22:11 +0100 +Subject: [PATCH 07/41] CI: allow truffleruby-head to fail + +See https://github.com/sinatra/sinatra/actions/runs/4138714331/jobs/7155450583#step:5:639 + +Failure was + + 1) Failure: + IntegrationTest#test_with_webrick_does_not_generate_warnings_0 [/home/runner/work/sinatra/sinatra/test/integration_test.rb:56]: +--- + .github/workflows/test.yml | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml +index 926b69e7..f7a78087 100644 +--- a/.github/workflows/test.yml ++++ b/.github/workflows/test.yml +@@ -25,12 +25,13 @@ jobs: + rack: + - '~> 2' + # Due to https://github.com/actions/runner/issues/849, we have to use quotes for '3.0' +- ruby: [2.6, 2.7, '3.0', 3.1, 3.2, 'jruby-9.3', truffleruby, truffleruby-head] ++ ruby: [2.6, 2.7, '3.0', 3.1, 3.2, 'jruby-9.3', truffleruby] + include: + - { ruby: 2.6, rack: '~> 2', puma: '~> 5' } + - { ruby: 3.2, rack: '~> 2', puma: '~> 5' } + - { ruby: 3.2, rack: '~> 2', puma: latest } + - { ruby: jruby-head, rack: '~> 2', puma: stable, allow-failure: true } ++ - { ruby: truffleruby-head, rack: '~> 2', puma: stable, allow-failure: true } + env: + rack: ${{ matrix.rack }} + puma: ${{ matrix.puma }} +-- +2.39.2 + + +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Patrik Ragnarsson +Date: Fri, 10 Feb 2023 17:44:53 +0100 +Subject: [PATCH 08/41] Add JRuby 9.4 to CI matrix + +--- + .github/workflows/test.yml | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml +index f7a78087..60ec3c53 100644 +--- a/.github/workflows/test.yml ++++ b/.github/workflows/test.yml +@@ -25,7 +25,7 @@ jobs: + rack: + - '~> 2' + # Due to https://github.com/actions/runner/issues/849, we have to use quotes for '3.0' +- ruby: [2.6, 2.7, '3.0', 3.1, 3.2, 'jruby-9.3', truffleruby] ++ ruby: [2.6, 2.7, '3.0', 3.1, 3.2, 'jruby-9.3', 'jruby-9.4', truffleruby] + include: + - { ruby: 2.6, rack: '~> 2', puma: '~> 5' } + - { ruby: 3.2, rack: '~> 2', puma: '~> 5' } +-- +2.39.2 + + +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Patrik Ragnarsson +Date: Fri, 10 Feb 2023 17:59:17 +0100 +Subject: [PATCH 09/41] CI: allow JRuby 9.4 to fail + +Due to https://github.com/jruby/jruby/issues/7647 +--- + .github/workflows/test.yml | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml +index 60ec3c53..fe1f2276 100644 +--- a/.github/workflows/test.yml ++++ b/.github/workflows/test.yml +@@ -25,11 +25,13 @@ jobs: + rack: + - '~> 2' + # Due to https://github.com/actions/runner/issues/849, we have to use quotes for '3.0' +- ruby: [2.6, 2.7, '3.0', 3.1, 3.2, 'jruby-9.3', 'jruby-9.4', truffleruby] ++ ruby: [2.6, 2.7, '3.0', 3.1, 3.2, 'jruby-9.3', truffleruby] + include: + - { ruby: 2.6, rack: '~> 2', puma: '~> 5' } + - { ruby: 3.2, rack: '~> 2', puma: '~> 5' } + - { ruby: 3.2, rack: '~> 2', puma: latest } ++ # Due to https://github.com/jruby/jruby/issues/7647 ++ - { ruby: jruby-9.4, rack: '~> 2', puma: stable, allow-failure: true } + - { ruby: jruby-head, rack: '~> 2', puma: stable, allow-failure: true } + - { ruby: truffleruby-head, rack: '~> 2', puma: stable, allow-failure: true } + env: +-- +2.39.2 + + +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Patrik Ragnarsson +Date: Fri, 10 Feb 2023 18:07:23 +0100 +Subject: [PATCH 10/41] CI: allow JRuby 9.3 to fail + +Not enough bandwidth to battle these flaky specs on JRuby right now. +--- + .github/workflows/test.yml | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml +index fe1f2276..75cbd576 100644 +--- a/.github/workflows/test.yml ++++ b/.github/workflows/test.yml +@@ -25,13 +25,16 @@ jobs: + rack: + - '~> 2' + # Due to https://github.com/actions/runner/issues/849, we have to use quotes for '3.0' +- ruby: [2.6, 2.7, '3.0', 3.1, 3.2, 'jruby-9.3', truffleruby] ++ ruby: [2.6, 2.7, '3.0', 3.1, 3.2, truffleruby] + include: + - { ruby: 2.6, rack: '~> 2', puma: '~> 5' } + - { ruby: 3.2, rack: '~> 2', puma: '~> 5' } + - { ruby: 3.2, rack: '~> 2', puma: latest } ++ # Due to flaky tests, see https://github.com/sinatra/sinatra/pull/1870 ++ - { ruby: jruby-9.3, rack: '~> 2', puma: stable, allow-failure: true } + # Due to https://github.com/jruby/jruby/issues/7647 + - { ruby: jruby-9.4, rack: '~> 2', puma: stable, allow-failure: true } ++ # Never fail our build due to problems with head + - { ruby: jruby-head, rack: '~> 2', puma: stable, allow-failure: true } + - { ruby: truffleruby-head, rack: '~> 2', puma: stable, allow-failure: true } + env: +-- +2.39.2 + + +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Patrik Ragnarsson +Date: Fri, 10 Feb 2023 18:08:34 +0100 +Subject: [PATCH 11/41] CI: add ruby-head to matrix (allowed to fail) + +--- + .github/workflows/test.yml | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml +index 75cbd576..b0f7fef2 100644 +--- a/.github/workflows/test.yml ++++ b/.github/workflows/test.yml +@@ -35,6 +35,7 @@ jobs: + # Due to https://github.com/jruby/jruby/issues/7647 + - { ruby: jruby-9.4, rack: '~> 2', puma: stable, allow-failure: true } + # Never fail our build due to problems with head ++ - { ruby: ruby-head, rack: '~> 2', puma: stable, allow-failure: true } + - { ruby: jruby-head, rack: '~> 2', puma: stable, allow-failure: true } + - { ruby: truffleruby-head, rack: '~> 2', puma: stable, allow-failure: true } + env: +-- +2.39.2 + + +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jordan Owens +Date: Fri, 10 Feb 2023 12:35:35 -0500 +Subject: [PATCH 12/41] Add support to keep open streaming connections with + Puma (#1858) + +Also run async tests against Puma. + +Co-authored-by: Jordan Owens +Co-authored-by: Patrik Ragnarsson +--- + examples/chat.rb | 29 ++++++++++++++++++++--------- + lib/sinatra/base.rb | 14 ++++++++++++-- + test/integration/app.rb | 4 ++-- + test/integration_async_helper.rb | 2 +- + test/integration_helper.rb | 9 ++++++++- + test/streaming_test.rb | 1 + + 6 files changed, 44 insertions(+), 15 deletions(-) + +diff --git a/examples/chat.rb b/examples/chat.rb +index 9e5f8f89..afba7a3e 100755 +--- a/examples/chat.rb ++++ b/examples/chat.rb +@@ -1,11 +1,13 @@ + #!/usr/bin/env ruby -I ../lib -I lib + # frozen_string_literal: true + +-require_relative 'rainbows' ++# This example does *not* work properly with WEBrick or other ++# servers that buffer output. To shut down the server, close any ++# open browser tabs that are connected to the chat server. + + require 'sinatra' +-set :server, :rainbows +-connections = [] ++set :server, :puma ++connections = Set.new + + get '/' do + halt erb(:login) unless params[:user] +@@ -14,13 +16,22 @@ end + + get '/stream', provides: 'text/event-stream' do + stream :keep_open do |out| +- connections << out +- out.callback { connections.delete(out) } ++ if connections.add?(out) ++ out.callback { connections.delete(out) } ++ end ++ out << "heartbeat:\n" ++ sleep 1 ++ rescue ++ out.close + end + end + + post '/' do +- connections.each { |out| out << "data: #{params[:msg]}\n\n" } ++ connections.each do |out| ++ out << "data: #{params[:msg]}\n\n" ++ rescue ++ out.close ++ end + 204 # response without entity body + end + +@@ -37,10 +48,10 @@ __END__ + + + @@ login +-
++ + +- +- ++ ++ +
+ + @@ chat +diff --git a/lib/sinatra/base.rb b/lib/sinatra/base.rb +index ba330a46..c063c4a9 100644 +--- a/lib/sinatra/base.rb ++++ b/lib/sinatra/base.rb +@@ -474,8 +474,9 @@ module Sinatra + @back.call(self) + rescue Exception => e + @scheduler.schedule { raise e } ++ ensure ++ close unless @keep_open + end +- close unless @keep_open + end + end + +@@ -506,7 +507,16 @@ module Sinatra + def stream(keep_open = false) + scheduler = env['async.callback'] ? EventMachine : Stream + current = @params.dup +- body Stream.new(scheduler, keep_open) { |out| with_params(current) { yield(out) } } ++ stream = if scheduler == Stream && keep_open ++ Stream.new(scheduler, false) do |out| ++ until out.closed? ++ with_params(current) { yield(out) } ++ end ++ end ++ else ++ Stream.new(scheduler, keep_open) { |out| with_params(current) { yield(out) } } ++ end ++ body stream + end + + # Specify response freshness policy for HTTP caches (Cache-Control header). +diff --git a/test/integration/app.rb b/test/integration/app.rb +index 23632499..abbe7009 100644 +--- a/test/integration/app.rb ++++ b/test/integration/app.rb +@@ -37,7 +37,7 @@ end + + set :out, nil + get '/async' do +- stream(:keep_open) { |o| (settings.out = o) << "hi!" } ++ stream(:keep_open) { |o| (settings.out = o) << "hi!"; sleep 1 } + end + + get '/send' do +@@ -66,7 +66,7 @@ end + class Subclass < Sinatra::Base + set :out, nil + get '/subclass/async' do +- stream(:keep_open) { |o| (settings.out = o) << "hi!" } ++ stream(:keep_open) { |o| (settings.out = o) << "hi!"; sleep 1 } + end + + get '/subclass/send' do +diff --git a/test/integration_async_helper.rb b/test/integration_async_helper.rb +index cf05839d..fceceb5e 100644 +--- a/test/integration_async_helper.rb ++++ b/test/integration_async_helper.rb +@@ -4,7 +4,7 @@ module IntegrationAsyncHelper + def it(message, &block) + base_port = 5100 + Process.pid % 100 + +- %w(rainbows).each_with_index do |server_name, index| ++ %w(rainbows puma).each_with_index do |server_name, index| + server = IntegrationHelper::BaseServer.new(server_name, base_port + index) + next unless server.installed? + +diff --git a/test/integration_helper.rb b/test/integration_helper.rb +index f7edfa55..7525cf10 100644 +--- a/test/integration_helper.rb ++++ b/test/integration_helper.rb +@@ -44,7 +44,14 @@ module IntegrationHelper + + def installed? + return @installed unless @installed.nil? +- s = server == 'HTTP' ? 'net/http/server' : server ++ s = case server ++ when 'HTTP' ++ 'net/http/server' ++ when 'puma' ++ 'puma/rack/handler' ++ else ++ server ++ end + require s + @installed = true + rescue LoadError +diff --git a/test/streaming_test.rb b/test/streaming_test.rb +index 800bcca2..83b3bd0b 100644 +--- a/test/streaming_test.rb ++++ b/test/streaming_test.rb +@@ -133,6 +133,7 @@ class StreamingTest < Minitest::Test + env['async.close'] = close + stream(:keep_open) do |out| + out.callback { ran = true } ++ out.close + end + end + end +-- +2.39.2 + + +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Eloy=20P=C3=A9rez?= +Date: Sun, 12 Feb 2023 19:09:35 +0100 +Subject: [PATCH 13/41] Minor cleaning setting up tests (#1875) + +We use minitest for Sinatra's test suite but we weren't using its rake task. I've updated the Rakefile to require and use Minitest default rake task to simplify. + +Another change is to rename the `helper.rb` file to `test_helper.rb` because I think that name is used more in the community and require it directly without calling `File.expand_path` +--- + Rakefile | 25 ++++++++----------------- + test/asciidoctor_test.rb | 2 +- + test/base_test.rb | 2 +- + test/builder_test.rb | 2 +- + test/compile_test.rb | 3 +-- + test/delegator_test.rb | 2 +- + test/encoding_test.rb | 3 +-- + test/erb_test.rb | 2 +- + test/extensions_test.rb | 2 +- + test/filter_test.rb | 2 +- + test/haml_test.rb | 2 +- + test/helpers_test.rb | 6 +++--- + test/integration_async_test.rb | 2 +- + test/integration_test.rb | 2 +- + test/liquid_test.rb | 2 +- + test/mapped_error_test.rb | 2 +- + test/markaby_test.rb | 2 +- + test/markdown_test.rb | 2 +- + test/middleware_test.rb | 2 +- + test/nokogiri_test.rb | 2 +- + test/rabl_test.rb | 2 +- + test/rack_test.rb | 2 +- + test/rdoc_test.rb | 2 +- + test/readme_test.rb | 4 ++-- + test/request_test.rb | 2 +- + test/response_test.rb | 4 +--- + test/result_test.rb | 2 +- + test/route_added_hook_test.rb | 2 +- + test/routing_test.rb | 3 +-- + test/server_test.rb | 2 +- + test/settings_test.rb | 2 +- + test/sinatra_test.rb | 2 +- + test/slim_test.rb | 2 +- + test/static_test.rb | 2 +- + test/streaming_test.rb | 2 +- + test/templates_test.rb | 3 +-- + test/{helper.rb => test_helper.rb} | 0 + test/yajl_test.rb | 2 +- + 38 files changed, 47 insertions(+), 62 deletions(-) + rename test/{helper.rb => test_helper.rb} (100%) + +diff --git a/Rakefile b/Rakefile +index 7bbd08ca..732cfc84 100644 +--- a/Rakefile ++++ b/Rakefile +@@ -1,14 +1,11 @@ + # frozen_string_literal: true + + require 'rake/clean' +-require 'rake/testtask' ++require 'minitest/test_task' + require 'fileutils' + require 'date' + + task default: :test +-task spec: :test +- +-CLEAN.include '**/*.rbc' + + def source_version + @source_version ||= File.read(File.expand_path('VERSION', __dir__)).strip +@@ -24,27 +21,20 @@ def prev_version + source_version.gsub(/\d+$/) { |s| s.to_i - 1 } + end + +-# SPECS =============================================================== ++# Tests =============================================================== + +-Rake::TestTask.new(:test) do |t| +- t.test_files = FileList['test/*_test.rb'] +- t.ruby_opts = ['-r rubygems'] if defined? Gem ++Minitest::TestTask.create # Default `test` task ++Minitest::TestTask.create(:'test:core') do |t| + t.warning = true +-end +- +-Rake::TestTask.new(:'test:core') do |t| +- core_tests = %w[ ++ t.test_globs = %w[ + base delegator encoding extensions filter + helpers mapped_error middleware rdoc + readme request response result route_added_hook + routing server settings sinatra static templates +- ] +- t.test_files = core_tests.map { |n| "test/#{n}_test.rb" } +- t.ruby_opts = ['-r rubygems'] if defined? Gem +- t.warning = true ++ ].map { |n| "test/#{n}_test.rb" } + end + +-# Rcov ================================================================ ++# Test code coverage ================================================== + + namespace :test do + desc 'Measures test coverage' +@@ -54,6 +44,7 @@ namespace :test do + Rake::Task['test'].invoke + end + end ++CLEAN.include('coverage') + + # Website ============================================================= + +diff --git a/test/asciidoctor_test.rb b/test/asciidoctor_test.rb +index 9a296f46..fd0def74 100644 +--- a/test/asciidoctor_test.rb ++++ b/test/asciidoctor_test.rb +@@ -1,4 +1,4 @@ +-require File.expand_path('helper', __dir__) ++require_relative 'test_helper' + + begin + require 'asciidoctor' +diff --git a/test/base_test.rb b/test/base_test.rb +index 00ca61cf..2bccf2e9 100644 +--- a/test/base_test.rb ++++ b/test/base_test.rb +@@ -1,4 +1,4 @@ +-require File.expand_path('helper', __dir__) ++require_relative 'test_helper' + + class BaseTest < Minitest::Test + describe 'Sinatra::Base subclasses' do +diff --git a/test/builder_test.rb b/test/builder_test.rb +index 0729537c..ef1c95aa 100644 +--- a/test/builder_test.rb ++++ b/test/builder_test.rb +@@ -1,4 +1,4 @@ +-require File.expand_path('helper', __dir__) ++require_relative 'test_helper' + + begin + require 'builder' +diff --git a/test/compile_test.rb b/test/compile_test.rb +index 794f2846..6bbadd24 100644 +--- a/test/compile_test.rb ++++ b/test/compile_test.rb +@@ -1,5 +1,4 @@ +-# I like coding: UTF-8 +-require File.expand_path('helper', __dir__) ++require_relative 'test_helper' + + class CompileTest < Minitest::Test + def self.parses pattern, example, expected_params, mtype = :sinatra, mopts = {} +diff --git a/test/delegator_test.rb b/test/delegator_test.rb +index 52ce1594..da2b5d99 100644 +--- a/test/delegator_test.rb ++++ b/test/delegator_test.rb +@@ -1,4 +1,4 @@ +-require File.expand_path('helper', __dir__) ++require_relative 'test_helper' + + class DelegatorTest < Minitest::Test + class Mirror +diff --git a/test/encoding_test.rb b/test/encoding_test.rb +index 28ead0a0..fce8f316 100644 +--- a/test/encoding_test.rb ++++ b/test/encoding_test.rb +@@ -1,5 +1,4 @@ +-# encoding: UTF-8 +-require File.expand_path('helper', __dir__) ++require_relative 'test_helper' + require 'erb' + + class BaseTest < Minitest::Test +diff --git a/test/erb_test.rb b/test/erb_test.rb +index f306ae8e..646576f3 100644 +--- a/test/erb_test.rb ++++ b/test/erb_test.rb +@@ -1,4 +1,4 @@ +-require File.expand_path('helper', __dir__) ++require_relative 'test_helper' + + class ERBTest < Minitest::Test + def engine +diff --git a/test/extensions_test.rb b/test/extensions_test.rb +index bb58e966..cff43e3d 100644 +--- a/test/extensions_test.rb ++++ b/test/extensions_test.rb +@@ -1,4 +1,4 @@ +-require File.expand_path('helper', __dir__) ++require_relative 'test_helper' + + class ExtensionsTest < Minitest::Test + module FooExtensions +diff --git a/test/filter_test.rb b/test/filter_test.rb +index 3af2cadd..53249690 100644 +--- a/test/filter_test.rb ++++ b/test/filter_test.rb +@@ -1,4 +1,4 @@ +-require File.expand_path('helper', __dir__) ++require_relative 'test_helper' + + class BeforeFilterTest < Minitest::Test + it "executes filters in the order defined" do +diff --git a/test/haml_test.rb b/test/haml_test.rb +index ae13ae10..766d3d95 100644 +--- a/test/haml_test.rb ++++ b/test/haml_test.rb +@@ -1,4 +1,4 @@ +-require File.expand_path('helper', __dir__) ++require_relative 'test_helper' + + begin + require 'haml' +diff --git a/test/helpers_test.rb b/test/helpers_test.rb +index 71b42a8f..50ee6778 100644 +--- a/test/helpers_test.rb ++++ b/test/helpers_test.rb +@@ -1,4 +1,4 @@ +-require File.expand_path('helper', __dir__) ++require_relative 'test_helper' + require 'date' + require 'json' + +@@ -895,8 +895,8 @@ class HelpersTest < Minitest::Test + send_file_app :disposition => 'inline'.freeze + get '/file.txt' + assert_equal 'inline; filename="file.txt"', response['Content-Disposition'] +- end +- ++ end ++ + it "sets the Content-Disposition header when :filename provided" do + send_file_app :filename => 'foo.txt' + get '/file.txt' +diff --git a/test/integration_async_test.rb b/test/integration_async_test.rb +index cd283692..614f93fc 100644 +--- a/test/integration_async_test.rb ++++ b/test/integration_async_test.rb +@@ -1,4 +1,4 @@ +-require File.expand_path('helper', __dir__) ++require_relative 'test_helper' + require File.expand_path('integration_async_helper', __dir__) + + # These tests are like integration_test, but they test asynchronous streaming. +diff --git a/test/integration_test.rb b/test/integration_test.rb +index f7069d51..22d51634 100644 +--- a/test/integration_test.rb ++++ b/test/integration_test.rb +@@ -1,4 +1,4 @@ +-require File.expand_path('helper', __dir__) ++require_relative 'test_helper' + require File.expand_path('integration_helper', __dir__) + + # These tests start a real server and talk to it over TCP. +diff --git a/test/liquid_test.rb b/test/liquid_test.rb +index ecbceac2..747d539e 100644 +--- a/test/liquid_test.rb ++++ b/test/liquid_test.rb +@@ -1,4 +1,4 @@ +-require File.expand_path('helper', __dir__) ++require_relative 'test_helper' + + begin + require 'liquid' +diff --git a/test/mapped_error_test.rb b/test/mapped_error_test.rb +index 60d6a90a..0ea2019f 100644 +--- a/test/mapped_error_test.rb ++++ b/test/mapped_error_test.rb +@@ -1,4 +1,4 @@ +-require File.expand_path('helper', __dir__) ++require_relative 'test_helper' + + class FooError < RuntimeError + end +diff --git a/test/markaby_test.rb b/test/markaby_test.rb +index 32dff219..26d54af2 100644 +--- a/test/markaby_test.rb ++++ b/test/markaby_test.rb +@@ -1,4 +1,4 @@ +-require File.expand_path('helper', __dir__) ++require_relative 'test_helper' + + begin + require 'markaby' +diff --git a/test/markdown_test.rb b/test/markdown_test.rb +index 6e38fcc3..642391c0 100644 +--- a/test/markdown_test.rb ++++ b/test/markdown_test.rb +@@ -1,4 +1,4 @@ +-require File.expand_path('helper', __dir__) ++require_relative 'test_helper' + + MarkdownTest = proc do + def markdown_app(&block) +diff --git a/test/middleware_test.rb b/test/middleware_test.rb +index 3901b7b5..aa6fcdb9 100644 +--- a/test/middleware_test.rb ++++ b/test/middleware_test.rb +@@ -1,4 +1,4 @@ +-require File.expand_path('helper', __dir__) ++require_relative 'test_helper' + + class MiddlewareTest < Minitest::Test + setup do +diff --git a/test/nokogiri_test.rb b/test/nokogiri_test.rb +index 99461bfd..0801fbdd 100644 +--- a/test/nokogiri_test.rb ++++ b/test/nokogiri_test.rb +@@ -1,4 +1,4 @@ +-require File.expand_path('helper', __dir__) ++require_relative 'test_helper' + + begin + require 'nokogiri' +diff --git a/test/rabl_test.rb b/test/rabl_test.rb +index f72caef0..781569f1 100644 +--- a/test/rabl_test.rb ++++ b/test/rabl_test.rb +@@ -1,4 +1,4 @@ +-require File.expand_path('helper', __dir__) ++require_relative 'test_helper' + + begin + require 'rabl' +diff --git a/test/rack_test.rb b/test/rack_test.rb +index e20d0f63..c7b3eae3 100644 +--- a/test/rack_test.rb ++++ b/test/rack_test.rb +@@ -1,4 +1,4 @@ +-require File.expand_path('helper', __dir__) ++require_relative 'test_helper' + require 'rack' + + class RackTest < Minitest::Test +diff --git a/test/rdoc_test.rb b/test/rdoc_test.rb +index f13cea01..33e74798 100644 +--- a/test/rdoc_test.rb ++++ b/test/rdoc_test.rb +@@ -1,4 +1,4 @@ +-require File.expand_path('helper', __dir__) ++require_relative 'test_helper' + + begin + require 'rdoc' +diff --git a/test/readme_test.rb b/test/readme_test.rb +index eaa37924..e77e4ea8 100644 +--- a/test/readme_test.rb ++++ b/test/readme_test.rb +@@ -1,6 +1,6 @@ +-# Tests to check if all the README examples work. +-require File.expand_path('helper', __dir__) ++require_relative 'test_helper' + ++# Tests to check if all the README examples work. + class ReadmeTest < Minitest::Test + example do + mock_app { get('/') { 'Hello world!' } } +diff --git a/test/request_test.rb b/test/request_test.rb +index d405205b..78663b9b 100644 +--- a/test/request_test.rb ++++ b/test/request_test.rb +@@ -1,4 +1,4 @@ +-require File.expand_path('helper', __dir__) ++require_relative 'test_helper' + require 'stringio' + + class RequestTest < Minitest::Test +diff --git a/test/response_test.rb b/test/response_test.rb +index d713bd08..5c1e6057 100644 +--- a/test/response_test.rb ++++ b/test/response_test.rb +@@ -1,6 +1,4 @@ +-# encoding: utf-8 +- +-require File.expand_path('helper', __dir__) ++require_relative 'test_helper' + + class ResponseTest < Minitest::Test + setup { @response = Sinatra::Response.new([], 200, { 'Content-Type' => 'text/html' }) } +diff --git a/test/result_test.rb b/test/result_test.rb +index 28c4a1e2..293680bd 100644 +--- a/test/result_test.rb ++++ b/test/result_test.rb +@@ -1,4 +1,4 @@ +-require File.expand_path('helper', __dir__) ++require_relative 'test_helper' + + class ThirdPartyError < RuntimeError + def http_status; 400 end +diff --git a/test/route_added_hook_test.rb b/test/route_added_hook_test.rb +index db3edbbf..91a0c70b 100644 +--- a/test/route_added_hook_test.rb ++++ b/test/route_added_hook_test.rb +@@ -1,4 +1,4 @@ +-require File.expand_path('helper', __dir__) ++require_relative 'test_helper' + + module RouteAddedTest + @routes, @procs = [], [] +diff --git a/test/routing_test.rb b/test/routing_test.rb +index 73c5c92e..88c7532a 100644 +--- a/test/routing_test.rb ++++ b/test/routing_test.rb +@@ -1,5 +1,4 @@ +-# I like coding: UTF-8 +-require File.expand_path('helper', __dir__) ++require_relative 'test_helper' + + # Helper method for easy route pattern matching testing + def route_def(pattern) +diff --git a/test/server_test.rb b/test/server_test.rb +index efacddb1..764295b8 100644 +--- a/test/server_test.rb ++++ b/test/server_test.rb +@@ -1,4 +1,4 @@ +-require File.expand_path('helper', __dir__) ++require_relative 'test_helper' + require 'stringio' + + module Rack::Handler +diff --git a/test/settings_test.rb b/test/settings_test.rb +index 51770986..c1aa02c9 100644 +--- a/test/settings_test.rb ++++ b/test/settings_test.rb +@@ -1,4 +1,4 @@ +-require File.expand_path('helper', __dir__) ++require_relative 'test_helper' + + class SettingsTest < Minitest::Test + setup do +diff --git a/test/sinatra_test.rb b/test/sinatra_test.rb +index f903825f..92a30809 100644 +--- a/test/sinatra_test.rb ++++ b/test/sinatra_test.rb +@@ -1,4 +1,4 @@ +-require File.expand_path('helper', __dir__) ++require_relative 'test_helper' + + class SinatraTest < Minitest::Test + it 'creates a new Sinatra::Base subclass on new' do +diff --git a/test/slim_test.rb b/test/slim_test.rb +index dd304000..7eea7306 100644 +--- a/test/slim_test.rb ++++ b/test/slim_test.rb +@@ -1,4 +1,4 @@ +-require File.expand_path('helper', __dir__) ++require_relative 'test_helper' + + begin + require 'slim' +diff --git a/test/static_test.rb b/test/static_test.rb +index 0022f088..4d8cffb1 100644 +--- a/test/static_test.rb ++++ b/test/static_test.rb +@@ -1,4 +1,4 @@ +-require File.expand_path('helper', __dir__) ++require_relative 'test_helper' + + class StaticTest < Minitest::Test + setup do +diff --git a/test/streaming_test.rb b/test/streaming_test.rb +index 83b3bd0b..cdb10c2b 100644 +--- a/test/streaming_test.rb ++++ b/test/streaming_test.rb +@@ -1,4 +1,4 @@ +-require File.expand_path('helper', __dir__) ++require_relative 'test_helper' + + class StreamingTest < Minitest::Test + Stream = Sinatra::Helpers::Stream +diff --git a/test/templates_test.rb b/test/templates_test.rb +index 76284400..dc22666a 100644 +--- a/test/templates_test.rb ++++ b/test/templates_test.rb +@@ -1,5 +1,4 @@ +-# encoding: UTF-8 +-require File.expand_path('helper', __dir__) ++require_relative 'test_helper' + File.delete(__dir__ + '/views/layout.test') rescue nil + + class TestTemplate < Tilt::Template +diff --git a/test/helper.rb b/test/test_helper.rb +similarity index 100% +rename from test/helper.rb +rename to test/test_helper.rb +diff --git a/test/yajl_test.rb b/test/yajl_test.rb +index 6da0daed..8df06a73 100644 +--- a/test/yajl_test.rb ++++ b/test/yajl_test.rb +@@ -1,4 +1,4 @@ +-require File.expand_path('helper', __dir__) ++require_relative 'test_helper' + + begin + require 'yajl' +-- +2.39.2 + + +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Patrik Ragnarsson +Date: Sun, 12 Feb 2023 21:00:44 +0100 +Subject: [PATCH 14/41] Start servers for async tests before running tests + (#1876) + +Close #1874 +--- + test/integration_async_helper.rb | 19 ++++++++++++++----- + test/integration_helper.rb | 25 +++++++++++++------------ + test/integration_test.rb | 2 +- + 3 files changed, 28 insertions(+), 18 deletions(-) + +diff --git a/test/integration_async_helper.rb b/test/integration_async_helper.rb +index fceceb5e..b246c4ad 100644 +--- a/test/integration_async_helper.rb ++++ b/test/integration_async_helper.rb +@@ -1,14 +1,23 @@ + require File.expand_path('integration_helper', __dir__) + + module IntegrationAsyncHelper +- def it(message, &block) +- base_port = 5100 + Process.pid % 100 ++ Server = IntegrationHelper::BaseServer + +- %w(rainbows puma).each_with_index do |server_name, index| +- server = IntegrationHelper::BaseServer.new(server_name, base_port + index) ++ def it(message, &block) ++ Server.all_async.each do |server| + next unless server.installed? +- + super("with #{server.name}: #{message}") { server.run_test(self, &block) } + end + end ++ ++ def self.extend_object(obj) ++ super ++ ++ base_port = 5100 + Process.pid % 100 ++ servers = %w(rainbows puma) ++ ++ servers.each_with_index do |server, index| ++ Server.run(server, base_port+index, async: true) ++ end ++ end + end +diff --git a/test/integration_helper.rb b/test/integration_helper.rb +index 7525cf10..7cdbe879 100644 +--- a/test/integration_helper.rb ++++ b/test/integration_helper.rb +@@ -13,12 +13,16 @@ module IntegrationHelper + @all ||= [] + end + ++ def self.all_async ++ @all_async ||= [] ++ end ++ + def self.each(&block) + all.each(&block) + end + +- def self.run(server, port) +- new(server, port).run ++ def self.run(server, port, async: false) ++ new(server, port, async).run + end + + def app_file +@@ -29,9 +33,13 @@ module IntegrationHelper + "development" + end + +- def initialize(server, port) ++ def initialize(server, port, async) + @installed, @pipe, @server, @port = nil, nil, server, port +- Server.all << self ++ if async ++ Server.all_async << self ++ else ++ Server.all << self ++ end + end + + def run +@@ -44,14 +52,7 @@ module IntegrationHelper + + def installed? + return @installed unless @installed.nil? +- s = case server +- when 'HTTP' +- 'net/http/server' +- when 'puma' +- 'puma/rack/handler' +- else +- server +- end ++ s = server == 'HTTP' ? 'net/http/server' : server + require s + @installed = true + rescue LoadError +diff --git a/test/integration_test.rb b/test/integration_test.rb +index 22d51634..57c4a6d3 100644 +--- a/test/integration_test.rb ++++ b/test/integration_test.rb +@@ -47,7 +47,7 @@ class IntegrationTest < Minitest::Test + }ix + + # because Net HTTP Server logs to $stderr by default +- assert_match exp, server.log unless server.net_http_server? || server.rainbows? ++ assert_match exp, server.log unless server.net_http_server? + end + + it 'does not generate warnings' do +-- +2.39.2 + + +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Patrik Ragnarsson +Date: Thu, 16 Feb 2023 00:04:06 +0100 +Subject: [PATCH 15/41] `Rack::File` -> `Rack::Files` (#1877) + +The alias was deprecated: https://github.com/rack/rack/pull/1811 + +Looks like Rack::Files appeared in rack 2.1.0 according to +https://github.com/rack/rack/commit/626272b2bc6b270bbd3d2aa6aaa64722350f42be + +Sinatra needs at least rack 2.2.4 (since v3.0.0) so this should be fine +--- + lib/sinatra/base.rb | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/lib/sinatra/base.rb b/lib/sinatra/base.rb +index c063c4a9..9a2cd954 100644 +--- a/lib/sinatra/base.rb ++++ b/lib/sinatra/base.rb +@@ -291,7 +291,7 @@ module Sinatra + elsif value + # Rack 2.0 returns a Rack::File::Iterator here instead of + # Rack::File as it was in the previous API. +- unless request.head? || value.is_a?(Rack::File::Iterator) || value.is_a?(Stream) ++ unless request.head? || value.is_a?(Rack::Files::Iterator) || value.is_a?(Stream) + headers.delete 'Content-Length' + end + response.body = value +@@ -429,7 +429,7 @@ module Sinatra + + last_modified opts[:last_modified] if opts[:last_modified] + +- file = Rack::File.new(File.dirname(settings.app_file)) ++ file = Rack::Files.new(File.dirname(settings.app_file)) + result = file.serving(request, path) + + result[1].each { |k, v| headers[k] ||= v } +-- +2.39.2 + + +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Patrik Ragnarsson +Date: Wed, 22 Feb 2023 08:42:41 +0100 +Subject: [PATCH 16/41] CI: handle dependencies the same way, add Tilt to the + matrix (#1881) + +Adds Tilt (https://rubygems.org/gems/tilt) to the CI matrix. It now +resides at https://github.com/jeremyevans/tilt + +Changes `latest` to `head` because "latest" sounds a lot like "latest +release" but we mean using the main/master branch of the repo of the +dependency we test with. Matches `ruby-head`. +--- + .github/workflows/test.yml | 25 ++++++++++++++++--------- + Gemfile | 4 ++-- + rack-protection/Gemfile | 2 +- + sinatra-contrib/Gemfile | 18 +++++++++--------- + 4 files changed, 28 insertions(+), 21 deletions(-) + +diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml +index b0f7fef2..c3e41e67 100644 +--- a/.github/workflows/test.yml ++++ b/.github/workflows/test.yml +@@ -11,7 +11,7 @@ permissions: + + jobs: + test: +- name: ${{ matrix.ruby }} (Rack ${{ matrix.rack }}, Puma ${{ matrix.puma }}) ++ name: ${{ matrix.ruby }} (Rack ${{ matrix.rack }}, Puma ${{ matrix.puma }}, Tilt ${{ matrix.tilt }}) + permissions: + contents: read # to fetch code (actions/checkout) + actions: read # to list jobs for workflow run (8398a7/action-slack) +@@ -24,23 +24,30 @@ jobs: + - stable + rack: + - '~> 2' ++ tilt: ++ - '~> 2.0.0' + # Due to https://github.com/actions/runner/issues/849, we have to use quotes for '3.0' + ruby: [2.6, 2.7, '3.0', 3.1, 3.2, truffleruby] + include: +- - { ruby: 2.6, rack: '~> 2', puma: '~> 5' } +- - { ruby: 3.2, rack: '~> 2', puma: '~> 5' } +- - { ruby: 3.2, rack: '~> 2', puma: latest } ++ # Puma ++ - { ruby: 3.1, rack: '~> 2', puma: '~> 5', tilt: '~> 2.0.0' } ++ - { ruby: 3.2, rack: '~> 2', puma: '~> 6.0', tilt: '~> 2.0.0' } ++ - { ruby: 3.2, rack: '~> 2', puma: head, tilt: '~> 2.0.0', allow-failure: true } ++ # Tilt ++ - { ruby: 3.1, rack: '~> 2', puma: stable, tilt: '~> 2', allow-failure: true } ++ - { ruby: 3.2, rack: '~> 2', puma: stable, tilt: head, allow-failure: true } + # Due to flaky tests, see https://github.com/sinatra/sinatra/pull/1870 +- - { ruby: jruby-9.3, rack: '~> 2', puma: stable, allow-failure: true } ++ - { ruby: jruby-9.3, rack: '~> 2', puma: stable, tilt: '~> 2.0.0', allow-failure: true } + # Due to https://github.com/jruby/jruby/issues/7647 +- - { ruby: jruby-9.4, rack: '~> 2', puma: stable, allow-failure: true } ++ - { ruby: jruby-9.4, rack: '~> 2', puma: stable, tilt: '~> 2.0.0', allow-failure: true } + # Never fail our build due to problems with head +- - { ruby: ruby-head, rack: '~> 2', puma: stable, allow-failure: true } +- - { ruby: jruby-head, rack: '~> 2', puma: stable, allow-failure: true } +- - { ruby: truffleruby-head, rack: '~> 2', puma: stable, allow-failure: true } ++ - { ruby: ruby-head, rack: '~> 2', puma: stable, tilt: '~> 2.0.0', allow-failure: true } ++ - { ruby: jruby-head, rack: '~> 2', puma: stable, tilt: '~> 2.0.0', allow-failure: true } ++ - { ruby: truffleruby-head, rack: '~> 2', puma: stable, tilt: '~> 2.0.0', allow-failure: true } + env: + rack: ${{ matrix.rack }} + puma: ${{ matrix.puma }} ++ tilt: ${{ matrix.tilt }} + steps: + - name: Install dependencies + run: | +diff --git a/Gemfile b/Gemfile +index e29eab78..ea870323 100644 +--- a/Gemfile ++++ b/Gemfile +@@ -15,12 +15,12 @@ gem 'rake' + + rack_version = ENV['rack'].to_s + rack_version = nil if rack_version.empty? || (rack_version == 'stable') +-rack_version = { github: 'rack/rack' } if rack_version == 'latest' ++rack_version = { github: 'rack/rack' } if rack_version == 'head' + gem 'rack', rack_version + + puma_version = ENV['puma'].to_s + puma_version = nil if puma_version.empty? || (puma_version == 'stable') +-puma_version = { github: 'puma/puma' } if puma_version == 'latest' ++puma_version = { github: 'puma/puma' } if puma_version == 'head' + gem 'puma', puma_version + + gem 'minitest', '~> 5.0' +diff --git a/rack-protection/Gemfile b/rack-protection/Gemfile +index cc444308..690d4ca0 100644 +--- a/rack-protection/Gemfile ++++ b/rack-protection/Gemfile +@@ -7,7 +7,7 @@ gem 'rake' + + rack_version = ENV['rack'].to_s + rack_version = nil if rack_version.empty? || (rack_version == 'stable') +-rack_version = { github: 'rack/rack' } if rack_version == 'main' ++rack_version = { github: 'rack/rack' } if rack_version == 'head' + gem 'rack', rack_version + + gem 'sinatra', path: '..' +diff --git a/sinatra-contrib/Gemfile b/sinatra-contrib/Gemfile +index 0e8fa6de..64cd23a1 100644 +--- a/sinatra-contrib/Gemfile ++++ b/sinatra-contrib/Gemfile +@@ -32,12 +32,12 @@ group :development, :test do + gem 'multi_json' + end + +-# Allows stuff like `tilt=1.2.2 bundle install` or `tilt=master ...`. +-# Used by the CI. +-repos = { 'tilt' => 'rtomayko/tilt', 'rack' => 'rack/rack' } +-%w[tilt rack].each do |lib| +- dep = (ENV[lib] || 'stable').sub "#{lib}-", '' +- dep = nil if dep == 'stable' +- dep = { github: repos[lib], branch: dep } if dep && dep !~ (/(\d+\.?)+(\d+)?/) +- gem lib, dep if dep +-end ++rack_version = ENV['rack'].to_s ++rack_version = nil if rack_version.empty? || (rack_version == 'stable') ++rack_version = { github: 'rack/rack' } if rack_version == 'head' ++gem 'rack', rack_version ++ ++tilt_version = ENV['tilt'].to_s ++tilt_version = nil if tilt_version.empty? || (tilt_version == 'stable') ++tilt_version = { github: 'jeremyevans/tilt' } if tilt_version == 'head' ++gem 'tilt', tilt_version +-- +2.39.2 + + +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: MSP-Greg +Date: Wed, 22 Feb 2023 15:12:12 -0600 +Subject: [PATCH 17/41] CI: Fix up integration stream test for Puma (#1887) + +Fixes timing issue with Puma, adds 0.05 second to sleep, small changes to +asserts and time calc. + +Ensure Puma creates its request serving thread at server boot: If min_threads is +zero, the thread is created when the first request is received, which would +increase the time for the test assert that failed. For CI, if min_threads is +one, the thread is created as the server boots. + +Close https://github.com/puma/puma/issues/3085 +--- + .gitignore | 2 ++ + test/integration/app.rb | 2 +- + test/integration_helper.rb | 1 + + test/integration_test.rb | 10 ++++++---- + 4 files changed, 10 insertions(+), 5 deletions(-) + +diff --git a/.gitignore b/.gitignore +index f37e0f56..efc495ac 100644 +--- a/.gitignore ++++ b/.gitignore +@@ -8,3 +8,5 @@ + /coverage + .yardoc + /doc ++.bundle ++vendor +diff --git a/test/integration/app.rb b/test/integration/app.rb +index abbe7009..dbc9e3a2 100644 +--- a/test/integration/app.rb ++++ b/test/integration/app.rb +@@ -20,7 +20,7 @@ get '/stream' do + stream do |out| + sleep 0.1 + out << "a" +- sleep 1.2 ++ sleep 1.25 + out << "b" + end + end +diff --git a/test/integration_helper.rb b/test/integration_helper.rb +index 7cdbe879..997fa701 100644 +--- a/test/integration_helper.rb ++++ b/test/integration_helper.rb +@@ -35,6 +35,7 @@ module IntegrationHelper + + def initialize(server, port, async) + @installed, @pipe, @server, @port = nil, nil, server, port ++ ENV['PUMA_MIN_THREADS'] = '1' if server == 'puma' + if async + Server.all_async << self + else +diff --git a/test/integration_test.rb b/test/integration_test.rb +index 57c4a6d3..74b21cf5 100644 +--- a/test/integration_test.rb ++++ b/test/integration_test.rb +@@ -28,15 +28,17 @@ class IntegrationTest < Minitest::Test + + it 'streams' do + next if server.webrick? or server.trinidad? +- times, chunks = [Time.now], [] ++ times, chunks = [Process.clock_gettime(Process::CLOCK_MONOTONIC)], [] + server.get_stream do |chunk| + next if chunk.empty? + chunks << chunk +- times << Time.now ++ times << Process.clock_gettime(Process::CLOCK_MONOTONIC) + end + assert_equal ["a", "b"], chunks +- assert times[1] - times[0] < 1 +- assert times[2] - times[1] > 1 ++ int1 = (times[1] - times[0]).round 2 ++ int2 = (times[2] - times[1]).round 2 ++ assert_operator 1, :>, int1 ++ assert_operator 1, :<, int2 + end + + it 'starts the correct server' do +-- +2.39.2 + + +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Patrik Ragnarsson +Date: Sun, 26 Feb 2023 21:45:08 +0100 +Subject: [PATCH 18/41] Avoid crash in `uri` helper on Integer input (#1890) + +Ruby 3.2.0 removed Object#=~ in https://github.com/ruby/ruby/pull/5383 + +Has been deprecated since Ruby 2.6.0: +https://github.com/ruby/ruby/commit/ebff9dc10e6e72239c23e50acc7d3cbfdc659e7a +https://bugs.ruby-lang.org/issues/15231 +--- + lib/sinatra/base.rb | 2 +- + test/helpers_test.rb | 6 ++++++ + 2 files changed, 7 insertions(+), 1 deletion(-) + +diff --git a/lib/sinatra/base.rb b/lib/sinatra/base.rb +index 9a2cd954..4982034b 100644 +--- a/lib/sinatra/base.rb ++++ b/lib/sinatra/base.rb +@@ -317,7 +317,7 @@ module Sinatra + # Generates the absolute URI for a given path in the app. + # Takes Rack routers and reverse proxies into account. + def uri(addr = nil, absolute = true, add_script_name = true) +- return addr if addr =~ /\A[a-z][a-z0-9+.\-]*:/i ++ return addr if addr.to_s =~ /\A[a-z][a-z0-9+.\-]*:/i + + uri = [host = String.new] + if absolute +diff --git a/test/helpers_test.rb b/test/helpers_test.rb +index 50ee6778..ee1298b2 100644 +--- a/test/helpers_test.rb ++++ b/test/helpers_test.rb +@@ -1845,6 +1845,12 @@ class HelpersTest < Minitest::Test + assert_equal 'http://example.org/foo/bar', body + end + ++ it 'handles integer input' do ++ mock_app { get('/') { uri 123 }} ++ get '/' ++ assert_equal 'http://example.org/123', body ++ end ++ + it 'handles absolute URIs' do + mock_app { get('/') { uri 'http://google.com' }} + get '/' +-- +2.39.2 + + +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Stefan Sundin +Date: Sun, 26 Feb 2023 13:21:27 -0800 +Subject: [PATCH 19/41] Rescue `RuntimeError` when trying to use `SecureRandom` + (#1888) + +From https://bugs.ruby-lang.org/issues/19230 + +> securerandom first checks if Random.urandom is available, and if not available, +> it uses the openssl backend as a degeneration. However, the openssl backend +> does not work because it internally uses Random.urandom to create a seed. + +If the proposed change in Bug #19230 were introduced, NotImplementedError would +be raised and we would end up using the fallback in Sinatra anyway. + +Co-authored-by: Patrik Ragnarsson +--- + lib/sinatra/base.rb | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/lib/sinatra/base.rb b/lib/sinatra/base.rb +index 4982034b..307730b2 100644 +--- a/lib/sinatra/base.rb ++++ b/lib/sinatra/base.rb +@@ -1848,8 +1848,9 @@ module Sinatra + begin + require 'securerandom' + set :session_secret, SecureRandom.hex(64) +- rescue LoadError, NotImplementedError ++ rescue LoadError, NotImplementedError, RuntimeError + # SecureRandom raises a NotImplementedError if no random device is available ++ # RuntimeError raised due to broken openssl backend: https://bugs.ruby-lang.org/issues/19230 + set :session_secret, format('%064x', Kernel.rand((2**256) - 1)) + end + +-- +2.39.2 + + +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Patrik Ragnarsson +Date: Sun, 5 Mar 2023 16:06:38 +0100 +Subject: [PATCH 20/41] CI: Use slim HEAD branch (#1895) + +Needed to run with Tilt 2.1.0 in CI until slim-template/slim#910 is part of a Slim release +--- + .github/workflows/test.yml | 21 ++++++++++----------- + sinatra-contrib/Gemfile | 4 +++- + 2 files changed, 13 insertions(+), 12 deletions(-) + +diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml +index c3e41e67..a33b5068 100644 +--- a/.github/workflows/test.yml ++++ b/.github/workflows/test.yml +@@ -25,25 +25,24 @@ jobs: + rack: + - '~> 2' + tilt: +- - '~> 2.0.0' ++ - stable + # Due to https://github.com/actions/runner/issues/849, we have to use quotes for '3.0' + ruby: [2.6, 2.7, '3.0', 3.1, 3.2, truffleruby] + include: + # Puma +- - { ruby: 3.1, rack: '~> 2', puma: '~> 5', tilt: '~> 2.0.0' } +- - { ruby: 3.2, rack: '~> 2', puma: '~> 6.0', tilt: '~> 2.0.0' } +- - { ruby: 3.2, rack: '~> 2', puma: head, tilt: '~> 2.0.0', allow-failure: true } ++ - { ruby: 3.1, rack: '~> 2', puma: '~> 5', tilt: stable } ++ - { ruby: 3.2, rack: '~> 2', puma: '~> 6.0', tilt: stable } ++ - { ruby: 3.2, rack: '~> 2', puma: head, tilt: stable, allow-failure: true } + # Tilt +- - { ruby: 3.1, rack: '~> 2', puma: stable, tilt: '~> 2', allow-failure: true } +- - { ruby: 3.2, rack: '~> 2', puma: stable, tilt: head, allow-failure: true } ++ - { ruby: 3.2, rack: '~> 2', puma: stable, tilt: head, allow-failure: true } + # Due to flaky tests, see https://github.com/sinatra/sinatra/pull/1870 +- - { ruby: jruby-9.3, rack: '~> 2', puma: stable, tilt: '~> 2.0.0', allow-failure: true } ++ - { ruby: jruby-9.3, rack: '~> 2', puma: stable, tilt: stable, allow-failure: true } + # Due to https://github.com/jruby/jruby/issues/7647 +- - { ruby: jruby-9.4, rack: '~> 2', puma: stable, tilt: '~> 2.0.0', allow-failure: true } ++ - { ruby: jruby-9.4, rack: '~> 2', puma: stable, tilt: stable, allow-failure: true } + # Never fail our build due to problems with head +- - { ruby: ruby-head, rack: '~> 2', puma: stable, tilt: '~> 2.0.0', allow-failure: true } +- - { ruby: jruby-head, rack: '~> 2', puma: stable, tilt: '~> 2.0.0', allow-failure: true } +- - { ruby: truffleruby-head, rack: '~> 2', puma: stable, tilt: '~> 2.0.0', allow-failure: true } ++ - { ruby: ruby-head, rack: '~> 2', puma: stable, tilt: stable, allow-failure: true } ++ - { ruby: jruby-head, rack: '~> 2', puma: stable, tilt: stable, allow-failure: true } ++ - { ruby: truffleruby-head, rack: '~> 2', puma: stable, tilt: stable, allow-failure: true } + env: + rack: ${{ matrix.rack }} + puma: ${{ matrix.puma }} +diff --git a/sinatra-contrib/Gemfile b/sinatra-contrib/Gemfile +index 64cd23a1..83aabc5e 100644 +--- a/sinatra-contrib/Gemfile ++++ b/sinatra-contrib/Gemfile +@@ -19,7 +19,9 @@ group :development, :test do + platform :jruby, :ruby do + gem 'hamlit', '>= 3' + gem 'liquid', '~> 2.6.x' +- gem 'slim' ++ # Use main until there's a slim release that can be used with Tilt 2.1.0 ++ # https://github.com/slim-template/slim/pull/910 ++ gem 'slim', github: 'slim-template/slim' + end + + platform :ruby do +-- +2.39.2 + + +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Patrik Ragnarsson +Date: Sun, 5 Mar 2023 16:23:11 +0100 +Subject: [PATCH 21/41] CI: Use latest liquid release for sinatra-contrib + (#1896) + +2.6.x is very old, https://rubygems.org/gems/liquid/versions + + 2.6.3 - July 23, 2015 (45.5 KB) + +Found these traces in history: + +- +https://github.com/sinatra/sinatra/commit/741677743cbebe2cca5992b4dd767eec89350c30 +- +https://github.com/sinatra/sinatra-contrib/commit/2dc03c879a858a418cfc3697fe7afd0b53e0f766 +--- + sinatra-contrib/Gemfile | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/sinatra-contrib/Gemfile b/sinatra-contrib/Gemfile +index 83aabc5e..e8a225c5 100644 +--- a/sinatra-contrib/Gemfile ++++ b/sinatra-contrib/Gemfile +@@ -18,7 +18,7 @@ group :development, :test do + + platform :jruby, :ruby do + gem 'hamlit', '>= 3' +- gem 'liquid', '~> 2.6.x' ++ gem 'liquid' + # Use main until there's a slim release that can be used with Tilt 2.1.0 + # https://github.com/slim-template/slim/pull/910 + gem 'slim', github: 'slim-template/slim' +-- +2.39.2 + + +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Patrik Ragnarsson +Date: Sun, 5 Mar 2023 16:33:21 +0100 +Subject: [PATCH 22/41] CI: remove redundant Puma job (#1898) + +`~> 6.0` will install latest Puma release, that's what `stable` do too + +Better we add this job once Puma 7 exist. + +[ci skip] +--- + .github/workflows/test.yml | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml +index a33b5068..b14d4172 100644 +--- a/.github/workflows/test.yml ++++ b/.github/workflows/test.yml +@@ -31,7 +31,6 @@ jobs: + include: + # Puma + - { ruby: 3.1, rack: '~> 2', puma: '~> 5', tilt: stable } +- - { ruby: 3.2, rack: '~> 2', puma: '~> 6.0', tilt: stable } + - { ruby: 3.2, rack: '~> 2', puma: head, tilt: stable, allow-failure: true } + # Tilt + - { ruby: 3.2, rack: '~> 2', puma: stable, tilt: head, allow-failure: true } +-- +2.39.2 + + +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Patrik Ragnarsson +Date: Fri, 23 Dec 2022 22:41:02 +0100 +Subject: [PATCH 23/41] Don't allow Rack 3 builds to fail + +--- + .github/workflows/test.yml | 21 +++++++++------------ + 1 file changed, 9 insertions(+), 12 deletions(-) + +diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml +index b14d4172..a79021bf 100644 +--- a/.github/workflows/test.yml ++++ b/.github/workflows/test.yml +@@ -23,25 +23,25 @@ jobs: + puma: + - stable + rack: +- - '~> 2' ++ - stable + tilt: + - stable + # Due to https://github.com/actions/runner/issues/849, we have to use quotes for '3.0' + ruby: [2.6, 2.7, '3.0', 3.1, 3.2, truffleruby] + include: + # Puma +- - { ruby: 3.1, rack: '~> 2', puma: '~> 5', tilt: stable } +- - { ruby: 3.2, rack: '~> 2', puma: head, tilt: stable, allow-failure: true } ++ - { ruby: 3.1, rack: stable, puma: '~> 5', tilt: stable } ++ - { ruby: 3.2, rack: stable, puma: head, tilt: stable, allow-failure: true } + # Tilt +- - { ruby: 3.2, rack: '~> 2', puma: stable, tilt: head, allow-failure: true } ++ - { ruby: 3.2, rack: stable, puma: stable, tilt: head, allow-failure: true } + # Due to flaky tests, see https://github.com/sinatra/sinatra/pull/1870 +- - { ruby: jruby-9.3, rack: '~> 2', puma: stable, tilt: stable, allow-failure: true } ++ - { ruby: jruby-9.3, rack: stable, puma: stable, tilt: stable, allow-failure: true } + # Due to https://github.com/jruby/jruby/issues/7647 +- - { ruby: jruby-9.4, rack: '~> 2', puma: stable, tilt: stable, allow-failure: true } ++ - { ruby: jruby-9.4, rack: stable, puma: stable, tilt: stable, allow-failure: true } + # Never fail our build due to problems with head +- - { ruby: ruby-head, rack: '~> 2', puma: stable, tilt: stable, allow-failure: true } +- - { ruby: jruby-head, rack: '~> 2', puma: stable, tilt: stable, allow-failure: true } +- - { ruby: truffleruby-head, rack: '~> 2', puma: stable, tilt: stable, allow-failure: true } ++ - { ruby: ruby-head, rack: stable, puma: stable, tilt: stable, allow-failure: true } ++ - { ruby: jruby-head, rack: stable, puma: stable, tilt: stable, allow-failure: true } ++ - { ruby: truffleruby-head, rack: stable, puma: stable, tilt: stable, allow-failure: true } + env: + rack: ${{ matrix.rack }} + puma: ${{ matrix.puma }} +@@ -56,7 +56,6 @@ jobs: + libxml2-dev \ + libxslt-dev \ + libyaml-dev +- + - uses: actions/checkout@v3 + + - uses: ruby/setup-ruby@v1 +@@ -78,7 +77,6 @@ jobs: + run: | + bundle install --jobs=3 --retry=3 + bundle exec rake +- + - name: Run rack-protection tests + continue-on-error: ${{ matrix.allow-failure || false }} + id: protection-tests +@@ -86,7 +84,6 @@ jobs: + run: | + bundle install --jobs=3 --retry=3 + bundle exec rake +- + - uses: 8398a7/action-slack@v3 + with: + status: ${{ job.status }} +-- +2.39.2 + + +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Patrik Ragnarsson +Date: Sat, 24 Dec 2022 11:52:55 +0100 +Subject: [PATCH 24/41] Only use rainbows for Rack 2 + +--- + Gemfile | 4 +++- + test/integration/app.rb | 6 +++++- + 2 files changed, 8 insertions(+), 2 deletions(-) + +diff --git a/Gemfile b/Gemfile +index ea870323..cf0adce7 100644 +--- a/Gemfile ++++ b/Gemfile +@@ -46,7 +46,9 @@ gem 'markaby' + gem 'nokogiri', '> 1.5.0' + gem 'pandoc-ruby', '~> 2.0.2' + gem 'rabl' +-gem 'rainbows', platforms: [:mri] # uses #fork ++if rack_version == '~> 2' ++ gem 'rainbows', platforms: [:mri] # uses #fork ++end + gem 'rdiscount', platforms: [:ruby] + gem 'rdoc' + gem 'redcarpet', platforms: [:ruby] +diff --git a/test/integration/app.rb b/test/integration/app.rb +index dbc9e3a2..cd2a0b3c 100644 +--- a/test/integration/app.rb ++++ b/test/integration/app.rb +@@ -1,7 +1,11 @@ + $stderr.puts "loading" + require 'sinatra' + +-require_relative 'rainbows' if RUBY_ENGINE == 'ruby' ++begin ++ require_relative 'rainbows' if RUBY_ENGINE == 'ruby' ++rescue LoadError => e ++ warn "Skipping rainbows (#{e.inspect})" ++end + + configure do + set :foo, :bar +-- +2.39.2 + + +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Patrik Ragnarsson +Date: Sat, 24 Dec 2022 11:57:50 +0100 +Subject: [PATCH 25/41] Add `rackup`, `rack-session` as dependencies + +--- + sinatra.gemspec | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/sinatra.gemspec b/sinatra.gemspec +index 8f9cf599..285b2a17 100644 +--- a/sinatra.gemspec ++++ b/sinatra.gemspec +@@ -46,7 +46,9 @@ RubyGems 2.0 or newer is required to protect against public gem pushes. You can + s.required_ruby_version = '>= 2.6.0' + + s.add_dependency 'mustermann', '~> 3.0' +- s.add_dependency 'rack', '~> 2.2', '>= 2.2.4' ++ s.add_dependency 'rack', '>= 2.2.4', '< 4' ++ s.add_dependency 'rackup', '>= 0.2.3', '< 1' ++ s.add_dependency 'rack-session', '>= 0.3.0', '< 1' + s.add_dependency 'rack-protection', version + s.add_dependency 'tilt', '~> 2.0' + +-- +2.39.2 + + +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Patrik Ragnarsson +Date: Sat, 24 Dec 2022 12:19:26 +0100 +Subject: [PATCH 26/41] Remove Rack 2 jobs + +Probably too hard to make Sinatra work with both Rack 2 and Rack 3 in +the same version. +--- + .github/workflows/test.yml | 1 + + sinatra.gemspec | 2 +- + 2 files changed, 2 insertions(+), 1 deletion(-) + +diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml +index a79021bf..59bba7aa 100644 +--- a/.github/workflows/test.yml ++++ b/.github/workflows/test.yml +@@ -46,6 +46,7 @@ jobs: + rack: ${{ matrix.rack }} + puma: ${{ matrix.puma }} + tilt: ${{ matrix.tilt }} ++ + steps: + - name: Install dependencies + run: | +diff --git a/sinatra.gemspec b/sinatra.gemspec +index 285b2a17..34752098 100644 +--- a/sinatra.gemspec ++++ b/sinatra.gemspec +@@ -46,7 +46,7 @@ RubyGems 2.0 or newer is required to protect against public gem pushes. You can + s.required_ruby_version = '>= 2.6.0' + + s.add_dependency 'mustermann', '~> 3.0' +- s.add_dependency 'rack', '>= 2.2.4', '< 4' ++ s.add_dependency 'rack', '>= 3.0.0.beta1', '< 4' + s.add_dependency 'rackup', '>= 0.2.3', '< 1' + s.add_dependency 'rack-session', '>= 0.3.0', '< 1' + s.add_dependency 'rack-protection', version +-- +2.39.2 + + +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Patrik Ragnarsson +Date: Sat, 24 Dec 2022 12:26:38 +0100 +Subject: [PATCH 27/41] Run tests for sinatra-contrib and rack-protection + +--- + .github/workflows/test.yml | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml +index 59bba7aa..2aae0c26 100644 +--- a/.github/workflows/test.yml ++++ b/.github/workflows/test.yml +@@ -67,12 +67,12 @@ jobs: + bundler-cache: true + + - name: Run sinatra tests +- continue-on-error: ${{ matrix.allow-failure || false }} ++ continue-on-error: true + id: tests + run: bundle exec rake + + - name: Run sinatra-contrib tests +- continue-on-error: ${{ matrix.allow-failure || false }} ++ continue-on-error: true + id: contrib-tests + working-directory: sinatra-contrib + run: | +-- +2.39.2 + + +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Patrik Ragnarsson +Date: Wed, 28 Dec 2022 12:55:29 +0100 +Subject: [PATCH 28/41] Rack 3 requires all response headers to be lowercase + +--- + lib/sinatra/base.rb | 34 +++++++++---------- + lib/sinatra/show_exceptions.rb | 4 +-- + rack-protection/lib/rack/protection/base.rb | 2 +- + .../protection/content_security_policy.rb | 4 +-- + .../lib/rack/protection/cookie_tossing.rb | 2 +- + .../lib/rack/protection/frame_options.rb | 2 +- + .../lib/rack/protection/json_csrf.rb | 2 +- + .../lib/rack/protection/referrer_policy.rb | 2 +- + .../lib/rack/protection/strict_transport.rb | 2 +- + .../lib/rack/protection/xss_header.rb | 4 +-- + .../protection/authenticity_token_spec.rb | 2 +- + .../content_security_policy_spec.rb | 2 +- + .../rack/protection/escaped_params_spec.rb | 10 +++--- + .../lib/rack/protection/frame_options_spec.rb | 2 +- + .../lib/rack/protection/json_csrf_spec.rb | 10 +++--- + .../rack/protection/path_traversal_spec.rb | 4 +-- + .../lib/rack/protection/protection_spec.rb | 4 +-- + .../lib/rack/protection/xss_header_spec.rb | 4 +-- + rack-protection/spec/support/dummy_app.rb | 2 +- + rack-protection/spec/support/spec_helpers.rb | 2 +- + test/response_test.rb | 2 +- + 21 files changed, 51 insertions(+), 51 deletions(-) + +diff --git a/lib/sinatra/base.rb b/lib/sinatra/base.rb +index 307730b2..0c547711 100644 +--- a/lib/sinatra/base.rb ++++ b/lib/sinatra/base.rb +@@ -176,8 +176,8 @@ module Sinatra + result = body + + if drop_content_info? +- headers.delete 'Content-Length' +- headers.delete 'Content-Type' ++ headers.delete 'content-length' ++ headers.delete 'content-type' + end + + if drop_body? +@@ -186,9 +186,9 @@ module Sinatra + end + + if calculate_content_length? +- # if some other code has already set Content-Length, don't muck with it ++ # if some other code has already set content-length, don't muck with it + # currently, this would be the static file-handler +- headers['Content-Length'] = body.map(&:bytesize).reduce(0, :+).to_s ++ headers['content-length'] = body.map(&:bytesize).reduce(0, :+).to_s + end + + [status, headers, result] +@@ -197,7 +197,7 @@ module Sinatra + private + + def calculate_content_length? +- headers['Content-Type'] && !headers['Content-Length'] && (Array === body) ++ headers['content-type'] && !headers['content-length'] && (Array === body) + end + + def drop_content_info? +@@ -291,8 +291,8 @@ module Sinatra + elsif value + # Rack 2.0 returns a Rack::File::Iterator here instead of + # Rack::File as it was in the previous API. +- unless request.head? || value.is_a?(Rack::Files::Iterator) || value.is_a?(Stream) +- headers.delete 'Content-Length' ++ unless request.head? || value.is_a?(Rack::File::Iterator) || value.is_a?(Stream) ++ headers.delete 'content-length' + end + response.body = value + else +@@ -372,10 +372,10 @@ module Sinatra + Base.mime_type(type) + end + +- # Set the Content-Type of the response body given a media type or file ++ # Set the content-type of the response body given a media type or file + # extension. + def content_type(type = nil, params = {}) +- return response['Content-Type'] unless type ++ return response['content-type'] unless type + + default = params.delete :default + mime_type = mime_type(type) || default +@@ -393,7 +393,7 @@ module Sinatra + "#{key}=#{val}" + end.join(', ') + end +- response['Content-Type'] = mime_type ++ response['content-type'] = mime_type + end + + # https://html.spec.whatwg.org/#multipart-form-data +@@ -412,12 +412,12 @@ module Sinatra + params = format('; filename="%s"', File.basename(filename).gsub(/["\r\n]/, MULTIPART_FORM_DATA_REPLACEMENT_TABLE)) + response['Content-Disposition'] << params + ext = File.extname(filename) +- content_type(ext) unless response['Content-Type'] || ext.empty? ++ content_type(ext) unless response['content-type'] || ext.empty? + end + + # Use the contents of the file at +path+ as the response body. + def send_file(path, opts = {}) +- if opts[:type] || !response['Content-Type'] ++ if opts[:type] || !response['content-type'] + content_type opts[:type] || File.extname(path), default: 'application/octet-stream' + end + +@@ -433,7 +433,7 @@ module Sinatra + result = file.serving(request, path) + + result[1].each { |k, v| headers[k] ||= v } +- headers['Content-Length'] = result[1]['Content-Length'] ++ headers['content-length'] = result[1]['content-length'] + opts[:status] &&= Integer(opts[:status]) + halt (opts[:status] || result[0]), result[2] + rescue Errno::ENOENT +@@ -949,7 +949,7 @@ module Sinatra + invoke { dispatch! } + invoke { error_block!(response.status) } unless @env['sinatra.error'] + +- unless @response['Content-Type'] ++ unless @response['content-type'] + if Array === body && body[0].respond_to?(:content_type) + content_type body[0].content_type + elsif (default = settings.default_content_type) +@@ -1012,7 +1012,7 @@ module Sinatra + routes = base.routes[@request.request_method] + + routes&.each do |pattern, conditions, block| +- response.delete_header('Content-Type') unless @pinned_response ++ response.delete_header('content-type') unless @pinned_response + + returned_pass_block = process_route(pattern, conditions) do |*args| + env['sinatra.route'] = "#{@request.request_method} #{pattern}" +@@ -1133,7 +1133,7 @@ module Sinatra + invoke do + static! if settings.static? && (request.get? || request.head?) + filter! :before do +- @pinned_response = !response['Content-Type'].nil? ++ @pinned_response = !response['content-type'].nil? + end + route! + end +@@ -1655,7 +1655,7 @@ module Sinatra + types.map! { |t| mime_types(t) } + types.flatten! + condition do +- response_content_type = response['Content-Type'] ++ response_content_type = response['content-type'] + preferred_type = request.preferred_type(types) + + if response_content_type +diff --git a/lib/sinatra/show_exceptions.rb b/lib/sinatra/show_exceptions.rb +index db847ffa..293e88fb 100644 +--- a/lib/sinatra/show_exceptions.rb ++++ b/lib/sinatra/show_exceptions.rb +@@ -38,8 +38,8 @@ module Sinatra + [ + 500, + { +- 'Content-Type' => content_type, +- 'Content-Length' => body.bytesize.to_s ++ 'content-type' => content_type, ++ 'content-length' => body.bytesize.to_s + }, + [body] + ] +diff --git a/rack-protection/lib/rack/protection/base.rb b/rack-protection/lib/rack/protection/base.rb +index a1314adb..ed570d68 100644 +--- a/rack-protection/lib/rack/protection/base.rb ++++ b/rack-protection/lib/rack/protection/base.rb +@@ -74,7 +74,7 @@ module Rack + + def deny(env) + warn env, "attack prevented by #{self.class}" +- [options[:status], { 'Content-Type' => 'text/plain' }, [options[:message]]] ++ [options[:status], { 'content-type' => 'text/plain' }, [options[:message]]] + end + + def report(env) +diff --git a/rack-protection/lib/rack/protection/content_security_policy.rb b/rack-protection/lib/rack/protection/content_security_policy.rb +index 32d8ac70..d85f28b4 100644 +--- a/rack-protection/lib/rack/protection/content_security_policy.rb ++++ b/rack-protection/lib/rack/protection/content_security_policy.rb +@@ -26,7 +26,7 @@ module Rack + # https://scotthelme.co.uk/csp-cheat-sheet/ + # http://www.html5rocks.com/en/tutorials/security/content-security-policy/ + # +- # Sets the 'Content-Security-Policy[-Report-Only]' header. ++ # Sets the 'content-security-policy[-report-only]' header. + # + # Options: ContentSecurityPolicy configuration is a complex topic with + # several levels of support that has evolved over time. +@@ -71,7 +71,7 @@ module Rack + + def call(env) + status, headers, body = @app.call(env) +- header = options[:report_only] ? 'Content-Security-Policy-Report-Only' : 'Content-Security-Policy' ++ header = options[:report_only] ? 'content-security-policy-report-only' : 'content-security-policy' + headers[header] ||= csp_policy if html? headers + [status, headers, body] + end +diff --git a/rack-protection/lib/rack/protection/cookie_tossing.rb b/rack-protection/lib/rack/protection/cookie_tossing.rb +index 0a7f43ad..fbef360a 100644 +--- a/rack-protection/lib/rack/protection/cookie_tossing.rb ++++ b/rack-protection/lib/rack/protection/cookie_tossing.rb +@@ -51,7 +51,7 @@ module Rack + def redirect(env) + request = Request.new(env) + warn env, "attack prevented by #{self.class}" +- [302, { 'Content-Type' => 'text/html', 'Location' => request.path }, []] ++ [302, { 'content-type' => 'text/html', 'location' => request.path }, []] + end + + def bad_cookies +diff --git a/rack-protection/lib/rack/protection/frame_options.rb b/rack-protection/lib/rack/protection/frame_options.rb +index c159d4a9..80e7fc1e 100644 +--- a/rack-protection/lib/rack/protection/frame_options.rb ++++ b/rack-protection/lib/rack/protection/frame_options.rb +@@ -31,7 +31,7 @@ module Rack + + def call(env) + status, headers, body = @app.call(env) +- headers['X-Frame-Options'] ||= frame_options if html? headers ++ headers['x-frame-options'] ||= frame_options if html? headers + [status, headers, body] + end + end +diff --git a/rack-protection/lib/rack/protection/json_csrf.rb b/rack-protection/lib/rack/protection/json_csrf.rb +index 9f6817ac..59d8e496 100644 +--- a/rack-protection/lib/rack/protection/json_csrf.rb ++++ b/rack-protection/lib/rack/protection/json_csrf.rb +@@ -39,7 +39,7 @@ module Rack + def has_vector?(request, headers) + return false if request.xhr? + return false if options[:allow_if]&.call(request.env) +- return false unless headers['Content-Type'].to_s.split(';', 2).first =~ %r{^\s*application/json\s*$} ++ return false unless headers['content-type'].to_s.split(';', 2).first =~ %r{^\s*application/json\s*$} + + origin(request.env).nil? and referrer(request.env) != request.host + end +diff --git a/rack-protection/lib/rack/protection/referrer_policy.rb b/rack-protection/lib/rack/protection/referrer_policy.rb +index eaff7020..c5c44f42 100644 +--- a/rack-protection/lib/rack/protection/referrer_policy.rb ++++ b/rack-protection/lib/rack/protection/referrer_policy.rb +@@ -19,7 +19,7 @@ module Rack + + def call(env) + status, headers, body = @app.call(env) +- headers['Referrer-Policy'] ||= options[:referrer_policy] ++ headers['referrer-policy'] ||= options[:referrer_policy] + [status, headers, body] + end + end +diff --git a/rack-protection/lib/rack/protection/strict_transport.rb b/rack-protection/lib/rack/protection/strict_transport.rb +index 05fe4ae6..40e45a68 100644 +--- a/rack-protection/lib/rack/protection/strict_transport.rb ++++ b/rack-protection/lib/rack/protection/strict_transport.rb +@@ -33,7 +33,7 @@ module Rack + + def call(env) + status, headers, body = @app.call(env) +- headers['Strict-Transport-Security'] ||= strict_transport ++ headers['strict-transport-security'] ||= strict_transport + [status, headers, body] + end + end +diff --git a/rack-protection/lib/rack/protection/xss_header.rb b/rack-protection/lib/rack/protection/xss_header.rb +index 14c679d9..8eee599b 100644 +--- a/rack-protection/lib/rack/protection/xss_header.rb ++++ b/rack-protection/lib/rack/protection/xss_header.rb +@@ -18,8 +18,8 @@ module Rack + + def call(env) + status, headers, body = @app.call(env) +- headers['X-XSS-Protection'] ||= "1; mode=#{options[:xss_mode]}" if html? headers +- headers['X-Content-Type-Options'] ||= 'nosniff' if options[:nosniff] ++ headers['x-xss-protection'] ||= "1; mode=#{options[:xss_mode]}" if html? headers ++ headers['x-content-type-options'] ||= 'nosniff' if options[:nosniff] + [status, headers, body] + end + end +diff --git a/rack-protection/spec/lib/rack/protection/authenticity_token_spec.rb b/rack-protection/spec/lib/rack/protection/authenticity_token_spec.rb +index e589726b..adf64f1b 100644 +--- a/rack-protection/spec/lib/rack/protection/authenticity_token_spec.rb ++++ b/rack-protection/spec/lib/rack/protection/authenticity_token_spec.rb +@@ -61,7 +61,7 @@ RSpec.describe Rack::Protection::AuthenticityToken do + it 'allows for a custom authenticity token param' do + mock_app do + use Rack::Protection::AuthenticityToken, authenticity_param: 'csrf_param' +- run proc { |_e| [200, { 'Content-Type' => 'text/plain' }, ['hi']] } ++ run proc { |_e| [200, { 'content-type' => 'text/plain' }, ['hi']] } + end + + post('/', { 'csrf_param' => token }, 'rack.session' => { csrf: token }) +diff --git a/rack-protection/spec/lib/rack/protection/content_security_policy_spec.rb b/rack-protection/spec/lib/rack/protection/content_security_policy_spec.rb +index 35a26ad8..0d26c14a 100644 +--- a/rack-protection/spec/lib/rack/protection/content_security_policy_spec.rb ++++ b/rack-protection/spec/lib/rack/protection/content_security_policy_spec.rb +@@ -62,7 +62,7 @@ RSpec.describe Rack::Protection::ContentSecurityPolicy do + end + + it 'should not override the header if already set' do +- mock_app with_headers('Content-Security-Policy' => 'default-src: none') ++ mock_app with_headers('content-security-policy' => 'default-src: none') + expect(get('/', {}, 'wants' => 'text/html').headers['Content-Security-Policy']).to eq('default-src: none') + end + end +diff --git a/rack-protection/spec/lib/rack/protection/escaped_params_spec.rb b/rack-protection/spec/lib/rack/protection/escaped_params_spec.rb +index d6b1727b..f99582f6 100644 +--- a/rack-protection/spec/lib/rack/protection/escaped_params_spec.rb ++++ b/rack-protection/spec/lib/rack/protection/escaped_params_spec.rb +@@ -7,7 +7,7 @@ RSpec.describe Rack::Protection::EscapedParams do + it 'escapes html entities' do + mock_app do |env| + request = Rack::Request.new(env) +- [200, { 'Content-Type' => 'text/plain' }, [request.params['foo']]] ++ [200, { 'content-type' => 'text/plain' }, [request.params['foo']]] + end + get '/', foo: '' + expect(body).to eq('<bar>') +@@ -16,7 +16,7 @@ RSpec.describe Rack::Protection::EscapedParams do + it 'leaves normal params untouched' do + mock_app do |env| + request = Rack::Request.new(env) +- [200, { 'Content-Type' => 'text/plain' }, [request.params['foo']]] ++ [200, { 'content-type' => 'text/plain' }, [request.params['foo']]] + end + get '/', foo: 'bar' + expect(body).to eq('bar') +@@ -25,7 +25,7 @@ RSpec.describe Rack::Protection::EscapedParams do + it 'copes with nested arrays' do + mock_app do |env| + request = Rack::Request.new(env) +- [200, { 'Content-Type' => 'text/plain' }, [request.params['foo']['bar']]] ++ [200, { 'content-type' => 'text/plain' }, [request.params['foo']['bar']]] + end + get '/', foo: { bar: '' } + expect(body).to eq('<bar>') +@@ -33,7 +33,7 @@ RSpec.describe Rack::Protection::EscapedParams do + + it 'leaves cache-breaker params untouched' do + mock_app do |_env| +- [200, { 'Content-Type' => 'text/plain' }, ['hi']] ++ [200, { 'content-type' => 'text/plain' }, ['hi']] + end + + get '/?95df8d9bf5237ad08df3115ee74dcb10' +@@ -43,7 +43,7 @@ RSpec.describe Rack::Protection::EscapedParams do + it 'leaves TempFiles untouched' do + mock_app do |env| + request = Rack::Request.new(env) +- [200, { 'Content-Type' => 'text/plain' }, ["#{request.params['file'][:filename]}\n#{request.params['file'][:tempfile].read}\n#{request.params['other']}"]] ++ [200, { 'content-type' => 'text/plain' }, ["#{request.params['file'][:filename]}\n#{request.params['file'][:tempfile].read}\n#{request.params['other']}"]] + end + + temp_file = File.open('_escaped_params_tmp_file', 'w') +diff --git a/rack-protection/spec/lib/rack/protection/frame_options_spec.rb b/rack-protection/spec/lib/rack/protection/frame_options_spec.rb +index 23ba8f11..aa516072 100644 +--- a/rack-protection/spec/lib/rack/protection/frame_options_spec.rb ++++ b/rack-protection/spec/lib/rack/protection/frame_options_spec.rb +@@ -32,7 +32,7 @@ RSpec.describe Rack::Protection::FrameOptions do + end + + it 'should not override the header if already set' do +- mock_app with_headers('X-Frame-Options' => 'allow') ++ mock_app with_headers('x-frame-options' => 'allow') + expect(get('/', {}, 'wants' => 'text/html').headers['X-Frame-Options']).to eq('allow') + end + end +diff --git a/rack-protection/spec/lib/rack/protection/json_csrf_spec.rb b/rack-protection/spec/lib/rack/protection/json_csrf_spec.rb +index 06312665..59167656 100644 +--- a/rack-protection/spec/lib/rack/protection/json_csrf_spec.rb ++++ b/rack-protection/spec/lib/rack/protection/json_csrf_spec.rb +@@ -24,13 +24,13 @@ RSpec.describe Rack::Protection::JsonCsrf do + + def self.call(env) + Thread.current[:last_env] = env +- [200, { 'Content-Type' => 'application/json' }, body] ++ [200, { 'content-type' => 'application/json' }, body] + end + end + + describe 'json response' do + before do +- mock_app { |_e| [200, { 'Content-Type' => 'application/json' }, []] } ++ mock_app { |_e| [200, { 'content-type' => 'application/json' }, []] } + end + + it 'denies get requests with json responses with a remote referrer' do +@@ -39,7 +39,7 @@ RSpec.describe Rack::Protection::JsonCsrf do + + it 'closes the body returned by the app if it denies the get request' do + mock_app DummyAppWithBody do |_e| +- [200, { 'Content-Type' => 'application/json' }, []] ++ [200, { 'content-type' => 'application/json' }, []] + end + + get('/', {}, 'HTTP_REFERER' => 'http://evil.com') +@@ -50,7 +50,7 @@ RSpec.describe Rack::Protection::JsonCsrf do + it 'accepts requests with json responses with a remote referrer when allow_if is true' do + mock_app do + use Rack::Protection::JsonCsrf, allow_if: ->(env) { env['HTTP_REFERER'] == 'http://good.com' } +- run proc { |_e| [200, { 'Content-Type' => 'application/json' }, []] } ++ run proc { |_e| [200, { 'content-type' => 'application/json' }, []] } + end + + expect(get('/', {}, 'HTTP_REFERER' => 'http://good.com')).to be_ok +@@ -88,7 +88,7 @@ RSpec.describe Rack::Protection::JsonCsrf do + it 'still denies' do + mock_app do + use Rack::Protection, reaction: :drop_session +- run proc { |_e| [200, { 'Content-Type' => 'application/json' }, []] } ++ run proc { |_e| [200, { 'content-type' => 'application/json' }, []] } + end + + session = { foo: :bar } +diff --git a/rack-protection/spec/lib/rack/protection/path_traversal_spec.rb b/rack-protection/spec/lib/rack/protection/path_traversal_spec.rb +index 1eca34bd..a5921f15 100644 +--- a/rack-protection/spec/lib/rack/protection/path_traversal_spec.rb ++++ b/rack-protection/spec/lib/rack/protection/path_traversal_spec.rb +@@ -5,7 +5,7 @@ RSpec.describe Rack::Protection::PathTraversal do + + context 'escaping' do + before do +- mock_app { |e| [200, { 'Content-Type' => 'text/plain' }, [e['PATH_INFO']]] } ++ mock_app { |e| [200, { 'content-type' => 'text/plain' }, [e['PATH_INFO']]] } + end + + %w[/foo/bar /foo/bar/ / /.f /a.x].each do |path| +@@ -28,7 +28,7 @@ RSpec.describe Rack::Protection::PathTraversal do + + context "PATH_INFO's encoding" do + before do +- @app = Rack::Protection::PathTraversal.new(proc { |e| [200, { 'Content-Type' => 'text/plain' }, [e['PATH_INFO'].encoding.to_s]] }) ++ @app = Rack::Protection::PathTraversal.new(proc { |e| [200, { 'content-type' => 'text/plain' }, [e['PATH_INFO'].encoding.to_s]] }) + end + + it 'should remain unchanged as ASCII-8BIT' do +diff --git a/rack-protection/spec/lib/rack/protection/protection_spec.rb b/rack-protection/spec/lib/rack/protection/protection_spec.rb +index e8dc55df..5213c870 100644 +--- a/rack-protection/spec/lib/rack/protection/protection_spec.rb ++++ b/rack-protection/spec/lib/rack/protection/protection_spec.rb +@@ -6,7 +6,7 @@ RSpec.describe Rack::Protection do + it 'passes on options' do + mock_app do + use Rack::Protection, track: ['HTTP_FOO'] +- run proc { |_e| [200, { 'Content-Type' => 'text/plain' }, ['hi']] } ++ run proc { |_e| [200, { 'content-type' => 'text/plain' }, ['hi']] } + end + + session = { foo: :bar } +@@ -21,7 +21,7 @@ RSpec.describe Rack::Protection do + it 'passes errors through if :reaction => :report is used' do + mock_app do + use Rack::Protection, reaction: :report +- run proc { |e| [200, { 'Content-Type' => 'text/plain' }, [e['protection.failed'].to_s]] } ++ run proc { |e| [200, { 'content-type' => 'text/plain' }, [e['protection.failed'].to_s]] } + end + + session = { foo: :bar } +diff --git a/rack-protection/spec/lib/rack/protection/xss_header_spec.rb b/rack-protection/spec/lib/rack/protection/xss_header_spec.rb +index 2fef0edb..bd98eef6 100644 +--- a/rack-protection/spec/lib/rack/protection/xss_header_spec.rb ++++ b/rack-protection/spec/lib/rack/protection/xss_header_spec.rb +@@ -26,7 +26,7 @@ RSpec.describe Rack::Protection::XSSHeader do + end + + it 'should not override the header if already set' do +- mock_app with_headers('X-XSS-Protection' => '0') ++ mock_app with_headers('x-xss-protection' => '0') + expect(get('/', {}, 'wants' => 'text/html').headers['X-XSS-Protection']).to eq('0') + end + +@@ -48,7 +48,7 @@ RSpec.describe Rack::Protection::XSSHeader do + end + + it 'should not override the header if already set X-Content-Type-Options' do +- mock_app with_headers('X-Content-Type-Options' => 'sniff') ++ mock_app with_headers('x-content-type-options' => 'sniff') + expect(get('/', {}, 'wants' => 'text/html').headers['X-Content-Type-Options']).to eq('sniff') + end + end +diff --git a/rack-protection/spec/support/dummy_app.rb b/rack-protection/spec/support/dummy_app.rb +index fb83d36b..0b73ae57 100644 +--- a/rack-protection/spec/support/dummy_app.rb ++++ b/rack-protection/spec/support/dummy_app.rb +@@ -4,6 +4,6 @@ module DummyApp + def self.call(env) + Thread.current[:last_env] = env + body = (env['REQUEST_METHOD'] == 'HEAD' ? '' : 'ok') +- [200, { 'Content-Type' => env['wants'] || 'text/plain' }, [body]] ++ [200, { 'content-type' => env['wants'] || 'text/plain' }, [body]] + end + end +diff --git a/rack-protection/spec/support/spec_helpers.rb b/rack-protection/spec/support/spec_helpers.rb +index d9f40a27..4a47bb68 100644 +--- a/rack-protection/spec/support/spec_helpers.rb ++++ b/rack-protection/spec/support/spec_helpers.rb +@@ -29,7 +29,7 @@ module SpecHelpers + end + + def with_headers(headers) +- proc { [200, { 'Content-Type' => 'text/plain' }.merge(headers), ['ok']] } ++ proc { [200, { 'content-type' => 'text/plain' }.merge(headers), ['ok']] } + end + + def env +diff --git a/test/response_test.rb b/test/response_test.rb +index 5c1e6057..5c85330d 100644 +--- a/test/response_test.rb ++++ b/test/response_test.rb +@@ -40,7 +40,7 @@ class ResponseTest < Minitest::Test + @response.body = ['Hello World'] + assert_equal [ + status_code, +- { 'Content-Type' => 'text/html', 'Content-Length' => '11' }, ++ { 'content-type' => 'text/html', 'content-length' => '11' }, + ['Hello World'] + ], @response.finish + end +-- +2.39.2 + + +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Patrik Ragnarsson +Date: Wed, 28 Dec 2022 18:22:42 +0100 +Subject: [PATCH 29/41] `SERVER_PROTOCOL` -> `HTTP_VERSION` + +Similar change as https://github.com/jeremyevans/roda/commit/50f0ddf06728f8fd1b460e7a643eb48025d2ef17 + +Good to know is that rack-test defaults to HTTP/1.0: +https://github.com/rack/rack-test/blob/v2.0.2/lib/rack/test.rb#L277-L285 +(so Rack::Lint does not catch all usage of HTTP_VERSION) +--- + lib/sinatra/base.rb | 5 ++++- + .../spec/lib/rack/protection/session_hijacking_spec.rb | 4 ++-- + test/helpers_test.rb | 4 ++-- + 3 files changed, 8 insertions(+), 5 deletions(-) + +diff --git a/lib/sinatra/base.rb b/lib/sinatra/base.rb +index 0c547711..e990d4b9 100644 +--- a/lib/sinatra/base.rb ++++ b/lib/sinatra/base.rb +@@ -302,7 +302,10 @@ module Sinatra + + # Halt processing and redirect to the URI provided. + def redirect(uri, *args) +- if (env['HTTP_VERSION'] == 'HTTP/1.1') && (env['REQUEST_METHOD'] != 'GET') ++ # SERVER_PROTOCOL is required in Rack 3, fall back to HTTP_VERSION ++ # for servers not updated for Rack 3 (like Puma 5) ++ http_version = env['SERVER_PROTOCOL'] || env['HTTP_VERSION'] ++ if (http_version == 'HTTP/1.1') && (env['REQUEST_METHOD'] != 'GET') + status 303 + else + status 302 +diff --git a/rack-protection/spec/lib/rack/protection/session_hijacking_spec.rb b/rack-protection/spec/lib/rack/protection/session_hijacking_spec.rb +index e39497b1..508b5814 100644 +--- a/rack-protection/spec/lib/rack/protection/session_hijacking_spec.rb ++++ b/rack-protection/spec/lib/rack/protection/session_hijacking_spec.rb +@@ -27,8 +27,8 @@ RSpec.describe Rack::Protection::SessionHijacking do + + it 'accepts requests with a changing Version header' do + session = { foo: :bar } +- get '/', {}, 'rack.session' => session, 'HTTP_VERSION' => '1.0' +- get '/', {}, 'rack.session' => session, 'HTTP_VERSION' => '1.1' ++ get '/', {}, 'rack.session' => session, 'SERVER_PROTOCOL' => 'HTTP/1.0' ++ get '/', {}, 'rack.session' => session, 'SERVER_PROTOCOL' => 'HTTP/1.1' + expect(session[:foo]).to eq(:bar) + end + end +diff --git a/test/helpers_test.rb b/test/helpers_test.rb +index ee1298b2..04117610 100644 +--- a/test/helpers_test.rb ++++ b/test/helpers_test.rb +@@ -236,7 +236,7 @@ class HelpersTest < Minitest::Test + + it 'uses 303 for post requests if request is HTTP 1.1' do + mock_app { post('/') { redirect '/'} } +- post('/', {}, 'HTTP_VERSION' => 'HTTP/1.1') ++ post('/', {}, 'SERVER_PROTOCOL' => 'HTTP/1.1') + assert_equal 303, status + assert_equal '', body + assert_equal 'http://example.org/', response['Location'] +@@ -244,7 +244,7 @@ class HelpersTest < Minitest::Test + + it 'uses 302 for post requests if request is HTTP 1.0' do + mock_app { post('/') { redirect '/'} } +- post('/', {}, 'HTTP_VERSION' => 'HTTP/1.0') ++ post('/', {}, 'SERVER_PROTOCOL' => 'HTTP/1.0') + assert_equal 302, status + assert_equal '', body + assert_equal 'http://example.org/', response['Location'] +-- +2.39.2 + + +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Patrik Ragnarsson +Date: Wed, 28 Dec 2022 18:43:11 +0100 +Subject: [PATCH 30/41] Rack 3 does not allow newlines in headers + +See https://github.com/rack/rack/issues/1598, https://github.com/rack/rack/pull/1793 +--- + sinatra-contrib/lib/sinatra/link_header.rb | 8 ++++---- + sinatra-contrib/spec/link_header_spec.rb | 2 +- + 2 files changed, 5 insertions(+), 5 deletions(-) + +diff --git a/sinatra-contrib/lib/sinatra/link_header.rb b/sinatra-contrib/lib/sinatra/link_header.rb +index fcf1dcb4..022c26bb 100644 +--- a/sinatra-contrib/lib/sinatra/link_header.rb ++++ b/sinatra-contrib/lib/sinatra/link_header.rb +@@ -89,10 +89,10 @@ module Sinatra + link = (response['Link'] ||= '') + + urls.map do |url| +- link << ",\n" unless link.empty? ++ link << "," unless link.empty? + link << (http_pattern % url) + html_pattern % url +- end.join "\n" ++ end.join + end + + ## +@@ -117,10 +117,10 @@ module Sinatra + yield if block_given? + return '' unless response.include? 'Link' + +- response['Link'].split(",\n").map do |line| ++ response['Link'].split(",").map do |line| + url, *opts = line.split(';').map(&:strip) + "" +- end.join "\n" ++ end.join + end + + def self.registered(_base) +diff --git a/sinatra-contrib/spec/link_header_spec.rb b/sinatra-contrib/spec/link_header_spec.rb +index f18cabb0..dfd07c8e 100644 +--- a/sinatra-contrib/spec/link_header_spec.rb ++++ b/sinatra-contrib/spec/link_header_spec.rb +@@ -41,7 +41,7 @@ RSpec.describe Sinatra::LinkHeader do + it "takes an options hash" do + get '/' + elements = ["", "foo=\"bar\"", "rel=\"from-filter\""] +- expect(headers['Link'].split(",\n").first.strip.split('; ').sort).to eq(elements) ++ expect(headers['Link'].split(",").first.strip.split('; ').sort).to eq(elements) + end + end + +-- +2.39.2 + + +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Patrik Ragnarsson +Date: Wed, 28 Dec 2022 18:52:13 +0100 +Subject: [PATCH 31/41] Find `RACK_SESSION_UNPACKED_COOKIE_DATA` + +It moved to Rack::Session: +https://github.com/rack/rack-session/blob/v0.3.0/lib/rack/session/constants.rb +--- + rack-protection/lib/rack/protection/encrypted_cookie.rb | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/rack-protection/lib/rack/protection/encrypted_cookie.rb b/rack-protection/lib/rack/protection/encrypted_cookie.rb +index bf682312..006d496a 100644 +--- a/rack-protection/lib/rack/protection/encrypted_cookie.rb ++++ b/rack-protection/lib/rack/protection/encrypted_cookie.rb +@@ -202,7 +202,7 @@ module Rack + end + + def unpacked_cookie_data(request) +- request.fetch_header(RACK_SESSION_UNPACKED_COOKIE_DATA) do |k| ++ request.fetch_header(Session::RACK_SESSION_UNPACKED_COOKIE_DATA) do |k| + session_data = cookie_data = request.cookies[@key] + + # Try to decrypt with the first secret, if that returns nil, try +-- +2.39.2 + + +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Patrik Ragnarsson +Date: Wed, 28 Dec 2022 21:31:44 +0100 +Subject: [PATCH 32/41] rack-protection tests now pass (on Ruby 2.7.7) + +--- + rack-protection/lib/rack/protection.rb | 1 + + rack-protection/spec/lib/rack/protection/cookie_tossing_spec.rb | 2 +- + 2 files changed, 2 insertions(+), 1 deletion(-) + +diff --git a/rack-protection/lib/rack/protection.rb b/rack-protection/lib/rack/protection.rb +index 21d2ec92..b4a5429e 100644 +--- a/rack-protection/lib/rack/protection.rb ++++ b/rack-protection/lib/rack/protection.rb +@@ -2,6 +2,7 @@ + + require 'rack/protection/version' + require 'rack' ++require 'rack/session' + + module Rack + module Protection +diff --git a/rack-protection/spec/lib/rack/protection/cookie_tossing_spec.rb b/rack-protection/spec/lib/rack/protection/cookie_tossing_spec.rb +index f24b9920..9e90b438 100644 +--- a/rack-protection/spec/lib/rack/protection/cookie_tossing_spec.rb ++++ b/rack-protection/spec/lib/rack/protection/cookie_tossing_spec.rb +@@ -29,7 +29,7 @@ RSpec.describe Rack::Protection::CookieTossing do + it 'adds the correct Set-Cookie header' do + get '/some/path', {}, 'HTTP_COOKIE' => 'rack.%73ession=EVIL_SESSION_TOKEN; rack.session=EVIL_SESSION_TOKEN; rack.session=SESSION_TOKEN' + +- expected_header = <<-END.chomp ++ expected_header = <<-END.chomp.split("\n") + rack.%2573ession=; domain=example.org; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT + rack.%2573ession=; domain=example.org; path=/some; expires=Thu, 01 Jan 1970 00:00:00 GMT + rack.%2573ession=; domain=example.org; path=/some/path; expires=Thu, 01 Jan 1970 00:00:00 GMT +-- +2.39.2 + + +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Patrik Ragnarsson +Date: Wed, 28 Dec 2022 21:44:10 +0100 +Subject: [PATCH 33/41] Cookie attributes are lowercase in Rack 3 + +See https://github.com/rack/rack/pull/1849, https://github.com/rack/rack/issues/1848 +--- + sinatra-contrib/spec/cookies_spec.rb | 12 ++++++------ + 1 file changed, 6 insertions(+), 6 deletions(-) + +diff --git a/sinatra-contrib/spec/cookies_spec.rb b/sinatra-contrib/spec/cookies_spec.rb +index 852f6425..bab46365 100644 +--- a/sinatra-contrib/spec/cookies_spec.rb ++++ b/sinatra-contrib/spec/cookies_spec.rb +@@ -93,7 +93,7 @@ RSpec.describe Sinatra::Cookies do + expect(cookie_route do + cookies['foo'] = 'bar' + response['Set-Cookie'].lines.detect { |l| l.start_with? 'foo=' } +- end).to include('HttpOnly') ++ end).to include('httponly') + end + + it 'sets domain to nil if localhost' do +@@ -217,7 +217,7 @@ RSpec.describe Sinatra::Cookies do + cookies.delete 'foo' + response['Set-Cookie'] + end +- expect(cookie_header).to include("path=/foo;", "domain=bar.com;", "secure;", "HttpOnly") ++ expect(cookie_header).to include("path=/foo;", "domain=bar.com;", "secure;", "httponly") + end + + it 'does not touch other cookies' do +@@ -724,20 +724,20 @@ RSpec.describe Sinatra::Cookies do + expect(cookie_jar['foo']).to eq('bar') + end + +- it 'sets a cookie with HttpOnly' do ++ it 'sets a cookie with httponly' do + expect(cookie_route do + request.script_name = '/foo' + cookies.set('foo', value: 'bar', httponly: true) + response['Set-Cookie'].lines.detect { |l| l.start_with? 'foo=' } +- end).to include('HttpOnly') ++ end).to include('httponly') + end + +- it 'sets a cookie without HttpOnly' do ++ it 'sets a cookie without httponly' do + expect(cookie_route do + request.script_name = '/foo' + cookies.set('foo', value: 'bar', httponly: false) + response['Set-Cookie'].lines.detect { |l| l.start_with? 'foo=' } +- end).not_to include('HttpOnly') ++ end).not_to include('httponly') + end + end + +-- +2.39.2 + + +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Patrik Ragnarsson +Date: Thu, 29 Dec 2022 14:36:32 +0100 +Subject: [PATCH 34/41] sinatra-contrib tests now pass + +Multiple response header values are encoded using an Array instead of +newlines: https://github.com/rack/rack/blob/v3.0.3/UPGRADE-GUIDE.md#multiple-response-header-values-are-encoded-using-an-array + +Rack 3 does not remove cookies from the internal storage (because it +doesn't make much sense), see https://github.com/rack/rack/pull/1844, https://github.com/rack/rack/issues/1840 +--- + sinatra-contrib/lib/sinatra/cookies.rb | 10 +++++----- + sinatra-contrib/spec/cookies_spec.rb | 25 +++++++++++++----------- + sinatra-contrib/spec/link_header_spec.rb | 2 +- + 3 files changed, 20 insertions(+), 17 deletions(-) + +diff --git a/sinatra-contrib/lib/sinatra/cookies.rb b/sinatra-contrib/lib/sinatra/cookies.rb +index 742f45d7..d6303ce8 100644 +--- a/sinatra-contrib/lib/sinatra/cookies.rb ++++ b/sinatra-contrib/lib/sinatra/cookies.rb +@@ -60,7 +60,7 @@ module Sinatra + attr_reader :options + + def initialize(app) +- @response_string = nil ++ @response_array = nil + @response_hash = {} + @response = app.response + @request = app.request +@@ -309,12 +309,12 @@ module Sinatra + end + + def parse_response +- string = @response['Set-Cookie'] +- return if @response_string == string ++ cookies_from_response = Array(@response['Set-Cookie']) ++ return if @response_array == cookies_from_response + + hash = {} + +- string.each_line do |line| ++ cookies_from_response.each do |line| + key, value = line.split(';', 2).first.to_s.split('=', 2) + next if key.nil? + +@@ -328,7 +328,7 @@ module Sinatra + end + + @response_hash.replace hash +- @response_string = string ++ @response_array = cookies_from_response + end + + def request_cookies +diff --git a/sinatra-contrib/spec/cookies_spec.rb b/sinatra-contrib/spec/cookies_spec.rb +index bab46365..a87a6b36 100644 +--- a/sinatra-contrib/spec/cookies_spec.rb ++++ b/sinatra-contrib/spec/cookies_spec.rb +@@ -153,12 +153,12 @@ RSpec.describe Sinatra::Cookies do + expect(jar['foo']).to be_nil + end + +- it 'removes response cookies from cookies hash' do ++ it 'does not remove response cookies from cookies hash' do + expect(cookie_route do + cookies['foo'] = 'bar' + cookies.clear + cookies['foo'] +- end).to be_nil ++ end).to eq('bar') + end + + it 'expires existing cookies' do +@@ -189,12 +189,12 @@ RSpec.describe Sinatra::Cookies do + expect(jar['foo']).to be_nil + end + +- it 'removes response cookies from cookies hash' do ++ it 'does not remove response cookies from cookies hash' do + expect(cookie_route do + cookies['foo'] = 'bar' + cookies.delete 'foo' + cookies['foo'] +- end).to be_nil ++ end).to eq('bar') + end + + it 'expires existing cookies' do +@@ -246,13 +246,16 @@ RSpec.describe Sinatra::Cookies do + end + + describe :delete_if do +- it 'deletes cookies that match the block' do ++ it 'expires cookies that match the block' do + expect(cookie_route('foo=bar') do + cookies['bar'] = 'baz' + cookies['baz'] = 'foo' + cookies.delete_if { |*a| a.include? 'bar' } +- cookies.values_at 'foo', 'bar', 'baz' +- end).to eq([nil, nil, 'foo']) ++ response['Set-Cookie'] ++ end).to eq(["bar=baz; domain=example.org; path=/; httponly", ++ "baz=foo; domain=example.org; path=/; httponly", ++ "foo=; domain=example.org; path=/; max-age=0; expires=Thu, 01 Jan 1970 00:00:00 GMT; httponly", ++ "bar=; domain=example.org; path=/; max-age=0; expires=Thu, 01 Jan 1970 00:00:00 GMT; httponly"]) + end + end + +@@ -454,12 +457,12 @@ RSpec.describe Sinatra::Cookies do + end).to be false + end + +- it 'becomes true if response cookies are removed' do ++ it 'deos not become true if response cookies are removed' do + expect(cookie_route do + cookies['foo'] = 'bar' + cookies.delete :foo + cookies.empty? +- end).to be true ++ end).to be false + end + + it 'becomes true if request cookies are removed' do +@@ -469,12 +472,12 @@ RSpec.describe Sinatra::Cookies do + end).to be_truthy + end + +- it 'becomes true after clear' do ++ it 'does not become true after clear' do + expect(cookie_route('foo=bar', 'bar=baz') do + cookies['foo'] = 'bar' + cookies.clear + cookies.empty? +- end).to be_truthy ++ end).to be false + end + end + +diff --git a/sinatra-contrib/spec/link_header_spec.rb b/sinatra-contrib/spec/link_header_spec.rb +index dfd07c8e..2bea382f 100644 +--- a/sinatra-contrib/spec/link_header_spec.rb ++++ b/sinatra-contrib/spec/link_header_spec.rb +@@ -30,7 +30,7 @@ RSpec.describe Sinatra::LinkHeader do + describe :link do + it "sets link headers" do + get '/' +- expect(headers['Link'].lines).to include('; rel="something"') ++ expect(headers['Link']).to include('; rel="something"') + end + + it "returns link html tags" do +-- +2.39.2 + + +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Patrik Ragnarsson +Date: Fri, 30 Dec 2022 13:13:48 +0100 +Subject: [PATCH 35/41] Fix server registration + +Co-authored-by: Samuel Williams +--- + test/server_test.rb | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/test/server_test.rb b/test/server_test.rb +index 764295b8..c85a92a3 100644 +--- a/test/server_test.rb ++++ b/test/server_test.rb +@@ -24,7 +24,7 @@ module Rack::Handler + end + end + +- register 'mock', 'Rack::Handler::Mock' ++ register :mock, Mock + end + + class ServerTest < Minitest::Test +-- +2.39.2 + + +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Patrik Ragnarsson +Date: Fri, 30 Dec 2022 20:39:37 +0100 +Subject: [PATCH 36/41] Remove mongrel, time to let go of the past + +--- + examples/stream.ru | 1 - + lib/sinatra/base.rb | 3 +-- + 2 files changed, 1 insertion(+), 3 deletions(-) + +diff --git a/examples/stream.ru b/examples/stream.ru +index ee1615d6..272236c6 100644 +--- a/examples/stream.ru ++++ b/examples/stream.ru +@@ -4,7 +4,6 @@ + # + # run *one* of these: + # +-# rackup -s mongrel stream.ru # gem install mongrel + # unicorn stream.ru # gem install unicorn + # puma stream.ru # gem install puma + # rainbows -c rainbows.conf stream.ru # gem install rainbows eventmachine +diff --git a/lib/sinatra/base.rb b/lib/sinatra/base.rb +index e990d4b9..2de23f7e 100644 +--- a/lib/sinatra/base.rb ++++ b/lib/sinatra/base.rb +@@ -1520,7 +1520,7 @@ module Sinatra + alias stop! quit! + + # Run the Sinatra app as a self-hosted server using +- # Puma, Falcon, Mongrel, or WEBrick (in that order). If given a block, will call ++ # Puma, Falcon or WEBrick (in that order). If given a block, will call + # with the constructed handler once we have taken the stage. + def run!(options = {}, &block) + return if running? +@@ -1875,7 +1875,6 @@ module Sinatra + + server.unshift 'puma' + server.unshift 'falcon' if ruby_engine != 'jruby' +- server.unshift 'mongrel' if ruby_engine.nil? + server.unshift 'thin' if ruby_engine != 'jruby' + server.unshift 'trinidad' if ruby_engine == 'jruby' + +-- +2.39.2 + + +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Patrik Ragnarsson +Date: Mon, 13 Feb 2023 20:45:27 +0100 +Subject: [PATCH 37/41] Restore `continue-on-error` + +Revert "Run tests for sinatra-contrib and rack-protection" + +This reverts commit 13b5b0f9c375fbfe6afdb3e19533fbf5bb1dbc32. +--- + .github/workflows/test.yml | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml +index 2aae0c26..59bba7aa 100644 +--- a/.github/workflows/test.yml ++++ b/.github/workflows/test.yml +@@ -67,12 +67,12 @@ jobs: + bundler-cache: true + + - name: Run sinatra tests +- continue-on-error: true ++ continue-on-error: ${{ matrix.allow-failure || false }} + id: tests + run: bundle exec rake + + - name: Run sinatra-contrib tests +- continue-on-error: true ++ continue-on-error: ${{ matrix.allow-failure || false }} + id: contrib-tests + working-directory: sinatra-contrib + run: | +-- +2.39.2 + + +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Patrik Ragnarsson +Date: Fri, 24 Feb 2023 18:25:00 +0100 +Subject: [PATCH 38/41] Depend on `rackup` and `rack-session` >=2.0.0 + +The 2.0 series of these games are the same as the 0.x series, as these +gems were created for Rack 3. +The 1.x series of these games are for Rack 2. +--- + sinatra.gemspec | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/sinatra.gemspec b/sinatra.gemspec +index 34752098..ea544577 100644 +--- a/sinatra.gemspec ++++ b/sinatra.gemspec +@@ -47,8 +47,8 @@ RubyGems 2.0 or newer is required to protect against public gem pushes. You can + + s.add_dependency 'mustermann', '~> 3.0' + s.add_dependency 'rack', '>= 3.0.0.beta1', '< 4' +- s.add_dependency 'rackup', '>= 0.2.3', '< 1' +- s.add_dependency 'rack-session', '>= 0.3.0', '< 1' ++ s.add_dependency 'rackup', '>= 2.0.0', '< 3' ++ s.add_dependency 'rack-session', '>= 2.0.0', '< 3' + s.add_dependency 'rack-protection', version + s.add_dependency 'tilt', '~> 2.0' + +-- +2.39.2 + + +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Patrik Ragnarsson +Date: Fri, 24 Feb 2023 18:33:27 +0100 +Subject: [PATCH 39/41] `Rack::File` -> `Rack::Files` again + +Rebase gone wrong. + +Remove the old comment from 1dfae3d87e236785cade93b7b2d1adbd4b450eaa. +--- + lib/sinatra/base.rb | 4 +--- + 1 file changed, 1 insertion(+), 3 deletions(-) + +diff --git a/lib/sinatra/base.rb b/lib/sinatra/base.rb +index 2de23f7e..07673337 100644 +--- a/lib/sinatra/base.rb ++++ b/lib/sinatra/base.rb +@@ -289,9 +289,7 @@ module Sinatra + def block.each; yield(call) end + response.body = block + elsif value +- # Rack 2.0 returns a Rack::File::Iterator here instead of +- # Rack::File as it was in the previous API. +- unless request.head? || value.is_a?(Rack::File::Iterator) || value.is_a?(Stream) ++ unless request.head? || value.is_a?(Rack::Files::Iterator) || value.is_a?(Stream) + headers.delete 'content-length' + end + response.body = value +-- +2.39.2 + + +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Patrik Ragnarsson +Date: Sat, 25 Feb 2023 19:29:27 +0100 +Subject: [PATCH 40/41] Support `text/javascript` as JavaScript MIME type + +Next Rack release (probably 3.1) will include this change: + +https://github.com/rack/rack/commit/1bd0f1597d8f4a90d47115f3e156a8ce7870c9c8 +https://github.com/sinatra/sinatra/pull/1857#issuecomment-1445062212 +--- + lib/sinatra/base.rb | 8 +++++++- + sinatra-contrib/spec/json_spec.rb | 5 ++++- + test/helpers_test.rb | 4 +++- + 3 files changed, 14 insertions(+), 3 deletions(-) + +diff --git a/lib/sinatra/base.rb b/lib/sinatra/base.rb +index 07673337..cdeb655b 100644 +--- a/lib/sinatra/base.rb ++++ b/lib/sinatra/base.rb +@@ -1402,7 +1402,13 @@ module Sinatra + # mime_types :js # => ['application/javascript', 'text/javascript'] + def mime_types(type) + type = mime_type type +- type =~ %r{^application/(xml|javascript)$} ? [type, "text/#{$1}"] : [type] ++ if type =~ %r{^application/(xml|javascript)$} ++ [type, "text/#{$1}"] ++ elsif type =~ %r{^text/(xml|javascript)$} ++ [type, "application/#{$1}"] ++ else ++ [type] ++ end + end + + # Define a before filter; runs before all requests within the same +diff --git a/sinatra-contrib/spec/json_spec.rb b/sinatra-contrib/spec/json_spec.rb +index ec76c42f..047cdda0 100644 +--- a/sinatra-contrib/spec/json_spec.rb ++++ b/sinatra-contrib/spec/json_spec.rb +@@ -57,7 +57,10 @@ RSpec.describe Sinatra::JSON do + + it "accepts shorthands for :content_type" do + mock_app { get('/') { json({}, :content_type => :js) } } +- expect(get('/')["Content-Type"]).to eq("application/javascript;charset=utf-8") ++ # Changed to "text/javascript" in Rack >3.0 ++ # https://github.com/sinatra/sinatra/pull/1857#issuecomment-1445062212 ++ expect(get('/')["Content-Type"]) ++ .to eq("application/javascript;charset=utf-8").or eq("text/javascript;charset=utf-8") + end + + it 'calls generate on :encoder if available' do +diff --git a/test/helpers_test.rb b/test/helpers_test.rb +index 04117610..a856bb91 100644 +--- a/test/helpers_test.rb ++++ b/test/helpers_test.rb +@@ -681,11 +681,13 @@ class HelpersTest < Minitest::Test + assert_equal content_type(:foo), 'text/foo;charset=utf-8' + assert_equal content_type(:xml), 'application/xml;charset=utf-8' + assert_equal content_type(:xhtml), 'application/xhtml+xml;charset=utf-8' +- assert_equal content_type(:js), 'application/javascript;charset=utf-8' + assert_equal content_type(:json), 'application/json' + assert_equal content_type(:bar), 'application/bar' + assert_equal content_type(:png), 'image/png' + assert_equal content_type(:baz), 'application/baz;charset=utf-8' ++ # Changed to "text/javascript" in Rack >3.0 ++ # https://github.com/sinatra/sinatra/pull/1857#issuecomment-1445062212 ++ assert_match %r{^application|text/javascript;charset=utf-8$}, content_type(:js) + tests_ran = true + "done" + end +-- +2.39.2 + + +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Patrik Ragnarsson +Date: Sat, 4 Mar 2023 23:17:55 +0100 +Subject: [PATCH 41/41] `Rack::Handler` -> `Rackup::Handler` + +--- + lib/sinatra/base.rb | 3 ++- + test/integration/rainbows.rb | 4 ++-- + test/server_test.rb | 2 +- + 3 files changed, 5 insertions(+), 4 deletions(-) + +diff --git a/lib/sinatra/base.rb b/lib/sinatra/base.rb +index cdeb655b..f207748d 100644 +--- a/lib/sinatra/base.rb ++++ b/lib/sinatra/base.rb +@@ -2,6 +2,7 @@ + + # external dependencies + require 'rack' ++require 'rackup' + require 'tilt' + require 'rack/protection' + require 'mustermann' +@@ -1530,7 +1531,7 @@ module Sinatra + return if running? + + set options +- handler = Rack::Handler.pick(server) ++ handler = Rackup::Handler.pick(server) + handler_name = handler.name.gsub(/.*::/, '') + server_settings = settings.respond_to?(:server_settings) ? settings.server_settings : {} + server_settings.merge!(Port: port, Host: bind) +diff --git a/test/integration/rainbows.rb b/test/integration/rainbows.rb +index 895e19a2..004e54a9 100644 +--- a/test/integration/rainbows.rb ++++ b/test/integration/rainbows.rb +@@ -1,6 +1,6 @@ + require 'rainbows' + +-module Rack ++module Rackup + module Handler + class Rainbows + def self.run(app, **options) +@@ -15,6 +15,6 @@ module Rack + end + end + +- register :rainbows, ::Rack::Handler::Rainbows ++ register :rainbows, ::Rackup::Handler::Rainbows + end + end +diff --git a/test/server_test.rb b/test/server_test.rb +index c85a92a3..be5e51df 100644 +--- a/test/server_test.rb ++++ b/test/server_test.rb +@@ -1,7 +1,7 @@ + require_relative 'test_helper' + require 'stringio' + +-module Rack::Handler ++module Rackup::Handler + class Mock + extend Minitest::Assertions + # Allow assertions in request context +-- +2.39.2 +