actionpack 5.2.8.1 → 6.0.0.beta1

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 (108) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +109 -482
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +1 -1
  5. data/lib/abstract_controller/base.rb +4 -2
  6. data/lib/abstract_controller/caching/fragments.rb +6 -21
  7. data/lib/abstract_controller/callbacks.rb +12 -0
  8. data/lib/abstract_controller/collector.rb +1 -1
  9. data/lib/abstract_controller/helpers.rb +2 -2
  10. data/lib/abstract_controller/railties/routes_helpers.rb +1 -1
  11. data/lib/action_controller/api.rb +2 -1
  12. data/lib/action_controller/base.rb +2 -7
  13. data/lib/action_controller/caching.rb +1 -1
  14. data/lib/action_controller/log_subscriber.rb +8 -5
  15. data/lib/action_controller/metal/conditional_get.rb +9 -3
  16. data/lib/action_controller/metal/data_streaming.rb +5 -6
  17. data/lib/action_controller/metal/default_headers.rb +17 -0
  18. data/lib/action_controller/metal/exceptions.rb +22 -1
  19. data/lib/action_controller/metal/flash.rb +5 -5
  20. data/lib/action_controller/metal/force_ssl.rb +17 -57
  21. data/lib/action_controller/metal/head.rb +1 -1
  22. data/lib/action_controller/metal/helpers.rb +1 -2
  23. data/lib/action_controller/metal/http_authentication.rb +21 -22
  24. data/lib/action_controller/metal/implicit_render.rb +2 -12
  25. data/lib/action_controller/metal/instrumentation.rb +3 -5
  26. data/lib/action_controller/metal/live.rb +28 -26
  27. data/lib/action_controller/metal/mime_responds.rb +13 -2
  28. data/lib/action_controller/metal/params_wrapper.rb +18 -14
  29. data/lib/action_controller/metal/redirecting.rb +32 -11
  30. data/lib/action_controller/metal/rendering.rb +1 -1
  31. data/lib/action_controller/metal/request_forgery_protection.rb +32 -97
  32. data/lib/action_controller/metal/strong_parameters.rb +57 -34
  33. data/lib/action_controller/metal/url_for.rb +1 -1
  34. data/lib/action_controller/metal.rb +2 -2
  35. data/lib/action_controller/railties/helpers.rb +1 -1
  36. data/lib/action_controller/renderer.rb +15 -2
  37. data/lib/action_controller/test_case.rb +5 -9
  38. data/lib/action_controller.rb +1 -0
  39. data/lib/action_dispatch/http/cache.rb +14 -10
  40. data/lib/action_dispatch/http/content_disposition.rb +45 -0
  41. data/lib/action_dispatch/http/content_security_policy.rb +17 -8
  42. data/lib/action_dispatch/http/filter_parameters.rb +8 -6
  43. data/lib/action_dispatch/http/filter_redirect.rb +1 -1
  44. data/lib/action_dispatch/http/headers.rb +1 -1
  45. data/lib/action_dispatch/http/mime_negotiation.rb +7 -10
  46. data/lib/action_dispatch/http/mime_type.rb +1 -5
  47. data/lib/action_dispatch/http/parameter_filter.rb +5 -79
  48. data/lib/action_dispatch/http/parameters.rb +13 -3
  49. data/lib/action_dispatch/http/request.rb +10 -13
  50. data/lib/action_dispatch/http/response.rb +14 -14
  51. data/lib/action_dispatch/http/upload.rb +5 -0
  52. data/lib/action_dispatch/http/url.rb +81 -81
  53. data/lib/action_dispatch/journey/formatter.rb +1 -1
  54. data/lib/action_dispatch/journey/nfa/simulator.rb +0 -2
  55. data/lib/action_dispatch/journey/nodes/node.rb +9 -8
  56. data/lib/action_dispatch/journey/path/pattern.rb +3 -4
  57. data/lib/action_dispatch/journey/router/utils.rb +10 -10
  58. data/lib/action_dispatch/journey/router.rb +0 -3
  59. data/lib/action_dispatch/journey/scanner.rb +11 -4
  60. data/lib/action_dispatch/journey/visitors.rb +1 -1
  61. data/lib/action_dispatch/middleware/callbacks.rb +2 -4
  62. data/lib/action_dispatch/middleware/cookies.rb +49 -70
  63. data/lib/action_dispatch/middleware/debug_exceptions.rb +32 -58
  64. data/lib/action_dispatch/middleware/debug_locks.rb +5 -5
  65. data/lib/action_dispatch/middleware/debug_view.rb +50 -0
  66. data/lib/action_dispatch/middleware/exception_wrapper.rb +36 -7
  67. data/lib/action_dispatch/middleware/executor.rb +1 -1
  68. data/lib/action_dispatch/middleware/flash.rb +1 -1
  69. data/lib/action_dispatch/middleware/host_authorization.rb +103 -0
  70. data/lib/action_dispatch/middleware/remote_ip.rb +6 -8
  71. data/lib/action_dispatch/middleware/request_id.rb +2 -2
  72. data/lib/action_dispatch/middleware/session/abstract_store.rb +0 -14
  73. data/lib/action_dispatch/middleware/session/cache_store.rb +6 -11
  74. data/lib/action_dispatch/middleware/session/cookie_store.rb +11 -27
  75. data/lib/action_dispatch/middleware/ssl.rb +8 -8
  76. data/lib/action_dispatch/middleware/stack.rb +2 -2
  77. data/lib/action_dispatch/middleware/static.rb +5 -6
  78. data/lib/action_dispatch/middleware/templates/rescues/_source.html.erb +4 -2
  79. data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +45 -35
  80. data/lib/action_dispatch/middleware/templates/rescues/blocked_host.html.erb +7 -0
  81. data/lib/action_dispatch/middleware/templates/rescues/blocked_host.text.erb +5 -0
  82. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +20 -2
  83. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +4 -4
  84. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.text.erb +2 -2
  85. data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.html.erb +19 -0
  86. data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.text.erb +3 -0
  87. data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +2 -2
  88. data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +1 -1
  89. data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +2 -2
  90. data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +3 -0
  91. data/lib/action_dispatch/railtie.rb +1 -0
  92. data/lib/action_dispatch/request/session.rb +8 -6
  93. data/lib/action_dispatch/routing/inspector.rb +99 -50
  94. data/lib/action_dispatch/routing/mapper.rb +36 -29
  95. data/lib/action_dispatch/routing/polymorphic_routes.rb +7 -12
  96. data/lib/action_dispatch/routing/route_set.rb +11 -12
  97. data/lib/action_dispatch/routing/url_for.rb +1 -0
  98. data/lib/action_dispatch/routing.rb +3 -2
  99. data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +3 -3
  100. data/lib/action_dispatch/testing/assertions/response.rb +2 -3
  101. data/lib/action_dispatch/testing/assertions/routing.rb +7 -2
  102. data/lib/action_dispatch/testing/integration.rb +11 -5
  103. data/lib/action_dispatch/testing/test_process.rb +2 -2
  104. data/lib/action_dispatch/testing/test_response.rb +4 -32
  105. data/lib/action_dispatch.rb +7 -6
  106. data/lib/action_pack/gem_version.rb +4 -4
  107. data/lib/action_pack.rb +1 -1
  108. metadata +25 -23
@@ -1,86 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "active_support/core_ext/object/duplicable"
3
+ require "active_support/deprecation/constant_accessor"
4
+ require "active_support/parameter_filter"
4
5
 
5
6
  module ActionDispatch
6
7
  module Http
7
- class ParameterFilter
8
- FILTERED = "[FILTERED]".freeze # :nodoc:
9
-
10
- def initialize(filters = [])
11
- @filters = filters
12
- end
13
-
14
- def filter(params)
15
- compiled_filter.call(params)
16
- end
17
-
18
- private
19
-
20
- def compiled_filter
21
- @compiled_filter ||= CompiledFilter.compile(@filters)
22
- end
23
-
24
- class CompiledFilter # :nodoc:
25
- def self.compile(filters)
26
- return lambda { |params| params.dup } if filters.empty?
27
-
28
- strings, regexps, blocks = [], [], []
29
-
30
- filters.each do |item|
31
- case item
32
- when Proc
33
- blocks << item
34
- when Regexp
35
- regexps << item
36
- else
37
- strings << Regexp.escape(item.to_s)
38
- end
39
- end
40
-
41
- deep_regexps, regexps = regexps.partition { |r| r.to_s.include?("\\.".freeze) }
42
- deep_strings, strings = strings.partition { |s| s.include?("\\.".freeze) }
43
-
44
- regexps << Regexp.new(strings.join("|".freeze), true) unless strings.empty?
45
- deep_regexps << Regexp.new(deep_strings.join("|".freeze), true) unless deep_strings.empty?
46
-
47
- new regexps, deep_regexps, blocks
48
- end
49
-
50
- attr_reader :regexps, :deep_regexps, :blocks
51
-
52
- def initialize(regexps, deep_regexps, blocks)
53
- @regexps = regexps
54
- @deep_regexps = deep_regexps.any? ? deep_regexps : nil
55
- @blocks = blocks
56
- end
57
-
58
- def call(original_params, parents = [])
59
- filtered_params = original_params.class.new
60
-
61
- original_params.each do |key, value|
62
- parents.push(key) if deep_regexps
63
- if regexps.any? { |r| key =~ r }
64
- value = FILTERED
65
- elsif deep_regexps && (joined = parents.join(".")) && deep_regexps.any? { |r| joined =~ r }
66
- value = FILTERED
67
- elsif value.is_a?(Hash)
68
- value = call(value, parents)
69
- elsif value.is_a?(Array)
70
- value = value.map { |v| v.is_a?(Hash) ? call(v, parents) : v }
71
- elsif blocks.any?
72
- key = key.dup if key.duplicable?
73
- value = value.dup if value.duplicable?
74
- blocks.each { |b| b.call(key, value) }
75
- end
76
- parents.pop if deep_regexps
77
-
78
- filtered_params[key] = value
79
- end
80
-
81
- filtered_params
82
- end
83
- end
84
- end
8
+ include ActiveSupport::Deprecation::DeprecatedConstantAccessor
9
+ deprecate_constant "ParameterFilter", "ActiveSupport::ParameterFilter",
10
+ message: "ActionDispatch::Http::ParameterFilter is deprecated and will be removed from Rails 6.1. Use ActiveSupport::ParameterFilter instead."
85
11
  end
86
12
  end
@@ -111,13 +111,23 @@ module ActionDispatch
111
111
  begin
112
112
  strategy.call(raw_post)
113
113
  rescue # JSON or Ruby code block errors.
114
- my_logger = logger || ActiveSupport::Logger.new($stderr)
115
- my_logger.debug "Error occurred while parsing request parameters.\nContents:\n\n#{raw_post}"
116
-
114
+ log_parse_error_once
117
115
  raise ParseError
118
116
  end
119
117
  end
120
118
 
119
+ def log_parse_error_once
120
+ @parse_error_logged ||= begin
121
+ parse_logger = logger || ActiveSupport::Logger.new($stderr)
122
+ parse_logger.debug <<~MSG.chomp
123
+ Error occurred while parsing request parameters.
124
+ Contents:
125
+
126
+ #{raw_post}
127
+ MSG
128
+ end
129
+ end
130
+
121
131
  def params_parsers
122
132
  ActionDispatch::Request.parameter_parsers
123
133
  end
@@ -136,11 +136,11 @@ module ActionDispatch
136
136
  end
137
137
 
138
138
  def routes # :nodoc:
139
- get_header("action_dispatch.routes".freeze)
139
+ get_header("action_dispatch.routes")
140
140
  end
141
141
 
142
142
  def routes=(routes) # :nodoc:
143
- set_header("action_dispatch.routes".freeze, routes)
143
+ set_header("action_dispatch.routes", routes)
144
144
  end
145
145
 
146
146
  def engine_script_name(_routes) # :nodoc:
@@ -158,11 +158,11 @@ module ActionDispatch
158
158
  end
159
159
 
160
160
  def controller_instance # :nodoc:
161
- get_header("action_controller.instance".freeze)
161
+ get_header("action_controller.instance")
162
162
  end
163
163
 
164
164
  def controller_instance=(controller) # :nodoc:
165
- set_header("action_controller.instance".freeze, controller)
165
+ set_header("action_controller.instance", controller)
166
166
  end
167
167
 
168
168
  def http_auth_salt
@@ -173,7 +173,7 @@ module ActionDispatch
173
173
  # We're treating `nil` as "unset", and we want the default setting to be
174
174
  # `true`. This logic should be extracted to `env_config` and calculated
175
175
  # once.
176
- !(get_header("action_dispatch.show_exceptions".freeze) == false)
176
+ !(get_header("action_dispatch.show_exceptions") == false)
177
177
  end
178
178
 
179
179
  # Returns a symbol form of the #request_method.
@@ -280,10 +280,10 @@ module ActionDispatch
280
280
  end
281
281
 
282
282
  def remote_ip=(remote_ip)
283
- set_header "action_dispatch.remote_ip".freeze, remote_ip
283
+ set_header "action_dispatch.remote_ip", remote_ip
284
284
  end
285
285
 
286
- ACTION_DISPATCH_REQUEST_ID = "action_dispatch.request_id".freeze # :nodoc:
286
+ ACTION_DISPATCH_REQUEST_ID = "action_dispatch.request_id" # :nodoc:
287
287
 
288
288
  # Returns the unique request id, which is based on either the X-Request-Id header that can
289
289
  # be generated by a firewall, load balancer, or web server or by the RequestId middleware
@@ -383,9 +383,6 @@ module ActionDispatch
383
383
  end
384
384
  self.request_parameters = Request::Utils.normalize_encode_params(pr)
385
385
  end
386
- rescue Http::Parameters::ParseError # one of the parse strategies blew up
387
- self.request_parameters = Request::Utils.normalize_encode_params(super || {})
388
- raise
389
386
  rescue Rack::Utils::ParameterTypeError, Rack::Utils::InvalidParameterError => e
390
387
  raise ActionController::BadRequest.new("Invalid request parameters: #{e.message}")
391
388
  end
@@ -407,18 +404,18 @@ module ActionDispatch
407
404
 
408
405
  def request_parameters=(params)
409
406
  raise if params.nil?
410
- set_header("action_dispatch.request.request_parameters".freeze, params)
407
+ set_header("action_dispatch.request.request_parameters", params)
411
408
  end
412
409
 
413
410
  def logger
414
- get_header("action_dispatch.logger".freeze)
411
+ get_header("action_dispatch.logger")
415
412
  end
416
413
 
417
414
  def commit_flash
418
415
  end
419
416
 
420
417
  def ssl?
421
- super || scheme == "wss".freeze
418
+ super || scheme == "wss"
422
419
  end
423
420
 
424
421
  private
@@ -78,9 +78,9 @@ module ActionDispatch # :nodoc:
78
78
  x
79
79
  end
80
80
 
81
- CONTENT_TYPE = "Content-Type".freeze
82
- SET_COOKIE = "Set-Cookie".freeze
83
- LOCATION = "Location".freeze
81
+ CONTENT_TYPE = "Content-Type"
82
+ SET_COOKIE = "Set-Cookie"
83
+ LOCATION = "Location"
84
84
  NO_CONTENT_CODES = [100, 101, 102, 204, 205, 304]
85
85
 
86
86
  cattr_accessor :default_charset, default: "utf-8"
@@ -105,7 +105,7 @@ module ActionDispatch # :nodoc:
105
105
 
106
106
  def body
107
107
  @str_body ||= begin
108
- buf = "".dup
108
+ buf = +""
109
109
  each { |chunk| buf << chunk }
110
110
  buf
111
111
  end
@@ -224,16 +224,6 @@ module ActionDispatch # :nodoc:
224
224
  @status = Rack::Utils.status_code(status)
225
225
  end
226
226
 
227
- # Sets the HTTP content type.
228
- def content_type=(content_type)
229
- return unless content_type
230
- new_header_info = parse_content_type(content_type.to_s)
231
- prev_header_info = parsed_content_type_header
232
- charset = new_header_info.charset || prev_header_info.charset
233
- charset ||= self.class.default_charset unless prev_header_info.mime_type
234
- set_content_type new_header_info.mime_type, charset
235
- end
236
-
237
227
  # Sets the HTTP response's content MIME type. For example, in the controller
238
228
  # you could write this:
239
229
  #
@@ -242,7 +232,17 @@ module ActionDispatch # :nodoc:
242
232
  # If a character set has been defined for this response (see charset=) then
243
233
  # the character set information will also be included in the content type
244
234
  # information.
235
+ def content_type=(content_type)
236
+ return unless content_type
237
+ new_header_info = parse_content_type(content_type.to_s)
238
+ prev_header_info = parsed_content_type_header
239
+ charset = new_header_info.charset || prev_header_info.charset
240
+ charset ||= self.class.default_charset unless prev_header_info.mime_type
241
+ set_content_type new_header_info.mime_type, charset
242
+ end
245
243
 
244
+ # Content type of response.
245
+ # It returns just MIME type and does NOT contain charset part.
246
246
  def content_type
247
247
  parsed_content_type_header.mime_type
248
248
  end
@@ -65,6 +65,11 @@ module ActionDispatch
65
65
  @tempfile.path
66
66
  end
67
67
 
68
+ # Shortcut for +tempfile.to_path+.
69
+ def to_path
70
+ @tempfile.to_path
71
+ end
72
+
68
73
  # Shortcut for +tempfile.rewind+.
69
74
  def rewind
70
75
  @tempfile.rewind
@@ -67,7 +67,7 @@ module ActionDispatch
67
67
  end
68
68
 
69
69
  def path_for(options)
70
- path = options[:script_name].to_s.chomp("/".freeze)
70
+ path = options[:script_name].to_s.chomp("/")
71
71
  path << options[:path] if options.key?(:path)
72
72
 
73
73
  add_trailing_slash(path) if options[:trailing_slash]
@@ -79,108 +79,108 @@ module ActionDispatch
79
79
 
80
80
  private
81
81
 
82
- def add_params(path, params)
83
- params = { params: params } unless params.is_a?(Hash)
84
- params.reject! { |_, v| v.to_param.nil? }
85
- query = params.to_query
86
- path << "?#{query}" unless query.empty?
87
- end
88
-
89
- def add_anchor(path, anchor)
90
- if anchor
91
- path << "##{Journey::Router::Utils.escape_fragment(anchor.to_param)}"
82
+ def add_params(path, params)
83
+ params = { params: params } unless params.is_a?(Hash)
84
+ params.reject! { |_, v| v.to_param.nil? }
85
+ query = params.to_query
86
+ path << "?#{query}" unless query.empty?
92
87
  end
93
- end
94
88
 
95
- def extract_domain_from(host, tld_length)
96
- host.split(".").last(1 + tld_length).join(".")
97
- end
89
+ def add_anchor(path, anchor)
90
+ if anchor
91
+ path << "##{Journey::Router::Utils.escape_fragment(anchor.to_param)}"
92
+ end
93
+ end
98
94
 
99
- def extract_subdomains_from(host, tld_length)
100
- parts = host.split(".")
101
- parts[0..-(tld_length + 2)]
102
- end
95
+ def extract_domain_from(host, tld_length)
96
+ host.split(".").last(1 + tld_length).join(".")
97
+ end
103
98
 
104
- def add_trailing_slash(path)
105
- if path.include?("?")
106
- path.sub!(/\?/, '/\&')
107
- elsif !path.include?(".")
108
- path.sub!(/[^\/]\z|\A\z/, '\&/')
99
+ def extract_subdomains_from(host, tld_length)
100
+ parts = host.split(".")
101
+ parts[0..-(tld_length + 2)]
109
102
  end
110
- end
111
103
 
112
- def build_host_url(host, port, protocol, options, path)
113
- if match = host.match(HOST_REGEXP)
114
- protocol ||= match[1] unless protocol == false
115
- host = match[2]
116
- port = match[3] unless options.key? :port
104
+ def add_trailing_slash(path)
105
+ if path.include?("?")
106
+ path.sub!(/\?/, '/\&')
107
+ elsif !path.include?(".")
108
+ path.sub!(/[^\/]\z|\A\z/, '\&/')
109
+ end
117
110
  end
118
111
 
119
- protocol = normalize_protocol protocol
120
- host = normalize_host(host, options)
112
+ def build_host_url(host, port, protocol, options, path)
113
+ if match = host.match(HOST_REGEXP)
114
+ protocol ||= match[1] unless protocol == false
115
+ host = match[2]
116
+ port = match[3] unless options.key? :port
117
+ end
121
118
 
122
- result = protocol.dup
119
+ protocol = normalize_protocol protocol
120
+ host = normalize_host(host, options)
123
121
 
124
- if options[:user] && options[:password]
125
- result << "#{Rack::Utils.escape(options[:user])}:#{Rack::Utils.escape(options[:password])}@"
126
- end
122
+ result = protocol.dup
127
123
 
128
- result << host
129
- normalize_port(port, protocol) { |normalized_port|
130
- result << ":#{normalized_port}"
131
- }
124
+ if options[:user] && options[:password]
125
+ result << "#{Rack::Utils.escape(options[:user])}:#{Rack::Utils.escape(options[:password])}@"
126
+ end
132
127
 
133
- result.concat path
134
- end
128
+ result << host
129
+ normalize_port(port, protocol) { |normalized_port|
130
+ result << ":#{normalized_port}"
131
+ }
135
132
 
136
- def named_host?(host)
137
- IP_HOST_REGEXP !~ host
138
- end
133
+ result.concat path
134
+ end
139
135
 
140
- def normalize_protocol(protocol)
141
- case protocol
142
- when nil
143
- "http://"
144
- when false, "//"
145
- "//"
146
- when PROTOCOL_REGEXP
147
- "#{$1}://"
148
- else
149
- raise ArgumentError, "Invalid :protocol option: #{protocol.inspect}"
136
+ def named_host?(host)
137
+ IP_HOST_REGEXP !~ host
150
138
  end
151
- end
152
139
 
153
- def normalize_host(_host, options)
154
- return _host unless named_host?(_host)
140
+ def normalize_protocol(protocol)
141
+ case protocol
142
+ when nil
143
+ "http://"
144
+ when false, "//"
145
+ "//"
146
+ when PROTOCOL_REGEXP
147
+ "#{$1}://"
148
+ else
149
+ raise ArgumentError, "Invalid :protocol option: #{protocol.inspect}"
150
+ end
151
+ end
155
152
 
156
- tld_length = options[:tld_length] || @@tld_length
157
- subdomain = options.fetch :subdomain, true
158
- domain = options[:domain]
153
+ def normalize_host(_host, options)
154
+ return _host unless named_host?(_host)
159
155
 
160
- host = "".dup
161
- if subdomain == true
162
- return _host if domain.nil?
156
+ tld_length = options[:tld_length] || @@tld_length
157
+ subdomain = options.fetch :subdomain, true
158
+ domain = options[:domain]
163
159
 
164
- host << extract_subdomains_from(_host, tld_length).join(".")
165
- elsif subdomain
166
- host << subdomain.to_param
160
+ host = +""
161
+ if subdomain == true
162
+ return _host if domain.nil?
163
+
164
+ host << extract_subdomains_from(_host, tld_length).join(".")
165
+ elsif subdomain
166
+ host << subdomain.to_param
167
+ end
168
+ host << "." unless host.empty?
169
+ host << (domain || extract_domain_from(_host, tld_length))
170
+ host
167
171
  end
168
- host << "." unless host.empty?
169
- host << (domain || extract_domain_from(_host, tld_length))
170
- host
171
- end
172
172
 
173
- def normalize_port(port, protocol)
174
- return unless port
173
+ def normalize_port(port, protocol)
174
+ return unless port
175
175
 
176
- case protocol
177
- when "//" then yield port
178
- when "https://"
179
- yield port unless port.to_i == 443
180
- else
181
- yield port unless port.to_i == 80
176
+ case protocol
177
+ when "//" then yield port
178
+ when "https://"
179
+ yield port unless port.to_i == 443
180
+ else
181
+ yield port unless port.to_i == 80
182
+ end
182
183
  end
183
- end
184
184
  end
185
185
 
186
186
  def initialize
@@ -231,7 +231,7 @@ module ActionDispatch
231
231
  # req = ActionDispatch::Request.new 'HTTP_HOST' => 'example.com:8080'
232
232
  # req.host # => "example.com"
233
233
  def host
234
- raw_host_with_port.sub(/:\d+$/, "".freeze)
234
+ raw_host_with_port.sub(/:\d+$/, "")
235
235
  end
236
236
 
237
237
  # Returns a \host:\port string for this request, such as "example.com" or
@@ -50,7 +50,7 @@ module ActionDispatch
50
50
  unmatched_keys = (missing_keys || []) & constraints.keys
51
51
  missing_keys = (missing_keys || []) - unmatched_keys
52
52
 
53
- message = "No route matches #{Hash[constraints.sort_by { |k, v| k.to_s }].inspect}".dup
53
+ message = +"No route matches #{Hash[constraints.sort_by { |k, v| k.to_s }].inspect}"
54
54
  message << ", missing required keys: #{missing_keys.sort.inspect}" if missing_keys && !missing_keys.empty?
55
55
  message << ", possible unmatched constraints: #{unmatched_keys.sort.inspect}" if unmatched_keys && !unmatched_keys.empty?
56
56
 
@@ -25,8 +25,6 @@ module ActionDispatch
25
25
  state = tt.eclosure(0)
26
26
  until input.eos?
27
27
  sym = input.scan(%r([/.?]|[^/.?]+))
28
-
29
- # FIXME: tt.eclosure is not needed for the GTG
30
28
  state = tt.eclosure(tt.move(state, sym))
31
29
  end
32
30
 
@@ -32,7 +32,7 @@ module ActionDispatch
32
32
  end
33
33
 
34
34
  def name
35
- left.tr "*:".freeze, "".freeze
35
+ -left.tr("*:", "")
36
36
  end
37
37
 
38
38
  def type
@@ -65,12 +65,12 @@ module ActionDispatch
65
65
  def literal?; false; end
66
66
  end
67
67
 
68
- %w{ Symbol Slash Dot }.each do |t|
69
- class_eval <<-eoruby, __FILE__, __LINE__ + 1
70
- class #{t} < Terminal;
71
- def type; :#{t.upcase}; end
72
- end
73
- eoruby
68
+ class Slash < Terminal # :nodoc:
69
+ def type; :SLASH; end
70
+ end
71
+
72
+ class Dot < Terminal # :nodoc:
73
+ def type; :DOT; end
74
74
  end
75
75
 
76
76
  class Symbol < Terminal # :nodoc:
@@ -82,13 +82,14 @@ module ActionDispatch
82
82
  def initialize(left)
83
83
  super
84
84
  @regexp = DEFAULT_EXP
85
- @name = left.tr "*:".freeze, "".freeze
85
+ @name = -left.tr("*:", "")
86
86
  end
87
87
 
88
88
  def default_regexp?
89
89
  regexp == DEFAULT_EXP
90
90
  end
91
91
 
92
+ def type; :SYMBOL; end
92
93
  def symbol?; true; end
93
94
  end
94
95
 
@@ -90,7 +90,7 @@ module ActionDispatch
90
90
  return @separator_re unless @matchers.key?(node)
91
91
 
92
92
  re = @matchers[node]
93
- "(#{re})"
93
+ "(#{Regexp.union(re)})"
94
94
  end
95
95
 
96
96
  def visit_GROUP(node)
@@ -119,8 +119,7 @@ module ActionDispatch
119
119
 
120
120
  class UnanchoredRegexp < AnchoredRegexp # :nodoc:
121
121
  def accept(node)
122
- path = visit node
123
- path == "/" ? %r{\A/} : %r{\A#{path}(?:\b|\Z|/)}
122
+ %r{\A#{visit node}}
124
123
  end
125
124
  end
126
125
 
@@ -184,7 +183,7 @@ module ActionDispatch
184
183
  node = node.to_sym
185
184
 
186
185
  if @requirements.key?(node)
187
- re = /#{@requirements[node]}|/
186
+ re = /#{Regexp.union(@requirements[node])}|/
188
187
  @offsets.push((re.match("").length - 1) + @offsets.last)
189
188
  else
190
189
  @offsets << @offsets.last
@@ -17,11 +17,11 @@ module ActionDispatch
17
17
  def self.normalize_path(path)
18
18
  path ||= ""
19
19
  encoding = path.encoding
20
- path = "/#{path}".dup
21
- path.squeeze!("/".freeze)
22
- path.sub!(%r{/+\Z}, "".freeze)
20
+ path = +"/#{path}"
21
+ path.squeeze!("/")
22
+ path.sub!(%r{/+\Z}, "")
23
23
  path.gsub!(/(%[a-f0-9]{2})/) { $1.upcase }
24
- path = "/".dup if path == "".freeze
24
+ path = +"/" if path == ""
25
25
  path.force_encoding(encoding)
26
26
  path
27
27
  end
@@ -29,16 +29,16 @@ module ActionDispatch
29
29
  # URI path and fragment escaping
30
30
  # https://tools.ietf.org/html/rfc3986
31
31
  class UriEncoder # :nodoc:
32
- ENCODE = "%%%02X".freeze
32
+ ENCODE = "%%%02X"
33
33
  US_ASCII = Encoding::US_ASCII
34
34
  UTF_8 = Encoding::UTF_8
35
- EMPTY = "".dup.force_encoding(US_ASCII).freeze
35
+ EMPTY = (+"").force_encoding(US_ASCII).freeze
36
36
  DEC2HEX = (0..255).to_a.map { |i| ENCODE % i }.map { |s| s.force_encoding(US_ASCII) }
37
37
 
38
- ALPHA = "a-zA-Z".freeze
39
- DIGIT = "0-9".freeze
40
- UNRESERVED = "#{ALPHA}#{DIGIT}\\-\\._~".freeze
41
- SUB_DELIMS = "!\\$&'\\(\\)\\*\\+,;=".freeze
38
+ ALPHA = "a-zA-Z"
39
+ DIGIT = "0-9"
40
+ UNRESERVED = "#{ALPHA}#{DIGIT}\\-\\._~"
41
+ SUB_DELIMS = "!\\$&'\\(\\)\\*\\+,;="
42
42
 
43
43
  ESCAPED = /%[a-zA-Z0-9]{2}/.freeze
44
44
 
@@ -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)
@@ -34,6 +34,13 @@ module ActionDispatch
34
34
 
35
35
  private
36
36
 
37
+ # takes advantage of String @- deduping capabilities in Ruby 2.5 upwards
38
+ # see: https://bugs.ruby-lang.org/issues/13077
39
+ def dedup_scan(regex)
40
+ r = @ss.scan(regex)
41
+ r ? -r : nil
42
+ end
43
+
37
44
  def scan
38
45
  case
39
46
  # /
@@ -47,15 +54,15 @@ module ActionDispatch
47
54
  [:OR, "|"]
48
55
  when @ss.skip(/\./)
49
56
  [:DOT, "."]
50
- when text = @ss.scan(/:\w+/)
57
+ when text = dedup_scan(/:\w+/)
51
58
  [:SYMBOL, text]
52
- when text = @ss.scan(/\*\w+/)
59
+ when text = dedup_scan(/\*\w+/)
53
60
  [:STAR, text]
54
61
  when text = @ss.scan(/(?:[\w%\-~!$&'*+,;=@]|\\[:()])+/)
55
62
  text.tr! "\\", ""
56
- [:LITERAL, text]
63
+ [:LITERAL, -text]
57
64
  # any char
58
- when text = @ss.scan(/./)
65
+ when text = dedup_scan(/./)
59
66
  [:LITERAL, text]
60
67
  end
61
68
  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 "".freeze unless value
43
+ return "" unless value
44
44
  parts[index] = param.escape value
45
45
  end
46
46