actionpack 4.2.11.3 → 5.0.7.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 (136) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +890 -384
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +2 -3
  5. data/lib/abstract_controller/base.rb +28 -38
  6. data/lib/{action_controller → abstract_controller}/caching/fragments.rb +51 -11
  7. data/lib/abstract_controller/caching.rb +62 -0
  8. data/lib/abstract_controller/callbacks.rb +54 -19
  9. data/lib/abstract_controller/collector.rb +4 -9
  10. data/lib/abstract_controller/error.rb +4 -0
  11. data/lib/abstract_controller/helpers.rb +4 -3
  12. data/lib/abstract_controller/railties/routes_helpers.rb +2 -2
  13. data/lib/abstract_controller/rendering.rb +28 -18
  14. data/lib/abstract_controller/translation.rb +8 -7
  15. data/lib/abstract_controller.rb +6 -2
  16. data/lib/action_controller/api/api_rendering.rb +14 -0
  17. data/lib/action_controller/api.rb +147 -0
  18. data/lib/action_controller/base.rb +14 -11
  19. data/lib/action_controller/caching.rb +13 -58
  20. data/lib/action_controller/form_builder.rb +48 -0
  21. data/lib/action_controller/log_subscriber.rb +3 -10
  22. data/lib/action_controller/metal/basic_implicit_render.rb +11 -0
  23. data/lib/action_controller/metal/conditional_get.rb +106 -34
  24. data/lib/action_controller/metal/cookies.rb +1 -3
  25. data/lib/action_controller/metal/data_streaming.rb +14 -34
  26. data/lib/action_controller/metal/etag_with_template_digest.rb +8 -2
  27. data/lib/action_controller/metal/exceptions.rb +11 -6
  28. data/lib/action_controller/metal/force_ssl.rb +11 -11
  29. data/lib/action_controller/metal/head.rb +14 -8
  30. data/lib/action_controller/metal/helpers.rb +15 -6
  31. data/lib/action_controller/metal/http_authentication.rb +44 -35
  32. data/lib/action_controller/metal/implicit_render.rb +61 -6
  33. data/lib/action_controller/metal/instrumentation.rb +5 -5
  34. data/lib/action_controller/metal/live.rb +71 -88
  35. data/lib/action_controller/metal/mime_responds.rb +27 -42
  36. data/lib/action_controller/metal/params_wrapper.rb +9 -9
  37. data/lib/action_controller/metal/redirecting.rb +32 -9
  38. data/lib/action_controller/metal/renderers.rb +83 -40
  39. data/lib/action_controller/metal/rendering.rb +38 -6
  40. data/lib/action_controller/metal/request_forgery_protection.rb +126 -48
  41. data/lib/action_controller/metal/rescue.rb +3 -12
  42. data/lib/action_controller/metal/streaming.rb +4 -4
  43. data/lib/action_controller/metal/strong_parameters.rb +527 -134
  44. data/lib/action_controller/metal/testing.rb +1 -12
  45. data/lib/action_controller/metal/url_for.rb +12 -5
  46. data/lib/action_controller/metal.rb +88 -63
  47. data/lib/action_controller/railtie.rb +11 -7
  48. data/lib/action_controller/renderer.rb +113 -0
  49. data/lib/action_controller/template_assertions.rb +9 -0
  50. data/lib/action_controller/test_case.rb +311 -374
  51. data/lib/action_controller.rb +12 -9
  52. data/lib/action_dispatch/http/cache.rb +73 -34
  53. data/lib/action_dispatch/http/filter_parameters.rb +16 -12
  54. data/lib/action_dispatch/http/filter_redirect.rb +7 -8
  55. data/lib/action_dispatch/http/headers.rb +45 -14
  56. data/lib/action_dispatch/http/mime_negotiation.rb +42 -23
  57. data/lib/action_dispatch/http/mime_type.rb +126 -90
  58. data/lib/action_dispatch/http/mime_types.rb +3 -4
  59. data/lib/action_dispatch/http/parameter_filter.rb +19 -9
  60. data/lib/action_dispatch/http/parameters.rb +70 -40
  61. data/lib/action_dispatch/http/request.rb +144 -89
  62. data/lib/action_dispatch/http/response.rb +215 -102
  63. data/lib/action_dispatch/http/upload.rb +6 -2
  64. data/lib/action_dispatch/http/url.rb +117 -8
  65. data/lib/action_dispatch/journey/formatter.rb +47 -30
  66. data/lib/action_dispatch/journey/gtg/transition_table.rb +1 -1
  67. data/lib/action_dispatch/journey/nfa/dot.rb +0 -2
  68. data/lib/action_dispatch/journey/nfa/transition_table.rb +1 -46
  69. data/lib/action_dispatch/journey/nodes/node.rb +14 -4
  70. data/lib/action_dispatch/journey/parser.rb +2 -0
  71. data/lib/action_dispatch/journey/parser_extras.rb +8 -2
  72. data/lib/action_dispatch/journey/path/pattern.rb +38 -42
  73. data/lib/action_dispatch/journey/route.rb +88 -26
  74. data/lib/action_dispatch/journey/router/utils.rb +5 -5
  75. data/lib/action_dispatch/journey/router.rb +8 -10
  76. data/lib/action_dispatch/journey/routes.rb +14 -15
  77. data/lib/action_dispatch/journey/visitors.rb +89 -44
  78. data/lib/action_dispatch/middleware/callbacks.rb +10 -1
  79. data/lib/action_dispatch/middleware/cookies.rb +188 -134
  80. data/lib/action_dispatch/middleware/debug_exceptions.rb +128 -49
  81. data/lib/action_dispatch/middleware/debug_locks.rb +122 -0
  82. data/lib/action_dispatch/middleware/exception_wrapper.rb +21 -21
  83. data/lib/action_dispatch/middleware/executor.rb +19 -0
  84. data/lib/action_dispatch/middleware/flash.rb +66 -45
  85. data/lib/action_dispatch/middleware/params_parser.rb +32 -46
  86. data/lib/action_dispatch/middleware/public_exceptions.rb +2 -2
  87. data/lib/action_dispatch/middleware/reloader.rb +14 -58
  88. data/lib/action_dispatch/middleware/remote_ip.rb +29 -19
  89. data/lib/action_dispatch/middleware/request_id.rb +11 -6
  90. data/lib/action_dispatch/middleware/session/abstract_store.rb +23 -11
  91. data/lib/action_dispatch/middleware/session/cache_store.rb +9 -6
  92. data/lib/action_dispatch/middleware/session/cookie_store.rb +30 -24
  93. data/lib/action_dispatch/middleware/session/mem_cache_store.rb +4 -0
  94. data/lib/action_dispatch/middleware/show_exceptions.rb +11 -9
  95. data/lib/action_dispatch/middleware/ssl.rb +124 -36
  96. data/lib/action_dispatch/middleware/stack.rb +44 -40
  97. data/lib/action_dispatch/middleware/static.rb +51 -35
  98. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +2 -14
  99. data/lib/action_dispatch/middleware/templates/rescues/_source.text.erb +8 -0
  100. data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +1 -1
  101. data/lib/action_dispatch/middleware/templates/rescues/template_error.text.erb +1 -1
  102. data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +4 -4
  103. data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +59 -63
  104. data/lib/action_dispatch/railtie.rb +2 -2
  105. data/lib/action_dispatch/request/session.rb +69 -33
  106. data/lib/action_dispatch/request/utils.rb +51 -19
  107. data/lib/action_dispatch/routing/inspector.rb +32 -43
  108. data/lib/action_dispatch/routing/mapper.rb +515 -348
  109. data/lib/action_dispatch/routing/polymorphic_routes.rb +8 -14
  110. data/lib/action_dispatch/routing/redirection.rb +5 -4
  111. data/lib/action_dispatch/routing/route_set.rb +148 -240
  112. data/lib/action_dispatch/routing/url_for.rb +27 -10
  113. data/lib/action_dispatch/routing.rb +17 -13
  114. data/lib/action_dispatch/testing/assertion_response.rb +45 -0
  115. data/lib/action_dispatch/testing/assertions/response.rb +38 -20
  116. data/lib/action_dispatch/testing/assertions/routing.rb +16 -12
  117. data/lib/action_dispatch/testing/assertions.rb +1 -1
  118. data/lib/action_dispatch/testing/integration.rb +377 -149
  119. data/lib/action_dispatch/testing/request_encoder.rb +53 -0
  120. data/lib/action_dispatch/testing/test_process.rb +24 -20
  121. data/lib/action_dispatch/testing/test_request.rb +22 -31
  122. data/lib/action_dispatch/testing/test_response.rb +12 -4
  123. data/lib/action_dispatch.rb +4 -1
  124. data/lib/action_pack/gem_version.rb +4 -4
  125. data/lib/action_pack.rb +1 -1
  126. metadata +32 -34
  127. data/lib/action_controller/metal/hide_actions.rb +0 -40
  128. data/lib/action_controller/metal/rack_delegation.rb +0 -32
  129. data/lib/action_controller/middleware.rb +0 -39
  130. data/lib/action_controller/model_naming.rb +0 -12
  131. data/lib/action_dispatch/journey/backwards.rb +0 -5
  132. data/lib/action_dispatch/journey/router/strexp.rb +0 -27
  133. data/lib/action_dispatch/testing/assertions/dom.rb +0 -3
  134. data/lib/action_dispatch/testing/assertions/selector.rb +0 -3
  135. data/lib/action_dispatch/testing/assertions/tag.rb +0 -3
  136. /data/lib/action_dispatch/middleware/templates/rescues/{_source.erb → _source.html.erb} +0 -0
@@ -1,60 +1,46 @@
1
- require 'active_support/core_ext/hash/conversions'
2
1
  require 'action_dispatch/http/request'
3
- require 'active_support/core_ext/hash/indifferent_access'
4
2
 
5
3
  module ActionDispatch
4
+ # ActionDispatch::ParamsParser works for all the requests having any Content-Length
5
+ # (like POST). It takes raw data from the request and puts it through the parser
6
+ # that is picked based on Content-Type header.
7
+ #
8
+ # In case of any error while parsing data ParamsParser::ParseError is raised.
6
9
  class ParamsParser
10
+ # Raised when raw data from the request cannot be parsed by the parser
11
+ # defined for request's content mime type.
7
12
  class ParseError < StandardError
8
- attr_reader :original_exception
9
13
 
10
- def initialize(message, original_exception)
11
- super(message)
12
- @original_exception = original_exception
13
- end
14
- end
15
-
16
- DEFAULT_PARSERS = { Mime::JSON => :json }
17
-
18
- def initialize(app, parsers = {})
19
- @app, @parsers = app, DEFAULT_PARSERS.merge(parsers)
20
- end
21
-
22
- def call(env)
23
- if params = parse_formatted_parameters(env)
24
- env["action_dispatch.request.request_parameters"] = params
25
- end
26
-
27
- @app.call(env)
28
- end
29
-
30
- private
31
- def parse_formatted_parameters(env)
32
- request = Request.new(env)
33
-
34
- return false if request.content_length.zero?
35
-
36
- strategy = @parsers[request.content_mime_type]
37
-
38
- return false unless strategy
14
+ def initialize(message = nil, original_exception = nil)
15
+ if message
16
+ ActiveSupport::Deprecation.warn("Passing #message is deprecated and has no effect. " \
17
+ "#{self.class} will automatically capture the message " \
18
+ "of the original exception.", caller)
19
+ end
39
20
 
40
- case strategy
41
- when Proc
42
- strategy.call(request.raw_post)
43
- when :json
44
- data = ActiveSupport::JSON.decode(request.raw_post)
45
- data = {:_json => data} unless data.is_a?(Hash)
46
- Request::Utils.deep_munge(data).with_indifferent_access
47
- else
48
- false
21
+ if original_exception
22
+ ActiveSupport::Deprecation.warn("Passing #original_exception is deprecated and has no effect. " \
23
+ "Exceptions will automatically capture the original exception.", caller)
49
24
  end
50
- rescue => e # JSON or Ruby code block errors
51
- logger(env).debug "Error occurred while parsing request parameters.\nContents:\n\n#{request.raw_post}"
52
25
 
53
- raise ParseError.new(e.message, e)
26
+ super($!.message)
54
27
  end
55
28
 
56
- def logger(env)
57
- env['action_dispatch.logger'] || ActiveSupport::Logger.new($stderr)
29
+ def original_exception
30
+ ActiveSupport::Deprecation.warn("#original_exception is deprecated. Use #cause instead.", caller)
31
+ cause
58
32
  end
33
+ end
34
+
35
+ # Create a new +ParamsParser+ middleware instance.
36
+ #
37
+ # The +parsers+ argument can take Hash of parsers where key is identifying
38
+ # content mime type, and value is a lambda that is going to process data.
39
+ def self.new(app, parsers = {})
40
+ ActiveSupport::Deprecation.warn('ActionDispatch::ParamsParser is deprecated and will be removed in Rails 5.1. Configure the parameter parsing in ActionDispatch::Request.parameter_parsers.')
41
+ parsers = parsers.transform_keys { |key| key.respond_to?(:symbol) ? key.symbol : key }
42
+ ActionDispatch::Request.parameter_parsers = ActionDispatch::Request::DEFAULT_PARSERS.merge(parsers)
43
+ app
44
+ end
59
45
  end
60
46
  end
@@ -17,10 +17,10 @@ module ActionDispatch
17
17
  end
18
18
 
19
19
  def call(env)
20
- status = env["PATH_INFO"][1..-1]
21
20
  request = ActionDispatch::Request.new(env)
21
+ status = request.path_info[1..-1].to_i
22
22
  content_type = request.formats.first
23
- body = { :status => status, :error => Rack::Utils::HTTP_STATUS_CODES.fetch(status.to_i, Rack::Utils::HTTP_STATUS_CODES[500]) }
23
+ body = { :status => status, :error => Rack::Utils::HTTP_STATUS_CODES.fetch(status, Rack::Utils::HTTP_STATUS_CODES[500]) }
24
24
 
25
25
  render(status, content_type, body)
26
26
  end
@@ -1,5 +1,3 @@
1
- require 'active_support/deprecation/reporting'
2
-
3
1
  module ActionDispatch
4
2
  # ActionDispatch::Reloader provides prepare and cleanup callbacks,
5
3
  # intended to assist with code reloading during development.
@@ -11,9 +9,9 @@ module ActionDispatch
11
9
  # the response body. This is important for streaming responses such as the
12
10
  # following:
13
11
  #
14
- # self.response_body = lambda { |response, output|
12
+ # self.response_body = -> (response, output) do
15
13
  # # code here which refers to application models
16
- # }
14
+ # end
17
15
  #
18
16
  # Cleanup callbacks will not be called until after the response_body lambda
19
17
  # is evaluated, ensuring that it can refer to application models and other
@@ -25,74 +23,32 @@ module ActionDispatch
25
23
  # middleware stack, but are executed only when <tt>ActionDispatch::Reloader.prepare!</tt>
26
24
  # or <tt>ActionDispatch::Reloader.cleanup!</tt> are called manually.
27
25
  #
28
- class Reloader
29
- include ActiveSupport::Callbacks
30
- include ActiveSupport::Deprecation::Reporting
31
-
32
- define_callbacks :prepare
33
- define_callbacks :cleanup
34
-
35
- # Add a prepare callback. Prepare callbacks are run before each request, prior
36
- # to ActionDispatch::Callback's before callbacks.
26
+ class Reloader < Executor
37
27
  def self.to_prepare(*args, &block)
38
- unless block_given?
39
- warn "to_prepare without a block is deprecated. Please use a block"
40
- end
41
- set_callback(:prepare, *args, &block)
28
+ ActiveSupport::Reloader.to_prepare(*args, &block)
42
29
  end
43
30
 
44
- # Add a cleanup callback. Cleanup callbacks are run after each request is
45
- # complete (after #close is called on the response body).
46
31
  def self.to_cleanup(*args, &block)
47
- unless block_given?
48
- warn "to_cleanup without a block is deprecated. Please use a block"
49
- end
50
- set_callback(:cleanup, *args, &block)
32
+ ActiveSupport::Reloader.to_complete(*args, &block)
51
33
  end
52
34
 
53
- # Execute all prepare callbacks.
54
35
  def self.prepare!
55
- new(nil).prepare!
36
+ default_reloader.prepare!
56
37
  end
57
38
 
58
- # Execute all cleanup callbacks.
59
39
  def self.cleanup!
60
- new(nil).cleanup!
61
- end
62
-
63
- def initialize(app, condition=nil)
64
- @app = app
65
- @condition = condition || lambda { true }
66
- @validated = true
67
- end
68
-
69
- def call(env)
70
- @validated = @condition.call
71
- prepare!
72
-
73
- response = @app.call(env)
74
- response[2] = ::Rack::BodyProxy.new(response[2]) { cleanup! }
75
-
76
- response
77
- rescue Exception
78
- cleanup!
79
- raise
40
+ default_reloader.reload!
80
41
  end
81
42
 
82
- def prepare! #:nodoc:
83
- run_callbacks :prepare if validated?
84
- end
43
+ class << self
44
+ attr_accessor :default_reloader # :nodoc:
85
45
 
86
- def cleanup! #:nodoc:
87
- run_callbacks :cleanup if validated?
88
- ensure
89
- @validated = true
46
+ deprecate to_prepare: 'use ActiveSupport::Reloader.to_prepare instead',
47
+ to_cleanup: 'use ActiveSupport::Reloader.to_complete instead',
48
+ prepare!: 'use Rails.application.reloader.prepare! instead',
49
+ cleanup!: 'use Rails.application.reloader.reload! instead of cleanup + prepare'
90
50
  end
91
51
 
92
- private
93
-
94
- def validated? #:nodoc:
95
- @validated
96
- end
52
+ self.default_reloader = ActiveSupport::Reloader
97
53
  end
98
54
  end
@@ -43,7 +43,7 @@ module ActionDispatch
43
43
 
44
44
  # Create a new +RemoteIp+ middleware instance.
45
45
  #
46
- # The +check_ip_spoofing+ option is on by default. When on, an exception
46
+ # The +ip_spoofing_check+ option is on by default. When on, an exception
47
47
  # is raised if it looks like the client is trying to lie about its own IP
48
48
  # address. It makes sense to turn off this check on sites aimed at non-IP
49
49
  # clients (like WAP devices), or behind proxies that set headers in an
@@ -57,9 +57,9 @@ module ActionDispatch
57
57
  # with your proxy servers after it. If your proxies aren't removed, pass
58
58
  # them in via the +custom_proxies+ parameter. That way, the middleware will
59
59
  # ignore those IP addresses, and return the one that you want.
60
- def initialize(app, check_ip_spoofing = true, custom_proxies = nil)
60
+ def initialize(app, ip_spoofing_check = true, custom_proxies = nil)
61
61
  @app = app
62
- @check_ip = check_ip_spoofing
62
+ @check_ip = ip_spoofing_check
63
63
  @proxies = if custom_proxies.blank?
64
64
  TRUSTED_PROXIES
65
65
  elsif custom_proxies.respond_to?(:any?)
@@ -74,18 +74,19 @@ module ActionDispatch
74
74
  # requests. For those requests that do need to know the IP, the
75
75
  # GetIp#calculate_ip method will calculate the memoized client IP address.
76
76
  def call(env)
77
- env["action_dispatch.remote_ip"] = GetIp.new(env, self)
78
- @app.call(env)
77
+ req = ActionDispatch::Request.new env
78
+ req.remote_ip = GetIp.new(req, check_ip, proxies)
79
+ @app.call(req.env)
79
80
  end
80
81
 
81
82
  # The GetIp class exists as a way to defer processing of the request data
82
83
  # into an actual IP address. If the ActionDispatch::Request#remote_ip method
83
84
  # is called, this class will calculate the value and then memoize it.
84
85
  class GetIp
85
- def initialize(env, middleware)
86
- @env = env
87
- @check_ip = middleware.check_ip
88
- @proxies = middleware.proxies
86
+ def initialize(req, check_ip, proxies)
87
+ @req = req
88
+ @check_ip = check_ip
89
+ @proxies = proxies
89
90
  end
90
91
 
91
92
  # Sort through the various IP address headers, looking for the IP most
@@ -108,23 +109,31 @@ module ActionDispatch
108
109
  # the last address left, which was presumably set by one of those proxies.
109
110
  def calculate_ip
110
111
  # Set by the Rack web server, this is a single value.
111
- remote_addr = ips_from('REMOTE_ADDR').last
112
+ remote_addr = ips_from(@req.remote_addr).last
112
113
 
113
114
  # Could be a CSV list and/or repeated headers that were concatenated.
114
- client_ips = ips_from('HTTP_CLIENT_IP').reverse
115
- forwarded_ips = ips_from('HTTP_X_FORWARDED_FOR').reverse
115
+ client_ips = ips_from(@req.client_ip).reverse
116
+ forwarded_ips = ips_from(@req.x_forwarded_for).reverse
116
117
 
117
118
  # +Client-Ip+ and +X-Forwarded-For+ should not, generally, both be set.
118
- # If they are both set, it means that this request passed through two
119
- # proxies with incompatible IP header conventions, and there is no way
120
- # for us to determine which header is the right one after the fact.
121
- # Since we have no idea, we give up and explode.
119
+ # If they are both set, it means that either:
120
+ #
121
+ # 1) This request passed through two proxies with incompatible IP header
122
+ # conventions.
123
+ # 2) The client passed one of +Client-Ip+ or +X-Forwarded-For+
124
+ # (whichever the proxy servers weren't using) themselves.
125
+ #
126
+ # Either way, there is no way for us to determine which header is the
127
+ # right one after the fact. Since we have no idea, if we are concerned
128
+ # about IP spoofing we need to give up and explode. (If you're not
129
+ # concerned about IP spoofing you can turn the +ip_spoofing_check+
130
+ # option off.)
122
131
  should_check_ip = @check_ip && client_ips.last && forwarded_ips.last
123
132
  if should_check_ip && !forwarded_ips.include?(client_ips.last)
124
133
  # We don't know which came from the proxy, and which from the user
125
134
  raise IpSpoofAttackError, "IP spoofing attack?! " +
126
- "HTTP_CLIENT_IP=#{@env['HTTP_CLIENT_IP'].inspect} " +
127
- "HTTP_X_FORWARDED_FOR=#{@env['HTTP_X_FORWARDED_FOR'].inspect}"
135
+ "HTTP_CLIENT_IP=#{@req.client_ip.inspect} " +
136
+ "HTTP_X_FORWARDED_FOR=#{@req.x_forwarded_for.inspect}"
128
137
  end
129
138
 
130
139
  # We assume these things about the IP headers:
@@ -147,8 +156,9 @@ module ActionDispatch
147
156
  protected
148
157
 
149
158
  def ips_from(header)
159
+ return [] unless header
150
160
  # Split the comma-separated list into an array of strings
151
- ips = @env[header] ? @env[header].strip.split(/[,\s]+/) : []
161
+ ips = header.strip.split(/[,\s]+/)
152
162
  ips.select do |ip|
153
163
  begin
154
164
  # Only return IPs that are valid according to the IPAddr#new method
@@ -3,7 +3,7 @@ require 'active_support/core_ext/string/access'
3
3
 
4
4
  module ActionDispatch
5
5
  # Makes a unique request id available to the action_dispatch.request_id env variable (which is then accessible through
6
- # ActionDispatch::Request#uuid) and sends the same id to the client via the X-Request-Id header.
6
+ # ActionDispatch::Request#uuid or the alias ActionDispatch::Request#request_id) and sends the same id to the client via the X-Request-Id header.
7
7
  #
8
8
  # The unique request id is either based on the X-Request-Id header in the request, which would typically be generated
9
9
  # by a firewall, load balancer, or the web server, or, if this header is not available, a random uuid. If the
@@ -12,19 +12,24 @@ module ActionDispatch
12
12
  # The unique request id can be used to trace a request end-to-end and would typically end up being part of log files
13
13
  # from multiple pieces of the stack.
14
14
  class RequestId
15
+ X_REQUEST_ID = "X-Request-Id".freeze # :nodoc:
16
+
15
17
  def initialize(app)
16
18
  @app = app
17
19
  end
18
20
 
19
21
  def call(env)
20
- env["action_dispatch.request_id"] = external_request_id(env) || internal_request_id
21
- @app.call(env).tap { |_status, headers, _body| headers["X-Request-Id"] = env["action_dispatch.request_id"] }
22
+ req = ActionDispatch::Request.new env
23
+ req.request_id = make_request_id(req.x_request_id)
24
+ @app.call(env).tap { |_status, headers, _body| headers[X_REQUEST_ID] = req.request_id }
22
25
  end
23
26
 
24
27
  private
25
- def external_request_id(env)
26
- if request_id = env["HTTP_X_REQUEST_ID"].presence
27
- request_id.gsub(/[^\w\-]/, "").first(255)
28
+ def make_request_id(request_id)
29
+ if request_id.presence
30
+ request_id.gsub(/[^\w\-]/, "".freeze).first(255)
31
+ else
32
+ internal_request_id
28
33
  end
29
34
  end
30
35
 
@@ -7,14 +7,22 @@ require 'action_dispatch/request/session'
7
7
  module ActionDispatch
8
8
  module Session
9
9
  class SessionRestoreError < StandardError #:nodoc:
10
- attr_reader :original_exception
11
10
 
12
- def initialize(const_error)
13
- @original_exception = const_error
11
+ def initialize(const_error = nil)
12
+ if const_error
13
+ ActiveSupport::Deprecation.warn("Passing #original_exception is deprecated and has no effect. " \
14
+ "Exceptions will automatically capture the original exception.", caller)
15
+ end
14
16
 
15
17
  super("Session contains objects whose class definition isn't available.\n" +
16
18
  "Remember to require the classes for all objects kept in the session.\n" +
17
- "(Original exception: #{const_error.message} [#{const_error.class}])\n")
19
+ "(Original exception: #{$!.message} [#{$!.class}])\n")
20
+ set_backtrace $!.backtrace
21
+ end
22
+
23
+ def original_exception
24
+ ActiveSupport::Deprecation.warn("#original_exception is deprecated. Use #cause instead.", caller)
25
+ cause
18
26
  end
19
27
  end
20
28
 
@@ -36,6 +44,11 @@ module ActionDispatch
36
44
  @default_options.delete(:sidbits)
37
45
  @default_options.delete(:secure_random)
38
46
  end
47
+
48
+ private
49
+ def make_request(env)
50
+ ActionDispatch::Request.new env
51
+ end
39
52
  end
40
53
 
41
54
  module StaleSessionCheck
@@ -54,8 +67,8 @@ module ActionDispatch
54
67
  begin
55
68
  # Note that the regexp does not allow $1 to end with a ':'
56
69
  $1.constantize
57
- rescue LoadError, NameError => e
58
- raise ActionDispatch::Session::SessionRestoreError, e, e.backtrace
70
+ rescue LoadError, NameError
71
+ raise ActionDispatch::Session::SessionRestoreError
59
72
  end
60
73
  retry
61
74
  else
@@ -65,8 +78,8 @@ module ActionDispatch
65
78
  end
66
79
 
67
80
  module SessionObject # :nodoc:
68
- def prepare_session(env)
69
- Request::Session.create(self, env, @default_options)
81
+ def prepare_session(req)
82
+ Request::Session.create(self, req, @default_options)
70
83
  end
71
84
 
72
85
  def loaded_session?(session)
@@ -74,15 +87,14 @@ module ActionDispatch
74
87
  end
75
88
  end
76
89
 
77
- class AbstractStore < Rack::Session::Abstract::ID
90
+ class AbstractStore < Rack::Session::Abstract::Persisted
78
91
  include Compatibility
79
92
  include StaleSessionCheck
80
93
  include SessionObject
81
94
 
82
95
  private
83
96
 
84
- def set_cookie(env, session_id, cookie)
85
- request = ActionDispatch::Request.new(env)
97
+ def set_cookie(request, session_id, cookie)
86
98
  request.cookie_jar[key] = cookie
87
99
  end
88
100
  end
@@ -2,12 +2,15 @@ require 'action_dispatch/middleware/session/abstract_store'
2
2
 
3
3
  module ActionDispatch
4
4
  module Session
5
- # Session store that uses an ActiveSupport::Cache::Store to store the sessions. This store is most useful
5
+ # A session store that uses an ActiveSupport::Cache::Store to store the sessions. This store is most useful
6
6
  # if you don't store critical data in your sessions and you don't need them to live for extended periods
7
7
  # of time.
8
+ #
9
+ # ==== Options
10
+ # * <tt>cache</tt> - The cache to use. If it is not specified, <tt>Rails.cache</tt> will be used.
11
+ # * <tt>expire_after</tt> - The length of time a session will be stored before automatically expiring.
12
+ # By default, the <tt>:expires_in</tt> option of the cache is used.
8
13
  class CacheStore < AbstractStore
9
- # Create a new store. The cache to use can be passed in the <tt>:cache</tt> option. If it is
10
- # not specified, <tt>Rails.cache</tt> will be used.
11
14
  def initialize(app, options = {})
12
15
  @cache = options[:cache] || Rails.cache
13
16
  options[:expire_after] ||= @cache.options[:expires_in]
@@ -15,7 +18,7 @@ module ActionDispatch
15
18
  end
16
19
 
17
20
  # Get a session from the cache.
18
- def get_session(env, sid)
21
+ def find_session(env, sid)
19
22
  unless sid and session = @cache.read(cache_key(sid))
20
23
  sid, session = generate_sid, {}
21
24
  end
@@ -23,7 +26,7 @@ module ActionDispatch
23
26
  end
24
27
 
25
28
  # Set a session in the cache.
26
- def set_session(env, sid, session, options)
29
+ def write_session(env, sid, session, options)
27
30
  key = cache_key(sid)
28
31
  if session
29
32
  @cache.write(key, session, :expires_in => options[:expire_after])
@@ -34,7 +37,7 @@ module ActionDispatch
34
37
  end
35
38
 
36
39
  # Remove a session from the cache.
37
- def destroy_session(env, sid, options)
40
+ def delete_session(env, sid, options)
38
41
  @cache.delete(cache_key(sid))
39
42
  generate_sid
40
43
  end
@@ -23,7 +23,7 @@ module ActionDispatch
23
23
  # goes a step further than signed cookies in that encrypted cookies cannot
24
24
  # be altered or read by users. This is the default starting in Rails 4.
25
25
  #
26
- # If you have both secret_token and secret_key base set, your cookies will
26
+ # If you have both secret_token and secret_key_base set, your cookies will
27
27
  # be encrypted, and signed cookies generated by Rails 3 will be
28
28
  # transparently read and encrypted to provide a smooth upgrade path.
29
29
  #
@@ -36,7 +36,7 @@ module ActionDispatch
36
36
  # development:
37
37
  # secret_key_base: 'secret key'
38
38
  #
39
- # To generate a secret key for an existing application, run `rake secret`.
39
+ # To generate a secret key for an existing application, run `rails secret`.
40
40
  #
41
41
  # If you are upgrading an existing Rails 3 app, you should leave your
42
42
  # existing secret_token in place and simply add the new secret_key_base.
@@ -52,25 +52,31 @@ module ActionDispatch
52
52
  # JavaScript before upgrading.
53
53
  #
54
54
  # Note that changing the secret key will invalidate all existing sessions!
55
- class CookieStore < Rack::Session::Abstract::ID
56
- include Compatibility
57
- include StaleSessionCheck
58
- include SessionObject
59
-
55
+ #
56
+ # Because CookieStore extends Rack::Session::Abstract::Persisted, many of the
57
+ # options described there can be used to customize the session cookie that
58
+ # is generated. For example:
59
+ #
60
+ # Rails.application.config.session_store :cookie_store, expire_after: 14.days
61
+ #
62
+ # would set the session cookie to expire automatically 14 days after creation.
63
+ # Other useful options include <tt>:key</tt>, <tt>:secure</tt> and
64
+ # <tt>:httponly</tt>.
65
+ class CookieStore < AbstractStore
60
66
  def initialize(app, options={})
61
67
  super(app, options.merge!(:cookie_only => true))
62
68
  end
63
69
 
64
- def destroy_session(env, session_id, options)
70
+ def delete_session(req, session_id, options)
65
71
  new_sid = generate_sid unless options[:drop]
66
72
  # Reset hash and Assign the new session id
67
- env["action_dispatch.request.unsigned_session_cookie"] = new_sid ? { "session_id" => new_sid } : {}
73
+ req.set_header("action_dispatch.request.unsigned_session_cookie", new_sid ? { "session_id" => new_sid } : {})
68
74
  new_sid
69
75
  end
70
76
 
71
- def load_session(env)
77
+ def load_session(req)
72
78
  stale_session_check! do
73
- data = unpacked_cookie_data(env)
79
+ data = unpacked_cookie_data(req)
74
80
  data = persistent_session_id!(data)
75
81
  [data["session_id"], data]
76
82
  end
@@ -78,20 +84,21 @@ module ActionDispatch
78
84
 
79
85
  private
80
86
 
81
- def extract_session_id(env)
87
+ def extract_session_id(req)
82
88
  stale_session_check! do
83
- unpacked_cookie_data(env)["session_id"]
89
+ unpacked_cookie_data(req)["session_id"]
84
90
  end
85
91
  end
86
92
 
87
- def unpacked_cookie_data(env)
88
- env["action_dispatch.request.unsigned_session_cookie"] ||= begin
89
- stale_session_check! do
90
- if data = get_cookie(env)
93
+ def unpacked_cookie_data(req)
94
+ req.fetch_header("action_dispatch.request.unsigned_session_cookie") do |k|
95
+ v = stale_session_check! do
96
+ if data = get_cookie(req)
91
97
  data.stringify_keys!
92
98
  end
93
99
  data || {}
94
100
  end
101
+ req.set_header k, v
95
102
  end
96
103
  end
97
104
 
@@ -101,21 +108,20 @@ module ActionDispatch
101
108
  data
102
109
  end
103
110
 
104
- def set_session(env, sid, session_data, options)
111
+ def write_session(req, sid, session_data, options)
105
112
  session_data["session_id"] = sid
106
113
  session_data
107
114
  end
108
115
 
109
- def set_cookie(env, session_id, cookie)
110
- cookie_jar(env)[@key] = cookie
116
+ def set_cookie(request, session_id, cookie)
117
+ cookie_jar(request)[@key] = cookie
111
118
  end
112
119
 
113
- def get_cookie(env)
114
- cookie_jar(env)[@key]
120
+ def get_cookie(req)
121
+ cookie_jar(req)[@key]
115
122
  end
116
123
 
117
- def cookie_jar(env)
118
- request = ActionDispatch::Request.new(env)
124
+ def cookie_jar(request)
119
125
  request.cookie_jar.signed_or_encrypted
120
126
  end
121
127
  end
@@ -8,6 +8,10 @@ end
8
8
 
9
9
  module ActionDispatch
10
10
  module Session
11
+ # A session store that uses MemCache to implement storage.
12
+ #
13
+ # ==== Options
14
+ # * <tt>expire_after</tt> - The length of time a session will be stored before automatically expiring.
11
15
  class MemCacheStore < Rack::Session::Dalli
12
16
  include Compatibility
13
17
  include StaleSessionCheck
@@ -27,24 +27,26 @@ module ActionDispatch
27
27
  end
28
28
 
29
29
  def call(env)
30
+ request = ActionDispatch::Request.new env
30
31
  @app.call(env)
31
32
  rescue Exception => exception
32
- if env['action_dispatch.show_exceptions'] == false
33
- raise exception
33
+ if request.show_exceptions?
34
+ render_exception(request, exception)
34
35
  else
35
- render_exception(env, exception)
36
+ raise exception
36
37
  end
37
38
  end
38
39
 
39
40
  private
40
41
 
41
- def render_exception(env, exception)
42
- wrapper = ExceptionWrapper.new(env, exception)
42
+ def render_exception(request, exception)
43
+ backtrace_cleaner = request.get_header 'action_dispatch.backtrace_cleaner'
44
+ wrapper = ExceptionWrapper.new(backtrace_cleaner, exception)
43
45
  status = wrapper.status_code
44
- env["action_dispatch.exception"] = wrapper.exception
45
- env["action_dispatch.original_path"] = env["PATH_INFO"]
46
- env["PATH_INFO"] = "/#{status}"
47
- response = @exceptions_app.call(env)
46
+ request.set_header "action_dispatch.exception", wrapper.exception
47
+ request.set_header "action_dispatch.original_path", request.path_info
48
+ request.path_info = "/#{status}"
49
+ response = @exceptions_app.call(request.env)
48
50
  response[1]['X-Cascade'] == 'pass' ? pass_response(status) : response
49
51
  rescue Exception => failsafe_error
50
52
  $stderr.puts "Error during failsafe response: #{failsafe_error}\n #{failsafe_error.backtrace * "\n "}"