roda 3.55.0 → 3.56.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG +16 -0
- data/doc/release_notes/3.56.0.txt +33 -0
- data/lib/roda/plugins/chunked.rb +1 -1
- data/lib/roda/plugins/common_logger.rb +1 -1
- data/lib/roda/plugins/cookies.rb +2 -0
- data/lib/roda/plugins/json_parser.rb +6 -2
- data/lib/roda/plugins/multi_public.rb +8 -0
- data/lib/roda/plugins/not_allowed.rb +13 -0
- data/lib/roda/plugins/public.rb +8 -0
- data/lib/roda/plugins/route_csrf.rb +1 -0
- data/lib/roda/plugins/run_append_slash.rb +1 -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 +10 -0
- 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 +5 -0
- data/lib/roda/version.rb +1 -1
- metadata +22 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 25ca2a1c88af9f7305cdcdb21e3b5983f879386f07c296d7ea0e8f863fddfeac
|
4
|
+
data.tar.gz: 24d9dbfeaa438c859b2b3fcecd2170d55e9d4335b1b57ca06c140b900d4a8283
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8c1b58f0a4ac491533eebb08e83c5801d095d94dbf4270d5834fb3511c82fd305244c0c02e23ba51dba1479e81b361faf556ad85698ea77ebd8a130f471b9527
|
7
|
+
data.tar.gz: b18e88e98b3c42a83f0e66891e48b03b9b641ecaf5963e5e5fd71d086c92176cefd5642464c790ed5edde30459c4a1d78b62864b1f14cd38f3bea1c9b6cefa30
|
data/CHANGELOG
CHANGED
@@ -1,3 +1,19 @@
|
|
1
|
+
= 3.56.0 (2022-05-13)
|
2
|
+
|
3
|
+
* Make status_303 plugin use 303 responses for HTTP/2 and higher versions (jeremyevans)
|
4
|
+
|
5
|
+
* Add RodaRequest#http_version for determining the HTTP version in use (jeremyevans)
|
6
|
+
|
7
|
+
* Do not set a body for 405 responses when using the verb methods in the not_allowed plugin (jeremyevans) (#267)
|
8
|
+
|
9
|
+
* Support status_handler method :keep_headers option in status_handler plugin (jeremyevans) (#267)
|
10
|
+
|
11
|
+
* Make not_allowed plugin have r.root return 405 responses for non-GET requests (jeremyevans) (#266)
|
12
|
+
|
13
|
+
* In Rack 3, only require the parts of rack used by Roda, instead of requiring rack itself and relying on autoload (jeremyevans)
|
14
|
+
|
15
|
+
* Add run_require_slash plugin, for skipping application dispatch for remaining paths that would violate Rack SPEC (jeremyevans)
|
16
|
+
|
1
17
|
= 3.55.0 (2022-04-12)
|
2
18
|
|
3
19
|
* Allow passing blocks to the view method in the render plugin (jeremyevans) (#262)
|
@@ -0,0 +1,33 @@
|
|
1
|
+
= New Features
|
2
|
+
|
3
|
+
* RodaRequest#http_version has been added for determining the HTTP
|
4
|
+
version the request was submitted with. This will be a string
|
5
|
+
such as "HTTP/1.0", "HTTP/1.1", "HTTP/2", etc. This will use the
|
6
|
+
SERVER_PROTOCOL and HTTP_VERSION entries from the environment to
|
7
|
+
determine which HTTP version is in use.
|
8
|
+
|
9
|
+
* The status_handler method in the status_handler plugin now supports
|
10
|
+
a :keep_headers option. The value for this option should be an
|
11
|
+
array of header names to keep. All other headers are removed. The
|
12
|
+
default behavior without the option is still to remove all headers.
|
13
|
+
|
14
|
+
* A run_require_slash plugin has been added, which will skip
|
15
|
+
dispatching to another rack application if the remaining path is not
|
16
|
+
empty and does not start with a slash.
|
17
|
+
|
18
|
+
= Other Improvements
|
19
|
+
|
20
|
+
* The status_303 plugin will use 303 as the default redirect status
|
21
|
+
for non-GET requests for HTTP/2 and higher HTTP versions. Previously,
|
22
|
+
it only used 303 for HTTP/1.1.
|
23
|
+
|
24
|
+
* The not_allowed plugin now overrides the r.root method to return
|
25
|
+
405 responses to non-GET requests to the root.
|
26
|
+
|
27
|
+
* The not_allowed plugin no longer sets the body when returning 405
|
28
|
+
responses using methods such as r.get and r.post. Previously, the
|
29
|
+
body was unintentionally set to the same value as the Allow header.
|
30
|
+
|
31
|
+
* When using the Rack master branch (what will become Rack 3), Roda
|
32
|
+
only requires the parts of rack that it uses, instead of requiring
|
33
|
+
rack and relying on autoload to load the parts of rack in use.
|
data/lib/roda/plugins/chunked.rb
CHANGED
@@ -226,7 +226,7 @@ class Roda
|
|
226
226
|
# an overview. If a block is given, it is passed to #delay.
|
227
227
|
def chunked(template, opts=OPTS, &block)
|
228
228
|
unless defined?(@_chunked)
|
229
|
-
@_chunked = !self.opts[:force_chunked_encoding] ||
|
229
|
+
@_chunked = !self.opts[:force_chunked_encoding] || @_request.http_version == "HTTP/1.1"
|
230
230
|
end
|
231
231
|
|
232
232
|
if block
|
@@ -53,7 +53,7 @@ class Roda
|
|
53
53
|
|
54
54
|
env = @_request.env
|
55
55
|
|
56
|
-
opts[:common_logger_meth].call("#{env['HTTP_X_FORWARDED_FOR'] || env["REMOTE_ADDR"] || "-"} - #{env["REMOTE_USER"] || "-"} [#{Time.now.strftime("%d/%b/%Y:%H:%M:%S %z")}] \"#{env["REQUEST_METHOD"]} #{env["SCRIPT_NAME"]}#{env["PATH_INFO"]}#{"?#{env["QUERY_STRING"]}" if ((qs = env["QUERY_STRING"]) && !qs.empty?)} #{
|
56
|
+
opts[:common_logger_meth].call("#{env['HTTP_X_FORWARDED_FOR'] || env["REMOTE_ADDR"] || "-"} - #{env["REMOTE_USER"] || "-"} [#{Time.now.strftime("%d/%b/%Y:%H:%M:%S %z")}] \"#{env["REQUEST_METHOD"]} #{env["SCRIPT_NAME"]}#{env["PATH_INFO"]}#{"?#{env["QUERY_STRING"]}" if ((qs = env["QUERY_STRING"]) && !qs.empty?)} #{@_request.http_version}\" #{result[0]} #{((length = result[1]['Content-Length']) && (length unless length == '0')) || '-'} #{elapsed_time}\n")
|
57
57
|
end
|
58
58
|
|
59
59
|
# Create timer instance used for timing
|
data/lib/roda/plugins/cookies.rb
CHANGED
@@ -4,12 +4,16 @@ require 'json'
|
|
4
4
|
|
5
5
|
class Roda
|
6
6
|
module RodaPlugins
|
7
|
-
# The json_parser plugin parses request bodies in
|
7
|
+
# The json_parser plugin parses request bodies in JSON format
|
8
8
|
# if the request's content type specifies json. This is mostly
|
9
9
|
# designed for use with JSON API sites.
|
10
10
|
#
|
11
11
|
# This only parses the request body as JSON if the Content-Type
|
12
12
|
# header for the request includes "json".
|
13
|
+
#
|
14
|
+
# The parsed JSON body will be available in +r.POST+, just as a
|
15
|
+
# parsed HTML form body would be. It will also be available in
|
16
|
+
# +r.params+ (which merges +r.GET+ with +r.POST+).
|
13
17
|
module JsonParser
|
14
18
|
DEFAULT_ERROR_HANDLER = proc{|r| r.halt [400, {}, []]}
|
15
19
|
|
@@ -25,7 +29,7 @@ class Roda
|
|
25
29
|
# object as the second argument, so the parser needs
|
26
30
|
# to respond to +call(str, request)+.
|
27
31
|
# :wrap :: Whether to wrap uploaded JSON data in a hash with a "_json"
|
28
|
-
# key. Without this, calls to r.params will fail if a non-Hash
|
32
|
+
# key. Without this, calls to +r.params+ will fail if a non-Hash
|
29
33
|
# (such as an array) is uploaded in JSON format. A value of
|
30
34
|
# :always will wrap all values, and a value of :unless_hash will
|
31
35
|
# only wrap values that are not already hashes.
|
@@ -17,6 +17,9 @@ class Roda
|
|
17
17
|
# will return a 200 response for <tt>GET /</tt> and a 405
|
18
18
|
# response for <tt>POST /</tt>.
|
19
19
|
#
|
20
|
+
# This plugin changes the +r.root+ method to return a 405 status
|
21
|
+
# for non-GET requests to +/+.
|
22
|
+
#
|
20
23
|
# This plugin also changes the +r.is+ method so that if you use
|
21
24
|
# a verb method inside +r.is+, it returns a 405 status if none
|
22
25
|
# of the verb methods match. So this code:
|
@@ -100,6 +103,15 @@ class Roda
|
|
100
103
|
end
|
101
104
|
end
|
102
105
|
|
106
|
+
# Treat +r.root+ similar to <tt>r.get ''</tt>, using a 405
|
107
|
+
# response for non-GET requests.
|
108
|
+
def root
|
109
|
+
super
|
110
|
+
if @remaining_path == "/" && !is_get?
|
111
|
+
always{method_not_allowed("GET")}
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
103
115
|
# Setup methods for all verbs. If inside an is block and not given
|
104
116
|
# arguments, record the verb used. If given an argument, add an is
|
105
117
|
# check with the arguments.
|
@@ -129,6 +141,7 @@ class Roda
|
|
129
141
|
res = response
|
130
142
|
res.status = 405
|
131
143
|
res['Allow'] = verbs
|
144
|
+
nil
|
132
145
|
end
|
133
146
|
end
|
134
147
|
end
|
data/lib/roda/plugins/public.rb
CHANGED
@@ -34,7 +34,7 @@ class Roda
|
|
34
34
|
# path internally, or a redirect is issued when configured with
|
35
35
|
# <tt>use_redirects: true</tt>.
|
36
36
|
def run(*)
|
37
|
-
if remaining_path.empty?
|
37
|
+
if @remaining_path.empty?
|
38
38
|
if scope.opts[:run_append_slash_redirect]
|
39
39
|
redirect("#{path}/")
|
40
40
|
else
|
@@ -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
|
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
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
|
@@ -213,6 +227,7 @@ extra_rdoc_files:
|
|
213
227
|
- doc/release_notes/3.53.0.txt
|
214
228
|
- doc/release_notes/3.54.0.txt
|
215
229
|
- doc/release_notes/3.55.0.txt
|
230
|
+
- doc/release_notes/3.56.0.txt
|
216
231
|
- doc/release_notes/3.6.0.txt
|
217
232
|
- doc/release_notes/3.7.0.txt
|
218
233
|
- doc/release_notes/3.8.0.txt
|
@@ -275,6 +290,7 @@ files:
|
|
275
290
|
- doc/release_notes/3.53.0.txt
|
276
291
|
- doc/release_notes/3.54.0.txt
|
277
292
|
- doc/release_notes/3.55.0.txt
|
293
|
+
- doc/release_notes/3.56.0.txt
|
278
294
|
- doc/release_notes/3.6.0.txt
|
279
295
|
- doc/release_notes/3.7.0.txt
|
280
296
|
- doc/release_notes/3.8.0.txt
|
@@ -374,6 +390,7 @@ files:
|
|
374
390
|
- lib/roda/plugins/route_csrf.rb
|
375
391
|
- lib/roda/plugins/run_append_slash.rb
|
376
392
|
- lib/roda/plugins/run_handler.rb
|
393
|
+
- lib/roda/plugins/run_require_slash.rb
|
377
394
|
- lib/roda/plugins/sessions.rb
|
378
395
|
- lib/roda/plugins/shared_vars.rb
|
379
396
|
- lib/roda/plugins/sinatra_helpers.rb
|
@@ -396,13 +413,13 @@ files:
|
|
396
413
|
- lib/roda/response.rb
|
397
414
|
- lib/roda/session_middleware.rb
|
398
415
|
- lib/roda/version.rb
|
399
|
-
homepage:
|
416
|
+
homepage: https://roda.jeremyevans.net
|
400
417
|
licenses:
|
401
418
|
- MIT
|
402
419
|
metadata:
|
403
420
|
bug_tracker_uri: https://github.com/jeremyevans/roda/issues
|
404
|
-
changelog_uri:
|
405
|
-
documentation_uri:
|
421
|
+
changelog_uri: https://roda.jeremyevans.net/rdoc/files/CHANGELOG.html
|
422
|
+
documentation_uri: https://roda.jeremyevans.net/documentation.html
|
406
423
|
mailing_list_uri: https://github.com/jeremyevans/roda/discussions
|
407
424
|
source_code_uri: https://github.com/jeremyevans/roda
|
408
425
|
post_install_message:
|