roda 3.72.0 → 3.73.0

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: d5c28e38d35f59176f159798f68d82bbd30df62a5907ace3a3750b1daf7b4fe6
4
- data.tar.gz: cb5fd8d5a9d665bc820c55ccbf1fd6a068873ae3e265a1f877bbdf82b51c4d6e
3
+ metadata.gz: 7a47f703af806a15f523b99b38d084520bada92f23196374d6d9781d5953faa8
4
+ data.tar.gz: 4612153820b89e6dbcfa2370c04269666c49277d8f11256869f6c1f0bb0b571a
5
5
  SHA512:
6
- metadata.gz: d8710537511b8f332c443fd41bd279c99de53c0bc9618cd92f763ab7d354f70c4496ba9eafa076438087b5ab52f36e69f649aa00d2fb6f07d5315b7d61aef4a1
7
- data.tar.gz: e0af216b465facc81e81ab8a495d472685c26aa6b2c1d56c0e48631586555f78f4d5922ac5ee92c6118a08b8162ee35866dc5708155bdbed70d5851b2ff8339b
6
+ metadata.gz: bdc42cdc9540750195327dac290e3f0bb08c3ed0100b610441b644c85849e7e9f9f974013cea041cdf640f590e2027996d68b62eea9c31d41bd42a57df6530bb
7
+ data.tar.gz: 3ab05db704cf45803ad706e78b558ea7798d8d9782a06c7dec79814a4d862582249e5554946c59fff88e1f1d2d360e242268c593a8c9c95fe87b5489e4a4a44c
data/CHANGELOG CHANGED
@@ -1,3 +1,9 @@
1
+ = 3.73.0 (2023-10-13)
2
+
3
+ * Support :next_if_not_found option for middleware plugin (jeremyevans) (#334)
4
+
5
+ * 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)
6
+
1
7
  = 3.72.0 (2023-09-12)
2
8
 
3
9
  * Add invalid_request_body plugin for custom handling of invalid request bodies (jeremyevans)
@@ -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,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?
@@ -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'
@@ -163,6 +162,10 @@ class Roda
163
162
  # a valid CSRF token was not provided.
164
163
  class InvalidToken < RodaError; end
165
164
 
165
+ def self.load_dependencies(app, opts=OPTS)
166
+ app.plugin :_base64
167
+ end
168
+
166
169
  def self.configure(app, opts=OPTS, &block)
167
170
  options = app.opts[:route_csrf] = (app.opts[:route_csrf] || DEFAULTS).merge(opts)
168
171
  if block || opts[:csrf_failure].is_a?(Proc)
@@ -260,7 +263,7 @@ class Roda
260
263
  def csrf_token(path=nil, method=('POST' if path))
261
264
  token = SecureRandom.random_bytes(31)
262
265
  token << csrf_hmac(token, method, path)
263
- Base64.strict_encode64(token)
266
+ [token].pack("m0")
264
267
  end
265
268
 
266
269
  # Whether request-specific CSRF tokens should be used by default.
@@ -314,7 +317,7 @@ class Roda
314
317
  end
315
318
 
316
319
  begin
317
- submitted_hmac = Base64.strict_decode64(encoded_token)
320
+ submitted_hmac = Base64_.decode64(encoded_token)
318
321
  rescue ArgumentError
319
322
  return "encoded token is not valid base64"
320
323
  end
@@ -354,7 +357,7 @@ class Roda
354
357
  # JSON is used for session serialization).
355
358
  def csrf_secret
356
359
  key = session[csrf_options[:key]] ||= SecureRandom.base64(32)
357
- Base64.strict_decode64(key)
360
+ Base64_.decode64(key)
358
361
  end
359
362
  end
360
363
  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'
@@ -171,6 +170,10 @@ class Roda
171
170
  [cipher_secret.freeze, hmac_secret.freeze]
172
171
  end
173
172
 
173
+ def self.load_dependencies(app, opts=OPTS)
174
+ app.plugin :_base64
175
+ end
176
+
174
177
  # Configure the plugin, see Sessions for details on options.
175
178
  def self.configure(app, opts=OPTS)
176
179
  opts = (app.opts[:sessions] || DEFAULT_OPTIONS).merge(opts)
@@ -344,7 +347,7 @@ class Roda
344
347
  opts = roda_class.opts[:sessions]
345
348
 
346
349
  begin
347
- data = Base64.urlsafe_decode64(data)
350
+ data = Base64_.urlsafe_decode64(data)
348
351
  rescue ArgumentError
349
352
  return _session_serialization_error("Unable to decode session: invalid base64")
350
353
  end
@@ -493,7 +496,7 @@ class Roda
493
496
  data << encrypted_data
494
497
  data << OpenSSL::HMAC.digest(OpenSSL::Digest::SHA256.new, opts[:hmac_secret], data+opts[:key])
495
498
 
496
- data = Base64.urlsafe_encode64(data)
499
+ data = Base64_.urlsafe_encode64(data)
497
500
 
498
501
  if data.bytesize >= 4096
499
502
  raise CookieTooLarge, "attempted to create cookie larger than 4096 bytes"
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 = 73
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.73.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-10-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rack
@@ -246,6 +246,7 @@ 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
249
250
  - doc/release_notes/3.8.0.txt
250
251
  - doc/release_notes/3.9.0.txt
251
252
  files:
@@ -325,6 +326,7 @@ files:
325
326
  - doc/release_notes/3.70.0.txt
326
327
  - doc/release_notes/3.71.0.txt
327
328
  - doc/release_notes/3.72.0.txt
329
+ - doc/release_notes/3.73.0.txt
328
330
  - doc/release_notes/3.8.0.txt
329
331
  - doc/release_notes/3.9.0.txt
330
332
  - lib/roda.rb
@@ -332,6 +334,7 @@ files:
332
334
  - lib/roda/plugins.rb
333
335
  - lib/roda/plugins/Integer_matcher_max.rb
334
336
  - lib/roda/plugins/_after_hook.rb
337
+ - lib/roda/plugins/_base64.rb
335
338
  - lib/roda/plugins/_before_hook.rb
336
339
  - lib/roda/plugins/_optimized_matching.rb
337
340
  - lib/roda/plugins/_symbol_regexp_matchers.rb