roda 3.72.0 → 3.74.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d5c28e38d35f59176f159798f68d82bbd30df62a5907ace3a3750b1daf7b4fe6
4
- data.tar.gz: cb5fd8d5a9d665bc820c55ccbf1fd6a068873ae3e265a1f877bbdf82b51c4d6e
3
+ metadata.gz: ae3f95331c6c3d69453feb96a7dbd3629a6d5f52bd9d1862edecab893d95a36f
4
+ data.tar.gz: c5457f54b2ca5a9971f93a23db10abe3b6fd16a743e2651749ec14c480c248a3
5
5
  SHA512:
6
- metadata.gz: d8710537511b8f332c443fd41bd279c99de53c0bc9618cd92f763ab7d354f70c4496ba9eafa076438087b5ab52f36e69f649aa00d2fb6f07d5315b7d61aef4a1
7
- data.tar.gz: e0af216b465facc81e81ab8a495d472685c26aa6b2c1d56c0e48631586555f78f4d5922ac5ee92c6118a08b8162ee35866dc5708155bdbed70d5851b2ff8339b
6
+ metadata.gz: f314713b33ec4400e5ef8849c6a3e30f1b5956b428dea55a5bf45855fef4a965f9b417063182cc4412effa592a7d311715cf4541826781675ca26bd20f202ca3
7
+ data.tar.gz: 735cb4cf3e5480be4462d88ccb0090001e7eca27bfe7338a923f5f190730095037ff0c77e1cbe3c875b137175bc7022b8d0611ccbf8b82426cbbaec5b7802149
data/CHANGELOG CHANGED
@@ -1,3 +1,13 @@
1
+ = 3.74.0 (2023-11-13)
2
+
3
+ * Add redirect_http_to_https plugin, helping to ensure future requests from the browser are submitted via HTTPS (jeremyevans)
4
+
5
+ = 3.73.0 (2023-10-13)
6
+
7
+ * Support :next_if_not_found option for middleware plugin (jeremyevans) (#334)
8
+
9
+ * Remove dependency on base64 library from sessions and route_csrf plugin, as it will not be part of the standard library in Ruby 3.4+ (jeremyevans)
10
+
1
11
  = 3.72.0 (2023-09-12)
2
12
 
3
13
  * Add invalid_request_body plugin for custom handling of invalid request bodies (jeremyevans)
data/README.rdoc CHANGED
@@ -723,7 +723,7 @@ Note that when subclassing, Roda only does a shallow clone of the settings.
723
723
 
724
724
  If you store nested structures and plan to mutate them in subclasses,
725
725
  it is your responsibility to dup the nested structures inside +Roda.inherited+
726
- (making sure to call +super+). This should be is done so that that modifications
726
+ (making sure to call +super+). This should be is done so that modifications
727
727
  to the parent class made after subclassing do _not_ affect the subclass, and
728
728
  vice-versa.
729
729
 
@@ -851,9 +851,8 @@ should use an appropriate external middleware.
851
851
  It is possible to use other session cookie middleware such as
852
852
  <tt>Rack::Session::Cookie</tt>, but other middleware may not have the same security
853
853
  features that Roda's session support does. For example, the session cookies used by
854
- the <tt>Rack::Session::Cookie</tt> middleware are not encrypted, just signed to
855
- prevent tampering. This means you should not store any secret data in the session
856
- when using <tt>Rack::Session::Cookie</tt>.
854
+ the <tt>Rack::Session::Cookie</tt> middleware provided by Rack before Rack 3 are not
855
+ encrypted, just signed to prevent tampering.
857
856
 
858
857
  For any cookie-based sessions, make sure that the necessary secrets (+:secret+ option)
859
858
  are not disclosed to an attacker. Knowledge of the
@@ -960,17 +959,17 @@ application level using using the +default_headers+ plugin:
960
959
  Strict-Transport-Security :: Enforces SSL/TLS Connections to the application.
961
960
  X-Content-Type-Options :: Forces some browsers to respect a declared Content-Type header.
962
961
  X-Frame-Options :: Provides click-jacking protection by not allowing usage inside a frame.
963
- X-XSS-Protection :: Enables an XSS mitigation filter in some browsers.
962
+ Only include this if you want to support and protect old browsers that
963
+ do not support Content-Security-Policy.
964
964
 
965
965
  Example:
966
966
 
967
967
  class App < Roda
968
968
  plugin :default_headers,
969
969
  'Content-Type'=>'text/html',
970
- 'Strict-Transport-Security'=>'max-age=16070400;',
970
+ 'Strict-Transport-Security'=>'max-age=63072000; includeSubDomains',
971
971
  'X-Content-Type-Options'=>'nosniff',
972
- 'X-Frame-Options'=>'deny',
973
- 'X-XSS-Protection'=>'1; mode=block'
972
+ 'X-Frame-Options'=>'deny'
974
973
  end
975
974
 
976
975
  === Rendering Templates Derived From User Input
@@ -1007,25 +1006,16 @@ constants and removing them when any of the reloadable loaded files changes. It
1007
1006
  +require+ and +require_relative+ when activated (usually in the development environment). No
1008
1007
  configurations other than +reloadable_paths+ are required.
1009
1008
 
1010
- Both {rerun}[https://github.com/alexch/rerun] and
1011
- {shotgun}[https://github.com/rtomayko/shotgun] use a fork/exec approach for loading new
1012
- versions of your app. rerun is faster as it only reloads the app on changes, whereas
1013
- shotgun reloads the app on every request. Both work without any changes to application
1009
+ {rerun}[https://github.com/alexch/rerun] uses a fork/exec approach for loading new
1010
+ versions of your app. It work without any changes to application
1014
1011
  code, but may be slower as they have to reload the entire application on every change.
1015
- However, for small apps that load quickly, either may be a good approach.
1016
-
1017
- {Rack::Reloader}[https://github.com/rack/rack/blob/master/lib/rack/reloader.rb] ships
1018
- with rack and just reloads monitored files when they change, without unloading constants.
1019
- It's fast but may cause issues in cases where you remove classes, constants, or methods,
1020
- or when you are not clearing out cached data manually when files are reloaded.
1012
+ However, for small apps that load quickly, it may be a good approach.
1021
1013
 
1022
1014
  There is no one reloading solution that is the best for all applications and development
1023
1015
  approaches. Consider your needs and the tradeoffs of each of the reloading approaches,
1024
- and pick the one you think will work best.
1025
-
1026
- If you are unsure where to start, it may be best to start with rerun or shotgun
1027
- (unless you're running on JRuby or Windows), and only consider other options if rerun or
1028
- shotgun are not fast enough.
1016
+ and pick the one you think will work best. If you are unsure where to start,
1017
+ it may be best to start with Zeitwerk, and only consider other options if it does not
1018
+ work well for you.
1029
1019
 
1030
1020
  == Plugins
1031
1021
 
@@ -1144,4 +1134,3 @@ MIT
1144
1134
  == Maintainer
1145
1135
 
1146
1136
  Jeremy Evans <code@jeremyevans.net>
1147
-
data/doc/conventions.rdoc CHANGED
@@ -55,7 +55,7 @@ via <tt>irb -r ./models</tt>, without loading the Roda application.
55
55
  migrations.
56
56
 
57
57
  +spec/+ (or +test/+ should contain your specifications/tests. For a small application, it's recommended
58
- to a have a single file for your model tests, and a single file for your web/integration tests.
58
+ to have a single file for your model tests, and a single file for your web/integration tests.
59
59
 
60
60
  +Rakefile+ should contain the rake tasks for the application. The convention is that the
61
61
  default rake task will run all specs/tests related to the application. If you are using
@@ -0,0 +1,33 @@
1
+ = New Features
2
+
3
+ * The middleware plugin now accepts a :next_if_not_found option.
4
+ This allows the middleware plugin to pass the request to the next
5
+ application if the current application handles the request but
6
+ ends up calling the not_found handler. With the following
7
+ middleware:
8
+
9
+ class Mid < Roda
10
+ plugin :middleware
11
+
12
+ route do |r|
13
+ r.on "foo" do
14
+ r.get "bar" do
15
+ 'bar'
16
+ end
17
+ end
18
+ end
19
+ end
20
+
21
+ Requests for /x would be forwarded to the next application, since
22
+ the application doesn't handle the request, but requests for /foo/x
23
+ would not be, because the middleware is partially handling the
24
+ request in the r.on "foo" block. With the :next_if_not_found
25
+ option, only requests for /foo/bar would be handled by the
26
+ middleware, and all other requests would be forwarded to the next
27
+ application.
28
+
29
+ = Other Improvements
30
+
31
+ * The sessions and route_csrf plugins no longer depend on the base64
32
+ library. base64 will be removed from Ruby's standard library
33
+ starting in Ruby 3.4.
@@ -0,0 +1,28 @@
1
+ = New Features
2
+
3
+ * A redirect_http_to_https plugin has been added, redirecting HTTP
4
+ requests to the same path on an HTTPS site. Using the routing tree,
5
+ you can control where to do the redirection, which allows you to
6
+ easily have part of your site accessible via HTTP, with sensitive
7
+ sections requiring HTTPS:
8
+
9
+ plugin :redirect_http_to_https
10
+
11
+ route do |r|
12
+ # routes available via both HTTP and HTTPS
13
+ r.redirect_http_to_https
14
+ # routes available only via HTTPS
15
+ end
16
+
17
+ If you want to redirect to HTTPS for all routes in the routing tree, you
18
+ can have r.redirect_http_to_https as the very first method call in the
19
+ routing tree. Note that in Roda it is possible to handle routing before
20
+ the normal routing tree using before hooks. The static_routing and
21
+ heartbeat plugins use this feature. If you would like to handle routes
22
+ before the normal routing tree, you can setup a before hook:
23
+
24
+ plugin :hooks
25
+
26
+ before do
27
+ request.redirect_http_to_https
28
+ end
@@ -0,0 +1,34 @@
1
+ # frozen-string-literal: true
2
+
3
+ #
4
+ class Roda
5
+ module RodaPlugins
6
+ module Base64_
7
+ class << self
8
+ if RUBY_VERSION >= '2.4'
9
+ def decode64(str)
10
+ str.unpack1("m0")
11
+ end
12
+ # :nocov:
13
+ else
14
+ def decode64(str)
15
+ str.unpack("m0")[0]
16
+ end
17
+ # :nocov:
18
+ end
19
+
20
+ def urlsafe_encode64(bin)
21
+ str = [bin].pack("m0")
22
+ str.tr!("+/", "-_")
23
+ str
24
+ end
25
+
26
+ def urlsafe_decode64(str)
27
+ decode64(str.tr("-_", "+/"))
28
+ end
29
+ end
30
+ end
31
+
32
+ register_plugin(:_base64, Base64_)
33
+ end
34
+ end
@@ -411,7 +411,7 @@ END
411
411
 
412
412
  private
413
413
 
414
- if RUBY_VERSION >= '3.2'
414
+ if Exception.method_defined?(:detailed_message)
415
415
  def exception_page_exception_message(exception)
416
416
  exception.detailed_message(highlight: false).to_s
417
417
  end
@@ -33,6 +33,43 @@ class Roda
33
33
  #
34
34
  # run App
35
35
  #
36
+ # By default, when the app is used as middleware and handles the request at
37
+ # all, it does not forward the request to the next middleware. For the
38
+ # following setup:
39
+ #
40
+ # class Mid < Roda
41
+ # plugin :middleware
42
+ #
43
+ # route do |r|
44
+ # r.on "foo" do
45
+ # r.is "mid" do
46
+ # "Mid"
47
+ # end
48
+ # end
49
+ # end
50
+ # end
51
+ #
52
+ # class App < Roda
53
+ # use Mid
54
+ #
55
+ # route do |r|
56
+ # r.on "foo" do
57
+ # r.is "app" do
58
+ # "App"
59
+ # end
60
+ # end
61
+ # end
62
+ # end
63
+ #
64
+ # run App
65
+ #
66
+ # Requests for +/foo/mid will+ return +Mid+, but requests for +/foo/app+
67
+ # will return an empty 404 response, because the middleware handles the
68
+ # +/foo/app+ request in the <tt>r.on "foo" do</tt> block, but does not
69
+ # have the block return a result, which Roda treats as an empty 404 response.
70
+ # If you would like the middleware to forward +/foo/app+ request to the
71
+ # application, you should use the +:next_if_not_found+ plugin option.
72
+ #
36
73
  # It is possible to use the Roda app as a regular app even when using
37
74
  # the middleware plugin. Using an app as middleware automatically creates
38
75
  # a subclass of the app for the middleware. Because a subclass is automatically
@@ -64,6 +101,9 @@ class Roda
64
101
  # # Request to App for /mid returns
65
102
  # # "foo bar baz"
66
103
  module Middleware
104
+ NEXT_PROC = lambda{throw :next, true}
105
+ private_constant :NEXT_PROC
106
+
67
107
  # Configure the middleware plugin. Options:
68
108
  # :env_var :: Set the environment variable to use to indicate to the roda
69
109
  # application that the current request is a middleware request.
@@ -77,12 +117,15 @@ class Roda
77
117
  # the middleware's route block should be applied to the
78
118
  # final response when the request is forwarded to the app.
79
119
  # Defaults to false.
120
+ # :next_if_not_found :: If the middleware handles the request but returns a not found
121
+ # result (404 with no body), forward the result to the next middleware.
80
122
  def self.configure(app, opts={}, &block)
81
123
  app.opts[:middleware_env_var] = opts[:env_var] if opts.has_key?(:env_var)
82
124
  app.opts[:middleware_env_var] ||= 'roda.forward_next'
83
125
  app.opts[:middleware_configure] = block if block
84
126
  app.opts[:middleware_handle_result] = opts[:handle_result]
85
127
  app.opts[:middleware_forward_response_headers] = opts[:forward_response_headers]
128
+ app.opts[:middleware_next_if_not_found] = opts[:next_if_not_found]
86
129
  end
87
130
 
88
131
  # Forwarder instances are what is actually used as middleware.
@@ -91,6 +134,9 @@ class Roda
91
134
  # and store +app+ as the next middleware to call.
92
135
  def initialize(mid, app, *args, &block)
93
136
  @mid = Class.new(mid)
137
+ if @mid.opts[:middleware_next_if_not_found]
138
+ @mid.plugin(:not_found, &NEXT_PROC)
139
+ end
94
140
  if configure = @mid.opts[:middleware_configure]
95
141
  configure.call(@mid, *args, &block)
96
142
  elsif block || !args.empty?
@@ -0,0 +1,99 @@
1
+ # frozen-string-literal: true
2
+
3
+ #
4
+ class Roda
5
+ module RodaPlugins
6
+ # The redirect_http_to_https plugin exposes a +redirect_http_to_https+
7
+ # request method that redirects HTTP requests to HTTPS, helping to ensure
8
+ # that future requests by the same browser will be submitted securely.
9
+ #
10
+ # You should use this plugin if you have an application that can receive
11
+ # requests using both HTTP and HTTPS, and you want to make sure that all
12
+ # or a subset of routes are only handled for HTTPS requests.
13
+ #
14
+ # The reason this exposes a request method is so that you can choose where
15
+ # in your routing tree to do the redirection:
16
+ #
17
+ # route do |r|
18
+ # # routes available via both HTTP and HTTPS
19
+ # r.redirect_http_to_https
20
+ # # routes available only via HTTPS
21
+ # end
22
+ #
23
+ # If you want to redirect to HTTPS for all routes in the routing tree, you
24
+ # can have this as the very first method call in the routing tree. Note that
25
+ # in Roda it is possible to handle routing before the normal routing tree
26
+ # using before hooks. The static_routing and heartbeat plugins use this
27
+ # feature. If you would like to handle routes before the normal routing tree,
28
+ # you can setup a before hook:
29
+ #
30
+ # plugin :hooks
31
+ #
32
+ # before do
33
+ # request.redirect_http_to_https
34
+ # end
35
+ module RedirectHttpToHttps
36
+ status_map = Hash.new(307)
37
+ status_map['GET'] = status_map['HEAD'] = 301
38
+ status_map.freeze
39
+ DEFAULTS = {:status_map => status_map}.freeze
40
+ private_constant :DEFAULTS
41
+
42
+ # Configures redirection from HTTP to HTTPS. Available options:
43
+ #
44
+ # :body :: The body used in the redirect. If not set, uses an empty body.
45
+ # :headers :: Any additional headers used in the redirect response. By default,
46
+ # no additional headers are set, the only header used is the Location header.
47
+ # :host :: The host to redirect to. If not set, redirects to the same host as the HTTP
48
+ # requested to. It is highly recommended that you set this if requests with
49
+ # arbitrary Host headers can be submitted to the application.
50
+ # :port :: The port to use in the redirect. By default, will not set an explicit port,
51
+ # so that it will implicitly use the HTTPS default port of 443.
52
+ # :status_map :: A hash mapping request methods to response status codes. By default,
53
+ # uses a hash that redirects GET and HEAD requests with a 301 status,
54
+ # and other request methods with a 307 status.
55
+ def self.configure(app, opts=OPTS)
56
+ previous = app.opts[:redirect_http_to_https] || DEFAULTS
57
+ opts = app.opts[:redirect_http_to_https] = previous.merge(opts)
58
+ opts[:port_string] = opts[:port] ? ":#{opts[:port]}".freeze : "".freeze
59
+ opts[:prefix] = opts[:host] ? "https://#{opts[:host]}#{opts[:port_string]}".freeze : nil
60
+ opts.freeze
61
+ end
62
+
63
+ module RequestMethods
64
+ # Redirect HTTP requests to HTTPS. While this doesn't secure the
65
+ # current request, it makes it more likely that the browser will submit
66
+ # future requests securely via HTTPS.
67
+ def redirect_http_to_https
68
+ return if ssl?
69
+
70
+ opts = roda_class.opts[:redirect_http_to_https]
71
+
72
+ res = response
73
+
74
+ if body = opts[:body]
75
+ res.write(body)
76
+ end
77
+
78
+ if headers = opts[:headers]
79
+ res.headers.merge!(headers)
80
+ end
81
+
82
+ path = if prefix = opts[:prefix]
83
+ prefix + fullpath
84
+ else
85
+ "https://#{host}#{opts[:port_string]}#{fullpath}"
86
+ end
87
+
88
+ unless status = opts[:status_map][@env['REQUEST_METHOD']]
89
+ raise RodaError, "redirect_http_to_https :status_map provided does not support #{@env['REQUEST_METHOD']}"
90
+ end
91
+
92
+ redirect(path, status)
93
+ end
94
+ end
95
+ end
96
+
97
+ register_plugin(:redirect_http_to_https, RedirectHttpToHttps)
98
+ end
99
+ end
@@ -1,6 +1,5 @@
1
1
  # frozen-string-literal: true
2
2
 
3
- require 'base64'
4
3
  require 'openssl'
5
4
  require 'securerandom'
6
5
  require 'uri'
@@ -23,9 +22,11 @@ class Roda
23
22
  # and request path even if they have access to a token that is not
24
23
  # specific to request method and request path. To get this security
25
24
  # benefit, you must ensure an attacker does not have access to the
26
- # session. Rack::Session::Cookie uses signed sessions, not encrypted
25
+ # session. Rack::Session::Cookie versions shipped with Rack before
26
+ # Rack 3 use signed sessions, not encrypted
27
27
  # sessions, so if the attacker has the ability to read cookie data
28
- # and you are using Rack::Session::Cookie, it will still be possible
28
+ # and you are using one of those Rack::Session::Cookie versions,
29
+ # it will still be possible
29
30
  # for an attacker to generate valid CSRF tokens specific to arbitrary
30
31
  # request method and request path. Roda's session plugin uses
31
32
  # encrypted sessions and therefore is safe even if the attacker can
@@ -163,6 +164,10 @@ class Roda
163
164
  # a valid CSRF token was not provided.
164
165
  class InvalidToken < RodaError; end
165
166
 
167
+ def self.load_dependencies(app, opts=OPTS)
168
+ app.plugin :_base64
169
+ end
170
+
166
171
  def self.configure(app, opts=OPTS, &block)
167
172
  options = app.opts[:route_csrf] = (app.opts[:route_csrf] || DEFAULTS).merge(opts)
168
173
  if block || opts[:csrf_failure].is_a?(Proc)
@@ -260,7 +265,7 @@ class Roda
260
265
  def csrf_token(path=nil, method=('POST' if path))
261
266
  token = SecureRandom.random_bytes(31)
262
267
  token << csrf_hmac(token, method, path)
263
- Base64.strict_encode64(token)
268
+ [token].pack("m0")
264
269
  end
265
270
 
266
271
  # Whether request-specific CSRF tokens should be used by default.
@@ -314,7 +319,7 @@ class Roda
314
319
  end
315
320
 
316
321
  begin
317
- submitted_hmac = Base64.strict_decode64(encoded_token)
322
+ submitted_hmac = Base64_.decode64(encoded_token)
318
323
  rescue ArgumentError
319
324
  return "encoded token is not valid base64"
320
325
  end
@@ -354,7 +359,7 @@ class Roda
354
359
  # JSON is used for session serialization).
355
360
  def csrf_secret
356
361
  key = session[csrf_options[:key]] ||= SecureRandom.base64(32)
357
- Base64.strict_decode64(key)
362
+ Base64_.decode64(key)
358
363
  end
359
364
  end
360
365
  end
@@ -10,7 +10,6 @@ rescue OpenSSL::Cipher::CipherError
10
10
  # :nocov:
11
11
  end
12
12
 
13
- require 'base64'
14
13
  require 'json'
15
14
  require 'securerandom'
16
15
  require 'zlib'
@@ -41,7 +40,8 @@ class Roda
41
40
  #
42
41
  # Session secrets can be rotated. See options below.
43
42
  #
44
- # The sessions plugin can transparently upgrade sessions from Rack::Session::Cookie
43
+ # The sessions plugin can transparently upgrade sessions from versions of Rack::Session::Cookie
44
+ # shipped with Rack before Rack 3,
45
45
  # if the default Rack::Session::Cookie coder and HMAC are used, see options below.
46
46
  # It is recommended to only enable transparent upgrades for a brief transition period,
47
47
  # and remove support for them once old sessions have converted or timed out.
@@ -171,6 +171,10 @@ class Roda
171
171
  [cipher_secret.freeze, hmac_secret.freeze]
172
172
  end
173
173
 
174
+ def self.load_dependencies(app, opts=OPTS)
175
+ app.plugin :_base64
176
+ end
177
+
174
178
  # Configure the plugin, see Sessions for details on options.
175
179
  def self.configure(app, opts=OPTS)
176
180
  opts = (app.opts[:sessions] || DEFAULT_OPTIONS).merge(opts)
@@ -344,7 +348,7 @@ class Roda
344
348
  opts = roda_class.opts[:sessions]
345
349
 
346
350
  begin
347
- data = Base64.urlsafe_decode64(data)
351
+ data = Base64_.urlsafe_decode64(data)
348
352
  rescue ArgumentError
349
353
  return _session_serialization_error("Unable to decode session: invalid base64")
350
354
  end
@@ -493,7 +497,7 @@ class Roda
493
497
  data << encrypted_data
494
498
  data << OpenSSL::HMAC.digest(OpenSSL::Digest::SHA256.new, opts[:hmac_secret], data+opts[:key])
495
499
 
496
- data = Base64.urlsafe_encode64(data)
500
+ data = Base64_.urlsafe_encode64(data)
497
501
 
498
502
  if data.bytesize >= 4096
499
503
  raise CookieTooLarge, "attempted to create cookie larger than 4096 bytes"
data/lib/roda/response.rb CHANGED
@@ -10,19 +10,15 @@ class Roda
10
10
  # headers used internally by Roda can be lower case on Rack 3, so that it is
11
11
  # possible to use a plain hash of response headers instead of using Rack::Headers.
12
12
  module RodaResponseHeaders
13
- headers = %w'Allow Cache-Control Content-Disposition Content-Encoding Content-Length
14
- Content-Security-Policy Content-Security-Policy-Report-Only Content-Type
15
- ETag Expires Last-Modified Link Location Set-Cookie Transfer-Encoding Vary'.freeze.each(&:freeze)
13
+ downcase = defined?(Rack::Headers) && Rack::Headers.is_a?(Class)
16
14
 
17
- if defined?(Rack::Headers) && Rack::Headers.is_a?(Class)
18
- headers.each do |mixed_case|
19
- const_set(mixed_case.gsub('-', '_').upcase!.to_sym, mixed_case.downcase.freeze)
20
- end
21
- else
22
- headers.each do |mixed_case|
23
- const_set(mixed_case.gsub('-', '_').upcase!.to_sym, mixed_case.freeze)
15
+ %w'Allow Cache-Control Content-Disposition Content-Encoding Content-Length
16
+ Content-Security-Policy Content-Security-Policy-Report-Only Content-Type
17
+ ETag Expires Last-Modified Link Location Set-Cookie Transfer-Encoding Vary'.
18
+ each do |value|
19
+ value = value.downcase if downcase
20
+ const_set(value.gsub('-', '_').upcase!.to_sym, value.freeze)
24
21
  end
25
- end
26
22
  end
27
23
 
28
24
  # Base class used for Roda responses. The instance methods for this
data/lib/roda/version.rb CHANGED
@@ -4,7 +4,7 @@ class Roda
4
4
  RodaMajorVersion = 3
5
5
 
6
6
  # The minor version of Roda, updated for new feature releases of Roda.
7
- RodaMinorVersion = 72
7
+ RodaMinorVersion = 74
8
8
 
9
9
  # The patch version of Roda, updated only for bug fixes from the last
10
10
  # feature release.
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: roda
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.72.0
4
+ version: 3.74.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jeremy Evans
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-09-12 00:00:00.000000000 Z
11
+ date: 2023-11-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rack
@@ -246,6 +246,8 @@ extra_rdoc_files:
246
246
  - doc/release_notes/3.70.0.txt
247
247
  - doc/release_notes/3.71.0.txt
248
248
  - doc/release_notes/3.72.0.txt
249
+ - doc/release_notes/3.73.0.txt
250
+ - doc/release_notes/3.74.0.txt
249
251
  - doc/release_notes/3.8.0.txt
250
252
  - doc/release_notes/3.9.0.txt
251
253
  files:
@@ -325,6 +327,8 @@ files:
325
327
  - doc/release_notes/3.70.0.txt
326
328
  - doc/release_notes/3.71.0.txt
327
329
  - doc/release_notes/3.72.0.txt
330
+ - doc/release_notes/3.73.0.txt
331
+ - doc/release_notes/3.74.0.txt
328
332
  - doc/release_notes/3.8.0.txt
329
333
  - doc/release_notes/3.9.0.txt
330
334
  - lib/roda.rb
@@ -332,6 +336,7 @@ files:
332
336
  - lib/roda/plugins.rb
333
337
  - lib/roda/plugins/Integer_matcher_max.rb
334
338
  - lib/roda/plugins/_after_hook.rb
339
+ - lib/roda/plugins/_base64.rb
335
340
  - lib/roda/plugins/_before_hook.rb
336
341
  - lib/roda/plugins/_optimized_matching.rb
337
342
  - lib/roda/plugins/_symbol_regexp_matchers.rb
@@ -425,6 +430,7 @@ files:
425
430
  - lib/roda/plugins/public.rb
426
431
  - lib/roda/plugins/r.rb
427
432
  - lib/roda/plugins/recheck_precompiled_assets.rb
433
+ - lib/roda/plugins/redirect_http_to_https.rb
428
434
  - lib/roda/plugins/relative_path.rb
429
435
  - lib/roda/plugins/render.rb
430
436
  - lib/roda/plugins/render_coverage.rb