actionpack 5.2.4.4 → 6.1.1

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.

Files changed (155) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +264 -322
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +4 -3
  5. data/lib/abstract_controller.rb +1 -0
  6. data/lib/abstract_controller/base.rb +38 -4
  7. data/lib/abstract_controller/caching.rb +1 -1
  8. data/lib/abstract_controller/caching/fragments.rb +6 -22
  9. data/lib/abstract_controller/callbacks.rb +14 -2
  10. data/lib/abstract_controller/collector.rb +1 -2
  11. data/lib/abstract_controller/helpers.rb +106 -90
  12. data/lib/abstract_controller/railties/routes_helpers.rb +1 -1
  13. data/lib/abstract_controller/rendering.rb +9 -9
  14. data/lib/abstract_controller/translation.rb +11 -5
  15. data/lib/action_controller.rb +7 -4
  16. data/lib/action_controller/api.rb +4 -3
  17. data/lib/action_controller/base.rb +6 -9
  18. data/lib/action_controller/caching.rb +1 -3
  19. data/lib/action_controller/log_subscriber.rb +10 -7
  20. data/lib/action_controller/metal.rb +10 -8
  21. data/lib/action_controller/metal/basic_implicit_render.rb +1 -1
  22. data/lib/action_controller/metal/conditional_get.rb +19 -5
  23. data/lib/action_controller/metal/content_security_policy.rb +1 -2
  24. data/lib/action_controller/metal/cookies.rb +3 -1
  25. data/lib/action_controller/metal/data_streaming.rb +6 -7
  26. data/lib/action_controller/metal/default_headers.rb +17 -0
  27. data/lib/action_controller/metal/etag_with_template_digest.rb +3 -5
  28. data/lib/action_controller/metal/exceptions.rb +56 -2
  29. data/lib/action_controller/metal/flash.rb +5 -5
  30. data/lib/action_controller/metal/head.rb +7 -4
  31. data/lib/action_controller/metal/helpers.rb +14 -5
  32. data/lib/action_controller/metal/http_authentication.rb +24 -23
  33. data/lib/action_controller/metal/implicit_render.rb +5 -15
  34. data/lib/action_controller/metal/instrumentation.rb +13 -14
  35. data/lib/action_controller/metal/live.rb +30 -32
  36. data/lib/action_controller/metal/logging.rb +20 -0
  37. data/lib/action_controller/metal/mime_responds.rb +19 -4
  38. data/lib/action_controller/metal/parameter_encoding.rb +35 -4
  39. data/lib/action_controller/metal/params_wrapper.rb +31 -22
  40. data/lib/action_controller/metal/permissions_policy.rb +46 -0
  41. data/lib/action_controller/metal/redirecting.rb +6 -6
  42. data/lib/action_controller/metal/renderers.rb +4 -4
  43. data/lib/action_controller/metal/rendering.rb +8 -3
  44. data/lib/action_controller/metal/request_forgery_protection.rb +62 -34
  45. data/lib/action_controller/metal/rescue.rb +1 -1
  46. data/lib/action_controller/metal/streaming.rb +0 -1
  47. data/lib/action_controller/metal/strong_parameters.rb +167 -58
  48. data/lib/action_controller/metal/url_for.rb +1 -1
  49. data/lib/action_controller/railties/helpers.rb +1 -1
  50. data/lib/action_controller/renderer.rb +37 -13
  51. data/lib/action_controller/template_assertions.rb +1 -1
  52. data/lib/action_controller/test_case.rb +70 -65
  53. data/lib/action_dispatch.rb +9 -3
  54. data/lib/action_dispatch/http/cache.rb +26 -21
  55. data/lib/action_dispatch/http/content_disposition.rb +45 -0
  56. data/lib/action_dispatch/http/content_security_policy.rb +33 -19
  57. data/lib/action_dispatch/http/filter_parameters.rb +9 -8
  58. data/lib/action_dispatch/http/filter_redirect.rb +2 -3
  59. data/lib/action_dispatch/http/headers.rb +4 -4
  60. data/lib/action_dispatch/http/mime_negotiation.rb +26 -13
  61. data/lib/action_dispatch/http/mime_type.rb +42 -23
  62. data/lib/action_dispatch/http/parameters.rb +14 -23
  63. data/lib/action_dispatch/http/permissions_policy.rb +173 -0
  64. data/lib/action_dispatch/http/request.rb +45 -22
  65. data/lib/action_dispatch/http/response.rb +45 -25
  66. data/lib/action_dispatch/http/upload.rb +9 -1
  67. data/lib/action_dispatch/http/url.rb +82 -82
  68. data/lib/action_dispatch/journey.rb +0 -2
  69. data/lib/action_dispatch/journey/formatter.rb +54 -30
  70. data/lib/action_dispatch/journey/gtg/builder.rb +22 -37
  71. data/lib/action_dispatch/journey/gtg/simulator.rb +8 -7
  72. data/lib/action_dispatch/journey/gtg/transition_table.rb +6 -5
  73. data/lib/action_dispatch/journey/nfa/dot.rb +0 -11
  74. data/lib/action_dispatch/journey/nodes/node.rb +13 -11
  75. data/lib/action_dispatch/journey/parser.rb +13 -13
  76. data/lib/action_dispatch/journey/parser.y +1 -1
  77. data/lib/action_dispatch/journey/path/pattern.rb +19 -21
  78. data/lib/action_dispatch/journey/route.rb +10 -20
  79. data/lib/action_dispatch/journey/router.rb +26 -34
  80. data/lib/action_dispatch/journey/router/utils.rb +14 -12
  81. data/lib/action_dispatch/journey/routes.rb +0 -2
  82. data/lib/action_dispatch/journey/scanner.rb +10 -4
  83. data/lib/action_dispatch/journey/visitors.rb +1 -4
  84. data/lib/action_dispatch/middleware/actionable_exceptions.rb +46 -0
  85. data/lib/action_dispatch/middleware/callbacks.rb +2 -4
  86. data/lib/action_dispatch/middleware/cookies.rb +128 -109
  87. data/lib/action_dispatch/middleware/debug_exceptions.rb +43 -66
  88. data/lib/action_dispatch/middleware/debug_locks.rb +5 -5
  89. data/lib/action_dispatch/middleware/debug_view.rb +66 -0
  90. data/lib/action_dispatch/middleware/exception_wrapper.rb +75 -30
  91. data/lib/action_dispatch/middleware/flash.rb +1 -1
  92. data/lib/action_dispatch/middleware/host_authorization.rb +121 -0
  93. data/lib/action_dispatch/middleware/public_exceptions.rb +6 -3
  94. data/lib/action_dispatch/middleware/remote_ip.rb +14 -16
  95. data/lib/action_dispatch/middleware/request_id.rb +5 -6
  96. data/lib/action_dispatch/middleware/session/abstract_store.rb +2 -3
  97. data/lib/action_dispatch/middleware/session/cookie_store.rb +3 -9
  98. data/lib/action_dispatch/middleware/show_exceptions.rb +3 -2
  99. data/lib/action_dispatch/middleware/ssl.rb +20 -15
  100. data/lib/action_dispatch/middleware/stack.rb +56 -2
  101. data/lib/action_dispatch/middleware/static.rb +153 -93
  102. data/lib/action_dispatch/middleware/templates/rescues/_actions.html.erb +13 -0
  103. data/lib/action_dispatch/middleware/templates/rescues/_actions.text.erb +0 -0
  104. data/lib/action_dispatch/middleware/templates/rescues/_message_and_suggestions.html.erb +22 -0
  105. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +3 -1
  106. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.text.erb +1 -1
  107. data/lib/action_dispatch/middleware/templates/rescues/_source.html.erb +4 -2
  108. data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +45 -35
  109. data/lib/action_dispatch/middleware/templates/rescues/blocked_host.html.erb +7 -0
  110. data/lib/action_dispatch/middleware/templates/rescues/blocked_host.text.erb +5 -0
  111. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +23 -4
  112. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +1 -1
  113. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +6 -3
  114. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.text.erb +3 -1
  115. data/lib/action_dispatch/middleware/templates/rescues/layout.erb +104 -8
  116. data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.html.erb +19 -0
  117. data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.text.erb +3 -0
  118. data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +2 -2
  119. data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +1 -1
  120. data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +2 -2
  121. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +1 -1
  122. data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +24 -1
  123. data/lib/action_dispatch/railtie.rb +8 -2
  124. data/lib/action_dispatch/request/session.rb +10 -9
  125. data/lib/action_dispatch/request/utils.rb +26 -2
  126. data/lib/action_dispatch/routing.rb +21 -20
  127. data/lib/action_dispatch/routing/inspector.rb +100 -52
  128. data/lib/action_dispatch/routing/mapper.rb +155 -103
  129. data/lib/action_dispatch/routing/polymorphic_routes.rb +13 -15
  130. data/lib/action_dispatch/routing/redirection.rb +3 -3
  131. data/lib/action_dispatch/routing/route_set.rb +71 -69
  132. data/lib/action_dispatch/routing/url_for.rb +2 -2
  133. data/lib/action_dispatch/system_test_case.rb +54 -11
  134. data/lib/action_dispatch/system_testing/browser.rb +53 -16
  135. data/lib/action_dispatch/system_testing/driver.rb +11 -3
  136. data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +49 -7
  137. data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +8 -10
  138. data/lib/action_dispatch/testing/assertion_response.rb +0 -1
  139. data/lib/action_dispatch/testing/assertions.rb +1 -1
  140. data/lib/action_dispatch/testing/assertions/response.rb +4 -7
  141. data/lib/action_dispatch/testing/assertions/routing.rb +20 -8
  142. data/lib/action_dispatch/testing/integration.rb +61 -28
  143. data/lib/action_dispatch/testing/request_encoder.rb +2 -2
  144. data/lib/action_dispatch/testing/test_process.rb +29 -4
  145. data/lib/action_dispatch/testing/test_request.rb +3 -3
  146. data/lib/action_dispatch/testing/test_response.rb +4 -32
  147. data/lib/action_pack.rb +1 -1
  148. data/lib/action_pack/gem_version.rb +4 -4
  149. metadata +38 -26
  150. data/lib/action_controller/metal/force_ssl.rb +0 -99
  151. data/lib/action_dispatch/http/parameter_filter.rb +0 -86
  152. data/lib/action_dispatch/journey/nfa/builder.rb +0 -78
  153. data/lib/action_dispatch/journey/nfa/simulator.rb +0 -49
  154. data/lib/action_dispatch/journey/nfa/transition_table.rb +0 -120
  155. data/lib/action_dispatch/system_testing/test_helpers/undef_methods.rb +0 -26
@@ -57,7 +57,6 @@ module ActionDispatch
57
57
  query_parameters.dup
58
58
  end
59
59
  params.merge!(path_parameters)
60
- params = set_binary_encoding(params, params[:controller], params[:action])
61
60
  set_header("action_dispatch.request.parameters", params)
62
61
  params
63
62
  end
@@ -66,7 +65,7 @@ module ActionDispatch
66
65
  def path_parameters=(parameters) #:nodoc:
67
66
  delete_header("action_dispatch.request.parameters")
68
67
 
69
- parameters = set_binary_encoding(parameters, parameters[:controller], parameters[:action])
68
+ parameters = Request::Utils.set_binary_encoding(self, parameters, parameters[:controller], parameters[:action])
70
69
  # If any of the path parameters has an invalid encoding then
71
70
  # raise since it's likely to trigger errors further on.
72
71
  Request::Utils.check_param_encoding(parameters)
@@ -85,24 +84,6 @@ module ActionDispatch
85
84
  end
86
85
 
87
86
  private
88
-
89
- def set_binary_encoding(params, controller, action)
90
- return params unless controller && controller.valid_encoding?
91
-
92
- if binary_params_for?(controller, action)
93
- ActionDispatch::Request::Utils.each_param_value(params) do |param|
94
- param.force_encoding ::Encoding::ASCII_8BIT
95
- end
96
- end
97
- params
98
- end
99
-
100
- def binary_params_for?(controller, action)
101
- controller_class_for(controller).binary_params_for?(action)
102
- rescue NameError
103
- false
104
- end
105
-
106
87
  def parse_formatted_parameters(parsers)
107
88
  return yield if content_length.zero? || content_mime_type.nil?
108
89
 
@@ -111,13 +92,23 @@ module ActionDispatch
111
92
  begin
112
93
  strategy.call(raw_post)
113
94
  rescue # JSON or Ruby code block errors.
114
- my_logger = logger || ActiveSupport::Logger.new($stderr)
115
- my_logger.debug "Error occurred while parsing request parameters.\nContents:\n\n#{raw_post}"
116
-
95
+ log_parse_error_once
117
96
  raise ParseError
118
97
  end
119
98
  end
120
99
 
100
+ def log_parse_error_once
101
+ @parse_error_logged ||= begin
102
+ parse_logger = logger || ActiveSupport::Logger.new($stderr)
103
+ parse_logger.debug <<~MSG.chomp
104
+ Error occurred while parsing request parameters.
105
+ Contents:
106
+
107
+ #{raw_post}
108
+ MSG
109
+ end
110
+ end
111
+
121
112
  def params_parsers
122
113
  ActionDispatch::Request.parameter_parsers
123
114
  end
@@ -0,0 +1,173 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/object/deep_dup"
4
+
5
+ module ActionDispatch #:nodoc:
6
+ class PermissionsPolicy
7
+ class Middleware
8
+ CONTENT_TYPE = "Content-Type"
9
+ # The Feature-Policy header has been renamed to Permissions-Policy.
10
+ # The Permissions-Policy requires a different implementation and isn't
11
+ # yet supported by all browsers. To avoid having to rename this
12
+ # middleware in the future we use the new name for the middleware but
13
+ # keep the old header name and implementation for now.
14
+ POLICY = "Feature-Policy"
15
+
16
+ def initialize(app)
17
+ @app = app
18
+ end
19
+
20
+ def call(env)
21
+ request = ActionDispatch::Request.new(env)
22
+ _, headers, _ = response = @app.call(env)
23
+
24
+ return response unless html_response?(headers)
25
+ return response if policy_present?(headers)
26
+
27
+ if policy = request.permissions_policy
28
+ headers[POLICY] = policy.build(request.controller_instance)
29
+ end
30
+
31
+ if policy_empty?(policy)
32
+ headers.delete(POLICY)
33
+ end
34
+
35
+ response
36
+ end
37
+
38
+ private
39
+ def html_response?(headers)
40
+ if content_type = headers[CONTENT_TYPE]
41
+ /html/.match?(content_type)
42
+ end
43
+ end
44
+
45
+ def policy_present?(headers)
46
+ headers[POLICY]
47
+ end
48
+
49
+ def policy_empty?(policy)
50
+ policy&.directives&.empty?
51
+ end
52
+ end
53
+
54
+ module Request
55
+ POLICY = "action_dispatch.permissions_policy"
56
+
57
+ def permissions_policy
58
+ get_header(POLICY)
59
+ end
60
+
61
+ def permissions_policy=(policy)
62
+ set_header(POLICY, policy)
63
+ end
64
+ end
65
+
66
+ MAPPINGS = {
67
+ self: "'self'",
68
+ none: "'none'",
69
+ }.freeze
70
+
71
+ # List of available permissions can be found at
72
+ # https://github.com/w3c/webappsec-permissions-policy/blob/master/features.md#policy-controlled-features
73
+ DIRECTIVES = {
74
+ accelerometer: "accelerometer",
75
+ ambient_light_sensor: "ambient-light-sensor",
76
+ autoplay: "autoplay",
77
+ camera: "camera",
78
+ encrypted_media: "encrypted-media",
79
+ fullscreen: "fullscreen",
80
+ geolocation: "geolocation",
81
+ gyroscope: "gyroscope",
82
+ magnetometer: "magnetometer",
83
+ microphone: "microphone",
84
+ midi: "midi",
85
+ payment: "payment",
86
+ picture_in_picture: "picture-in-picture",
87
+ speaker: "speaker",
88
+ usb: "usb",
89
+ vibrate: "vibrate",
90
+ vr: "vr",
91
+ }.freeze
92
+
93
+ private_constant :MAPPINGS, :DIRECTIVES
94
+
95
+ attr_reader :directives
96
+
97
+ def initialize
98
+ @directives = {}
99
+ yield self if block_given?
100
+ end
101
+
102
+ def initialize_copy(other)
103
+ @directives = other.directives.deep_dup
104
+ end
105
+
106
+ DIRECTIVES.each do |name, directive|
107
+ define_method(name) do |*sources|
108
+ if sources.first
109
+ @directives[directive] = apply_mappings(sources)
110
+ else
111
+ @directives.delete(directive)
112
+ end
113
+ end
114
+ end
115
+
116
+ def build(context = nil)
117
+ build_directives(context).compact.join("; ")
118
+ end
119
+
120
+ private
121
+ def apply_mappings(sources)
122
+ sources.map do |source|
123
+ case source
124
+ when Symbol
125
+ apply_mapping(source)
126
+ when String, Proc
127
+ source
128
+ else
129
+ raise ArgumentError, "Invalid HTTP permissions policy source: #{source.inspect}"
130
+ end
131
+ end
132
+ end
133
+
134
+ def apply_mapping(source)
135
+ MAPPINGS.fetch(source) do
136
+ raise ArgumentError, "Unknown HTTP permissions policy source mapping: #{source.inspect}"
137
+ end
138
+ end
139
+
140
+ def build_directives(context)
141
+ @directives.map do |directive, sources|
142
+ if sources.is_a?(Array)
143
+ "#{directive} #{build_directive(sources, context).join(' ')}"
144
+ elsif sources
145
+ directive
146
+ else
147
+ nil
148
+ end
149
+ end
150
+ end
151
+
152
+ def build_directive(sources, context)
153
+ sources.map { |source| resolve_source(source, context) }
154
+ end
155
+
156
+ def resolve_source(source, context)
157
+ case source
158
+ when String
159
+ source
160
+ when Symbol
161
+ source.to_s
162
+ when Proc
163
+ if context.nil?
164
+ raise RuntimeError, "Missing context for the dynamic permissions policy source: #{source.inspect}"
165
+ else
166
+ context.instance_exec(&source)
167
+ end
168
+ else
169
+ raise RuntimeError, "Unexpected permissions policy source: #{source.inspect}"
170
+ end
171
+ end
172
+ end
173
+ end
@@ -23,6 +23,7 @@ module ActionDispatch
23
23
  include ActionDispatch::Http::FilterParameters
24
24
  include ActionDispatch::Http::URL
25
25
  include ActionDispatch::ContentSecurityPolicy::Request
26
+ include ActionDispatch::PermissionsPolicy::Request
26
27
  include Rack::Request::Env
27
28
 
28
29
  autoload :Session, "action_dispatch/request/session"
@@ -44,11 +45,14 @@ module ActionDispatch
44
45
  SERVER_ADDR
45
46
  ].freeze
46
47
 
48
+ # TODO: Remove SERVER_ADDR when we remove support to Rack 2.1.
49
+ # See https://github.com/rack/rack/commit/c173b188d81ee437b588c1e046a1c9f031dea550
47
50
  ENV_METHODS.each do |env|
48
51
  class_eval <<-METHOD, __FILE__, __LINE__ + 1
49
- def #{env.sub(/^HTTP_/n, '').downcase} # def accept_charset
50
- get_header "#{env}".freeze # get_header "HTTP_ACCEPT_CHARSET".freeze
51
- end # end
52
+ # frozen_string_literal: true
53
+ def #{env.delete_prefix("HTTP_").downcase} # def accept_charset
54
+ get_header "#{env}" # get_header "HTTP_ACCEPT_CHARSET"
55
+ end # end
52
56
  METHOD
53
57
  end
54
58
 
@@ -72,7 +76,7 @@ module ActionDispatch
72
76
  PASS_NOT_FOUND = Class.new { # :nodoc:
73
77
  def self.action(_); self; end
74
78
  def self.call(_); [404, { "X-Cascade" => "pass" }, []]; end
75
- def self.binary_params_for?(action); false; end
79
+ def self.action_encoding_template(action); false; end
76
80
  }
77
81
 
78
82
  def controller_class
@@ -84,8 +88,16 @@ module ActionDispatch
84
88
  def controller_class_for(name)
85
89
  if name
86
90
  controller_param = name.underscore
87
- const_name = "#{controller_param.camelize}Controller"
88
- ActiveSupport::Dependencies.constantize(const_name)
91
+ const_name = controller_param.camelize << "Controller"
92
+ begin
93
+ ActiveSupport::Dependencies.constantize(const_name)
94
+ rescue NameError => error
95
+ if error.missing_name == const_name || const_name.start_with?("#{error.missing_name}::")
96
+ raise MissingController.new(error.message, error.name)
97
+ else
98
+ raise
99
+ end
100
+ end
89
101
  else
90
102
  PASS_NOT_FOUND
91
103
  end
@@ -125,6 +137,8 @@ module ActionDispatch
125
137
  HTTP_METHOD_LOOKUP[method] = method.underscore.to_sym
126
138
  }
127
139
 
140
+ alias raw_request_method request_method # :nodoc:
141
+
128
142
  # Returns the HTTP \method that the application should see.
129
143
  # In the case where the \method was overridden by a middleware
130
144
  # (for instance, if a HEAD request was converted to a GET,
@@ -136,11 +150,11 @@ module ActionDispatch
136
150
  end
137
151
 
138
152
  def routes # :nodoc:
139
- get_header("action_dispatch.routes".freeze)
153
+ get_header("action_dispatch.routes")
140
154
  end
141
155
 
142
156
  def routes=(routes) # :nodoc:
143
- set_header("action_dispatch.routes".freeze, routes)
157
+ set_header("action_dispatch.routes", routes)
144
158
  end
145
159
 
146
160
  def engine_script_name(_routes) # :nodoc:
@@ -158,11 +172,11 @@ module ActionDispatch
158
172
  end
159
173
 
160
174
  def controller_instance # :nodoc:
161
- get_header("action_controller.instance".freeze)
175
+ get_header("action_controller.instance")
162
176
  end
163
177
 
164
178
  def controller_instance=(controller) # :nodoc:
165
- set_header("action_controller.instance".freeze, controller)
179
+ set_header("action_controller.instance", controller)
166
180
  end
167
181
 
168
182
  def http_auth_salt
@@ -173,7 +187,7 @@ module ActionDispatch
173
187
  # We're treating `nil` as "unset", and we want the default setting to be
174
188
  # `true`. This logic should be extracted to `env_config` and calculated
175
189
  # once.
176
- !(get_header("action_dispatch.show_exceptions".freeze) == false)
190
+ !(get_header("action_dispatch.show_exceptions") == false)
177
191
  end
178
192
 
179
193
  # Returns a symbol form of the #request_method.
@@ -264,7 +278,7 @@ module ActionDispatch
264
278
  # (case-insensitive), which may need to be manually added depending on the
265
279
  # choice of JavaScript libraries and frameworks.
266
280
  def xml_http_request?
267
- get_header("HTTP_X_REQUESTED_WITH") =~ /XMLHttpRequest/i
281
+ /XMLHttpRequest/i.match?(get_header("HTTP_X_REQUESTED_WITH"))
268
282
  end
269
283
  alias :xhr? :xml_http_request?
270
284
 
@@ -280,10 +294,11 @@ module ActionDispatch
280
294
  end
281
295
 
282
296
  def remote_ip=(remote_ip)
283
- set_header "action_dispatch.remote_ip".freeze, remote_ip
297
+ @remote_ip = nil
298
+ set_header "action_dispatch.remote_ip", remote_ip
284
299
  end
285
300
 
286
- ACTION_DISPATCH_REQUEST_ID = "action_dispatch.request_id".freeze # :nodoc:
301
+ ACTION_DISPATCH_REQUEST_ID = "action_dispatch.request_id" # :nodoc:
287
302
 
288
303
  # Returns the unique request id, which is based on either the X-Request-Id header that can
289
304
  # be generated by a firewall, load balancer, or web server or by the RequestId middleware
@@ -321,7 +336,7 @@ module ActionDispatch
321
336
  # variable is already set, wrap it in a StringIO.
322
337
  def body
323
338
  if raw_post = get_header("RAW_POST_DATA")
324
- raw_post = raw_post.dup.force_encoding(Encoding::BINARY)
339
+ raw_post = (+raw_post).force_encoding(Encoding::BINARY)
325
340
  StringIO.new(raw_post)
326
341
  else
327
342
  body_stream
@@ -366,6 +381,9 @@ module ActionDispatch
366
381
  def GET
367
382
  fetch_header("action_dispatch.request.query_parameters") do |k|
368
383
  rack_query_params = super || {}
384
+ controller = path_parameters[:controller]
385
+ action = path_parameters[:action]
386
+ rack_query_params = Request::Utils.set_binary_encoding(self, rack_query_params, controller, action)
369
387
  # Check for non UTF-8 parameter values, which would cause errors later
370
388
  Request::Utils.check_param_encoding(rack_query_params)
371
389
  set_header k, Request::Utils.normalize_encode_params(rack_query_params)
@@ -381,11 +399,10 @@ module ActionDispatch
381
399
  pr = parse_formatted_parameters(params_parsers) do |params|
382
400
  super || {}
383
401
  end
402
+ pr = Request::Utils.set_binary_encoding(self, pr, path_parameters[:controller], path_parameters[:action])
403
+ Request::Utils.check_param_encoding(pr)
384
404
  self.request_parameters = Request::Utils.normalize_encode_params(pr)
385
405
  end
386
- rescue Http::Parameters::ParseError # one of the parse strategies blew up
387
- self.request_parameters = Request::Utils.normalize_encode_params(super || {})
388
- raise
389
406
  rescue Rack::Utils::ParameterTypeError, Rack::Utils::InvalidParameterError => e
390
407
  raise ActionController::BadRequest.new("Invalid request parameters: #{e.message}")
391
408
  end
@@ -402,23 +419,27 @@ module ActionDispatch
402
419
 
403
420
  # True if the request came from localhost, 127.0.0.1, or ::1.
404
421
  def local?
405
- LOCALHOST =~ remote_addr && LOCALHOST =~ remote_ip
422
+ LOCALHOST.match?(remote_addr) && LOCALHOST.match?(remote_ip)
406
423
  end
407
424
 
408
425
  def request_parameters=(params)
409
426
  raise if params.nil?
410
- set_header("action_dispatch.request.request_parameters".freeze, params)
427
+ set_header("action_dispatch.request.request_parameters", params)
411
428
  end
412
429
 
413
430
  def logger
414
- get_header("action_dispatch.logger".freeze)
431
+ get_header("action_dispatch.logger")
415
432
  end
416
433
 
417
434
  def commit_flash
418
435
  end
419
436
 
420
437
  def ssl?
421
- super || scheme == "wss".freeze
438
+ super || scheme == "wss"
439
+ end
440
+
441
+ def inspect # :nodoc:
442
+ "#<#{self.class.name} #{method} #{original_url.dump} for #{remote_ip}>"
422
443
  end
423
444
 
424
445
  private
@@ -428,3 +449,5 @@ module ActionDispatch
428
449
  end
429
450
  end
430
451
  end
452
+
453
+ ActiveSupport.run_load_hooks :action_dispatch_request, ActionDispatch::Request
@@ -78,14 +78,26 @@ module ActionDispatch # :nodoc:
78
78
  x
79
79
  end
80
80
 
81
- CONTENT_TYPE = "Content-Type".freeze
82
- SET_COOKIE = "Set-Cookie".freeze
83
- LOCATION = "Location".freeze
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
88
88
 
89
+ def self.return_only_media_type_on_content_type=(*)
90
+ ActiveSupport::Deprecation.warn(
91
+ ".return_only_media_type_on_content_type= is dreprecated with no replacement and will be removed in 6.2."
92
+ )
93
+ end
94
+
95
+ def self.return_only_media_type_on_content_type
96
+ ActiveSupport::Deprecation.warn(
97
+ ".return_only_media_type_on_content_type is dreprecated with no replacement and will be removed in 6.2."
98
+ )
99
+ end
100
+
89
101
  include Rack::Response::Helpers
90
102
  # Aliasing these off because AD::Http::Cache::Response defines them.
91
103
  alias :_cache_control :cache_control
@@ -105,7 +117,7 @@ module ActionDispatch # :nodoc:
105
117
 
106
118
  def body
107
119
  @str_body ||= begin
108
- buf = "".dup
120
+ buf = +""
109
121
  each { |chunk| buf << chunk }
110
122
  buf
111
123
  end
@@ -142,7 +154,6 @@ module ActionDispatch # :nodoc:
142
154
  end
143
155
 
144
156
  private
145
-
146
157
  def each_chunk(&block)
147
158
  @buf.each(&block)
148
159
  end
@@ -224,16 +235,6 @@ module ActionDispatch # :nodoc:
224
235
  @status = Rack::Utils.status_code(status)
225
236
  end
226
237
 
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
238
  # Sets the HTTP response's content MIME type. For example, in the controller
238
239
  # you could write this:
239
240
  #
@@ -242,8 +243,22 @@ module ActionDispatch # :nodoc:
242
243
  # If a character set has been defined for this response (see charset=) then
243
244
  # the character set information will also be included in the content type
244
245
  # information.
246
+ def content_type=(content_type)
247
+ return unless content_type
248
+ new_header_info = parse_content_type(content_type.to_s)
249
+ prev_header_info = parsed_content_type_header
250
+ charset = new_header_info.charset || prev_header_info.charset
251
+ charset ||= self.class.default_charset unless prev_header_info.mime_type
252
+ set_content_type new_header_info.mime_type, charset
253
+ end
245
254
 
255
+ # Content type of response.
246
256
  def content_type
257
+ super.presence
258
+ end
259
+
260
+ # Media type of response.
261
+ def media_type
247
262
  parsed_content_type_header.mime_type
248
263
  end
249
264
 
@@ -404,15 +419,18 @@ module ActionDispatch # :nodoc:
404
419
  end
405
420
 
406
421
  private
407
-
408
422
  ContentTypeHeader = Struct.new :mime_type, :charset
409
423
  NullContentTypeHeader = ContentTypeHeader.new nil, nil
410
424
 
425
+ CONTENT_TYPE_PARSER = /
426
+ \A
427
+ (?<mime_type>[^;\s]+\s*(?:;\s*(?:(?!charset)[^;\s])+)*)?
428
+ (?:;\s*charset=(?<quote>"?)(?<charset>[^;\s]+)\k<quote>)?
429
+ /x # :nodoc:
430
+
411
431
  def parse_content_type(content_type)
412
- if content_type
413
- type, charset = content_type.split(/;\s*charset=/)
414
- type = nil if type && type.empty?
415
- ContentTypeHeader.new(type, charset)
432
+ if content_type && match = CONTENT_TYPE_PARSER.match(content_type)
433
+ ContentTypeHeader.new(match[:mime_type], match[:charset])
416
434
  else
417
435
  NullContentTypeHeader
418
436
  end
@@ -425,8 +443,8 @@ module ActionDispatch # :nodoc:
425
443
  end
426
444
 
427
445
  def set_content_type(content_type, charset)
428
- type = (content_type || "").dup
429
- type << "; charset=#{charset.to_s.downcase}" if charset
446
+ type = content_type || ""
447
+ type = "#{type}; charset=#{charset.to_s.downcase}" if charset
430
448
  set_header CONTENT_TYPE, type
431
449
  end
432
450
 
@@ -459,7 +477,7 @@ module ActionDispatch # :nodoc:
459
477
  end
460
478
 
461
479
  def assign_default_content_type_and_charset!
462
- return if content_type
480
+ return if media_type
463
481
 
464
482
  ct = parsed_content_type_header
465
483
  set_content_type(ct.mime_type || Mime[:html].to_s,
@@ -486,7 +504,7 @@ module ActionDispatch # :nodoc:
486
504
  end
487
505
 
488
506
  def respond_to?(method, include_private = false)
489
- if method.to_s == "to_path"
507
+ if method.to_sym == :to_path
490
508
  @response.stream.respond_to?(method)
491
509
  else
492
510
  super
@@ -517,4 +535,6 @@ module ActionDispatch # :nodoc:
517
535
  end
518
536
  end
519
537
  end
538
+
539
+ ActiveSupport.run_load_hooks(:action_dispatch_response, Response)
520
540
  end