actionpack 5.2.1 → 7.0.2.4
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.
Potentially problematic release.
This version of actionpack might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +264 -220
- data/MIT-LICENSE +1 -1
- data/README.rdoc +6 -6
- data/lib/abstract_controller/asset_paths.rb +1 -1
- data/lib/abstract_controller/base.rb +24 -4
- data/lib/abstract_controller/caching/fragments.rb +8 -24
- data/lib/abstract_controller/caching.rb +2 -2
- data/lib/abstract_controller/callbacks.rb +34 -8
- data/lib/abstract_controller/collector.rb +5 -4
- data/lib/abstract_controller/error.rb +1 -1
- data/lib/abstract_controller/helpers.rb +107 -90
- data/lib/abstract_controller/logger.rb +1 -1
- data/lib/abstract_controller/railties/routes_helpers.rb +19 -1
- data/lib/abstract_controller/rendering.rb +9 -9
- data/lib/abstract_controller/translation.rb +12 -5
- data/lib/abstract_controller/url_for.rb +4 -6
- data/lib/abstract_controller.rb +2 -0
- data/lib/action_controller/api.rb +5 -4
- data/lib/action_controller/base.rb +6 -9
- data/lib/action_controller/caching.rb +1 -3
- data/lib/action_controller/log_subscriber.rb +13 -9
- data/lib/action_controller/metal/basic_implicit_render.rb +1 -1
- data/lib/action_controller/metal/conditional_get.rb +57 -6
- data/lib/action_controller/metal/content_security_policy.rb +2 -3
- data/lib/action_controller/metal/cookies.rb +4 -2
- data/lib/action_controller/metal/data_streaming.rb +9 -18
- data/lib/action_controller/metal/default_headers.rb +17 -0
- data/lib/action_controller/metal/etag_with_template_digest.rb +4 -6
- data/lib/action_controller/metal/exceptions.rb +55 -12
- data/lib/action_controller/metal/flash.rb +10 -6
- data/lib/action_controller/metal/head.rb +7 -4
- data/lib/action_controller/metal/helpers.rb +15 -6
- data/lib/action_controller/metal/http_authentication.rb +41 -39
- data/lib/action_controller/metal/implicit_render.rb +5 -15
- data/lib/action_controller/metal/instrumentation.rb +59 -55
- data/lib/action_controller/metal/live.rb +80 -33
- data/lib/action_controller/metal/logging.rb +20 -0
- data/lib/action_controller/metal/mime_responds.rb +22 -7
- data/lib/action_controller/metal/parameter_encoding.rb +35 -4
- data/lib/action_controller/metal/params_wrapper.rb +50 -31
- data/lib/action_controller/metal/permissions_policy.rb +46 -0
- data/lib/action_controller/metal/redirecting.rb +93 -23
- data/lib/action_controller/metal/renderers.rb +4 -4
- data/lib/action_controller/metal/rendering.rb +14 -9
- data/lib/action_controller/metal/request_forgery_protection.rb +160 -58
- data/lib/action_controller/metal/rescue.rb +2 -2
- data/lib/action_controller/metal/streaming.rb +1 -4
- data/lib/action_controller/metal/strong_parameters.rb +236 -88
- data/lib/action_controller/metal/testing.rb +9 -2
- data/lib/action_controller/metal/url_for.rb +1 -1
- data/lib/action_controller/metal.rb +16 -17
- data/lib/action_controller/railtie.rb +49 -6
- data/lib/action_controller/railties/helpers.rb +1 -1
- data/lib/action_controller/renderer.rb +37 -13
- data/lib/action_controller/template_assertions.rb +1 -1
- data/lib/action_controller/test_case.rb +98 -68
- data/lib/action_controller.rb +4 -5
- data/lib/action_dispatch/http/cache.rb +45 -32
- data/lib/action_dispatch/http/content_disposition.rb +45 -0
- data/lib/action_dispatch/http/content_security_policy.rb +69 -56
- data/lib/action_dispatch/http/filter_parameters.rb +14 -8
- data/lib/action_dispatch/http/filter_redirect.rb +2 -3
- data/lib/action_dispatch/http/headers.rb +4 -4
- data/lib/action_dispatch/http/mime_negotiation.rb +44 -16
- data/lib/action_dispatch/http/mime_type.rb +47 -30
- data/lib/action_dispatch/http/parameters.rb +18 -27
- data/lib/action_dispatch/http/permissions_policy.rb +173 -0
- data/lib/action_dispatch/http/request.rb +49 -35
- data/lib/action_dispatch/http/response.rb +34 -26
- data/lib/action_dispatch/http/upload.rb +9 -1
- data/lib/action_dispatch/http/url.rb +86 -94
- data/lib/action_dispatch/journey/formatter.rb +55 -31
- data/lib/action_dispatch/journey/gtg/builder.rb +30 -46
- data/lib/action_dispatch/journey/gtg/simulator.rb +15 -8
- data/lib/action_dispatch/journey/gtg/transition_table.rb +78 -21
- data/lib/action_dispatch/journey/nfa/dot.rb +0 -11
- data/lib/action_dispatch/journey/nodes/node.rb +83 -16
- data/lib/action_dispatch/journey/parser.rb +13 -13
- data/lib/action_dispatch/journey/parser.y +1 -1
- data/lib/action_dispatch/journey/path/pattern.rb +42 -34
- data/lib/action_dispatch/journey/route.rb +14 -31
- data/lib/action_dispatch/journey/router/utils.rb +16 -14
- data/lib/action_dispatch/journey/router.rb +27 -35
- data/lib/action_dispatch/journey/routes.rb +3 -5
- data/lib/action_dispatch/journey/scanner.rb +10 -4
- data/lib/action_dispatch/journey/visitors.rb +1 -4
- data/lib/action_dispatch/journey/visualizer/fsm.js +49 -24
- data/lib/action_dispatch/journey/visualizer/index.html.erb +1 -1
- data/lib/action_dispatch/journey.rb +0 -2
- data/lib/action_dispatch/middleware/actionable_exceptions.rb +45 -0
- data/lib/action_dispatch/middleware/callbacks.rb +2 -4
- data/lib/action_dispatch/middleware/cookies.rb +136 -113
- data/lib/action_dispatch/middleware/debug_exceptions.rb +47 -68
- data/lib/action_dispatch/middleware/debug_locks.rb +8 -8
- data/lib/action_dispatch/middleware/debug_view.rb +66 -0
- data/lib/action_dispatch/middleware/exception_wrapper.rb +79 -30
- data/lib/action_dispatch/middleware/executor.rb +4 -1
- data/lib/action_dispatch/middleware/flash.rb +10 -12
- data/lib/action_dispatch/middleware/host_authorization.rb +159 -0
- data/lib/action_dispatch/middleware/public_exceptions.rb +6 -3
- data/lib/action_dispatch/middleware/remote_ip.rb +30 -20
- data/lib/action_dispatch/middleware/request_id.rb +5 -6
- data/lib/action_dispatch/middleware/server_timing.rb +33 -0
- data/lib/action_dispatch/middleware/session/abstract_store.rb +16 -3
- data/lib/action_dispatch/middleware/session/cache_store.rb +11 -6
- data/lib/action_dispatch/middleware/session/cookie_store.rb +24 -19
- data/lib/action_dispatch/middleware/show_exceptions.rb +20 -11
- data/lib/action_dispatch/middleware/ssl.rb +20 -15
- data/lib/action_dispatch/middleware/stack.rb +79 -7
- data/lib/action_dispatch/middleware/static.rb +150 -94
- 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 +6 -11
- data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.text.erb +1 -1
- data/lib/action_dispatch/middleware/templates/rescues/_source.html.erb +4 -2
- data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +46 -36
- data/lib/action_dispatch/middleware/templates/rescues/blocked_host.html.erb +8 -0
- data/lib/action_dispatch/middleware/templates/rescues/blocked_host.text.erb +7 -0
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +25 -6
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +1 -1
- data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +9 -6
- data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.text.erb +4 -1
- data/lib/action_dispatch/middleware/templates/rescues/layout.erb +121 -15
- data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.html.erb +19 -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 +5 -5
- data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +4 -4
- data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +5 -5
- data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +4 -4
- data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +16 -2
- data/lib/action_dispatch/railtie.rb +16 -4
- data/lib/action_dispatch/request/session.rb +59 -22
- data/lib/action_dispatch/request/utils.rb +28 -2
- data/lib/action_dispatch/routing/inspector.rb +102 -54
- data/lib/action_dispatch/routing/mapper.rb +184 -156
- data/lib/action_dispatch/routing/polymorphic_routes.rb +21 -19
- data/lib/action_dispatch/routing/redirection.rb +4 -6
- data/lib/action_dispatch/routing/route_set.rb +83 -73
- data/lib/action_dispatch/routing/routes_proxy.rb +1 -1
- data/lib/action_dispatch/routing/url_for.rb +2 -3
- data/lib/action_dispatch/routing.rb +23 -22
- data/lib/action_dispatch/system_test_case.rb +65 -16
- data/lib/action_dispatch/system_testing/browser.rb +43 -16
- data/lib/action_dispatch/system_testing/driver.rb +42 -10
- data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +58 -12
- data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +3 -10
- data/lib/action_dispatch/testing/assertion_response.rb +0 -1
- data/lib/action_dispatch/testing/assertions/response.rb +4 -7
- data/lib/action_dispatch/testing/assertions/routing.rb +20 -8
- data/lib/action_dispatch/testing/assertions.rb +3 -6
- data/lib/action_dispatch/testing/integration.rb +61 -30
- data/lib/action_dispatch/testing/request_encoder.rb +2 -2
- data/lib/action_dispatch/testing/test_process.rb +8 -6
- data/lib/action_dispatch/testing/test_request.rb +3 -3
- data/lib/action_dispatch/testing/test_response.rb +4 -32
- data/lib/action_dispatch.rb +15 -7
- data/lib/action_pack/gem_version.rb +4 -4
- data/lib/action_pack.rb +1 -1
- metadata +44 -25
- data/lib/action_controller/metal/force_ssl.rb +0 -99
- data/lib/action_dispatch/http/parameter_filter.rb +0 -86
- data/lib/action_dispatch/journey/nfa/builder.rb +0 -78
- data/lib/action_dispatch/journey/nfa/simulator.rb +0 -49
- data/lib/action_dispatch/journey/nfa/transition_table.rb +0 -120
- data/lib/action_dispatch/system_testing/test_helpers/undef_methods.rb +0 -26
@@ -78,10 +78,10 @@ module ActionDispatch # :nodoc:
|
|
78
78
|
x
|
79
79
|
end
|
80
80
|
|
81
|
-
CONTENT_TYPE = "Content-Type"
|
82
|
-
SET_COOKIE = "Set-Cookie"
|
83
|
-
LOCATION = "Location"
|
84
|
-
NO_CONTENT_CODES = [100, 101, 102, 204, 205, 304]
|
81
|
+
CONTENT_TYPE = "Content-Type"
|
82
|
+
SET_COOKIE = "Set-Cookie"
|
83
|
+
LOCATION = "Location"
|
84
|
+
NO_CONTENT_CODES = [100, 101, 102, 103, 204, 205, 304]
|
85
85
|
|
86
86
|
cattr_accessor :default_charset, default: "utf-8"
|
87
87
|
cattr_accessor :default_headers
|
@@ -105,7 +105,7 @@ module ActionDispatch # :nodoc:
|
|
105
105
|
|
106
106
|
def body
|
107
107
|
@str_body ||= begin
|
108
|
-
buf = ""
|
108
|
+
buf = +""
|
109
109
|
each { |chunk| buf << chunk }
|
110
110
|
buf
|
111
111
|
end
|
@@ -142,7 +142,6 @@ module ActionDispatch # :nodoc:
|
|
142
142
|
end
|
143
143
|
|
144
144
|
private
|
145
|
-
|
146
145
|
def each_chunk(&block)
|
147
146
|
@buf.each(&block)
|
148
147
|
end
|
@@ -224,16 +223,6 @@ module ActionDispatch # :nodoc:
|
|
224
223
|
@status = Rack::Utils.status_code(status)
|
225
224
|
end
|
226
225
|
|
227
|
-
# Sets the HTTP content type.
|
228
|
-
def content_type=(content_type)
|
229
|
-
return unless content_type
|
230
|
-
new_header_info = parse_content_type(content_type.to_s)
|
231
|
-
prev_header_info = parsed_content_type_header
|
232
|
-
charset = new_header_info.charset || prev_header_info.charset
|
233
|
-
charset ||= self.class.default_charset unless prev_header_info.mime_type
|
234
|
-
set_content_type new_header_info.mime_type, charset
|
235
|
-
end
|
236
|
-
|
237
226
|
# Sets the HTTP response's content MIME type. For example, in the controller
|
238
227
|
# you could write this:
|
239
228
|
#
|
@@ -242,8 +231,22 @@ module ActionDispatch # :nodoc:
|
|
242
231
|
# If a character set has been defined for this response (see charset=) then
|
243
232
|
# the character set information will also be included in the content type
|
244
233
|
# information.
|
234
|
+
def content_type=(content_type)
|
235
|
+
return unless content_type
|
236
|
+
new_header_info = parse_content_type(content_type.to_s)
|
237
|
+
prev_header_info = parsed_content_type_header
|
238
|
+
charset = new_header_info.charset || prev_header_info.charset
|
239
|
+
charset ||= self.class.default_charset unless prev_header_info.mime_type
|
240
|
+
set_content_type new_header_info.mime_type, charset
|
241
|
+
end
|
245
242
|
|
243
|
+
# Content type of response.
|
246
244
|
def content_type
|
245
|
+
super.presence
|
246
|
+
end
|
247
|
+
|
248
|
+
# Media type of response.
|
249
|
+
def media_type
|
247
250
|
parsed_content_type_header.mime_type
|
248
251
|
end
|
249
252
|
|
@@ -321,7 +324,7 @@ module ActionDispatch # :nodoc:
|
|
321
324
|
# Avoid having to pass an open file handle as the response body.
|
322
325
|
# Rack::Sendfile will usually intercept the response and uses
|
323
326
|
# the path directly, so there is no reason to open the file.
|
324
|
-
class FileBody
|
327
|
+
class FileBody # :nodoc:
|
325
328
|
attr_reader :to_path
|
326
329
|
|
327
330
|
def initialize(path)
|
@@ -404,15 +407,18 @@ module ActionDispatch # :nodoc:
|
|
404
407
|
end
|
405
408
|
|
406
409
|
private
|
407
|
-
|
408
410
|
ContentTypeHeader = Struct.new :mime_type, :charset
|
409
411
|
NullContentTypeHeader = ContentTypeHeader.new nil, nil
|
410
412
|
|
413
|
+
CONTENT_TYPE_PARSER = /
|
414
|
+
\A
|
415
|
+
(?<mime_type>[^;\s]+\s*(?:;\s*(?:(?!charset)[^;\s])+)*)?
|
416
|
+
(?:;\s*charset=(?<quote>"?)(?<charset>[^;\s]+)\k<quote>)?
|
417
|
+
/x # :nodoc:
|
418
|
+
|
411
419
|
def parse_content_type(content_type)
|
412
|
-
if content_type
|
413
|
-
|
414
|
-
type = nil if type && type.empty?
|
415
|
-
ContentTypeHeader.new(type, charset)
|
420
|
+
if content_type && match = CONTENT_TYPE_PARSER.match(content_type)
|
421
|
+
ContentTypeHeader.new(match[:mime_type], match[:charset])
|
416
422
|
else
|
417
423
|
NullContentTypeHeader
|
418
424
|
end
|
@@ -425,8 +431,8 @@ module ActionDispatch # :nodoc:
|
|
425
431
|
end
|
426
432
|
|
427
433
|
def set_content_type(content_type, charset)
|
428
|
-
type =
|
429
|
-
type
|
434
|
+
type = content_type || ""
|
435
|
+
type = "#{type}; charset=#{charset.to_s.downcase}" if charset
|
430
436
|
set_header CONTENT_TYPE, type
|
431
437
|
end
|
432
438
|
|
@@ -459,7 +465,7 @@ module ActionDispatch # :nodoc:
|
|
459
465
|
end
|
460
466
|
|
461
467
|
def assign_default_content_type_and_charset!
|
462
|
-
return if
|
468
|
+
return if media_type
|
463
469
|
|
464
470
|
ct = parsed_content_type_header
|
465
471
|
set_content_type(ct.mime_type || Mime[:html].to_s,
|
@@ -486,7 +492,7 @@ module ActionDispatch # :nodoc:
|
|
486
492
|
end
|
487
493
|
|
488
494
|
def respond_to?(method, include_private = false)
|
489
|
-
if method.
|
495
|
+
if method.to_sym == :to_path
|
490
496
|
@response.stream.respond_to?(method)
|
491
497
|
else
|
492
498
|
super
|
@@ -517,4 +523,6 @@ module ActionDispatch # :nodoc:
|
|
517
523
|
end
|
518
524
|
end
|
519
525
|
end
|
526
|
+
|
527
|
+
ActiveSupport.run_load_hooks(:action_dispatch_response, Response)
|
520
528
|
end
|
@@ -20,7 +20,6 @@ module ActionDispatch
|
|
20
20
|
# A +Tempfile+ object with the actual uploaded file. Note that some of
|
21
21
|
# its interface is available directly.
|
22
22
|
attr_accessor :tempfile
|
23
|
-
alias :to_io :tempfile
|
24
23
|
|
25
24
|
# A string with the headers of the multipart request.
|
26
25
|
attr_accessor :headers
|
@@ -65,6 +64,11 @@ module ActionDispatch
|
|
65
64
|
@tempfile.path
|
66
65
|
end
|
67
66
|
|
67
|
+
# Shortcut for +tempfile.to_path+.
|
68
|
+
def to_path
|
69
|
+
@tempfile.to_path
|
70
|
+
end
|
71
|
+
|
68
72
|
# Shortcut for +tempfile.rewind+.
|
69
73
|
def rewind
|
70
74
|
@tempfile.rewind
|
@@ -79,6 +83,10 @@ module ActionDispatch
|
|
79
83
|
def eof?
|
80
84
|
@tempfile.eof?
|
81
85
|
end
|
86
|
+
|
87
|
+
def to_io
|
88
|
+
@tempfile.to_io
|
89
|
+
end
|
82
90
|
end
|
83
91
|
end
|
84
92
|
end
|
@@ -9,6 +9,7 @@ module ActionDispatch
|
|
9
9
|
HOST_REGEXP = /(^[^:]+:\/\/)?(\[[^\]]+\]|[^:]+)(?::(\d+$))?/
|
10
10
|
PROTOCOL_REGEXP = /^([^:]+)(:)?(\/\/)?$/
|
11
11
|
|
12
|
+
mattr_accessor :secure_protocol, default: false
|
12
13
|
mattr_accessor :tld_length, default: 1
|
13
14
|
|
14
15
|
class << self
|
@@ -67,10 +68,11 @@ module ActionDispatch
|
|
67
68
|
end
|
68
69
|
|
69
70
|
def path_for(options)
|
70
|
-
path = options[:script_name].to_s.chomp("/"
|
71
|
+
path = options[:script_name].to_s.chomp("/")
|
71
72
|
path << options[:path] if options.key?(:path)
|
72
73
|
|
73
|
-
|
74
|
+
path = "/" if options[:trailing_slash] && path.blank?
|
75
|
+
|
74
76
|
add_params(path, options[:params]) if options.key?(:params)
|
75
77
|
add_anchor(path, options[:anchor]) if options.key?(:anchor)
|
76
78
|
|
@@ -78,109 +80,100 @@ module ActionDispatch
|
|
78
80
|
end
|
79
81
|
|
80
82
|
private
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
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)}"
|
83
|
+
def add_params(path, params)
|
84
|
+
params = { params: params } unless params.is_a?(Hash)
|
85
|
+
params.reject! { |_, v| v.to_param.nil? }
|
86
|
+
query = params.to_query
|
87
|
+
path << "?#{query}" unless query.empty?
|
92
88
|
end
|
93
|
-
end
|
94
|
-
|
95
|
-
def extract_domain_from(host, tld_length)
|
96
|
-
host.split(".").last(1 + tld_length).join(".")
|
97
|
-
end
|
98
89
|
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
90
|
+
def add_anchor(path, anchor)
|
91
|
+
if anchor
|
92
|
+
path << "##{Journey::Router::Utils.escape_fragment(anchor.to_param)}"
|
93
|
+
end
|
94
|
+
end
|
103
95
|
|
104
|
-
|
105
|
-
|
106
|
-
path.sub!(/\?/, '/\&')
|
107
|
-
elsif !path.include?(".")
|
108
|
-
path.sub!(/[^\/]\z|\A\z/, '\&/')
|
96
|
+
def extract_domain_from(host, tld_length)
|
97
|
+
host.split(".").last(1 + tld_length).join(".")
|
109
98
|
end
|
110
|
-
end
|
111
99
|
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
host = match[2]
|
116
|
-
port = match[3] unless options.key? :port
|
100
|
+
def extract_subdomains_from(host, tld_length)
|
101
|
+
parts = host.split(".")
|
102
|
+
parts[0..-(tld_length + 2)]
|
117
103
|
end
|
118
104
|
|
119
|
-
|
120
|
-
|
105
|
+
def build_host_url(host, port, protocol, options, path)
|
106
|
+
if match = host.match(HOST_REGEXP)
|
107
|
+
protocol ||= match[1] unless protocol == false
|
108
|
+
host = match[2]
|
109
|
+
port = match[3] unless options.key? :port
|
110
|
+
end
|
121
111
|
|
122
|
-
|
112
|
+
protocol = normalize_protocol protocol
|
113
|
+
host = normalize_host(host, options)
|
123
114
|
|
124
|
-
|
125
|
-
result << "#{Rack::Utils.escape(options[:user])}:#{Rack::Utils.escape(options[:password])}@"
|
126
|
-
end
|
115
|
+
result = protocol.dup
|
127
116
|
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
}
|
117
|
+
if options[:user] && options[:password]
|
118
|
+
result << "#{Rack::Utils.escape(options[:user])}:#{Rack::Utils.escape(options[:password])}@"
|
119
|
+
end
|
132
120
|
|
133
|
-
|
134
|
-
|
121
|
+
result << host
|
122
|
+
normalize_port(port, protocol) { |normalized_port|
|
123
|
+
result << ":#{normalized_port}"
|
124
|
+
}
|
135
125
|
|
136
|
-
|
137
|
-
|
138
|
-
end
|
126
|
+
result.concat path
|
127
|
+
end
|
139
128
|
|
140
|
-
|
141
|
-
|
142
|
-
when nil
|
143
|
-
"http://"
|
144
|
-
when false, "//"
|
145
|
-
"//"
|
146
|
-
when PROTOCOL_REGEXP
|
147
|
-
"#{$1}://"
|
148
|
-
else
|
149
|
-
raise ArgumentError, "Invalid :protocol option: #{protocol.inspect}"
|
129
|
+
def named_host?(host)
|
130
|
+
!IP_HOST_REGEXP.match?(host)
|
150
131
|
end
|
151
|
-
end
|
152
132
|
|
153
|
-
|
154
|
-
|
133
|
+
def normalize_protocol(protocol)
|
134
|
+
case protocol
|
135
|
+
when nil
|
136
|
+
secure_protocol ? "https://" : "http://"
|
137
|
+
when false, "//"
|
138
|
+
"//"
|
139
|
+
when PROTOCOL_REGEXP
|
140
|
+
"#{$1}://"
|
141
|
+
else
|
142
|
+
raise ArgumentError, "Invalid :protocol option: #{protocol.inspect}"
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
def normalize_host(_host, options)
|
147
|
+
return _host unless named_host?(_host)
|
155
148
|
|
156
|
-
|
157
|
-
|
158
|
-
|
149
|
+
tld_length = options[:tld_length] || @@tld_length
|
150
|
+
subdomain = options.fetch :subdomain, true
|
151
|
+
domain = options[:domain]
|
159
152
|
|
160
|
-
|
161
|
-
|
162
|
-
|
153
|
+
host = +""
|
154
|
+
if subdomain == true
|
155
|
+
return _host if domain.nil?
|
163
156
|
|
164
|
-
|
165
|
-
|
166
|
-
|
157
|
+
host << extract_subdomains_from(_host, tld_length).join(".")
|
158
|
+
elsif subdomain
|
159
|
+
host << subdomain.to_param
|
160
|
+
end
|
161
|
+
host << "." unless host.empty?
|
162
|
+
host << (domain || extract_domain_from(_host, tld_length))
|
163
|
+
host
|
167
164
|
end
|
168
|
-
host << "." unless host.empty?
|
169
|
-
host << (domain || extract_domain_from(_host, tld_length))
|
170
|
-
host
|
171
|
-
end
|
172
165
|
|
173
|
-
|
174
|
-
|
166
|
+
def normalize_port(port, protocol)
|
167
|
+
return unless port
|
175
168
|
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
169
|
+
case protocol
|
170
|
+
when "//" then yield port
|
171
|
+
when "https://"
|
172
|
+
yield port unless port.to_i == 443
|
173
|
+
else
|
174
|
+
yield port unless port.to_i == 80
|
175
|
+
end
|
182
176
|
end
|
183
|
-
end
|
184
177
|
end
|
185
178
|
|
186
179
|
def initialize
|
@@ -222,7 +215,7 @@ module ActionDispatch
|
|
222
215
|
if forwarded = x_forwarded_host.presence
|
223
216
|
forwarded.split(/,\s?/).last
|
224
217
|
else
|
225
|
-
get_header("HTTP_HOST") || "#{server_name
|
218
|
+
get_header("HTTP_HOST") || "#{server_name}:#{get_header('SERVER_PORT')}"
|
226
219
|
end
|
227
220
|
end
|
228
221
|
|
@@ -231,7 +224,7 @@ module ActionDispatch
|
|
231
224
|
# req = ActionDispatch::Request.new 'HTTP_HOST' => 'example.com:8080'
|
232
225
|
# req.host # => "example.com"
|
233
226
|
def host
|
234
|
-
raw_host_with_port.sub(/:\d+$/, ""
|
227
|
+
raw_host_with_port.sub(/:\d+$/, "")
|
235
228
|
end
|
236
229
|
|
237
230
|
# Returns a \host:\port string for this request, such as "example.com" or
|
@@ -258,12 +251,10 @@ module ActionDispatch
|
|
258
251
|
# req = ActionDispatch::Request.new 'HTTP_HOST' => 'example.com:8080'
|
259
252
|
# req.port # => 8080
|
260
253
|
def port
|
261
|
-
@port ||=
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
standard_port
|
266
|
-
end
|
254
|
+
@port ||= if raw_host_with_port =~ /:(\d+)$/
|
255
|
+
$1.to_i
|
256
|
+
else
|
257
|
+
standard_port
|
267
258
|
end
|
268
259
|
end
|
269
260
|
|
@@ -272,9 +263,10 @@ module ActionDispatch
|
|
272
263
|
# req = ActionDispatch::Request.new 'HTTP_HOST' => 'example.com:8080'
|
273
264
|
# req.standard_port # => 80
|
274
265
|
def standard_port
|
275
|
-
|
276
|
-
|
277
|
-
else
|
266
|
+
if "https://" == protocol
|
267
|
+
443
|
268
|
+
else
|
269
|
+
80
|
278
270
|
end
|
279
271
|
end
|
280
272
|
|
@@ -15,12 +15,53 @@ module ActionDispatch
|
|
15
15
|
@cache = nil
|
16
16
|
end
|
17
17
|
|
18
|
-
|
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)
|
19
60
|
constraints = path_parameters.merge(options)
|
20
61
|
missing_keys = nil
|
21
62
|
|
22
63
|
match_route(name, constraints) do |route|
|
23
|
-
parameterized_parts = extract_parameterized_parts(route, options, path_parameters
|
64
|
+
parameterized_parts = extract_parameterized_parts(route, options, path_parameters)
|
24
65
|
|
25
66
|
# Skip this route unless a name has been provided or it is a
|
26
67
|
# standard Rails route since we can't determine whether an options
|
@@ -44,17 +85,13 @@ module ActionDispatch
|
|
44
85
|
parameterized_parts.delete(key)
|
45
86
|
end
|
46
87
|
|
47
|
-
return
|
88
|
+
return RouteWithParams.new(route, parameterized_parts, params)
|
48
89
|
end
|
49
90
|
|
50
91
|
unmatched_keys = (missing_keys || []) & constraints.keys
|
51
92
|
missing_keys = (missing_keys || []) - unmatched_keys
|
52
93
|
|
53
|
-
|
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
|
94
|
+
MissingRoute.new(constraints, missing_keys, unmatched_keys, routes, name)
|
58
95
|
end
|
59
96
|
|
60
97
|
def clear
|
@@ -62,25 +99,26 @@ module ActionDispatch
|
|
62
99
|
end
|
63
100
|
|
64
101
|
private
|
65
|
-
|
66
|
-
def extract_parameterized_parts(route, options, recall, parameterize = nil)
|
102
|
+
def extract_parameterized_parts(route, options, recall)
|
67
103
|
parameterized_parts = recall.merge(options)
|
68
104
|
|
69
105
|
keys_to_keep = route.parts.reverse_each.drop_while { |part|
|
70
|
-
!options.key?(part) || (options[part]
|
106
|
+
!(options.key?(part) || route.scope_options.key?(part)) || (options[part].nil? && recall[part].nil?)
|
71
107
|
} | route.required_parts
|
72
108
|
|
73
109
|
parameterized_parts.delete_if do |bad_key, _|
|
74
110
|
!keys_to_keep.include?(bad_key)
|
75
111
|
end
|
76
112
|
|
77
|
-
|
78
|
-
|
79
|
-
parameterized_parts[k] =
|
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
|
80
118
|
end
|
81
119
|
end
|
82
120
|
|
83
|
-
parameterized_parts.
|
121
|
+
parameterized_parts.compact!
|
84
122
|
parameterized_parts
|
85
123
|
end
|
86
124
|
|
@@ -126,19 +164,10 @@ module ActionDispatch
|
|
126
164
|
routes
|
127
165
|
end
|
128
166
|
|
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
167
|
# Returns an array populated with missing keys if any are present.
|
139
168
|
def missing_keys(route, parts)
|
140
169
|
missing_keys = nil
|
141
|
-
tests = route.path.
|
170
|
+
tests = route.path.requirements_for_missing_keys_check
|
142
171
|
route.required_parts.each { |key|
|
143
172
|
case tests[key]
|
144
173
|
when nil
|
@@ -146,13 +175,8 @@ module ActionDispatch
|
|
146
175
|
missing_keys ||= []
|
147
176
|
missing_keys << key
|
148
177
|
end
|
149
|
-
when RegexCaseComparator
|
150
|
-
unless RegexCaseComparator::DEFAULT_REGEX === parts[key]
|
151
|
-
missing_keys ||= []
|
152
|
-
missing_keys << key
|
153
|
-
end
|
154
178
|
else
|
155
|
-
unless
|
179
|
+
unless tests[key].match?(parts[key])
|
156
180
|
missing_keys ||= []
|
157
181
|
missing_keys << key
|
158
182
|
end
|