actionpack 6.1.7.5 → 7.0.8.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +323 -399
- data/MIT-LICENSE +1 -0
- data/README.rdoc +4 -5
- data/lib/abstract_controller/asset_paths.rb +1 -1
- data/lib/abstract_controller/base.rb +13 -26
- 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 +2 -2
- data/lib/abstract_controller/error.rb +1 -1
- data/lib/abstract_controller/helpers.rb +17 -12
- data/lib/abstract_controller/logger.rb +1 -1
- data/lib/abstract_controller/railties/routes_helpers.rb +2 -0
- data/lib/abstract_controller/rendering.rb +9 -11
- data/lib/abstract_controller/translation.rb +27 -4
- data/lib/abstract_controller/url_for.rb +4 -6
- data/lib/action_controller/api.rb +7 -7
- data/lib/action_controller/base.rb +5 -4
- data/lib/action_controller/form_builder.rb +2 -2
- data/lib/action_controller/log_subscriber.rb +4 -3
- data/lib/action_controller/metal/basic_implicit_render.rb +3 -1
- data/lib/action_controller/metal/conditional_get.rb +137 -102
- data/lib/action_controller/metal/content_security_policy.rb +36 -2
- data/lib/action_controller/metal/cookies.rb +1 -1
- data/lib/action_controller/metal/data_streaming.rb +23 -31
- data/lib/action_controller/metal/etag_with_flash.rb +1 -1
- data/lib/action_controller/metal/exceptions.rb +19 -30
- data/lib/action_controller/metal/flash.rb +6 -2
- data/lib/action_controller/metal/head.rb +1 -1
- data/lib/action_controller/metal/helpers.rb +2 -2
- data/lib/action_controller/metal/http_authentication.rb +66 -39
- data/lib/action_controller/metal/instrumentation.rb +57 -52
- data/lib/action_controller/metal/live.rb +43 -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 +19 -28
- data/lib/action_controller/metal/redirecting.rb +95 -22
- data/lib/action_controller/metal/renderers.rb +12 -13
- data/lib/action_controller/metal/rendering.rb +121 -9
- data/lib/action_controller/metal/request_forgery_protection.rb +83 -32
- data/lib/action_controller/metal/rescue.rb +5 -4
- data/lib/action_controller/metal/streaming.rb +7 -9
- data/lib/action_controller/metal/strong_parameters.rb +138 -115
- data/lib/action_controller/metal/testing.rb +9 -2
- data/lib/action_controller/metal/url_for.rb +3 -5
- data/lib/action_controller/metal.rb +10 -13
- data/lib/action_controller/railtie.rb +50 -6
- data/lib/action_controller/renderer.rb +1 -20
- data/lib/action_controller/test_case.rb +28 -7
- data/lib/action_controller.rb +2 -5
- data/lib/action_dispatch/http/cache.rb +20 -13
- data/lib/action_dispatch/http/content_security_policy.rb +113 -36
- data/lib/action_dispatch/http/filter_parameters.rb +4 -19
- data/lib/action_dispatch/http/headers.rb +1 -1
- data/lib/action_dispatch/http/mime_negotiation.rb +15 -5
- data/lib/action_dispatch/http/mime_type.rb +9 -11
- data/lib/action_dispatch/http/parameters.rb +5 -5
- data/lib/action_dispatch/http/permissions_policy.rb +17 -1
- data/lib/action_dispatch/http/request.rb +27 -37
- data/lib/action_dispatch/http/response.rb +3 -20
- data/lib/action_dispatch/http/upload.rb +13 -2
- 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 +20 -13
- 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 +17 -18
- data/lib/action_dispatch/middleware/host_authorization.rb +13 -17
- data/lib/action_dispatch/middleware/remote_ip.rb +20 -8
- data/lib/action_dispatch/middleware/request_id.rb +3 -3
- data/lib/action_dispatch/middleware/server_timing.rb +76 -0
- data/lib/action_dispatch/middleware/session/abstract_store.rb +1 -1
- data/lib/action_dispatch/middleware/session/cookie_store.rb +9 -9
- data/lib/action_dispatch/middleware/show_exceptions.rb +17 -16
- data/lib/action_dispatch/middleware/stack.rb +27 -9
- data/lib/action_dispatch/middleware/static.rb +5 -9
- 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 +10 -5
- data/lib/action_dispatch/middleware/templates/rescues/blocked_host.text.erb +7 -3
- 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 +22 -22
- 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 +82 -83
- data/lib/action_dispatch/routing/redirection.rb +5 -2
- data/lib/action_dispatch/routing/route_set.rb +17 -7
- data/lib/action_dispatch/routing/routes_proxy.rb +1 -1
- data/lib/action_dispatch/routing/url_for.rb +24 -25
- data/lib/action_dispatch/routing.rb +5 -6
- data/lib/action_dispatch/system_test_case.rb +5 -5
- data/lib/action_dispatch/system_testing/browser.rb +3 -13
- data/lib/action_dispatch/system_testing/driver.rb +34 -10
- data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +11 -7
- data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +0 -8
- data/lib/action_dispatch/testing/assertions/response.rb +1 -1
- data/lib/action_dispatch/testing/assertions/routing.rb +3 -2
- 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 -29
- data/lib/action_dispatch/testing/test_response.rb +20 -2
- data/lib/action_dispatch.rb +1 -0
- data/lib/action_pack/gem_version.rb +5 -5
- data/lib/action_pack/version.rb +1 -1
- metadata +16 -15
@@ -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,10 +88,14 @@ module ActionDispatch
|
|
88
88
|
# :startdoc:
|
89
89
|
end
|
90
90
|
|
91
|
-
|
91
|
+
ActiveSupport.on_load(:action_dispatch_request) do
|
92
|
+
include RequestCookieMethods
|
93
|
+
end
|
94
|
+
|
95
|
+
# Read and write data to cookies through ActionController::Base#cookies.
|
92
96
|
#
|
93
97
|
# When reading cookie data, the data is read from the HTTP request header, Cookie.
|
94
|
-
# When writing cookie data, the data is sent out in the HTTP response header, Set-Cookie
|
98
|
+
# When writing cookie data, the data is sent out in the HTTP response header, +Set-Cookie+.
|
95
99
|
#
|
96
100
|
# Examples of writing:
|
97
101
|
#
|
@@ -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.
|
@@ -135,7 +139,7 @@ module ActionDispatch
|
|
135
139
|
#
|
136
140
|
# cookies.delete :user_name
|
137
141
|
#
|
138
|
-
# Please note that if you specify a
|
142
|
+
# Please note that if you specify a +:domain+ when setting a cookie, you must also specify the domain when deleting the cookie:
|
139
143
|
#
|
140
144
|
# cookies[:name] = {
|
141
145
|
# value: 'a yummy cookie',
|
@@ -172,6 +176,9 @@ module ActionDispatch
|
|
172
176
|
# Default is +false+.
|
173
177
|
# * <tt>:httponly</tt> - Whether this cookie is accessible via scripting or
|
174
178
|
# only HTTP. Defaults to +false+.
|
179
|
+
# * <tt>:same_site</tt> - The value of the +SameSite+ cookie attribute, which
|
180
|
+
# determines how this cookie should be restricted in cross-site contexts.
|
181
|
+
# Possible values are +:none+, +:lax+, and +:strict+. Defaults to +:lax+.
|
175
182
|
class Cookies
|
176
183
|
HTTP_HEADER = "Set-Cookie"
|
177
184
|
GENERATOR_KEY = "action_dispatch.key_generator"
|
@@ -195,7 +202,7 @@ module ActionDispatch
|
|
195
202
|
# Raised when storing more than 4K of session data.
|
196
203
|
CookieOverflow = Class.new StandardError
|
197
204
|
|
198
|
-
# Include in a cookie jar to allow chaining, e.g. cookies.permanent.signed
|
205
|
+
# Include in a cookie jar to allow chaining, e.g. +cookies.permanent.signed+.
|
199
206
|
module ChainedCookieJars
|
200
207
|
# Returns a jar that'll automatically set the assigned cookies to have an expiration date 20 years from now. Example:
|
201
208
|
#
|
@@ -280,7 +287,7 @@ module ActionDispatch
|
|
280
287
|
end
|
281
288
|
end
|
282
289
|
|
283
|
-
class CookieJar
|
290
|
+
class CookieJar # :nodoc:
|
284
291
|
include Enumerable, ChainedCookieJars
|
285
292
|
|
286
293
|
def self.build(req, cookies)
|
@@ -421,7 +428,7 @@ module ActionDispatch
|
|
421
428
|
end
|
422
429
|
|
423
430
|
def write_cookie?(cookie)
|
424
|
-
request.ssl? || !cookie[:secure] || always_write_cookie
|
431
|
+
request.ssl? || !cookie[:secure] || always_write_cookie || request.host.end_with?(".onion")
|
425
432
|
end
|
426
433
|
|
427
434
|
def handle_options(options)
|
@@ -436,7 +443,7 @@ module ActionDispatch
|
|
436
443
|
|
437
444
|
if options[:domain] == :all || options[:domain] == "all"
|
438
445
|
cookie_domain = ""
|
439
|
-
dot_splitted_host = request.host.split(
|
446
|
+
dot_splitted_host = request.host.split(".", -1)
|
440
447
|
|
441
448
|
# Case where request.host is not an IP address or it's an invalid domain
|
442
449
|
# (ip confirms to the domain structure we expect so we explicitly check for ip)
|
@@ -446,10 +453,10 @@ module ActionDispatch
|
|
446
453
|
end
|
447
454
|
|
448
455
|
# If there is a provided tld length then we use it otherwise default domain.
|
449
|
-
if options[:tld_length].present?
|
456
|
+
if options[:tld_length].present?
|
450
457
|
# Case where the tld_length provided is valid
|
451
458
|
if dot_splitted_host.length >= options[:tld_length]
|
452
|
-
cookie_domain = dot_splitted_host.last(options[:tld_length]).join(
|
459
|
+
cookie_domain = dot_splitted_host.last(options[:tld_length]).join(".")
|
453
460
|
end
|
454
461
|
# Case where tld_length is not provided
|
455
462
|
else
|
@@ -458,7 +465,7 @@ module ActionDispatch
|
|
458
465
|
cookie_domain = dot_splitted_host.last(2).join(".")
|
459
466
|
# **.**, ***.** style TLDs like co.uk and com.au
|
460
467
|
else
|
461
|
-
cookie_domain = dot_splitted_host.last(3).join(
|
468
|
+
cookie_domain = dot_splitted_host.last(3).join(".")
|
462
469
|
end
|
463
470
|
end
|
464
471
|
|
@@ -676,7 +683,7 @@ module ActionDispatch
|
|
676
683
|
deserialize(name) do |rotate|
|
677
684
|
@encryptor.decrypt_and_verify(encrypted_message, on_rotation: rotate, purpose: purpose)
|
678
685
|
end
|
679
|
-
rescue ActiveSupport::MessageEncryptor::InvalidMessage, ActiveSupport::MessageVerifier::InvalidSignature
|
686
|
+
rescue ActiveSupport::MessageEncryptor::InvalidMessage, ActiveSupport::MessageVerifier::InvalidSignature, JSON::ParserError
|
680
687
|
nil
|
681
688
|
end
|
682
689
|
|