actionpack 6.1.4 → 7.0.0.rc1
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 +189 -372
- data/MIT-LICENSE +1 -1
- data/README.rdoc +2 -3
- data/lib/abstract_controller/asset_paths.rb +1 -1
- data/lib/abstract_controller/base.rb +7 -21
- data/lib/abstract_controller/caching/fragments.rb +2 -2
- data/lib/abstract_controller/caching.rb +1 -1
- data/lib/abstract_controller/callbacks.rb +21 -7
- data/lib/abstract_controller/collector.rb +4 -2
- data/lib/abstract_controller/error.rb +1 -1
- data/lib/abstract_controller/helpers.rb +3 -2
- data/lib/abstract_controller/logger.rb +1 -1
- data/lib/abstract_controller/railties/routes_helpers.rb +2 -0
- data/lib/abstract_controller/translation.rb +3 -2
- data/lib/abstract_controller/url_for.rb +4 -6
- data/lib/action_controller/api.rb +1 -1
- data/lib/action_controller/log_subscriber.rb +4 -3
- data/lib/action_controller/metal/conditional_get.rb +38 -1
- data/lib/action_controller/metal/content_security_policy.rb +1 -1
- data/lib/action_controller/metal/cookies.rb +1 -1
- data/lib/action_controller/metal/data_streaming.rb +5 -13
- data/lib/action_controller/metal/exceptions.rb +19 -30
- data/lib/action_controller/metal/flash.rb +6 -2
- data/lib/action_controller/metal/helpers.rb +1 -1
- data/lib/action_controller/metal/http_authentication.rb +17 -16
- data/lib/action_controller/metal/instrumentation.rb +57 -52
- data/lib/action_controller/metal/live.rb +42 -2
- data/lib/action_controller/metal/mime_responds.rb +3 -3
- data/lib/action_controller/metal/params_wrapper.rb +20 -11
- data/lib/action_controller/metal/permissions_policy.rb +1 -1
- data/lib/action_controller/metal/redirecting.rb +86 -16
- data/lib/action_controller/metal/rendering.rb +7 -7
- data/lib/action_controller/metal/request_forgery_protection.rb +64 -24
- data/lib/action_controller/metal/rescue.rb +1 -1
- data/lib/action_controller/metal/streaming.rb +1 -3
- data/lib/action_controller/metal/strong_parameters.rb +84 -47
- data/lib/action_controller/metal/testing.rb +0 -2
- data/lib/action_controller/metal.rb +7 -10
- data/lib/action_controller/railtie.rb +49 -6
- data/lib/action_controller/test_case.rb +19 -4
- data/lib/action_controller.rb +1 -5
- data/lib/action_dispatch/http/cache.rb +13 -6
- data/lib/action_dispatch/http/content_security_policy.rb +39 -35
- data/lib/action_dispatch/http/filter_parameters.rb +5 -0
- data/lib/action_dispatch/http/mime_negotiation.rb +13 -3
- data/lib/action_dispatch/http/mime_type.rb +9 -11
- data/lib/action_dispatch/http/parameters.rb +4 -4
- data/lib/action_dispatch/http/permissions_policy.rb +1 -1
- data/lib/action_dispatch/http/request.rb +10 -19
- data/lib/action_dispatch/http/response.rb +1 -13
- data/lib/action_dispatch/http/url.rb +11 -19
- data/lib/action_dispatch/journey/gtg/builder.rb +11 -12
- data/lib/action_dispatch/journey/gtg/simulator.rb +10 -4
- data/lib/action_dispatch/journey/gtg/transition_table.rb +77 -21
- data/lib/action_dispatch/journey/nodes/node.rb +70 -5
- data/lib/action_dispatch/journey/path/pattern.rb +22 -13
- data/lib/action_dispatch/journey/route.rb +6 -13
- data/lib/action_dispatch/journey/router/utils.rb +2 -2
- data/lib/action_dispatch/journey/router.rb +1 -1
- data/lib/action_dispatch/journey/routes.rb +3 -3
- 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/middleware/actionable_exceptions.rb +0 -1
- data/lib/action_dispatch/middleware/cookies.rb +8 -4
- data/lib/action_dispatch/middleware/debug_exceptions.rb +6 -4
- data/lib/action_dispatch/middleware/debug_locks.rb +3 -3
- data/lib/action_dispatch/middleware/exception_wrapper.rb +4 -0
- data/lib/action_dispatch/middleware/executor.rb +3 -0
- data/lib/action_dispatch/middleware/flash.rb +9 -11
- data/lib/action_dispatch/middleware/host_authorization.rb +49 -37
- data/lib/action_dispatch/middleware/remote_ip.rb +16 -4
- data/lib/action_dispatch/middleware/server_timing.rb +33 -0
- data/lib/action_dispatch/middleware/session/abstract_store.rb +1 -1
- data/lib/action_dispatch/middleware/show_exceptions.rb +17 -9
- data/lib/action_dispatch/middleware/stack.rb +27 -9
- data/lib/action_dispatch/middleware/static.rb +2 -6
- data/lib/action_dispatch/middleware/templates/rescues/_message_and_suggestions.html.erb +1 -1
- data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +4 -11
- data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +2 -2
- data/lib/action_dispatch/middleware/templates/rescues/blocked_host.html.erb +4 -3
- data/lib/action_dispatch/middleware/templates/rescues/blocked_host.text.erb +3 -1
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +4 -4
- data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +3 -3
- data/lib/action_dispatch/middleware/templates/rescues/layout.erb +28 -18
- data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.html.erb +3 -3
- data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +3 -3
- data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +3 -3
- data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +3 -3
- data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +3 -3
- data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +5 -14
- data/lib/action_dispatch/railtie.rb +8 -2
- data/lib/action_dispatch/request/session.rb +43 -13
- data/lib/action_dispatch/routing/inspector.rb +1 -1
- data/lib/action_dispatch/routing/mapper.rb +54 -78
- data/lib/action_dispatch/routing/redirection.rb +0 -2
- data/lib/action_dispatch/routing/route_set.rb +14 -6
- data/lib/action_dispatch/routing/routes_proxy.rb +1 -1
- data/lib/action_dispatch/routing/url_for.rb +1 -2
- data/lib/action_dispatch/routing.rb +2 -2
- data/lib/action_dispatch/system_test_case.rb +12 -6
- data/lib/action_dispatch/system_testing/browser.rb +2 -12
- data/lib/action_dispatch/system_testing/driver.rb +35 -11
- data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +10 -6
- data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +0 -8
- data/lib/action_dispatch/testing/assertions.rb +2 -5
- data/lib/action_dispatch/testing/integration.rb +6 -8
- data/lib/action_dispatch/testing/test_process.rb +3 -26
- data/lib/action_dispatch.rb +2 -1
- data/lib/action_pack/gem_version.rb +4 -4
- data/lib/action_pack.rb +1 -1
- metadata +19 -17
@@ -10,11 +10,15 @@ module ActionDispatch
|
|
10
10
|
|
11
11
|
attr_reader :memos
|
12
12
|
|
13
|
+
DEFAULT_EXP = /[^.\/?]+/
|
14
|
+
DEFAULT_EXP_ANCHORED = /\A#{DEFAULT_EXP}\Z/
|
15
|
+
|
13
16
|
def initialize
|
14
|
-
@
|
15
|
-
@
|
16
|
-
@
|
17
|
-
@
|
17
|
+
@stdparam_states = {}
|
18
|
+
@regexp_states = {}
|
19
|
+
@string_states = {}
|
20
|
+
@accepting = {}
|
21
|
+
@memos = Hash.new { |h, k| h[k] = [] }
|
18
22
|
end
|
19
23
|
|
20
24
|
def add_accepting(state)
|
@@ -41,22 +45,54 @@ module ActionDispatch
|
|
41
45
|
Array(t)
|
42
46
|
end
|
43
47
|
|
44
|
-
def move(t,
|
48
|
+
def move(t, full_string, start_index, end_index)
|
45
49
|
return [] if t.empty?
|
46
50
|
|
47
|
-
|
48
|
-
strings = []
|
51
|
+
next_states = []
|
49
52
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
+
tok = full_string.slice(start_index, end_index - start_index)
|
54
|
+
token_matches_default_component = DEFAULT_EXP_ANCHORED.match?(tok)
|
55
|
+
|
56
|
+
t.each { |s, previous_start|
|
57
|
+
if previous_start.nil?
|
58
|
+
# In the simple case of a "default" param regex do this fast-path
|
59
|
+
# and add all next states.
|
60
|
+
if token_matches_default_component && states = @stdparam_states[s]
|
61
|
+
states.each { |re, v| next_states << [v, nil].freeze if !v.nil? }
|
62
|
+
end
|
63
|
+
|
64
|
+
# When we have a literal string, we can just pull the next state
|
65
|
+
if states = @string_states[s]
|
66
|
+
next_states << [states[tok], nil].freeze unless states[tok].nil?
|
67
|
+
end
|
53
68
|
end
|
54
69
|
|
55
|
-
|
56
|
-
|
70
|
+
# For regexes that aren't the "default" style, they may potentially
|
71
|
+
# not be terminated by the first "token" [./?], so we need to continue
|
72
|
+
# to attempt to match this regexp as well as any successful paths that
|
73
|
+
# continue out of it. both paths could be valid.
|
74
|
+
if states = @regexp_states[s]
|
75
|
+
slice_start = if previous_start.nil?
|
76
|
+
start_index
|
77
|
+
else
|
78
|
+
previous_start
|
79
|
+
end
|
80
|
+
|
81
|
+
slice_length = end_index - slice_start
|
82
|
+
curr_slice = full_string.slice(slice_start, slice_length)
|
83
|
+
|
84
|
+
states.each { |re, v|
|
85
|
+
# if we match, we can try moving past this
|
86
|
+
next_states << [v, nil].freeze if !v.nil? && re.match?(curr_slice)
|
87
|
+
}
|
88
|
+
|
89
|
+
# and regardless, we must continue accepting tokens and retrying this regexp.
|
90
|
+
# we need to remember where we started as well so we can take bigger slices.
|
91
|
+
next_states << [s, slice_start].freeze
|
57
92
|
end
|
58
93
|
}
|
59
|
-
|
94
|
+
|
95
|
+
next_states
|
60
96
|
end
|
61
97
|
|
62
98
|
def as_json(options = nil)
|
@@ -69,9 +105,10 @@ module ActionDispatch
|
|
69
105
|
end
|
70
106
|
|
71
107
|
{
|
72
|
-
regexp_states:
|
73
|
-
string_states:
|
74
|
-
|
108
|
+
regexp_states: simple_regexp,
|
109
|
+
string_states: @string_states,
|
110
|
+
stdparam_states: @stdparam_states,
|
111
|
+
accepting: @accepting
|
75
112
|
}
|
76
113
|
end
|
77
114
|
|
@@ -93,7 +130,7 @@ module ActionDispatch
|
|
93
130
|
states = "function tt() { return #{to_json}; }"
|
94
131
|
|
95
132
|
fun_routes = paths.sample(3).map do |ast|
|
96
|
-
ast.
|
133
|
+
ast.filter_map { |n|
|
97
134
|
case n
|
98
135
|
when Nodes::Symbol
|
99
136
|
case n.left
|
@@ -106,7 +143,7 @@ module ActionDispatch
|
|
106
143
|
else
|
107
144
|
nil
|
108
145
|
end
|
109
|
-
}.
|
146
|
+
}.join
|
110
147
|
end
|
111
148
|
|
112
149
|
stylesheets = [fsm_css]
|
@@ -125,18 +162,33 @@ module ActionDispatch
|
|
125
162
|
|
126
163
|
def []=(from, to, sym)
|
127
164
|
to_mappings = states_hash_for(sym)[from] ||= {}
|
165
|
+
case sym
|
166
|
+
when Regexp
|
167
|
+
# we must match the whole string to a token boundary
|
168
|
+
if sym == DEFAULT_EXP
|
169
|
+
sym = DEFAULT_EXP_ANCHORED
|
170
|
+
else
|
171
|
+
sym = /\A#{sym}\Z/
|
172
|
+
end
|
173
|
+
when Symbol
|
174
|
+
# account for symbols in the constraints the same as strings
|
175
|
+
sym = sym.to_s
|
176
|
+
end
|
128
177
|
to_mappings[sym] = to
|
129
178
|
end
|
130
179
|
|
131
180
|
def states
|
132
181
|
ss = @string_states.keys + @string_states.values.flat_map(&:values)
|
182
|
+
ps = @stdparam_states.keys + @stdparam_states.values.flat_map(&:values)
|
133
183
|
rs = @regexp_states.keys + @regexp_states.values.flat_map(&:values)
|
134
|
-
(ss + rs).uniq
|
184
|
+
(ss + ps + rs).uniq
|
135
185
|
end
|
136
186
|
|
137
187
|
def transitions
|
138
188
|
@string_states.flat_map { |from, hash|
|
139
189
|
hash.map { |s, to| [from, s, to] }
|
190
|
+
} + @stdparam_states.flat_map { |from, hash|
|
191
|
+
hash.map { |s, to| [from, s, to] }
|
140
192
|
} + @regexp_states.flat_map { |from, hash|
|
141
193
|
hash.map { |s, to| [from, s, to] }
|
142
194
|
}
|
@@ -145,10 +197,14 @@ module ActionDispatch
|
|
145
197
|
private
|
146
198
|
def states_hash_for(sym)
|
147
199
|
case sym
|
148
|
-
when String
|
200
|
+
when String, Symbol
|
149
201
|
@string_states
|
150
202
|
when Regexp
|
151
|
-
|
203
|
+
if sym == DEFAULT_EXP
|
204
|
+
@stdparam_states
|
205
|
+
else
|
206
|
+
@regexp_states
|
207
|
+
end
|
152
208
|
else
|
153
209
|
raise ArgumentError, "unknown symbol: %s" % sym.class
|
154
210
|
end
|
@@ -4,6 +4,66 @@ require "action_dispatch/journey/visitors"
|
|
4
4
|
|
5
5
|
module ActionDispatch
|
6
6
|
module Journey # :nodoc:
|
7
|
+
class Ast # :nodoc:
|
8
|
+
attr_reader :names, :path_params, :tree, :wildcard_options, :terminals
|
9
|
+
alias :root :tree
|
10
|
+
|
11
|
+
def initialize(tree, formatted)
|
12
|
+
@tree = tree
|
13
|
+
@path_params = []
|
14
|
+
@names = []
|
15
|
+
@symbols = []
|
16
|
+
@stars = []
|
17
|
+
@terminals = []
|
18
|
+
@wildcard_options = {}
|
19
|
+
|
20
|
+
visit_tree(formatted)
|
21
|
+
end
|
22
|
+
|
23
|
+
def requirements=(requirements)
|
24
|
+
# inject any regexp requirements for `star` nodes so they can be
|
25
|
+
# determined nullable, which requires knowing if the regex accepts an
|
26
|
+
# empty string.
|
27
|
+
(symbols + stars).each do |node|
|
28
|
+
re = requirements[node.to_sym]
|
29
|
+
node.regexp = re if re
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def route=(route)
|
34
|
+
terminals.each { |n| n.memo = route }
|
35
|
+
end
|
36
|
+
|
37
|
+
def glob?
|
38
|
+
stars.any?
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
attr_reader :symbols, :stars
|
43
|
+
|
44
|
+
def visit_tree(formatted)
|
45
|
+
tree.each do |node|
|
46
|
+
if node.symbol?
|
47
|
+
path_params << node.to_sym
|
48
|
+
names << node.name
|
49
|
+
symbols << node
|
50
|
+
elsif node.star?
|
51
|
+
stars << node
|
52
|
+
|
53
|
+
if formatted != false
|
54
|
+
# Add a constraint for wildcard route to make it non-greedy and
|
55
|
+
# match the optional format part of the route by default.
|
56
|
+
wildcard_options[node.name.to_sym] ||= /.+?/m
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
if node.terminal?
|
61
|
+
terminals << node
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
7
67
|
module Nodes # :nodoc:
|
8
68
|
class Node # :nodoc:
|
9
69
|
include Enumerable
|
@@ -78,7 +138,7 @@ module ActionDispatch
|
|
78
138
|
alias :symbol :regexp
|
79
139
|
attr_reader :name
|
80
140
|
|
81
|
-
DEFAULT_EXP = /[
|
141
|
+
DEFAULT_EXP = /[^.\/?]+/
|
82
142
|
GREEDY_EXP = /(.+)/
|
83
143
|
def initialize(left, regexp = DEFAULT_EXP)
|
84
144
|
super(left)
|
@@ -86,10 +146,6 @@ module ActionDispatch
|
|
86
146
|
@name = -left.tr("*:", "")
|
87
147
|
end
|
88
148
|
|
89
|
-
def default_regexp?
|
90
|
-
regexp == DEFAULT_EXP
|
91
|
-
end
|
92
|
-
|
93
149
|
def type; :SYMBOL; end
|
94
150
|
def symbol?; true; end
|
95
151
|
end
|
@@ -104,6 +160,15 @@ module ActionDispatch
|
|
104
160
|
end
|
105
161
|
|
106
162
|
class Star < Unary # :nodoc:
|
163
|
+
attr_accessor :regexp
|
164
|
+
|
165
|
+
def initialize(left)
|
166
|
+
super(left)
|
167
|
+
|
168
|
+
# By default wildcard routes are non-greedy and must match something.
|
169
|
+
@regexp = /.+?/m
|
170
|
+
end
|
171
|
+
|
107
172
|
def star?; true; end
|
108
173
|
def type; :STAR; end
|
109
174
|
|
@@ -4,15 +4,16 @@ module ActionDispatch
|
|
4
4
|
module Journey # :nodoc:
|
5
5
|
module Path # :nodoc:
|
6
6
|
class Pattern # :nodoc:
|
7
|
-
attr_reader :
|
7
|
+
attr_reader :ast, :names, :requirements, :anchored, :spec
|
8
8
|
|
9
9
|
def initialize(ast, requirements, separators, anchored)
|
10
|
-
@
|
10
|
+
@ast = ast
|
11
|
+
@spec = ast.root
|
11
12
|
@requirements = requirements
|
12
13
|
@separators = separators
|
13
14
|
@anchored = anchored
|
14
15
|
|
15
|
-
@names =
|
16
|
+
@names = ast.names
|
16
17
|
@optional_names = nil
|
17
18
|
@required_names = nil
|
18
19
|
@re = nil
|
@@ -27,20 +28,28 @@ module ActionDispatch
|
|
27
28
|
required_names
|
28
29
|
offsets
|
29
30
|
to_regexp
|
30
|
-
nil
|
31
|
+
@ast = nil
|
31
32
|
end
|
32
33
|
|
33
|
-
def
|
34
|
-
|
35
|
-
|
36
|
-
node.regexp = re if re
|
37
|
-
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
|
38
37
|
|
39
|
-
|
40
|
-
|
38
|
+
terminals.each_with_index { |s, index|
|
39
|
+
next if index < 1
|
40
|
+
next if s.type == :DOT || s.type == :SLASH
|
41
|
+
|
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
|
+
}
|
41
51
|
|
42
|
-
|
43
|
-
@names ||= spec.find_all(&:symbol?).map(&:name)
|
52
|
+
true
|
44
53
|
end
|
45
54
|
|
46
55
|
def required_names
|
@@ -5,7 +5,7 @@ module ActionDispatch
|
|
5
5
|
module Journey
|
6
6
|
class Route
|
7
7
|
attr_reader :app, :path, :defaults, :name, :precedence, :constraints,
|
8
|
-
:internal, :scope_options
|
8
|
+
:internal, :scope_options, :ast
|
9
9
|
|
10
10
|
alias :conditions :constraints
|
11
11
|
|
@@ -65,29 +65,22 @@ module ActionDispatch
|
|
65
65
|
@_required_defaults = required_defaults
|
66
66
|
@required_parts = nil
|
67
67
|
@parts = nil
|
68
|
-
@decorated_ast = nil
|
69
68
|
@precedence = precedence
|
70
69
|
@path_formatter = @path.build_formatter
|
71
70
|
@scope_options = scope_options
|
72
71
|
@internal = internal
|
72
|
+
|
73
|
+
@ast = @path.ast.root
|
74
|
+
@path.ast.route = self
|
73
75
|
end
|
74
76
|
|
75
77
|
def eager_load!
|
76
78
|
path.eager_load!
|
77
|
-
ast
|
78
79
|
parts
|
79
80
|
required_defaults
|
80
81
|
nil
|
81
82
|
end
|
82
83
|
|
83
|
-
def ast
|
84
|
-
@decorated_ast ||= begin
|
85
|
-
decorated_ast = path.ast
|
86
|
-
decorated_ast.find_all(&:terminal?).each { |n| n.memo = self }
|
87
|
-
decorated_ast
|
88
|
-
end
|
89
|
-
end
|
90
|
-
|
91
84
|
# Needed for `bin/rails routes`. Picks up succinctly defined requirements
|
92
85
|
# for a route, for example route
|
93
86
|
#
|
@@ -98,7 +91,7 @@ module ActionDispatch
|
|
98
91
|
# as requirements.
|
99
92
|
def requirements
|
100
93
|
@defaults.merge(path.requirements).delete_if { |_, v|
|
101
|
-
/.+?/ == v
|
94
|
+
/.+?/m == v
|
102
95
|
}
|
103
96
|
end
|
104
97
|
|
@@ -142,7 +135,7 @@ module ActionDispatch
|
|
142
135
|
end
|
143
136
|
|
144
137
|
def glob?
|
145
|
-
path.
|
138
|
+
path.ast.glob?
|
146
139
|
end
|
147
140
|
|
148
141
|
def dispatcher?
|
@@ -35,7 +35,7 @@ module ActionDispatch
|
|
35
35
|
US_ASCII = Encoding::US_ASCII
|
36
36
|
UTF_8 = Encoding::UTF_8
|
37
37
|
EMPTY = (+"").force_encoding(US_ASCII).freeze
|
38
|
-
DEC2HEX = (0..255).
|
38
|
+
DEC2HEX = (0..255).map { |i| (ENCODE % i).force_encoding(US_ASCII) }
|
39
39
|
|
40
40
|
ALPHA = "a-zA-Z"
|
41
41
|
DIGIT = "0-9"
|
@@ -44,7 +44,7 @@ module ActionDispatch
|
|
44
44
|
|
45
45
|
ESCAPED = /%[a-zA-Z0-9]{2}/.freeze
|
46
46
|
|
47
|
-
FRAGMENT = /[^#{UNRESERVED}#{SUB_DELIMS}
|
47
|
+
FRAGMENT = /[^#{UNRESERVED}#{SUB_DELIMS}:@\/?]/.freeze
|
48
48
|
SEGMENT = /[^#{UNRESERVED}#{SUB_DELIMS}:@]/.freeze
|
49
49
|
PATH = /[^#{UNRESERVED}#{SUB_DELIMS}:@\/]/.freeze
|
50
50
|
|
@@ -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,8 +50,8 @@ 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
|
|
@@ -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">
|
@@ -7,7 +7,7 @@ require "active_support/json"
|
|
7
7
|
require "rack/utils"
|
8
8
|
|
9
9
|
module ActionDispatch
|
10
|
-
|
10
|
+
module RequestCookieMethods
|
11
11
|
def cookie_jar
|
12
12
|
fetch_header("action_dispatch.cookies") do
|
13
13
|
self.cookie_jar = Cookies::CookieJar.build(self, cookies)
|
@@ -88,6 +88,10 @@ module ActionDispatch
|
|
88
88
|
# :startdoc:
|
89
89
|
end
|
90
90
|
|
91
|
+
ActiveSupport.on_load(:action_dispatch_request) do
|
92
|
+
include RequestCookieMethods
|
93
|
+
end
|
94
|
+
|
91
95
|
# Read and write data to cookies through ActionController#cookies.
|
92
96
|
#
|
93
97
|
# When reading cookie data, the data is read from the HTTP request header, Cookie.
|
@@ -99,7 +103,7 @@ module ActionDispatch
|
|
99
103
|
# # This cookie will be deleted when the user's browser is closed.
|
100
104
|
# cookies[:user_name] = "david"
|
101
105
|
#
|
102
|
-
# # Cookie values are String
|
106
|
+
# # Cookie values are String-based. Other data types need to be serialized.
|
103
107
|
# cookies[:lat_lon] = JSON.generate([47.68, -122.37])
|
104
108
|
#
|
105
109
|
# # Sets a cookie that expires in 1 hour.
|
@@ -280,7 +284,7 @@ module ActionDispatch
|
|
280
284
|
end
|
281
285
|
end
|
282
286
|
|
283
|
-
class CookieJar
|
287
|
+
class CookieJar # :nodoc:
|
284
288
|
include Enumerable, ChainedCookieJars
|
285
289
|
|
286
290
|
# This regular expression is used to split the levels of a domain.
|
@@ -435,7 +439,7 @@ module ActionDispatch
|
|
435
439
|
end
|
436
440
|
|
437
441
|
def write_cookie?(cookie)
|
438
|
-
request.ssl? || !cookie[:secure] || always_write_cookie
|
442
|
+
request.ssl? || !cookie[:secure] || always_write_cookie || request.host.end_with?(".onion")
|
439
443
|
end
|
440
444
|
|
441
445
|
def handle_options(options)
|
@@ -1,6 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "action_dispatch/http/request"
|
4
3
|
require "action_dispatch/middleware/exception_wrapper"
|
5
4
|
require "action_dispatch/routing/inspector"
|
6
5
|
|
@@ -135,6 +134,7 @@ module ActionDispatch
|
|
135
134
|
logger = logger(request)
|
136
135
|
|
137
136
|
return unless logger
|
137
|
+
return if !log_rescued_responses?(request) && wrapper.rescue_response?
|
138
138
|
|
139
139
|
exception = wrapper.exception
|
140
140
|
trace = wrapper.exception_trace
|
@@ -149,9 +149,7 @@ module ActionDispatch
|
|
149
149
|
log_array(logger, message)
|
150
150
|
end
|
151
151
|
|
152
|
-
def log_array(logger,
|
153
|
-
lines = Array(array)
|
154
|
-
|
152
|
+
def log_array(logger, lines)
|
155
153
|
return if lines.empty?
|
156
154
|
|
157
155
|
if logger.formatter && logger.formatter.respond_to?(:tags_text)
|
@@ -178,5 +176,9 @@ module ActionDispatch
|
|
178
176
|
def api_request?(content_type)
|
179
177
|
@response_format == :api && !content_type.html?
|
180
178
|
end
|
179
|
+
|
180
|
+
def log_rescued_responses?(request)
|
181
|
+
request.get_header("action_dispatch.log_rescued_responses")
|
182
|
+
end
|
181
183
|
end
|
182
184
|
end
|
@@ -9,9 +9,9 @@ module ActionDispatch
|
|
9
9
|
# config.middleware.insert_before Rack::Sendfile, ActionDispatch::DebugLocks
|
10
10
|
#
|
11
11
|
# After restarting the application and re-triggering the deadlock condition,
|
12
|
-
# <tt>/rails/locks</tt> will show a summary of all threads currently
|
13
|
-
# the interlock, which lock level they are holding or awaiting, and
|
14
|
-
# current backtrace.
|
12
|
+
# the route <tt>/rails/locks</tt> will show a summary of all threads currently
|
13
|
+
# known to the interlock, which lock level they are holding or awaiting, and
|
14
|
+
# their current backtrace.
|
15
15
|
#
|
16
16
|
# Generally a deadlock will be caused by the interlock conflicting with some
|
17
17
|
# other external lock or blocking I/O call. These cannot be automatically
|
@@ -118,6 +118,10 @@ module ActionDispatch
|
|
118
118
|
Rack::Utils.status_code(@@rescue_responses[class_name])
|
119
119
|
end
|
120
120
|
|
121
|
+
def rescue_response?
|
122
|
+
@@rescue_responses.key?(exception.class.name)
|
123
|
+
end
|
124
|
+
|
121
125
|
def source_extracts
|
122
126
|
backtrace.map do |trace|
|
123
127
|
file, line_number = extract_file_and_line_number(trace)
|
@@ -13,6 +13,9 @@ module ActionDispatch
|
|
13
13
|
begin
|
14
14
|
response = @app.call(env)
|
15
15
|
returned = response << ::Rack::BodyProxy.new(response.pop) { state.complete! }
|
16
|
+
rescue => error
|
17
|
+
@executor.error_reporter.report(error, handled: false)
|
18
|
+
raise
|
16
19
|
ensure
|
17
20
|
state.complete! unless returned
|
18
21
|
end
|