actionpack 4.0.1 → 4.2.11.1

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 (241) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +402 -1173
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +7 -7
  5. data/lib/abstract_controller/base.rb +39 -7
  6. data/lib/abstract_controller/callbacks.rb +32 -53
  7. data/lib/abstract_controller/collector.rb +11 -1
  8. data/lib/abstract_controller/helpers.rb +26 -16
  9. data/lib/abstract_controller/railties/routes_helpers.rb +3 -3
  10. data/lib/abstract_controller/rendering.rb +57 -127
  11. data/lib/abstract_controller/url_for.rb +1 -1
  12. data/lib/abstract_controller.rb +1 -2
  13. data/lib/action_controller/base.rb +19 -10
  14. data/lib/action_controller/caching/fragments.rb +7 -1
  15. data/lib/action_controller/caching.rb +2 -12
  16. data/lib/action_controller/log_subscriber.rb +29 -20
  17. data/lib/action_controller/metal/conditional_get.rb +37 -12
  18. data/lib/action_controller/metal/data_streaming.rb +1 -1
  19. data/lib/action_controller/metal/etag_with_template_digest.rb +50 -0
  20. data/lib/action_controller/metal/exceptions.rb +1 -1
  21. data/lib/action_controller/metal/flash.rb +17 -0
  22. data/lib/action_controller/metal/force_ssl.rb +2 -2
  23. data/lib/action_controller/metal/head.rb +8 -6
  24. data/lib/action_controller/metal/helpers.rb +6 -2
  25. data/lib/action_controller/metal/http_authentication.rb +45 -23
  26. data/lib/action_controller/metal/instrumentation.rb +9 -6
  27. data/lib/action_controller/metal/live.rb +173 -20
  28. data/lib/action_controller/metal/mime_responds.rb +127 -232
  29. data/lib/action_controller/metal/params_wrapper.rb +16 -9
  30. data/lib/action_controller/metal/rack_delegation.rb +1 -1
  31. data/lib/action_controller/metal/redirecting.rb +34 -26
  32. data/lib/action_controller/metal/renderers.rb +39 -12
  33. data/lib/action_controller/metal/rendering.rb +41 -14
  34. data/lib/action_controller/metal/request_forgery_protection.rb +147 -19
  35. data/lib/action_controller/metal/streaming.rb +19 -21
  36. data/lib/action_controller/metal/strong_parameters.rb +166 -22
  37. data/lib/action_controller/metal/testing.rb +0 -1
  38. data/lib/action_controller/metal/url_for.rb +11 -12
  39. data/lib/action_controller/metal.rb +14 -8
  40. data/lib/action_controller/model_naming.rb +1 -1
  41. data/lib/action_controller/railtie.rb +5 -1
  42. data/lib/action_controller/test_case.rb +160 -94
  43. data/lib/action_controller.rb +2 -18
  44. data/lib/action_dispatch/http/cache.rb +5 -4
  45. data/lib/action_dispatch/http/filter_parameters.rb +2 -2
  46. data/lib/action_dispatch/http/filter_redirect.rb +5 -4
  47. data/lib/action_dispatch/http/headers.rb +46 -10
  48. data/lib/action_dispatch/http/mime_negotiation.rb +31 -4
  49. data/lib/action_dispatch/http/mime_type.rb +25 -26
  50. data/lib/action_dispatch/http/mime_types.rb +1 -0
  51. data/lib/action_dispatch/http/parameter_filter.rb +1 -1
  52. data/lib/action_dispatch/http/parameters.rb +25 -41
  53. data/lib/action_dispatch/http/request.rb +49 -32
  54. data/lib/action_dispatch/http/response.rb +127 -25
  55. data/lib/action_dispatch/http/upload.rb +9 -21
  56. data/lib/action_dispatch/http/url.rb +97 -70
  57. data/lib/action_dispatch/journey/formatter.rb +35 -19
  58. data/lib/action_dispatch/journey/gtg/builder.rb +3 -3
  59. data/lib/action_dispatch/journey/gtg/simulator.rb +10 -7
  60. data/lib/action_dispatch/journey/gtg/transition_table.rb +23 -33
  61. data/lib/action_dispatch/journey/nfa/dot.rb +2 -2
  62. data/lib/action_dispatch/journey/nfa/simulator.rb +1 -1
  63. data/lib/action_dispatch/journey/nfa/transition_table.rb +5 -5
  64. data/lib/action_dispatch/journey/nodes/node.rb +4 -0
  65. data/lib/action_dispatch/journey/parser.rb +51 -59
  66. data/lib/action_dispatch/journey/parser.y +12 -10
  67. data/lib/action_dispatch/journey/path/pattern.rb +16 -19
  68. data/lib/action_dispatch/journey/route.rb +8 -19
  69. data/lib/action_dispatch/journey/router/strexp.rb +9 -6
  70. data/lib/action_dispatch/journey/router/utils.rb +54 -18
  71. data/lib/action_dispatch/journey/router.rb +53 -75
  72. data/lib/action_dispatch/journey/routes.rb +4 -0
  73. data/lib/action_dispatch/journey/scanner.rb +5 -5
  74. data/lib/action_dispatch/journey/visitors.rb +81 -60
  75. data/lib/action_dispatch/journey/visualizer/fsm.css +0 -4
  76. data/lib/action_dispatch/journey/visualizer/index.html.erb +2 -2
  77. data/lib/action_dispatch/middleware/callbacks.rb +7 -7
  78. data/lib/action_dispatch/middleware/cookies.rb +119 -43
  79. data/lib/action_dispatch/middleware/debug_exceptions.rb +32 -13
  80. data/lib/action_dispatch/middleware/exception_wrapper.rb +60 -20
  81. data/lib/action_dispatch/middleware/flash.rb +37 -24
  82. data/lib/action_dispatch/middleware/params_parser.rb +2 -2
  83. data/lib/action_dispatch/middleware/public_exceptions.rb +12 -3
  84. data/lib/action_dispatch/middleware/reloader.rb +11 -2
  85. data/lib/action_dispatch/middleware/remote_ip.rb +40 -54
  86. data/lib/action_dispatch/middleware/request_id.rb +1 -1
  87. data/lib/action_dispatch/middleware/session/cache_store.rb +3 -3
  88. data/lib/action_dispatch/middleware/session/cookie_store.rb +8 -7
  89. data/lib/action_dispatch/middleware/show_exceptions.rb +6 -2
  90. data/lib/action_dispatch/middleware/ssl.rb +10 -7
  91. data/lib/action_dispatch/middleware/static.rb +79 -23
  92. data/lib/action_dispatch/middleware/templates/rescues/{_request_and_response.erb → _request_and_response.html.erb} +0 -0
  93. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.text.erb +23 -0
  94. data/lib/action_dispatch/middleware/templates/rescues/_source.erb +21 -19
  95. data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +52 -0
  96. data/lib/action_dispatch/middleware/templates/rescues/_trace.text.erb +9 -0
  97. data/lib/action_dispatch/middleware/templates/rescues/{diagnostics.erb → diagnostics.html.erb} +1 -1
  98. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +9 -0
  99. data/lib/action_dispatch/middleware/templates/rescues/layout.erb +6 -0
  100. data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +11 -0
  101. data/lib/action_dispatch/middleware/templates/rescues/missing_template.text.erb +3 -0
  102. data/lib/action_dispatch/middleware/templates/rescues/{routing_error.erb → routing_error.html.erb} +3 -1
  103. data/lib/action_dispatch/middleware/templates/rescues/routing_error.text.erb +11 -0
  104. data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +20 -0
  105. data/lib/action_dispatch/middleware/templates/rescues/template_error.text.erb +7 -0
  106. data/lib/action_dispatch/middleware/templates/rescues/{unknown_action.erb → unknown_action.html.erb} +1 -1
  107. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.text.erb +3 -0
  108. data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +120 -64
  109. data/lib/action_dispatch/railtie.rb +5 -2
  110. data/lib/action_dispatch/request/session.rb +12 -0
  111. data/lib/action_dispatch/request/utils.rb +35 -0
  112. data/lib/action_dispatch/routing/endpoint.rb +10 -0
  113. data/lib/action_dispatch/routing/inspector.rb +11 -17
  114. data/lib/action_dispatch/routing/mapper.rb +519 -312
  115. data/lib/action_dispatch/routing/polymorphic_routes.rb +204 -79
  116. data/lib/action_dispatch/routing/redirection.rb +51 -26
  117. data/lib/action_dispatch/routing/route_set.rb +331 -206
  118. data/lib/action_dispatch/routing/routes_proxy.rb +5 -4
  119. data/lib/action_dispatch/routing/url_for.rb +19 -5
  120. data/lib/action_dispatch/routing.rb +9 -6
  121. data/lib/action_dispatch/testing/assertions/dom.rb +2 -26
  122. data/lib/action_dispatch/testing/assertions/response.rb +9 -15
  123. data/lib/action_dispatch/testing/assertions/routing.rb +22 -22
  124. data/lib/action_dispatch/testing/assertions/selector.rb +2 -429
  125. data/lib/action_dispatch/testing/assertions/tag.rb +2 -134
  126. data/lib/action_dispatch/testing/assertions.rb +11 -7
  127. data/lib/action_dispatch/testing/integration.rb +31 -29
  128. data/lib/action_dispatch/testing/test_request.rb +1 -1
  129. data/lib/action_dispatch/testing/test_response.rb +1 -5
  130. data/lib/action_dispatch.rb +5 -8
  131. data/lib/action_pack/gem_version.rb +15 -0
  132. data/lib/action_pack/version.rb +4 -7
  133. data/lib/action_pack.rb +1 -1
  134. metadata +77 -159
  135. data/lib/abstract_controller/layouts.rb +0 -423
  136. data/lib/abstract_controller/view_paths.rb +0 -96
  137. data/lib/action_controller/deprecated/integration_test.rb +0 -5
  138. data/lib/action_controller/deprecated.rb +0 -7
  139. data/lib/action_controller/metal/responder.rb +0 -287
  140. data/lib/action_controller/record_identifier.rb +0 -31
  141. data/lib/action_controller/vendor/html-scanner.rb +0 -5
  142. data/lib/action_dispatch/middleware/templates/rescues/_trace.erb +0 -24
  143. data/lib/action_dispatch/middleware/templates/rescues/missing_template.erb +0 -7
  144. data/lib/action_dispatch/middleware/templates/rescues/template_error.erb +0 -43
  145. data/lib/action_view/base.rb +0 -201
  146. data/lib/action_view/buffers.rb +0 -49
  147. data/lib/action_view/context.rb +0 -36
  148. data/lib/action_view/dependency_tracker.rb +0 -93
  149. data/lib/action_view/digestor.rb +0 -113
  150. data/lib/action_view/flows.rb +0 -76
  151. data/lib/action_view/helpers/active_model_helper.rb +0 -49
  152. data/lib/action_view/helpers/asset_tag_helper.rb +0 -320
  153. data/lib/action_view/helpers/asset_url_helper.rb +0 -355
  154. data/lib/action_view/helpers/atom_feed_helper.rb +0 -203
  155. data/lib/action_view/helpers/cache_helper.rb +0 -196
  156. data/lib/action_view/helpers/capture_helper.rb +0 -216
  157. data/lib/action_view/helpers/controller_helper.rb +0 -25
  158. data/lib/action_view/helpers/csrf_helper.rb +0 -30
  159. data/lib/action_view/helpers/date_helper.rb +0 -1083
  160. data/lib/action_view/helpers/debug_helper.rb +0 -39
  161. data/lib/action_view/helpers/form_helper.rb +0 -1880
  162. data/lib/action_view/helpers/form_options_helper.rb +0 -838
  163. data/lib/action_view/helpers/form_tag_helper.rb +0 -785
  164. data/lib/action_view/helpers/javascript_helper.rb +0 -117
  165. data/lib/action_view/helpers/number_helper.rb +0 -441
  166. data/lib/action_view/helpers/output_safety_helper.rb +0 -38
  167. data/lib/action_view/helpers/record_tag_helper.rb +0 -106
  168. data/lib/action_view/helpers/rendering_helper.rb +0 -90
  169. data/lib/action_view/helpers/sanitize_helper.rb +0 -256
  170. data/lib/action_view/helpers/tag_helper.rb +0 -173
  171. data/lib/action_view/helpers/tags/base.rb +0 -148
  172. data/lib/action_view/helpers/tags/check_box.rb +0 -64
  173. data/lib/action_view/helpers/tags/checkable.rb +0 -16
  174. data/lib/action_view/helpers/tags/collection_check_boxes.rb +0 -44
  175. data/lib/action_view/helpers/tags/collection_helpers.rb +0 -84
  176. data/lib/action_view/helpers/tags/collection_radio_buttons.rb +0 -36
  177. data/lib/action_view/helpers/tags/collection_select.rb +0 -28
  178. data/lib/action_view/helpers/tags/color_field.rb +0 -25
  179. data/lib/action_view/helpers/tags/date_field.rb +0 -13
  180. data/lib/action_view/helpers/tags/date_select.rb +0 -72
  181. data/lib/action_view/helpers/tags/datetime_field.rb +0 -22
  182. data/lib/action_view/helpers/tags/datetime_local_field.rb +0 -19
  183. data/lib/action_view/helpers/tags/datetime_select.rb +0 -8
  184. data/lib/action_view/helpers/tags/email_field.rb +0 -8
  185. data/lib/action_view/helpers/tags/file_field.rb +0 -8
  186. data/lib/action_view/helpers/tags/grouped_collection_select.rb +0 -29
  187. data/lib/action_view/helpers/tags/hidden_field.rb +0 -8
  188. data/lib/action_view/helpers/tags/label.rb +0 -66
  189. data/lib/action_view/helpers/tags/month_field.rb +0 -13
  190. data/lib/action_view/helpers/tags/number_field.rb +0 -18
  191. data/lib/action_view/helpers/tags/password_field.rb +0 -12
  192. data/lib/action_view/helpers/tags/radio_button.rb +0 -31
  193. data/lib/action_view/helpers/tags/range_field.rb +0 -8
  194. data/lib/action_view/helpers/tags/search_field.rb +0 -24
  195. data/lib/action_view/helpers/tags/select.rb +0 -40
  196. data/lib/action_view/helpers/tags/tel_field.rb +0 -8
  197. data/lib/action_view/helpers/tags/text_area.rb +0 -18
  198. data/lib/action_view/helpers/tags/text_field.rb +0 -29
  199. data/lib/action_view/helpers/tags/time_field.rb +0 -13
  200. data/lib/action_view/helpers/tags/time_select.rb +0 -8
  201. data/lib/action_view/helpers/tags/time_zone_select.rb +0 -20
  202. data/lib/action_view/helpers/tags/url_field.rb +0 -8
  203. data/lib/action_view/helpers/tags/week_field.rb +0 -13
  204. data/lib/action_view/helpers/tags.rb +0 -39
  205. data/lib/action_view/helpers/text_helper.rb +0 -443
  206. data/lib/action_view/helpers/translation_helper.rb +0 -107
  207. data/lib/action_view/helpers/url_helper.rb +0 -635
  208. data/lib/action_view/helpers.rb +0 -58
  209. data/lib/action_view/locale/en.yml +0 -56
  210. data/lib/action_view/log_subscriber.rb +0 -30
  211. data/lib/action_view/lookup_context.rb +0 -241
  212. data/lib/action_view/model_naming.rb +0 -12
  213. data/lib/action_view/path_set.rb +0 -77
  214. data/lib/action_view/railtie.rb +0 -43
  215. data/lib/action_view/record_identifier.rb +0 -84
  216. data/lib/action_view/renderer/abstract_renderer.rb +0 -47
  217. data/lib/action_view/renderer/partial_renderer.rb +0 -492
  218. data/lib/action_view/renderer/renderer.rb +0 -50
  219. data/lib/action_view/renderer/streaming_template_renderer.rb +0 -103
  220. data/lib/action_view/renderer/template_renderer.rb +0 -96
  221. data/lib/action_view/routing_url_for.rb +0 -107
  222. data/lib/action_view/tasks/dependencies.rake +0 -17
  223. data/lib/action_view/template/error.rb +0 -138
  224. data/lib/action_view/template/handlers/builder.rb +0 -26
  225. data/lib/action_view/template/handlers/erb.rb +0 -146
  226. data/lib/action_view/template/handlers/raw.rb +0 -11
  227. data/lib/action_view/template/handlers.rb +0 -53
  228. data/lib/action_view/template/resolver.rb +0 -326
  229. data/lib/action_view/template/text.rb +0 -34
  230. data/lib/action_view/template/types.rb +0 -57
  231. data/lib/action_view/template.rb +0 -339
  232. data/lib/action_view/test_case.rb +0 -270
  233. data/lib/action_view/testing/resolvers.rb +0 -50
  234. data/lib/action_view/vendor/html-scanner/html/document.rb +0 -68
  235. data/lib/action_view/vendor/html-scanner/html/node.rb +0 -532
  236. data/lib/action_view/vendor/html-scanner/html/sanitizer.rb +0 -188
  237. data/lib/action_view/vendor/html-scanner/html/selector.rb +0 -830
  238. data/lib/action_view/vendor/html-scanner/html/tokenizer.rb +0 -107
  239. data/lib/action_view/vendor/html-scanner/html/version.rb +0 -11
  240. data/lib/action_view/vendor/html-scanner.rb +0 -20
  241. data/lib/action_view.rb +0 -93
@@ -1,3 +1,5 @@
1
+ require 'active_support/core_ext/hash/keys'
2
+
1
3
  module ActionDispatch
2
4
  class Request < Rack::Request
3
5
  # Access the contents of the flash. Use <tt>flash["notice"]</tt> to
@@ -8,7 +10,7 @@ module ActionDispatch
8
10
  end
9
11
  end
10
12
 
11
- # The flash provides a way to pass temporary objects between actions. Anything you place in the flash will be exposed
13
+ # The flash provides a way to pass temporary primitive-types (String, Array, Hash) between actions. Anything you place in the flash will be exposed
12
14
  # to the very next action and then cleared out. This is a great way of doing notices and alerts, such as a create
13
15
  # action that sets <tt>flash[:notice] = "Post successfully created"</tt> before redirecting to a display action that can
14
16
  # then expose the flash to its template. Actually, that exposure is automatically done.
@@ -35,8 +37,11 @@ module ActionDispatch
35
37
  # flash.alert = "You must be logged in"
36
38
  # flash.notice = "Post successfully created"
37
39
  #
38
- # This example just places a string in the flash, but you can put any object in there. And of course, you can put as
39
- # many as you like at a time too. Just remember: They'll be gone by the time the next action has been performed.
40
+ # This example places a string in the flash. And of course, you can put as many as you like at a time too. If you want to pass
41
+ # non-primitive types, you will have to handle that in your application. Example: To show messages with links, you will have to
42
+ # use sanitize helper.
43
+ #
44
+ # Just remember: They'll be gone by the time the next action has been performed.
40
45
  #
41
46
  # See docs on the FlashHash class for more details about the flash.
42
47
  class Flash
@@ -50,13 +55,14 @@ module ActionDispatch
50
55
  end
51
56
 
52
57
  def []=(k, v)
58
+ k = k.to_s
53
59
  @flash[k] = v
54
60
  @flash.discard(k)
55
61
  v
56
62
  end
57
63
 
58
64
  def [](k)
59
- @flash[k]
65
+ @flash[k.to_s]
60
66
  end
61
67
 
62
68
  # Convenience accessor for <tt>flash.now[:alert]=</tt>.
@@ -73,7 +79,7 @@ module ActionDispatch
73
79
  class FlashHash
74
80
  include Enumerable
75
81
 
76
- def self.from_session_value(value)
82
+ def self.from_session_value(value) #:nodoc:
77
83
  flash = case value
78
84
  when FlashHash # Rails 3.1, 3.2
79
85
  new(value.instance_variable_get(:@flashes), value.instance_variable_get(:@used))
@@ -85,15 +91,18 @@ module ActionDispatch
85
91
 
86
92
  flash.tap(&:sweep)
87
93
  end
88
-
89
- def to_session_value
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.
98
+ def to_session_value #:nodoc:
90
99
  return nil if empty?
91
100
  {'discard' => @discard.to_a, 'flashes' => @flashes}
92
101
  end
93
102
 
94
103
  def initialize(flashes = {}, discard = []) #:nodoc:
95
- @discard = Set.new(discard)
96
- @flashes = flashes
104
+ @discard = Set.new(stringify_array(discard))
105
+ @flashes = flashes.stringify_keys
97
106
  @now = nil
98
107
  end
99
108
 
@@ -106,17 +115,18 @@ module ActionDispatch
106
115
  end
107
116
 
108
117
  def []=(k, v)
118
+ k = k.to_s
109
119
  @discard.delete k
110
120
  @flashes[k] = v
111
121
  end
112
122
 
113
123
  def [](k)
114
- @flashes[k]
124
+ @flashes[k.to_s]
115
125
  end
116
126
 
117
127
  def update(h) #:nodoc:
118
- @discard.subtract h.keys
119
- @flashes.update h
128
+ @discard.subtract stringify_array(h.keys)
129
+ @flashes.update h.stringify_keys
120
130
  self
121
131
  end
122
132
 
@@ -125,10 +135,11 @@ module ActionDispatch
125
135
  end
126
136
 
127
137
  def key?(name)
128
- @flashes.key? name
138
+ @flashes.key? name.to_s
129
139
  end
130
140
 
131
141
  def delete(key)
142
+ key = key.to_s
132
143
  @discard.delete key
133
144
  @flashes.delete key
134
145
  self
@@ -155,7 +166,7 @@ module ActionDispatch
155
166
 
156
167
  def replace(h) #:nodoc:
157
168
  @discard.clear
158
- @flashes.replace h
169
+ @flashes.replace h.stringify_keys
159
170
  self
160
171
  end
161
172
 
@@ -186,6 +197,7 @@ module ActionDispatch
186
197
  # flash.keep # keeps the entire flash
187
198
  # flash.keep(:notice) # keeps only the "notice" entry, the rest of the flash is discarded
188
199
  def keep(k = nil)
200
+ k = k.to_s if k
189
201
  @discard.subtract Array(k || keys)
190
202
  k ? self[k] : self
191
203
  end
@@ -195,6 +207,7 @@ module ActionDispatch
195
207
  # flash.discard # discard the entire flash at the end of the current action
196
208
  # flash.discard(:warning) # discard only the "warning" entry at the end of the current action
197
209
  def discard(k = nil)
210
+ k = k.to_s if k
198
211
  @discard.merge Array(k || keys)
199
212
  k ? self[k] : self
200
213
  end
@@ -231,6 +244,12 @@ module ActionDispatch
231
244
  def now_is_loaded?
232
245
  @now
233
246
  end
247
+
248
+ def stringify_array(array)
249
+ array.map do |item|
250
+ item.kind_of?(Symbol) ? item.to_s : item
251
+ end
252
+ end
234
253
  end
235
254
 
236
255
  def initialize(app)
@@ -243,19 +262,13 @@ module ActionDispatch
243
262
  session = Request::Session.find(env) || {}
244
263
  flash_hash = env[KEY]
245
264
 
246
- if flash_hash
247
- if !flash_hash.empty? || session.key?('flash')
248
- session["flash"] = flash_hash.to_session_value
249
- new_hash = flash_hash.dup
250
- else
251
- new_hash = flash_hash
252
- end
253
-
254
- env[KEY] = new_hash
265
+ if flash_hash && (flash_hash.present? || session.key?('flash'))
266
+ session["flash"] = flash_hash.to_session_value
267
+ env[KEY] = flash_hash.dup
255
268
  end
256
269
 
257
270
  if (!session.respond_to?(:loaded?) || session.loaded?) && # (reset_session uses {}, which doesn't implement #loaded?)
258
- session.key?('flash') && session['flash'].nil?
271
+ session.key?('flash') && session['flash'].nil?
259
272
  session.delete('flash')
260
273
  end
261
274
  end
@@ -43,11 +43,11 @@ module ActionDispatch
43
43
  when :json
44
44
  data = ActiveSupport::JSON.decode(request.raw_post)
45
45
  data = {:_json => data} unless data.is_a?(Hash)
46
- request.deep_munge(data).with_indifferent_access
46
+ Request::Utils.deep_munge(data).with_indifferent_access
47
47
  else
48
48
  false
49
49
  end
50
- rescue Exception => e # JSON or Ruby code block errors
50
+ rescue => e # JSON or Ruby code block errors
51
51
  logger(env).debug "Error occurred while parsing request parameters.\nContents:\n\n#{request.raw_post}"
52
52
 
53
53
  raise ParseError.new(e.message, e)
@@ -1,4 +1,14 @@
1
1
  module ActionDispatch
2
+ # 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`
4
+ # directory. For example when this middleware receives a 500 response it will
5
+ # render the template found in `/public/500.html`.
6
+ # 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
+ #
10
+ # When a request with a content type other than HTML is made, this middleware
11
+ # will attempt to convert error information into the appropriate response type.
2
12
  class PublicExceptions
3
13
  attr_accessor :public_path
4
14
 
@@ -32,9 +42,8 @@ module ActionDispatch
32
42
  end
33
43
 
34
44
  def render_html(status)
35
- found = false
36
- path = "#{public_path}/#{status}.#{I18n.locale}.html" if I18n.locale
37
- path = "#{public_path}/#{status}.html" unless path && (found = File.exist?(path))
45
+ path = "#{public_path}/#{status}.#{I18n.locale}.html"
46
+ path = "#{public_path}/#{status}.html" unless (found = File.exist?(path))
38
47
 
39
48
  if found || File.exist?(path)
40
49
  render_format(status, 'text/html', File.read(path))
@@ -1,3 +1,5 @@
1
+ require 'active_support/deprecation/reporting'
2
+
1
3
  module ActionDispatch
2
4
  # ActionDispatch::Reloader provides prepare and cleanup callbacks,
3
5
  # intended to assist with code reloading during development.
@@ -25,19 +27,26 @@ module ActionDispatch
25
27
  #
26
28
  class Reloader
27
29
  include ActiveSupport::Callbacks
30
+ include ActiveSupport::Deprecation::Reporting
28
31
 
29
- define_callbacks :prepare, :scope => :name
30
- define_callbacks :cleanup, :scope => :name
32
+ define_callbacks :prepare
33
+ define_callbacks :cleanup
31
34
 
32
35
  # Add a prepare callback. Prepare callbacks are run before each request, prior
33
36
  # to ActionDispatch::Callback's before callbacks.
34
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
35
41
  set_callback(:prepare, *args, &block)
36
42
  end
37
43
 
38
44
  # Add a cleanup callback. Cleanup callbacks are run after each request is
39
45
  # complete (after #close is called on the response body).
40
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
41
50
  set_callback(:cleanup, *args, &block)
42
51
  end
43
52
 
@@ -1,3 +1,5 @@
1
+ require 'ipaddr'
2
+
1
3
  module ActionDispatch
2
4
  # This middleware calculates the IP address of the remote client that is
3
5
  # making the request. It does this by checking various headers that could
@@ -11,7 +13,7 @@ module ActionDispatch
11
13
  # Some Rack servers concatenate repeated headers, like {HTTP RFC 2616}[http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2]
12
14
  # requires. Some Rack servers simply drop preceding headers, and only report
13
15
  # the value that was {given in the last header}[http://andre.arko.net/2011/12/26/repeated-headers-and-ruby-web-servers].
14
- # If you are behind multiple proxy servers (like Nginx to HAProxy to Unicorn)
16
+ # If you are behind multiple proxy servers (like NGINX to HAProxy to Unicorn)
15
17
  # then you should test your Rack server to make sure your data is good.
16
18
  #
17
19
  # IF YOU DON'T USE A PROXY, THIS MAKES YOU VULNERABLE TO IP SPOOFING.
@@ -28,14 +30,14 @@ module ActionDispatch
28
30
  # guaranteed by the IP specification to be private addresses. Those will
29
31
  # not be the ultimate client IP in production, and so are discarded. See
30
32
  # http://en.wikipedia.org/wiki/Private_network for details.
31
- TRUSTED_PROXIES = %r{
32
- ^127\.0\.0\.1$ | # localhost IPv4
33
- ^::1$ | # localhost IPv6
34
- ^fc00: | # private IPv6 range fc00
35
- ^10\. | # private IPv4 range 10.x.x.x
36
- ^172\.(1[6-9]|2[0-9]|3[0-1])\.| # private IPv4 range 172.16.0.0 .. 172.31.255.255
37
- ^192\.168\. # private IPv4 range 192.168.x.x
38
- }x
33
+ TRUSTED_PROXIES = [
34
+ "127.0.0.1", # localhost IPv4
35
+ "::1", # localhost IPv6
36
+ "fc00::/7", # private IPv6 range fc00::/7
37
+ "10.0.0.0/8", # private IPv4 range 10.x.x.x
38
+ "172.16.0.0/12", # private IPv4 range 172.16.0.0 .. 172.31.255.255
39
+ "192.168.0.0/16", # private IPv4 range 192.168.x.x
40
+ ].map { |proxy| IPAddr.new(proxy) }
39
41
 
40
42
  attr_reader :check_ip, :proxies
41
43
 
@@ -47,24 +49,24 @@ module ActionDispatch
47
49
  # clients (like WAP devices), or behind proxies that set headers in an
48
50
  # incorrect or confusing way (like AWS ELB).
49
51
  #
50
- # The +custom_trusted+ argument can take a regex, which will be used
51
- # instead of +TRUSTED_PROXIES+, or a string, which will be used in addition
52
- # to +TRUSTED_PROXIES+. Any proxy setup will put the value you want in the
53
- # middle (or at the beginning) of the X-Forwarded-For list, with your proxy
54
- # servers after it. If your proxies aren't removed, pass them in via the
55
- # +custom_trusted+ parameter. That way, the middleware will ignore those
56
- # IP addresses, and return the one that you want.
52
+ # The +custom_proxies+ argument can take an Array of string, IPAddr, or
53
+ # Regexp objects which will be used instead of +TRUSTED_PROXIES+. If a
54
+ # single string, IPAddr, or Regexp object is provided, it will be used in
55
+ # addition to +TRUSTED_PROXIES+. Any proxy setup will put the value you
56
+ # want in the middle (or at the beginning) of the X-Forwarded-For list,
57
+ # with your proxy servers after it. If your proxies aren't removed, pass
58
+ # them in via the +custom_proxies+ parameter. That way, the middleware will
59
+ # ignore those IP addresses, and return the one that you want.
57
60
  def initialize(app, check_ip_spoofing = true, custom_proxies = nil)
58
61
  @app = app
59
62
  @check_ip = check_ip_spoofing
60
- @proxies = case custom_proxies
61
- when Regexp
62
- custom_proxies
63
- when nil
64
- TRUSTED_PROXIES
65
- else
66
- Regexp.union(TRUSTED_PROXIES, custom_proxies)
67
- end
63
+ @proxies = if custom_proxies.blank?
64
+ TRUSTED_PROXIES
65
+ elsif custom_proxies.respond_to?(:any?)
66
+ custom_proxies
67
+ else
68
+ Array(custom_proxies) + TRUSTED_PROXIES
69
+ end
68
70
  end
69
71
 
70
72
  # Since the IP address may not be needed, we store the object here
@@ -80,32 +82,6 @@ module ActionDispatch
80
82
  # into an actual IP address. If the ActionDispatch::Request#remote_ip method
81
83
  # is called, this class will calculate the value and then memoize it.
82
84
  class GetIp
83
-
84
- # This constant contains a regular expression that validates every known
85
- # form of IP v4 and v6 address, with or without abbreviations, adapted
86
- # from {this gist}[https://gist.github.com/gazay/1289635].
87
- VALID_IP = %r{
88
- (^(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[0-9]{1,2})(\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[0-9]{1,2})){3}$) | # ip v4
89
- (^(
90
- (([0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4}) | # ip v6 not abbreviated
91
- (([0-9A-Fa-f]{1,4}:){6}:[0-9A-Fa-f]{1,4}) | # ip v6 with double colon in the end
92
- (([0-9A-Fa-f]{1,4}:){5}:([0-9A-Fa-f]{1,4}:)?[0-9A-Fa-f]{1,4}) | # - ip addresses v6
93
- (([0-9A-Fa-f]{1,4}:){4}:([0-9A-Fa-f]{1,4}:){0,2}[0-9A-Fa-f]{1,4}) | # - with
94
- (([0-9A-Fa-f]{1,4}:){3}:([0-9A-Fa-f]{1,4}:){0,3}[0-9A-Fa-f]{1,4}) | # - double colon
95
- (([0-9A-Fa-f]{1,4}:){2}:([0-9A-Fa-f]{1,4}:){0,4}[0-9A-Fa-f]{1,4}) | # - in the middle
96
- (([0-9A-Fa-f]{1,4}:){6} ((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3} (\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)) | # ip v6 with compatible to v4
97
- (([0-9A-Fa-f]{1,4}:){1,5}:((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)) | # ip v6 with compatible to v4
98
- (([0-9A-Fa-f]{1,4}:){1}:([0-9A-Fa-f]{1,4}:){0,4}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)) | # ip v6 with compatible to v4
99
- (([0-9A-Fa-f]{1,4}:){0,2}:([0-9A-Fa-f]{1,4}:){0,3}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)) | # ip v6 with compatible to v4
100
- (([0-9A-Fa-f]{1,4}:){0,3}:([0-9A-Fa-f]{1,4}:){0,2}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)) | # ip v6 with compatible to v4
101
- (([0-9A-Fa-f]{1,4}:){0,4}:([0-9A-Fa-f]{1,4}:){1}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)) | # ip v6 with compatible to v4
102
- (::([0-9A-Fa-f]{1,4}:){0,5}((\b((25[0-5])|(1\d{2})|(2[0-4]\d) |(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)) | # ip v6 with compatible to v4
103
- ([0-9A-Fa-f]{1,4}::([0-9A-Fa-f]{1,4}:){0,5}[0-9A-Fa-f]{1,4}) | # ip v6 with compatible to v4
104
- (::([0-9A-Fa-f]{1,4}:){0,6}[0-9A-Fa-f]{1,4}) | # ip v6 with double colon at the beginning
105
- (([0-9A-Fa-f]{1,4}:){1,7}:) # ip v6 without ending
106
- )$)
107
- }x
108
-
109
85
  def initialize(env, middleware)
110
86
  @env = env
111
87
  @check_ip = middleware.check_ip
@@ -118,7 +94,7 @@ module ActionDispatch
118
94
  #
119
95
  # REMOTE_ADDR will be correct if the request is made directly against the
120
96
  # Ruby process, on e.g. Heroku. When the request is proxied by another
121
- # server like HAProxy or Nginx, the IP address that made the original
97
+ # server like HAProxy or NGINX, the IP address that made the original
122
98
  # request will be put in an X-Forwarded-For header. If there are multiple
123
99
  # proxies, that header may contain a list of IPs. Other proxy services
124
100
  # set the Client-Ip header instead, so we check that too.
@@ -173,12 +149,22 @@ module ActionDispatch
173
149
  def ips_from(header)
174
150
  # Split the comma-separated list into an array of strings
175
151
  ips = @env[header] ? @env[header].strip.split(/[,\s]+/) : []
176
- # Only return IPs that are valid according to the regex
177
- ips.select{ |ip| ip =~ VALID_IP }
152
+ ips.select do |ip|
153
+ begin
154
+ # Only return IPs that are valid according to the IPAddr#new method
155
+ range = IPAddr.new(ip).to_range
156
+ # we want to make sure nobody is sneaking a netmask in
157
+ range.begin == range.end
158
+ rescue ArgumentError
159
+ nil
160
+ end
161
+ end
178
162
  end
179
163
 
180
164
  def filter_proxies(ips)
181
- ips.reject { |ip| ip =~ @proxies }
165
+ ips.reject do |ip|
166
+ @proxies.any? { |proxy| proxy === ip }
167
+ end
182
168
  end
183
169
 
184
170
  end
@@ -5,7 +5,7 @@ module ActionDispatch
5
5
  # Makes a unique request id available to the action_dispatch.request_id env variable (which is then accessible through
6
6
  # ActionDispatch::Request#uuid) and sends the same id to the client via the X-Request-Id header.
7
7
  #
8
- # The unique request id is either based off the X-Request-Id header in the request, which would typically be generated
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
10
10
  # header is accepted from the outside world, we sanitize it to a max of 255 chars and alphanumeric and dashes only.
11
11
  #
@@ -16,9 +16,9 @@ module ActionDispatch
16
16
 
17
17
  # Get a session from the cache.
18
18
  def get_session(env, sid)
19
- sid ||= generate_sid
20
- session = @cache.read(cache_key(sid))
21
- session ||= {}
19
+ unless sid and session = @cache.read(cache_key(sid))
20
+ sid, session = generate_sid, {}
21
+ end
22
22
  [sid, session]
23
23
  end
24
24
 
@@ -15,8 +15,8 @@ module ActionDispatch
15
15
  # best possible option given your application's configuration.
16
16
  #
17
17
  # If you only have secret_token set, your cookies will be signed, but
18
- # not encrypted. This means a user cannot alter his +user_id+ without
19
- # knowing your app's secret key, but can easily read his +user_id+. This
18
+ # not encrypted. This means a user cannot alter their +user_id+ without
19
+ # knowing your app's secret key, but can easily read their +user_id+. This
20
20
  # was the default for Rails 3 apps.
21
21
  #
22
22
  # If you have secret_key_base set, your cookies will be encrypted. This
@@ -29,11 +29,12 @@ module ActionDispatch
29
29
  #
30
30
  # Configure your session store in config/initializers/session_store.rb:
31
31
  #
32
- # Myapp::Application.config.session_store :cookie_store, key: '_your_app_session'
32
+ # Rails.application.config.session_store :cookie_store, key: '_your_app_session'
33
33
  #
34
- # Configure your secret key in config/initializers/secret_token.rb:
34
+ # Configure your secret key in config/secrets.yml:
35
35
  #
36
- # Myapp::Application.config.secret_key_base 'secret key'
36
+ # development:
37
+ # secret_key_base: 'secret key'
37
38
  #
38
39
  # To generate a secret key for an existing application, run `rake secret`.
39
40
  #
@@ -48,9 +49,9 @@ module ActionDispatch
48
49
  # reasonably sure that your upgrade is otherwise complete. Additionally,
49
50
  # you should take care to make sure you are not relying on the ability to
50
51
  # decode signed cookies generated by your app in external applications or
51
- # Javascript before upgrading.
52
+ # JavaScript before upgrading.
52
53
  #
53
- # Note that changing digest or secret invalidates all existing sessions!
54
+ # Note that changing the secret key will invalidate all existing sessions!
54
55
  class CookieStore < Rack::Session::Abstract::ID
55
56
  include Compatibility
56
57
  include StaleSessionCheck
@@ -29,8 +29,11 @@ module ActionDispatch
29
29
  def call(env)
30
30
  @app.call(env)
31
31
  rescue Exception => exception
32
- raise exception if env['action_dispatch.show_exceptions'] == false
33
- render_exception(env, exception)
32
+ if env['action_dispatch.show_exceptions'] == false
33
+ raise exception
34
+ else
35
+ render_exception(env, exception)
36
+ end
34
37
  end
35
38
 
36
39
  private
@@ -39,6 +42,7 @@ module ActionDispatch
39
42
  wrapper = ExceptionWrapper.new(env, exception)
40
43
  status = wrapper.status_code
41
44
  env["action_dispatch.exception"] = wrapper.exception
45
+ env["action_dispatch.original_path"] = env["PATH_INFO"]
42
46
  env["PATH_INFO"] = "/#{status}"
43
47
  response = @exceptions_app.call(env)
44
48
  response[1]['X-Cascade'] == 'pass' ? pass_response(status) : response
@@ -22,7 +22,7 @@ module ActionDispatch
22
22
 
23
23
  if request.ssl?
24
24
  status, headers, body = @app.call(env)
25
- headers = hsts_headers.merge(headers)
25
+ headers.reverse_merge!(hsts_headers)
26
26
  flag_cookies_as_secure!(headers)
27
27
  [status, headers, body]
28
28
  else
@@ -32,11 +32,14 @@ module ActionDispatch
32
32
 
33
33
  private
34
34
  def redirect_to_https(request)
35
- url = URI(request.url)
36
- url.scheme = "https"
37
- url.host = @host if @host
38
- url.port = @port if @port
39
- headers = { 'Content-Type' => 'text/html', 'Location' => url.to_s }
35
+ host = @host || request.host
36
+ port = @port || request.port
37
+
38
+ location = "https://#{host}"
39
+ location << ":#{port}" if port != 80
40
+ location << request.fullpath
41
+
42
+ headers = { 'Content-Type' => 'text/html', 'Location' => location }
40
43
 
41
44
  [301, headers, []]
42
45
  end
@@ -57,7 +60,7 @@ module ActionDispatch
57
60
  cookies = cookies.split("\n")
58
61
 
59
62
  headers['Set-Cookie'] = cookies.map { |cookie|
60
- if cookie !~ /;\s+secure(;|$)/i
63
+ if cookie !~ /;\s*secure\s*(;|$)/i
61
64
  "#{cookie}; secure"
62
65
  else
63
66
  cookie
@@ -2,49 +2,105 @@ require 'rack/utils'
2
2
  require 'active_support/core_ext/uri'
3
3
 
4
4
  module ActionDispatch
5
+ # This middleware returns a file's contents from disk in the body response.
6
+ # When initialized it can accept an optional 'Cache-Control' header which
7
+ # will be set when a response containing a file's contents is delivered.
8
+ #
9
+ # This middleware will render the file specified in `env["PATH_INFO"]`
10
+ # where the base path is in the +root+ directory. For example if the +root+
11
+ # is set to `public/` then a request with `env["PATH_INFO"]` of
12
+ # `assets/application.js` will return a response with contents of a file
13
+ # located at `public/assets/application.js` if the file exists. If the file
14
+ # does not exist a 404 "File not Found" response will be returned.
5
15
  class FileHandler
6
16
  def initialize(root, cache_control)
7
17
  @root = root.chomp('/')
8
18
  @compiled_root = /^#{Regexp.escape(root)}/
9
- headers = cache_control && { 'Cache-Control' => cache_control }
10
- @file_server = ::Rack::File.new(@root, headers)
19
+ headers = cache_control && { 'Cache-Control' => cache_control }
20
+ @file_server = ::Rack::File.new(@root, headers)
11
21
  end
12
22
 
13
23
  def match?(path)
14
- path = path.dup
24
+ path = URI.parser.unescape(path)
25
+ return false unless valid_path?(path)
15
26
 
16
- full_path = path.empty? ? @root : File.join(@root, escape_glob_chars(unescape_path(path)))
17
- paths = "#{full_path}#{ext}"
27
+ paths = [path, "#{path}#{ext}", "#{path}/index#{ext}"].map { |v|
28
+ Rack::Utils.clean_path_info v
29
+ }
18
30
 
19
- matches = Dir[paths]
20
- match = matches.detect { |m| File.file?(m) }
21
- if match
22
- match.sub!(@compiled_root, '')
23
- ::Rack::Utils.escape(match)
31
+ if match = paths.detect { |p|
32
+ path = File.join(@root, p.force_encoding('UTF-8'))
33
+ begin
34
+ File.file?(path) && File.readable?(path)
35
+ rescue SystemCallError
36
+ false
37
+ end
38
+
39
+ }
40
+ return ::Rack::Utils.escape(match)
24
41
  end
25
42
  end
26
43
 
27
44
  def call(env)
28
- @file_server.call(env)
29
- end
45
+ path = env['PATH_INFO']
46
+ gzip_path = gzip_file_path(path)
30
47
 
31
- def ext
32
- @ext ||= begin
33
- ext = ::ActionController::Base.default_static_extension
34
- "{,#{ext},/index#{ext}}"
48
+ if gzip_path && gzip_encoding_accepted?(env)
49
+ env['PATH_INFO'] = gzip_path
50
+ status, headers, body = @file_server.call(env)
51
+ if status == 304
52
+ return [status, headers, body]
53
+ end
54
+ headers['Content-Encoding'] = 'gzip'
55
+ headers['Content-Type'] = content_type(path)
56
+ else
57
+ status, headers, body = @file_server.call(env)
35
58
  end
36
- end
37
59
 
38
- def unescape_path(path)
39
- URI.parser.unescape(path)
40
- end
60
+ headers['Vary'] = 'Accept-Encoding' if gzip_path
41
61
 
42
- def escape_glob_chars(path)
43
- path.force_encoding('binary') if path.respond_to? :force_encoding
44
- path.gsub(/[*?{}\[\]]/, "\\\\\\&")
62
+ return [status, headers, body]
63
+ ensure
64
+ env['PATH_INFO'] = path
45
65
  end
66
+
67
+ private
68
+ def ext
69
+ ::ActionController::Base.default_static_extension
70
+ end
71
+
72
+ def content_type(path)
73
+ ::Rack::Mime.mime_type(::File.extname(path), 'text/plain')
74
+ end
75
+
76
+ def gzip_encoding_accepted?(env)
77
+ env['HTTP_ACCEPT_ENCODING'] =~ /\bgzip\b/i
78
+ end
79
+
80
+ def gzip_file_path(path)
81
+ can_gzip_mime = content_type(path) =~ /\A(?:text\/|application\/javascript)/
82
+ gzip_path = "#{path}.gz"
83
+ if can_gzip_mime && File.exist?(File.join(@root, ::Rack::Utils.unescape(gzip_path)))
84
+ gzip_path
85
+ else
86
+ false
87
+ end
88
+ end
89
+
90
+ def valid_path?(path)
91
+ path.valid_encoding? && !path.include?("\0")
92
+ end
46
93
  end
47
94
 
95
+ # This middleware will attempt to return the contents of a file's body from
96
+ # disk in the response. If a file is not found on disk, the request will be
97
+ # delegated to the application stack. This middleware is commonly initialized
98
+ # to serve assets from a server's `public/` directory.
99
+ #
100
+ # This middleware verifies the path to ensure that only files
101
+ # living in the root directory can be rendered. A request cannot
102
+ # produce a directory traversal using this middleware. Only 'GET' and 'HEAD'
103
+ # requests will result in a file being returned.
48
104
  class Static
49
105
  def initialize(app, path, cache_control=nil)
50
106
  @app = app