actionpack 4.2.11.1 → 6.1.3.2

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 (187) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +291 -489
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +9 -9
  5. data/lib/abstract_controller/asset_paths.rb +2 -0
  6. data/lib/abstract_controller/base.rb +81 -51
  7. data/lib/{action_controller → abstract_controller}/caching/fragments.rb +64 -17
  8. data/lib/abstract_controller/caching.rb +66 -0
  9. data/lib/abstract_controller/callbacks.rb +61 -33
  10. data/lib/abstract_controller/collector.rb +9 -13
  11. data/lib/abstract_controller/error.rb +6 -0
  12. data/lib/abstract_controller/helpers.rb +115 -99
  13. data/lib/abstract_controller/logger.rb +2 -0
  14. data/lib/abstract_controller/railties/routes_helpers.rb +21 -3
  15. data/lib/abstract_controller/rendering.rb +48 -47
  16. data/lib/abstract_controller/translation.rb +17 -8
  17. data/lib/abstract_controller/url_for.rb +2 -0
  18. data/lib/abstract_controller.rb +13 -5
  19. data/lib/action_controller/api/api_rendering.rb +16 -0
  20. data/lib/action_controller/api.rb +150 -0
  21. data/lib/action_controller/base.rb +29 -24
  22. data/lib/action_controller/caching.rb +12 -57
  23. data/lib/action_controller/form_builder.rb +50 -0
  24. data/lib/action_controller/log_subscriber.rb +17 -19
  25. data/lib/action_controller/metal/basic_implicit_render.rb +13 -0
  26. data/lib/action_controller/metal/conditional_get.rb +134 -46
  27. data/lib/action_controller/metal/content_security_policy.rb +51 -0
  28. data/lib/action_controller/metal/cookies.rb +6 -4
  29. data/lib/action_controller/metal/data_streaming.rb +30 -50
  30. data/lib/action_controller/metal/default_headers.rb +17 -0
  31. data/lib/action_controller/metal/etag_with_flash.rb +18 -0
  32. data/lib/action_controller/metal/etag_with_template_digest.rb +21 -16
  33. data/lib/action_controller/metal/exceptions.rb +63 -15
  34. data/lib/action_controller/metal/flash.rb +9 -8
  35. data/lib/action_controller/metal/head.rb +26 -21
  36. data/lib/action_controller/metal/helpers.rb +37 -18
  37. data/lib/action_controller/metal/http_authentication.rb +81 -73
  38. data/lib/action_controller/metal/implicit_render.rb +53 -9
  39. data/lib/action_controller/metal/instrumentation.rb +32 -35
  40. data/lib/action_controller/metal/live.rb +102 -120
  41. data/lib/action_controller/metal/logging.rb +20 -0
  42. data/lib/action_controller/metal/mime_responds.rb +49 -47
  43. data/lib/action_controller/metal/parameter_encoding.rb +82 -0
  44. data/lib/action_controller/metal/params_wrapper.rb +83 -66
  45. data/lib/action_controller/metal/permissions_policy.rb +46 -0
  46. data/lib/action_controller/metal/redirecting.rb +53 -32
  47. data/lib/action_controller/metal/renderers.rb +87 -44
  48. data/lib/action_controller/metal/rendering.rb +77 -50
  49. data/lib/action_controller/metal/request_forgery_protection.rb +267 -103
  50. data/lib/action_controller/metal/rescue.rb +10 -17
  51. data/lib/action_controller/metal/streaming.rb +12 -11
  52. data/lib/action_controller/metal/strong_parameters.rb +714 -186
  53. data/lib/action_controller/metal/testing.rb +2 -17
  54. data/lib/action_controller/metal/url_for.rb +19 -10
  55. data/lib/action_controller/metal.rb +104 -87
  56. data/lib/action_controller/railtie.rb +28 -10
  57. data/lib/action_controller/railties/helpers.rb +3 -1
  58. data/lib/action_controller/renderer.rb +141 -0
  59. data/lib/action_controller/template_assertions.rb +11 -0
  60. data/lib/action_controller/test_case.rb +296 -422
  61. data/lib/action_controller.rb +34 -23
  62. data/lib/action_dispatch/http/cache.rb +107 -56
  63. data/lib/action_dispatch/http/content_disposition.rb +45 -0
  64. data/lib/action_dispatch/http/content_security_policy.rb +286 -0
  65. data/lib/action_dispatch/http/filter_parameters.rb +32 -25
  66. data/lib/action_dispatch/http/filter_redirect.rb +10 -12
  67. data/lib/action_dispatch/http/headers.rb +55 -22
  68. data/lib/action_dispatch/http/mime_negotiation.rb +79 -51
  69. data/lib/action_dispatch/http/mime_type.rb +153 -121
  70. data/lib/action_dispatch/http/mime_types.rb +20 -6
  71. data/lib/action_dispatch/http/parameters.rb +90 -40
  72. data/lib/action_dispatch/http/permissions_policy.rb +173 -0
  73. data/lib/action_dispatch/http/rack_cache.rb +2 -0
  74. data/lib/action_dispatch/http/request.rb +226 -121
  75. data/lib/action_dispatch/http/response.rb +248 -113
  76. data/lib/action_dispatch/http/upload.rb +21 -7
  77. data/lib/action_dispatch/http/url.rb +182 -100
  78. data/lib/action_dispatch/journey/formatter.rb +90 -43
  79. data/lib/action_dispatch/journey/gtg/builder.rb +28 -41
  80. data/lib/action_dispatch/journey/gtg/simulator.rb +11 -16
  81. data/lib/action_dispatch/journey/gtg/transition_table.rb +23 -21
  82. data/lib/action_dispatch/journey/nfa/dot.rb +3 -14
  83. data/lib/action_dispatch/journey/nodes/node.rb +29 -15
  84. data/lib/action_dispatch/journey/parser.rb +17 -16
  85. data/lib/action_dispatch/journey/parser.y +4 -3
  86. data/lib/action_dispatch/journey/parser_extras.rb +12 -4
  87. data/lib/action_dispatch/journey/path/pattern.rb +58 -54
  88. data/lib/action_dispatch/journey/route.rb +100 -32
  89. data/lib/action_dispatch/journey/router/utils.rb +29 -18
  90. data/lib/action_dispatch/journey/router.rb +55 -51
  91. data/lib/action_dispatch/journey/routes.rb +17 -17
  92. data/lib/action_dispatch/journey/scanner.rb +26 -17
  93. data/lib/action_dispatch/journey/visitors.rb +98 -54
  94. data/lib/action_dispatch/journey.rb +5 -5
  95. data/lib/action_dispatch/middleware/actionable_exceptions.rb +46 -0
  96. data/lib/action_dispatch/middleware/callbacks.rb +3 -6
  97. data/lib/action_dispatch/middleware/cookies.rb +347 -217
  98. data/lib/action_dispatch/middleware/debug_exceptions.rb +135 -63
  99. data/lib/action_dispatch/middleware/debug_locks.rb +124 -0
  100. data/lib/action_dispatch/middleware/debug_view.rb +66 -0
  101. data/lib/action_dispatch/middleware/exception_wrapper.rb +115 -71
  102. data/lib/action_dispatch/middleware/executor.rb +21 -0
  103. data/lib/action_dispatch/middleware/flash.rb +78 -54
  104. data/lib/action_dispatch/middleware/host_authorization.rb +130 -0
  105. data/lib/action_dispatch/middleware/public_exceptions.rb +32 -27
  106. data/lib/action_dispatch/middleware/reloader.rb +5 -91
  107. data/lib/action_dispatch/middleware/remote_ip.rb +53 -45
  108. data/lib/action_dispatch/middleware/request_id.rb +17 -10
  109. data/lib/action_dispatch/middleware/session/abstract_store.rb +41 -26
  110. data/lib/action_dispatch/middleware/session/cache_store.rb +24 -14
  111. data/lib/action_dispatch/middleware/session/cookie_store.rb +74 -75
  112. data/lib/action_dispatch/middleware/session/mem_cache_store.rb +8 -2
  113. data/lib/action_dispatch/middleware/show_exceptions.rb +28 -23
  114. data/lib/action_dispatch/middleware/ssl.rb +118 -35
  115. data/lib/action_dispatch/middleware/stack.rb +82 -41
  116. data/lib/action_dispatch/middleware/static.rb +156 -89
  117. data/lib/action_dispatch/middleware/templates/rescues/_actions.html.erb +13 -0
  118. data/lib/action_dispatch/middleware/templates/rescues/_actions.text.erb +0 -0
  119. data/lib/action_dispatch/middleware/templates/rescues/_message_and_suggestions.html.erb +22 -0
  120. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +4 -14
  121. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.text.erb +1 -1
  122. data/lib/action_dispatch/middleware/templates/rescues/{_source.erb → _source.html.erb} +4 -2
  123. data/lib/action_dispatch/middleware/templates/rescues/_source.text.erb +8 -0
  124. data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +45 -35
  125. data/lib/action_dispatch/middleware/templates/rescues/blocked_host.html.erb +7 -0
  126. data/lib/action_dispatch/middleware/templates/rescues/blocked_host.text.erb +5 -0
  127. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +23 -4
  128. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +1 -1
  129. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +24 -0
  130. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.text.erb +15 -0
  131. data/lib/action_dispatch/middleware/templates/rescues/layout.erb +105 -8
  132. data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.html.erb +19 -0
  133. data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.text.erb +3 -0
  134. data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +2 -2
  135. data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +1 -1
  136. data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +3 -3
  137. data/lib/action_dispatch/middleware/templates/rescues/template_error.text.erb +1 -1
  138. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +1 -1
  139. data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +4 -4
  140. data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +87 -64
  141. data/lib/action_dispatch/railtie.rb +27 -13
  142. data/lib/action_dispatch/request/session.rb +109 -61
  143. data/lib/action_dispatch/request/utils.rb +90 -23
  144. data/lib/action_dispatch/routing/endpoint.rb +9 -2
  145. data/lib/action_dispatch/routing/inspector.rb +141 -102
  146. data/lib/action_dispatch/routing/mapper.rb +811 -473
  147. data/lib/action_dispatch/routing/polymorphic_routes.rb +167 -143
  148. data/lib/action_dispatch/routing/redirection.rb +37 -27
  149. data/lib/action_dispatch/routing/route_set.rb +363 -331
  150. data/lib/action_dispatch/routing/routes_proxy.rb +32 -5
  151. data/lib/action_dispatch/routing/url_for.rb +66 -26
  152. data/lib/action_dispatch/routing.rb +36 -36
  153. data/lib/action_dispatch/system_test_case.rb +190 -0
  154. data/lib/action_dispatch/system_testing/browser.rb +86 -0
  155. data/lib/action_dispatch/system_testing/driver.rb +67 -0
  156. data/lib/action_dispatch/system_testing/server.rb +31 -0
  157. data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +138 -0
  158. data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +29 -0
  159. data/lib/action_dispatch/testing/assertion_response.rb +46 -0
  160. data/lib/action_dispatch/testing/assertions/response.rb +44 -22
  161. data/lib/action_dispatch/testing/assertions/routing.rb +47 -31
  162. data/lib/action_dispatch/testing/assertions.rb +6 -4
  163. data/lib/action_dispatch/testing/integration.rb +391 -220
  164. data/lib/action_dispatch/testing/request_encoder.rb +55 -0
  165. data/lib/action_dispatch/testing/test_process.rb +53 -22
  166. data/lib/action_dispatch/testing/test_request.rb +27 -34
  167. data/lib/action_dispatch/testing/test_response.rb +11 -11
  168. data/lib/action_dispatch.rb +35 -21
  169. data/lib/action_pack/gem_version.rb +6 -4
  170. data/lib/action_pack/version.rb +3 -1
  171. data/lib/action_pack.rb +4 -2
  172. metadata +78 -48
  173. data/lib/action_controller/metal/force_ssl.rb +0 -97
  174. data/lib/action_controller/metal/hide_actions.rb +0 -40
  175. data/lib/action_controller/metal/rack_delegation.rb +0 -32
  176. data/lib/action_controller/middleware.rb +0 -39
  177. data/lib/action_controller/model_naming.rb +0 -12
  178. data/lib/action_dispatch/http/parameter_filter.rb +0 -72
  179. data/lib/action_dispatch/journey/backwards.rb +0 -5
  180. data/lib/action_dispatch/journey/nfa/builder.rb +0 -76
  181. data/lib/action_dispatch/journey/nfa/simulator.rb +0 -47
  182. data/lib/action_dispatch/journey/nfa/transition_table.rb +0 -163
  183. data/lib/action_dispatch/journey/router/strexp.rb +0 -27
  184. data/lib/action_dispatch/middleware/params_parser.rb +0 -60
  185. data/lib/action_dispatch/testing/assertions/dom.rb +0 -3
  186. data/lib/action_dispatch/testing/assertions/selector.rb +0 -3
  187. data/lib/action_dispatch/testing/assertions/tag.rb +0 -3
@@ -1,5 +1,6 @@
1
- require 'active_support/core_ext/module/attribute_accessors'
2
- require 'active_support/core_ext/hash/slice'
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/module/attribute_accessors"
3
4
 
4
5
  module ActionDispatch
5
6
  module Http
@@ -8,14 +9,26 @@ module ActionDispatch
8
9
  HOST_REGEXP = /(^[^:]+:\/\/)?(\[[^\]]+\]|[^:]+)(?::(\d+$))?/
9
10
  PROTOCOL_REGEXP = /^([^:]+)(:)?(\/\/)?$/
10
11
 
11
- mattr_accessor :tld_length
12
- self.tld_length = 1
12
+ mattr_accessor :secure_protocol, default: false
13
+ mattr_accessor :tld_length, default: 1
13
14
 
14
15
  class << self
16
+ # Returns the domain part of a host given the domain level.
17
+ #
18
+ # # Top-level domain example
19
+ # extract_domain('www.example.com', 1) # => "example.com"
20
+ # # Second-level domain example
21
+ # extract_domain('dev.www.example.co.uk', 2) # => "example.co.uk"
15
22
  def extract_domain(host, tld_length)
16
23
  extract_domain_from(host, tld_length) if named_host?(host)
17
24
  end
18
25
 
26
+ # Returns the subdomains of a host as an Array given the domain level.
27
+ #
28
+ # # Top-level domain example
29
+ # extract_subdomains('www.example.com', 1) # => ["www"]
30
+ # # Second-level domain example
31
+ # extract_subdomains('dev.www.example.co.uk', 2) # => ["dev", "www"]
19
32
  def extract_subdomains(host, tld_length)
20
33
  if named_host?(host)
21
34
  extract_subdomains_from(host, tld_length)
@@ -24,8 +37,14 @@ module ActionDispatch
24
37
  end
25
38
  end
26
39
 
40
+ # Returns the subdomains of a host as a String given the domain level.
41
+ #
42
+ # # Top-level domain example
43
+ # extract_subdomain('www.example.com', 1) # => "www"
44
+ # # Second-level domain example
45
+ # extract_subdomain('dev.www.example.co.uk', 2) # => "dev.www"
27
46
  def extract_subdomain(host, tld_length)
28
- extract_subdomains(host, tld_length).join('.')
47
+ extract_subdomains(host, tld_length).join(".")
29
48
  end
30
49
 
31
50
  def url_for(options)
@@ -42,14 +61,14 @@ module ActionDispatch
42
61
  port = options[:port]
43
62
 
44
63
  unless host
45
- raise ArgumentError, 'Missing host to link to! Please provide the :host parameter, set default_url_options[:host], or set :only_path to true'
64
+ raise ArgumentError, "Missing host to link to! Please provide the :host parameter, set default_url_options[:host], or set :only_path to true"
46
65
  end
47
66
 
48
67
  build_host_url(host, port, protocol, options, path_for(options))
49
68
  end
50
69
 
51
70
  def path_for(options)
52
- path = options[:script_name].to_s.chomp("/")
71
+ path = options[:script_name].to_s.chomp("/")
53
72
  path << options[:path] if options.key?(:path)
54
73
 
55
74
  add_trailing_slash(path) if options[:trailing_slash]
@@ -60,149 +79,184 @@ module ActionDispatch
60
79
  end
61
80
 
62
81
  private
63
-
64
- def add_params(path, params)
65
- params = { params: params } unless params.is_a?(Hash)
66
- params.reject! { |_,v| v.to_param.nil? }
67
- path << "?#{params.to_query}" unless params.empty?
68
- end
69
-
70
- def add_anchor(path, anchor)
71
- if anchor
72
- 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?
73
87
  end
74
- end
75
88
 
76
- def extract_domain_from(host, tld_length)
77
- host.split('.').last(1 + tld_length).join('.')
78
- end
89
+ def add_anchor(path, anchor)
90
+ if anchor
91
+ path << "##{Journey::Router::Utils.escape_fragment(anchor.to_param)}"
92
+ end
93
+ end
79
94
 
80
- def extract_subdomains_from(host, tld_length)
81
- parts = host.split('.')
82
- parts[0..-(tld_length + 2)]
83
- end
95
+ def extract_domain_from(host, tld_length)
96
+ host.split(".").last(1 + tld_length).join(".")
97
+ end
84
98
 
85
- def add_trailing_slash(path)
86
- # includes querysting
87
- if path.include?('?')
88
- path.sub!(/\?/, '/\&')
89
- # does not have a .format
90
- elsif !path.include?(".")
91
- path.sub!(/[^\/]\z|\A\z/, '\&/')
99
+ def extract_subdomains_from(host, tld_length)
100
+ parts = host.split(".")
101
+ parts[0..-(tld_length + 2)]
92
102
  end
93
- end
94
103
 
95
- def build_host_url(host, port, protocol, options, path)
96
- if match = host.match(HOST_REGEXP)
97
- protocol ||= match[1] unless protocol == false
98
- host = match[2]
99
- 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
100
110
  end
101
111
 
102
- protocol = normalize_protocol protocol
103
- 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
104
118
 
105
- result = protocol.dup
119
+ protocol = normalize_protocol protocol
120
+ host = normalize_host(host, options)
106
121
 
107
- if options[:user] && options[:password]
108
- result << "#{Rack::Utils.escape(options[:user])}:#{Rack::Utils.escape(options[:password])}@"
109
- end
122
+ result = protocol.dup
110
123
 
111
- result << host
112
- normalize_port(port, protocol) { |normalized_port|
113
- result << ":#{normalized_port}"
114
- }
124
+ if options[:user] && options[:password]
125
+ result << "#{Rack::Utils.escape(options[:user])}:#{Rack::Utils.escape(options[:password])}@"
126
+ end
115
127
 
116
- result.concat path
117
- end
128
+ result << host
129
+ normalize_port(port, protocol) { |normalized_port|
130
+ result << ":#{normalized_port}"
131
+ }
118
132
 
119
- def named_host?(host)
120
- IP_HOST_REGEXP !~ host
121
- end
133
+ result.concat path
134
+ end
122
135
 
123
- def normalize_protocol(protocol)
124
- case protocol
125
- when nil
126
- "http://"
127
- when false, "//"
128
- "//"
129
- when PROTOCOL_REGEXP
130
- "#{$1}://"
131
- else
132
- raise ArgumentError, "Invalid :protocol option: #{protocol.inspect}"
136
+ def named_host?(host)
137
+ !IP_HOST_REGEXP.match?(host)
133
138
  end
134
- end
135
139
 
136
- def normalize_host(_host, options)
137
- return _host unless named_host?(_host)
140
+ def normalize_protocol(protocol)
141
+ case protocol
142
+ when nil
143
+ secure_protocol ? "https://" : "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
152
+
153
+ def normalize_host(_host, options)
154
+ return _host unless named_host?(_host)
138
155
 
139
- tld_length = options[:tld_length] || @@tld_length
140
- subdomain = options.fetch :subdomain, true
141
- domain = options[:domain]
156
+ tld_length = options[:tld_length] || @@tld_length
157
+ subdomain = options.fetch :subdomain, true
158
+ domain = options[:domain]
142
159
 
143
- host = ""
144
- if subdomain == true
145
- return _host if domain.nil?
160
+ host = +""
161
+ if subdomain == true
162
+ return _host if domain.nil?
146
163
 
147
- host << extract_subdomains_from(_host, tld_length).join('.')
148
- elsif subdomain
149
- host << subdomain.to_param
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
150
171
  end
151
- host << "." unless host.empty?
152
- host << (domain || extract_domain_from(_host, tld_length))
153
- host
154
- end
155
172
 
156
- def normalize_port(port, protocol)
157
- return unless port
173
+ def normalize_port(port, protocol)
174
+ return unless port
158
175
 
159
- case protocol
160
- when "//" then yield port
161
- when "https://"
162
- yield port unless port.to_i == 443
163
- else
164
- 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
165
183
  end
166
- end
167
184
  end
168
185
 
169
- def initialize(env)
186
+ def initialize
170
187
  super
171
188
  @protocol = nil
172
189
  @port = nil
173
190
  end
174
191
 
175
192
  # Returns the complete URL used for this request.
193
+ #
194
+ # req = ActionDispatch::Request.new 'HTTP_HOST' => 'example.com'
195
+ # req.url # => "http://example.com"
176
196
  def url
177
197
  protocol + host_with_port + fullpath
178
198
  end
179
199
 
180
200
  # Returns 'https://' if this is an SSL request and 'http://' otherwise.
201
+ #
202
+ # req = ActionDispatch::Request.new 'HTTP_HOST' => 'example.com'
203
+ # req.protocol # => "http://"
204
+ #
205
+ # req = ActionDispatch::Request.new 'HTTP_HOST' => 'example.com', 'HTTPS' => 'on'
206
+ # req.protocol # => "https://"
181
207
  def protocol
182
- @protocol ||= ssl? ? 'https://' : 'http://'
208
+ @protocol ||= ssl? ? "https://" : "http://"
183
209
  end
184
210
 
185
- # Returns the \host for this request, such as "example.com".
211
+ # Returns the \host and port for this request, such as "example.com:8080".
212
+ #
213
+ # req = ActionDispatch::Request.new 'HTTP_HOST' => 'example.com'
214
+ # req.raw_host_with_port # => "example.com"
215
+ #
216
+ # req = ActionDispatch::Request.new 'HTTP_HOST' => 'example.com:80'
217
+ # req.raw_host_with_port # => "example.com:80"
218
+ #
219
+ # req = ActionDispatch::Request.new 'HTTP_HOST' => 'example.com:8080'
220
+ # req.raw_host_with_port # => "example.com:8080"
186
221
  def raw_host_with_port
187
- if forwarded = env["HTTP_X_FORWARDED_HOST"].presence
222
+ if forwarded = x_forwarded_host.presence
188
223
  forwarded.split(/,\s?/).last
189
224
  else
190
- env['HTTP_HOST'] || "#{env['SERVER_NAME'] || env['SERVER_ADDR']}:#{env['SERVER_PORT']}"
225
+ get_header("HTTP_HOST") || "#{server_name || server_addr}:#{get_header('SERVER_PORT')}"
191
226
  end
192
227
  end
193
228
 
194
- # Returns the host for this request, such as example.com.
229
+ # Returns the host for this request, such as "example.com".
230
+ #
231
+ # req = ActionDispatch::Request.new 'HTTP_HOST' => 'example.com:8080'
232
+ # req.host # => "example.com"
195
233
  def host
196
- raw_host_with_port.sub(/:\d+$/, '')
234
+ raw_host_with_port.sub(/:\d+$/, "")
197
235
  end
198
236
 
199
237
  # Returns a \host:\port string for this request, such as "example.com" or
200
- # "example.com:8080".
238
+ # "example.com:8080". Port is only included if it is not a default port
239
+ # (80 or 443)
240
+ #
241
+ # req = ActionDispatch::Request.new 'HTTP_HOST' => 'example.com'
242
+ # req.host_with_port # => "example.com"
243
+ #
244
+ # req = ActionDispatch::Request.new 'HTTP_HOST' => 'example.com:80'
245
+ # req.host_with_port # => "example.com"
246
+ #
247
+ # req = ActionDispatch::Request.new 'HTTP_HOST' => 'example.com:8080'
248
+ # req.host_with_port # => "example.com:8080"
201
249
  def host_with_port
202
250
  "#{host}#{port_string}"
203
251
  end
204
252
 
205
253
  # Returns the port number of this request as an integer.
254
+ #
255
+ # req = ActionDispatch::Request.new 'HTTP_HOST' => 'example.com'
256
+ # req.port # => 80
257
+ #
258
+ # req = ActionDispatch::Request.new 'HTTP_HOST' => 'example.com:8080'
259
+ # req.port # => 8080
206
260
  def port
207
261
  @port ||= begin
208
262
  if raw_host_with_port =~ /:(\d+)$/
@@ -214,32 +268,60 @@ module ActionDispatch
214
268
  end
215
269
 
216
270
  # Returns the standard \port number for this request's protocol.
271
+ #
272
+ # req = ActionDispatch::Request.new 'HTTP_HOST' => 'example.com:8080'
273
+ # req.standard_port # => 80
217
274
  def standard_port
218
275
  case protocol
219
- when 'https://' then 443
220
- else 80
276
+ when "https://" then 443
277
+ else 80
221
278
  end
222
279
  end
223
280
 
224
281
  # Returns whether this request is using the standard port
282
+ #
283
+ # req = ActionDispatch::Request.new 'HTTP_HOST' => 'example.com:80'
284
+ # req.standard_port? # => true
285
+ #
286
+ # req = ActionDispatch::Request.new 'HTTP_HOST' => 'example.com:8080'
287
+ # req.standard_port? # => false
225
288
  def standard_port?
226
289
  port == standard_port
227
290
  end
228
291
 
229
292
  # Returns a number \port suffix like 8080 if the \port number of this request
230
293
  # is not the default HTTP \port 80 or HTTPS \port 443.
294
+ #
295
+ # req = ActionDispatch::Request.new 'HTTP_HOST' => 'example.com:80'
296
+ # req.optional_port # => nil
297
+ #
298
+ # req = ActionDispatch::Request.new 'HTTP_HOST' => 'example.com:8080'
299
+ # req.optional_port # => 8080
231
300
  def optional_port
232
301
  standard_port? ? nil : port
233
302
  end
234
303
 
235
304
  # Returns a string \port suffix, including colon, like ":8080" if the \port
236
305
  # number of this request is not the default HTTP \port 80 or HTTPS \port 443.
306
+ #
307
+ # req = ActionDispatch::Request.new 'HTTP_HOST' => 'example.com:80'
308
+ # req.port_string # => ""
309
+ #
310
+ # req = ActionDispatch::Request.new 'HTTP_HOST' => 'example.com:8080'
311
+ # req.port_string # => ":8080"
237
312
  def port_string
238
- standard_port? ? '' : ":#{port}"
313
+ standard_port? ? "" : ":#{port}"
239
314
  end
240
315
 
316
+ # Returns the requested port, such as 8080, based on SERVER_PORT
317
+ #
318
+ # req = ActionDispatch::Request.new 'SERVER_PORT' => '80'
319
+ # req.server_port # => 80
320
+ #
321
+ # req = ActionDispatch::Request.new 'SERVER_PORT' => '8080'
322
+ # req.server_port # => 8080
241
323
  def server_port
242
- @env['SERVER_PORT'].to_i
324
+ get_header("SERVER_PORT").to_i
243
325
  end
244
326
 
245
327
  # Returns the \domain part of a \host, such as "rubyonrails.org" in "www.rubyonrails.org". You can specify
@@ -1,11 +1,13 @@
1
- require 'action_controller/metal/exceptions'
2
- require 'active_support/deprecation'
1
+ # frozen_string_literal: true
2
+
3
+ require "action_controller/metal/exceptions"
3
4
 
4
5
  module ActionDispatch
6
+ # :stopdoc:
5
7
  module Journey
6
8
  # The Formatter class is used for formatting URLs. For example, parameters
7
9
  # passed to +url_for+ in Rails will eventually call Formatter#generate.
8
- class Formatter # :nodoc:
10
+ class Formatter
9
11
  attr_reader :routes
10
12
 
11
13
  def initialize(routes)
@@ -13,12 +15,53 @@ module ActionDispatch
13
15
  @cache = nil
14
16
  end
15
17
 
16
- def generate(name, options, path_parameters, parameterize = nil)
18
+ class RouteWithParams
19
+ attr_reader :params
20
+
21
+ def initialize(route, parameterized_parts, params)
22
+ @route = route
23
+ @parameterized_parts = parameterized_parts
24
+ @params = params
25
+ end
26
+
27
+ def path(_)
28
+ @route.format(@parameterized_parts)
29
+ end
30
+ end
31
+
32
+ class MissingRoute
33
+ attr_reader :routes, :name, :constraints, :missing_keys, :unmatched_keys
34
+
35
+ def initialize(constraints, missing_keys, unmatched_keys, routes, name)
36
+ @constraints = constraints
37
+ @missing_keys = missing_keys
38
+ @unmatched_keys = unmatched_keys
39
+ @routes = routes
40
+ @name = name
41
+ end
42
+
43
+ def path(method_name)
44
+ raise ActionController::UrlGenerationError.new(message, routes, name, method_name)
45
+ end
46
+
47
+ def params
48
+ path("unknown")
49
+ end
50
+
51
+ def message
52
+ message = +"No route matches #{Hash[constraints.sort_by { |k, v| k.to_s }].inspect}"
53
+ message << ", missing required keys: #{missing_keys.sort.inspect}" if missing_keys && !missing_keys.empty?
54
+ message << ", possible unmatched constraints: #{unmatched_keys.sort.inspect}" if unmatched_keys && !unmatched_keys.empty?
55
+ message
56
+ end
57
+ end
58
+
59
+ def generate(name, options, path_parameters)
17
60
  constraints = path_parameters.merge(options)
18
- missing_keys = []
61
+ missing_keys = nil
19
62
 
20
63
  match_route(name, constraints) do |route|
21
- parameterized_parts = extract_parameterized_parts(route, options, path_parameters, parameterize)
64
+ parameterized_parts = extract_parameterized_parts(route, options, path_parameters)
22
65
 
23
66
  # Skip this route unless a name has been provided or it is a
24
67
  # standard Rails route since we can't determine whether an options
@@ -26,24 +69,29 @@ module ActionDispatch
26
69
  next unless name || route.dispatcher?
27
70
 
28
71
  missing_keys = missing_keys(route, parameterized_parts)
29
- next unless missing_keys.empty?
72
+ next if missing_keys && !missing_keys.empty?
30
73
  params = options.dup.delete_if do |key, _|
31
74
  parameterized_parts.key?(key) || route.defaults.key?(key)
32
75
  end
33
76
 
34
77
  defaults = route.defaults
35
78
  required_parts = route.required_parts
36
- parameterized_parts.delete_if do |key, value|
37
- value.to_s == defaults[key].to_s && !required_parts.include?(key)
79
+
80
+ route.parts.reverse_each do |key|
81
+ break if defaults[key].nil? && parameterized_parts[key].present?
82
+ next if parameterized_parts[key].to_s != defaults[key].to_s
83
+ break if required_parts.include?(key)
84
+
85
+ parameterized_parts.delete(key)
38
86
  end
39
87
 
40
- return [route.format(parameterized_parts), params]
88
+ return RouteWithParams.new(route, parameterized_parts, params)
41
89
  end
42
90
 
43
- message = "No route matches #{Hash[constraints.sort_by{|k,v| k.to_s}].inspect}"
44
- message << " missing required keys: #{missing_keys.sort.inspect}" unless missing_keys.empty?
91
+ unmatched_keys = (missing_keys || []) & constraints.keys
92
+ missing_keys = (missing_keys || []) - unmatched_keys
45
93
 
46
- raise ActionController::UrlGenerationError, message
94
+ MissingRoute.new(constraints, missing_keys, unmatched_keys, routes, name)
47
95
  end
48
96
 
49
97
  def clear
@@ -51,21 +99,22 @@ module ActionDispatch
51
99
  end
52
100
 
53
101
  private
54
-
55
- def extract_parameterized_parts(route, options, recall, parameterize = nil)
102
+ def extract_parameterized_parts(route, options, recall)
56
103
  parameterized_parts = recall.merge(options)
57
104
 
58
- keys_to_keep = route.parts.reverse.drop_while { |part|
59
- !options.key?(part) || (options[part] || recall[part]).nil?
105
+ keys_to_keep = route.parts.reverse_each.drop_while { |part|
106
+ !(options.key?(part) || route.scope_options.key?(part)) || (options[part] || recall[part]).nil?
60
107
  } | route.required_parts
61
108
 
62
- (parameterized_parts.keys - keys_to_keep).each do |bad_key|
63
- parameterized_parts.delete(bad_key)
109
+ parameterized_parts.delete_if do |bad_key, _|
110
+ !keys_to_keep.include?(bad_key)
64
111
  end
65
112
 
66
- if parameterize
67
- parameterized_parts.each do |k, v|
68
- parameterized_parts[k] = parameterize.call(k, v)
113
+ parameterized_parts.each do |k, v|
114
+ if k == :controller
115
+ parameterized_parts[k] = v
116
+ else
117
+ parameterized_parts[k] = v.to_param
69
118
  end
70
119
  end
71
120
 
@@ -81,28 +130,18 @@ module ActionDispatch
81
130
  if named_routes.key?(name)
82
131
  yield named_routes[name]
83
132
  else
84
- # Make sure we don't show the deprecation warning more than once
85
- warned = false
86
-
87
133
  routes = non_recursive(cache, options)
88
134
 
89
- hash = routes.group_by { |_, r| r.score(options) }
135
+ supplied_keys = options.each_with_object({}) do |(k, v), h|
136
+ h[k.to_s] = true if v
137
+ end
138
+
139
+ hash = routes.group_by { |_, r| r.score(supplied_keys) }
90
140
 
91
141
  hash.keys.sort.reverse_each do |score|
92
142
  break if score < 0
93
143
 
94
144
  hash[score].sort_by { |i, _| i }.each do |_, route|
95
- if name && !warned
96
- ActiveSupport::Deprecation.warn <<-MSG.squish
97
- You are trying to generate the URL for a named route called
98
- #{name.inspect} but no such route was found. In the future,
99
- this will result in an `ActionController::UrlGenerationError`
100
- exception.
101
- MSG
102
-
103
- warned = true
104
- end
105
-
106
145
  yield route
107
146
  end
108
147
  end
@@ -127,13 +166,20 @@ module ActionDispatch
127
166
 
128
167
  # Returns an array populated with missing keys if any are present.
129
168
  def missing_keys(route, parts)
130
- missing_keys = []
131
- tests = route.path.requirements
169
+ missing_keys = nil
170
+ tests = route.path.requirements_for_missing_keys_check
132
171
  route.required_parts.each { |key|
133
- if tests.key?(key)
134
- missing_keys << key unless /\A#{tests[key]}\Z/ === parts[key]
172
+ case tests[key]
173
+ when nil
174
+ unless parts[key]
175
+ missing_keys ||= []
176
+ missing_keys << key
177
+ end
135
178
  else
136
- missing_keys << key unless parts[key]
179
+ unless tests[key].match?(parts[key])
180
+ missing_keys ||= []
181
+ missing_keys << key
182
+ end
137
183
  end
138
184
  }
139
185
  missing_keys
@@ -149,7 +195,7 @@ module ActionDispatch
149
195
 
150
196
  def build_cache
151
197
  root = { ___routes: [] }
152
- routes.each_with_index do |route, i|
198
+ routes.routes.each_with_index do |route, i|
153
199
  leaf = route.required_defaults.inject(root) do |h, tuple|
154
200
  h[tuple] ||= {}
155
201
  end
@@ -163,4 +209,5 @@ module ActionDispatch
163
209
  end
164
210
  end
165
211
  end
212
+ # :startdoc:
166
213
  end