actionpack 5.2.1 → 7.0.2.4
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.
Potentially problematic release.
This version of actionpack might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +264 -220
- data/MIT-LICENSE +1 -1
- data/README.rdoc +6 -6
- data/lib/abstract_controller/asset_paths.rb +1 -1
- data/lib/abstract_controller/base.rb +24 -4
- data/lib/abstract_controller/caching/fragments.rb +8 -24
- data/lib/abstract_controller/caching.rb +2 -2
- data/lib/abstract_controller/callbacks.rb +34 -8
- data/lib/abstract_controller/collector.rb +5 -4
- data/lib/abstract_controller/error.rb +1 -1
- data/lib/abstract_controller/helpers.rb +107 -90
- data/lib/abstract_controller/logger.rb +1 -1
- data/lib/abstract_controller/railties/routes_helpers.rb +19 -1
- data/lib/abstract_controller/rendering.rb +9 -9
- data/lib/abstract_controller/translation.rb +12 -5
- data/lib/abstract_controller/url_for.rb +4 -6
- data/lib/abstract_controller.rb +2 -0
- data/lib/action_controller/api.rb +5 -4
- data/lib/action_controller/base.rb +6 -9
- data/lib/action_controller/caching.rb +1 -3
- data/lib/action_controller/log_subscriber.rb +13 -9
- data/lib/action_controller/metal/basic_implicit_render.rb +1 -1
- data/lib/action_controller/metal/conditional_get.rb +57 -6
- data/lib/action_controller/metal/content_security_policy.rb +2 -3
- data/lib/action_controller/metal/cookies.rb +4 -2
- data/lib/action_controller/metal/data_streaming.rb +9 -18
- data/lib/action_controller/metal/default_headers.rb +17 -0
- data/lib/action_controller/metal/etag_with_template_digest.rb +4 -6
- data/lib/action_controller/metal/exceptions.rb +55 -12
- data/lib/action_controller/metal/flash.rb +10 -6
- data/lib/action_controller/metal/head.rb +7 -4
- data/lib/action_controller/metal/helpers.rb +15 -6
- data/lib/action_controller/metal/http_authentication.rb +41 -39
- data/lib/action_controller/metal/implicit_render.rb +5 -15
- data/lib/action_controller/metal/instrumentation.rb +59 -55
- data/lib/action_controller/metal/live.rb +80 -33
- data/lib/action_controller/metal/logging.rb +20 -0
- data/lib/action_controller/metal/mime_responds.rb +22 -7
- data/lib/action_controller/metal/parameter_encoding.rb +35 -4
- data/lib/action_controller/metal/params_wrapper.rb +50 -31
- data/lib/action_controller/metal/permissions_policy.rb +46 -0
- data/lib/action_controller/metal/redirecting.rb +93 -23
- data/lib/action_controller/metal/renderers.rb +4 -4
- data/lib/action_controller/metal/rendering.rb +14 -9
- data/lib/action_controller/metal/request_forgery_protection.rb +160 -58
- data/lib/action_controller/metal/rescue.rb +2 -2
- data/lib/action_controller/metal/streaming.rb +1 -4
- data/lib/action_controller/metal/strong_parameters.rb +236 -88
- data/lib/action_controller/metal/testing.rb +9 -2
- data/lib/action_controller/metal/url_for.rb +1 -1
- data/lib/action_controller/metal.rb +16 -17
- data/lib/action_controller/railtie.rb +49 -6
- data/lib/action_controller/railties/helpers.rb +1 -1
- data/lib/action_controller/renderer.rb +37 -13
- data/lib/action_controller/template_assertions.rb +1 -1
- data/lib/action_controller/test_case.rb +98 -68
- data/lib/action_controller.rb +4 -5
- data/lib/action_dispatch/http/cache.rb +45 -32
- data/lib/action_dispatch/http/content_disposition.rb +45 -0
- data/lib/action_dispatch/http/content_security_policy.rb +69 -56
- data/lib/action_dispatch/http/filter_parameters.rb +14 -8
- data/lib/action_dispatch/http/filter_redirect.rb +2 -3
- data/lib/action_dispatch/http/headers.rb +4 -4
- data/lib/action_dispatch/http/mime_negotiation.rb +44 -16
- data/lib/action_dispatch/http/mime_type.rb +47 -30
- data/lib/action_dispatch/http/parameters.rb +18 -27
- data/lib/action_dispatch/http/permissions_policy.rb +173 -0
- data/lib/action_dispatch/http/request.rb +49 -35
- data/lib/action_dispatch/http/response.rb +34 -26
- data/lib/action_dispatch/http/upload.rb +9 -1
- data/lib/action_dispatch/http/url.rb +86 -94
- data/lib/action_dispatch/journey/formatter.rb +55 -31
- data/lib/action_dispatch/journey/gtg/builder.rb +30 -46
- data/lib/action_dispatch/journey/gtg/simulator.rb +15 -8
- data/lib/action_dispatch/journey/gtg/transition_table.rb +78 -21
- data/lib/action_dispatch/journey/nfa/dot.rb +0 -11
- data/lib/action_dispatch/journey/nodes/node.rb +83 -16
- data/lib/action_dispatch/journey/parser.rb +13 -13
- data/lib/action_dispatch/journey/parser.y +1 -1
- data/lib/action_dispatch/journey/path/pattern.rb +42 -34
- data/lib/action_dispatch/journey/route.rb +14 -31
- data/lib/action_dispatch/journey/router/utils.rb +16 -14
- data/lib/action_dispatch/journey/router.rb +27 -35
- data/lib/action_dispatch/journey/routes.rb +3 -5
- data/lib/action_dispatch/journey/scanner.rb +10 -4
- data/lib/action_dispatch/journey/visitors.rb +1 -4
- data/lib/action_dispatch/journey/visualizer/fsm.js +49 -24
- data/lib/action_dispatch/journey/visualizer/index.html.erb +1 -1
- data/lib/action_dispatch/journey.rb +0 -2
- data/lib/action_dispatch/middleware/actionable_exceptions.rb +45 -0
- data/lib/action_dispatch/middleware/callbacks.rb +2 -4
- data/lib/action_dispatch/middleware/cookies.rb +136 -113
- data/lib/action_dispatch/middleware/debug_exceptions.rb +47 -68
- data/lib/action_dispatch/middleware/debug_locks.rb +8 -8
- data/lib/action_dispatch/middleware/debug_view.rb +66 -0
- data/lib/action_dispatch/middleware/exception_wrapper.rb +79 -30
- data/lib/action_dispatch/middleware/executor.rb +4 -1
- data/lib/action_dispatch/middleware/flash.rb +10 -12
- data/lib/action_dispatch/middleware/host_authorization.rb +159 -0
- data/lib/action_dispatch/middleware/public_exceptions.rb +6 -3
- data/lib/action_dispatch/middleware/remote_ip.rb +30 -20
- data/lib/action_dispatch/middleware/request_id.rb +5 -6
- data/lib/action_dispatch/middleware/server_timing.rb +33 -0
- data/lib/action_dispatch/middleware/session/abstract_store.rb +16 -3
- data/lib/action_dispatch/middleware/session/cache_store.rb +11 -6
- data/lib/action_dispatch/middleware/session/cookie_store.rb +24 -19
- data/lib/action_dispatch/middleware/show_exceptions.rb +20 -11
- data/lib/action_dispatch/middleware/ssl.rb +20 -15
- data/lib/action_dispatch/middleware/stack.rb +79 -7
- data/lib/action_dispatch/middleware/static.rb +150 -94
- 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 +6 -11
- data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.text.erb +1 -1
- data/lib/action_dispatch/middleware/templates/rescues/_source.html.erb +4 -2
- data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +46 -36
- data/lib/action_dispatch/middleware/templates/rescues/blocked_host.html.erb +8 -0
- data/lib/action_dispatch/middleware/templates/rescues/blocked_host.text.erb +7 -0
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +25 -6
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +1 -1
- data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +9 -6
- data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.text.erb +4 -1
- data/lib/action_dispatch/middleware/templates/rescues/layout.erb +121 -15
- 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 +5 -5
- data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +4 -4
- data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +5 -5
- data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +4 -4
- data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +16 -2
- data/lib/action_dispatch/railtie.rb +16 -4
- data/lib/action_dispatch/request/session.rb +59 -22
- data/lib/action_dispatch/request/utils.rb +28 -2
- data/lib/action_dispatch/routing/inspector.rb +102 -54
- data/lib/action_dispatch/routing/mapper.rb +184 -156
- data/lib/action_dispatch/routing/polymorphic_routes.rb +21 -19
- data/lib/action_dispatch/routing/redirection.rb +4 -6
- data/lib/action_dispatch/routing/route_set.rb +83 -73
- data/lib/action_dispatch/routing/routes_proxy.rb +1 -1
- data/lib/action_dispatch/routing/url_for.rb +2 -3
- data/lib/action_dispatch/routing.rb +23 -22
- data/lib/action_dispatch/system_test_case.rb +65 -16
- data/lib/action_dispatch/system_testing/browser.rb +43 -16
- data/lib/action_dispatch/system_testing/driver.rb +42 -10
- data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +58 -12
- data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +3 -10
- data/lib/action_dispatch/testing/assertion_response.rb +0 -1
- data/lib/action_dispatch/testing/assertions/response.rb +4 -7
- data/lib/action_dispatch/testing/assertions/routing.rb +20 -8
- data/lib/action_dispatch/testing/assertions.rb +3 -6
- data/lib/action_dispatch/testing/integration.rb +61 -30
- data/lib/action_dispatch/testing/request_encoder.rb +2 -2
- data/lib/action_dispatch/testing/test_process.rb +8 -6
- data/lib/action_dispatch/testing/test_request.rb +3 -3
- data/lib/action_dispatch/testing/test_response.rb +4 -32
- data/lib/action_dispatch.rb +15 -7
- data/lib/action_pack/gem_version.rb +4 -4
- data/lib/action_pack.rb +1 -1
- metadata +44 -25
- data/lib/action_controller/metal/force_ssl.rb +0 -99
- data/lib/action_dispatch/http/parameter_filter.rb +0 -86
- data/lib/action_dispatch/journey/nfa/builder.rb +0 -78
- data/lib/action_dispatch/journey/nfa/simulator.rb +0 -49
- data/lib/action_dispatch/journey/nfa/transition_table.rb +0 -120
- data/lib/action_dispatch/system_testing/test_helpers/undef_methods.rb +0 -26
@@ -4,25 +4,16 @@ module ActionDispatch
|
|
4
4
|
module Journey # :nodoc:
|
5
5
|
module Path # :nodoc:
|
6
6
|
class Pattern # :nodoc:
|
7
|
-
attr_reader :
|
8
|
-
|
9
|
-
def self.from_string(string)
|
10
|
-
build(string, {}, "/.?", true)
|
11
|
-
end
|
12
|
-
|
13
|
-
def self.build(path, requirements, separators, anchored)
|
14
|
-
parser = Journey::Parser.new
|
15
|
-
ast = parser.parse path
|
16
|
-
new ast, requirements, separators, anchored
|
17
|
-
end
|
7
|
+
attr_reader :ast, :names, :requirements, :anchored, :spec
|
18
8
|
|
19
9
|
def initialize(ast, requirements, separators, anchored)
|
20
|
-
@
|
10
|
+
@ast = ast
|
11
|
+
@spec = ast.root
|
21
12
|
@requirements = requirements
|
22
13
|
@separators = separators
|
23
14
|
@anchored = anchored
|
24
15
|
|
25
|
-
@names =
|
16
|
+
@names = ast.names
|
26
17
|
@optional_names = nil
|
27
18
|
@required_names = nil
|
28
19
|
@re = nil
|
@@ -37,25 +28,28 @@ module ActionDispatch
|
|
37
28
|
required_names
|
38
29
|
offsets
|
39
30
|
to_regexp
|
40
|
-
nil
|
31
|
+
@ast = nil
|
41
32
|
end
|
42
33
|
|
43
|
-
def
|
44
|
-
|
45
|
-
|
46
|
-
node.regexp = re if re
|
47
|
-
end
|
34
|
+
def requirements_anchored?
|
35
|
+
# each required param must not be surrounded by a literal, otherwise it isn't simple to chunk-match the url piecemeal
|
36
|
+
terminals = ast.terminals
|
48
37
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
end
|
38
|
+
terminals.each_with_index { |s, index|
|
39
|
+
next if index < 1
|
40
|
+
next if s.type == :DOT || s.type == :SLASH
|
53
41
|
|
54
|
-
|
55
|
-
|
42
|
+
back = terminals[index - 1]
|
43
|
+
fwd = terminals[index + 1]
|
44
|
+
|
45
|
+
# we also don't support this yet, constraints must be regexps
|
46
|
+
return false if s.symbol? && s.regexp.is_a?(Array)
|
47
|
+
|
48
|
+
return false if back.literal?
|
49
|
+
return false if !fwd.nil? && fwd.literal?
|
50
|
+
}
|
56
51
|
|
57
|
-
|
58
|
-
@names ||= spec.find_all(&:symbol?).map(&:name)
|
52
|
+
true
|
59
53
|
end
|
60
54
|
|
61
55
|
def required_names
|
@@ -81,7 +75,7 @@ module ActionDispatch
|
|
81
75
|
end
|
82
76
|
|
83
77
|
def visit_CAT(node)
|
84
|
-
|
78
|
+
"#{visit(node.left)}#{visit(node.right)}"
|
85
79
|
end
|
86
80
|
|
87
81
|
def visit_SYMBOL(node)
|
@@ -90,7 +84,7 @@ module ActionDispatch
|
|
90
84
|
return @separator_re unless @matchers.key?(node)
|
91
85
|
|
92
86
|
re = @matchers[node]
|
93
|
-
"(#{re})"
|
87
|
+
"(#{Regexp.union(re)})"
|
94
88
|
end
|
95
89
|
|
96
90
|
def visit_GROUP(node)
|
@@ -107,8 +101,8 @@ module ActionDispatch
|
|
107
101
|
end
|
108
102
|
|
109
103
|
def visit_STAR(node)
|
110
|
-
re = @matchers[node.left.to_sym]
|
111
|
-
"(#{re})"
|
104
|
+
re = @matchers[node.left.to_sym]
|
105
|
+
re ? "(#{re})" : "(.+)"
|
112
106
|
end
|
113
107
|
|
114
108
|
def visit_OR(node)
|
@@ -119,7 +113,8 @@ module ActionDispatch
|
|
119
113
|
|
120
114
|
class UnanchoredRegexp < AnchoredRegexp # :nodoc:
|
121
115
|
def accept(node)
|
122
|
-
|
116
|
+
path = visit node
|
117
|
+
path == "/" ? %r{\A/} : %r{\A#{path}(?:\b|\Z|/)}
|
123
118
|
end
|
124
119
|
end
|
125
120
|
|
@@ -136,6 +131,10 @@ module ActionDispatch
|
|
136
131
|
Array.new(length - 1) { |i| self[i + 1] }
|
137
132
|
end
|
138
133
|
|
134
|
+
def named_captures
|
135
|
+
@names.zip(captures).to_h
|
136
|
+
end
|
137
|
+
|
139
138
|
def [](x)
|
140
139
|
idx = @offsets[x - 1] + x
|
141
140
|
@match[idx]
|
@@ -160,6 +159,10 @@ module ActionDispatch
|
|
160
159
|
end
|
161
160
|
alias :=~ :match
|
162
161
|
|
162
|
+
def match?(other)
|
163
|
+
to_regexp.match?(other)
|
164
|
+
end
|
165
|
+
|
163
166
|
def source
|
164
167
|
to_regexp.source
|
165
168
|
end
|
@@ -168,8 +171,13 @@ module ActionDispatch
|
|
168
171
|
@re ||= regexp_visitor.new(@separators, @requirements).accept spec
|
169
172
|
end
|
170
173
|
|
171
|
-
|
174
|
+
def requirements_for_missing_keys_check
|
175
|
+
@requirements_for_missing_keys_check ||= requirements.transform_values do |regex|
|
176
|
+
/\A#{regex}\Z/
|
177
|
+
end
|
178
|
+
end
|
172
179
|
|
180
|
+
private
|
173
181
|
def regexp_visitor
|
174
182
|
@anchored ? AnchoredRegexp : UnanchoredRegexp
|
175
183
|
end
|
@@ -183,7 +191,7 @@ module ActionDispatch
|
|
183
191
|
node = node.to_sym
|
184
192
|
|
185
193
|
if @requirements.key?(node)
|
186
|
-
re = /#{@requirements[node]}|/
|
194
|
+
re = /#{Regexp.union(@requirements[node])}|/
|
187
195
|
@offsets.push((re.match("").length - 1) + @offsets.last)
|
188
196
|
else
|
189
197
|
@offsets << @offsets.last
|
@@ -4,15 +4,16 @@ module ActionDispatch
|
|
4
4
|
# :stopdoc:
|
5
5
|
module Journey
|
6
6
|
class Route
|
7
|
-
attr_reader :app, :path, :defaults, :name, :precedence
|
7
|
+
attr_reader :app, :path, :defaults, :name, :precedence, :constraints,
|
8
|
+
:internal, :scope_options, :ast
|
8
9
|
|
9
|
-
attr_reader :constraints, :internal
|
10
10
|
alias :conditions :constraints
|
11
11
|
|
12
12
|
module VerbMatchers
|
13
13
|
VERBS = %w{ DELETE GET HEAD OPTIONS LINK PATCH POST PUT TRACE UNLINK }
|
14
14
|
VERBS.each do |v|
|
15
15
|
class_eval <<-eoc, __FILE__, __LINE__ + 1
|
16
|
+
# frozen_string_literal: true
|
16
17
|
class #{v}
|
17
18
|
def self.verb; name.split("::").last; end
|
18
19
|
def self.call(req); req.#{v.downcase}?; end
|
@@ -27,7 +28,7 @@ module ActionDispatch
|
|
27
28
|
@verb = verb
|
28
29
|
end
|
29
30
|
|
30
|
-
def call(request); @verb
|
31
|
+
def call(request); @verb == request.request_method; end
|
31
32
|
end
|
32
33
|
|
33
34
|
class All
|
@@ -49,15 +50,10 @@ module ActionDispatch
|
|
49
50
|
end
|
50
51
|
end
|
51
52
|
|
52
|
-
def self.build(name, app, path, constraints, required_defaults, defaults)
|
53
|
-
request_method_match = verb_matcher(constraints.delete(:request_method))
|
54
|
-
new name, app, path, constraints, required_defaults, defaults, request_method_match, 0
|
55
|
-
end
|
56
|
-
|
57
53
|
##
|
58
54
|
# +path+ is a path constraint.
|
59
55
|
# +constraints+ is a hash of constraints to be applied to this route.
|
60
|
-
def initialize(name
|
56
|
+
def initialize(name:, app: nil, path:, constraints: {}, required_defaults: [], defaults: {}, request_method_match: nil, precedence: 0, scope_options: {}, internal: false)
|
61
57
|
@name = name
|
62
58
|
@app = app
|
63
59
|
@path = path
|
@@ -69,29 +65,23 @@ module ActionDispatch
|
|
69
65
|
@_required_defaults = required_defaults
|
70
66
|
@required_parts = nil
|
71
67
|
@parts = nil
|
72
|
-
@decorated_ast = nil
|
73
68
|
@precedence = precedence
|
74
69
|
@path_formatter = @path.build_formatter
|
70
|
+
@scope_options = scope_options
|
75
71
|
@internal = internal
|
72
|
+
|
73
|
+
@ast = @path.ast.root
|
74
|
+
@path.ast.route = self
|
76
75
|
end
|
77
76
|
|
78
77
|
def eager_load!
|
79
78
|
path.eager_load!
|
80
|
-
ast
|
81
79
|
parts
|
82
80
|
required_defaults
|
83
81
|
nil
|
84
82
|
end
|
85
83
|
|
86
|
-
|
87
|
-
@decorated_ast ||= begin
|
88
|
-
decorated_ast = path.ast
|
89
|
-
decorated_ast.find_all(&:terminal?).each { |n| n.memo = self }
|
90
|
-
decorated_ast
|
91
|
-
end
|
92
|
-
end
|
93
|
-
|
94
|
-
# Needed for `rails routes`. Picks up succinctly defined requirements
|
84
|
+
# Needed for `bin/rails routes`. Picks up succinctly defined requirements
|
95
85
|
# for a route, for example route
|
96
86
|
#
|
97
87
|
# get 'photo/:id', :controller => 'photos', :action => 'show',
|
@@ -101,7 +91,7 @@ module ActionDispatch
|
|
101
91
|
# as requirements.
|
102
92
|
def requirements
|
103
93
|
@defaults.merge(path.requirements).delete_if { |_, v|
|
104
|
-
/.+?/ == v
|
94
|
+
/.+?/m == v
|
105
95
|
}
|
106
96
|
end
|
107
97
|
|
@@ -114,18 +104,11 @@ module ActionDispatch
|
|
114
104
|
end
|
115
105
|
|
116
106
|
def score(supplied_keys)
|
117
|
-
|
118
|
-
|
119
|
-
required_keys.each do |k|
|
107
|
+
path.required_names.each do |k|
|
120
108
|
return -1 unless supplied_keys.include?(k)
|
121
109
|
end
|
122
110
|
|
123
|
-
|
124
|
-
path.names.each do |k|
|
125
|
-
score += 1 if supplied_keys.include?(k)
|
126
|
-
end
|
127
|
-
|
128
|
-
score + (required_defaults.length * 2)
|
111
|
+
(required_defaults.length * 2) + path.names.count { |k| supplied_keys.include?(k) }
|
129
112
|
end
|
130
113
|
|
131
114
|
def parts
|
@@ -152,7 +135,7 @@ module ActionDispatch
|
|
152
135
|
end
|
153
136
|
|
154
137
|
def glob?
|
155
|
-
|
138
|
+
path.ast.glob?
|
156
139
|
end
|
157
140
|
|
158
141
|
def dispatcher?
|
@@ -17,32 +17,34 @@ module ActionDispatch
|
|
17
17
|
def self.normalize_path(path)
|
18
18
|
path ||= ""
|
19
19
|
encoding = path.encoding
|
20
|
-
path = "/#{path}"
|
21
|
-
path.squeeze!("/"
|
22
|
-
|
23
|
-
path
|
24
|
-
|
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
|
+
|
25
28
|
path.force_encoding(encoding)
|
26
|
-
path
|
27
29
|
end
|
28
30
|
|
29
31
|
# URI path and fragment escaping
|
30
32
|
# https://tools.ietf.org/html/rfc3986
|
31
33
|
class UriEncoder # :nodoc:
|
32
|
-
ENCODE = "%%%02X"
|
34
|
+
ENCODE = "%%%02X"
|
33
35
|
US_ASCII = Encoding::US_ASCII
|
34
36
|
UTF_8 = Encoding::UTF_8
|
35
|
-
EMPTY = "".
|
36
|
-
DEC2HEX = (0..255).
|
37
|
+
EMPTY = (+"").force_encoding(US_ASCII).freeze
|
38
|
+
DEC2HEX = (0..255).map { |i| (ENCODE % i).force_encoding(US_ASCII) }
|
37
39
|
|
38
|
-
ALPHA = "a-zA-Z"
|
39
|
-
DIGIT = "0-9"
|
40
|
-
UNRESERVED = "#{ALPHA}#{DIGIT}\\-\\._~"
|
41
|
-
SUB_DELIMS = "!\\$&'\\(\\)\\*\\+,;="
|
40
|
+
ALPHA = "a-zA-Z"
|
41
|
+
DIGIT = "0-9"
|
42
|
+
UNRESERVED = "#{ALPHA}#{DIGIT}\\-\\._~"
|
43
|
+
SUB_DELIMS = "!\\$&'\\(\\)\\*\\+,;="
|
42
44
|
|
43
45
|
ESCAPED = /%[a-zA-Z0-9]{2}/.freeze
|
44
46
|
|
45
|
-
FRAGMENT = /[^#{UNRESERVED}#{SUB_DELIMS}
|
47
|
+
FRAGMENT = /[^#{UNRESERVED}#{SUB_DELIMS}:@\/?]/.freeze
|
46
48
|
SEGMENT = /[^#{UNRESERVED}#{SUB_DELIMS}:@]/.freeze
|
47
49
|
PATH = /[^#{UNRESERVED}#{SUB_DELIMS}:@\/]/.freeze
|
48
50
|
|
@@ -15,9 +15,6 @@ require "action_dispatch/journey/path/pattern"
|
|
15
15
|
module ActionDispatch
|
16
16
|
module Journey # :nodoc:
|
17
17
|
class Router # :nodoc:
|
18
|
-
class RoutingError < ::StandardError # :nodoc:
|
19
|
-
end
|
20
|
-
|
21
18
|
attr_accessor :routes
|
22
19
|
|
23
20
|
def initialize(routes)
|
@@ -43,11 +40,12 @@ module ActionDispatch
|
|
43
40
|
req.path_info = "/" + req.path_info unless req.path_info.start_with? "/"
|
44
41
|
end
|
45
42
|
|
46
|
-
|
47
|
-
|
43
|
+
tmp_params = set_params.merge route.defaults
|
44
|
+
parameters.each_pair { |key, val|
|
45
|
+
tmp_params[key] = val.force_encoding(::Encoding::UTF_8)
|
48
46
|
}
|
49
47
|
|
50
|
-
req.path_parameters =
|
48
|
+
req.path_parameters = tmp_params
|
51
49
|
|
52
50
|
status, headers, body = route.app.serve(req)
|
53
51
|
|
@@ -68,7 +66,8 @@ module ActionDispatch
|
|
68
66
|
find_routes(rails_req).each do |match, parameters, route|
|
69
67
|
unless route.path.anchored
|
70
68
|
rails_req.script_name = match.to_s
|
71
|
-
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? "/"
|
72
71
|
end
|
73
72
|
|
74
73
|
parameters = route.defaults.merge parameters
|
@@ -84,10 +83,9 @@ module ActionDispatch
|
|
84
83
|
end
|
85
84
|
|
86
85
|
private
|
87
|
-
|
88
86
|
def partitioned_routes
|
89
87
|
routes.partition { |r|
|
90
|
-
r.path.anchored && r.
|
88
|
+
r.path.anchored && r.path.requirements_anchored?
|
91
89
|
}
|
92
90
|
end
|
93
91
|
|
@@ -109,23 +107,24 @@ module ActionDispatch
|
|
109
107
|
end
|
110
108
|
|
111
109
|
def find_routes(req)
|
112
|
-
|
113
|
-
|
110
|
+
path_info = req.path_info
|
111
|
+
routes = filter_routes(path_info).concat custom_routes.find_all { |r|
|
112
|
+
r.path.match?(path_info)
|
114
113
|
}
|
115
114
|
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
end
|
115
|
+
if req.head?
|
116
|
+
routes = match_head_routes(routes, req)
|
117
|
+
else
|
118
|
+
routes.select! { |r| r.matches?(req) }
|
119
|
+
end
|
122
120
|
|
123
121
|
routes.sort_by!(&:precedence)
|
124
122
|
|
125
123
|
routes.map! { |r|
|
126
|
-
match_data = r.path.match(
|
124
|
+
match_data = r.path.match(path_info)
|
127
125
|
path_parameters = {}
|
128
|
-
match_data.names.
|
126
|
+
match_data.names.each_with_index { |name, i|
|
127
|
+
val = match_data[i + 1]
|
129
128
|
path_parameters[name.to_sym] = Utils.unescape_uri(val) if val
|
130
129
|
}
|
131
130
|
[match_data, path_parameters, r]
|
@@ -133,24 +132,17 @@ module ActionDispatch
|
|
133
132
|
end
|
134
133
|
|
135
134
|
def match_head_routes(routes, req)
|
136
|
-
|
137
|
-
head_routes
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
end
|
146
|
-
else
|
147
|
-
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"
|
148
144
|
end
|
149
145
|
end
|
150
|
-
|
151
|
-
def match_routes(routes, req)
|
152
|
-
routes.select { |r| r.matches?(req) }
|
153
|
-
end
|
154
146
|
end
|
155
147
|
end
|
156
148
|
end
|
@@ -41,7 +41,7 @@ module ActionDispatch
|
|
41
41
|
end
|
42
42
|
|
43
43
|
def partition_route(route)
|
44
|
-
if route.path.anchored && route.
|
44
|
+
if route.path.anchored && route.path.requirements_anchored?
|
45
45
|
anchored_routes << route
|
46
46
|
else
|
47
47
|
custom_routes << route
|
@@ -50,13 +50,12 @@ module ActionDispatch
|
|
50
50
|
|
51
51
|
def ast
|
52
52
|
@ast ||= begin
|
53
|
-
|
54
|
-
Nodes::Or.new(
|
53
|
+
nodes = anchored_routes.map(&:ast)
|
54
|
+
Nodes::Or.new(nodes)
|
55
55
|
end
|
56
56
|
end
|
57
57
|
|
58
58
|
def simulator
|
59
|
-
return if ast.nil?
|
60
59
|
@simulator ||= begin
|
61
60
|
gtg = GTG::Builder.new(ast).transition_table
|
62
61
|
GTG::Simulator.new(gtg)
|
@@ -72,7 +71,6 @@ module ActionDispatch
|
|
72
71
|
end
|
73
72
|
|
74
73
|
private
|
75
|
-
|
76
74
|
def clear_cache!
|
77
75
|
@ast = nil
|
78
76
|
@simulator = nil
|
@@ -33,6 +33,12 @@ module ActionDispatch
|
|
33
33
|
end
|
34
34
|
|
35
35
|
private
|
36
|
+
# takes advantage of String @- deduping capabilities in Ruby 2.5 upwards
|
37
|
+
# see: https://bugs.ruby-lang.org/issues/13077
|
38
|
+
def dedup_scan(regex)
|
39
|
+
r = @ss.scan(regex)
|
40
|
+
r ? -r : nil
|
41
|
+
end
|
36
42
|
|
37
43
|
def scan
|
38
44
|
case
|
@@ -47,15 +53,15 @@ module ActionDispatch
|
|
47
53
|
[:OR, "|"]
|
48
54
|
when @ss.skip(/\./)
|
49
55
|
[:DOT, "."]
|
50
|
-
when text =
|
56
|
+
when text = dedup_scan(/:\w+/)
|
51
57
|
[:SYMBOL, text]
|
52
|
-
when text =
|
58
|
+
when text = dedup_scan(/\*\w+/)
|
53
59
|
[:STAR, text]
|
54
60
|
when text = @ss.scan(/(?:[\w%\-~!$&'*+,;=@]|\\[:()])+/)
|
55
61
|
text.tr! "\\", ""
|
56
|
-
[:LITERAL, text]
|
62
|
+
[:LITERAL, -text]
|
57
63
|
# any char
|
58
|
-
when text =
|
64
|
+
when text = dedup_scan(/./)
|
59
65
|
[:LITERAL, text]
|
60
66
|
end
|
61
67
|
end
|
@@ -40,7 +40,7 @@ module ActionDispatch
|
|
40
40
|
@parameters.each do |index|
|
41
41
|
param = parts[index]
|
42
42
|
value = hash[param.name]
|
43
|
-
return ""
|
43
|
+
return "" if value.nil?
|
44
44
|
parts[index] = param.escape value
|
45
45
|
end
|
46
46
|
|
@@ -59,7 +59,6 @@ module ActionDispatch
|
|
59
59
|
end
|
60
60
|
|
61
61
|
private
|
62
|
-
|
63
62
|
def visit(node)
|
64
63
|
send(DISPATCH_CACHE[node.type], node)
|
65
64
|
end
|
@@ -168,7 +167,6 @@ module ActionDispatch
|
|
168
167
|
|
169
168
|
class String < FunctionalVisitor # :nodoc:
|
170
169
|
private
|
171
|
-
|
172
170
|
def binary(node, seed)
|
173
171
|
visit(node.right, visit(node.left, seed))
|
174
172
|
end
|
@@ -214,7 +212,6 @@ module ActionDispatch
|
|
214
212
|
end
|
215
213
|
|
216
214
|
private
|
217
|
-
|
218
215
|
def binary(node, seed)
|
219
216
|
seed.last.concat node.children.map { |c|
|
220
217
|
"#{node.object_id} -> #{c.object_id};"
|
@@ -68,7 +68,7 @@ function highlight_state(index, color) {
|
|
68
68
|
}
|
69
69
|
|
70
70
|
function highlight_finish(index) {
|
71
|
-
svg_nodes[index].select('
|
71
|
+
svg_nodes[index].select('ellipse')
|
72
72
|
.style("fill", "while")
|
73
73
|
.transition().duration(500)
|
74
74
|
.style("fill", "blue");
|
@@ -76,54 +76,79 @@ function highlight_finish(index) {
|
|
76
76
|
|
77
77
|
function match(input) {
|
78
78
|
reset_graph();
|
79
|
-
var table
|
80
|
-
var states
|
81
|
-
var regexp_states
|
82
|
-
var string_states
|
83
|
-
var
|
79
|
+
var table = tt();
|
80
|
+
var states = [[0, null]];
|
81
|
+
var regexp_states = table['regexp_states'];
|
82
|
+
var string_states = table['string_states'];
|
83
|
+
var stdparam_states = table['stdparam_states'];
|
84
|
+
var accepting = table['accepting'];
|
85
|
+
var default_re = new RegExp("^[^.\/?]+$");
|
86
|
+
var start_index = 0;
|
84
87
|
|
85
88
|
highlight_state(0);
|
86
89
|
|
87
90
|
tokenize(input, function(token) {
|
91
|
+
var end_index = start_index + token.length;
|
92
|
+
|
88
93
|
var new_states = [];
|
89
94
|
for(var key in states) {
|
90
|
-
var
|
95
|
+
var state_parts = states[key];
|
96
|
+
var state = state_parts[0];
|
97
|
+
var previous_start = state_parts[1];
|
98
|
+
|
99
|
+
if(previous_start == null) {
|
100
|
+
if(string_states[state] && string_states[state][token]) {
|
101
|
+
var new_state = string_states[state][token];
|
102
|
+
highlight_edge(state, new_state);
|
103
|
+
highlight_state(new_state);
|
104
|
+
new_states.push([new_state, null]);
|
105
|
+
}
|
91
106
|
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
107
|
+
if(stdparam_states[state] && default_re.test(token)) {
|
108
|
+
for(var key in stdparam_states[state]) {
|
109
|
+
var new_state = stdparam_states[state][key];
|
110
|
+
highlight_edge(state, new_state);
|
111
|
+
highlight_state(new_state);
|
112
|
+
new_states.push([new_state, null]);
|
113
|
+
}
|
114
|
+
}
|
97
115
|
}
|
98
116
|
|
99
117
|
if(regexp_states[state]) {
|
118
|
+
var slice_start = previous_start != null ? previous_start : start_index;
|
119
|
+
|
100
120
|
for(var key in regexp_states[state]) {
|
101
121
|
var re = new RegExp("^" + key + "$");
|
102
|
-
|
122
|
+
|
123
|
+
var accumulation = input.slice(slice_start, end_index);
|
124
|
+
|
125
|
+
if(re.test(accumulation)) {
|
103
126
|
var new_state = regexp_states[state][key];
|
104
127
|
highlight_edge(state, new_state);
|
105
128
|
highlight_state(new_state);
|
106
|
-
new_states.push(new_state);
|
129
|
+
new_states.push([new_state, null]);
|
107
130
|
}
|
131
|
+
|
132
|
+
// retry the same regexp with the accumulated data either way
|
133
|
+
new_states.push([state, slice_start]);
|
108
134
|
}
|
109
135
|
}
|
110
136
|
}
|
111
137
|
|
112
|
-
if(new_states.length == 0) {
|
113
|
-
return;
|
114
|
-
}
|
115
138
|
states = new_states;
|
139
|
+
start_index = end_index;
|
116
140
|
});
|
117
141
|
|
118
142
|
for(var key in states) {
|
119
|
-
var
|
143
|
+
var state_parts = states[key];
|
144
|
+
var state = state_parts[0];
|
145
|
+
var slice_start = state_parts[1];
|
146
|
+
|
147
|
+
// we must ignore ones that are still accepting more data
|
148
|
+
if (slice_start != null) continue;
|
149
|
+
|
120
150
|
if(accepting[state]) {
|
121
|
-
|
122
|
-
if(!regexp_states[mkey] && !string_states[mkey]) {
|
123
|
-
highlight_edge(state, mkey);
|
124
|
-
highlight_finish(mkey);
|
125
|
-
}
|
126
|
-
}
|
151
|
+
highlight_finish(state);
|
127
152
|
} else {
|
128
153
|
highlight_state(state, "red");
|
129
154
|
}
|
@@ -8,7 +8,7 @@
|
|
8
8
|
<%= style %>
|
9
9
|
<% end %>
|
10
10
|
</style>
|
11
|
-
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.8/d3.min.js"
|
11
|
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.8/d3.min.js"></script>
|
12
12
|
</head>
|
13
13
|
<body>
|
14
14
|
<div id="wrapper">
|