actionpack 5.2.7.1 → 6.1.4.6
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +329 -352
- data/MIT-LICENSE +1 -1
- data/README.rdoc +4 -3
- data/lib/abstract_controller/base.rb +38 -4
- data/lib/abstract_controller/caching/fragments.rb +6 -22
- data/lib/abstract_controller/caching.rb +1 -1
- data/lib/abstract_controller/callbacks.rb +14 -2
- data/lib/abstract_controller/collector.rb +1 -2
- data/lib/abstract_controller/helpers.rb +106 -90
- data/lib/abstract_controller/railties/routes_helpers.rb +17 -1
- data/lib/abstract_controller/rendering.rb +9 -9
- data/lib/abstract_controller/translation.rb +11 -5
- data/lib/abstract_controller.rb +1 -0
- data/lib/action_controller/api.rb +4 -3
- data/lib/action_controller/base.rb +6 -9
- data/lib/action_controller/caching.rb +1 -3
- data/lib/action_controller/log_subscriber.rb +10 -7
- data/lib/action_controller/metal/basic_implicit_render.rb +1 -1
- data/lib/action_controller/metal/conditional_get.rb +19 -5
- data/lib/action_controller/metal/content_security_policy.rb +1 -2
- data/lib/action_controller/metal/cookies.rb +3 -1
- data/lib/action_controller/metal/data_streaming.rb +6 -7
- 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 +56 -2
- data/lib/action_controller/metal/flash.rb +5 -5
- data/lib/action_controller/metal/head.rb +7 -4
- data/lib/action_controller/metal/helpers.rb +14 -5
- data/lib/action_controller/metal/http_authentication.rb +24 -23
- data/lib/action_controller/metal/implicit_render.rb +5 -15
- data/lib/action_controller/metal/instrumentation.rb +13 -14
- data/lib/action_controller/metal/live.rb +39 -32
- data/lib/action_controller/metal/logging.rb +20 -0
- data/lib/action_controller/metal/mime_responds.rb +19 -4
- data/lib/action_controller/metal/parameter_encoding.rb +35 -4
- data/lib/action_controller/metal/params_wrapper.rb +32 -22
- data/lib/action_controller/metal/permissions_policy.rb +46 -0
- data/lib/action_controller/metal/redirecting.rb +6 -6
- data/lib/action_controller/metal/renderers.rb +4 -4
- data/lib/action_controller/metal/rendering.rb +8 -3
- data/lib/action_controller/metal/request_forgery_protection.rb +26 -49
- data/lib/action_controller/metal/rescue.rb +1 -1
- data/lib/action_controller/metal/streaming.rb +0 -1
- data/lib/action_controller/metal/strong_parameters.rb +167 -58
- data/lib/action_controller/metal/url_for.rb +1 -1
- data/lib/action_controller/metal.rb +10 -8
- 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 +71 -63
- data/lib/action_controller.rb +7 -4
- data/lib/action_dispatch/http/cache.rb +31 -27
- data/lib/action_dispatch/http/content_disposition.rb +45 -0
- data/lib/action_dispatch/http/content_security_policy.rb +39 -17
- data/lib/action_dispatch/http/filter_parameters.rb +9 -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 +26 -13
- data/lib/action_dispatch/http/mime_type.rb +43 -24
- data/lib/action_dispatch/http/parameters.rb +14 -23
- data/lib/action_dispatch/http/permissions_policy.rb +173 -0
- data/lib/action_dispatch/http/request.rb +45 -22
- data/lib/action_dispatch/http/response.rb +45 -25
- data/lib/action_dispatch/http/upload.rb +9 -1
- data/lib/action_dispatch/http/url.rb +82 -82
- data/lib/action_dispatch/journey/formatter.rb +55 -31
- data/lib/action_dispatch/journey/gtg/builder.rb +22 -37
- data/lib/action_dispatch/journey/gtg/simulator.rb +8 -7
- data/lib/action_dispatch/journey/gtg/transition_table.rb +6 -5
- data/lib/action_dispatch/journey/nfa/dot.rb +0 -11
- data/lib/action_dispatch/journey/nodes/node.rb +13 -11
- 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 +19 -21
- data/lib/action_dispatch/journey/route.rb +10 -20
- data/lib/action_dispatch/journey/router/utils.rb +14 -12
- data/lib/action_dispatch/journey/router.rb +26 -34
- data/lib/action_dispatch/journey/routes.rb +0 -2
- data/lib/action_dispatch/journey/scanner.rb +10 -4
- data/lib/action_dispatch/journey/visitors.rb +1 -4
- data/lib/action_dispatch/journey.rb +0 -2
- data/lib/action_dispatch/middleware/actionable_exceptions.rb +46 -0
- data/lib/action_dispatch/middleware/callbacks.rb +2 -4
- data/lib/action_dispatch/middleware/cookies.rb +128 -109
- data/lib/action_dispatch/middleware/debug_exceptions.rb +43 -66
- data/lib/action_dispatch/middleware/debug_locks.rb +5 -5
- data/lib/action_dispatch/middleware/debug_view.rb +66 -0
- data/lib/action_dispatch/middleware/exception_wrapper.rb +75 -30
- data/lib/action_dispatch/middleware/flash.rb +1 -1
- data/lib/action_dispatch/middleware/host_authorization.rb +141 -0
- data/lib/action_dispatch/middleware/public_exceptions.rb +6 -3
- data/lib/action_dispatch/middleware/remote_ip.rb +14 -16
- data/lib/action_dispatch/middleware/request_id.rb +5 -6
- data/lib/action_dispatch/middleware/session/abstract_store.rb +2 -3
- data/lib/action_dispatch/middleware/session/cookie_store.rb +3 -9
- data/lib/action_dispatch/middleware/show_exceptions.rb +3 -2
- data/lib/action_dispatch/middleware/ssl.rb +20 -15
- data/lib/action_dispatch/middleware/stack.rb +56 -2
- data/lib/action_dispatch/middleware/static.rb +153 -93
- 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 +3 -1
- 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 +45 -35
- data/lib/action_dispatch/middleware/templates/rescues/blocked_host.html.erb +7 -0
- data/lib/action_dispatch/middleware/templates/rescues/blocked_host.text.erb +5 -0
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +23 -4
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +1 -1
- data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +6 -3
- data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.text.erb +4 -1
- data/lib/action_dispatch/middleware/templates/rescues/layout.erb +104 -8
- 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 +2 -2
- data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +1 -1
- data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +2 -2
- data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +1 -1
- data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +24 -1
- data/lib/action_dispatch/railtie.rb +8 -2
- data/lib/action_dispatch/request/session.rb +11 -10
- data/lib/action_dispatch/request/utils.rb +26 -2
- data/lib/action_dispatch/routing/inspector.rb +100 -52
- data/lib/action_dispatch/routing/mapper.rb +155 -103
- data/lib/action_dispatch/routing/polymorphic_routes.rb +13 -15
- data/lib/action_dispatch/routing/redirection.rb +4 -4
- data/lib/action_dispatch/routing/route_set.rb +71 -69
- data/lib/action_dispatch/routing/url_for.rb +2 -2
- data/lib/action_dispatch/routing.rb +21 -20
- data/lib/action_dispatch/system_test_case.rb +54 -11
- data/lib/action_dispatch/system_testing/browser.rb +53 -16
- data/lib/action_dispatch/system_testing/driver.rb +11 -3
- data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +49 -7
- data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +8 -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 +1 -1
- data/lib/action_dispatch/testing/integration.rb +60 -28
- data/lib/action_dispatch/testing/request_encoder.rb +2 -2
- data/lib/action_dispatch/testing/test_process.rb +29 -4
- 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 +9 -3
- data/lib/action_pack/gem_version.rb +4 -4
- data/lib/action_pack.rb +1 -1
- metadata +35 -23
- 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
@@ -12,7 +12,7 @@ module ActionController
|
|
12
12
|
#
|
13
13
|
# An API Controller is different from a normal controller in the sense that
|
14
14
|
# by default it doesn't include a number of features that are usually required
|
15
|
-
# by browser access only: layouts and templates rendering,
|
15
|
+
# by browser access only: layouts and templates rendering,
|
16
16
|
# flash, assets, and so on. This makes the entire controller stack thinner,
|
17
17
|
# suitable for API applications. It doesn't mean you won't have such
|
18
18
|
# features if you need them: they're all available for you to include in
|
@@ -93,7 +93,7 @@ module ActionController
|
|
93
93
|
# the ones passed as arguments:
|
94
94
|
#
|
95
95
|
# class MyAPIBaseController < ActionController::Metal
|
96
|
-
# ActionController::API.without_modules(:
|
96
|
+
# ActionController::API.without_modules(:UrlFor).each do |left|
|
97
97
|
# include left
|
98
98
|
# end
|
99
99
|
# end
|
@@ -120,8 +120,9 @@ module ActionController
|
|
120
120
|
BasicImplicitRender,
|
121
121
|
StrongParameters,
|
122
122
|
|
123
|
-
ForceSSL,
|
124
123
|
DataStreaming,
|
124
|
+
DefaultHeaders,
|
125
|
+
Logging,
|
125
126
|
|
126
127
|
# Before callbacks should also be executed as early as possible, so
|
127
128
|
# also include them at the bottom.
|
@@ -78,7 +78,7 @@ module ActionController
|
|
78
78
|
#
|
79
79
|
# You can retrieve it again through the same hash:
|
80
80
|
#
|
81
|
-
# Hello #{session[:person]}
|
81
|
+
# "Hello #{session[:person]}"
|
82
82
|
#
|
83
83
|
# For removing objects from the session, you can either assign a single key to +nil+:
|
84
84
|
#
|
@@ -226,12 +226,14 @@ module ActionController
|
|
226
226
|
FormBuilder,
|
227
227
|
RequestForgeryProtection,
|
228
228
|
ContentSecurityPolicy,
|
229
|
-
|
229
|
+
PermissionsPolicy,
|
230
230
|
Streaming,
|
231
231
|
DataStreaming,
|
232
232
|
HttpAuthentication::Basic::ControllerMethods,
|
233
233
|
HttpAuthentication::Digest::ControllerMethods,
|
234
234
|
HttpAuthentication::Token::ControllerMethods,
|
235
|
+
DefaultHeaders,
|
236
|
+
Logging,
|
235
237
|
|
236
238
|
# Before callbacks should also be executed as early as possible, so
|
237
239
|
# also include them at the bottom.
|
@@ -260,15 +262,10 @@ module ActionController
|
|
260
262
|
@_view_renderer @_lookup_context @_routes @_view_runtime @_db_runtime @_helper_proxy
|
261
263
|
)
|
262
264
|
|
263
|
-
def _protected_ivars
|
265
|
+
def _protected_ivars
|
264
266
|
PROTECTED_IVARS
|
265
267
|
end
|
266
|
-
|
267
|
-
def self.make_response!(request)
|
268
|
-
ActionDispatch::Response.create.tap do |res|
|
269
|
-
res.request = request
|
270
|
-
end
|
271
|
-
end
|
268
|
+
private :_protected_ivars
|
272
269
|
|
273
270
|
ActiveSupport.run_load_hooks(:action_controller_base, self)
|
274
271
|
ActiveSupport.run_load_hooks(:action_controller, self)
|
@@ -22,7 +22,6 @@ module ActionController
|
|
22
22
|
# config.action_controller.cache_store = :mem_cache_store, Memcached::Rails.new('localhost:11211')
|
23
23
|
# config.action_controller.cache_store = MyOwnStore.new('parameter')
|
24
24
|
module Caching
|
25
|
-
extend ActiveSupport::Autoload
|
26
25
|
extend ActiveSupport::Concern
|
27
26
|
|
28
27
|
included do
|
@@ -30,7 +29,6 @@ module ActionController
|
|
30
29
|
end
|
31
30
|
|
32
31
|
private
|
33
|
-
|
34
32
|
def instrument_payload(key)
|
35
33
|
{
|
36
34
|
controller: controller_name,
|
@@ -40,7 +38,7 @@ module ActionController
|
|
40
38
|
end
|
41
39
|
|
42
40
|
def instrument_name
|
43
|
-
"action_controller"
|
41
|
+
"action_controller"
|
44
42
|
end
|
45
43
|
end
|
46
44
|
end
|
@@ -11,6 +11,7 @@ module ActionController
|
|
11
11
|
params = payload[:params].except(*INTERNAL_PARAMS)
|
12
12
|
format = payload[:format]
|
13
13
|
format = format.to_s.upcase if format.is_a?(Symbol)
|
14
|
+
format = "*/*" if format.nil?
|
14
15
|
|
15
16
|
info "Processing by #{payload[:controller]}##{payload[:action]} as #{format}"
|
16
17
|
info " Parameters: #{params.inspect}" unless params.empty?
|
@@ -18,16 +19,18 @@ module ActionController
|
|
18
19
|
|
19
20
|
def process_action(event)
|
20
21
|
info do
|
21
|
-
payload
|
22
|
+
payload = event.payload
|
22
23
|
additions = ActionController::Base.log_process_action(payload)
|
23
|
-
|
24
24
|
status = payload[:status]
|
25
|
-
|
26
|
-
|
25
|
+
|
26
|
+
if status.nil? && (exception_class_name = payload[:exception]&.first)
|
27
27
|
status = ActionDispatch::ExceptionWrapper.status_code_for_exception(exception_class_name)
|
28
28
|
end
|
29
|
-
|
30
|
-
|
29
|
+
|
30
|
+
additions << "Allocations: #{event.allocations}"
|
31
|
+
|
32
|
+
message = +"Completed #{status} #{Rack::Utils::HTTP_STATUS_CODES[status]} in #{event.duration.round}ms"
|
33
|
+
message << " (#{additions.join(" | ")})"
|
31
34
|
message << "\n\n" if defined?(Rails.env) && Rails.env.development?
|
32
35
|
|
33
36
|
message
|
@@ -53,7 +56,7 @@ module ActionController
|
|
53
56
|
def unpermitted_parameters(event)
|
54
57
|
debug do
|
55
58
|
unpermitted_keys = event.payload[:keys]
|
56
|
-
"Unpermitted parameter#{'s' if unpermitted_keys.size > 1}: #{unpermitted_keys.map { |e| ":#{e}" }.join(", ")}"
|
59
|
+
color("Unpermitted parameter#{'s' if unpermitted_keys.size > 1}: #{unpermitted_keys.map { |e| ":#{e}" }.join(", ")}", RED)
|
57
60
|
end
|
58
61
|
end
|
59
62
|
|
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "active_support/core_ext/
|
3
|
+
require "active_support/core_ext/object/try"
|
4
|
+
require "active_support/core_ext/integer/time"
|
4
5
|
|
5
6
|
module ActionController
|
6
7
|
module ConditionalGet
|
@@ -19,12 +20,12 @@ module ActionController
|
|
19
20
|
# of cached pages.
|
20
21
|
#
|
21
22
|
# class InvoicesController < ApplicationController
|
22
|
-
# etag { current_user
|
23
|
+
# etag { current_user&.id }
|
23
24
|
#
|
24
25
|
# def show
|
25
26
|
# # Etag will differ even for the same invoice when it's viewed by a different current_user
|
26
27
|
# @invoice = Invoice.find(params[:id])
|
27
|
-
# fresh_when
|
28
|
+
# fresh_when etag: @invoice
|
28
29
|
# end
|
29
30
|
# end
|
30
31
|
def etag(&etagger)
|
@@ -181,7 +182,7 @@ module ActionController
|
|
181
182
|
#
|
182
183
|
# You can also pass an object that responds to +maximum+, such as a
|
183
184
|
# collection of active records. In this case +last_modified+ will be set by
|
184
|
-
# calling
|
185
|
+
# calling <tt>maximum(:updated_at)</tt> on the collection (the timestamp of the
|
185
186
|
# most recently updated record) and the +etag+ by passing the object itself.
|
186
187
|
#
|
187
188
|
# def index
|
@@ -230,12 +231,25 @@ module ActionController
|
|
230
231
|
# This method will overwrite an existing Cache-Control header.
|
231
232
|
# See https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html for more possibilities.
|
232
233
|
#
|
234
|
+
# HTTP Cache-Control Extensions for Stale Content. See https://tools.ietf.org/html/rfc5861
|
235
|
+
# It helps to cache an asset and serve it while is being revalidated and/or returning with an error.
|
236
|
+
#
|
237
|
+
# expires_in 3.hours, public: true, stale_while_revalidate: 60.seconds
|
238
|
+
# expires_in 3.hours, public: true, stale_while_revalidate: 60.seconds, stale_if_error: 5.minutes
|
239
|
+
#
|
240
|
+
# HTTP Cache-Control Extensions other values: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control
|
241
|
+
# Any additional key-value pairs are concatenated onto the `Cache-Control` header in the response:
|
242
|
+
#
|
243
|
+
# expires_in 3.hours, public: true, "s-maxage": 3.hours, "no-transform": true
|
244
|
+
#
|
233
245
|
# The method will also ensure an HTTP Date header for client compatibility.
|
234
246
|
def expires_in(seconds, options = {})
|
235
247
|
response.cache_control.merge!(
|
236
248
|
max_age: seconds,
|
237
249
|
public: options.delete(:public),
|
238
|
-
must_revalidate: options.delete(:must_revalidate)
|
250
|
+
must_revalidate: options.delete(:must_revalidate),
|
251
|
+
stale_while_revalidate: options.delete(:stale_while_revalidate),
|
252
|
+
stale_if_error: options.delete(:stale_if_error),
|
239
253
|
)
|
240
254
|
options.delete(:private)
|
241
255
|
|
@@ -36,7 +36,6 @@ module ActionController #:nodoc:
|
|
36
36
|
end
|
37
37
|
|
38
38
|
private
|
39
|
-
|
40
39
|
def content_security_policy?
|
41
40
|
request.content_security_policy
|
42
41
|
end
|
@@ -46,7 +45,7 @@ module ActionController #:nodoc:
|
|
46
45
|
end
|
47
46
|
|
48
47
|
def current_content_security_policy
|
49
|
-
request.content_security_policy
|
48
|
+
request.content_security_policy&.clone || ActionDispatch::ContentSecurityPolicy.new
|
50
49
|
end
|
51
50
|
end
|
52
51
|
end
|
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "action_controller/metal/exceptions"
|
4
|
+
require "action_dispatch/http/content_disposition"
|
4
5
|
|
5
6
|
module ActionController #:nodoc:
|
6
7
|
# Methods for sending arbitrary data and for streaming files to the browser,
|
@@ -10,8 +11,8 @@ module ActionController #:nodoc:
|
|
10
11
|
|
11
12
|
include ActionController::Rendering
|
12
13
|
|
13
|
-
DEFAULT_SEND_FILE_TYPE = "application/octet-stream"
|
14
|
-
DEFAULT_SEND_FILE_DISPOSITION = "attachment"
|
14
|
+
DEFAULT_SEND_FILE_TYPE = "application/octet-stream" #:nodoc:
|
15
|
+
DEFAULT_SEND_FILE_DISPOSITION = "attachment" #:nodoc:
|
15
16
|
|
16
17
|
private
|
17
18
|
# Sends the file. This uses a server-appropriate method (such as X-Sendfile)
|
@@ -52,7 +53,7 @@ module ActionController #:nodoc:
|
|
52
53
|
#
|
53
54
|
# Show a 404 page in the browser:
|
54
55
|
#
|
55
|
-
# send_file '/path/to/404.html', type: 'text/html; charset=utf-8', status: 404
|
56
|
+
# send_file '/path/to/404.html', type: 'text/html; charset=utf-8', disposition: 'inline', status: 404
|
56
57
|
#
|
57
58
|
# Read about the other Content-* HTTP headers if you'd like to
|
58
59
|
# provide the user with more information (such as Content-Description) in
|
@@ -132,10 +133,8 @@ module ActionController #:nodoc:
|
|
132
133
|
end
|
133
134
|
|
134
135
|
disposition = options.fetch(:disposition, DEFAULT_SEND_FILE_DISPOSITION)
|
135
|
-
|
136
|
-
|
137
|
-
disposition += %(; filename="#{options[:filename]}") if options[:filename]
|
138
|
-
headers["Content-Disposition"] = disposition
|
136
|
+
if disposition
|
137
|
+
headers["Content-Disposition"] = ActionDispatch::Http::ContentDisposition.format(disposition: disposition, filename: options[:filename])
|
139
138
|
end
|
140
139
|
|
141
140
|
headers["Content-Transfer-Encoding"] = "binary"
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActionController
|
4
|
+
# Allows configuring default headers that will be automatically merged into
|
5
|
+
# each response.
|
6
|
+
module DefaultHeaders
|
7
|
+
extend ActiveSupport::Concern
|
8
|
+
|
9
|
+
module ClassMethods
|
10
|
+
def make_response!(request)
|
11
|
+
ActionDispatch::Response.create.tap do |res|
|
12
|
+
res.request = request
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -26,10 +26,8 @@ module ActionController
|
|
26
26
|
included do
|
27
27
|
class_attribute :etag_with_template_digest, default: true
|
28
28
|
|
29
|
-
|
30
|
-
|
31
|
-
determine_template_etag(options) if etag_with_template_digest
|
32
|
-
end
|
29
|
+
etag do |options|
|
30
|
+
determine_template_etag(options) if etag_with_template_digest
|
33
31
|
end
|
34
32
|
end
|
35
33
|
|
@@ -46,12 +44,12 @@ module ActionController
|
|
46
44
|
# template digest from the ETag.
|
47
45
|
def pick_template_for_etag(options)
|
48
46
|
unless options[:template] == false
|
49
|
-
options[:template] ||
|
47
|
+
options[:template] || lookup_context.find_all(action_name, _prefixes).first&.virtual_path
|
50
48
|
end
|
51
49
|
end
|
52
50
|
|
53
51
|
def lookup_and_digest_template(template)
|
54
|
-
ActionView::Digestor.digest name: template, finder: lookup_context
|
52
|
+
ActionView::Digestor.digest name: template, format: nil, finder: lookup_context
|
55
53
|
end
|
56
54
|
end
|
57
55
|
end
|
@@ -22,12 +22,45 @@ module ActionController
|
|
22
22
|
end
|
23
23
|
end
|
24
24
|
|
25
|
-
class
|
25
|
+
class UrlGenerationError < ActionControllerError #:nodoc:
|
26
|
+
attr_reader :routes, :route_name, :method_name
|
27
|
+
|
28
|
+
def initialize(message, routes = nil, route_name = nil, method_name = nil)
|
29
|
+
@routes = routes
|
30
|
+
@route_name = route_name
|
31
|
+
@method_name = method_name
|
32
|
+
|
33
|
+
super(message)
|
34
|
+
end
|
35
|
+
|
36
|
+
class Correction
|
37
|
+
def initialize(error)
|
38
|
+
@error = error
|
39
|
+
end
|
40
|
+
|
41
|
+
def corrections
|
42
|
+
if @error.method_name
|
43
|
+
maybe_these = @error.routes.named_routes.helper_names.grep(/#{@error.route_name}/)
|
44
|
+
maybe_these -= [@error.method_name.to_s] # remove exact match
|
45
|
+
|
46
|
+
maybe_these.sort_by { |n|
|
47
|
+
DidYouMean::Jaro.distance(@error.route_name, n)
|
48
|
+
}.reverse.first(4)
|
49
|
+
else
|
50
|
+
[]
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# We may not have DYM, and DYM might not let us register error handlers
|
56
|
+
if defined?(DidYouMean) && DidYouMean.respond_to?(:correct_error)
|
57
|
+
DidYouMean.correct_error(self, Correction)
|
58
|
+
end
|
26
59
|
end
|
27
60
|
|
28
61
|
class MethodNotAllowed < ActionControllerError #:nodoc:
|
29
62
|
def initialize(*allowed_methods)
|
30
|
-
super("Only #{allowed_methods.to_sentence
|
63
|
+
super("Only #{allowed_methods.to_sentence} requests are allowed.")
|
31
64
|
end
|
32
65
|
end
|
33
66
|
|
@@ -50,4 +83,25 @@ module ActionController
|
|
50
83
|
|
51
84
|
class UnknownFormat < ActionControllerError #:nodoc:
|
52
85
|
end
|
86
|
+
|
87
|
+
# Raised when a nested respond_to is triggered and the content types of each
|
88
|
+
# are incompatible. For example:
|
89
|
+
#
|
90
|
+
# respond_to do |outer_type|
|
91
|
+
# outer_type.js do
|
92
|
+
# respond_to do |inner_type|
|
93
|
+
# inner_type.html { render body: "HTML" }
|
94
|
+
# end
|
95
|
+
# end
|
96
|
+
# end
|
97
|
+
class RespondToMismatchError < ActionControllerError
|
98
|
+
DEFAULT_MESSAGE = "respond_to was called multiple times and matched with conflicting formats in this action. Please note that you may only call respond_to and match on a single format per action."
|
99
|
+
|
100
|
+
def initialize(message = nil)
|
101
|
+
super(message || DEFAULT_MESSAGE)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
class MissingExactTemplate < UnknownFormat #:nodoc:
|
106
|
+
end
|
53
107
|
end
|
@@ -36,7 +36,7 @@ module ActionController #:nodoc:
|
|
36
36
|
define_method(type) do
|
37
37
|
request.flash[type]
|
38
38
|
end
|
39
|
-
helper_method
|
39
|
+
helper_method(type) if respond_to?(:helper_method)
|
40
40
|
|
41
41
|
self._flash_types += [type]
|
42
42
|
end
|
@@ -44,18 +44,18 @@ module ActionController #:nodoc:
|
|
44
44
|
end
|
45
45
|
|
46
46
|
private
|
47
|
-
def redirect_to(options = {},
|
47
|
+
def redirect_to(options = {}, response_options_and_flash = {}) #:doc:
|
48
48
|
self.class._flash_types.each do |flash_type|
|
49
|
-
if type =
|
49
|
+
if type = response_options_and_flash.delete(flash_type)
|
50
50
|
flash[flash_type] = type
|
51
51
|
end
|
52
52
|
end
|
53
53
|
|
54
|
-
if other_flashes =
|
54
|
+
if other_flashes = response_options_and_flash.delete(:flash)
|
55
55
|
flash.update(other_flashes)
|
56
56
|
end
|
57
57
|
|
58
|
-
super(options,
|
58
|
+
super(options, response_options_and_flash)
|
59
59
|
end
|
60
60
|
end
|
61
61
|
end
|
@@ -29,19 +29,22 @@ module ActionController
|
|
29
29
|
content_type = options.delete(:content_type)
|
30
30
|
|
31
31
|
options.each do |key, value|
|
32
|
-
headers[key.to_s.
|
32
|
+
headers[key.to_s.split(/[-_]/).each { |v| v[0] = v[0].upcase }.join("-")] = value.to_s
|
33
33
|
end
|
34
34
|
|
35
35
|
self.status = status
|
36
36
|
self.location = url_for(location) if location
|
37
37
|
|
38
|
-
self.response_body = ""
|
39
|
-
|
40
38
|
if include_content?(response_code)
|
41
|
-
self.
|
39
|
+
unless self.media_type
|
40
|
+
self.content_type = content_type || (Mime[formats.first] if formats) || Mime[:html]
|
41
|
+
end
|
42
|
+
|
42
43
|
response.charset = false
|
43
44
|
end
|
44
45
|
|
46
|
+
self.response_body = ""
|
47
|
+
|
45
48
|
true
|
46
49
|
end
|
47
50
|
|
@@ -11,7 +11,12 @@ module ActionController
|
|
11
11
|
#
|
12
12
|
# In previous versions of \Rails the controller will include a helper which
|
13
13
|
# matches the name of the controller, e.g., <tt>MyController</tt> will automatically
|
14
|
-
# include <tt>MyHelper</tt>.
|
14
|
+
# include <tt>MyHelper</tt>. You can revert to the old behavior with the following:
|
15
|
+
#
|
16
|
+
# # config/application.rb
|
17
|
+
# class Application < Rails::Application
|
18
|
+
# config.action_controller.include_all_helpers = false
|
19
|
+
# end
|
15
20
|
#
|
16
21
|
# Additional helpers can be specified using the +helper+ class method in ActionController::Base or any
|
17
22
|
# controller which inherits from it.
|
@@ -34,7 +39,7 @@ module ActionController
|
|
34
39
|
# end
|
35
40
|
# end
|
36
41
|
#
|
37
|
-
# Then, in any view rendered by <tt>
|
42
|
+
# Then, in any view rendered by <tt>EventsController</tt>, the <tt>format_time</tt> method can be called:
|
38
43
|
#
|
39
44
|
# <% @events.each do |event| -%>
|
40
45
|
# <p>
|
@@ -73,9 +78,14 @@ module ActionController
|
|
73
78
|
end
|
74
79
|
|
75
80
|
# Provides a proxy to access helper methods from outside the view.
|
81
|
+
#
|
82
|
+
# Note that the proxy is rendered under a different view context.
|
83
|
+
# This may cause incorrect behaviour with capture methods. Consider
|
84
|
+
# using {helper}[rdoc-ref:AbstractController::Helpers::ClassMethods#helper]
|
85
|
+
# instead when using +capture+.
|
76
86
|
def helpers
|
77
87
|
@helper_proxy ||= begin
|
78
|
-
proxy = ActionView::Base.
|
88
|
+
proxy = ActionView::Base.empty
|
79
89
|
proxy.config = config.inheritable_copy
|
80
90
|
proxy.extend(_helpers)
|
81
91
|
end
|
@@ -100,8 +110,7 @@ module ActionController
|
|
100
110
|
# # => ["application", "chart", "rubygems"]
|
101
111
|
def all_helpers_from_path(path)
|
102
112
|
helpers = Array(path).flat_map do |_path|
|
103
|
-
|
104
|
-
names = Dir["#{_path}/**/*_helper.rb"].map { |file| file.sub(extract, '\1'.freeze) }
|
113
|
+
names = Dir["#{_path}/**/*_helper.rb"].map { |file| file[_path.to_s.size + 1..-"_helper.rb".size - 1] }
|
105
114
|
names.sort!
|
106
115
|
end
|
107
116
|
helpers.uniq!
|
@@ -56,8 +56,9 @@ module ActionController
|
|
56
56
|
# In your integration tests, you can do something like this:
|
57
57
|
#
|
58
58
|
# def test_access_granted_from_xml
|
59
|
-
#
|
60
|
-
#
|
59
|
+
# authorization = ActionController::HttpAuthentication::Basic.encode_credentials(users(:dhh).name, users(:dhh).password)
|
60
|
+
#
|
61
|
+
# get "/notes/1.xml", headers: { 'HTTP_AUTHORIZATION' => authorization }
|
61
62
|
#
|
62
63
|
# assert_equal 200, status
|
63
64
|
# end
|
@@ -68,21 +69,22 @@ module ActionController
|
|
68
69
|
extend ActiveSupport::Concern
|
69
70
|
|
70
71
|
module ClassMethods
|
71
|
-
def http_basic_authenticate_with(
|
72
|
-
before_action(options
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
72
|
+
def http_basic_authenticate_with(name:, password:, realm: nil, **options)
|
73
|
+
before_action(options) { http_basic_authenticate_or_request_with name: name, password: password, realm: realm }
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def http_basic_authenticate_or_request_with(name:, password:, realm: nil, message: nil)
|
78
|
+
authenticate_or_request_with_http_basic(realm, message) do |given_name, given_password|
|
79
|
+
# This comparison uses & so that it doesn't short circuit and
|
80
|
+
# uses `secure_compare` so that length information isn't leaked.
|
81
|
+
ActiveSupport::SecurityUtils.secure_compare(given_name, name) &
|
82
|
+
ActiveSupport::SecurityUtils.secure_compare(given_password, password)
|
81
83
|
end
|
82
84
|
end
|
83
85
|
|
84
|
-
def authenticate_or_request_with_http_basic(realm =
|
85
|
-
authenticate_with_http_basic(&login_procedure) || request_http_basic_authentication(realm, message)
|
86
|
+
def authenticate_or_request_with_http_basic(realm = nil, message = nil, &login_procedure)
|
87
|
+
authenticate_with_http_basic(&login_procedure) || request_http_basic_authentication(realm || "Application", message)
|
86
88
|
end
|
87
89
|
|
88
90
|
def authenticate_with_http_basic(&login_procedure)
|
@@ -126,7 +128,7 @@ module ActionController
|
|
126
128
|
|
127
129
|
def authentication_request(controller, realm, message)
|
128
130
|
message ||= "HTTP Basic: Access denied.\n"
|
129
|
-
controller.headers["WWW-Authenticate"] = %(Basic realm="#{realm.tr('"'
|
131
|
+
controller.headers["WWW-Authenticate"] = %(Basic realm="#{realm.tr('"', "")}")
|
130
132
|
controller.status = 401
|
131
133
|
controller.response_body = message
|
132
134
|
end
|
@@ -136,7 +138,7 @@ module ActionController
|
|
136
138
|
#
|
137
139
|
# === Simple \Digest example
|
138
140
|
#
|
139
|
-
# require
|
141
|
+
# require "digest/md5"
|
140
142
|
# class PostsController < ApplicationController
|
141
143
|
# REALM = "SuperSecret"
|
142
144
|
# USERS = {"dhh" => "secret", #plain text password
|
@@ -389,10 +391,9 @@ module ActionController
|
|
389
391
|
# In your integration tests, you can do something like this:
|
390
392
|
#
|
391
393
|
# def test_access_granted_from_xml
|
392
|
-
#
|
393
|
-
#
|
394
|
-
#
|
395
|
-
# )
|
394
|
+
# authorization = ActionController::HttpAuthentication::Token.encode_credentials(users(:dhh).token)
|
395
|
+
#
|
396
|
+
# get "/notes/1.xml", headers: { 'HTTP_AUTHORIZATION' => authorization }
|
396
397
|
#
|
397
398
|
# assert_equal 200, status
|
398
399
|
# end
|
@@ -474,7 +475,7 @@ module ActionController
|
|
474
475
|
|
475
476
|
# This removes the <tt>"</tt> characters wrapping the value.
|
476
477
|
def rewrite_param_values(array_params)
|
477
|
-
array_params.each { |param| (param[1] || ""
|
478
|
+
array_params.each { |param| (param[1] || +"").gsub! %r/^"|"$/, "" }
|
478
479
|
end
|
479
480
|
|
480
481
|
# This method takes an authorization body and splits up the key-value
|
@@ -483,7 +484,7 @@ module ActionController
|
|
483
484
|
def raw_params(auth)
|
484
485
|
_raw_params = auth.sub(TOKEN_REGEX, "").split(/\s*#{AUTHN_PAIR_DELIMITERS}\s*/)
|
485
486
|
|
486
|
-
if !
|
487
|
+
if !_raw_params.first&.start_with?(TOKEN_KEY)
|
487
488
|
_raw_params[0] = "#{TOKEN_KEY}#{_raw_params.first}"
|
488
489
|
end
|
489
490
|
|
@@ -511,7 +512,7 @@ module ActionController
|
|
511
512
|
# Returns nothing.
|
512
513
|
def authentication_request(controller, realm, message = nil)
|
513
514
|
message ||= "HTTP Token: Access denied.\n"
|
514
|
-
controller.headers["WWW-Authenticate"] = %(Token realm="#{realm.tr('"'
|
515
|
+
controller.headers["WWW-Authenticate"] = %(Token realm="#{realm.tr('"', "")}")
|
515
516
|
controller.__send__ :render, plain: message, status: :unauthorized
|
516
517
|
end
|
517
518
|
end
|
@@ -22,7 +22,7 @@ module ActionController
|
|
22
22
|
# Third, if we DON'T find a template AND the request is a page load in a web
|
23
23
|
# browser (technically, a non-XHR GET request for an HTML response) where
|
24
24
|
# you reasonably expect to have rendered a template, then we raise
|
25
|
-
# <tt>
|
25
|
+
# <tt>ActionController::MissingExactTemplate</tt> with an explanation.
|
26
26
|
#
|
27
27
|
# Finally, if we DON'T find a template AND the request isn't a browser page
|
28
28
|
# load, then we implicitly respond with <tt>204 No Content</tt>.
|
@@ -30,9 +30,9 @@ module ActionController
|
|
30
30
|
# :stopdoc:
|
31
31
|
include BasicImplicitRender
|
32
32
|
|
33
|
-
def default_render
|
33
|
+
def default_render
|
34
34
|
if template_exists?(action_name.to_s, _prefixes, variants: request.variant)
|
35
|
-
render
|
35
|
+
render
|
36
36
|
elsif any_templates?(action_name.to_s, _prefixes)
|
37
37
|
message = "#{self.class.name}\##{action_name} is missing a template " \
|
38
38
|
"for this request format and variant.\n" \
|
@@ -41,18 +41,8 @@ module ActionController
|
|
41
41
|
|
42
42
|
raise ActionController::UnknownFormat, message
|
43
43
|
elsif interactive_browser_request?
|
44
|
-
message = "#{self.class.name}\##{action_name} is missing a template "
|
45
|
-
|
46
|
-
"request.formats: #{request.formats.map(&:to_s).inspect}\n" \
|
47
|
-
"request.variant: #{request.variant.inspect}\n\n" \
|
48
|
-
"NOTE! For XHR/Ajax or API requests, this action would normally " \
|
49
|
-
"respond with 204 No Content: an empty white screen. Since you're " \
|
50
|
-
"loading it in a web browser, we assume that you expected to " \
|
51
|
-
"actually render a template, not nothing, so we're showing an " \
|
52
|
-
"error to be extra-clear. If you expect 204 No Content, carry on. " \
|
53
|
-
"That's what you'll get from an XHR or API request. Give it a shot."
|
54
|
-
|
55
|
-
raise ActionController::UnknownFormat, message
|
44
|
+
message = "#{self.class.name}\##{action_name} is missing a template for request formats: #{request.formats.map(&:to_s).join(',')}"
|
45
|
+
raise ActionController::MissingExactTemplate, message
|
56
46
|
else
|
57
47
|
logger.info "No template found for #{self.class.name}\##{action_name}, rendering head :no_content" if logger
|
58
48
|
super
|