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
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rack/body_proxy"
4
+
5
+ module ActionDispatch
6
+ class Executor
7
+ def initialize(app, executor)
8
+ @app, @executor = app, executor
9
+ end
10
+
11
+ def call(env)
12
+ state = @executor.run!
13
+ begin
14
+ response = @app.call(env)
15
+ returned = response << ::Rack::BodyProxy.new(response.pop) { state.complete! }
16
+ ensure
17
+ state.complete! unless returned
18
+ end
19
+ end
20
+ end
21
+ end
@@ -1,15 +1,8 @@
1
- require 'active_support/core_ext/hash/keys'
1
+ # frozen_string_literal: true
2
2
 
3
- module ActionDispatch
4
- class Request < Rack::Request
5
- # Access the contents of the flash. Use <tt>flash["notice"]</tt> to
6
- # read a notice you put there or <tt>flash["notice"] = "hello"</tt>
7
- # to put a new one.
8
- def flash
9
- @env[Flash::KEY] ||= Flash::FlashHash.from_session_value(session["flash"])
10
- end
11
- end
3
+ require "active_support/core_ext/hash/keys"
12
4
 
5
+ module ActionDispatch
13
6
  # The flash provides a way to pass temporary primitive-types (String, Array, Hash) between actions. Anything you place in the flash will be exposed
14
7
  # to the very next action and then cleared out. This is a great way of doing notices and alerts, such as a create
15
8
  # action that sets <tt>flash[:notice] = "Post successfully created"</tt> before redirecting to a display action that can
@@ -45,7 +38,46 @@ module ActionDispatch
45
38
  #
46
39
  # See docs on the FlashHash class for more details about the flash.
47
40
  class Flash
48
- KEY = 'action_dispatch.request.flash_hash'.freeze
41
+ KEY = "action_dispatch.request.flash_hash".freeze
42
+
43
+ module RequestMethods
44
+ # Access the contents of the flash. Use <tt>flash["notice"]</tt> to
45
+ # read a notice you put there or <tt>flash["notice"] = "hello"</tt>
46
+ # to put a new one.
47
+ def flash
48
+ flash = flash_hash
49
+ return flash if flash
50
+ self.flash = Flash::FlashHash.from_session_value(session["flash"])
51
+ end
52
+
53
+ def flash=(flash)
54
+ set_header Flash::KEY, flash
55
+ end
56
+
57
+ def flash_hash # :nodoc:
58
+ get_header Flash::KEY
59
+ end
60
+
61
+ def commit_flash # :nodoc:
62
+ session = self.session || {}
63
+ flash_hash = self.flash_hash
64
+
65
+ if flash_hash && (flash_hash.present? || session.key?("flash"))
66
+ session["flash"] = flash_hash.to_session_value
67
+ self.flash = flash_hash.dup
68
+ end
69
+
70
+ if (!session.respond_to?(:loaded?) || session.loaded?) && # reset_session uses {}, which doesn't implement #loaded?
71
+ session.key?("flash") && session["flash"].nil?
72
+ session.delete("flash")
73
+ end
74
+ end
75
+
76
+ def reset_session # :nodoc:
77
+ super
78
+ self.flash = nil
79
+ end
80
+ end
49
81
 
50
82
  class FlashNow #:nodoc:
51
83
  attr_accessor :flash
@@ -80,24 +112,30 @@ module ActionDispatch
80
112
  include Enumerable
81
113
 
82
114
  def self.from_session_value(value) #:nodoc:
83
- flash = case value
84
- when FlashHash # Rails 3.1, 3.2
85
- new(value.instance_variable_get(:@flashes), value.instance_variable_get(:@used))
86
- when Hash # Rails 4.0
87
- new(value['flashes'], value['discard'])
88
- else
89
- new
90
- end
91
-
92
- flash.tap(&:sweep)
93
- end
94
-
95
- # Builds a hash containing the discarded values and the hashes
96
- # representing the flashes.
97
- # If there are no values in @flashes, returns nil.
115
+ case value
116
+ when FlashHash # Rails 3.1, 3.2
117
+ flashes = value.instance_variable_get(:@flashes)
118
+ if discard = value.instance_variable_get(:@used)
119
+ flashes.except!(*discard)
120
+ end
121
+ new(flashes, flashes.keys)
122
+ when Hash # Rails 4.0
123
+ flashes = value["flashes"]
124
+ if discard = value["discard"]
125
+ flashes.except!(*discard)
126
+ end
127
+ new(flashes, flashes.keys)
128
+ else
129
+ new
130
+ end
131
+ end
132
+
133
+ # Builds a hash containing the flashes to keep for the next request.
134
+ # If there are none to keep, returns +nil+.
98
135
  def to_session_value #:nodoc:
99
- return nil if empty?
100
- {'discard' => @discard.to_a, 'flashes' => @flashes}
136
+ flashes_to_keep = @flashes.except(*@discard)
137
+ return nil if flashes_to_keep.empty?
138
+ { "discard" => [], "flashes" => flashes_to_keep }
101
139
  end
102
140
 
103
141
  def initialize(flashes = {}, discard = []) #:nodoc:
@@ -241,36 +279,22 @@ module ActionDispatch
241
279
  end
242
280
 
243
281
  protected
244
- def now_is_loaded?
245
- @now
246
- end
247
-
248
- def stringify_array(array)
249
- array.map do |item|
250
- item.kind_of?(Symbol) ? item.to_s : item
282
+ def now_is_loaded?
283
+ @now
251
284
  end
252
- end
253
- end
254
285
 
255
- def initialize(app)
256
- @app = app
286
+ private
287
+ def stringify_array(array) # :doc:
288
+ array.map do |item|
289
+ item.kind_of?(Symbol) ? item.to_s : item
290
+ end
291
+ end
257
292
  end
258
293
 
259
- def call(env)
260
- @app.call(env)
261
- ensure
262
- session = Request::Session.find(env) || {}
263
- flash_hash = env[KEY]
264
-
265
- if flash_hash && (flash_hash.present? || session.key?('flash'))
266
- session["flash"] = flash_hash.to_session_value
267
- env[KEY] = flash_hash.dup
268
- end
294
+ def self.new(app) app; end
295
+ end
269
296
 
270
- if (!session.respond_to?(:loaded?) || session.loaded?) && # (reset_session uses {}, which doesn't implement #loaded?)
271
- session.key?('flash') && session['flash'].nil?
272
- session.delete('flash')
273
- end
274
- end
297
+ class Request
298
+ prepend Flash::RequestMethods
275
299
  end
276
300
  end
@@ -1,11 +1,13 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActionDispatch
2
4
  # When called, this middleware renders an error page. By default if an HTML
3
- # response is expected it will render static error pages from the `/public`
5
+ # response is expected it will render static error pages from the <tt>/public</tt>
4
6
  # directory. For example when this middleware receives a 500 response it will
5
- # render the template found in `/public/500.html`.
7
+ # render the template found in <tt>/public/500.html</tt>.
6
8
  # If an internationalized locale is set, this middleware will attempt to render
7
- # the template in `/public/500.<locale>.html`. If an internationalized template
8
- # is not found it will fall back on `/public/500.html`.
9
+ # the template in <tt>/public/500.<locale>.html</tt>. If an internationalized template
10
+ # is not found it will fall back on <tt>/public/500.html</tt>.
9
11
  #
10
12
  # When a request with a content type other than HTML is made, this middleware
11
13
  # will attempt to convert error information into the appropriate response type.
@@ -17,39 +19,39 @@ module ActionDispatch
17
19
  end
18
20
 
19
21
  def call(env)
20
- status = env["PATH_INFO"][1..-1]
21
22
  request = ActionDispatch::Request.new(env)
23
+ status = request.path_info[1..-1].to_i
22
24
  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]) }
25
+ body = { status: status, error: Rack::Utils::HTTP_STATUS_CODES.fetch(status, Rack::Utils::HTTP_STATUS_CODES[500]) }
24
26
 
25
27
  render(status, content_type, body)
26
28
  end
27
29
 
28
30
  private
29
31
 
30
- def render(status, content_type, body)
31
- format = "to_#{content_type.to_sym}" if content_type
32
- if format && body.respond_to?(format)
33
- render_format(status, content_type, body.public_send(format))
34
- else
35
- render_html(status)
32
+ def render(status, content_type, body)
33
+ format = "to_#{content_type.to_sym}" if content_type
34
+ if format && body.respond_to?(format)
35
+ render_format(status, content_type, body.public_send(format))
36
+ else
37
+ render_html(status)
38
+ end
36
39
  end
37
- end
38
40
 
39
- def render_format(status, content_type, body)
40
- [status, {'Content-Type' => "#{content_type}; charset=#{ActionDispatch::Response.default_charset}",
41
- 'Content-Length' => body.bytesize.to_s}, [body]]
42
- end
41
+ def render_format(status, content_type, body)
42
+ [status, { "Content-Type" => "#{content_type}; charset=#{ActionDispatch::Response.default_charset}",
43
+ "Content-Length" => body.bytesize.to_s }, [body]]
44
+ end
43
45
 
44
- def render_html(status)
45
- path = "#{public_path}/#{status}.#{I18n.locale}.html"
46
- path = "#{public_path}/#{status}.html" unless (found = File.exist?(path))
46
+ def render_html(status)
47
+ path = "#{public_path}/#{status}.#{I18n.locale}.html"
48
+ path = "#{public_path}/#{status}.html" unless (found = File.exist?(path))
47
49
 
48
- if found || File.exist?(path)
49
- render_format(status, 'text/html', File.read(path))
50
- else
51
- [404, { "X-Cascade" => "pass" }, []]
50
+ if found || File.exist?(path)
51
+ render_format(status, "text/html", File.read(path))
52
+ else
53
+ [404, { "X-Cascade" => "pass" }, []]
54
+ end
52
55
  end
53
- end
54
56
  end
55
57
  end
@@ -1,98 +1,12 @@
1
- require 'active_support/deprecation/reporting'
1
+ # frozen_string_literal: true
2
2
 
3
3
  module ActionDispatch
4
- # ActionDispatch::Reloader provides prepare and cleanup callbacks,
5
- # intended to assist with code reloading during development.
6
- #
7
- # Prepare callbacks are run before each request, and cleanup callbacks
8
- # after each request. In this respect they are analogs of ActionDispatch::Callback's
9
- # before and after callbacks. However, cleanup callbacks are not called until the
10
- # request is fully complete -- that is, after #close has been called on
11
- # the response body. This is important for streaming responses such as the
12
- # following:
13
- #
14
- # self.response_body = lambda { |response, output|
15
- # # code here which refers to application models
16
- # }
17
- #
18
- # Cleanup callbacks will not be called until after the response_body lambda
19
- # is evaluated, ensuring that it can refer to application models and other
20
- # classes before they are unloaded.
4
+ # ActionDispatch::Reloader wraps the request with callbacks provided by ActiveSupport::Reloader
5
+ # callbacks, intended to assist with code reloading during development.
21
6
  #
22
7
  # By default, ActionDispatch::Reloader is included in the middleware stack
23
8
  # only in the development environment; specifically, when +config.cache_classes+
24
- # is false. Callbacks may be registered even when it is not included in the
25
- # middleware stack, but are executed only when <tt>ActionDispatch::Reloader.prepare!</tt>
26
- # or <tt>ActionDispatch::Reloader.cleanup!</tt> are called manually.
27
- #
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.
37
- 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)
42
- end
43
-
44
- # Add a cleanup callback. Cleanup callbacks are run after each request is
45
- # complete (after #close is called on the response body).
46
- 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)
51
- end
52
-
53
- # Execute all prepare callbacks.
54
- def self.prepare!
55
- new(nil).prepare!
56
- end
57
-
58
- # Execute all cleanup callbacks.
59
- 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
80
- end
81
-
82
- def prepare! #:nodoc:
83
- run_callbacks :prepare if validated?
84
- end
85
-
86
- def cleanup! #:nodoc:
87
- run_callbacks :cleanup if validated?
88
- ensure
89
- @validated = true
90
- end
91
-
92
- private
93
-
94
- def validated? #:nodoc:
95
- @validated
96
- end
9
+ # is false.
10
+ class Reloader < Executor
97
11
  end
98
12
  end
@@ -1,4 +1,6 @@
1
- require 'ipaddr'
1
+ # frozen_string_literal: true
2
+
3
+ require "ipaddr"
2
4
 
3
5
  module ActionDispatch
4
6
  # This middleware calculates the IP address of the remote client that is
@@ -10,7 +12,7 @@ module ActionDispatch
10
12
  # by @gingerlime. A more detailed explanation of the algorithm is given
11
13
  # at GetIp#calculate_ip.
12
14
  #
13
- # Some Rack servers concatenate repeated headers, like {HTTP RFC 2616}[http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2]
15
+ # Some Rack servers concatenate repeated headers, like {HTTP RFC 2616}[https://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2]
14
16
  # requires. Some Rack servers simply drop preceding headers, and only report
15
17
  # the value that was {given in the last header}[http://andre.arko.net/2011/12/26/repeated-headers-and-ruby-web-servers].
16
18
  # If you are behind multiple proxy servers (like NGINX to HAProxy to Unicorn)
@@ -29,7 +31,7 @@ module ActionDispatch
29
31
  # The default trusted IPs list simply includes IP addresses that are
30
32
  # guaranteed by the IP specification to be private addresses. Those will
31
33
  # not be the ultimate client IP in production, and so are discarded. See
32
- # http://en.wikipedia.org/wiki/Private_network for details.
34
+ # https://en.wikipedia.org/wiki/Private_network for details.
33
35
  TRUSTED_PROXIES = [
34
36
  "127.0.0.1", # localhost IPv4
35
37
  "::1", # localhost IPv6
@@ -43,7 +45,7 @@ module ActionDispatch
43
45
 
44
46
  # Create a new +RemoteIp+ middleware instance.
45
47
  #
46
- # The +check_ip_spoofing+ option is on by default. When on, an exception
48
+ # The +ip_spoofing_check+ option is on by default. When on, an exception
47
49
  # is raised if it looks like the client is trying to lie about its own IP
48
50
  # address. It makes sense to turn off this check on sites aimed at non-IP
49
51
  # clients (like WAP devices), or behind proxies that set headers in an
@@ -57,9 +59,9 @@ module ActionDispatch
57
59
  # with your proxy servers after it. If your proxies aren't removed, pass
58
60
  # them in via the +custom_proxies+ parameter. That way, the middleware will
59
61
  # ignore those IP addresses, and return the one that you want.
60
- def initialize(app, check_ip_spoofing = true, custom_proxies = nil)
62
+ def initialize(app, ip_spoofing_check = true, custom_proxies = nil)
61
63
  @app = app
62
- @check_ip = check_ip_spoofing
64
+ @check_ip = ip_spoofing_check
63
65
  @proxies = if custom_proxies.blank?
64
66
  TRUSTED_PROXIES
65
67
  elsif custom_proxies.respond_to?(:any?)
@@ -74,18 +76,19 @@ module ActionDispatch
74
76
  # requests. For those requests that do need to know the IP, the
75
77
  # GetIp#calculate_ip method will calculate the memoized client IP address.
76
78
  def call(env)
77
- env["action_dispatch.remote_ip"] = GetIp.new(env, self)
78
- @app.call(env)
79
+ req = ActionDispatch::Request.new env
80
+ req.remote_ip = GetIp.new(req, check_ip, proxies)
81
+ @app.call(req.env)
79
82
  end
80
83
 
81
84
  # The GetIp class exists as a way to defer processing of the request data
82
85
  # into an actual IP address. If the ActionDispatch::Request#remote_ip method
83
86
  # is called, this class will calculate the value and then memoize it.
84
87
  class GetIp
85
- def initialize(env, middleware)
86
- @env = env
87
- @check_ip = middleware.check_ip
88
- @proxies = middleware.proxies
88
+ def initialize(req, check_ip, proxies)
89
+ @req = req
90
+ @check_ip = check_ip
91
+ @proxies = proxies
89
92
  end
90
93
 
91
94
  # Sort through the various IP address headers, looking for the IP most
@@ -108,23 +111,31 @@ module ActionDispatch
108
111
  # the last address left, which was presumably set by one of those proxies.
109
112
  def calculate_ip
110
113
  # Set by the Rack web server, this is a single value.
111
- remote_addr = ips_from('REMOTE_ADDR').last
114
+ remote_addr = ips_from(@req.remote_addr).last
112
115
 
113
116
  # 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
117
+ client_ips = ips_from(@req.client_ip).reverse
118
+ forwarded_ips = ips_from(@req.x_forwarded_for).reverse
116
119
 
117
120
  # +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.
121
+ # If they are both set, it means that either:
122
+ #
123
+ # 1) This request passed through two proxies with incompatible IP header
124
+ # conventions.
125
+ # 2) The client passed one of +Client-Ip+ or +X-Forwarded-For+
126
+ # (whichever the proxy servers weren't using) themselves.
127
+ #
128
+ # Either way, there is no way for us to determine which header is the
129
+ # right one after the fact. Since we have no idea, if we are concerned
130
+ # about IP spoofing we need to give up and explode. (If you're not
131
+ # concerned about IP spoofing you can turn the +ip_spoofing_check+
132
+ # option off.)
122
133
  should_check_ip = @check_ip && client_ips.last && forwarded_ips.last
123
134
  if should_check_ip && !forwarded_ips.include?(client_ips.last)
124
135
  # We don't know which came from the proxy, and which from the user
125
- 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}"
136
+ raise IpSpoofAttackError, "IP spoofing attack?! " \
137
+ "HTTP_CLIENT_IP=#{@req.client_ip.inspect} " \
138
+ "HTTP_X_FORWARDED_FOR=#{@req.x_forwarded_for.inspect}"
128
139
  end
129
140
 
130
141
  # We assume these things about the IP headers:
@@ -144,16 +155,17 @@ module ActionDispatch
144
155
  @ip ||= calculate_ip
145
156
  end
146
157
 
147
- protected
158
+ private
148
159
 
149
- def ips_from(header)
150
- # Split the comma-separated list into an array of strings
151
- ips = @env[header] ? @env[header].strip.split(/[,\s]+/) : []
160
+ def ips_from(header) # :doc:
161
+ return [] unless header
162
+ # Split the comma-separated list into an array of strings.
163
+ ips = header.strip.split(/[,\s]+/)
152
164
  ips.select do |ip|
153
165
  begin
154
- # Only return IPs that are valid according to the IPAddr#new method
166
+ # Only return IPs that are valid according to the IPAddr#new method.
155
167
  range = IPAddr.new(ip).to_range
156
- # we want to make sure nobody is sneaking a netmask in
168
+ # We want to make sure nobody is sneaking a netmask in.
157
169
  range.begin == range.end
158
170
  rescue ArgumentError
159
171
  nil
@@ -161,13 +173,11 @@ module ActionDispatch
161
173
  end
162
174
  end
163
175
 
164
- def filter_proxies(ips)
176
+ def filter_proxies(ips) # :doc:
165
177
  ips.reject do |ip|
166
178
  @proxies.any? { |proxy| proxy === ip }
167
179
  end
168
180
  end
169
-
170
181
  end
171
-
172
182
  end
173
183
  end