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