roda 3.53.0 → 3.56.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 +4 -4
- data/CHANGELOG +42 -588
- data/doc/release_notes/3.54.0.txt +48 -0
- data/doc/release_notes/3.55.0.txt +12 -0
- data/doc/release_notes/3.56.0.txt +33 -0
- data/lib/roda/plugins/additional_view_directories.rb +1 -3
- data/lib/roda/plugins/assets.rb +6 -3
- data/lib/roda/plugins/chunked.rb +48 -22
- data/lib/roda/plugins/common_logger.rb +1 -1
- data/lib/roda/plugins/cookies.rb +2 -0
- data/lib/roda/plugins/drop_body.rb +5 -4
- data/lib/roda/plugins/heartbeat.rb +6 -1
- data/lib/roda/plugins/json_parser.rb +24 -5
- data/lib/roda/plugins/middleware.rb +17 -2
- data/lib/roda/plugins/multi_public.rb +13 -1
- data/lib/roda/plugins/not_allowed.rb +13 -0
- data/lib/roda/plugins/public.rb +22 -7
- data/lib/roda/plugins/render.rb +5 -3
- data/lib/roda/plugins/route_csrf.rb +4 -1
- data/lib/roda/plugins/run_append_slash.rb +1 -1
- data/lib/roda/plugins/run_handler.rb +7 -1
- data/lib/roda/plugins/run_require_slash.rb +46 -0
- data/lib/roda/plugins/sessions.rb +1 -0
- data/lib/roda/plugins/sinatra_helpers.rb +15 -1
- data/lib/roda/plugins/static.rb +2 -0
- data/lib/roda/plugins/status_303.rb +6 -3
- data/lib/roda/plugins/status_handler.rb +35 -9
- data/lib/roda/plugins/symbol_status.rb +2 -0
- data/lib/roda/plugins/unescape_path.rb +2 -0
- data/lib/roda/request.rb +35 -1
- data/lib/roda/response.rb +39 -7
- data/lib/roda/version.rb +1 -1
- metadata +28 -7
|
@@ -35,7 +35,13 @@ class Roda
|
|
|
35
35
|
def run(app, opts=OPTS)
|
|
36
36
|
res = catch(:halt){super(app)}
|
|
37
37
|
yield res if defined?(yield)
|
|
38
|
-
|
|
38
|
+
if opts[:not_found] == :pass && res[0] == 404
|
|
39
|
+
body = res[2]
|
|
40
|
+
body.close if body.respond_to?(:close)
|
|
41
|
+
nil
|
|
42
|
+
else
|
|
43
|
+
throw(:halt, res)
|
|
44
|
+
end
|
|
39
45
|
end
|
|
40
46
|
end
|
|
41
47
|
end
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# frozen-string-literal: true
|
|
2
|
+
|
|
3
|
+
#
|
|
4
|
+
class Roda
|
|
5
|
+
module RodaPlugins
|
|
6
|
+
# The run_require_slash plugin makes +r.run+ a no-op if the remaining
|
|
7
|
+
# path is not empty and does not start with +/+. The Rack SPEC requires that
|
|
8
|
+
# +PATH_INFO+ start with a slash if not empty, so this plugin prevents
|
|
9
|
+
# dispatching to the application with an environment that would violate the
|
|
10
|
+
# Rack SPEC.
|
|
11
|
+
#
|
|
12
|
+
# You are unlikely to want to use this plugin unless are consuming partial
|
|
13
|
+
# segments of the request path, or using the match_affix plugin to change
|
|
14
|
+
# how routing is done:
|
|
15
|
+
#
|
|
16
|
+
# plugin :match_affix, "", /(\/|\z)/
|
|
17
|
+
# route do |r|
|
|
18
|
+
# r.on "/a" do
|
|
19
|
+
# r.on "b" do
|
|
20
|
+
# r.run App
|
|
21
|
+
# end
|
|
22
|
+
# end
|
|
23
|
+
# end
|
|
24
|
+
#
|
|
25
|
+
# # with run_require_slash:
|
|
26
|
+
# # GET /a/b/e => App not dispatched to
|
|
27
|
+
# # GET /a/b => App gets "" as PATH_INFO
|
|
28
|
+
#
|
|
29
|
+
# # with run_require_slash:
|
|
30
|
+
# # GET /a/b/e => App gets "e" as PATH_INFO, violating rack SPEC
|
|
31
|
+
# # GET /a/b => App gets "" as PATH_INFO
|
|
32
|
+
module RunRequireSlash
|
|
33
|
+
module RequestMethods
|
|
34
|
+
# Calls the given rack app only if the remaining patch is empty or
|
|
35
|
+
# starts with a slash.
|
|
36
|
+
def run(*)
|
|
37
|
+
if @remaining_path.empty? || @remaining_path.start_with?('/')
|
|
38
|
+
super
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
register_plugin(:run_require_slash, RunRequireSlash)
|
|
45
|
+
end
|
|
46
|
+
end
|
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# frozen-string-literal: true
|
|
2
2
|
|
|
3
|
+
require 'rack/mime'
|
|
4
|
+
begin
|
|
5
|
+
require 'rack/files'
|
|
6
|
+
rescue LoadError
|
|
7
|
+
# :nocov:
|
|
8
|
+
require 'rack/file'
|
|
9
|
+
# :nocov:
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
|
|
3
13
|
#
|
|
4
14
|
class Roda
|
|
5
15
|
module RodaPlugins
|
|
@@ -215,6 +225,10 @@ class Roda
|
|
|
215
225
|
ISO88591_ENCODING = Encoding.find('ISO-8859-1')
|
|
216
226
|
BINARY_ENCODING = Encoding.find('BINARY')
|
|
217
227
|
|
|
228
|
+
# :nocov:
|
|
229
|
+
RACK_FILES = defined?(Rack::Files) ? Rack::Files : Rack::File
|
|
230
|
+
# :nocov:
|
|
231
|
+
|
|
218
232
|
# Depend on the status_303 plugin.
|
|
219
233
|
def self.load_dependencies(app, _opts = nil)
|
|
220
234
|
app.plugin :status_303
|
|
@@ -333,7 +347,7 @@ class Roda
|
|
|
333
347
|
last_modified(lm)
|
|
334
348
|
end
|
|
335
349
|
|
|
336
|
-
file =
|
|
350
|
+
file = RACK_FILES.new nil
|
|
337
351
|
s, h, b = if Rack.release > '2'
|
|
338
352
|
file.serving(self, path)
|
|
339
353
|
else
|
data/lib/roda/plugins/static.rb
CHANGED
|
@@ -17,10 +17,13 @@ class Roda
|
|
|
17
17
|
private
|
|
18
18
|
|
|
19
19
|
def default_redirect_status
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
20
|
+
return super if is_get?
|
|
21
|
+
|
|
22
|
+
case http_version
|
|
23
|
+
when 'HTTP/1.0', 'HTTP/0.9', nil
|
|
23
24
|
super
|
|
25
|
+
else
|
|
26
|
+
303
|
|
24
27
|
end
|
|
25
28
|
end
|
|
26
29
|
end
|
|
@@ -15,24 +15,53 @@ class Roda
|
|
|
15
15
|
# status_handler(403) do
|
|
16
16
|
# "You are forbidden from seeing that!"
|
|
17
17
|
# end
|
|
18
|
+
#
|
|
18
19
|
# status_handler(404) do
|
|
19
20
|
# "Where did it go?"
|
|
20
21
|
# end
|
|
21
22
|
#
|
|
23
|
+
# status_handler(405, keep_headers: ['Accept']) do
|
|
24
|
+
# "Use a different method!"
|
|
25
|
+
# end
|
|
26
|
+
#
|
|
22
27
|
# Before a block is called, any existing headers on the response will be
|
|
23
|
-
# cleared
|
|
24
|
-
#
|
|
28
|
+
# cleared, unless the +:keep_headers+ option is used. If the +:keep_headers+
|
|
29
|
+
# option is used, the value should be an array, and only the headers listed
|
|
30
|
+
# in the array will be kept.
|
|
25
31
|
module StatusHandler
|
|
32
|
+
CLEAR_HEADERS = :clear.to_proc
|
|
33
|
+
private_constant :CLEAR_HEADERS
|
|
34
|
+
|
|
26
35
|
def self.configure(app)
|
|
27
36
|
app.opts[:status_handler] ||= {}
|
|
28
37
|
end
|
|
29
38
|
|
|
30
39
|
module ClassMethods
|
|
31
40
|
# Install the given block as a status handler for the given HTTP response code.
|
|
32
|
-
def status_handler(code, &block)
|
|
41
|
+
def status_handler(code, opts=OPTS, &block)
|
|
33
42
|
# For backwards compatibility, pass request argument if block accepts argument
|
|
34
43
|
arity = block.arity == 0 ? 0 : 1
|
|
35
|
-
|
|
44
|
+
handle_headers = case keep_headers = opts[:keep_headers]
|
|
45
|
+
when nil, false
|
|
46
|
+
CLEAR_HEADERS
|
|
47
|
+
when Array
|
|
48
|
+
# :nocov:
|
|
49
|
+
if Rack.release >= '2.3'
|
|
50
|
+
keep_headers = keep_headers.map(&:downcase)
|
|
51
|
+
end
|
|
52
|
+
# :nocov:
|
|
53
|
+
lambda{|headers| headers.delete_if{|k,_| !keep_headers.include?(k)}}
|
|
54
|
+
else
|
|
55
|
+
raise RodaError, "Invalid :keep_headers option"
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
meth = define_roda_method(:"_roda_status_handler__#{code}", arity, &block)
|
|
59
|
+
self.opts[:status_handler][code] = define_roda_method(:"_roda_status_handler_#{code}", 1) do |result|
|
|
60
|
+
res = @_response
|
|
61
|
+
res.status = result[0]
|
|
62
|
+
handle_headers.call(res.headers)
|
|
63
|
+
result.replace(_roda_handle_route{arity == 1 ? send(meth, @_request) : send(meth)})
|
|
64
|
+
end
|
|
36
65
|
end
|
|
37
66
|
|
|
38
67
|
# Freeze the hash of status handlers so that there can be no thread safety issues at runtime.
|
|
@@ -47,11 +76,8 @@ class Roda
|
|
|
47
76
|
|
|
48
77
|
# If routing returns a response we have a handler for, call that handler.
|
|
49
78
|
def _roda_after_20__status_handler(result)
|
|
50
|
-
if result && (meth
|
|
51
|
-
|
|
52
|
-
res.headers.clear
|
|
53
|
-
res.status = result[0]
|
|
54
|
-
result.replace(_roda_handle_route{arity == 1 ? send(meth, @_request) : send(meth)})
|
|
79
|
+
if result && (meth = opts[:status_handler][result[0]]) && (v = result[2]).is_a?(Array) && v.empty?
|
|
80
|
+
send(meth, result)
|
|
55
81
|
end
|
|
56
82
|
end
|
|
57
83
|
end
|
data/lib/roda/request.rb
CHANGED
|
@@ -1,6 +1,19 @@
|
|
|
1
1
|
# frozen-string-literal: true
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
# :nocov:
|
|
4
|
+
begin
|
|
5
|
+
require "rack/version"
|
|
6
|
+
rescue LoadError
|
|
7
|
+
require "rack"
|
|
8
|
+
else
|
|
9
|
+
if Rack.release >= '2.3'
|
|
10
|
+
require "rack/request"
|
|
11
|
+
else
|
|
12
|
+
require "rack"
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
# :nocov:
|
|
16
|
+
|
|
4
17
|
require_relative "cache"
|
|
5
18
|
|
|
6
19
|
class Roda
|
|
@@ -116,6 +129,27 @@ class Roda
|
|
|
116
129
|
"#<#{self.class.inspect} #{@env["REQUEST_METHOD"]} #{path}>"
|
|
117
130
|
end
|
|
118
131
|
|
|
132
|
+
# :nocov:
|
|
133
|
+
if Rack.release >= '2.3'
|
|
134
|
+
def http_version
|
|
135
|
+
# Prefer SERVER_PROTOCOL as it is required in Rack 3.
|
|
136
|
+
# Still fall back to HTTP_VERSION if SERVER_PROTOCOL
|
|
137
|
+
# is not set, in case the server in use is not Rack 3
|
|
138
|
+
# compliant.
|
|
139
|
+
@env['SERVER_PROTOCOL'] || @env['HTTP_VERSION']
|
|
140
|
+
end
|
|
141
|
+
else
|
|
142
|
+
# :nocov:
|
|
143
|
+
# What HTTP version the request was submitted with.
|
|
144
|
+
def http_version
|
|
145
|
+
# Prefer HTTP_VERSION as it is backwards compatible
|
|
146
|
+
# with previous Roda versions. Fallback to
|
|
147
|
+
# SERVER_PROTOCOL for servers that do not set
|
|
148
|
+
# HTTP_VERSION.
|
|
149
|
+
@env['HTTP_VERSION'] || @env['SERVER_PROTOCOL']
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
|
|
119
153
|
# Does a terminal match on the current path, matching only if the arguments
|
|
120
154
|
# have fully matched the path. If it matches, the match block is
|
|
121
155
|
# executed, and when the match block returns, the rack response is
|
data/lib/roda/response.rb
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
# frozen-string-literal: true
|
|
2
2
|
|
|
3
|
+
begin
|
|
4
|
+
require 'rack/headers'
|
|
5
|
+
rescue LoadError
|
|
6
|
+
end
|
|
7
|
+
|
|
3
8
|
class Roda
|
|
4
9
|
# Base class used for Roda responses. The instance methods for this
|
|
5
10
|
# class are added by Roda::RodaPlugins::Base::ResponseMethods, the class
|
|
@@ -37,11 +42,22 @@ class Roda
|
|
|
37
42
|
# code for non-empty responses and a 404 code for empty responses.
|
|
38
43
|
attr_accessor :status
|
|
39
44
|
|
|
40
|
-
#
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
+
# :nocov:
|
|
46
|
+
if defined?(Rack::Headers) && Rack::Headers.is_a?(Class)
|
|
47
|
+
# Set the default headers when creating a response.
|
|
48
|
+
def initialize
|
|
49
|
+
@headers = Rack::Headers.new
|
|
50
|
+
@body = []
|
|
51
|
+
@length = 0
|
|
52
|
+
end
|
|
53
|
+
else
|
|
54
|
+
# :nocov:
|
|
55
|
+
# Set the default headers when creating a response.
|
|
56
|
+
def initialize
|
|
57
|
+
@headers = {}
|
|
58
|
+
@body = []
|
|
59
|
+
@length = 0
|
|
60
|
+
end
|
|
45
61
|
end
|
|
46
62
|
|
|
47
63
|
# Return the response header with the given key. Example:
|
|
@@ -96,8 +112,7 @@ class Roda
|
|
|
96
112
|
if (s == 304 || s == 204 || (s >= 100 && s <= 199))
|
|
97
113
|
h.delete("Content-Type")
|
|
98
114
|
elsif s == 205
|
|
99
|
-
h
|
|
100
|
-
h["Content-Length"] = '0'
|
|
115
|
+
empty_205_headers(h)
|
|
101
116
|
else
|
|
102
117
|
h["Content-Length"] ||= '0'
|
|
103
118
|
end
|
|
@@ -158,6 +173,23 @@ class Roda
|
|
|
158
173
|
|
|
159
174
|
private
|
|
160
175
|
|
|
176
|
+
# :nocov:
|
|
177
|
+
if Rack.release < '2.0.2'
|
|
178
|
+
# Don't use a content length for empty 205 responses on
|
|
179
|
+
# rack 1, as it violates Rack::Lint in that version.
|
|
180
|
+
def empty_205_headers(headers)
|
|
181
|
+
headers.delete("Content-Type")
|
|
182
|
+
headers.delete("Content-Length")
|
|
183
|
+
end
|
|
184
|
+
# :nocov:
|
|
185
|
+
else
|
|
186
|
+
# Set the content length for empty 205 responses to 0
|
|
187
|
+
def empty_205_headers(headers)
|
|
188
|
+
headers.delete("Content-Type")
|
|
189
|
+
headers["Content-Length"] = '0'
|
|
190
|
+
end
|
|
191
|
+
end
|
|
192
|
+
|
|
161
193
|
# For each default header, if a header has not already been set for the
|
|
162
194
|
# response, set the header in the response.
|
|
163
195
|
def set_default_headers
|
data/lib/roda/version.rb
CHANGED
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.
|
|
4
|
+
version: 3.56.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: 2022-
|
|
11
|
+
date: 2022-05-13 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: rack
|
|
@@ -52,6 +52,20 @@ dependencies:
|
|
|
52
52
|
- - ">="
|
|
53
53
|
- !ruby/object:Gem::Version
|
|
54
54
|
version: 5.7.0
|
|
55
|
+
- !ruby/object:Gem::Dependency
|
|
56
|
+
name: minitest-hooks
|
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
|
58
|
+
requirements:
|
|
59
|
+
- - ">="
|
|
60
|
+
- !ruby/object:Gem::Version
|
|
61
|
+
version: '0'
|
|
62
|
+
type: :development
|
|
63
|
+
prerelease: false
|
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
65
|
+
requirements:
|
|
66
|
+
- - ">="
|
|
67
|
+
- !ruby/object:Gem::Version
|
|
68
|
+
version: '0'
|
|
55
69
|
- !ruby/object:Gem::Dependency
|
|
56
70
|
name: minitest-global_expectations
|
|
57
71
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -109,7 +123,7 @@ dependencies:
|
|
|
109
123
|
- !ruby/object:Gem::Version
|
|
110
124
|
version: '0'
|
|
111
125
|
- !ruby/object:Gem::Dependency
|
|
112
|
-
name:
|
|
126
|
+
name: sassc
|
|
113
127
|
requirement: !ruby/object:Gem::Requirement
|
|
114
128
|
requirements:
|
|
115
129
|
- - ">="
|
|
@@ -211,6 +225,9 @@ extra_rdoc_files:
|
|
|
211
225
|
- doc/release_notes/3.51.0.txt
|
|
212
226
|
- doc/release_notes/3.52.0.txt
|
|
213
227
|
- doc/release_notes/3.53.0.txt
|
|
228
|
+
- doc/release_notes/3.54.0.txt
|
|
229
|
+
- doc/release_notes/3.55.0.txt
|
|
230
|
+
- doc/release_notes/3.56.0.txt
|
|
214
231
|
- doc/release_notes/3.6.0.txt
|
|
215
232
|
- doc/release_notes/3.7.0.txt
|
|
216
233
|
- doc/release_notes/3.8.0.txt
|
|
@@ -271,6 +288,9 @@ files:
|
|
|
271
288
|
- doc/release_notes/3.51.0.txt
|
|
272
289
|
- doc/release_notes/3.52.0.txt
|
|
273
290
|
- doc/release_notes/3.53.0.txt
|
|
291
|
+
- doc/release_notes/3.54.0.txt
|
|
292
|
+
- doc/release_notes/3.55.0.txt
|
|
293
|
+
- doc/release_notes/3.56.0.txt
|
|
274
294
|
- doc/release_notes/3.6.0.txt
|
|
275
295
|
- doc/release_notes/3.7.0.txt
|
|
276
296
|
- doc/release_notes/3.8.0.txt
|
|
@@ -370,6 +390,7 @@ files:
|
|
|
370
390
|
- lib/roda/plugins/route_csrf.rb
|
|
371
391
|
- lib/roda/plugins/run_append_slash.rb
|
|
372
392
|
- lib/roda/plugins/run_handler.rb
|
|
393
|
+
- lib/roda/plugins/run_require_slash.rb
|
|
373
394
|
- lib/roda/plugins/sessions.rb
|
|
374
395
|
- lib/roda/plugins/shared_vars.rb
|
|
375
396
|
- lib/roda/plugins/sinatra_helpers.rb
|
|
@@ -392,13 +413,13 @@ files:
|
|
|
392
413
|
- lib/roda/response.rb
|
|
393
414
|
- lib/roda/session_middleware.rb
|
|
394
415
|
- lib/roda/version.rb
|
|
395
|
-
homepage:
|
|
416
|
+
homepage: https://roda.jeremyevans.net
|
|
396
417
|
licenses:
|
|
397
418
|
- MIT
|
|
398
419
|
metadata:
|
|
399
420
|
bug_tracker_uri: https://github.com/jeremyevans/roda/issues
|
|
400
|
-
changelog_uri:
|
|
401
|
-
documentation_uri:
|
|
421
|
+
changelog_uri: https://roda.jeremyevans.net/rdoc/files/CHANGELOG.html
|
|
422
|
+
documentation_uri: https://roda.jeremyevans.net/documentation.html
|
|
402
423
|
mailing_list_uri: https://github.com/jeremyevans/roda/discussions
|
|
403
424
|
source_code_uri: https://github.com/jeremyevans/roda
|
|
404
425
|
post_install_message:
|
|
@@ -416,7 +437,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
416
437
|
- !ruby/object:Gem::Version
|
|
417
438
|
version: '0'
|
|
418
439
|
requirements: []
|
|
419
|
-
rubygems_version: 3.3.
|
|
440
|
+
rubygems_version: 3.3.7
|
|
420
441
|
signing_key:
|
|
421
442
|
specification_version: 4
|
|
422
443
|
summary: Routing tree web toolkit
|