actionpack 3.2.22.5 → 5.2.4

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 (271) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +279 -603
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +13 -297
  5. data/lib/abstract_controller/asset_paths.rb +4 -2
  6. data/lib/abstract_controller/base.rb +82 -52
  7. data/lib/abstract_controller/caching/fragments.rb +166 -0
  8. data/lib/abstract_controller/caching.rb +66 -0
  9. data/lib/abstract_controller/callbacks.rb +117 -103
  10. data/lib/abstract_controller/collector.rb +18 -7
  11. data/lib/abstract_controller/error.rb +6 -0
  12. data/lib/abstract_controller/helpers.rb +65 -38
  13. data/lib/abstract_controller/logger.rb +3 -2
  14. data/lib/abstract_controller/railties/routes_helpers.rb +5 -3
  15. data/lib/abstract_controller/rendering.rb +77 -129
  16. data/lib/abstract_controller/translation.rb +21 -3
  17. data/lib/abstract_controller/url_for.rb +9 -7
  18. data/lib/abstract_controller.rb +12 -13
  19. data/lib/action_controller/api/api_rendering.rb +16 -0
  20. data/lib/action_controller/api.rb +149 -0
  21. data/lib/action_controller/base.rb +81 -40
  22. data/lib/action_controller/caching.rb +22 -62
  23. data/lib/action_controller/form_builder.rb +50 -0
  24. data/lib/action_controller/log_subscriber.rb +30 -18
  25. data/lib/action_controller/metal/basic_implicit_render.rb +13 -0
  26. data/lib/action_controller/metal/conditional_get.rb +190 -47
  27. data/lib/action_controller/metal/content_security_policy.rb +52 -0
  28. data/lib/action_controller/metal/cookies.rb +3 -3
  29. data/lib/action_controller/metal/data_streaming.rb +40 -65
  30. data/lib/action_controller/metal/etag_with_flash.rb +18 -0
  31. data/lib/action_controller/metal/etag_with_template_digest.rb +57 -0
  32. data/lib/action_controller/metal/exceptions.rb +19 -12
  33. data/lib/action_controller/metal/flash.rb +42 -9
  34. data/lib/action_controller/metal/force_ssl.rb +79 -19
  35. data/lib/action_controller/metal/head.rb +35 -10
  36. data/lib/action_controller/metal/helpers.rb +31 -21
  37. data/lib/action_controller/metal/http_authentication.rb +182 -134
  38. data/lib/action_controller/metal/implicit_render.rb +62 -8
  39. data/lib/action_controller/metal/instrumentation.rb +28 -26
  40. data/lib/action_controller/metal/live.rb +312 -0
  41. data/lib/action_controller/metal/mime_responds.rb +159 -163
  42. data/lib/action_controller/metal/parameter_encoding.rb +51 -0
  43. data/lib/action_controller/metal/params_wrapper.rb +146 -93
  44. data/lib/action_controller/metal/redirecting.rb +80 -56
  45. data/lib/action_controller/metal/renderers.rb +119 -47
  46. data/lib/action_controller/metal/rendering.rb +89 -32
  47. data/lib/action_controller/metal/request_forgery_protection.rb +373 -41
  48. data/lib/action_controller/metal/rescue.rb +9 -16
  49. data/lib/action_controller/metal/streaming.rb +39 -45
  50. data/lib/action_controller/metal/strong_parameters.rb +1086 -0
  51. data/lib/action_controller/metal/testing.rb +8 -29
  52. data/lib/action_controller/metal/url_for.rb +43 -32
  53. data/lib/action_controller/metal.rb +112 -106
  54. data/lib/action_controller/railtie.rb +56 -18
  55. data/lib/action_controller/railties/helpers.rb +24 -0
  56. data/lib/action_controller/renderer.rb +117 -0
  57. data/lib/action_controller/template_assertions.rb +11 -0
  58. data/lib/action_controller/test_case.rb +402 -347
  59. data/lib/action_controller.rb +31 -30
  60. data/lib/action_dispatch/http/cache.rb +133 -34
  61. data/lib/action_dispatch/http/content_security_policy.rb +272 -0
  62. data/lib/action_dispatch/http/filter_parameters.rb +40 -24
  63. data/lib/action_dispatch/http/filter_redirect.rb +37 -0
  64. data/lib/action_dispatch/http/headers.rb +117 -16
  65. data/lib/action_dispatch/http/mime_negotiation.rb +98 -33
  66. data/lib/action_dispatch/http/mime_type.rb +198 -146
  67. data/lib/action_dispatch/http/mime_types.rb +22 -7
  68. data/lib/action_dispatch/http/parameter_filter.rb +61 -49
  69. data/lib/action_dispatch/http/parameters.rb +94 -51
  70. data/lib/action_dispatch/http/rack_cache.rb +4 -3
  71. data/lib/action_dispatch/http/request.rb +262 -117
  72. data/lib/action_dispatch/http/response.rb +400 -86
  73. data/lib/action_dispatch/http/upload.rb +66 -29
  74. data/lib/action_dispatch/http/url.rb +232 -60
  75. data/lib/action_dispatch/journey/formatter.rb +189 -0
  76. data/lib/action_dispatch/journey/gtg/builder.rb +164 -0
  77. data/lib/action_dispatch/journey/gtg/simulator.rb +41 -0
  78. data/lib/action_dispatch/journey/gtg/transition_table.rb +158 -0
  79. data/lib/action_dispatch/journey/nfa/builder.rb +78 -0
  80. data/lib/action_dispatch/journey/nfa/dot.rb +36 -0
  81. data/lib/action_dispatch/journey/nfa/simulator.rb +49 -0
  82. data/lib/action_dispatch/journey/nfa/transition_table.rb +120 -0
  83. data/lib/action_dispatch/journey/nodes/node.rb +140 -0
  84. data/lib/action_dispatch/journey/parser.rb +199 -0
  85. data/lib/action_dispatch/journey/parser.y +50 -0
  86. data/lib/action_dispatch/journey/parser_extras.rb +31 -0
  87. data/lib/action_dispatch/journey/path/pattern.rb +199 -0
  88. data/lib/action_dispatch/journey/route.rb +203 -0
  89. data/lib/action_dispatch/journey/router/utils.rb +102 -0
  90. data/lib/action_dispatch/journey/router.rb +156 -0
  91. data/lib/action_dispatch/journey/routes.rb +82 -0
  92. data/lib/action_dispatch/journey/scanner.rb +64 -0
  93. data/lib/action_dispatch/journey/visitors.rb +268 -0
  94. data/lib/action_dispatch/journey/visualizer/fsm.css +30 -0
  95. data/lib/action_dispatch/journey/visualizer/fsm.js +134 -0
  96. data/lib/action_dispatch/journey/visualizer/index.html.erb +52 -0
  97. data/lib/action_dispatch/journey.rb +7 -0
  98. data/lib/action_dispatch/middleware/callbacks.rb +17 -13
  99. data/lib/action_dispatch/middleware/cookies.rb +494 -162
  100. data/lib/action_dispatch/middleware/debug_exceptions.rb +176 -53
  101. data/lib/action_dispatch/middleware/debug_locks.rb +124 -0
  102. data/lib/action_dispatch/middleware/exception_wrapper.rb +103 -38
  103. data/lib/action_dispatch/middleware/executor.rb +21 -0
  104. data/lib/action_dispatch/middleware/flash.rb +128 -91
  105. data/lib/action_dispatch/middleware/public_exceptions.rb +43 -16
  106. data/lib/action_dispatch/middleware/reloader.rb +6 -83
  107. data/lib/action_dispatch/middleware/remote_ip.rb +151 -49
  108. data/lib/action_dispatch/middleware/request_id.rb +19 -15
  109. data/lib/action_dispatch/middleware/session/abstract_store.rb +38 -34
  110. data/lib/action_dispatch/middleware/session/cache_store.rb +14 -9
  111. data/lib/action_dispatch/middleware/session/cookie_store.rb +94 -44
  112. data/lib/action_dispatch/middleware/session/mem_cache_store.rb +15 -4
  113. data/lib/action_dispatch/middleware/show_exceptions.rb +36 -61
  114. data/lib/action_dispatch/middleware/ssl.rb +150 -0
  115. data/lib/action_dispatch/middleware/stack.rb +33 -41
  116. data/lib/action_dispatch/middleware/static.rb +92 -48
  117. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +22 -0
  118. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.text.erb +23 -0
  119. data/lib/action_dispatch/middleware/templates/rescues/_source.html.erb +27 -0
  120. data/lib/action_dispatch/middleware/templates/rescues/_source.text.erb +8 -0
  121. data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +52 -0
  122. data/lib/action_dispatch/middleware/templates/rescues/_trace.text.erb +9 -0
  123. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +16 -0
  124. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +9 -0
  125. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +21 -0
  126. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.text.erb +13 -0
  127. data/lib/action_dispatch/middleware/templates/rescues/layout.erb +134 -5
  128. data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +11 -0
  129. data/lib/action_dispatch/middleware/templates/rescues/missing_template.text.erb +3 -0
  130. data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +32 -0
  131. data/lib/action_dispatch/middleware/templates/rescues/routing_error.text.erb +11 -0
  132. data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +20 -0
  133. data/lib/action_dispatch/middleware/templates/rescues/template_error.text.erb +7 -0
  134. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +6 -0
  135. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.text.erb +3 -0
  136. data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +16 -0
  137. data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +200 -0
  138. data/lib/action_dispatch/railtie.rb +29 -8
  139. data/lib/action_dispatch/request/session.rb +234 -0
  140. data/lib/action_dispatch/request/utils.rb +78 -0
  141. data/lib/action_dispatch/routing/endpoint.rb +17 -0
  142. data/lib/action_dispatch/routing/inspector.rb +225 -0
  143. data/lib/action_dispatch/routing/mapper.rb +1329 -582
  144. data/lib/action_dispatch/routing/polymorphic_routes.rb +237 -94
  145. data/lib/action_dispatch/routing/redirection.rb +120 -50
  146. data/lib/action_dispatch/routing/route_set.rb +545 -322
  147. data/lib/action_dispatch/routing/routes_proxy.rb +37 -7
  148. data/lib/action_dispatch/routing/url_for.rb +103 -34
  149. data/lib/action_dispatch/routing.rb +66 -99
  150. data/lib/action_dispatch/system_test_case.rb +147 -0
  151. data/lib/action_dispatch/system_testing/browser.rb +49 -0
  152. data/lib/action_dispatch/system_testing/driver.rb +59 -0
  153. data/lib/action_dispatch/system_testing/server.rb +31 -0
  154. data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +96 -0
  155. data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +31 -0
  156. data/lib/action_dispatch/system_testing/test_helpers/undef_methods.rb +26 -0
  157. data/lib/action_dispatch/testing/assertion_response.rb +47 -0
  158. data/lib/action_dispatch/testing/assertions/response.rb +53 -42
  159. data/lib/action_dispatch/testing/assertions/routing.rb +79 -74
  160. data/lib/action_dispatch/testing/assertions.rb +15 -9
  161. data/lib/action_dispatch/testing/integration.rb +361 -207
  162. data/lib/action_dispatch/testing/request_encoder.rb +55 -0
  163. data/lib/action_dispatch/testing/test_process.rb +28 -19
  164. data/lib/action_dispatch/testing/test_request.rb +30 -33
  165. data/lib/action_dispatch/testing/test_response.rb +35 -11
  166. data/lib/action_dispatch.rb +42 -32
  167. data/lib/action_pack/gem_version.rb +17 -0
  168. data/lib/action_pack/version.rb +7 -7
  169. data/lib/action_pack.rb +4 -2
  170. metadata +116 -175
  171. data/lib/abstract_controller/layouts.rb +0 -423
  172. data/lib/abstract_controller/view_paths.rb +0 -96
  173. data/lib/action_controller/caching/actions.rb +0 -185
  174. data/lib/action_controller/caching/fragments.rb +0 -127
  175. data/lib/action_controller/caching/pages.rb +0 -187
  176. data/lib/action_controller/caching/sweeping.rb +0 -97
  177. data/lib/action_controller/deprecated/integration_test.rb +0 -2
  178. data/lib/action_controller/deprecated/performance_test.rb +0 -1
  179. data/lib/action_controller/deprecated.rb +0 -3
  180. data/lib/action_controller/metal/compatibility.rb +0 -65
  181. data/lib/action_controller/metal/hide_actions.rb +0 -41
  182. data/lib/action_controller/metal/rack_delegation.rb +0 -26
  183. data/lib/action_controller/metal/responder.rb +0 -286
  184. data/lib/action_controller/metal/session_management.rb +0 -14
  185. data/lib/action_controller/middleware.rb +0 -39
  186. data/lib/action_controller/railties/paths.rb +0 -25
  187. data/lib/action_controller/record_identifier.rb +0 -85
  188. data/lib/action_controller/vendor/html-scanner/html/document.rb +0 -68
  189. data/lib/action_controller/vendor/html-scanner/html/node.rb +0 -532
  190. data/lib/action_controller/vendor/html-scanner/html/sanitizer.rb +0 -177
  191. data/lib/action_controller/vendor/html-scanner/html/selector.rb +0 -830
  192. data/lib/action_controller/vendor/html-scanner/html/tokenizer.rb +0 -107
  193. data/lib/action_controller/vendor/html-scanner/html/version.rb +0 -11
  194. data/lib/action_controller/vendor/html-scanner.rb +0 -20
  195. data/lib/action_dispatch/middleware/best_standards_support.rb +0 -30
  196. data/lib/action_dispatch/middleware/body_proxy.rb +0 -30
  197. data/lib/action_dispatch/middleware/head.rb +0 -18
  198. data/lib/action_dispatch/middleware/params_parser.rb +0 -75
  199. data/lib/action_dispatch/middleware/rescue.rb +0 -26
  200. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb +0 -31
  201. data/lib/action_dispatch/middleware/templates/rescues/_trace.erb +0 -26
  202. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.erb +0 -10
  203. data/lib/action_dispatch/middleware/templates/rescues/missing_template.erb +0 -2
  204. data/lib/action_dispatch/middleware/templates/rescues/routing_error.erb +0 -15
  205. data/lib/action_dispatch/middleware/templates/rescues/template_error.erb +0 -17
  206. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.erb +0 -2
  207. data/lib/action_dispatch/testing/assertions/dom.rb +0 -37
  208. data/lib/action_dispatch/testing/assertions/selector.rb +0 -435
  209. data/lib/action_dispatch/testing/assertions/tag.rb +0 -138
  210. data/lib/action_dispatch/testing/performance_test.rb +0 -10
  211. data/lib/action_view/asset_paths.rb +0 -142
  212. data/lib/action_view/base.rb +0 -220
  213. data/lib/action_view/buffers.rb +0 -43
  214. data/lib/action_view/context.rb +0 -36
  215. data/lib/action_view/flows.rb +0 -79
  216. data/lib/action_view/helpers/active_model_helper.rb +0 -50
  217. data/lib/action_view/helpers/asset_paths.rb +0 -7
  218. data/lib/action_view/helpers/asset_tag_helper.rb +0 -457
  219. data/lib/action_view/helpers/asset_tag_helpers/asset_include_tag.rb +0 -146
  220. data/lib/action_view/helpers/asset_tag_helpers/asset_paths.rb +0 -93
  221. data/lib/action_view/helpers/asset_tag_helpers/javascript_tag_helpers.rb +0 -193
  222. data/lib/action_view/helpers/asset_tag_helpers/stylesheet_tag_helpers.rb +0 -148
  223. data/lib/action_view/helpers/atom_feed_helper.rb +0 -200
  224. data/lib/action_view/helpers/cache_helper.rb +0 -64
  225. data/lib/action_view/helpers/capture_helper.rb +0 -203
  226. data/lib/action_view/helpers/controller_helper.rb +0 -25
  227. data/lib/action_view/helpers/csrf_helper.rb +0 -32
  228. data/lib/action_view/helpers/date_helper.rb +0 -1062
  229. data/lib/action_view/helpers/debug_helper.rb +0 -40
  230. data/lib/action_view/helpers/form_helper.rb +0 -1486
  231. data/lib/action_view/helpers/form_options_helper.rb +0 -658
  232. data/lib/action_view/helpers/form_tag_helper.rb +0 -685
  233. data/lib/action_view/helpers/javascript_helper.rb +0 -110
  234. data/lib/action_view/helpers/number_helper.rb +0 -622
  235. data/lib/action_view/helpers/output_safety_helper.rb +0 -38
  236. data/lib/action_view/helpers/record_tag_helper.rb +0 -111
  237. data/lib/action_view/helpers/rendering_helper.rb +0 -92
  238. data/lib/action_view/helpers/sanitize_helper.rb +0 -259
  239. data/lib/action_view/helpers/tag_helper.rb +0 -167
  240. data/lib/action_view/helpers/text_helper.rb +0 -426
  241. data/lib/action_view/helpers/translation_helper.rb +0 -91
  242. data/lib/action_view/helpers/url_helper.rb +0 -693
  243. data/lib/action_view/helpers.rb +0 -60
  244. data/lib/action_view/locale/en.yml +0 -160
  245. data/lib/action_view/log_subscriber.rb +0 -28
  246. data/lib/action_view/lookup_context.rb +0 -258
  247. data/lib/action_view/path_set.rb +0 -101
  248. data/lib/action_view/railtie.rb +0 -55
  249. data/lib/action_view/renderer/abstract_renderer.rb +0 -41
  250. data/lib/action_view/renderer/partial_renderer.rb +0 -415
  251. data/lib/action_view/renderer/renderer.rb +0 -61
  252. data/lib/action_view/renderer/streaming_template_renderer.rb +0 -106
  253. data/lib/action_view/renderer/template_renderer.rb +0 -95
  254. data/lib/action_view/template/error.rb +0 -128
  255. data/lib/action_view/template/handlers/builder.rb +0 -26
  256. data/lib/action_view/template/handlers/erb.rb +0 -125
  257. data/lib/action_view/template/handlers.rb +0 -50
  258. data/lib/action_view/template/resolver.rb +0 -298
  259. data/lib/action_view/template/text.rb +0 -30
  260. data/lib/action_view/template.rb +0 -337
  261. data/lib/action_view/test_case.rb +0 -246
  262. data/lib/action_view/testing/resolvers.rb +0 -49
  263. data/lib/action_view.rb +0 -84
  264. data/lib/sprockets/assets.rake +0 -99
  265. data/lib/sprockets/bootstrap.rb +0 -37
  266. data/lib/sprockets/compressors.rb +0 -83
  267. data/lib/sprockets/helpers/isolated_helper.rb +0 -13
  268. data/lib/sprockets/helpers/rails_helper.rb +0 -182
  269. data/lib/sprockets/helpers.rb +0 -6
  270. data/lib/sprockets/railtie.rb +0 -62
  271. data/lib/sprockets/static_compiler.rb +0 -56
@@ -1,118 +1,262 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/module/attribute_accessors"
4
+
1
5
  module ActionDispatch
2
6
  module Http
3
7
  module URL
4
- mattr_accessor :tld_length
5
- self.tld_length = 1
8
+ IP_HOST_REGEXP = /\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/
9
+ HOST_REGEXP = /(^[^:]+:\/\/)?(\[[^\]]+\]|[^:]+)(?::(\d+$))?/
10
+ PROTOCOL_REGEXP = /^([^:]+)(:)?(\/\/)?$/
11
+
12
+ mattr_accessor :tld_length, default: 1
6
13
 
7
14
  class << self
8
- def extract_domain(host, tld_length = @@tld_length)
9
- return nil unless named_host?(host)
10
- host.split('.').last(1 + tld_length).join('.')
15
+ # Returns the domain part of a host given the domain level.
16
+ #
17
+ # # Top-level domain example
18
+ # extract_domain('www.example.com', 1) # => "example.com"
19
+ # # Second-level domain example
20
+ # extract_domain('dev.www.example.co.uk', 2) # => "example.co.uk"
21
+ def extract_domain(host, tld_length)
22
+ extract_domain_from(host, tld_length) if named_host?(host)
11
23
  end
12
24
 
13
- def extract_subdomains(host, tld_length = @@tld_length)
14
- return [] unless named_host?(host)
15
- parts = host.split('.')
16
- parts[0..-(tld_length+2)]
25
+ # Returns the subdomains of a host as an Array given the domain level.
26
+ #
27
+ # # Top-level domain example
28
+ # extract_subdomains('www.example.com', 1) # => ["www"]
29
+ # # Second-level domain example
30
+ # extract_subdomains('dev.www.example.co.uk', 2) # => ["dev", "www"]
31
+ def extract_subdomains(host, tld_length)
32
+ if named_host?(host)
33
+ extract_subdomains_from(host, tld_length)
34
+ else
35
+ []
36
+ end
17
37
  end
18
38
 
19
- def extract_subdomain(host, tld_length = @@tld_length)
20
- extract_subdomains(host, tld_length).join('.')
39
+ # Returns the subdomains of a host as a String given the domain level.
40
+ #
41
+ # # Top-level domain example
42
+ # extract_subdomain('www.example.com', 1) # => "www"
43
+ # # Second-level domain example
44
+ # extract_subdomain('dev.www.example.co.uk', 2) # => "dev.www"
45
+ def extract_subdomain(host, tld_length)
46
+ extract_subdomains(host, tld_length).join(".")
21
47
  end
22
48
 
23
- def url_for(options = {})
24
- unless options[:host].present? || options[:only_path].present?
25
- raise ArgumentError, 'Missing host to link to! Please provide the :host parameter, set default_url_options[:host], or set :only_path to true'
49
+ def url_for(options)
50
+ if options[:only_path]
51
+ path_for options
52
+ else
53
+ full_url_for options
26
54
  end
55
+ end
56
+
57
+ def full_url_for(options)
58
+ host = options[:host]
59
+ protocol = options[:protocol]
60
+ port = options[:port]
27
61
 
28
- rewritten_url = ""
29
-
30
- unless options[:only_path]
31
- unless options[:protocol] == false
32
- rewritten_url << (options[:protocol] || "http")
33
- rewritten_url << ":" unless rewritten_url.match(%r{:|//})
34
- end
35
- rewritten_url << "//" unless rewritten_url.match("//")
36
- rewritten_url << rewrite_authentication(options)
37
- rewritten_url << host_or_subdomain_and_domain(options)
38
- rewritten_url << ":#{options.delete(:port)}" if options[:port]
62
+ unless host
63
+ raise ArgumentError, "Missing host to link to! Please provide the :host parameter, set default_url_options[:host], or set :only_path to true"
39
64
  end
40
65
 
41
- path = options.delete(:path) || ''
66
+ build_host_url(host, port, protocol, options, path_for(options))
67
+ end
42
68
 
43
- params = options[:params] || {}
44
- params.reject! {|k,v| v.to_param.nil? }
69
+ def path_for(options)
70
+ path = options[:script_name].to_s.chomp("/".freeze)
71
+ path << options[:path] if options.key?(:path)
45
72
 
46
- if options[:trailing_slash] && !path.ends_with?('/')
47
- rewritten_url << path.sub(/(\?|\z)/) { "/" + $& }
48
- else
49
- rewritten_url << path
50
- end
51
- rewritten_url << "?#{params.to_query}" unless params.empty?
52
- rewritten_url << "##{Journey::Router::Utils.escape_fragment(options[:anchor].to_param.to_s)}" if options[:anchor]
53
- rewritten_url
73
+ add_trailing_slash(path) if options[:trailing_slash]
74
+ add_params(path, options[:params]) if options.key?(:params)
75
+ add_anchor(path, options[:anchor]) if options.key?(:anchor)
76
+
77
+ path
54
78
  end
55
79
 
56
80
  private
57
81
 
58
- def named_host?(host)
59
- !(host.nil? || /\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.match(host))
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)}"
92
+ end
60
93
  end
61
94
 
62
- def rewrite_authentication(options)
95
+ def extract_domain_from(host, tld_length)
96
+ host.split(".").last(1 + tld_length).join(".")
97
+ end
98
+
99
+ def extract_subdomains_from(host, tld_length)
100
+ parts = host.split(".")
101
+ parts[0..-(tld_length + 2)]
102
+ end
103
+
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
110
+ end
111
+
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
118
+
119
+ protocol = normalize_protocol protocol
120
+ host = normalize_host(host, options)
121
+
122
+ result = protocol.dup
123
+
63
124
  if options[:user] && options[:password]
64
- "#{Rack::Utils.escape(options[:user])}:#{Rack::Utils.escape(options[:password])}@"
125
+ result << "#{Rack::Utils.escape(options[:user])}:#{Rack::Utils.escape(options[:password])}@"
126
+ end
127
+
128
+ result << host
129
+ normalize_port(port, protocol) { |normalized_port|
130
+ result << ":#{normalized_port}"
131
+ }
132
+
133
+ result.concat path
134
+ end
135
+
136
+ def named_host?(host)
137
+ IP_HOST_REGEXP !~ host
138
+ end
139
+
140
+ def normalize_protocol(protocol)
141
+ case protocol
142
+ when nil
143
+ "http://"
144
+ when false, "//"
145
+ "//"
146
+ when PROTOCOL_REGEXP
147
+ "#{$1}://"
65
148
  else
66
- ""
149
+ raise ArgumentError, "Invalid :protocol option: #{protocol.inspect}"
67
150
  end
68
151
  end
69
152
 
70
- def host_or_subdomain_and_domain(options)
71
- return options[:host] if !named_host?(options[:host]) || (options[:subdomain].nil? && options[:domain].nil?)
153
+ def normalize_host(_host, options)
154
+ return _host unless named_host?(_host)
72
155
 
73
156
  tld_length = options[:tld_length] || @@tld_length
157
+ subdomain = options.fetch :subdomain, true
158
+ domain = options[:domain]
159
+
160
+ host = "".dup
161
+ if subdomain == true
162
+ return _host if domain.nil?
74
163
 
75
- host = ""
76
- unless options[:subdomain] == false
77
- host << (options[:subdomain] || extract_subdomain(options[:host], tld_length)).to_param
78
- host << "."
164
+ host << extract_subdomains_from(_host, tld_length).join(".")
165
+ elsif subdomain
166
+ host << subdomain.to_param
79
167
  end
80
- host << (options[:domain] || extract_domain(options[:host], tld_length))
168
+ host << "." unless host.empty?
169
+ host << (domain || extract_domain_from(_host, tld_length))
81
170
  host
82
171
  end
172
+
173
+ def normalize_port(port, protocol)
174
+ return unless port
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
182
+ end
183
+ end
184
+ end
185
+
186
+ def initialize
187
+ super
188
+ @protocol = nil
189
+ @port = nil
83
190
  end
84
191
 
85
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"
86
196
  def url
87
197
  protocol + host_with_port + fullpath
88
198
  end
89
199
 
90
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://"
91
207
  def protocol
92
- @protocol ||= ssl? ? 'https://' : 'http://'
208
+ @protocol ||= ssl? ? "https://" : "http://"
93
209
  end
94
210
 
95
- # 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"
96
221
  def raw_host_with_port
97
- if forwarded = env["HTTP_X_FORWARDED_HOST"]
222
+ if forwarded = x_forwarded_host.presence
98
223
  forwarded.split(/,\s?/).last
99
224
  else
100
- 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')}"
101
226
  end
102
227
  end
103
228
 
104
- # 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"
105
233
  def host
106
- raw_host_with_port.sub(/:\d+$/, '')
234
+ raw_host_with_port.sub(/:\d+$/, "".freeze)
107
235
  end
108
236
 
109
237
  # Returns a \host:\port string for this request, such as "example.com" or
110
- # "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"
111
249
  def host_with_port
112
250
  "#{host}#{port_string}"
113
251
  end
114
252
 
115
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
116
260
  def port
117
261
  @port ||= begin
118
262
  if raw_host_with_port =~ /:(\d+)$/
@@ -124,32 +268,60 @@ module ActionDispatch
124
268
  end
125
269
 
126
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
127
274
  def standard_port
128
275
  case protocol
129
- when 'https://' then 443
130
- else 80
276
+ when "https://" then 443
277
+ else 80
131
278
  end
132
279
  end
133
280
 
134
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
135
288
  def standard_port?
136
289
  port == standard_port
137
290
  end
138
291
 
139
292
  # Returns a number \port suffix like 8080 if the \port number of this request
140
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
141
300
  def optional_port
142
301
  standard_port? ? nil : port
143
302
  end
144
303
 
145
304
  # Returns a string \port suffix, including colon, like ":8080" if the \port
146
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"
147
312
  def port_string
148
- standard_port? ? '' : ":#{port}"
313
+ standard_port? ? "" : ":#{port}"
149
314
  end
150
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
151
323
  def server_port
152
- @env['SERVER_PORT'].to_i
324
+ get_header("SERVER_PORT").to_i
153
325
  end
154
326
 
155
327
  # Returns the \domain part of a \host, such as "rubyonrails.org" in "www.rubyonrails.org". You can specify
@@ -171,7 +343,7 @@ module ActionDispatch
171
343
  # such as 2 to catch <tt>"www"</tt> instead of <tt>"www.rubyonrails"</tt>
172
344
  # in "www.rubyonrails.co.uk".
173
345
  def subdomain(tld_length = @@tld_length)
174
- subdomains(tld_length).join(".")
346
+ ActionDispatch::Http::URL.extract_subdomain(host, tld_length)
175
347
  end
176
348
  end
177
349
  end
@@ -0,0 +1,189 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "action_controller/metal/exceptions"
4
+
5
+ module ActionDispatch
6
+ # :stopdoc:
7
+ module Journey
8
+ # The Formatter class is used for formatting URLs. For example, parameters
9
+ # passed to +url_for+ in Rails will eventually call Formatter#generate.
10
+ class Formatter
11
+ attr_reader :routes
12
+
13
+ def initialize(routes)
14
+ @routes = routes
15
+ @cache = nil
16
+ end
17
+
18
+ def generate(name, options, path_parameters, parameterize = nil)
19
+ constraints = path_parameters.merge(options)
20
+ missing_keys = nil
21
+
22
+ match_route(name, constraints) do |route|
23
+ parameterized_parts = extract_parameterized_parts(route, options, path_parameters, parameterize)
24
+
25
+ # Skip this route unless a name has been provided or it is a
26
+ # standard Rails route since we can't determine whether an options
27
+ # hash passed to url_for matches a Rack application or a redirect.
28
+ next unless name || route.dispatcher?
29
+
30
+ missing_keys = missing_keys(route, parameterized_parts)
31
+ next if missing_keys && !missing_keys.empty?
32
+ params = options.dup.delete_if do |key, _|
33
+ parameterized_parts.key?(key) || route.defaults.key?(key)
34
+ end
35
+
36
+ defaults = route.defaults
37
+ required_parts = route.required_parts
38
+
39
+ route.parts.reverse_each do |key|
40
+ break if defaults[key].nil? && parameterized_parts[key].present?
41
+ next if parameterized_parts[key].to_s != defaults[key].to_s
42
+ break if required_parts.include?(key)
43
+
44
+ parameterized_parts.delete(key)
45
+ end
46
+
47
+ return [route.format(parameterized_parts), params]
48
+ end
49
+
50
+ unmatched_keys = (missing_keys || []) & constraints.keys
51
+ missing_keys = (missing_keys || []) - unmatched_keys
52
+
53
+ message = "No route matches #{Hash[constraints.sort_by { |k, v| k.to_s }].inspect}".dup
54
+ message << ", missing required keys: #{missing_keys.sort.inspect}" if missing_keys && !missing_keys.empty?
55
+ message << ", possible unmatched constraints: #{unmatched_keys.sort.inspect}" if unmatched_keys && !unmatched_keys.empty?
56
+
57
+ raise ActionController::UrlGenerationError, message
58
+ end
59
+
60
+ def clear
61
+ @cache = nil
62
+ end
63
+
64
+ private
65
+
66
+ def extract_parameterized_parts(route, options, recall, parameterize = nil)
67
+ parameterized_parts = recall.merge(options)
68
+
69
+ keys_to_keep = route.parts.reverse_each.drop_while { |part|
70
+ !options.key?(part) || (options[part] || recall[part]).nil?
71
+ } | route.required_parts
72
+
73
+ parameterized_parts.delete_if do |bad_key, _|
74
+ !keys_to_keep.include?(bad_key)
75
+ end
76
+
77
+ if parameterize
78
+ parameterized_parts.each do |k, v|
79
+ parameterized_parts[k] = parameterize.call(k, v)
80
+ end
81
+ end
82
+
83
+ parameterized_parts.keep_if { |_, v| v }
84
+ parameterized_parts
85
+ end
86
+
87
+ def named_routes
88
+ routes.named_routes
89
+ end
90
+
91
+ def match_route(name, options)
92
+ if named_routes.key?(name)
93
+ yield named_routes[name]
94
+ else
95
+ routes = non_recursive(cache, options)
96
+
97
+ supplied_keys = options.each_with_object({}) do |(k, v), h|
98
+ h[k.to_s] = true if v
99
+ end
100
+
101
+ hash = routes.group_by { |_, r| r.score(supplied_keys) }
102
+
103
+ hash.keys.sort.reverse_each do |score|
104
+ break if score < 0
105
+
106
+ hash[score].sort_by { |i, _| i }.each do |_, route|
107
+ yield route
108
+ end
109
+ end
110
+ end
111
+ end
112
+
113
+ def non_recursive(cache, options)
114
+ routes = []
115
+ queue = [cache]
116
+
117
+ while queue.any?
118
+ c = queue.shift
119
+ routes.concat(c[:___routes]) if c.key?(:___routes)
120
+
121
+ options.each do |pair|
122
+ queue << c[pair] if c.key?(pair)
123
+ end
124
+ end
125
+
126
+ routes
127
+ end
128
+
129
+ module RegexCaseComparator
130
+ DEFAULT_INPUT = /[-_.a-zA-Z0-9]+\/[-_.a-zA-Z0-9]+/
131
+ DEFAULT_REGEX = /\A#{DEFAULT_INPUT}\Z/
132
+
133
+ def self.===(regex)
134
+ DEFAULT_INPUT == regex
135
+ end
136
+ end
137
+
138
+ # Returns an array populated with missing keys if any are present.
139
+ def missing_keys(route, parts)
140
+ missing_keys = nil
141
+ tests = route.path.requirements
142
+ route.required_parts.each { |key|
143
+ case tests[key]
144
+ when nil
145
+ unless parts[key]
146
+ missing_keys ||= []
147
+ missing_keys << key
148
+ end
149
+ when RegexCaseComparator
150
+ unless RegexCaseComparator::DEFAULT_REGEX === parts[key]
151
+ missing_keys ||= []
152
+ missing_keys << key
153
+ end
154
+ else
155
+ unless /\A#{tests[key]}\Z/ === parts[key]
156
+ missing_keys ||= []
157
+ missing_keys << key
158
+ end
159
+ end
160
+ }
161
+ missing_keys
162
+ end
163
+
164
+ def possibles(cache, options, depth = 0)
165
+ cache.fetch(:___routes) { [] } + options.find_all { |pair|
166
+ cache.key?(pair)
167
+ }.flat_map { |pair|
168
+ possibles(cache[pair], options, depth + 1)
169
+ }
170
+ end
171
+
172
+ def build_cache
173
+ root = { ___routes: [] }
174
+ routes.routes.each_with_index do |route, i|
175
+ leaf = route.required_defaults.inject(root) do |h, tuple|
176
+ h[tuple] ||= {}
177
+ end
178
+ (leaf[:___routes] ||= []) << [i, route]
179
+ end
180
+ root
181
+ end
182
+
183
+ def cache
184
+ @cache ||= build_cache
185
+ end
186
+ end
187
+ end
188
+ # :startdoc:
189
+ end