actionpack 4.2.11.1 → 6.1.3.2
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of actionpack might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +291 -489
- data/MIT-LICENSE +1 -1
- data/README.rdoc +9 -9
- data/lib/abstract_controller/asset_paths.rb +2 -0
- data/lib/abstract_controller/base.rb +81 -51
- data/lib/{action_controller → abstract_controller}/caching/fragments.rb +64 -17
- data/lib/abstract_controller/caching.rb +66 -0
- data/lib/abstract_controller/callbacks.rb +61 -33
- data/lib/abstract_controller/collector.rb +9 -13
- data/lib/abstract_controller/error.rb +6 -0
- data/lib/abstract_controller/helpers.rb +115 -99
- data/lib/abstract_controller/logger.rb +2 -0
- data/lib/abstract_controller/railties/routes_helpers.rb +21 -3
- data/lib/abstract_controller/rendering.rb +48 -47
- data/lib/abstract_controller/translation.rb +17 -8
- data/lib/abstract_controller/url_for.rb +2 -0
- data/lib/abstract_controller.rb +13 -5
- data/lib/action_controller/api/api_rendering.rb +16 -0
- data/lib/action_controller/api.rb +150 -0
- data/lib/action_controller/base.rb +29 -24
- data/lib/action_controller/caching.rb +12 -57
- data/lib/action_controller/form_builder.rb +50 -0
- data/lib/action_controller/log_subscriber.rb +17 -19
- data/lib/action_controller/metal/basic_implicit_render.rb +13 -0
- data/lib/action_controller/metal/conditional_get.rb +134 -46
- data/lib/action_controller/metal/content_security_policy.rb +51 -0
- data/lib/action_controller/metal/cookies.rb +6 -4
- data/lib/action_controller/metal/data_streaming.rb +30 -50
- data/lib/action_controller/metal/default_headers.rb +17 -0
- data/lib/action_controller/metal/etag_with_flash.rb +18 -0
- data/lib/action_controller/metal/etag_with_template_digest.rb +21 -16
- data/lib/action_controller/metal/exceptions.rb +63 -15
- data/lib/action_controller/metal/flash.rb +9 -8
- data/lib/action_controller/metal/head.rb +26 -21
- data/lib/action_controller/metal/helpers.rb +37 -18
- data/lib/action_controller/metal/http_authentication.rb +81 -73
- data/lib/action_controller/metal/implicit_render.rb +53 -9
- data/lib/action_controller/metal/instrumentation.rb +32 -35
- data/lib/action_controller/metal/live.rb +102 -120
- data/lib/action_controller/metal/logging.rb +20 -0
- data/lib/action_controller/metal/mime_responds.rb +49 -47
- data/lib/action_controller/metal/parameter_encoding.rb +82 -0
- data/lib/action_controller/metal/params_wrapper.rb +83 -66
- data/lib/action_controller/metal/permissions_policy.rb +46 -0
- data/lib/action_controller/metal/redirecting.rb +53 -32
- data/lib/action_controller/metal/renderers.rb +87 -44
- data/lib/action_controller/metal/rendering.rb +77 -50
- data/lib/action_controller/metal/request_forgery_protection.rb +267 -103
- data/lib/action_controller/metal/rescue.rb +10 -17
- data/lib/action_controller/metal/streaming.rb +12 -11
- data/lib/action_controller/metal/strong_parameters.rb +714 -186
- data/lib/action_controller/metal/testing.rb +2 -17
- data/lib/action_controller/metal/url_for.rb +19 -10
- data/lib/action_controller/metal.rb +104 -87
- data/lib/action_controller/railtie.rb +28 -10
- data/lib/action_controller/railties/helpers.rb +3 -1
- data/lib/action_controller/renderer.rb +141 -0
- data/lib/action_controller/template_assertions.rb +11 -0
- data/lib/action_controller/test_case.rb +296 -422
- data/lib/action_controller.rb +34 -23
- data/lib/action_dispatch/http/cache.rb +107 -56
- data/lib/action_dispatch/http/content_disposition.rb +45 -0
- data/lib/action_dispatch/http/content_security_policy.rb +286 -0
- data/lib/action_dispatch/http/filter_parameters.rb +32 -25
- data/lib/action_dispatch/http/filter_redirect.rb +10 -12
- data/lib/action_dispatch/http/headers.rb +55 -22
- data/lib/action_dispatch/http/mime_negotiation.rb +79 -51
- data/lib/action_dispatch/http/mime_type.rb +153 -121
- data/lib/action_dispatch/http/mime_types.rb +20 -6
- data/lib/action_dispatch/http/parameters.rb +90 -40
- data/lib/action_dispatch/http/permissions_policy.rb +173 -0
- data/lib/action_dispatch/http/rack_cache.rb +2 -0
- data/lib/action_dispatch/http/request.rb +226 -121
- data/lib/action_dispatch/http/response.rb +248 -113
- data/lib/action_dispatch/http/upload.rb +21 -7
- data/lib/action_dispatch/http/url.rb +182 -100
- data/lib/action_dispatch/journey/formatter.rb +90 -43
- data/lib/action_dispatch/journey/gtg/builder.rb +28 -41
- data/lib/action_dispatch/journey/gtg/simulator.rb +11 -16
- data/lib/action_dispatch/journey/gtg/transition_table.rb +23 -21
- data/lib/action_dispatch/journey/nfa/dot.rb +3 -14
- data/lib/action_dispatch/journey/nodes/node.rb +29 -15
- data/lib/action_dispatch/journey/parser.rb +17 -16
- data/lib/action_dispatch/journey/parser.y +4 -3
- data/lib/action_dispatch/journey/parser_extras.rb +12 -4
- data/lib/action_dispatch/journey/path/pattern.rb +58 -54
- data/lib/action_dispatch/journey/route.rb +100 -32
- data/lib/action_dispatch/journey/router/utils.rb +29 -18
- data/lib/action_dispatch/journey/router.rb +55 -51
- data/lib/action_dispatch/journey/routes.rb +17 -17
- data/lib/action_dispatch/journey/scanner.rb +26 -17
- data/lib/action_dispatch/journey/visitors.rb +98 -54
- data/lib/action_dispatch/journey.rb +5 -5
- data/lib/action_dispatch/middleware/actionable_exceptions.rb +46 -0
- data/lib/action_dispatch/middleware/callbacks.rb +3 -6
- data/lib/action_dispatch/middleware/cookies.rb +347 -217
- data/lib/action_dispatch/middleware/debug_exceptions.rb +135 -63
- data/lib/action_dispatch/middleware/debug_locks.rb +124 -0
- data/lib/action_dispatch/middleware/debug_view.rb +66 -0
- data/lib/action_dispatch/middleware/exception_wrapper.rb +115 -71
- data/lib/action_dispatch/middleware/executor.rb +21 -0
- data/lib/action_dispatch/middleware/flash.rb +78 -54
- data/lib/action_dispatch/middleware/host_authorization.rb +130 -0
- data/lib/action_dispatch/middleware/public_exceptions.rb +32 -27
- data/lib/action_dispatch/middleware/reloader.rb +5 -91
- data/lib/action_dispatch/middleware/remote_ip.rb +53 -45
- data/lib/action_dispatch/middleware/request_id.rb +17 -10
- data/lib/action_dispatch/middleware/session/abstract_store.rb +41 -26
- data/lib/action_dispatch/middleware/session/cache_store.rb +24 -14
- data/lib/action_dispatch/middleware/session/cookie_store.rb +74 -75
- data/lib/action_dispatch/middleware/session/mem_cache_store.rb +8 -2
- data/lib/action_dispatch/middleware/show_exceptions.rb +28 -23
- data/lib/action_dispatch/middleware/ssl.rb +118 -35
- data/lib/action_dispatch/middleware/stack.rb +82 -41
- data/lib/action_dispatch/middleware/static.rb +156 -89
- data/lib/action_dispatch/middleware/templates/rescues/_actions.html.erb +13 -0
- data/lib/action_dispatch/middleware/templates/rescues/_actions.text.erb +0 -0
- data/lib/action_dispatch/middleware/templates/rescues/_message_and_suggestions.html.erb +22 -0
- data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +4 -14
- data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.text.erb +1 -1
- data/lib/action_dispatch/middleware/templates/rescues/{_source.erb → _source.html.erb} +4 -2
- data/lib/action_dispatch/middleware/templates/rescues/_source.text.erb +8 -0
- data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +45 -35
- data/lib/action_dispatch/middleware/templates/rescues/blocked_host.html.erb +7 -0
- data/lib/action_dispatch/middleware/templates/rescues/blocked_host.text.erb +5 -0
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +23 -4
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +1 -1
- data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +24 -0
- data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.text.erb +15 -0
- data/lib/action_dispatch/middleware/templates/rescues/layout.erb +105 -8
- data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.html.erb +19 -0
- data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.text.erb +3 -0
- data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +2 -2
- data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +1 -1
- data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +3 -3
- data/lib/action_dispatch/middleware/templates/rescues/template_error.text.erb +1 -1
- data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +1 -1
- data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +4 -4
- data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +87 -64
- data/lib/action_dispatch/railtie.rb +27 -13
- data/lib/action_dispatch/request/session.rb +109 -61
- data/lib/action_dispatch/request/utils.rb +90 -23
- data/lib/action_dispatch/routing/endpoint.rb +9 -2
- data/lib/action_dispatch/routing/inspector.rb +141 -102
- data/lib/action_dispatch/routing/mapper.rb +811 -473
- data/lib/action_dispatch/routing/polymorphic_routes.rb +167 -143
- data/lib/action_dispatch/routing/redirection.rb +37 -27
- data/lib/action_dispatch/routing/route_set.rb +363 -331
- data/lib/action_dispatch/routing/routes_proxy.rb +32 -5
- data/lib/action_dispatch/routing/url_for.rb +66 -26
- data/lib/action_dispatch/routing.rb +36 -36
- data/lib/action_dispatch/system_test_case.rb +190 -0
- data/lib/action_dispatch/system_testing/browser.rb +86 -0
- data/lib/action_dispatch/system_testing/driver.rb +67 -0
- data/lib/action_dispatch/system_testing/server.rb +31 -0
- data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +138 -0
- data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +29 -0
- data/lib/action_dispatch/testing/assertion_response.rb +46 -0
- data/lib/action_dispatch/testing/assertions/response.rb +44 -22
- data/lib/action_dispatch/testing/assertions/routing.rb +47 -31
- data/lib/action_dispatch/testing/assertions.rb +6 -4
- data/lib/action_dispatch/testing/integration.rb +391 -220
- data/lib/action_dispatch/testing/request_encoder.rb +55 -0
- data/lib/action_dispatch/testing/test_process.rb +53 -22
- data/lib/action_dispatch/testing/test_request.rb +27 -34
- data/lib/action_dispatch/testing/test_response.rb +11 -11
- data/lib/action_dispatch.rb +35 -21
- data/lib/action_pack/gem_version.rb +6 -4
- data/lib/action_pack/version.rb +3 -1
- data/lib/action_pack.rb +4 -2
- metadata +78 -48
- data/lib/action_controller/metal/force_ssl.rb +0 -97
- data/lib/action_controller/metal/hide_actions.rb +0 -40
- data/lib/action_controller/metal/rack_delegation.rb +0 -32
- data/lib/action_controller/middleware.rb +0 -39
- data/lib/action_controller/model_naming.rb +0 -12
- data/lib/action_dispatch/http/parameter_filter.rb +0 -72
- data/lib/action_dispatch/journey/backwards.rb +0 -5
- data/lib/action_dispatch/journey/nfa/builder.rb +0 -76
- data/lib/action_dispatch/journey/nfa/simulator.rb +0 -47
- data/lib/action_dispatch/journey/nfa/transition_table.rb +0 -163
- data/lib/action_dispatch/journey/router/strexp.rb +0 -27
- data/lib/action_dispatch/middleware/params_parser.rb +0 -60
- data/lib/action_dispatch/testing/assertions/dom.rb +0 -3
- data/lib/action_dispatch/testing/assertions/selector.rb +0 -3
- data/lib/action_dispatch/testing/assertions/tag.rb +0 -3
@@ -1,4 +1,4 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module ActionDispatch
|
4
4
|
module Journey # :nodoc:
|
@@ -6,15 +6,11 @@ module ActionDispatch
|
|
6
6
|
class Pattern # :nodoc:
|
7
7
|
attr_reader :spec, :requirements, :anchored
|
8
8
|
|
9
|
-
def
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
@spec = strexp.ast
|
15
|
-
@requirements = strexp.requirements
|
16
|
-
@separators = strexp.separators.join
|
17
|
-
@anchored = strexp.anchor
|
9
|
+
def initialize(ast, requirements, separators, anchored)
|
10
|
+
@spec = ast
|
11
|
+
@requirements = requirements
|
12
|
+
@separators = separators
|
13
|
+
@anchored = anchored
|
18
14
|
|
19
15
|
@names = nil
|
20
16
|
@optional_names = nil
|
@@ -27,22 +23,24 @@ module ActionDispatch
|
|
27
23
|
Visitors::FormatBuilder.new.accept(spec)
|
28
24
|
end
|
29
25
|
|
26
|
+
def eager_load!
|
27
|
+
required_names
|
28
|
+
offsets
|
29
|
+
to_regexp
|
30
|
+
nil
|
31
|
+
end
|
32
|
+
|
30
33
|
def ast
|
31
|
-
@spec.
|
34
|
+
@spec.find_all(&:symbol?).each do |node|
|
32
35
|
re = @requirements[node.to_sym]
|
33
36
|
node.regexp = re if re
|
34
37
|
end
|
35
38
|
|
36
|
-
@spec.grep(Nodes::Star).each do |node|
|
37
|
-
node = node.left
|
38
|
-
node.regexp = @requirements[node.to_sym] || /(.+)/
|
39
|
-
end
|
40
|
-
|
41
39
|
@spec
|
42
40
|
end
|
43
41
|
|
44
42
|
def names
|
45
|
-
@names ||= spec.
|
43
|
+
@names ||= spec.find_all(&:symbol?).map(&:name)
|
46
44
|
end
|
47
45
|
|
48
46
|
def required_names
|
@@ -50,34 +48,9 @@ module ActionDispatch
|
|
50
48
|
end
|
51
49
|
|
52
50
|
def optional_names
|
53
|
-
@optional_names ||= spec.
|
54
|
-
group.
|
55
|
-
}.map
|
56
|
-
end
|
57
|
-
|
58
|
-
class RegexpOffsets < Journey::Visitors::Visitor # :nodoc:
|
59
|
-
attr_reader :offsets
|
60
|
-
|
61
|
-
def initialize(matchers)
|
62
|
-
@matchers = matchers
|
63
|
-
@capture_count = [0]
|
64
|
-
end
|
65
|
-
|
66
|
-
def visit(node)
|
67
|
-
super
|
68
|
-
@capture_count
|
69
|
-
end
|
70
|
-
|
71
|
-
def visit_SYMBOL(node)
|
72
|
-
node = node.to_sym
|
73
|
-
|
74
|
-
if @matchers.key?(node)
|
75
|
-
re = /#{@matchers[node]}|/
|
76
|
-
@capture_count.push((re.match('').length - 1) + (@capture_count.last || 0))
|
77
|
-
else
|
78
|
-
@capture_count << (@capture_count.last || 0)
|
79
|
-
end
|
80
|
-
end
|
51
|
+
@optional_names ||= spec.find_all(&:group?).flat_map { |group|
|
52
|
+
group.find_all(&:symbol?)
|
53
|
+
}.map(&:name).uniq
|
81
54
|
end
|
82
55
|
|
83
56
|
class AnchoredRegexp < Journey::Visitors::Visitor # :nodoc:
|
@@ -93,7 +66,7 @@ module ActionDispatch
|
|
93
66
|
end
|
94
67
|
|
95
68
|
def visit_CAT(node)
|
96
|
-
|
69
|
+
"#{visit(node.left)}#{visit(node.right)}"
|
97
70
|
end
|
98
71
|
|
99
72
|
def visit_SYMBOL(node)
|
@@ -102,7 +75,7 @@ module ActionDispatch
|
|
102
75
|
return @separator_re unless @matchers.key?(node)
|
103
76
|
|
104
77
|
re = @matchers[node]
|
105
|
-
"(#{re})"
|
78
|
+
"(#{Regexp.union(re)})"
|
106
79
|
end
|
107
80
|
|
108
81
|
def visit_GROUP(node)
|
@@ -119,14 +92,20 @@ module ActionDispatch
|
|
119
92
|
end
|
120
93
|
|
121
94
|
def visit_STAR(node)
|
122
|
-
re = @matchers[node.left.to_sym]
|
123
|
-
"(#{re})"
|
95
|
+
re = @matchers[node.left.to_sym]
|
96
|
+
re ? "(#{re})" : "(.+)"
|
97
|
+
end
|
98
|
+
|
99
|
+
def visit_OR(node)
|
100
|
+
children = node.children.map { |n| visit n }
|
101
|
+
"(?:#{children.join(?|)})"
|
124
102
|
end
|
125
103
|
end
|
126
104
|
|
127
105
|
class UnanchoredRegexp < AnchoredRegexp # :nodoc:
|
128
106
|
def accept(node)
|
129
|
-
|
107
|
+
path = visit node
|
108
|
+
path == "/" ? %r{\A/} : %r{\A#{path}(?:\b|\Z|/)}
|
130
109
|
end
|
131
110
|
end
|
132
111
|
|
@@ -140,7 +119,11 @@ module ActionDispatch
|
|
140
119
|
end
|
141
120
|
|
142
121
|
def captures
|
143
|
-
(length - 1)
|
122
|
+
Array.new(length - 1) { |i| self[i + 1] }
|
123
|
+
end
|
124
|
+
|
125
|
+
def named_captures
|
126
|
+
@names.zip(captures).to_h
|
144
127
|
end
|
145
128
|
|
146
129
|
def [](x)
|
@@ -167,6 +150,10 @@ module ActionDispatch
|
|
167
150
|
end
|
168
151
|
alias :=~ :match
|
169
152
|
|
153
|
+
def match?(other)
|
154
|
+
to_regexp.match?(other)
|
155
|
+
end
|
156
|
+
|
170
157
|
def source
|
171
158
|
to_regexp.source
|
172
159
|
end
|
@@ -175,8 +162,13 @@ module ActionDispatch
|
|
175
162
|
@re ||= regexp_visitor.new(@separators, @requirements).accept spec
|
176
163
|
end
|
177
164
|
|
178
|
-
|
165
|
+
def requirements_for_missing_keys_check
|
166
|
+
@requirements_for_missing_keys_check ||= requirements.transform_values do |regex|
|
167
|
+
/\A#{regex}\Z/
|
168
|
+
end
|
169
|
+
end
|
179
170
|
|
171
|
+
private
|
180
172
|
def regexp_visitor
|
181
173
|
@anchored ? AnchoredRegexp : UnanchoredRegexp
|
182
174
|
end
|
@@ -184,8 +176,20 @@ module ActionDispatch
|
|
184
176
|
def offsets
|
185
177
|
return @offsets if @offsets
|
186
178
|
|
187
|
-
|
188
|
-
|
179
|
+
@offsets = [0]
|
180
|
+
|
181
|
+
spec.find_all(&:symbol?).each do |node|
|
182
|
+
node = node.to_sym
|
183
|
+
|
184
|
+
if @requirements.key?(node)
|
185
|
+
re = /#{Regexp.union(@requirements[node])}|/
|
186
|
+
@offsets.push((re.match("").length - 1) + @offsets.last)
|
187
|
+
else
|
188
|
+
@offsets << @offsets.last
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
@offsets
|
189
193
|
end
|
190
194
|
end
|
191
195
|
end
|
@@ -1,42 +1,103 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module ActionDispatch
|
2
|
-
|
3
|
-
|
4
|
-
|
4
|
+
# :stopdoc:
|
5
|
+
module Journey
|
6
|
+
class Route
|
7
|
+
attr_reader :app, :path, :defaults, :name, :precedence, :constraints,
|
8
|
+
:internal, :scope_options
|
5
9
|
|
6
|
-
attr_reader :constraints
|
7
10
|
alias :conditions :constraints
|
8
11
|
|
9
|
-
|
12
|
+
module VerbMatchers
|
13
|
+
VERBS = %w{ DELETE GET HEAD OPTIONS LINK PATCH POST PUT TRACE UNLINK }
|
14
|
+
VERBS.each do |v|
|
15
|
+
class_eval <<-eoc, __FILE__, __LINE__ + 1
|
16
|
+
# frozen_string_literal: true
|
17
|
+
class #{v}
|
18
|
+
def self.verb; name.split("::").last; end
|
19
|
+
def self.call(req); req.#{v.downcase}?; end
|
20
|
+
end
|
21
|
+
eoc
|
22
|
+
end
|
23
|
+
|
24
|
+
class Unknown
|
25
|
+
attr_reader :verb
|
26
|
+
|
27
|
+
def initialize(verb)
|
28
|
+
@verb = verb
|
29
|
+
end
|
30
|
+
|
31
|
+
def call(request); @verb == request.request_method; end
|
32
|
+
end
|
33
|
+
|
34
|
+
class All
|
35
|
+
def self.call(_); true; end
|
36
|
+
def self.verb; ""; end
|
37
|
+
end
|
38
|
+
|
39
|
+
VERB_TO_CLASS = VERBS.each_with_object(all: All) do |verb, hash|
|
40
|
+
klass = const_get verb
|
41
|
+
hash[verb] = klass
|
42
|
+
hash[verb.downcase] = klass
|
43
|
+
hash[verb.downcase.to_sym] = klass
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.verb_matcher(verb)
|
48
|
+
VerbMatchers::VERB_TO_CLASS.fetch(verb) do
|
49
|
+
VerbMatchers::Unknown.new verb.to_s.dasherize.upcase
|
50
|
+
end
|
51
|
+
end
|
10
52
|
|
11
53
|
##
|
12
54
|
# +path+ is a path constraint.
|
13
55
|
# +constraints+ is a hash of constraints to be applied to this route.
|
14
|
-
def initialize(name
|
56
|
+
def initialize(name:, app: nil, path:, constraints: {}, required_defaults: [], defaults: {}, request_method_match: nil, precedence: 0, scope_options: {}, internal: false)
|
15
57
|
@name = name
|
16
58
|
@app = app
|
17
59
|
@path = path
|
18
60
|
|
61
|
+
@request_method_match = request_method_match
|
19
62
|
@constraints = constraints
|
20
63
|
@defaults = defaults
|
21
64
|
@required_defaults = nil
|
65
|
+
@_required_defaults = required_defaults
|
22
66
|
@required_parts = nil
|
23
67
|
@parts = nil
|
24
68
|
@decorated_ast = nil
|
25
|
-
@precedence =
|
69
|
+
@precedence = precedence
|
26
70
|
@path_formatter = @path.build_formatter
|
71
|
+
@scope_options = scope_options
|
72
|
+
@internal = internal
|
73
|
+
end
|
74
|
+
|
75
|
+
def eager_load!
|
76
|
+
path.eager_load!
|
77
|
+
ast
|
78
|
+
parts
|
79
|
+
required_defaults
|
80
|
+
nil
|
27
81
|
end
|
28
82
|
|
29
83
|
def ast
|
30
84
|
@decorated_ast ||= begin
|
31
85
|
decorated_ast = path.ast
|
32
|
-
decorated_ast.
|
86
|
+
decorated_ast.find_all(&:terminal?).each { |n| n.memo = self }
|
33
87
|
decorated_ast
|
34
88
|
end
|
35
89
|
end
|
36
90
|
|
37
|
-
|
38
|
-
|
39
|
-
|
91
|
+
# Needed for `bin/rails routes`. Picks up succinctly defined requirements
|
92
|
+
# for a route, for example route
|
93
|
+
#
|
94
|
+
# get 'photo/:id', :controller => 'photos', :action => 'show',
|
95
|
+
# :id => /[A-Z]\d{5}/
|
96
|
+
#
|
97
|
+
# will have {:controller=>"photos", :action=>"show", :id=>/[A-Z]\d{5}/}
|
98
|
+
# as requirements.
|
99
|
+
def requirements
|
100
|
+
@defaults.merge(path.requirements).delete_if { |_, v|
|
40
101
|
/.+?/ == v
|
41
102
|
}
|
42
103
|
end
|
@@ -49,18 +110,16 @@ module ActionDispatch
|
|
49
110
|
required_parts + required_defaults.keys
|
50
111
|
end
|
51
112
|
|
52
|
-
def score(
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
return -1 unless (required_keys - supplied_keys).empty?
|
113
|
+
def score(supplied_keys)
|
114
|
+
path.required_names.each do |k|
|
115
|
+
return -1 unless supplied_keys.include?(k)
|
116
|
+
end
|
57
117
|
|
58
|
-
|
59
|
-
score + (required_defaults.length * 2)
|
118
|
+
(required_defaults.length * 2) + path.names.count { |k| supplied_keys.include?(k) }
|
60
119
|
end
|
61
120
|
|
62
121
|
def parts
|
63
|
-
@parts ||= segments.map
|
122
|
+
@parts ||= segments.map(&:to_sym)
|
64
123
|
end
|
65
124
|
alias :segment_keys :parts
|
66
125
|
|
@@ -68,26 +127,22 @@ module ActionDispatch
|
|
68
127
|
@path_formatter.evaluate path_options
|
69
128
|
end
|
70
129
|
|
71
|
-
def optional_parts
|
72
|
-
path.optional_names.map { |n| n.to_sym }
|
73
|
-
end
|
74
|
-
|
75
130
|
def required_parts
|
76
|
-
@required_parts ||= path.required_names.map
|
131
|
+
@required_parts ||= path.required_names.map(&:to_sym)
|
77
132
|
end
|
78
133
|
|
79
134
|
def required_default?(key)
|
80
|
-
|
135
|
+
@_required_defaults.include?(key)
|
81
136
|
end
|
82
137
|
|
83
138
|
def required_defaults
|
84
|
-
@required_defaults ||= @defaults.dup.delete_if do |k,_|
|
139
|
+
@required_defaults ||= @defaults.dup.delete_if do |k, _|
|
85
140
|
parts.include?(k) || !required_default?(k)
|
86
141
|
end
|
87
142
|
end
|
88
143
|
|
89
144
|
def glob?
|
90
|
-
|
145
|
+
path.spec.any?(Nodes::Star)
|
91
146
|
end
|
92
147
|
|
93
148
|
def dispatcher?
|
@@ -95,9 +150,8 @@ module ActionDispatch
|
|
95
150
|
end
|
96
151
|
|
97
152
|
def matches?(request)
|
98
|
-
|
99
|
-
|
100
|
-
|
153
|
+
match_verb(request) &&
|
154
|
+
constraints.all? { |method, value|
|
101
155
|
case value
|
102
156
|
when Regexp, String
|
103
157
|
value === request.send(method).to_s
|
@@ -110,16 +164,30 @@ module ActionDispatch
|
|
110
164
|
else
|
111
165
|
value === request.send(method)
|
112
166
|
end
|
113
|
-
|
167
|
+
}
|
114
168
|
end
|
115
169
|
|
116
170
|
def ip
|
117
171
|
constraints[:ip] || //
|
118
172
|
end
|
119
173
|
|
174
|
+
def requires_matching_verb?
|
175
|
+
!@request_method_match.all? { |x| x == VerbMatchers::All }
|
176
|
+
end
|
177
|
+
|
120
178
|
def verb
|
121
|
-
|
179
|
+
verbs.join("|")
|
122
180
|
end
|
181
|
+
|
182
|
+
private
|
183
|
+
def verbs
|
184
|
+
@request_method_match.map(&:verb)
|
185
|
+
end
|
186
|
+
|
187
|
+
def match_verb(request)
|
188
|
+
@request_method_match.any? { |m| m.call request }
|
189
|
+
end
|
123
190
|
end
|
124
191
|
end
|
192
|
+
# :startdoc:
|
125
193
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module ActionDispatch
|
2
4
|
module Journey # :nodoc:
|
3
5
|
class Router # :nodoc:
|
@@ -5,7 +7,7 @@ module ActionDispatch
|
|
5
7
|
# Normalizes URI path.
|
6
8
|
#
|
7
9
|
# Strips off trailing slash and ensures there is a leading slash.
|
8
|
-
# Also converts downcase
|
10
|
+
# Also converts downcase URL encoded string to uppercase.
|
9
11
|
#
|
10
12
|
# normalize_path("/foo") # => "/foo"
|
11
13
|
# normalize_path("/foo/") # => "/foo"
|
@@ -13,27 +15,32 @@ module ActionDispatch
|
|
13
15
|
# normalize_path("") # => "/"
|
14
16
|
# normalize_path("/%ab") # => "/%AB"
|
15
17
|
def self.normalize_path(path)
|
16
|
-
path
|
17
|
-
path.
|
18
|
-
path
|
19
|
-
path.
|
20
|
-
|
21
|
-
path
|
18
|
+
path ||= ""
|
19
|
+
encoding = path.encoding
|
20
|
+
path = +"/#{path}"
|
21
|
+
path.squeeze!("/")
|
22
|
+
|
23
|
+
unless path == "/"
|
24
|
+
path.delete_suffix!("/")
|
25
|
+
path.gsub!(/(%[a-f0-9]{2})/) { $1.upcase }
|
26
|
+
end
|
27
|
+
|
28
|
+
path.force_encoding(encoding)
|
22
29
|
end
|
23
30
|
|
24
31
|
# URI path and fragment escaping
|
25
|
-
#
|
32
|
+
# https://tools.ietf.org/html/rfc3986
|
26
33
|
class UriEncoder # :nodoc:
|
27
|
-
ENCODE = "%%%02X"
|
34
|
+
ENCODE = "%%%02X"
|
28
35
|
US_ASCII = Encoding::US_ASCII
|
29
36
|
UTF_8 = Encoding::UTF_8
|
30
|
-
EMPTY = "".force_encoding(US_ASCII).freeze
|
31
|
-
DEC2HEX = (0..255).to_a.map{ |i| ENCODE % i }.map{ |s| s.force_encoding(US_ASCII) }
|
37
|
+
EMPTY = (+"").force_encoding(US_ASCII).freeze
|
38
|
+
DEC2HEX = (0..255).to_a.map { |i| ENCODE % i }.map { |s| s.force_encoding(US_ASCII) }
|
32
39
|
|
33
|
-
ALPHA = "a-zA-Z"
|
34
|
-
DIGIT = "0-9"
|
35
|
-
UNRESERVED = "#{ALPHA}#{DIGIT}\\-\\._~"
|
36
|
-
SUB_DELIMS = "!\\$&'\\(\\)\\*\\+,;="
|
40
|
+
ALPHA = "a-zA-Z"
|
41
|
+
DIGIT = "0-9"
|
42
|
+
UNRESERVED = "#{ALPHA}#{DIGIT}\\-\\._~"
|
43
|
+
SUB_DELIMS = "!\\$&'\\(\\)\\*\\+,;="
|
37
44
|
|
38
45
|
ESCAPED = /%[a-zA-Z0-9]{2}/.freeze
|
39
46
|
|
@@ -55,12 +62,12 @@ module ActionDispatch
|
|
55
62
|
|
56
63
|
def unescape_uri(uri)
|
57
64
|
encoding = uri.encoding == US_ASCII ? UTF_8 : uri.encoding
|
58
|
-
uri.gsub(ESCAPED) { [
|
65
|
+
uri.gsub(ESCAPED) { |match| [match[1, 2].hex].pack("C") }.force_encoding(encoding)
|
59
66
|
end
|
60
67
|
|
61
|
-
|
68
|
+
private
|
62
69
|
def escape(component, pattern)
|
63
|
-
component.gsub(pattern){ |unsafe| percent_encode(unsafe) }.force_encoding(US_ASCII)
|
70
|
+
component.gsub(pattern) { |unsafe| percent_encode(unsafe) }.force_encoding(US_ASCII)
|
64
71
|
end
|
65
72
|
|
66
73
|
def percent_encode(unsafe)
|
@@ -84,6 +91,10 @@ module ActionDispatch
|
|
84
91
|
ENCODER.escape_fragment(fragment.to_s)
|
85
92
|
end
|
86
93
|
|
94
|
+
# Replaces any escaped sequences with their unescaped representations.
|
95
|
+
#
|
96
|
+
# uri = "/topics?title=Ruby%20on%20Rails"
|
97
|
+
# unescape_uri(uri) #=> "/topics?title=Ruby on Rails"
|
87
98
|
def self.unescape_uri(uri)
|
88
99
|
ENCODER.unescape_uri(uri)
|
89
100
|
end
|
@@ -1,31 +1,33 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
require
|
4
|
-
require
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "action_dispatch/journey/router/utils"
|
4
|
+
require "action_dispatch/journey/routes"
|
5
|
+
require "action_dispatch/journey/formatter"
|
5
6
|
|
6
7
|
before = $-w
|
7
8
|
$-w = false
|
8
|
-
require
|
9
|
+
require "action_dispatch/journey/parser"
|
9
10
|
$-w = before
|
10
11
|
|
11
|
-
require
|
12
|
-
require
|
12
|
+
require "action_dispatch/journey/route"
|
13
|
+
require "action_dispatch/journey/path/pattern"
|
13
14
|
|
14
15
|
module ActionDispatch
|
15
16
|
module Journey # :nodoc:
|
16
17
|
class Router # :nodoc:
|
17
|
-
class RoutingError < ::StandardError # :nodoc:
|
18
|
-
end
|
19
|
-
|
20
|
-
# :nodoc:
|
21
|
-
VERSION = '2.0.0'
|
22
|
-
|
23
18
|
attr_accessor :routes
|
24
19
|
|
25
20
|
def initialize(routes)
|
26
21
|
@routes = routes
|
27
22
|
end
|
28
23
|
|
24
|
+
def eager_load!
|
25
|
+
# Eagerly trigger the simulator's initialization so
|
26
|
+
# it doesn't happen during a request cycle.
|
27
|
+
simulator
|
28
|
+
nil
|
29
|
+
end
|
30
|
+
|
29
31
|
def serve(req)
|
30
32
|
find_routes(req).each do |match, parameters, route|
|
31
33
|
set_params = req.path_parameters
|
@@ -33,16 +35,21 @@ module ActionDispatch
|
|
33
35
|
script_name = req.script_name
|
34
36
|
|
35
37
|
unless route.path.anchored
|
36
|
-
req.script_name = (script_name.to_s + match.to_s).chomp(
|
38
|
+
req.script_name = (script_name.to_s + match.to_s).chomp("/")
|
37
39
|
req.path_info = match.post_match
|
38
40
|
req.path_info = "/" + req.path_info unless req.path_info.start_with? "/"
|
39
41
|
end
|
40
42
|
|
41
|
-
|
43
|
+
tmp_params = set_params.merge route.defaults
|
44
|
+
parameters.each_pair { |key, val|
|
45
|
+
tmp_params[key] = val.force_encoding(::Encoding::UTF_8)
|
46
|
+
}
|
47
|
+
|
48
|
+
req.path_parameters = tmp_params
|
42
49
|
|
43
50
|
status, headers, body = route.app.serve(req)
|
44
51
|
|
45
|
-
if
|
52
|
+
if "pass" == headers["X-Cascade"]
|
46
53
|
req.script_name = script_name
|
47
54
|
req.path_info = path_info
|
48
55
|
req.path_parameters = set_params
|
@@ -52,31 +59,34 @@ module ActionDispatch
|
|
52
59
|
return [status, headers, body]
|
53
60
|
end
|
54
61
|
|
55
|
-
|
62
|
+
[404, { "X-Cascade" => "pass" }, ["Not Found"]]
|
56
63
|
end
|
57
64
|
|
58
65
|
def recognize(rails_req)
|
59
66
|
find_routes(rails_req).each do |match, parameters, route|
|
60
67
|
unless route.path.anchored
|
61
68
|
rails_req.script_name = match.to_s
|
62
|
-
rails_req.path_info = match.post_match
|
69
|
+
rails_req.path_info = match.post_match
|
70
|
+
rails_req.path_info = "/" + rails_req.path_info unless rails_req.path_info.start_with? "/"
|
63
71
|
end
|
64
72
|
|
73
|
+
parameters = route.defaults.merge parameters
|
65
74
|
yield(route, parameters)
|
66
75
|
end
|
67
76
|
end
|
68
77
|
|
69
78
|
def visualizer
|
70
79
|
tt = GTG::Builder.new(ast).transition_table
|
71
|
-
groups = partitioned_routes.first.map(&:ast).group_by
|
72
|
-
asts = groups.values.map
|
80
|
+
groups = partitioned_routes.first.map(&:ast).group_by(&:to_s)
|
81
|
+
asts = groups.values.map(&:first)
|
73
82
|
tt.visualizer(asts)
|
74
83
|
end
|
75
84
|
|
76
85
|
private
|
77
|
-
|
78
86
|
def partitioned_routes
|
79
|
-
routes.
|
87
|
+
routes.partition { |r|
|
88
|
+
r.path.anchored && r.ast.grep(Nodes::Symbol).all? { |n| n.default_regexp? }
|
89
|
+
}
|
80
90
|
end
|
81
91
|
|
82
92
|
def ast
|
@@ -88,7 +98,7 @@ module ActionDispatch
|
|
88
98
|
end
|
89
99
|
|
90
100
|
def custom_routes
|
91
|
-
|
101
|
+
routes.custom_routes
|
92
102
|
end
|
93
103
|
|
94
104
|
def filter_routes(path)
|
@@ -96,24 +106,25 @@ module ActionDispatch
|
|
96
106
|
simulator.memos(path) { [] }
|
97
107
|
end
|
98
108
|
|
99
|
-
def find_routes
|
100
|
-
|
101
|
-
|
109
|
+
def find_routes(req)
|
110
|
+
path_info = req.path_info
|
111
|
+
routes = filter_routes(path_info).concat custom_routes.find_all { |r|
|
112
|
+
r.path.match?(path_info)
|
102
113
|
}
|
103
114
|
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
end
|
115
|
+
if req.head?
|
116
|
+
routes = match_head_routes(routes, req)
|
117
|
+
else
|
118
|
+
routes.select! { |r| r.matches?(req) }
|
119
|
+
end
|
110
120
|
|
111
121
|
routes.sort_by!(&:precedence)
|
112
122
|
|
113
123
|
routes.map! { |r|
|
114
|
-
match_data
|
115
|
-
path_parameters =
|
116
|
-
match_data.names.
|
124
|
+
match_data = r.path.match(path_info)
|
125
|
+
path_parameters = {}
|
126
|
+
match_data.names.each_with_index { |name, i|
|
127
|
+
val = match_data[i + 1]
|
117
128
|
path_parameters[name.to_sym] = Utils.unescape_uri(val) if val
|
118
129
|
}
|
119
130
|
[match_data, path_parameters, r]
|
@@ -121,24 +132,17 @@ module ActionDispatch
|
|
121
132
|
end
|
122
133
|
|
123
134
|
def match_head_routes(routes, req)
|
124
|
-
|
125
|
-
head_routes
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
end
|
134
|
-
else
|
135
|
-
head_routes
|
135
|
+
head_routes = routes.select { |r| r.requires_matching_verb? && r.matches?(req) }
|
136
|
+
return head_routes unless head_routes.empty?
|
137
|
+
|
138
|
+
begin
|
139
|
+
req.request_method = "GET"
|
140
|
+
routes.select! { |r| r.matches?(req) }
|
141
|
+
routes
|
142
|
+
ensure
|
143
|
+
req.request_method = "HEAD"
|
136
144
|
end
|
137
145
|
end
|
138
|
-
|
139
|
-
def match_routes(routes, req)
|
140
|
-
routes.select { |r| r.matches?(req) }
|
141
|
-
end
|
142
146
|
end
|
143
147
|
end
|
144
148
|
end
|