actionpack 4.2.8 → 5.2.4.2

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of actionpack might be problematic. Click here for more details.

Files changed (166) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +285 -444
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +6 -7
  5. data/lib/abstract_controller.rb +12 -5
  6. data/lib/abstract_controller/asset_paths.rb +2 -0
  7. data/lib/abstract_controller/base.rb +45 -49
  8. data/lib/abstract_controller/caching.rb +66 -0
  9. data/lib/{action_controller → abstract_controller}/caching/fragments.rb +78 -15
  10. data/lib/abstract_controller/callbacks.rb +47 -31
  11. data/lib/abstract_controller/collector.rb +8 -11
  12. data/lib/abstract_controller/error.rb +6 -0
  13. data/lib/abstract_controller/helpers.rb +25 -25
  14. data/lib/abstract_controller/logger.rb +2 -0
  15. data/lib/abstract_controller/railties/routes_helpers.rb +4 -2
  16. data/lib/abstract_controller/rendering.rb +42 -41
  17. data/lib/abstract_controller/translation.rb +10 -7
  18. data/lib/abstract_controller/url_for.rb +2 -0
  19. data/lib/action_controller.rb +29 -21
  20. data/lib/action_controller/api.rb +149 -0
  21. data/lib/action_controller/api/api_rendering.rb +16 -0
  22. data/lib/action_controller/base.rb +27 -19
  23. data/lib/action_controller/caching.rb +14 -57
  24. data/lib/action_controller/form_builder.rb +50 -0
  25. data/lib/action_controller/log_subscriber.rb +10 -15
  26. data/lib/action_controller/metal.rb +98 -83
  27. data/lib/action_controller/metal/basic_implicit_render.rb +13 -0
  28. data/lib/action_controller/metal/conditional_get.rb +118 -44
  29. data/lib/action_controller/metal/content_security_policy.rb +52 -0
  30. data/lib/action_controller/metal/cookies.rb +3 -3
  31. data/lib/action_controller/metal/data_streaming.rb +27 -46
  32. data/lib/action_controller/metal/etag_with_flash.rb +18 -0
  33. data/lib/action_controller/metal/etag_with_template_digest.rb +20 -13
  34. data/lib/action_controller/metal/exceptions.rb +8 -14
  35. data/lib/action_controller/metal/flash.rb +4 -3
  36. data/lib/action_controller/metal/force_ssl.rb +23 -21
  37. data/lib/action_controller/metal/head.rb +21 -19
  38. data/lib/action_controller/metal/helpers.rb +24 -14
  39. data/lib/action_controller/metal/http_authentication.rb +64 -57
  40. data/lib/action_controller/metal/implicit_render.rb +62 -8
  41. data/lib/action_controller/metal/instrumentation.rb +19 -21
  42. data/lib/action_controller/metal/live.rb +90 -106
  43. data/lib/action_controller/metal/mime_responds.rb +33 -46
  44. data/lib/action_controller/metal/parameter_encoding.rb +51 -0
  45. data/lib/action_controller/metal/params_wrapper.rb +61 -53
  46. data/lib/action_controller/metal/redirecting.rb +49 -28
  47. data/lib/action_controller/metal/renderers.rb +87 -44
  48. data/lib/action_controller/metal/rendering.rb +72 -50
  49. data/lib/action_controller/metal/request_forgery_protection.rb +203 -92
  50. data/lib/action_controller/metal/rescue.rb +9 -16
  51. data/lib/action_controller/metal/streaming.rb +12 -10
  52. data/lib/action_controller/metal/strong_parameters.rb +582 -165
  53. data/lib/action_controller/metal/testing.rb +2 -17
  54. data/lib/action_controller/metal/url_for.rb +19 -10
  55. data/lib/action_controller/railtie.rb +28 -10
  56. data/lib/action_controller/railties/helpers.rb +2 -0
  57. data/lib/action_controller/renderer.rb +117 -0
  58. data/lib/action_controller/template_assertions.rb +11 -0
  59. data/lib/action_controller/test_case.rb +280 -411
  60. data/lib/action_dispatch.rb +27 -19
  61. data/lib/action_dispatch/http/cache.rb +93 -47
  62. data/lib/action_dispatch/http/content_security_policy.rb +272 -0
  63. data/lib/action_dispatch/http/filter_parameters.rb +26 -20
  64. data/lib/action_dispatch/http/filter_redirect.rb +10 -11
  65. data/lib/action_dispatch/http/headers.rb +55 -22
  66. data/lib/action_dispatch/http/mime_negotiation.rb +60 -41
  67. data/lib/action_dispatch/http/mime_type.rb +134 -121
  68. data/lib/action_dispatch/http/mime_types.rb +20 -6
  69. data/lib/action_dispatch/http/parameter_filter.rb +25 -11
  70. data/lib/action_dispatch/http/parameters.rb +98 -39
  71. data/lib/action_dispatch/http/rack_cache.rb +2 -0
  72. data/lib/action_dispatch/http/request.rb +200 -118
  73. data/lib/action_dispatch/http/response.rb +225 -110
  74. data/lib/action_dispatch/http/upload.rb +12 -6
  75. data/lib/action_dispatch/http/url.rb +110 -28
  76. data/lib/action_dispatch/journey.rb +7 -5
  77. data/lib/action_dispatch/journey/formatter.rb +55 -32
  78. data/lib/action_dispatch/journey/gtg/builder.rb +7 -5
  79. data/lib/action_dispatch/journey/gtg/simulator.rb +3 -9
  80. data/lib/action_dispatch/journey/gtg/transition_table.rb +17 -16
  81. data/lib/action_dispatch/journey/nfa/builder.rb +5 -3
  82. data/lib/action_dispatch/journey/nfa/dot.rb +13 -13
  83. data/lib/action_dispatch/journey/nfa/simulator.rb +3 -1
  84. data/lib/action_dispatch/journey/nfa/transition_table.rb +5 -48
  85. data/lib/action_dispatch/journey/nodes/node.rb +18 -6
  86. data/lib/action_dispatch/journey/parser.rb +23 -22
  87. data/lib/action_dispatch/journey/parser.y +3 -2
  88. data/lib/action_dispatch/journey/parser_extras.rb +12 -4
  89. data/lib/action_dispatch/journey/path/pattern.rb +50 -44
  90. data/lib/action_dispatch/journey/route.rb +106 -28
  91. data/lib/action_dispatch/journey/router.rb +35 -23
  92. data/lib/action_dispatch/journey/router/utils.rb +20 -11
  93. data/lib/action_dispatch/journey/routes.rb +18 -16
  94. data/lib/action_dispatch/journey/scanner.rb +18 -15
  95. data/lib/action_dispatch/journey/visitors.rb +99 -52
  96. data/lib/action_dispatch/middleware/callbacks.rb +1 -2
  97. data/lib/action_dispatch/middleware/cookies.rb +304 -193
  98. data/lib/action_dispatch/middleware/debug_exceptions.rb +152 -57
  99. data/lib/action_dispatch/middleware/debug_locks.rb +124 -0
  100. data/lib/action_dispatch/middleware/exception_wrapper.rb +68 -69
  101. data/lib/action_dispatch/middleware/executor.rb +21 -0
  102. data/lib/action_dispatch/middleware/flash.rb +78 -54
  103. data/lib/action_dispatch/middleware/public_exceptions.rb +27 -25
  104. data/lib/action_dispatch/middleware/reloader.rb +5 -91
  105. data/lib/action_dispatch/middleware/remote_ip.rb +41 -31
  106. data/lib/action_dispatch/middleware/request_id.rb +17 -9
  107. data/lib/action_dispatch/middleware/session/abstract_store.rb +41 -25
  108. data/lib/action_dispatch/middleware/session/cache_store.rb +24 -14
  109. data/lib/action_dispatch/middleware/session/cookie_store.rb +72 -67
  110. data/lib/action_dispatch/middleware/session/mem_cache_store.rb +8 -2
  111. data/lib/action_dispatch/middleware/show_exceptions.rb +26 -22
  112. data/lib/action_dispatch/middleware/ssl.rb +114 -36
  113. data/lib/action_dispatch/middleware/stack.rb +31 -44
  114. data/lib/action_dispatch/middleware/static.rb +57 -50
  115. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +2 -14
  116. data/lib/action_dispatch/middleware/templates/rescues/{_source.erb → _source.html.erb} +0 -0
  117. data/lib/action_dispatch/middleware/templates/rescues/_source.text.erb +8 -0
  118. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +21 -0
  119. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.text.erb +13 -0
  120. data/lib/action_dispatch/middleware/templates/rescues/layout.erb +1 -0
  121. data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +1 -1
  122. data/lib/action_dispatch/middleware/templates/rescues/template_error.text.erb +1 -1
  123. data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +4 -4
  124. data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +64 -64
  125. data/lib/action_dispatch/railtie.rb +19 -11
  126. data/lib/action_dispatch/request/session.rb +106 -59
  127. data/lib/action_dispatch/request/utils.rb +67 -24
  128. data/lib/action_dispatch/routing.rb +17 -18
  129. data/lib/action_dispatch/routing/endpoint.rb +9 -2
  130. data/lib/action_dispatch/routing/inspector.rb +58 -67
  131. data/lib/action_dispatch/routing/mapper.rb +734 -447
  132. data/lib/action_dispatch/routing/polymorphic_routes.rb +161 -139
  133. data/lib/action_dispatch/routing/redirection.rb +36 -26
  134. data/lib/action_dispatch/routing/route_set.rb +321 -291
  135. data/lib/action_dispatch/routing/routes_proxy.rb +32 -5
  136. data/lib/action_dispatch/routing/url_for.rb +65 -25
  137. data/lib/action_dispatch/system_test_case.rb +147 -0
  138. data/lib/action_dispatch/system_testing/browser.rb +49 -0
  139. data/lib/action_dispatch/system_testing/driver.rb +59 -0
  140. data/lib/action_dispatch/system_testing/server.rb +31 -0
  141. data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +96 -0
  142. data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +31 -0
  143. data/lib/action_dispatch/system_testing/test_helpers/undef_methods.rb +26 -0
  144. data/lib/action_dispatch/testing/assertion_response.rb +47 -0
  145. data/lib/action_dispatch/testing/assertions.rb +6 -4
  146. data/lib/action_dispatch/testing/assertions/response.rb +45 -20
  147. data/lib/action_dispatch/testing/assertions/routing.rb +30 -26
  148. data/lib/action_dispatch/testing/integration.rb +347 -209
  149. data/lib/action_dispatch/testing/request_encoder.rb +55 -0
  150. data/lib/action_dispatch/testing/test_process.rb +28 -22
  151. data/lib/action_dispatch/testing/test_request.rb +27 -34
  152. data/lib/action_dispatch/testing/test_response.rb +35 -7
  153. data/lib/action_pack.rb +4 -2
  154. data/lib/action_pack/gem_version.rb +5 -3
  155. data/lib/action_pack/version.rb +3 -1
  156. metadata +56 -39
  157. data/lib/action_controller/metal/hide_actions.rb +0 -40
  158. data/lib/action_controller/metal/rack_delegation.rb +0 -32
  159. data/lib/action_controller/middleware.rb +0 -39
  160. data/lib/action_controller/model_naming.rb +0 -12
  161. data/lib/action_dispatch/journey/backwards.rb +0 -5
  162. data/lib/action_dispatch/journey/router/strexp.rb +0 -27
  163. data/lib/action_dispatch/middleware/params_parser.rb +0 -60
  164. data/lib/action_dispatch/testing/assertions/dom.rb +0 -3
  165. data/lib/action_dispatch/testing/assertions/selector.rb +0 -3
  166. data/lib/action_dispatch/testing/assertions/tag.rb +0 -3
@@ -1,7 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/object/duplicable"
4
+
1
5
  module ActionDispatch
2
6
  module Http
3
7
  class ParameterFilter
4
- FILTERED = '[FILTERED]'.freeze # :nodoc:
8
+ FILTERED = "[FILTERED]".freeze # :nodoc:
5
9
 
6
10
  def initialize(filters = [])
7
11
  @filters = filters
@@ -30,36 +34,46 @@ module ActionDispatch
30
34
  when Regexp
31
35
  regexps << item
32
36
  else
33
- strings << item.to_s
37
+ strings << Regexp.escape(item.to_s)
34
38
  end
35
39
  end
36
40
 
37
- regexps << Regexp.new(strings.join('|'), true) unless strings.empty?
38
- new regexps, blocks
41
+ deep_regexps, regexps = regexps.partition { |r| r.to_s.include?("\\.".freeze) }
42
+ deep_strings, strings = strings.partition { |s| s.include?("\\.".freeze) }
43
+
44
+ regexps << Regexp.new(strings.join("|".freeze), true) unless strings.empty?
45
+ deep_regexps << Regexp.new(deep_strings.join("|".freeze), true) unless deep_strings.empty?
46
+
47
+ new regexps, deep_regexps, blocks
39
48
  end
40
49
 
41
- attr_reader :regexps, :blocks
50
+ attr_reader :regexps, :deep_regexps, :blocks
42
51
 
43
- def initialize(regexps, blocks)
52
+ def initialize(regexps, deep_regexps, blocks)
44
53
  @regexps = regexps
45
- @blocks = blocks
54
+ @deep_regexps = deep_regexps.any? ? deep_regexps : nil
55
+ @blocks = blocks
46
56
  end
47
57
 
48
- def call(original_params)
49
- filtered_params = {}
58
+ def call(original_params, parents = [])
59
+ filtered_params = original_params.class.new
50
60
 
51
61
  original_params.each do |key, value|
62
+ parents.push(key) if deep_regexps
52
63
  if regexps.any? { |r| key =~ r }
53
64
  value = FILTERED
65
+ elsif deep_regexps && (joined = parents.join(".")) && deep_regexps.any? { |r| joined =~ r }
66
+ value = FILTERED
54
67
  elsif value.is_a?(Hash)
55
- value = call(value)
68
+ value = call(value, parents)
56
69
  elsif value.is_a?(Array)
57
- value = value.map { |v| v.is_a?(Hash) ? call(v) : v }
70
+ value = value.map { |v| v.is_a?(Hash) ? call(v, parents) : v }
58
71
  elsif blocks.any?
59
72
  key = key.dup if key.duplicable?
60
73
  value = value.dup if value.duplicable?
61
74
  blocks.each { |b| b.call(key, value) }
62
75
  end
76
+ parents.pop if deep_regexps
63
77
 
64
78
  filtered_params[key] = value
65
79
  end
@@ -1,35 +1,79 @@
1
- require 'active_support/core_ext/hash/keys'
2
- require 'active_support/core_ext/hash/indifferent_access'
3
- require 'active_support/deprecation'
1
+ # frozen_string_literal: true
4
2
 
5
3
  module ActionDispatch
6
4
  module Http
7
5
  module Parameters
8
- PARAMETERS_KEY = 'action_dispatch.request.path_parameters'
6
+ extend ActiveSupport::Concern
7
+
8
+ PARAMETERS_KEY = "action_dispatch.request.path_parameters"
9
+
10
+ DEFAULT_PARSERS = {
11
+ Mime[:json].symbol => -> (raw_post) {
12
+ data = ActiveSupport::JSON.decode(raw_post)
13
+ data.is_a?(Hash) ? data : { _json: data }
14
+ }
15
+ }
16
+
17
+ # Raised when raw data from the request cannot be parsed by the parser
18
+ # defined for request's content MIME type.
19
+ class ParseError < StandardError
20
+ def initialize
21
+ super($!.message)
22
+ end
23
+ end
24
+
25
+ included do
26
+ class << self
27
+ # Returns the parameter parsers.
28
+ attr_reader :parameter_parsers
29
+ end
30
+
31
+ self.parameter_parsers = DEFAULT_PARSERS
32
+ end
33
+
34
+ module ClassMethods
35
+ # Configure the parameter parser for a given MIME type.
36
+ #
37
+ # It accepts a hash where the key is the symbol of the MIME type
38
+ # and the value is a proc.
39
+ #
40
+ # original_parsers = ActionDispatch::Request.parameter_parsers
41
+ # xml_parser = -> (raw_post) { Hash.from_xml(raw_post) || {} }
42
+ # new_parsers = original_parsers.merge(xml: xml_parser)
43
+ # ActionDispatch::Request.parameter_parsers = new_parsers
44
+ def parameter_parsers=(parsers)
45
+ @parameter_parsers = parsers.transform_keys { |key| key.respond_to?(:symbol) ? key.symbol : key }
46
+ end
47
+ end
9
48
 
10
49
  # Returns both GET and POST \parameters in a single hash.
11
50
  def parameters
12
- @env["action_dispatch.request.parameters"] ||= begin
13
- params = begin
14
- request_parameters.merge(query_parameters)
15
- rescue EOFError
16
- query_parameters.dup
17
- end
18
- params.merge!(path_parameters)
19
- end
51
+ params = get_header("action_dispatch.request.parameters")
52
+ return params if params
53
+
54
+ params = begin
55
+ request_parameters.merge(query_parameters)
56
+ rescue EOFError
57
+ query_parameters.dup
58
+ end
59
+ params.merge!(path_parameters)
60
+ params = set_binary_encoding(params, params[:controller], params[:action])
61
+ set_header("action_dispatch.request.parameters", params)
62
+ params
20
63
  end
21
64
  alias :params :parameters
22
65
 
23
66
  def path_parameters=(parameters) #:nodoc:
24
- @env.delete('action_dispatch.request.parameters')
25
- @env[PARAMETERS_KEY] = parameters
26
- end
67
+ delete_header("action_dispatch.request.parameters")
68
+
69
+ parameters = set_binary_encoding(parameters, parameters[:controller], parameters[:action])
70
+ # If any of the path parameters has an invalid encoding then
71
+ # raise since it's likely to trigger errors further on.
72
+ Request::Utils.check_param_encoding(parameters)
27
73
 
28
- def symbolized_path_parameters
29
- ActiveSupport::Deprecation.warn(
30
- '`symbolized_path_parameters` is deprecated. Please use `path_parameters`.'
31
- )
32
- path_parameters
74
+ set_header PARAMETERS_KEY, parameters
75
+ rescue Rack::Utils::ParameterTypeError, Rack::Utils::InvalidParameterError => e
76
+ raise ActionController::BadRequest.new("Invalid path parameters: #{e.message}")
33
77
  end
34
78
 
35
79
  # Returns a hash with the \parameters used to form the \path of the request.
@@ -37,31 +81,46 @@ module ActionDispatch
37
81
  #
38
82
  # {'action' => 'my_action', 'controller' => 'my_controller'}
39
83
  def path_parameters
40
- @env[PARAMETERS_KEY] ||= {}
84
+ get_header(PARAMETERS_KEY) || set_header(PARAMETERS_KEY, {})
41
85
  end
42
86
 
43
- private
87
+ private
44
88
 
45
- # Convert nested Hash to HashWithIndifferentAccess.
46
- #
47
- def normalize_encode_params(params)
48
- case params
49
- when Hash
50
- if params.has_key?(:tempfile)
51
- UploadedFile.new(params)
52
- else
53
- params.each_with_object({}) do |(key, val), new_hash|
54
- new_hash[key] = if val.is_a?(Array)
55
- val.map! { |el| normalize_encode_params(el) }
56
- else
57
- normalize_encode_params(val)
58
- end
59
- end.with_indifferent_access
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
60
96
  end
61
- else
62
97
  params
63
98
  end
64
- 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
+ def parse_formatted_parameters(parsers)
107
+ return yield if content_length.zero? || content_mime_type.nil?
108
+
109
+ strategy = parsers.fetch(content_mime_type.symbol) { return yield }
110
+
111
+ begin
112
+ strategy.call(raw_post)
113
+ 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
+
117
+ raise ParseError
118
+ end
119
+ end
120
+
121
+ def params_parsers
122
+ ActionDispatch::Request.parameter_parsers
123
+ end
65
124
  end
66
125
  end
67
126
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "rack/cache"
2
4
  require "rack/cache/context"
3
5
  require "active_support/cache"
@@ -1,27 +1,32 @@
1
- require 'stringio'
2
-
3
- require 'active_support/inflector'
4
- require 'action_dispatch/http/headers'
5
- require 'action_controller/metal/exceptions'
6
- require 'rack/request'
7
- require 'action_dispatch/http/cache'
8
- require 'action_dispatch/http/mime_negotiation'
9
- require 'action_dispatch/http/parameters'
10
- require 'action_dispatch/http/filter_parameters'
11
- require 'action_dispatch/http/upload'
12
- require 'action_dispatch/http/url'
13
- require 'active_support/core_ext/array/conversions'
1
+ # frozen_string_literal: true
2
+
3
+ require "stringio"
4
+
5
+ require "active_support/inflector"
6
+ require "action_dispatch/http/headers"
7
+ require "action_controller/metal/exceptions"
8
+ require "rack/request"
9
+ require "action_dispatch/http/cache"
10
+ require "action_dispatch/http/mime_negotiation"
11
+ require "action_dispatch/http/parameters"
12
+ require "action_dispatch/http/filter_parameters"
13
+ require "action_dispatch/http/upload"
14
+ require "action_dispatch/http/url"
15
+ require "active_support/core_ext/array/conversions"
14
16
 
15
17
  module ActionDispatch
16
- class Request < Rack::Request
18
+ class Request
19
+ include Rack::Request::Helpers
17
20
  include ActionDispatch::Http::Cache::Request
18
21
  include ActionDispatch::Http::MimeNegotiation
19
22
  include ActionDispatch::Http::Parameters
20
23
  include ActionDispatch::Http::FilterParameters
21
24
  include ActionDispatch::Http::URL
25
+ include ActionDispatch::ContentSecurityPolicy::Request
26
+ include Rack::Request::Env
22
27
 
23
- autoload :Session, 'action_dispatch/request/session'
24
- autoload :Utils, 'action_dispatch/request/utils'
28
+ autoload :Session, "action_dispatch/request/session"
29
+ autoload :Utils, "action_dispatch/request/utils"
25
30
 
26
31
  LOCALHOST = Regexp.union [/^127\.\d{1,3}\.\d{1,3}\.\d{1,3}$/, /^::1$/, /^0:0:0:0:0:0:0:1(%.*)?$/]
27
32
 
@@ -29,19 +34,28 @@ module ActionDispatch
29
34
  PATH_TRANSLATED REMOTE_HOST
30
35
  REMOTE_IDENT REMOTE_USER REMOTE_ADDR
31
36
  SERVER_NAME SERVER_PROTOCOL
37
+ ORIGINAL_SCRIPT_NAME
32
38
 
33
39
  HTTP_ACCEPT HTTP_ACCEPT_CHARSET HTTP_ACCEPT_ENCODING
34
40
  HTTP_ACCEPT_LANGUAGE HTTP_CACHE_CONTROL HTTP_FROM
35
- HTTP_NEGOTIATE HTTP_PRAGMA ].freeze
41
+ HTTP_NEGOTIATE HTTP_PRAGMA HTTP_CLIENT_IP
42
+ HTTP_X_FORWARDED_FOR HTTP_ORIGIN HTTP_VERSION
43
+ HTTP_X_CSRF_TOKEN HTTP_X_REQUEST_ID HTTP_X_FORWARDED_HOST
44
+ SERVER_ADDR
45
+ ].freeze
36
46
 
37
47
  ENV_METHODS.each do |env|
38
48
  class_eval <<-METHOD, __FILE__, __LINE__ + 1
39
49
  def #{env.sub(/^HTTP_/n, '').downcase} # def accept_charset
40
- @env["#{env}"] # @env["HTTP_ACCEPT_CHARSET"]
50
+ get_header "#{env}".freeze # get_header "HTTP_ACCEPT_CHARSET".freeze
41
51
  end # end
42
52
  METHOD
43
53
  end
44
54
 
55
+ def self.empty
56
+ new({})
57
+ end
58
+
45
59
  def initialize(env)
46
60
  super
47
61
  @method = nil
@@ -50,33 +64,49 @@ module ActionDispatch
50
64
  @original_fullpath = nil
51
65
  @fullpath = nil
52
66
  @ip = nil
53
- @uuid = nil
54
67
  end
55
68
 
56
- def check_path_parameters!
57
- # If any of the path parameters has an invalid encoding then
58
- # raise since it's likely to trigger errors further on.
59
- path_parameters.each do |key, value|
60
- next unless value.respond_to?(:valid_encoding?)
61
- unless value.valid_encoding?
62
- raise ActionController::BadRequest, "Invalid parameter: #{key} => #{value}"
63
- end
69
+ def commit_cookie_jar! # :nodoc:
70
+ end
71
+
72
+ PASS_NOT_FOUND = Class.new { # :nodoc:
73
+ def self.action(_); self; end
74
+ def self.call(_); [404, { "X-Cascade" => "pass" }, []]; end
75
+ def self.binary_params_for?(action); false; end
76
+ }
77
+
78
+ def controller_class
79
+ params = path_parameters
80
+ params[:action] ||= "index"
81
+ controller_class_for(params[:controller])
82
+ end
83
+
84
+ def controller_class_for(name)
85
+ if name
86
+ controller_param = name.underscore
87
+ const_name = "#{controller_param.camelize}Controller"
88
+ ActiveSupport::Dependencies.constantize(const_name)
89
+ else
90
+ PASS_NOT_FOUND
64
91
  end
65
92
  end
66
93
 
94
+ # Returns true if the request has a header matching the given key parameter.
95
+ #
96
+ # request.key? :ip_spoofing_check # => true
67
97
  def key?(key)
68
- @env.key?(key)
98
+ has_header? key
69
99
  end
70
100
 
71
101
  # List of HTTP request methods from the following RFCs:
72
- # Hypertext Transfer Protocol -- HTTP/1.1 (http://www.ietf.org/rfc/rfc2616.txt)
73
- # HTTP Extensions for Distributed Authoring -- WEBDAV (http://www.ietf.org/rfc/rfc2518.txt)
74
- # Versioning Extensions to WebDAV (http://www.ietf.org/rfc/rfc3253.txt)
75
- # Ordered Collections Protocol (WebDAV) (http://www.ietf.org/rfc/rfc3648.txt)
76
- # Web Distributed Authoring and Versioning (WebDAV) Access Control Protocol (http://www.ietf.org/rfc/rfc3744.txt)
77
- # Web Distributed Authoring and Versioning (WebDAV) SEARCH (http://www.ietf.org/rfc/rfc5323.txt)
78
- # Calendar Extensions to WebDAV (http://www.ietf.org/rfc/rfc4791.txt)
79
- # PATCH Method for HTTP (http://www.ietf.org/rfc/rfc5789.txt)
102
+ # Hypertext Transfer Protocol -- HTTP/1.1 (https://www.ietf.org/rfc/rfc2616.txt)
103
+ # HTTP Extensions for Distributed Authoring -- WEBDAV (https://www.ietf.org/rfc/rfc2518.txt)
104
+ # Versioning Extensions to WebDAV (https://www.ietf.org/rfc/rfc3253.txt)
105
+ # Ordered Collections Protocol (WebDAV) (https://www.ietf.org/rfc/rfc3648.txt)
106
+ # Web Distributed Authoring and Versioning (WebDAV) Access Control Protocol (https://www.ietf.org/rfc/rfc3744.txt)
107
+ # Web Distributed Authoring and Versioning (WebDAV) SEARCH (https://www.ietf.org/rfc/rfc5323.txt)
108
+ # Calendar Extensions to WebDAV (https://www.ietf.org/rfc/rfc4791.txt)
109
+ # PATCH Method for HTTP (https://www.ietf.org/rfc/rfc5789.txt)
80
110
  RFC2616 = %w(OPTIONS GET HEAD POST PUT DELETE TRACE CONNECT)
81
111
  RFC2518 = %w(PROPFIND PROPPATCH MKCOL COPY MOVE LOCK UNLOCK)
82
112
  RFC3253 = %w(VERSION-CONTROL REPORT CHECKOUT CHECKIN UNCHECKOUT MKWORKSPACE UPDATE LABEL MERGE BASELINE-CONTROL MKACTIVITY)
@@ -90,7 +120,7 @@ module ActionDispatch
90
120
 
91
121
  HTTP_METHOD_LOOKUP = {}
92
122
 
93
- # Populate the HTTP method lookup cache
123
+ # Populate the HTTP method lookup cache.
94
124
  HTTP_METHODS.each { |method|
95
125
  HTTP_METHOD_LOOKUP[method] = method.underscore.to_sym
96
126
  }
@@ -102,73 +132,89 @@ module ActionDispatch
102
132
  # the application should use), this \method returns the overridden
103
133
  # value, not the original.
104
134
  def request_method
105
- @request_method ||= check_method(env["REQUEST_METHOD"])
135
+ @request_method ||= check_method(super)
106
136
  end
107
137
 
108
- def request_method=(request_method) #:nodoc:
109
- if check_method(request_method)
110
- @request_method = env["REQUEST_METHOD"] = request_method
111
- end
138
+ def routes # :nodoc:
139
+ get_header("action_dispatch.routes".freeze)
112
140
  end
113
141
 
114
- # Returns a symbol form of the #request_method
115
- def request_method_symbol
116
- HTTP_METHOD_LOOKUP[request_method]
142
+ def routes=(routes) # :nodoc:
143
+ set_header("action_dispatch.routes".freeze, routes)
117
144
  end
118
145
 
119
- # Returns the original value of the environment's REQUEST_METHOD,
120
- # even if it was overridden by middleware. See #request_method for
121
- # more information.
122
- def method
123
- @method ||= check_method(env["rack.methodoverride.original_method"] || env['REQUEST_METHOD'])
146
+ def engine_script_name(_routes) # :nodoc:
147
+ get_header(_routes.env_key)
124
148
  end
125
149
 
126
- # Returns a symbol form of the #method
127
- def method_symbol
128
- HTTP_METHOD_LOOKUP[method]
150
+ def engine_script_name=(name) # :nodoc:
151
+ set_header(routes.env_key, name.dup)
129
152
  end
130
153
 
131
- # Is this a GET (or HEAD) request?
132
- # Equivalent to <tt>request.request_method_symbol == :get</tt>.
133
- def get?
134
- HTTP_METHOD_LOOKUP[request_method] == :get
154
+ def request_method=(request_method) #:nodoc:
155
+ if check_method(request_method)
156
+ @request_method = set_header("REQUEST_METHOD", request_method)
157
+ end
135
158
  end
136
159
 
137
- # Is this a POST request?
138
- # Equivalent to <tt>request.request_method_symbol == :post</tt>.
139
- def post?
140
- HTTP_METHOD_LOOKUP[request_method] == :post
160
+ def controller_instance # :nodoc:
161
+ get_header("action_controller.instance".freeze)
141
162
  end
142
163
 
143
- # Is this a PATCH request?
144
- # Equivalent to <tt>request.request_method == :patch</tt>.
145
- def patch?
146
- HTTP_METHOD_LOOKUP[request_method] == :patch
164
+ def controller_instance=(controller) # :nodoc:
165
+ set_header("action_controller.instance".freeze, controller)
147
166
  end
148
167
 
149
- # Is this a PUT request?
150
- # Equivalent to <tt>request.request_method_symbol == :put</tt>.
151
- def put?
152
- HTTP_METHOD_LOOKUP[request_method] == :put
168
+ def http_auth_salt
169
+ get_header "action_dispatch.http_auth_salt"
153
170
  end
154
171
 
155
- # Is this a DELETE request?
156
- # Equivalent to <tt>request.request_method_symbol == :delete</tt>.
157
- def delete?
158
- HTTP_METHOD_LOOKUP[request_method] == :delete
172
+ def show_exceptions? # :nodoc:
173
+ # We're treating `nil` as "unset", and we want the default setting to be
174
+ # `true`. This logic should be extracted to `env_config` and calculated
175
+ # once.
176
+ !(get_header("action_dispatch.show_exceptions".freeze) == false)
159
177
  end
160
178
 
161
- # Is this a HEAD request?
162
- # Equivalent to <tt>request.request_method_symbol == :head</tt>.
163
- def head?
164
- HTTP_METHOD_LOOKUP[request_method] == :head
179
+ # Returns a symbol form of the #request_method.
180
+ def request_method_symbol
181
+ HTTP_METHOD_LOOKUP[request_method]
182
+ end
183
+
184
+ # Returns the original value of the environment's REQUEST_METHOD,
185
+ # even if it was overridden by middleware. See #request_method for
186
+ # more information.
187
+ def method
188
+ @method ||= check_method(get_header("rack.methodoverride.original_method") || get_header("REQUEST_METHOD"))
189
+ end
190
+
191
+ # Returns a symbol form of the #method.
192
+ def method_symbol
193
+ HTTP_METHOD_LOOKUP[method]
165
194
  end
166
195
 
167
196
  # Provides access to the request's HTTP headers, for example:
168
197
  #
169
198
  # request.headers["Content-Type"] # => "text/plain"
170
199
  def headers
171
- Http::Headers.new(@env)
200
+ @headers ||= Http::Headers.new(self)
201
+ end
202
+
203
+ # Early Hints is an HTTP/2 status code that indicates hints to help a client start
204
+ # making preparations for processing the final response.
205
+ #
206
+ # If the env contains +rack.early_hints+ then the server accepts HTTP2 push for Link headers.
207
+ #
208
+ # The +send_early_hints+ method accepts a hash of links as follows:
209
+ #
210
+ # send_early_hints("Link" => "</style.css>; rel=preload; as=style\n</script.js>; rel=preload")
211
+ #
212
+ # If you are using +javascript_include_tag+ or +stylesheet_link_tag+ the
213
+ # Early Hints headers are included by default if supported.
214
+ def send_early_hints(links)
215
+ return unless env["rack.early_hints"]
216
+
217
+ env["rack.early_hints"].call(links)
172
218
  end
173
219
 
174
220
  # Returns a +String+ with the last requested path including their params.
@@ -179,7 +225,7 @@ module ActionDispatch
179
225
  # # get '/foo?bar'
180
226
  # request.original_fullpath # => '/foo?bar'
181
227
  def original_fullpath
182
- @original_fullpath ||= (env["ORIGINAL_FULLPATH"] || fullpath)
228
+ @original_fullpath ||= (get_header("ORIGINAL_FULLPATH") || fullpath)
183
229
  end
184
230
 
185
231
  # Returns the +String+ full path including params of the last URL requested.
@@ -218,62 +264,84 @@ module ActionDispatch
218
264
  # (case-insensitive), which may need to be manually added depending on the
219
265
  # choice of JavaScript libraries and frameworks.
220
266
  def xml_http_request?
221
- @env['HTTP_X_REQUESTED_WITH'] =~ /XMLHttpRequest/i
267
+ get_header("HTTP_X_REQUESTED_WITH") =~ /XMLHttpRequest/i
222
268
  end
223
269
  alias :xhr? :xml_http_request?
224
270
 
271
+ # Returns the IP address of client as a +String+.
225
272
  def ip
226
273
  @ip ||= super
227
274
  end
228
275
 
229
- # Originating IP address, usually set by the RemoteIp middleware.
276
+ # Returns the IP address of client as a +String+,
277
+ # usually set by the RemoteIp middleware.
230
278
  def remote_ip
231
- @remote_ip ||= (@env["action_dispatch.remote_ip"] || ip).to_s
279
+ @remote_ip ||= (get_header("action_dispatch.remote_ip") || ip).to_s
232
280
  end
233
281
 
282
+ def remote_ip=(remote_ip)
283
+ set_header "action_dispatch.remote_ip".freeze, remote_ip
284
+ end
285
+
286
+ ACTION_DISPATCH_REQUEST_ID = "action_dispatch.request_id".freeze # :nodoc:
287
+
234
288
  # Returns the unique request id, which is based on either the X-Request-Id header that can
235
289
  # be generated by a firewall, load balancer, or web server or by the RequestId middleware
236
290
  # (which sets the action_dispatch.request_id environment variable).
237
291
  #
238
292
  # This unique ID is useful for tracing a request from end-to-end as part of logging or debugging.
239
- # This relies on the rack variable set by the ActionDispatch::RequestId middleware.
240
- def uuid
241
- @uuid ||= env["action_dispatch.request_id"]
293
+ # This relies on the Rack variable set by the ActionDispatch::RequestId middleware.
294
+ def request_id
295
+ get_header ACTION_DISPATCH_REQUEST_ID
296
+ end
297
+
298
+ def request_id=(id) # :nodoc:
299
+ set_header ACTION_DISPATCH_REQUEST_ID, id
242
300
  end
243
301
 
302
+ alias_method :uuid, :request_id
303
+
244
304
  # Returns the lowercase name of the HTTP server software.
245
305
  def server_software
246
- (@env['SERVER_SOFTWARE'] && /^([a-zA-Z]+)/ =~ @env['SERVER_SOFTWARE']) ? $1.downcase : nil
306
+ (get_header("SERVER_SOFTWARE") && /^([a-zA-Z]+)/ =~ get_header("SERVER_SOFTWARE")) ? $1.downcase : nil
247
307
  end
248
308
 
249
309
  # Read the request \body. This is useful for web services that need to
250
310
  # work with raw requests directly.
251
311
  def raw_post
252
- unless @env.include? 'RAW_POST_DATA'
312
+ unless has_header? "RAW_POST_DATA"
253
313
  raw_post_body = body
254
- @env['RAW_POST_DATA'] = raw_post_body.read(content_length)
314
+ set_header("RAW_POST_DATA", raw_post_body.read(content_length))
255
315
  raw_post_body.rewind if raw_post_body.respond_to?(:rewind)
256
316
  end
257
- @env['RAW_POST_DATA']
317
+ get_header "RAW_POST_DATA"
258
318
  end
259
319
 
260
320
  # The request body is an IO input stream. If the RAW_POST_DATA environment
261
321
  # variable is already set, wrap it in a StringIO.
262
322
  def body
263
- if raw_post = @env['RAW_POST_DATA']
264
- raw_post.force_encoding(Encoding::BINARY)
323
+ if raw_post = get_header("RAW_POST_DATA")
324
+ raw_post = raw_post.dup.force_encoding(Encoding::BINARY)
265
325
  StringIO.new(raw_post)
266
326
  else
267
- @env['rack.input']
327
+ body_stream
268
328
  end
269
329
  end
270
330
 
331
+ # Determine whether the request body contains form-data by checking
332
+ # the request Content-Type for one of the media-types:
333
+ # "application/x-www-form-urlencoded" or "multipart/form-data". The
334
+ # list of form-data media types can be modified through the
335
+ # +FORM_DATA_MEDIA_TYPES+ array.
336
+ #
337
+ # A request body is not assumed to contain form-data when no
338
+ # Content-Type header is provided and the request_method is POST.
271
339
  def form_data?
272
- FORM_DATA_MEDIA_TYPES.include?(content_mime_type.to_s)
340
+ FORM_DATA_MEDIA_TYPES.include?(media_type)
273
341
  end
274
342
 
275
343
  def body_stream #:nodoc:
276
- @env['rack.input']
344
+ get_header("rack.input")
277
345
  end
278
346
 
279
347
  # TODO This should be broken apart into AD::Request::Session and probably
@@ -284,60 +352,74 @@ module ActionDispatch
284
352
  else
285
353
  self.session = {}
286
354
  end
287
- @env['action_dispatch.request.flash_hash'] = nil
288
355
  end
289
356
 
290
357
  def session=(session) #:nodoc:
291
- Session.set @env, session
358
+ Session.set self, session
292
359
  end
293
360
 
294
361
  def session_options=(options)
295
- Session::Options.set @env, options
362
+ Session::Options.set self, options
296
363
  end
297
364
 
298
- # Override Rack's GET method to support indifferent access
365
+ # Override Rack's GET method to support indifferent access.
299
366
  def GET
300
- @env["action_dispatch.request.query_parameters"] ||= Utils.deep_munge(normalize_encode_params(super || {}))
367
+ fetch_header("action_dispatch.request.query_parameters") do |k|
368
+ rack_query_params = super || {}
369
+ # Check for non UTF-8 parameter values, which would cause errors later
370
+ Request::Utils.check_param_encoding(rack_query_params)
371
+ set_header k, Request::Utils.normalize_encode_params(rack_query_params)
372
+ end
301
373
  rescue Rack::Utils::ParameterTypeError, Rack::Utils::InvalidParameterError => e
302
- raise ActionController::BadRequest.new(:query, e)
374
+ raise ActionController::BadRequest.new("Invalid query parameters: #{e.message}")
303
375
  end
304
376
  alias :query_parameters :GET
305
377
 
306
- # Override Rack's POST method to support indifferent access
378
+ # Override Rack's POST method to support indifferent access.
307
379
  def POST
308
- @env["action_dispatch.request.request_parameters"] ||= Utils.deep_munge(normalize_encode_params(super || {}))
380
+ fetch_header("action_dispatch.request.request_parameters") do
381
+ pr = parse_formatted_parameters(params_parsers) do |params|
382
+ super || {}
383
+ end
384
+ self.request_parameters = Request::Utils.normalize_encode_params(pr)
385
+ 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
309
389
  rescue Rack::Utils::ParameterTypeError, Rack::Utils::InvalidParameterError => e
310
- raise ActionController::BadRequest.new(:request, e)
390
+ raise ActionController::BadRequest.new("Invalid request parameters: #{e.message}")
311
391
  end
312
392
  alias :request_parameters :POST
313
393
 
314
394
  # Returns the authorization header regardless of whether it was specified directly or through one of the
315
395
  # proxy alternatives.
316
396
  def authorization
317
- @env['HTTP_AUTHORIZATION'] ||
318
- @env['X-HTTP_AUTHORIZATION'] ||
319
- @env['X_HTTP_AUTHORIZATION'] ||
320
- @env['REDIRECT_X_HTTP_AUTHORIZATION']
397
+ get_header("HTTP_AUTHORIZATION") ||
398
+ get_header("X-HTTP_AUTHORIZATION") ||
399
+ get_header("X_HTTP_AUTHORIZATION") ||
400
+ get_header("REDIRECT_X_HTTP_AUTHORIZATION")
321
401
  end
322
402
 
323
- # True if the request came from localhost, 127.0.0.1.
403
+ # True if the request came from localhost, 127.0.0.1, or ::1.
324
404
  def local?
325
405
  LOCALHOST =~ remote_addr && LOCALHOST =~ remote_ip
326
406
  end
327
407
 
328
- # Extracted into ActionDispatch::Request::Utils.deep_munge, but kept here for backwards compatibility.
329
- def deep_munge(hash)
330
- ActiveSupport::Deprecation.warn(
331
- 'This method has been extracted into `ActionDispatch::Request::Utils.deep_munge`. Please start using that instead.'
332
- )
408
+ def request_parameters=(params)
409
+ raise if params.nil?
410
+ set_header("action_dispatch.request.request_parameters".freeze, params)
411
+ end
333
412
 
334
- Utils.deep_munge(hash)
413
+ def logger
414
+ get_header("action_dispatch.logger".freeze)
335
415
  end
336
416
 
337
- protected
338
- def parse_query(qs)
339
- Utils.deep_munge(super)
340
- end
417
+ def commit_flash
418
+ end
419
+
420
+ def ssl?
421
+ super || scheme == "wss".freeze
422
+ end
341
423
 
342
424
  private
343
425
  def check_method(name)