sinatra 3.2.0 → 4.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6eddf8c0c677b8b8b7f15bb03834147034ead41bc6310bf773ac84712bb43a14
4
- data.tar.gz: c35de3457f199d57a3c8a4419bd0ad113df38ba071cfffbcc79311db910bc1cb
3
+ metadata.gz: 273dc42d5989e8186e18f3c93f386454a4c63ffa1e8da672e9d5f91a4e1ea1e5
4
+ data.tar.gz: 66a2357e2aab7d0acd8a780f692ce66286eb806175199fc7ccd421267b29bf4c
5
5
  SHA512:
6
- metadata.gz: 33e0785425af5a32c7e09d95e2d11e02d83cdda1d907b34197c63642bfec81cd04a40ffc2e25486fe45af66d9a681f57c41d083d7bc56f24b69f1cf4b3a478f6
7
- data.tar.gz: 6aa65f3b65c819171bcadb61a3a43c3ff7af748d29a9177dbd02549229e3b9fa64b338d0404383b97615e74aa5381dd8a4c997edbf86ca0e8a2970e0213b2ece
6
+ metadata.gz: 4a30a3cb2dad00c6c54c9ecf57066f2810e9599a1c612ecea5da910913d1380ca0307e6ed4897eb3d2cedba7faff3d225bb52c6b4975d3f9d2a120fe8d08a304
7
+ data.tar.gz: 3c241fe8a0613f29af44ed93d416fdc647febf9e0b4c725c8bc15d8328f98701181f3a01d2c8dc4987aa08a44d7b7501722945c10041ea0f37b732c4d894f5ae
data/CHANGELOG.md CHANGED
@@ -1,6 +1,89 @@
1
1
  ## Unreleased
2
2
 
3
- * _Your new feature here._
3
+ ## 4.2.1 / 2025-10-10
4
+
5
+ * Fix: Revert "`PATH_INFO` can never be empty" ([#2124](https://github.com/sinatra/sinatra/pull/2124))
6
+ * addresses issues with routing and 404, [more in the original pull request](https://github.com/sinatra/sinatra/issues/2113#issuecomment-3388476329)
7
+
8
+ ## 4.2.0 / 2025-10-08
9
+
10
+ * New: Add `:static_headers` setting for custom headers in static file responses ([#2089](https://github.com/sinatra/sinatra/pull/2089))
11
+ * Fix: Fix regex in `etag_matches?` to prevent ReDoS ([#2121](https://github.com/sinatra/sinatra/pull/2121))
12
+ * Fix: `PATH_INFO` can never be empty ([#2114](https://github.com/sinatra/sinatra/pull/2114))
13
+ * Fix: Fix malformed Content-Type headers ([#2081](https://github.com/sinatra/sinatra/pull/2081))
14
+ * Fix: Avoid crash for integer values in `content_type` parameters ([#2078](https://github.com/sinatra/sinatra/pull/2078))
15
+
16
+ ## 4.1.1 / 2024-11-20
17
+
18
+ * Fix: Restore WEBrick support ([#2067](https://github.com/sinatra/sinatra/pull/2067))
19
+
20
+ ## 4.1.0 / 2024-11-18
21
+
22
+ * New: Add `host_authorization` setting ([#2053](https://github.com/sinatra/sinatra/pull/2053))
23
+ * Defaults to `.localhost`, `.test` and any IP address in development mode.
24
+ * Security: addresses [CVE-2024-21510](https://github.com/advisories/GHSA-hxx2-7vcw-mqr3).
25
+ * Fix: Return an instance of `Sinatra::IndifferentHash` when calling `#except` ([#2044](https://github.com/sinatra/sinatra/pull/2044))
26
+ * Fix: Address warning from `URI` for Ruby 3.4 ([#2060](https://github.com/sinatra/sinatra/pull/2060))
27
+ * Fix: `rackup` no longer depends on WEBrick, recommend Puma instead ([`4a558503`](https://github.com/sinatra/sinatra/commit/4a558503a0ee41f26d4ebc07b478340e8a8a5ed6))
28
+ * Fix: Zeitwerk 2.7.0+ compatibility ([#2050](https://github.com/sinatra/sinatra/pull/2050))
29
+ * Fix: Address warning about Hash construction for Ruby 3.4 ([#2028](https://github.com/sinatra/sinatra/pull/2028))
30
+ * Fix: Declare missing dependencies for Ruby 3.5 ([#2032](https://github.com/sinatra/sinatra/pull/2032))
31
+ * Fix: Compatibility with `--enable-frozen-string-literal` ([#2033](https://github.com/sinatra/sinatra/pull/2033))
32
+ * Fix: Rack 3.1 compatibility ([#2035](https://github.com/sinatra/sinatra/pull/2035))
33
+ * Don't depend on `Rack::Logger`
34
+ * Don't delete `content-length` header when `Rack::Files` is used
35
+
36
+ ## 4.0.1 / 2025-05-24
37
+
38
+ * Rack 3.1 compatibility ([#2035])
39
+
40
+ * Fix malformed Content-Type headers ([#2081])
41
+
42
+ * Avoid crash for integer values in `content_type` parameters ([#2078])
43
+
44
+ * Fix compatibility with --enable-frozen-string-literal ([#2033])
45
+
46
+ * Declare missing dependencies for Ruby 3.5 ([#2032])
47
+
48
+ * Fix warning about Hash construction. ([#2028])
49
+
50
+ * Support Zeitwerk 2.7.0+ ([#2050])
51
+
52
+ * Address URI depreciation ([#2060])
53
+
54
+ [#2035]: https://github.com/sinatra/sinatra/pull/2035
55
+ [#2081]: https://github.com/sinatra/sinatra/pull/2081
56
+ [#2078]: https://github.com/sinatra/sinatra/pull/2078
57
+ [#2033]: https://github.com/sinatra/sinatra/pull/2033
58
+ [#2032]: https://github.com/sinatra/sinatra/pull/2032
59
+ [#2028]: https://github.com/sinatra/sinatra/pull/2028
60
+ [#2050]: https://github.com/sinatra/sinatra/pull/2050
61
+ [#2060]: https://github.com/sinatra/sinatra/pull/2060
62
+
63
+ ## 4.0.0. / 2024-01-19
64
+
65
+ * New: Add support for Rack 3 ([#1857])
66
+ * Note: you may want to read the [Rack 3 Upgrade Guide]
67
+
68
+ * Require Ruby 2.7.8 as minimum Ruby version ([#1993])
69
+
70
+ * Breaking change: Drop support for Rack 2 ([#1857])
71
+ * Note: when using Sinatra to start the web server, you now need the `rackup` gem installed
72
+
73
+ * Breaking change: Remove the `IndifferentHash` initializer ([#1982])
74
+
75
+ * Breaking change: Disable `session_hijacking` protection by default ([#1984])
76
+
77
+ * Breaking change: Remove `Rack::Protection::EncryptedCookie` ([#1989])
78
+ * Note: cookies are still encrypted (by [`Rack::Session::Cookie`])
79
+
80
+ [#1857]: https://github.com/sinatra/sinatra/pull/1857
81
+ [#1993]: https://github.com/sinatra/sinatra/pull/1993
82
+ [#1982]: https://github.com/sinatra/sinatra/pull/1982
83
+ [#1984]: https://github.com/sinatra/sinatra/pull/1984
84
+ [#1989]: https://github.com/sinatra/sinatra/pull/1989
85
+ [`Rack::Session::Cookie`]: https://github.com/rack/rack-session
86
+ [Rack 3 Upgrade Guide]: https://github.com/rack/rack/blob/main/UPGRADE-GUIDE.md
4
87
 
5
88
  ## 3.2.0 / 2023-12-29
6
89
 
@@ -23,7 +106,7 @@
23
106
  [#1949]: https://github.com/sinatra/sinatra/pull/1949
24
107
  [#1952]: https://github.com/sinatra/sinatra/pull/1952
25
108
  [#1960]: https://github.com/sinatra/sinatra/pull/1960
26
- [#1975]: https://github.com/sinatra/sinatra/pull/1960
109
+ [#1975]: https://github.com/sinatra/sinatra/pull/1975
27
110
 
28
111
  ## 3.1.0 / 2023-08-07
29
112
 
@@ -222,7 +305,7 @@
222
305
 
223
306
  * Fix issue with passed routes and provides Fixes [#1095](https://github.com/sinatra/sinatra/pull/1095) [#1606](https://github.com/sinatra/sinatra/pull/1606) by Mike Pastore, Jordan Owens
224
307
 
225
- * Add QuietLogger that excludes pathes from Rack::CommonLogger [1250](https://github.com/sinatra/sinatra/pull/1250) by Christoph Wagner
308
+ * Add QuietLogger that excludes paths from Rack::CommonLogger [1250](https://github.com/sinatra/sinatra/pull/1250) by Christoph Wagner
226
309
 
227
310
  * Sinatra::Contrib dependency updates. Fixes [#1207](https://github.com/sinatra/sinatra/pull/1207) [#1411](https://github.com/sinatra/sinatra/pull/1411) by Mike Pastore
228
311
 
@@ -1588,7 +1671,7 @@ the 1.0 release:
1588
1671
  Hash structure. e.g., "post[title]=Hello&post[body]=World" yields
1589
1672
  params: {'post' => {'title' => 'Hello', 'body' => 'World'}}.
1590
1673
 
1591
- * Regular expressions may now be used in route pattens; captures are
1674
+ * Regular expressions may now be used in route patterns; captures are
1592
1675
  available at "params[:captures]".
1593
1676
 
1594
1677
  * New ":provides" route condition takes an array of mime types and
data/Gemfile CHANGED
@@ -1,13 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Why use bundler?
4
- # Well, not all development dependencies install on all rubies. Moreover, `gem
5
- # install sinatra --development` doesn't work, as it will also try to install
6
- # development dependencies of our dependencies, and those are not conflict free.
7
- # So, here we are, `bundle install`.
8
- #
9
- # If you have issues with a gem: `bundle install --without-coffee-script`.
10
-
11
3
  source 'https://rubygems.org'
12
4
  gemspec
13
5
 
@@ -18,11 +10,22 @@ rack_version = nil if rack_version.empty? || (rack_version == 'stable')
18
10
  rack_version = { github: 'rack/rack' } if rack_version == 'head'
19
11
  gem 'rack', rack_version
20
12
 
13
+ rack_session_version = ENV['rack_session'].to_s
14
+ rack_session_version = nil if rack_session_version.empty? || (rack_session_version == 'stable')
15
+ rack_session_version = { github: 'rack/rack-session' } if rack_session_version == 'head'
16
+ gem 'rack-session', rack_session_version
17
+
18
+ gem 'rackup'
19
+
21
20
  puma_version = ENV['puma'].to_s
22
21
  puma_version = nil if puma_version.empty? || (puma_version == 'stable')
23
22
  puma_version = { github: 'puma/puma' } if puma_version == 'head'
24
23
  gem 'puma', puma_version
25
24
 
25
+ zeitwerk_version = ENV['zeitwerk'].to_s
26
+ zeitwerk_version = nil if zeitwerk_version.empty? || (zeitwerk_version == 'stable')
27
+ gem 'zeitwerk', zeitwerk_version
28
+
26
29
  gem 'minitest', '~> 5.0'
27
30
  gem 'rack-test'
28
31
  gem 'rubocop', '~> 1.32.0', require: false
@@ -31,15 +34,9 @@ gem 'yard' # used by rake doc
31
34
  gem 'rack-protection', path: 'rack-protection'
32
35
  gem 'sinatra-contrib', path: 'sinatra-contrib'
33
36
 
34
- # traces 0.10.0 started to use Ruby 2.7 syntax without specifying required Ruby version
35
- # https://github.com/socketry/traces/pull/8#discussion_r1237988182
36
- # async-http 0.60.2 added traces 0.10.0 as dependency
37
- # https://github.com/socketry/async-http/pull/124/files#r1237988899
38
- gem 'traces', '< 0.10.0' if RUBY_VERSION >= '2.6.0' && RUBY_VERSION < '2.7.0'
39
-
40
37
  gem 'asciidoctor'
41
38
  gem 'builder'
42
- gem 'childprocess'
39
+ gem 'childprocess', '>= 5'
43
40
  gem 'commonmarker', '~> 0.23.4', platforms: [:ruby]
44
41
  gem 'erubi'
45
42
  gem 'eventmachine'
@@ -47,25 +44,23 @@ gem 'falcon', '~> 0.40', platforms: [:ruby]
47
44
  gem 'haml', '~> 6'
48
45
  gem 'kramdown'
49
46
  gem 'liquid'
50
- # markaby 0.9.1 introduced Ruby 2.7 syntax in https://github.com/markaby/markaby/pull/44
51
- # and does not specify required_ruby_version
52
- if RUBY_VERSION >= '2.6.0' && RUBY_VERSION < '2.7.0'
53
- gem 'markaby', '< 0.9.1'
54
- else
55
- gem 'markaby'
56
- end
47
+ gem 'markaby'
57
48
  gem 'nokogiri', '> 1.5.0'
49
+ gem 'ostruct'
58
50
  gem 'pandoc-ruby', '~> 2.0.2'
59
51
  gem 'rabl'
60
- if RUBY_ENGINE == 'truffleruby'
61
- gem 'rdiscount', '< 2.2.7.2' # https://github.com/oracle/truffleruby/issues/3362
62
- else
63
- gem 'rdiscount', platforms: [:ruby]
64
- end
52
+ gem 'rdiscount', platforms: [:ruby]
65
53
  gem 'rdoc'
66
54
  gem 'redcarpet', platforms: [:ruby]
67
- gem 'sass-embedded', '~> 1.54'
68
55
  gem 'simplecov', require: false
69
- gem 'slim', '~> 4'
56
+ gem 'slim', '~> 5'
70
57
  gem 'yajl-ruby', platforms: [:ruby]
71
- gem 'zeitwerk'
58
+ gem 'webrick'
59
+
60
+ # sass-embedded depends on google-protobuf
61
+ # which fails to be installed on JRuby and TruffleRuby under aarch64
62
+ # https://github.com/jruby/jruby/issues/8062
63
+ # https://github.com/protocolbuffers/protobuf/issues/11935
64
+ java = %w(jruby truffleruby).include?(RUBY_ENGINE)
65
+ aarch64 = RbConfig::CONFIG["target_cpu"] == 'aarch64'
66
+ gem 'sass-embedded', '~> 1.54' unless java && aarch64
data/README.md CHANGED
@@ -15,11 +15,10 @@ get '/' do
15
15
  end
16
16
  ```
17
17
 
18
- Install the gem:
18
+ Install the gems needed:
19
19
 
20
20
  ```shell
21
- gem install sinatra
22
- gem install puma # or any other server
21
+ gem install sinatra rackup puma
23
22
  ```
24
23
 
25
24
  And run with:
@@ -35,9 +34,6 @@ Please restart the server every time you change or use a code reloader
35
34
  like [rerun](https://github.com/alexch/rerun) or
36
35
  [rack-unreloader](https://github.com/jeremyevans/rack-unreloader).
37
36
 
38
- It is recommended to also run `gem install puma`, which Sinatra will
39
- pick up if available.
40
-
41
37
  ## Table of Contents
42
38
 
43
39
  - [Sinatra](#sinatra)
@@ -424,6 +420,15 @@ Note that the public directory name is not included in the URL. A file
424
420
  Use the `:static_cache_control` setting (see [below](#cache-control)) to add
425
421
  `Cache-Control` header info.
426
422
 
423
+ By default, Sinatra serves static files from the `public/` folder without running middleware or filters. To add custom headers (e.g, for CORS or caching), use the `:static_headers` setting:
424
+
425
+ ```ruby
426
+ set :static_headers, {
427
+ 'access-control-allow-origin' => '*',
428
+ 'x-static-asset' => 'served-by-sinatra'
429
+ }
430
+ ```
431
+
427
432
  ## Views / Templates
428
433
 
429
434
  Each template language is exposed via its own rendering method. These
@@ -1922,7 +1927,7 @@ set :protection, :except => :path_traversal
1922
1927
  You can also hand in an array in order to disable a list of protections:
1923
1928
 
1924
1929
  ```ruby
1925
- set :protection, :except => [:path_traversal, :session_hijacking]
1930
+ set :protection, :except => [:path_traversal, :remote_token]
1926
1931
  ```
1927
1932
 
1928
1933
  By default, Sinatra will only set up session based protection if `:sessions`
@@ -1993,6 +1998,33 @@ set :protection, :session => true
1993
1998
  <tt>"development"</tt> if not available.
1994
1999
  </dd>
1995
2000
 
2001
+ <dt>host_authorization</dt>
2002
+ <dd>
2003
+ <p>
2004
+ You can pass a hash of options to <tt>host_authorization</tt>,
2005
+ to be used by the <tt>Rack::Protection::HostAuthorization</tt> middleware.
2006
+ </p>
2007
+ <p>
2008
+ The middleware can block requests with unrecognized hostnames, to prevent DNS rebinding
2009
+ and other host header attacks. It checks the <tt>Host</tt>, <tt>X-Forwarded-Host</tt>
2010
+ and <tt>Forwarded</tt> headers.
2011
+ </p>
2012
+ <p>
2013
+ Useful options are:
2014
+ <ul>
2015
+ <li><tt>permitted_hosts</tt> – an array of hostnames (and <tt>IPAddr</tt> objects) your app recognizes
2016
+ <ul>
2017
+ <li>in the <tt>development</tt> environment, it is set to <tt>.localhost</tt>, <tt>.test</tt> and any IPv4/IPv6 address</li>
2018
+ <li>if empty, any hostname is permitted (the default for any other environment)</li>
2019
+ </ul>
2020
+ </li>
2021
+ <li><tt>status</tt> – the HTTP status code used in the response when a request is blocked (defaults to <tt>403</tt>)</li>
2022
+ <li><tt>message</tt> – the body used in the response when a request is blocked (defaults to <tt>Host not permitted</tt>)</li>
2023
+ <li><tt>allow_if</tt> – supply a <tt>Proc</tt> to use custom allow/deny logic, the proc is passed the request environment</li>
2024
+ </ul>
2025
+ </p>
2026
+ </dd>
2027
+
1996
2028
  <dt>logging</dt>
1997
2029
  <dd>Use the logger.</dd>
1998
2030
 
@@ -2086,12 +2118,8 @@ set :protection, :session => true
2086
2118
 
2087
2119
  <dt>server_settings</dt>
2088
2120
  <dd>
2089
- If you are using a WEBrick web server, presumably for your development
2090
- environment, you can pass a hash of options to <tt>server_settings</tt>,
2091
- such as <tt>SSLEnable</tt> or <tt>SSLVerifyClient</tt>. However, web
2092
- servers such as Puma do not support this, so you can set
2093
- <tt>server_settings</tt> by defining it as a method when you call
2094
- <tt>configure</tt>.
2121
+ You can pass a hash of options to <tt>server_settings</tt>,
2122
+ such as <tt>Host</tt> or <tt>Port</tt>.
2095
2123
  </dd>
2096
2124
 
2097
2125
  <dt>sessions</dt>
@@ -2138,6 +2166,16 @@ set :protection, :session => true
2138
2166
  <tt>set :static_cache_control, [:public, :max_age => 300]</tt>
2139
2167
  </dd>
2140
2168
 
2169
+ <dt>static_headers</dt>
2170
+ <dd>
2171
+ Allows you to define custom header settings for static file responses.
2172
+ </dd>
2173
+ <dd>
2174
+ For example: <br>
2175
+ <tt>set :static_headers, {'access-control-allow-origin' => '*', 'x-static-asset' => 'served-by-sinatra'}</tt>
2176
+ </dd>
2177
+
2178
+
2141
2179
  <dt>threaded</dt>
2142
2180
  <dd>
2143
2181
  If set to <tt>true</tt>, will tell server to use
@@ -2812,7 +2850,7 @@ _Paraphrasing from
2812
2850
  by Konstantin_
2813
2851
 
2814
2852
  Sinatra doesn't impose any concurrency model but leaves that to the
2815
- underlying Rack handler (server) like Puma or WEBrick. Sinatra
2853
+ underlying Rack handler (server) like Puma or Falcon. Sinatra
2816
2854
  itself is thread-safe, so there won't be any problem if the Rack handler
2817
2855
  uses a threaded model of concurrency.
2818
2856
 
@@ -2820,30 +2858,24 @@ uses a threaded model of concurrency.
2820
2858
 
2821
2859
  The following Ruby versions are officially supported:
2822
2860
  <dl>
2823
- <dt>Ruby 2.6</dt>
2861
+ <dt>Ruby</dt>
2824
2862
  <dd>
2825
- 2.6 is fully supported and recommended. There are currently no plans to
2826
- drop official support for it.
2863
+ <a href="https://www.ruby-lang.org/en/downloads/">The stable releases</a> are fully supported and recommended.
2827
2864
  </dd>
2828
2865
 
2829
- <dt>Rubinius</dt>
2866
+ <dt>TruffleRuby</dt>
2830
2867
  <dd>
2831
- Rubinius is officially supported (Rubinius >= 2.x). It is recommended to
2832
- <tt>gem install puma</tt>.
2868
+ The latest stable release of TruffleRuby is supported.
2833
2869
  </dd>
2834
2870
 
2835
2871
  <dt>JRuby</dt>
2836
2872
  <dd>
2837
- The latest stable release of JRuby is officially supported. It is not
2838
- recommended to use C extensions with JRuby. It is recommended to
2839
- <tt>gem install trinidad</tt>.
2873
+ The latest stable release of JRuby is supported. It is not
2874
+ recommended to use C extensions with JRuby.
2840
2875
  </dd>
2841
2876
  </dl>
2842
2877
 
2843
- Versions of Ruby before 2.6 are no longer supported as of Sinatra 3.0.0.
2844
-
2845
- We also keep an eye on upcoming Ruby versions. Expect upcoming
2846
- 3.x releases to be fully supported.
2878
+ Versions of Ruby before 2.7.8 are no longer supported as of Sinatra 4.0.0.
2847
2879
 
2848
2880
  Sinatra should work on any operating system supported by the chosen Ruby
2849
2881
  implementation.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 3.2.0
1
+ 4.2.1
data/lib/sinatra/base.rb CHANGED
@@ -2,13 +2,19 @@
2
2
 
3
3
  # external dependencies
4
4
  require 'rack'
5
+ begin
6
+ require 'rackup'
7
+ rescue LoadError
8
+ end
5
9
  require 'tilt'
6
10
  require 'rack/protection'
11
+ require 'rack/session'
7
12
  require 'mustermann'
8
13
  require 'mustermann/sinatra'
9
14
  require 'mustermann/regular'
10
15
 
11
16
  # stdlib dependencies
17
+ require 'ipaddr'
12
18
  require 'time'
13
19
  require 'uri'
14
20
 
@@ -17,6 +23,8 @@ require 'sinatra/indifferent_hash'
17
23
  require 'sinatra/show_exceptions'
18
24
  require 'sinatra/version'
19
25
 
26
+ require_relative 'middleware/logger'
27
+
20
28
  module Sinatra
21
29
  # The request object. See Rack::Request for more info:
22
30
  # https://rubydoc.info/github/rack/rack/main/Rack/Request
@@ -56,7 +64,7 @@ module Sinatra
56
64
  alias secure? ssl?
57
65
 
58
66
  def forwarded?
59
- @env.include? 'HTTP_X_FORWARDED_HOST'
67
+ !forwarded_authority.nil?
60
68
  end
61
69
 
62
70
  def safe?
@@ -176,8 +184,8 @@ module Sinatra
176
184
  result = body
177
185
 
178
186
  if drop_content_info?
179
- headers.delete 'Content-Length'
180
- headers.delete 'Content-Type'
187
+ headers.delete 'content-length'
188
+ headers.delete 'content-type'
181
189
  end
182
190
 
183
191
  if drop_body?
@@ -186,9 +194,9 @@ module Sinatra
186
194
  end
187
195
 
188
196
  if calculate_content_length?
189
- # if some other code has already set Content-Length, don't muck with it
197
+ # if some other code has already set content-length, don't muck with it
190
198
  # currently, this would be the static file-handler
191
- headers['Content-Length'] = body.map(&:bytesize).reduce(0, :+).to_s
199
+ headers['content-length'] = body.map(&:bytesize).reduce(0, :+).to_s
192
200
  end
193
201
 
194
202
  [status, headers, result]
@@ -197,7 +205,7 @@ module Sinatra
197
205
  private
198
206
 
199
207
  def calculate_content_length?
200
- headers['Content-Type'] && !headers['Content-Length'] && (Array === body)
208
+ headers['content-type'] && !headers['content-length'] && (Array === body)
201
209
  end
202
210
 
203
211
  def drop_content_info?
@@ -289,10 +297,8 @@ module Sinatra
289
297
  def block.each; yield(call) end
290
298
  response.body = block
291
299
  elsif value
292
- # Rack 2.0 returns a Rack::File::Iterator here instead of
293
- # Rack::File as it was in the previous API.
294
- unless request.head? || value.is_a?(Rack::Files::Iterator) || value.is_a?(Stream)
295
- headers.delete 'Content-Length'
300
+ unless request.head? || value.is_a?(Rack::Files::BaseIterator) || value.is_a?(Stream)
301
+ headers.delete 'content-length'
296
302
  end
297
303
  response.body = value
298
304
  else
@@ -302,7 +308,10 @@ module Sinatra
302
308
 
303
309
  # Halt processing and redirect to the URI provided.
304
310
  def redirect(uri, *args)
305
- if (env['HTTP_VERSION'] == 'HTTP/1.1') && (env['REQUEST_METHOD'] != 'GET')
311
+ # SERVER_PROTOCOL is required in Rack 3, fall back to HTTP_VERSION
312
+ # for servers not updated for Rack 3 (like Puma 5)
313
+ http_version = env['SERVER_PROTOCOL'] || env['HTTP_VERSION']
314
+ if (http_version == 'HTTP/1.1') && (env['REQUEST_METHOD'] != 'GET')
306
315
  status 303
307
316
  else
308
317
  status 302
@@ -372,10 +381,10 @@ module Sinatra
372
381
  Base.mime_type(type)
373
382
  end
374
383
 
375
- # Set the Content-Type of the response body given a media type or file
384
+ # Set the content-type of the response body given a media type or file
376
385
  # extension.
377
386
  def content_type(type = nil, params = {})
378
- return response['Content-Type'] unless type
387
+ return response['content-type'] unless type
379
388
 
380
389
  default = params.delete :default
381
390
  mime_type = mime_type(type) || default
@@ -387,13 +396,13 @@ module Sinatra
387
396
  end
388
397
  params.delete :charset if mime_type.include? 'charset'
389
398
  unless params.empty?
390
- mime_type << (mime_type.include?(';') ? ', ' : ';')
399
+ mime_type << ';'
391
400
  mime_type << params.map do |key, val|
392
- val = val.inspect if val =~ /[";,]/
401
+ val = val.inspect if val.to_s =~ /[";,]/
393
402
  "#{key}=#{val}"
394
- end.join(', ')
403
+ end.join(';')
395
404
  end
396
- response['Content-Type'] = mime_type
405
+ response['content-type'] = mime_type
397
406
  end
398
407
 
399
408
  # https://html.spec.whatwg.org/#multipart-form-data
@@ -412,12 +421,12 @@ module Sinatra
412
421
  params = format('; filename="%s"', File.basename(filename).gsub(/["\r\n]/, MULTIPART_FORM_DATA_REPLACEMENT_TABLE))
413
422
  response['Content-Disposition'] << params
414
423
  ext = File.extname(filename)
415
- content_type(ext) unless response['Content-Type'] || ext.empty?
424
+ content_type(ext) unless response['content-type'] || ext.empty?
416
425
  end
417
426
 
418
427
  # Use the contents of the file at +path+ as the response body.
419
428
  def send_file(path, opts = {})
420
- if opts[:type] || !response['Content-Type']
429
+ if opts[:type] || !response['content-type']
421
430
  content_type opts[:type] || File.extname(path), default: 'application/octet-stream'
422
431
  end
423
432
 
@@ -433,7 +442,7 @@ module Sinatra
433
442
  result = file.serving(request, path)
434
443
 
435
444
  result[1].each { |k, v| headers[k] ||= v }
436
- headers['Content-Length'] = result[1]['Content-Length']
445
+ headers['content-length'] = result[1]['content-length']
437
446
  opts[:status] &&= Integer(opts[:status])
438
447
  halt (opts[:status] || result[0]), result[2]
439
448
  rescue Errno::ENOENT
@@ -702,7 +711,7 @@ module Sinatra
702
711
  def etag_matches?(list, new_resource = request.post?)
703
712
  return !new_resource if list == '*'
704
713
 
705
- list.to_s.split(/\s*,\s*/).include? response['ETag']
714
+ list.to_s.split(',').map(&:strip).include?(response['ETag'])
706
715
  end
707
716
 
708
717
  def with_params(temp_params)
@@ -966,7 +975,7 @@ module Sinatra
966
975
  include Helpers
967
976
  include Templates
968
977
 
969
- URI_INSTANCE = URI::Parser.new
978
+ URI_INSTANCE = defined?(URI::RFC2396_PARSER) ? URI::RFC2396_PARSER : URI::RFC2396_Parser.new
970
979
 
971
980
  attr_accessor :app, :env, :request, :response, :params
972
981
  attr_reader :template_cache
@@ -995,7 +1004,7 @@ module Sinatra
995
1004
  invoke { dispatch! }
996
1005
  invoke { error_block!(response.status) } unless @env['sinatra.error']
997
1006
 
998
- unless @response['Content-Type']
1007
+ unless @response['content-type']
999
1008
  if Array === body && body[0].respond_to?(:content_type)
1000
1009
  content_type body[0].content_type
1001
1010
  elsif (default = settings.default_content_type)
@@ -1058,7 +1067,7 @@ module Sinatra
1058
1067
  routes = base.routes[@request.request_method]
1059
1068
 
1060
1069
  routes&.each do |pattern, conditions, block|
1061
- response.delete_header('Content-Type') unless @pinned_response
1070
+ response.delete_header('content-type') unless @pinned_response
1062
1071
 
1063
1072
  returned_pass_block = process_route(pattern, conditions) do |*args|
1064
1073
  env['sinatra.route'] = "#{@request.request_method} #{pattern}"
@@ -1134,6 +1143,7 @@ module Sinatra
1134
1143
 
1135
1144
  # Attempt to serve static files from public directory. Throws :halt when
1136
1145
  # a matching file is found, returns nil otherwise.
1146
+ # If custom static headers are defined, use them.
1137
1147
  def static!(options = {})
1138
1148
  return if (public_dir = settings.public_folder).nil?
1139
1149
 
@@ -1147,6 +1157,9 @@ module Sinatra
1147
1157
 
1148
1158
  env['sinatra.static_file'] = path
1149
1159
  cache_control(*settings.static_cache_control) if settings.static_cache_control?
1160
+
1161
+ headers(settings.static_headers) if settings.static_headers?
1162
+
1150
1163
  send_file path, options.merge(disposition: nil)
1151
1164
  end
1152
1165
 
@@ -1179,7 +1192,7 @@ module Sinatra
1179
1192
  invoke do
1180
1193
  static! if settings.static? && (request.get? || request.head?)
1181
1194
  filter! :before do
1182
- @pinned_response = !response['Content-Type'].nil?
1195
+ @pinned_response = !response['content-type'].nil?
1183
1196
  end
1184
1197
  route!
1185
1198
  end
@@ -1286,7 +1299,7 @@ module Sinatra
1286
1299
  /active_support/, # active_support require hacks
1287
1300
  %r{bundler(/(?:runtime|inline))?\.rb}, # bundler require hacks
1288
1301
  /<internal:/, # internal in ruby >= 1.9.2
1289
- %r{zeitwerk/kernel\.rb} # Zeitwerk kernel#require decorator
1302
+ %r{zeitwerk/(core_ext/)?kernel\.rb} # Zeitwerk kernel#require decorator
1290
1303
  ].freeze
1291
1304
 
1292
1305
  attr_reader :routes, :filters, :templates, :errors, :on_start_callback, :on_stop_callback
@@ -1460,7 +1473,13 @@ module Sinatra
1460
1473
  # mime_types :js # => ['application/javascript', 'text/javascript']
1461
1474
  def mime_types(type)
1462
1475
  type = mime_type type
1463
- type =~ %r{^application/(xml|javascript)$} ? [type, "text/#{$1}"] : [type]
1476
+ if type =~ %r{^application/(xml|javascript)$}
1477
+ [type, "text/#{$1}"]
1478
+ elsif type =~ %r{^text/(xml|javascript)$}
1479
+ [type, "application/#{$1}"]
1480
+ else
1481
+ [type]
1482
+ end
1464
1483
  end
1465
1484
 
1466
1485
  # Define a before filter; runs before all requests within the same
@@ -1586,13 +1605,30 @@ module Sinatra
1586
1605
  alias stop! quit!
1587
1606
 
1588
1607
  # Run the Sinatra app as a self-hosted server using
1589
- # Puma, Falcon, or WEBrick (in that order). If given a block, will call
1608
+ # Puma, Falcon (in that order). If given a block, will call
1590
1609
  # with the constructed handler once we have taken the stage.
1591
1610
  def run!(options = {}, &block)
1611
+ unless defined?(Rackup::Handler)
1612
+ rackup_warning = <<~MISSING_RACKUP
1613
+ Sinatra could not start, the required gems weren't found!
1614
+
1615
+ Add them to your bundle with:
1616
+
1617
+ bundle add rackup puma
1618
+
1619
+ or install them with:
1620
+
1621
+ gem install rackup puma
1622
+
1623
+ MISSING_RACKUP
1624
+ warn rackup_warning
1625
+ exit 1
1626
+ end
1627
+
1592
1628
  return if running?
1593
1629
 
1594
1630
  set options
1595
- handler = Rack::Handler.pick(server)
1631
+ handler = Rackup::Handler.pick(server)
1596
1632
  handler_name = handler.name.gsub(/.*::/, '')
1597
1633
  server_settings = settings.respond_to?(:server_settings) ? settings.server_settings : {}
1598
1634
  server_settings.merge!(Port: port, Host: bind)
@@ -1724,7 +1760,7 @@ module Sinatra
1724
1760
  types.map! { |t| mime_types(t) }
1725
1761
  types.flatten!
1726
1762
  condition do
1727
- response_content_type = response['Content-Type']
1763
+ response_content_type = response['content-type']
1728
1764
  preferred_type = request.preferred_type(types)
1729
1765
 
1730
1766
  if response_content_type
@@ -1790,6 +1826,7 @@ module Sinatra
1790
1826
  setup_logging builder
1791
1827
  setup_sessions builder
1792
1828
  setup_protection builder
1829
+ setup_host_authorization builder
1793
1830
  end
1794
1831
 
1795
1832
  def setup_middleware(builder)
@@ -1806,7 +1843,7 @@ module Sinatra
1806
1843
  end
1807
1844
 
1808
1845
  def setup_null_logger(builder)
1809
- builder.use Rack::NullLogger
1846
+ builder.use Sinatra::Middleware::Logger, ::Logger::FATAL
1810
1847
  end
1811
1848
 
1812
1849
  def setup_common_logger(builder)
@@ -1815,9 +1852,9 @@ module Sinatra
1815
1852
 
1816
1853
  def setup_custom_logger(builder)
1817
1854
  if logging.respond_to? :to_int
1818
- builder.use Rack::Logger, logging
1855
+ builder.use Sinatra::Middleware::Logger, logging
1819
1856
  else
1820
- builder.use Rack::Logger
1857
+ builder.use Sinatra::Middleware::Logger
1821
1858
  end
1822
1859
  end
1823
1860
 
@@ -1838,6 +1875,10 @@ module Sinatra
1838
1875
  builder.use Rack::Protection, options
1839
1876
  end
1840
1877
 
1878
+ def setup_host_authorization(builder)
1879
+ builder.use Rack::Protection::HostAuthorization, host_authorization
1880
+ end
1881
+
1841
1882
  def setup_sessions(builder)
1842
1883
  return unless sessions?
1843
1884
 
@@ -1901,7 +1942,7 @@ module Sinatra
1901
1942
  set :dump_errors, proc { !test? }
1902
1943
  set :show_exceptions, proc { development? }
1903
1944
  set :sessions, false
1904
- set :session_store, Rack::Protection::EncryptedCookie
1945
+ set :session_store, Rack::Session::Cookie
1905
1946
  set :logging, false
1906
1947
  set :protection, true
1907
1948
  set :method_override, false
@@ -1932,10 +1973,25 @@ module Sinatra
1932
1973
  set :running_server, nil
1933
1974
  set :handler_name, nil
1934
1975
  set :traps, true
1935
- set :server, %w[HTTP webrick]
1976
+ set :server, %w[webrick]
1936
1977
  set :bind, proc { development? ? 'localhost' : '0.0.0.0' }
1937
1978
  set :port, Integer(ENV['PORT'] && !ENV['PORT'].empty? ? ENV['PORT'] : 4567)
1938
1979
  set :quiet, false
1980
+ set :host_authorization, ->() do
1981
+ if development?
1982
+ {
1983
+ permitted_hosts: [
1984
+ "localhost",
1985
+ ".localhost",
1986
+ ".test",
1987
+ IPAddr.new("0.0.0.0/0"),
1988
+ IPAddr.new("::/0"),
1989
+ ]
1990
+ }
1991
+ else
1992
+ {}
1993
+ end
1994
+ end
1939
1995
 
1940
1996
  ruby_engine = defined?(RUBY_ENGINE) && RUBY_ENGINE
1941
1997
 
@@ -1959,6 +2015,8 @@ module Sinatra
1959
2015
  set :public_folder, proc { root && File.join(root, 'public') }
1960
2016
  set :static, proc { public_folder && File.exist?(public_folder) }
1961
2017
  set :static_cache_control, false
2018
+
2019
+ set :static_headers, {}
1962
2020
 
1963
2021
  error ::Exception do
1964
2022
  response.status = 500
@@ -43,12 +43,6 @@ module Sinatra
43
43
  new.merge!(Hash[*args])
44
44
  end
45
45
 
46
- def initialize(*args)
47
- args.map!(&method(:convert_value))
48
-
49
- super(*args)
50
- end
51
-
52
46
  def default(*args)
53
47
  args.map!(&method(:convert_key))
54
48
 
@@ -191,7 +185,7 @@ module Sinatra
191
185
  def except(*keys)
192
186
  keys.map!(&method(:convert_key))
193
187
 
194
- super(*keys)
188
+ self.class[super(*keys)]
195
189
  end if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("3.0")
196
190
 
197
191
  private
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'logger'
4
+
5
+ module Sinatra
6
+ module Middleware
7
+ class Logger
8
+ def initialize(app, level = ::Logger::INFO)
9
+ @app, @level = app, level
10
+ end
11
+
12
+ def call(env)
13
+ logger = ::Logger.new(env[Rack::RACK_ERRORS])
14
+ logger.level = @level
15
+
16
+ env[Rack::RACK_LOGGER] = logger
17
+ @app.call(env)
18
+ end
19
+ end
20
+ end
21
+ end
@@ -38,8 +38,8 @@ module Sinatra
38
38
  [
39
39
  500,
40
40
  {
41
- 'Content-Type' => content_type,
42
- 'Content-Length' => body.bytesize.to_s
41
+ 'content-type' => content_type,
42
+ 'content-length' => body.bytesize.to_s
43
43
  },
44
44
  [body]
45
45
  ]
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Sinatra
4
- VERSION = '3.2.0'
4
+ VERSION = '4.2.1'
5
5
  end
data/sinatra.gemspec CHANGED
@@ -40,13 +40,16 @@ RubyGems 2.0 or newer is required to protect against public gem pushes. You can
40
40
  'homepage_uri' => 'http://sinatrarb.com/',
41
41
  'bug_tracker_uri' => 'https://github.com/sinatra/sinatra/issues',
42
42
  'mailing_list_uri' => 'http://groups.google.com/group/sinatrarb',
43
- 'documentation_uri' => 'https://www.rubydoc.info/gems/sinatra'
43
+ 'documentation_uri' => 'https://www.rubydoc.info/gems/sinatra',
44
+ 'rubygems_mfa_required' => 'true',
44
45
  }
45
46
 
46
- s.required_ruby_version = '>= 2.6.0'
47
+ s.required_ruby_version = '>= 2.7.8'
47
48
 
49
+ s.add_dependency 'logger', '>= 1.6.0'
48
50
  s.add_dependency 'mustermann', '~> 3.0'
49
- s.add_dependency 'rack', '~> 2.2', '>= 2.2.4'
51
+ s.add_dependency 'rack', '>= 3.0.0', '< 4'
50
52
  s.add_dependency 'rack-protection', version
53
+ s.add_dependency 'rack-session', '>= 2.0.0', '< 3'
51
54
  s.add_dependency 'tilt', '~> 2.0'
52
55
  end
metadata CHANGED
@@ -1,18 +1,31 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sinatra
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.2.0
4
+ version: 4.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Blake Mizerany
8
8
  - Ryan Tomayko
9
9
  - Simon Rozet
10
10
  - Konstantin Haase
11
- autorequire:
12
11
  bindir: bin
13
12
  cert_chain: []
14
- date: 2023-12-29 00:00:00.000000000 Z
13
+ date: 1980-01-02 00:00:00.000000000 Z
15
14
  dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: logger
17
+ requirement: !ruby/object:Gem::Requirement
18
+ requirements:
19
+ - - ">="
20
+ - !ruby/object:Gem::Version
21
+ version: 1.6.0
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ version: 1.6.0
16
29
  - !ruby/object:Gem::Dependency
17
30
  name: mustermann
18
31
  requirement: !ruby/object:Gem::Requirement
@@ -31,36 +44,56 @@ dependencies:
31
44
  name: rack
32
45
  requirement: !ruby/object:Gem::Requirement
33
46
  requirements:
34
- - - "~>"
35
- - !ruby/object:Gem::Version
36
- version: '2.2'
37
47
  - - ">="
38
48
  - !ruby/object:Gem::Version
39
- version: 2.2.4
49
+ version: 3.0.0
50
+ - - "<"
51
+ - !ruby/object:Gem::Version
52
+ version: '4'
40
53
  type: :runtime
41
54
  prerelease: false
42
55
  version_requirements: !ruby/object:Gem::Requirement
43
56
  requirements:
44
- - - "~>"
45
- - !ruby/object:Gem::Version
46
- version: '2.2'
47
57
  - - ">="
48
58
  - !ruby/object:Gem::Version
49
- version: 2.2.4
59
+ version: 3.0.0
60
+ - - "<"
61
+ - !ruby/object:Gem::Version
62
+ version: '4'
50
63
  - !ruby/object:Gem::Dependency
51
64
  name: rack-protection
52
65
  requirement: !ruby/object:Gem::Requirement
53
66
  requirements:
54
67
  - - '='
55
68
  - !ruby/object:Gem::Version
56
- version: 3.2.0
69
+ version: 4.2.1
57
70
  type: :runtime
58
71
  prerelease: false
59
72
  version_requirements: !ruby/object:Gem::Requirement
60
73
  requirements:
61
74
  - - '='
62
75
  - !ruby/object:Gem::Version
63
- version: 3.2.0
76
+ version: 4.2.1
77
+ - !ruby/object:Gem::Dependency
78
+ name: rack-session
79
+ requirement: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - ">="
82
+ - !ruby/object:Gem::Version
83
+ version: 2.0.0
84
+ - - "<"
85
+ - !ruby/object:Gem::Version
86
+ version: '3'
87
+ type: :runtime
88
+ prerelease: false
89
+ version_requirements: !ruby/object:Gem::Requirement
90
+ requirements:
91
+ - - ">="
92
+ - !ruby/object:Gem::Version
93
+ version: 2.0.0
94
+ - - "<"
95
+ - !ruby/object:Gem::Version
96
+ version: '3'
64
97
  - !ruby/object:Gem::Dependency
65
98
  name: tilt
66
99
  requirement: !ruby/object:Gem::Requirement
@@ -81,8 +114,8 @@ email: sinatrarb@googlegroups.com
81
114
  executables: []
82
115
  extensions: []
83
116
  extra_rdoc_files:
84
- - README.md
85
117
  - LICENSE
118
+ - README.md
86
119
  files:
87
120
  - ".yardopts"
88
121
  - AUTHORS.md
@@ -105,6 +138,7 @@ files:
105
138
  - lib/sinatra/images/500.png
106
139
  - lib/sinatra/indifferent_hash.rb
107
140
  - lib/sinatra/main.rb
141
+ - lib/sinatra/middleware/logger.rb
108
142
  - lib/sinatra/show_exceptions.rb
109
143
  - lib/sinatra/version.rb
110
144
  - sinatra.gemspec
@@ -118,7 +152,7 @@ metadata:
118
152
  bug_tracker_uri: https://github.com/sinatra/sinatra/issues
119
153
  mailing_list_uri: http://groups.google.com/group/sinatrarb
120
154
  documentation_uri: https://www.rubydoc.info/gems/sinatra
121
- post_install_message:
155
+ rubygems_mfa_required: 'true'
122
156
  rdoc_options:
123
157
  - "--line-numbers"
124
158
  - "--title"
@@ -132,15 +166,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
132
166
  requirements:
133
167
  - - ">="
134
168
  - !ruby/object:Gem::Version
135
- version: 2.6.0
169
+ version: 2.7.8
136
170
  required_rubygems_version: !ruby/object:Gem::Requirement
137
171
  requirements:
138
172
  - - ">="
139
173
  - !ruby/object:Gem::Version
140
174
  version: '0'
141
175
  requirements: []
142
- rubygems_version: 3.5.3
143
- signing_key:
176
+ rubygems_version: 3.6.9
144
177
  specification_version: 4
145
178
  summary: Classy web-development dressed in a DSL
146
179
  test_files: []