actionpack 7.0.8.6 → 7.1.0.beta1

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 (135) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +318 -423
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +2 -2
  5. data/lib/abstract_controller/base.rb +19 -10
  6. data/lib/abstract_controller/caching/fragments.rb +2 -0
  7. data/lib/abstract_controller/callbacks.rb +31 -6
  8. data/lib/abstract_controller/deprecator.rb +7 -0
  9. data/lib/abstract_controller/helpers.rb +61 -18
  10. data/lib/abstract_controller/railties/routes_helpers.rb +1 -16
  11. data/lib/abstract_controller/rendering.rb +3 -3
  12. data/lib/abstract_controller/translation.rb +1 -27
  13. data/lib/abstract_controller/url_for.rb +2 -0
  14. data/lib/abstract_controller.rb +6 -0
  15. data/lib/action_controller/api.rb +5 -3
  16. data/lib/action_controller/base.rb +3 -17
  17. data/lib/action_controller/caching.rb +2 -0
  18. data/lib/action_controller/deprecator.rb +7 -0
  19. data/lib/action_controller/form_builder.rb +2 -0
  20. data/lib/action_controller/log_subscriber.rb +16 -4
  21. data/lib/action_controller/metal/content_security_policy.rb +1 -1
  22. data/lib/action_controller/metal/data_streaming.rb +2 -0
  23. data/lib/action_controller/metal/default_headers.rb +2 -0
  24. data/lib/action_controller/metal/etag_with_flash.rb +2 -0
  25. data/lib/action_controller/metal/etag_with_template_digest.rb +2 -0
  26. data/lib/action_controller/metal/exceptions.rb +8 -0
  27. data/lib/action_controller/metal/head.rb +8 -6
  28. data/lib/action_controller/metal/helpers.rb +3 -14
  29. data/lib/action_controller/metal/http_authentication.rb +10 -4
  30. data/lib/action_controller/metal/implicit_render.rb +5 -3
  31. data/lib/action_controller/metal/instrumentation.rb +8 -1
  32. data/lib/action_controller/metal/live.rb +24 -0
  33. data/lib/action_controller/metal/mime_responds.rb +2 -2
  34. data/lib/action_controller/metal/params_wrapper.rb +3 -1
  35. data/lib/action_controller/metal/permissions_policy.rb +1 -1
  36. data/lib/action_controller/metal/redirecting.rb +6 -6
  37. data/lib/action_controller/metal/renderers.rb +2 -2
  38. data/lib/action_controller/metal/rendering.rb +0 -7
  39. data/lib/action_controller/metal/request_forgery_protection.rb +138 -50
  40. data/lib/action_controller/metal/rescue.rb +2 -0
  41. data/lib/action_controller/metal/streaming.rb +70 -30
  42. data/lib/action_controller/metal/strong_parameters.rb +89 -50
  43. data/lib/action_controller/metal/url_for.rb +7 -0
  44. data/lib/action_controller/metal.rb +79 -21
  45. data/lib/action_controller/railtie.rb +22 -9
  46. data/lib/action_controller/renderer.rb +98 -65
  47. data/lib/action_controller/test_case.rb +15 -5
  48. data/lib/action_controller.rb +8 -1
  49. data/lib/action_dispatch/constants.rb +32 -0
  50. data/lib/action_dispatch/deprecator.rb +7 -0
  51. data/lib/action_dispatch/http/cache.rb +1 -3
  52. data/lib/action_dispatch/http/content_security_policy.rb +9 -8
  53. data/lib/action_dispatch/http/filter_parameters.rb +15 -14
  54. data/lib/action_dispatch/http/headers.rb +2 -0
  55. data/lib/action_dispatch/http/mime_negotiation.rb +21 -21
  56. data/lib/action_dispatch/http/mime_type.rb +35 -12
  57. data/lib/action_dispatch/http/mime_types.rb +3 -1
  58. data/lib/action_dispatch/http/parameters.rb +1 -1
  59. data/lib/action_dispatch/http/permissions_policy.rb +44 -15
  60. data/lib/action_dispatch/http/rack_cache.rb +2 -0
  61. data/lib/action_dispatch/http/request.rb +48 -14
  62. data/lib/action_dispatch/http/response.rb +78 -59
  63. data/lib/action_dispatch/http/upload.rb +2 -0
  64. data/lib/action_dispatch/journey/formatter.rb +8 -2
  65. data/lib/action_dispatch/journey/path/pattern.rb +14 -14
  66. data/lib/action_dispatch/journey/route.rb +3 -2
  67. data/lib/action_dispatch/journey/router.rb +5 -4
  68. data/lib/action_dispatch/journey/routes.rb +2 -2
  69. data/lib/action_dispatch/log_subscriber.rb +23 -0
  70. data/lib/action_dispatch/middleware/actionable_exceptions.rb +5 -6
  71. data/lib/action_dispatch/middleware/assume_ssl.rb +24 -0
  72. data/lib/action_dispatch/middleware/callbacks.rb +2 -0
  73. data/lib/action_dispatch/middleware/cookies.rb +81 -98
  74. data/lib/action_dispatch/middleware/debug_exceptions.rb +26 -25
  75. data/lib/action_dispatch/middleware/debug_locks.rb +4 -1
  76. data/lib/action_dispatch/middleware/debug_view.rb +7 -2
  77. data/lib/action_dispatch/middleware/exception_wrapper.rb +181 -27
  78. data/lib/action_dispatch/middleware/executor.rb +1 -1
  79. data/lib/action_dispatch/middleware/flash.rb +7 -0
  80. data/lib/action_dispatch/middleware/host_authorization.rb +6 -3
  81. data/lib/action_dispatch/middleware/public_exceptions.rb +5 -3
  82. data/lib/action_dispatch/middleware/reloader.rb +7 -5
  83. data/lib/action_dispatch/middleware/remote_ip.rb +17 -16
  84. data/lib/action_dispatch/middleware/request_id.rb +2 -0
  85. data/lib/action_dispatch/middleware/server_timing.rb +4 -4
  86. data/lib/action_dispatch/middleware/session/abstract_store.rb +5 -0
  87. data/lib/action_dispatch/middleware/session/cache_store.rb +2 -0
  88. data/lib/action_dispatch/middleware/session/cookie_store.rb +11 -5
  89. data/lib/action_dispatch/middleware/session/mem_cache_store.rb +3 -1
  90. data/lib/action_dispatch/middleware/show_exceptions.rb +19 -15
  91. data/lib/action_dispatch/middleware/ssl.rb +18 -6
  92. data/lib/action_dispatch/middleware/stack.rb +7 -2
  93. data/lib/action_dispatch/middleware/static.rb +12 -8
  94. data/lib/action_dispatch/middleware/templates/rescues/_actions.html.erb +2 -2
  95. data/lib/action_dispatch/middleware/templates/rescues/_message_and_suggestions.html.erb +4 -4
  96. data/lib/action_dispatch/middleware/templates/rescues/_source.html.erb +8 -1
  97. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +7 -7
  98. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +2 -2
  99. data/lib/action_dispatch/middleware/templates/rescues/layout.erb +17 -0
  100. data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.html.erb +16 -12
  101. data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +1 -1
  102. data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +3 -3
  103. data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +4 -4
  104. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +1 -1
  105. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.text.erb +1 -1
  106. data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +3 -0
  107. data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +46 -37
  108. data/lib/action_dispatch/railtie.rb +14 -4
  109. data/lib/action_dispatch/request/session.rb +16 -6
  110. data/lib/action_dispatch/request/utils.rb +8 -3
  111. data/lib/action_dispatch/routing/inspector.rb +54 -6
  112. data/lib/action_dispatch/routing/mapper.rb +26 -14
  113. data/lib/action_dispatch/routing/polymorphic_routes.rb +2 -0
  114. data/lib/action_dispatch/routing/redirection.rb +15 -6
  115. data/lib/action_dispatch/routing/route_set.rb +52 -22
  116. data/lib/action_dispatch/routing/routes_proxy.rb +1 -1
  117. data/lib/action_dispatch/routing/url_for.rb +5 -1
  118. data/lib/action_dispatch/routing.rb +4 -4
  119. data/lib/action_dispatch/system_test_case.rb +3 -3
  120. data/lib/action_dispatch/system_testing/browser.rb +5 -6
  121. data/lib/action_dispatch/system_testing/driver.rb +13 -21
  122. data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +27 -16
  123. data/lib/action_dispatch/testing/assertions/response.rb +13 -6
  124. data/lib/action_dispatch/testing/assertions/routing.rb +67 -28
  125. data/lib/action_dispatch/testing/assertions.rb +3 -1
  126. data/lib/action_dispatch/testing/integration.rb +27 -17
  127. data/lib/action_dispatch/testing/request_encoder.rb +4 -1
  128. data/lib/action_dispatch/testing/test_process.rb +4 -3
  129. data/lib/action_dispatch/testing/test_request.rb +1 -1
  130. data/lib/action_dispatch/testing/test_response.rb +23 -9
  131. data/lib/action_dispatch.rb +37 -4
  132. data/lib/action_pack/gem_version.rb +4 -4
  133. data/lib/action_pack/version.rb +1 -1
  134. data/lib/action_pack.rb +1 -1
  135. metadata +55 -33
@@ -6,23 +6,23 @@ require "action_dispatch/http/cache"
6
6
  require "monitor"
7
7
 
8
8
  module ActionDispatch # :nodoc:
9
+ # = Action Dispatch \Response
10
+ #
9
11
  # Represents an HTTP response generated by a controller action. Use it to
10
12
  # retrieve the current state of the response, or customize the response. It can
11
13
  # either represent a real HTTP response (i.e. one that is meant to be sent
12
14
  # back to the web browser) or a TestResponse (i.e. one that is generated
13
15
  # from integration tests).
14
16
  #
15
- # \Response is mostly a Ruby on \Rails framework implementation detail, and
16
- # should never be used directly in controllers. Controllers should use the
17
- # methods defined in ActionController::Base instead. For example, if you want
18
- # to set the HTTP response's content MIME type, then use
19
- # ActionControllerBase#headers instead of Response#headers.
17
+ # The \Response object for the current request is exposed on controllers as
18
+ # ActionController::Metal#response. ActionController::Metal also provides a
19
+ # few additional methods that delegate to attributes of the \Response such as
20
+ # ActionController::Metal#headers.
20
21
  #
21
- # Nevertheless, integration tests may want to inspect controller responses in
22
- # more detail, and that's when \Response can be useful for application
23
- # developers. Integration test methods such as
24
- # Integration::RequestHelpers#get and Integration::RequestHelpers#post return
25
- # objects of type TestResponse (which are of course also of type \Response).
22
+ # Integration tests will likely also want to inspect responses in
23
+ # more detail. Methods such as Integration::RequestHelpers#get
24
+ # and Integration::RequestHelpers#post return instances of
25
+ # TestResponse (which inherits from \Response) for this purpose.
26
26
  #
27
27
  # For example, the following demo integration test prints the body of the
28
28
  # controller response to the console:
@@ -34,41 +34,43 @@ module ActionDispatch # :nodoc:
34
34
  # end
35
35
  # end
36
36
  class Response
37
- class Header < DelegateClass(Hash) # :nodoc:
38
- def initialize(response, header)
39
- @response = response
40
- super(header)
41
- end
42
-
43
- def []=(k, v)
44
- if @response.sending? || @response.sent?
45
- raise ActionDispatch::IllegalStateError, "header already sent"
46
- end
47
-
48
- super
49
- end
50
-
51
- def merge(other)
52
- self.class.new @response, __getobj__.merge(other)
53
- end
54
-
55
- def to_hash
56
- __getobj__.dup
57
- end
37
+ begin
38
+ # For `Rack::Headers` (Rack 3+):
39
+ require "rack/headers"
40
+ Headers = ::Rack::Headers
41
+ rescue LoadError
42
+ # For `Rack::Utils::HeaderHash`:
43
+ require "rack/utils"
44
+ Headers = ::Rack::Utils::HeaderHash
58
45
  end
59
46
 
47
+ # To be deprecated:
48
+ Header = Headers
49
+
60
50
  # The request that the response is responding to.
61
51
  attr_accessor :request
62
52
 
63
53
  # The HTTP status code.
64
54
  attr_reader :status
65
55
 
66
- # Get headers for this response.
67
- attr_reader :header
56
+ # The headers for the response.
57
+ #
58
+ # header["Content-Type"] # => "text/plain"
59
+ # header["Content-Type"] = "application/json"
60
+ # header["Content-Type"] # => "application/json"
61
+ #
62
+ # Also aliased as +headers+.
63
+ #
64
+ # headers["Content-Type"] # => "text/plain"
65
+ # headers["Content-Type"] = "application/json"
66
+ # headers["Content-Type"] # => "application/json"
67
+ #
68
+ # Also aliased as +header+ for compatibility.
69
+ attr_reader :headers
68
70
 
69
- alias_method :headers, :header
71
+ alias_method :header, :headers
70
72
 
71
- delegate :[], :[]=, to: :@header
73
+ delegate :[], :[]=, to: :@headers
72
74
 
73
75
  def each(&block)
74
76
  sending!
@@ -79,7 +81,6 @@ module ActionDispatch # :nodoc:
79
81
 
80
82
  CONTENT_TYPE = "Content-Type"
81
83
  SET_COOKIE = "Set-Cookie"
82
- LOCATION = "Location"
83
84
  NO_CONTENT_CODES = [100, 101, 102, 103, 204, 205, 304]
84
85
 
85
86
  cattr_accessor :default_charset, default: "utf-8"
@@ -102,6 +103,10 @@ module ActionDispatch # :nodoc:
102
103
  @str_body = nil
103
104
  end
104
105
 
106
+ def to_ary
107
+ @buf.to_ary
108
+ end
109
+
105
110
  def body
106
111
  @str_body ||= begin
107
112
  buf = +""
@@ -117,6 +122,7 @@ module ActionDispatch # :nodoc:
117
122
  @response.commit!
118
123
  @buf.push string
119
124
  end
125
+ alias_method :<<, :write
120
126
 
121
127
  def each(&block)
122
128
  if @str_body
@@ -146,9 +152,9 @@ module ActionDispatch # :nodoc:
146
152
  end
147
153
  end
148
154
 
149
- def self.create(status = 200, header = {}, body = [], default_headers: self.default_headers)
150
- header = merge_default_headers(header, default_headers)
151
- new status, header, body
155
+ def self.create(status = 200, headers = {}, body = [], default_headers: self.default_headers)
156
+ headers = merge_default_headers(headers, default_headers)
157
+ new status, headers, body
152
158
  end
153
159
 
154
160
  def self.merge_default_headers(original, default)
@@ -158,10 +164,14 @@ module ActionDispatch # :nodoc:
158
164
  # The underlying body, as a streamable object.
159
165
  attr_reader :stream
160
166
 
161
- def initialize(status = 200, header = {}, body = [])
167
+ def initialize(status = 200, headers = nil, body = [])
162
168
  super()
163
169
 
164
- @header = Header.new(self, header)
170
+ @headers = Headers.new
171
+
172
+ headers&.each do |key, value|
173
+ @headers[key] = value
174
+ end
165
175
 
166
176
  self.body, self.status = body, status
167
177
 
@@ -175,10 +185,10 @@ module ActionDispatch # :nodoc:
175
185
  yield self if block_given?
176
186
  end
177
187
 
178
- def has_header?(key); headers.key? key; end
179
- def get_header(key); headers[key]; end
180
- def set_header(key, v); headers[key] = v; end
181
- def delete_header(key); headers.delete key; end
188
+ def has_header?(key); @headers.key? key; end
189
+ def get_header(key); @headers[key]; end
190
+ def set_header(key, v); @headers[key] = v; end
191
+ def delete_header(key); @headers.delete key; end
182
192
 
183
193
  def await_commit
184
194
  synchronize do
@@ -281,7 +291,7 @@ module ActionDispatch # :nodoc:
281
291
  @status
282
292
  end
283
293
 
284
- # Returns a string to ensure compatibility with <tt>Net::HTTPResponse</tt>.
294
+ # Returns a string to ensure compatibility with +Net::HTTPResponse+.
285
295
  def code
286
296
  @status.to_s
287
297
  end
@@ -384,7 +394,7 @@ module ActionDispatch # :nodoc:
384
394
  # status, headers, body = *response
385
395
  def to_a
386
396
  commit!
387
- rack_response @status, @header.to_hash
397
+ rack_response @status, @headers.to_hash
388
398
  end
389
399
  alias prepare! to_a
390
400
 
@@ -451,8 +461,7 @@ module ActionDispatch # :nodoc:
451
461
  # our last chance.
452
462
  commit! unless committed?
453
463
 
454
- headers.freeze
455
- request.commit_cookie_jar! unless committed?
464
+ @request.commit_cookie_jar! unless committed?
456
465
  end
457
466
 
458
467
  def build_buffer(response, body)
@@ -476,10 +485,6 @@ module ActionDispatch # :nodoc:
476
485
  @response = response
477
486
  end
478
487
 
479
- def each(*args, &block)
480
- @response.each(*args, &block)
481
- end
482
-
483
488
  def close
484
489
  # Rack "close" maps to Response#abort, and *not* Response#close
485
490
  # (which is used when the controller's finished writing)
@@ -490,14 +495,28 @@ module ActionDispatch # :nodoc:
490
495
  @response.body
491
496
  end
492
497
 
498
+ BODY_METHODS = { to_ary: true, each: true, call: true, to_path: true }
499
+
493
500
  def respond_to?(method, include_private = false)
494
- if method.to_sym == :to_path
501
+ if BODY_METHODS.key?(method)
495
502
  @response.stream.respond_to?(method)
496
503
  else
497
504
  super
498
505
  end
499
506
  end
500
507
 
508
+ def to_ary
509
+ @response.stream.to_ary
510
+ end
511
+
512
+ def each(*args, &block)
513
+ @response.each(*args, &block)
514
+ end
515
+
516
+ def call(*arguments, &block)
517
+ @response.stream.call(*arguments, &block)
518
+ end
519
+
501
520
  def to_path
502
521
  @response.stream.to_path
503
522
  end
@@ -505,16 +524,16 @@ module ActionDispatch # :nodoc:
505
524
 
506
525
  def handle_no_content!
507
526
  if NO_CONTENT_CODES.include?(@status)
508
- @header.delete CONTENT_TYPE
509
- @header.delete "Content-Length"
527
+ @headers.delete CONTENT_TYPE
528
+ @headers.delete "Content-Length"
510
529
  end
511
530
  end
512
531
 
513
- def rack_response(status, header)
532
+ def rack_response(status, headers)
514
533
  if NO_CONTENT_CODES.include?(status)
515
- [status, header, []]
534
+ [status, headers, []]
516
535
  else
517
- [status, header, RackBody.new(self)]
536
+ [status, headers, RackBody.new(self)]
518
537
  end
519
538
  end
520
539
  end
@@ -2,6 +2,8 @@
2
2
 
3
3
  module ActionDispatch
4
4
  module Http
5
+ # = Action Dispatch HTTP \UploadedFile
6
+ #
5
7
  # Models uploaded files.
6
8
  #
7
9
  # The actual file is accessible via the +tempfile+ accessor, though some
@@ -57,6 +57,9 @@ module ActionDispatch
57
57
  end
58
58
 
59
59
  def generate(name, options, path_parameters)
60
+ original_options = options.dup
61
+ path_params = options.delete(:path_params) || {}
62
+ options = path_params.merge(options)
60
63
  constraints = path_parameters.merge(options)
61
64
  missing_keys = nil
62
65
 
@@ -70,8 +73,11 @@ module ActionDispatch
70
73
 
71
74
  missing_keys = missing_keys(route, parameterized_parts)
72
75
  next if missing_keys && !missing_keys.empty?
73
- params = options.dup.delete_if do |key, _|
74
- parameterized_parts.key?(key) || route.defaults.key?(key)
76
+ params = options.delete_if do |key, _|
77
+ # top-level params' normal behavior of generating query_params
78
+ # should be preserved even if the same key is also a bind_param
79
+ parameterized_parts.key?(key) || route.defaults.key?(key) ||
80
+ (path_params.key?(key) && !original_options.key?(key))
75
81
  end
76
82
 
77
83
  defaults = route.defaults
@@ -183,22 +183,22 @@ module ActionDispatch
183
183
  end
184
184
 
185
185
  def offsets
186
- return @offsets if @offsets
187
-
188
- @offsets = [0]
189
-
190
- spec.find_all(&:symbol?).each do |node|
191
- node = node.to_sym
192
-
193
- if @requirements.key?(node)
194
- re = /#{Regexp.union(@requirements[node])}|/
195
- @offsets.push((re.match("").length - 1) + @offsets.last)
196
- else
197
- @offsets << @offsets.last
186
+ @offsets ||= begin
187
+ offsets = [0]
188
+
189
+ spec.find_all(&:symbol?).each do |node|
190
+ node = node.to_sym
191
+
192
+ if @requirements.key?(node)
193
+ re = /#{Regexp.union(@requirements[node])}|/
194
+ offsets.push((re.match("").length - 1) + offsets.last)
195
+ else
196
+ offsets << offsets.last
197
+ end
198
198
  end
199
- end
200
199
 
201
- @offsets
200
+ offsets
201
+ end
202
202
  end
203
203
  end
204
204
  end
@@ -5,7 +5,7 @@ module ActionDispatch
5
5
  module Journey
6
6
  class Route
7
7
  attr_reader :app, :path, :defaults, :name, :precedence, :constraints,
8
- :internal, :scope_options, :ast
8
+ :internal, :scope_options, :ast, :source_location
9
9
 
10
10
  alias :conditions :constraints
11
11
 
@@ -53,7 +53,7 @@ module ActionDispatch
53
53
  ##
54
54
  # +path+ is a path constraint.
55
55
  # +constraints+ is a hash of constraints to be applied to this route.
56
- def initialize(name:, app: nil, path:, constraints: {}, required_defaults: [], defaults: {}, request_method_match: nil, precedence: 0, scope_options: {}, internal: false)
56
+ def initialize(name:, app: nil, path:, constraints: {}, required_defaults: [], defaults: {}, request_method_match: nil, precedence: 0, scope_options: {}, internal: false, source_location: nil)
57
57
  @name = name
58
58
  @app = app
59
59
  @path = path
@@ -69,6 +69,7 @@ module ActionDispatch
69
69
  @path_formatter = @path.build_formatter
70
70
  @scope_options = scope_options
71
71
  @internal = internal
72
+ @source_location = source_location
72
73
 
73
74
  @ast = @path.ast.root
74
75
  @path.ast.route = self
@@ -46,20 +46,21 @@ module ActionDispatch
46
46
  }
47
47
 
48
48
  req.path_parameters = tmp_params
49
+ req.route_uri_pattern = route.path.spec.to_s
49
50
 
50
- status, headers, body = route.app.serve(req)
51
+ _, headers, _ = response = route.app.serve(req)
51
52
 
52
- if "pass" == headers["X-Cascade"]
53
+ if "pass" == headers[Constants::X_CASCADE]
53
54
  req.script_name = script_name
54
55
  req.path_info = path_info
55
56
  req.path_parameters = set_params
56
57
  next
57
58
  end
58
59
 
59
- return [status, headers, body]
60
+ return response
60
61
  end
61
62
 
62
- [404, { "X-Cascade" => "pass" }, ["Not Found"]]
63
+ [404, { Constants::X_CASCADE => "pass" }, ["Not Found"]]
63
64
  end
64
65
 
65
66
  def recognize(rails_req)
@@ -9,8 +9,8 @@ module ActionDispatch
9
9
 
10
10
  attr_reader :routes, :custom_routes, :anchored_routes
11
11
 
12
- def initialize
13
- @routes = []
12
+ def initialize(routes = [])
13
+ @routes = routes
14
14
  @ast = nil
15
15
  @anchored_routes = []
16
16
  @custom_routes = []
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionDispatch
4
+ class LogSubscriber < ActiveSupport::LogSubscriber
5
+ def redirect(event)
6
+ payload = event.payload
7
+
8
+ info { "Redirected to #{payload[:location]}" }
9
+
10
+ info do
11
+ status = payload[:status]
12
+
13
+ message = +"Completed #{status} #{Rack::Utils::HTTP_STATUS_CODES[status]} in #{event.duration.round}ms"
14
+ message << "\n\n" if defined?(Rails.env) && Rails.env.development?
15
+
16
+ message
17
+ end
18
+ end
19
+ subscribe_log_level :redirect, :info
20
+ end
21
+ end
22
+
23
+ ActionDispatch::LogSubscriber.attach_to :action_dispatch
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "erb"
4
3
  require "uri"
5
4
  require "active_support/actionable_error"
6
5
 
@@ -30,15 +29,15 @@ module ActionDispatch
30
29
  uri = URI.parse location
31
30
 
32
31
  if uri.relative? || uri.scheme == "http" || uri.scheme == "https"
33
- body = "<html><body>You are being <a href=\"#{ERB::Util.unwrapped_html_escape(location)}\">redirected</a>.</body></html>"
32
+ body = ""
34
33
  else
35
- return [400, { "Content-Type" => "text/plain" }, ["Invalid redirection URI"]]
34
+ return [400, { Rack::CONTENT_TYPE => "text/plain; charset=utf-8" }, ["Invalid redirection URI"]]
36
35
  end
37
36
 
38
37
  [302, {
39
- "Content-Type" => "text/html; charset=#{Response.default_charset}",
40
- "Content-Length" => body.bytesize.to_s,
41
- "Location" => location,
38
+ Rack::CONTENT_TYPE => "text/html; charset=#{Response.default_charset}",
39
+ Rack::CONTENT_LENGTH => body.bytesize.to_s,
40
+ ActionDispatch::Constants::LOCATION => location,
42
41
  }, [body]]
43
42
  end
44
43
  end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionDispatch
4
+ # = Action Dispatch \AssumeSSL
5
+ #
6
+ # When proxying through a load balancer that terminates SSL, the forwarded request will appear
7
+ # as though its HTTP instead of HTTPS to the application. This makes redirects and cookie
8
+ # security target HTTP instead of HTTPS. This middleware makes the server assume that the
9
+ # proxy already terminated SSL, and that the request really is HTTPS.
10
+ class AssumeSSL
11
+ def initialize(app)
12
+ @app = app
13
+ end
14
+
15
+ def call(env)
16
+ env["HTTPS"] = "on"
17
+ env["HTTP_X_FORWARDED_PORT"] = "443"
18
+ env["HTTP_X_FORWARDED_PROTO"] = "https"
19
+ env["rack.url_scheme"] = "https"
20
+
21
+ @app.call(env)
22
+ end
23
+ end
24
+ end
@@ -1,6 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ActionDispatch
4
+ # = Action Dispatch \Callbacks
5
+ #
4
6
  # Provides callbacks to be executed before and after dispatching the request.
5
7
  class Callbacks
6
8
  include ActiveSupport::Callbacks