actionpack 6.1.7.3 → 7.0.8

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.

Files changed (128) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +320 -390
  3. data/MIT-LICENSE +1 -0
  4. data/README.rdoc +4 -5
  5. data/lib/abstract_controller/asset_paths.rb +1 -1
  6. data/lib/abstract_controller/base.rb +13 -26
  7. data/lib/abstract_controller/caching/fragments.rb +2 -2
  8. data/lib/abstract_controller/caching.rb +1 -1
  9. data/lib/abstract_controller/callbacks.rb +21 -7
  10. data/lib/abstract_controller/collector.rb +2 -2
  11. data/lib/abstract_controller/error.rb +1 -1
  12. data/lib/abstract_controller/helpers.rb +17 -12
  13. data/lib/abstract_controller/logger.rb +1 -1
  14. data/lib/abstract_controller/railties/routes_helpers.rb +2 -0
  15. data/lib/abstract_controller/rendering.rb +9 -11
  16. data/lib/abstract_controller/translation.rb +5 -4
  17. data/lib/abstract_controller/url_for.rb +4 -6
  18. data/lib/action_controller/api.rb +7 -7
  19. data/lib/action_controller/base.rb +5 -4
  20. data/lib/action_controller/form_builder.rb +2 -2
  21. data/lib/action_controller/log_subscriber.rb +4 -3
  22. data/lib/action_controller/metal/basic_implicit_render.rb +3 -1
  23. data/lib/action_controller/metal/conditional_get.rb +137 -102
  24. data/lib/action_controller/metal/content_security_policy.rb +36 -2
  25. data/lib/action_controller/metal/cookies.rb +1 -1
  26. data/lib/action_controller/metal/data_streaming.rb +23 -31
  27. data/lib/action_controller/metal/etag_with_flash.rb +1 -1
  28. data/lib/action_controller/metal/exceptions.rb +19 -30
  29. data/lib/action_controller/metal/flash.rb +6 -2
  30. data/lib/action_controller/metal/head.rb +1 -1
  31. data/lib/action_controller/metal/helpers.rb +2 -2
  32. data/lib/action_controller/metal/http_authentication.rb +66 -39
  33. data/lib/action_controller/metal/instrumentation.rb +57 -52
  34. data/lib/action_controller/metal/live.rb +43 -2
  35. data/lib/action_controller/metal/mime_responds.rb +3 -3
  36. data/lib/action_controller/metal/params_wrapper.rb +20 -11
  37. data/lib/action_controller/metal/permissions_policy.rb +19 -28
  38. data/lib/action_controller/metal/redirecting.rb +111 -19
  39. data/lib/action_controller/metal/renderers.rb +12 -13
  40. data/lib/action_controller/metal/rendering.rb +121 -9
  41. data/lib/action_controller/metal/request_forgery_protection.rb +83 -32
  42. data/lib/action_controller/metal/rescue.rb +5 -4
  43. data/lib/action_controller/metal/streaming.rb +7 -9
  44. data/lib/action_controller/metal/strong_parameters.rb +138 -115
  45. data/lib/action_controller/metal/testing.rb +9 -2
  46. data/lib/action_controller/metal/url_for.rb +3 -5
  47. data/lib/action_controller/metal.rb +10 -13
  48. data/lib/action_controller/railtie.rb +50 -6
  49. data/lib/action_controller/renderer.rb +1 -20
  50. data/lib/action_controller/test_case.rb +28 -7
  51. data/lib/action_controller.rb +2 -5
  52. data/lib/action_dispatch/http/cache.rb +20 -13
  53. data/lib/action_dispatch/http/content_security_policy.rb +113 -36
  54. data/lib/action_dispatch/http/filter_parameters.rb +4 -19
  55. data/lib/action_dispatch/http/headers.rb +1 -1
  56. data/lib/action_dispatch/http/mime_negotiation.rb +15 -5
  57. data/lib/action_dispatch/http/mime_type.rb +9 -11
  58. data/lib/action_dispatch/http/parameters.rb +5 -5
  59. data/lib/action_dispatch/http/permissions_policy.rb +17 -1
  60. data/lib/action_dispatch/http/request.rb +27 -37
  61. data/lib/action_dispatch/http/response.rb +3 -20
  62. data/lib/action_dispatch/http/upload.rb +13 -2
  63. data/lib/action_dispatch/http/url.rb +11 -19
  64. data/lib/action_dispatch/journey/gtg/builder.rb +11 -12
  65. data/lib/action_dispatch/journey/gtg/simulator.rb +10 -4
  66. data/lib/action_dispatch/journey/gtg/transition_table.rb +77 -21
  67. data/lib/action_dispatch/journey/nodes/node.rb +70 -5
  68. data/lib/action_dispatch/journey/path/pattern.rb +22 -13
  69. data/lib/action_dispatch/journey/route.rb +6 -13
  70. data/lib/action_dispatch/journey/router/utils.rb +2 -2
  71. data/lib/action_dispatch/journey/router.rb +1 -1
  72. data/lib/action_dispatch/journey/routes.rb +3 -3
  73. data/lib/action_dispatch/journey/visualizer/fsm.js +49 -24
  74. data/lib/action_dispatch/journey/visualizer/index.html.erb +1 -1
  75. data/lib/action_dispatch/middleware/actionable_exceptions.rb +0 -1
  76. data/lib/action_dispatch/middleware/cookies.rb +20 -13
  77. data/lib/action_dispatch/middleware/debug_exceptions.rb +6 -4
  78. data/lib/action_dispatch/middleware/debug_locks.rb +3 -3
  79. data/lib/action_dispatch/middleware/exception_wrapper.rb +4 -0
  80. data/lib/action_dispatch/middleware/executor.rb +3 -0
  81. data/lib/action_dispatch/middleware/flash.rb +17 -18
  82. data/lib/action_dispatch/middleware/host_authorization.rb +13 -17
  83. data/lib/action_dispatch/middleware/remote_ip.rb +20 -8
  84. data/lib/action_dispatch/middleware/request_id.rb +3 -3
  85. data/lib/action_dispatch/middleware/server_timing.rb +76 -0
  86. data/lib/action_dispatch/middleware/session/abstract_store.rb +1 -1
  87. data/lib/action_dispatch/middleware/session/cookie_store.rb +9 -9
  88. data/lib/action_dispatch/middleware/show_exceptions.rb +17 -16
  89. data/lib/action_dispatch/middleware/stack.rb +27 -9
  90. data/lib/action_dispatch/middleware/static.rb +5 -9
  91. data/lib/action_dispatch/middleware/templates/rescues/_message_and_suggestions.html.erb +1 -1
  92. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +4 -11
  93. data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +2 -2
  94. data/lib/action_dispatch/middleware/templates/rescues/blocked_host.html.erb +10 -5
  95. data/lib/action_dispatch/middleware/templates/rescues/blocked_host.text.erb +7 -3
  96. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +4 -4
  97. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +3 -3
  98. data/lib/action_dispatch/middleware/templates/rescues/layout.erb +28 -18
  99. data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.html.erb +3 -3
  100. data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +3 -3
  101. data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +3 -3
  102. data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +3 -3
  103. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +3 -3
  104. data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +22 -22
  105. data/lib/action_dispatch/railtie.rb +8 -2
  106. data/lib/action_dispatch/request/session.rb +43 -13
  107. data/lib/action_dispatch/routing/inspector.rb +1 -1
  108. data/lib/action_dispatch/routing/mapper.rb +82 -83
  109. data/lib/action_dispatch/routing/redirection.rb +5 -2
  110. data/lib/action_dispatch/routing/route_set.rb +17 -7
  111. data/lib/action_dispatch/routing/routes_proxy.rb +1 -1
  112. data/lib/action_dispatch/routing/url_for.rb +24 -25
  113. data/lib/action_dispatch/routing.rb +5 -6
  114. data/lib/action_dispatch/system_test_case.rb +5 -5
  115. data/lib/action_dispatch/system_testing/browser.rb +3 -13
  116. data/lib/action_dispatch/system_testing/driver.rb +34 -10
  117. data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +11 -7
  118. data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +0 -8
  119. data/lib/action_dispatch/testing/assertions/response.rb +1 -1
  120. data/lib/action_dispatch/testing/assertions/routing.rb +3 -2
  121. data/lib/action_dispatch/testing/assertions.rb +2 -5
  122. data/lib/action_dispatch/testing/integration.rb +6 -8
  123. data/lib/action_dispatch/testing/test_process.rb +3 -29
  124. data/lib/action_dispatch/testing/test_response.rb +20 -2
  125. data/lib/action_dispatch.rb +1 -0
  126. data/lib/action_pack/gem_version.rb +5 -5
  127. data/lib/action_pack/version.rb +1 -1
  128. 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
- @regexp_states = {}
15
- @string_states = {}
16
- @accepting = {}
17
- @memos = Hash.new { |h, k| h[k] = [] }
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, a)
48
+ def move(t, full_string, start_index, end_index)
45
49
  return [] if t.empty?
46
50
 
47
- regexps = []
48
- strings = []
51
+ next_states = []
49
52
 
50
- t.each { |s|
51
- if states = @regexp_states[s]
52
- states.each { |re, v| regexps << v if re.match?(a) && !v.nil? }
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
- if states = @string_states[s]
56
- strings << states[a] unless states[a].nil?
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
- strings.concat regexps
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: simple_regexp,
73
- string_states: @string_states,
74
- accepting: @accepting
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.map { |n|
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
- }.compact.join
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
- @regexp_states
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 :spec, :requirements, :anchored
7
+ attr_reader :ast, :names, :requirements, :anchored, :spec
8
8
 
9
9
  def initialize(ast, requirements, separators, anchored)
10
- @spec = ast
10
+ @ast = ast
11
+ @spec = ast.root
11
12
  @requirements = requirements
12
13
  @separators = separators
13
14
  @anchored = anchored
14
15
 
15
- @names = nil
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 ast
34
- @spec.find_all(&:symbol?).each do |node|
35
- re = @requirements[node.to_sym]
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
- @spec
40
- end
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
- def names
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.spec.any?(Nodes::Star)
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).to_a.map { |i| ENCODE % i }.map { |s| s.force_encoding(US_ASCII) }
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}:@\/\?]/.freeze
47
+ FRAGMENT = /[^#{UNRESERVED}#{SUB_DELIMS}:@\/?]/.freeze
48
48
  SEGMENT = /[^#{UNRESERVED}#{SUB_DELIMS}:@]/.freeze
49
49
  PATH = /[^#{UNRESERVED}#{SUB_DELIMS}:@\/]/.freeze
50
50
 
@@ -85,7 +85,7 @@ module ActionDispatch
85
85
  private
86
86
  def partitioned_routes
87
87
  routes.partition { |r|
88
- r.path.anchored && r.ast.grep(Nodes::Symbol).all? { |n| n.default_regexp? }
88
+ r.path.anchored && r.path.requirements_anchored?
89
89
  }
90
90
  end
91
91
 
@@ -41,7 +41,7 @@ module ActionDispatch
41
41
  end
42
42
 
43
43
  def partition_route(route)
44
- if route.path.anchored && route.ast.grep(Nodes::Symbol).all?(&:default_regexp?)
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
- asts = anchored_routes.map(&:ast)
54
- Nodes::Or.new(asts)
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('polygon')
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 = tt();
80
- var states = [0];
81
- var regexp_states = table['regexp_states'];
82
- var string_states = table['string_states'];
83
- var accepting = table['accepting'];
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 state = states[key];
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
- if(string_states[state] && string_states[state][token]) {
93
- var new_state = string_states[state][token];
94
- highlight_edge(state, new_state);
95
- highlight_state(new_state);
96
- new_states.push(new_state);
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
- if(re.test(token)) {
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 state = states[key];
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
- for(var mkey in svg_edges[state]) {
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" type="text/javascript"></script>
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">
@@ -2,7 +2,6 @@
2
2
 
3
3
  require "erb"
4
4
  require "uri"
5
- require "action_dispatch/http/request"
6
5
  require "active_support/actionable_error"
7
6
 
8
7
  module ActionDispatch
@@ -7,7 +7,7 @@ require "active_support/json"
7
7
  require "rack/utils"
8
8
 
9
9
  module ActionDispatch
10
- class Request
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
- # Read and write data to cookies through ActionController#cookies.
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 based. Other data types need to be serialized.
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 :domain when setting a cookie, you must also specify the domain when deleting the cookie:
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 #:nodoc:
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('.', -1)
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