actionpack 3.2.22.5 → 5.2.4

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 (271) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +279 -603
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +13 -297
  5. data/lib/abstract_controller/asset_paths.rb +4 -2
  6. data/lib/abstract_controller/base.rb +82 -52
  7. data/lib/abstract_controller/caching/fragments.rb +166 -0
  8. data/lib/abstract_controller/caching.rb +66 -0
  9. data/lib/abstract_controller/callbacks.rb +117 -103
  10. data/lib/abstract_controller/collector.rb +18 -7
  11. data/lib/abstract_controller/error.rb +6 -0
  12. data/lib/abstract_controller/helpers.rb +65 -38
  13. data/lib/abstract_controller/logger.rb +3 -2
  14. data/lib/abstract_controller/railties/routes_helpers.rb +5 -3
  15. data/lib/abstract_controller/rendering.rb +77 -129
  16. data/lib/abstract_controller/translation.rb +21 -3
  17. data/lib/abstract_controller/url_for.rb +9 -7
  18. data/lib/abstract_controller.rb +12 -13
  19. data/lib/action_controller/api/api_rendering.rb +16 -0
  20. data/lib/action_controller/api.rb +149 -0
  21. data/lib/action_controller/base.rb +81 -40
  22. data/lib/action_controller/caching.rb +22 -62
  23. data/lib/action_controller/form_builder.rb +50 -0
  24. data/lib/action_controller/log_subscriber.rb +30 -18
  25. data/lib/action_controller/metal/basic_implicit_render.rb +13 -0
  26. data/lib/action_controller/metal/conditional_get.rb +190 -47
  27. data/lib/action_controller/metal/content_security_policy.rb +52 -0
  28. data/lib/action_controller/metal/cookies.rb +3 -3
  29. data/lib/action_controller/metal/data_streaming.rb +40 -65
  30. data/lib/action_controller/metal/etag_with_flash.rb +18 -0
  31. data/lib/action_controller/metal/etag_with_template_digest.rb +57 -0
  32. data/lib/action_controller/metal/exceptions.rb +19 -12
  33. data/lib/action_controller/metal/flash.rb +42 -9
  34. data/lib/action_controller/metal/force_ssl.rb +79 -19
  35. data/lib/action_controller/metal/head.rb +35 -10
  36. data/lib/action_controller/metal/helpers.rb +31 -21
  37. data/lib/action_controller/metal/http_authentication.rb +182 -134
  38. data/lib/action_controller/metal/implicit_render.rb +62 -8
  39. data/lib/action_controller/metal/instrumentation.rb +28 -26
  40. data/lib/action_controller/metal/live.rb +312 -0
  41. data/lib/action_controller/metal/mime_responds.rb +159 -163
  42. data/lib/action_controller/metal/parameter_encoding.rb +51 -0
  43. data/lib/action_controller/metal/params_wrapper.rb +146 -93
  44. data/lib/action_controller/metal/redirecting.rb +80 -56
  45. data/lib/action_controller/metal/renderers.rb +119 -47
  46. data/lib/action_controller/metal/rendering.rb +89 -32
  47. data/lib/action_controller/metal/request_forgery_protection.rb +373 -41
  48. data/lib/action_controller/metal/rescue.rb +9 -16
  49. data/lib/action_controller/metal/streaming.rb +39 -45
  50. data/lib/action_controller/metal/strong_parameters.rb +1086 -0
  51. data/lib/action_controller/metal/testing.rb +8 -29
  52. data/lib/action_controller/metal/url_for.rb +43 -32
  53. data/lib/action_controller/metal.rb +112 -106
  54. data/lib/action_controller/railtie.rb +56 -18
  55. data/lib/action_controller/railties/helpers.rb +24 -0
  56. data/lib/action_controller/renderer.rb +117 -0
  57. data/lib/action_controller/template_assertions.rb +11 -0
  58. data/lib/action_controller/test_case.rb +402 -347
  59. data/lib/action_controller.rb +31 -30
  60. data/lib/action_dispatch/http/cache.rb +133 -34
  61. data/lib/action_dispatch/http/content_security_policy.rb +272 -0
  62. data/lib/action_dispatch/http/filter_parameters.rb +40 -24
  63. data/lib/action_dispatch/http/filter_redirect.rb +37 -0
  64. data/lib/action_dispatch/http/headers.rb +117 -16
  65. data/lib/action_dispatch/http/mime_negotiation.rb +98 -33
  66. data/lib/action_dispatch/http/mime_type.rb +198 -146
  67. data/lib/action_dispatch/http/mime_types.rb +22 -7
  68. data/lib/action_dispatch/http/parameter_filter.rb +61 -49
  69. data/lib/action_dispatch/http/parameters.rb +94 -51
  70. data/lib/action_dispatch/http/rack_cache.rb +4 -3
  71. data/lib/action_dispatch/http/request.rb +262 -117
  72. data/lib/action_dispatch/http/response.rb +400 -86
  73. data/lib/action_dispatch/http/upload.rb +66 -29
  74. data/lib/action_dispatch/http/url.rb +232 -60
  75. data/lib/action_dispatch/journey/formatter.rb +189 -0
  76. data/lib/action_dispatch/journey/gtg/builder.rb +164 -0
  77. data/lib/action_dispatch/journey/gtg/simulator.rb +41 -0
  78. data/lib/action_dispatch/journey/gtg/transition_table.rb +158 -0
  79. data/lib/action_dispatch/journey/nfa/builder.rb +78 -0
  80. data/lib/action_dispatch/journey/nfa/dot.rb +36 -0
  81. data/lib/action_dispatch/journey/nfa/simulator.rb +49 -0
  82. data/lib/action_dispatch/journey/nfa/transition_table.rb +120 -0
  83. data/lib/action_dispatch/journey/nodes/node.rb +140 -0
  84. data/lib/action_dispatch/journey/parser.rb +199 -0
  85. data/lib/action_dispatch/journey/parser.y +50 -0
  86. data/lib/action_dispatch/journey/parser_extras.rb +31 -0
  87. data/lib/action_dispatch/journey/path/pattern.rb +199 -0
  88. data/lib/action_dispatch/journey/route.rb +203 -0
  89. data/lib/action_dispatch/journey/router/utils.rb +102 -0
  90. data/lib/action_dispatch/journey/router.rb +156 -0
  91. data/lib/action_dispatch/journey/routes.rb +82 -0
  92. data/lib/action_dispatch/journey/scanner.rb +64 -0
  93. data/lib/action_dispatch/journey/visitors.rb +268 -0
  94. data/lib/action_dispatch/journey/visualizer/fsm.css +30 -0
  95. data/lib/action_dispatch/journey/visualizer/fsm.js +134 -0
  96. data/lib/action_dispatch/journey/visualizer/index.html.erb +52 -0
  97. data/lib/action_dispatch/journey.rb +7 -0
  98. data/lib/action_dispatch/middleware/callbacks.rb +17 -13
  99. data/lib/action_dispatch/middleware/cookies.rb +494 -162
  100. data/lib/action_dispatch/middleware/debug_exceptions.rb +176 -53
  101. data/lib/action_dispatch/middleware/debug_locks.rb +124 -0
  102. data/lib/action_dispatch/middleware/exception_wrapper.rb +103 -38
  103. data/lib/action_dispatch/middleware/executor.rb +21 -0
  104. data/lib/action_dispatch/middleware/flash.rb +128 -91
  105. data/lib/action_dispatch/middleware/public_exceptions.rb +43 -16
  106. data/lib/action_dispatch/middleware/reloader.rb +6 -83
  107. data/lib/action_dispatch/middleware/remote_ip.rb +151 -49
  108. data/lib/action_dispatch/middleware/request_id.rb +19 -15
  109. data/lib/action_dispatch/middleware/session/abstract_store.rb +38 -34
  110. data/lib/action_dispatch/middleware/session/cache_store.rb +14 -9
  111. data/lib/action_dispatch/middleware/session/cookie_store.rb +94 -44
  112. data/lib/action_dispatch/middleware/session/mem_cache_store.rb +15 -4
  113. data/lib/action_dispatch/middleware/show_exceptions.rb +36 -61
  114. data/lib/action_dispatch/middleware/ssl.rb +150 -0
  115. data/lib/action_dispatch/middleware/stack.rb +33 -41
  116. data/lib/action_dispatch/middleware/static.rb +92 -48
  117. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +22 -0
  118. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.text.erb +23 -0
  119. data/lib/action_dispatch/middleware/templates/rescues/_source.html.erb +27 -0
  120. data/lib/action_dispatch/middleware/templates/rescues/_source.text.erb +8 -0
  121. data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +52 -0
  122. data/lib/action_dispatch/middleware/templates/rescues/_trace.text.erb +9 -0
  123. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +16 -0
  124. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +9 -0
  125. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +21 -0
  126. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.text.erb +13 -0
  127. data/lib/action_dispatch/middleware/templates/rescues/layout.erb +134 -5
  128. data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +11 -0
  129. data/lib/action_dispatch/middleware/templates/rescues/missing_template.text.erb +3 -0
  130. data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +32 -0
  131. data/lib/action_dispatch/middleware/templates/rescues/routing_error.text.erb +11 -0
  132. data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +20 -0
  133. data/lib/action_dispatch/middleware/templates/rescues/template_error.text.erb +7 -0
  134. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +6 -0
  135. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.text.erb +3 -0
  136. data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +16 -0
  137. data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +200 -0
  138. data/lib/action_dispatch/railtie.rb +29 -8
  139. data/lib/action_dispatch/request/session.rb +234 -0
  140. data/lib/action_dispatch/request/utils.rb +78 -0
  141. data/lib/action_dispatch/routing/endpoint.rb +17 -0
  142. data/lib/action_dispatch/routing/inspector.rb +225 -0
  143. data/lib/action_dispatch/routing/mapper.rb +1329 -582
  144. data/lib/action_dispatch/routing/polymorphic_routes.rb +237 -94
  145. data/lib/action_dispatch/routing/redirection.rb +120 -50
  146. data/lib/action_dispatch/routing/route_set.rb +545 -322
  147. data/lib/action_dispatch/routing/routes_proxy.rb +37 -7
  148. data/lib/action_dispatch/routing/url_for.rb +103 -34
  149. data/lib/action_dispatch/routing.rb +66 -99
  150. data/lib/action_dispatch/system_test_case.rb +147 -0
  151. data/lib/action_dispatch/system_testing/browser.rb +49 -0
  152. data/lib/action_dispatch/system_testing/driver.rb +59 -0
  153. data/lib/action_dispatch/system_testing/server.rb +31 -0
  154. data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +96 -0
  155. data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +31 -0
  156. data/lib/action_dispatch/system_testing/test_helpers/undef_methods.rb +26 -0
  157. data/lib/action_dispatch/testing/assertion_response.rb +47 -0
  158. data/lib/action_dispatch/testing/assertions/response.rb +53 -42
  159. data/lib/action_dispatch/testing/assertions/routing.rb +79 -74
  160. data/lib/action_dispatch/testing/assertions.rb +15 -9
  161. data/lib/action_dispatch/testing/integration.rb +361 -207
  162. data/lib/action_dispatch/testing/request_encoder.rb +55 -0
  163. data/lib/action_dispatch/testing/test_process.rb +28 -19
  164. data/lib/action_dispatch/testing/test_request.rb +30 -33
  165. data/lib/action_dispatch/testing/test_response.rb +35 -11
  166. data/lib/action_dispatch.rb +42 -32
  167. data/lib/action_pack/gem_version.rb +17 -0
  168. data/lib/action_pack/version.rb +7 -7
  169. data/lib/action_pack.rb +4 -2
  170. metadata +116 -175
  171. data/lib/abstract_controller/layouts.rb +0 -423
  172. data/lib/abstract_controller/view_paths.rb +0 -96
  173. data/lib/action_controller/caching/actions.rb +0 -185
  174. data/lib/action_controller/caching/fragments.rb +0 -127
  175. data/lib/action_controller/caching/pages.rb +0 -187
  176. data/lib/action_controller/caching/sweeping.rb +0 -97
  177. data/lib/action_controller/deprecated/integration_test.rb +0 -2
  178. data/lib/action_controller/deprecated/performance_test.rb +0 -1
  179. data/lib/action_controller/deprecated.rb +0 -3
  180. data/lib/action_controller/metal/compatibility.rb +0 -65
  181. data/lib/action_controller/metal/hide_actions.rb +0 -41
  182. data/lib/action_controller/metal/rack_delegation.rb +0 -26
  183. data/lib/action_controller/metal/responder.rb +0 -286
  184. data/lib/action_controller/metal/session_management.rb +0 -14
  185. data/lib/action_controller/middleware.rb +0 -39
  186. data/lib/action_controller/railties/paths.rb +0 -25
  187. data/lib/action_controller/record_identifier.rb +0 -85
  188. data/lib/action_controller/vendor/html-scanner/html/document.rb +0 -68
  189. data/lib/action_controller/vendor/html-scanner/html/node.rb +0 -532
  190. data/lib/action_controller/vendor/html-scanner/html/sanitizer.rb +0 -177
  191. data/lib/action_controller/vendor/html-scanner/html/selector.rb +0 -830
  192. data/lib/action_controller/vendor/html-scanner/html/tokenizer.rb +0 -107
  193. data/lib/action_controller/vendor/html-scanner/html/version.rb +0 -11
  194. data/lib/action_controller/vendor/html-scanner.rb +0 -20
  195. data/lib/action_dispatch/middleware/best_standards_support.rb +0 -30
  196. data/lib/action_dispatch/middleware/body_proxy.rb +0 -30
  197. data/lib/action_dispatch/middleware/head.rb +0 -18
  198. data/lib/action_dispatch/middleware/params_parser.rb +0 -75
  199. data/lib/action_dispatch/middleware/rescue.rb +0 -26
  200. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb +0 -31
  201. data/lib/action_dispatch/middleware/templates/rescues/_trace.erb +0 -26
  202. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.erb +0 -10
  203. data/lib/action_dispatch/middleware/templates/rescues/missing_template.erb +0 -2
  204. data/lib/action_dispatch/middleware/templates/rescues/routing_error.erb +0 -15
  205. data/lib/action_dispatch/middleware/templates/rescues/template_error.erb +0 -17
  206. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.erb +0 -2
  207. data/lib/action_dispatch/testing/assertions/dom.rb +0 -37
  208. data/lib/action_dispatch/testing/assertions/selector.rb +0 -435
  209. data/lib/action_dispatch/testing/assertions/tag.rb +0 -138
  210. data/lib/action_dispatch/testing/performance_test.rb +0 -10
  211. data/lib/action_view/asset_paths.rb +0 -142
  212. data/lib/action_view/base.rb +0 -220
  213. data/lib/action_view/buffers.rb +0 -43
  214. data/lib/action_view/context.rb +0 -36
  215. data/lib/action_view/flows.rb +0 -79
  216. data/lib/action_view/helpers/active_model_helper.rb +0 -50
  217. data/lib/action_view/helpers/asset_paths.rb +0 -7
  218. data/lib/action_view/helpers/asset_tag_helper.rb +0 -457
  219. data/lib/action_view/helpers/asset_tag_helpers/asset_include_tag.rb +0 -146
  220. data/lib/action_view/helpers/asset_tag_helpers/asset_paths.rb +0 -93
  221. data/lib/action_view/helpers/asset_tag_helpers/javascript_tag_helpers.rb +0 -193
  222. data/lib/action_view/helpers/asset_tag_helpers/stylesheet_tag_helpers.rb +0 -148
  223. data/lib/action_view/helpers/atom_feed_helper.rb +0 -200
  224. data/lib/action_view/helpers/cache_helper.rb +0 -64
  225. data/lib/action_view/helpers/capture_helper.rb +0 -203
  226. data/lib/action_view/helpers/controller_helper.rb +0 -25
  227. data/lib/action_view/helpers/csrf_helper.rb +0 -32
  228. data/lib/action_view/helpers/date_helper.rb +0 -1062
  229. data/lib/action_view/helpers/debug_helper.rb +0 -40
  230. data/lib/action_view/helpers/form_helper.rb +0 -1486
  231. data/lib/action_view/helpers/form_options_helper.rb +0 -658
  232. data/lib/action_view/helpers/form_tag_helper.rb +0 -685
  233. data/lib/action_view/helpers/javascript_helper.rb +0 -110
  234. data/lib/action_view/helpers/number_helper.rb +0 -622
  235. data/lib/action_view/helpers/output_safety_helper.rb +0 -38
  236. data/lib/action_view/helpers/record_tag_helper.rb +0 -111
  237. data/lib/action_view/helpers/rendering_helper.rb +0 -92
  238. data/lib/action_view/helpers/sanitize_helper.rb +0 -259
  239. data/lib/action_view/helpers/tag_helper.rb +0 -167
  240. data/lib/action_view/helpers/text_helper.rb +0 -426
  241. data/lib/action_view/helpers/translation_helper.rb +0 -91
  242. data/lib/action_view/helpers/url_helper.rb +0 -693
  243. data/lib/action_view/helpers.rb +0 -60
  244. data/lib/action_view/locale/en.yml +0 -160
  245. data/lib/action_view/log_subscriber.rb +0 -28
  246. data/lib/action_view/lookup_context.rb +0 -258
  247. data/lib/action_view/path_set.rb +0 -101
  248. data/lib/action_view/railtie.rb +0 -55
  249. data/lib/action_view/renderer/abstract_renderer.rb +0 -41
  250. data/lib/action_view/renderer/partial_renderer.rb +0 -415
  251. data/lib/action_view/renderer/renderer.rb +0 -61
  252. data/lib/action_view/renderer/streaming_template_renderer.rb +0 -106
  253. data/lib/action_view/renderer/template_renderer.rb +0 -95
  254. data/lib/action_view/template/error.rb +0 -128
  255. data/lib/action_view/template/handlers/builder.rb +0 -26
  256. data/lib/action_view/template/handlers/erb.rb +0 -125
  257. data/lib/action_view/template/handlers.rb +0 -50
  258. data/lib/action_view/template/resolver.rb +0 -298
  259. data/lib/action_view/template/text.rb +0 -30
  260. data/lib/action_view/template.rb +0 -337
  261. data/lib/action_view/test_case.rb +0 -246
  262. data/lib/action_view/testing/resolvers.rb +0 -49
  263. data/lib/action_view.rb +0 -84
  264. data/lib/sprockets/assets.rake +0 -99
  265. data/lib/sprockets/bootstrap.rb +0 -37
  266. data/lib/sprockets/compressors.rb +0 -83
  267. data/lib/sprockets/helpers/isolated_helper.rb +0 -13
  268. data/lib/sprockets/helpers/rails_helper.rb +0 -182
  269. data/lib/sprockets/helpers.rb +0 -6
  270. data/lib/sprockets/railtie.rb +0 -62
  271. data/lib/sprockets/static_compiler.rb +0 -56
@@ -1,7 +1,9 @@
1
- require 'digest/md5'
2
- require 'active_support/core_ext/module/delegation'
3
- require 'active_support/core_ext/object/blank'
4
- require 'active_support/core_ext/class/attribute_accessors'
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/module/attribute_accessors"
4
+ require "action_dispatch/http/filter_redirect"
5
+ require "action_dispatch/http/cache"
6
+ require "monitor"
5
7
 
6
8
  module ActionDispatch # :nodoc:
7
9
  # Represents an HTTP response generated by a controller action. Use it to
@@ -29,158 +31,370 @@ module ActionDispatch # :nodoc:
29
31
  # class DemoControllerTest < ActionDispatch::IntegrationTest
30
32
  # def test_print_root_path_to_console
31
33
  # get('/')
32
- # puts @response.body
34
+ # puts response.body
33
35
  # end
34
36
  # end
35
37
  class Response
36
- attr_accessor :request, :header
38
+ class Header < DelegateClass(Hash) # :nodoc:
39
+ def initialize(response, header)
40
+ @response = response
41
+ super(header)
42
+ end
43
+
44
+ def []=(k, v)
45
+ if @response.sending? || @response.sent?
46
+ raise ActionDispatch::IllegalStateError, "header already sent"
47
+ end
48
+
49
+ super
50
+ end
51
+
52
+ def merge(other)
53
+ self.class.new @response, __getobj__.merge(other)
54
+ end
55
+
56
+ def to_hash
57
+ __getobj__.dup
58
+ end
59
+ end
60
+
61
+ # The request that the response is responding to.
62
+ attr_accessor :request
63
+
64
+ # The HTTP status code.
37
65
  attr_reader :status
38
- attr_writer :sending_file
39
66
 
40
- alias_method :headers=, :header=
67
+ # Get headers for this response.
68
+ attr_reader :header
69
+
41
70
  alias_method :headers, :header
42
71
 
43
- delegate :[], :[]=, :to => :@header
44
- delegate :each, :to => :@body
72
+ delegate :[], :[]=, to: :@header
45
73
 
46
- # Sets the HTTP response's content MIME type. For example, in the controller
47
- # you could write this:
48
- #
49
- # response.content_type = "text/plain"
50
- #
51
- # If a character set has been defined for this response (see charset=) then
52
- # the character set information will also be included in the content type
53
- # information.
54
- attr_accessor :charset, :content_type
74
+ def each(&block)
75
+ sending!
76
+ x = @stream.each(&block)
77
+ sent!
78
+ x
79
+ end
55
80
 
56
81
  CONTENT_TYPE = "Content-Type".freeze
57
82
  SET_COOKIE = "Set-Cookie".freeze
58
83
  LOCATION = "Location".freeze
59
-
60
- cattr_accessor(:default_charset) { "utf-8" }
84
+ NO_CONTENT_CODES = [100, 101, 102, 204, 205, 304]
85
+
86
+ cattr_accessor :default_charset, default: "utf-8"
87
+ cattr_accessor :default_headers
61
88
 
62
89
  include Rack::Response::Helpers
90
+ # Aliasing these off because AD::Http::Cache::Response defines them.
91
+ alias :_cache_control :cache_control
92
+ alias :_cache_control= :cache_control=
93
+
94
+ include ActionDispatch::Http::FilterRedirect
63
95
  include ActionDispatch::Http::Cache::Response
96
+ include MonitorMixin
97
+
98
+ class Buffer # :nodoc:
99
+ def initialize(response, buf)
100
+ @response = response
101
+ @buf = buf
102
+ @closed = false
103
+ @str_body = nil
104
+ end
64
105
 
65
- def initialize(status = 200, header = {}, body = [])
66
- self.body, self.header, self.status = body, header, status
106
+ def body
107
+ @str_body ||= begin
108
+ buf = "".dup
109
+ each { |chunk| buf << chunk }
110
+ buf
111
+ end
112
+ end
67
113
 
68
- @sending_file = false
69
- @blank = false
114
+ def write(string)
115
+ raise IOError, "closed stream" if closed?
70
116
 
71
- if content_type = self[CONTENT_TYPE]
72
- type, charset = content_type.split(/;\s*charset=/)
73
- @content_type = Mime::Type.lookup(type)
74
- @charset = charset || self.class.default_charset
117
+ @str_body = nil
118
+ @response.commit!
119
+ @buf.push string
75
120
  end
76
121
 
122
+ def each(&block)
123
+ if @str_body
124
+ return enum_for(:each) unless block_given?
125
+
126
+ yield @str_body
127
+ else
128
+ each_chunk(&block)
129
+ end
130
+ end
131
+
132
+ def abort
133
+ end
134
+
135
+ def close
136
+ @response.commit!
137
+ @closed = true
138
+ end
139
+
140
+ def closed?
141
+ @closed
142
+ end
143
+
144
+ private
145
+
146
+ def each_chunk(&block)
147
+ @buf.each(&block)
148
+ end
149
+ end
150
+
151
+ def self.create(status = 200, header = {}, body = [], default_headers: self.default_headers)
152
+ header = merge_default_headers(header, default_headers)
153
+ new status, header, body
154
+ end
155
+
156
+ def self.merge_default_headers(original, default)
157
+ default.respond_to?(:merge) ? default.merge(original) : original
158
+ end
159
+
160
+ # The underlying body, as a streamable object.
161
+ attr_reader :stream
162
+
163
+ def initialize(status = 200, header = {}, body = [])
164
+ super()
165
+
166
+ @header = Header.new(self, header)
167
+
168
+ self.body, self.status = body, status
169
+
170
+ @cv = new_cond
171
+ @committed = false
172
+ @sending = false
173
+ @sent = false
174
+
77
175
  prepare_cache_control!
78
176
 
79
177
  yield self if block_given?
80
178
  end
81
179
 
180
+ def has_header?(key); headers.key? key; end
181
+ def get_header(key); headers[key]; end
182
+ def set_header(key, v); headers[key] = v; end
183
+ def delete_header(key); headers.delete key; end
184
+
185
+ def await_commit
186
+ synchronize do
187
+ @cv.wait_until { @committed }
188
+ end
189
+ end
190
+
191
+ def await_sent
192
+ synchronize { @cv.wait_until { @sent } }
193
+ end
194
+
195
+ def commit!
196
+ synchronize do
197
+ before_committed
198
+ @committed = true
199
+ @cv.broadcast
200
+ end
201
+ end
202
+
203
+ def sending!
204
+ synchronize do
205
+ before_sending
206
+ @sending = true
207
+ @cv.broadcast
208
+ end
209
+ end
210
+
211
+ def sent!
212
+ synchronize do
213
+ @sent = true
214
+ @cv.broadcast
215
+ end
216
+ end
217
+
218
+ def sending?; synchronize { @sending }; end
219
+ def committed?; synchronize { @committed }; end
220
+ def sent?; synchronize { @sent }; end
221
+
222
+ # Sets the HTTP status code.
82
223
  def status=(status)
83
224
  @status = Rack::Utils.status_code(status)
84
225
  end
85
226
 
86
- # The response code of the request
227
+ # Sets the HTTP content type.
228
+ def content_type=(content_type)
229
+ return unless content_type
230
+ new_header_info = parse_content_type(content_type.to_s)
231
+ prev_header_info = parsed_content_type_header
232
+ charset = new_header_info.charset || prev_header_info.charset
233
+ charset ||= self.class.default_charset unless prev_header_info.mime_type
234
+ set_content_type new_header_info.mime_type, charset
235
+ end
236
+
237
+ # Sets the HTTP response's content MIME type. For example, in the controller
238
+ # you could write this:
239
+ #
240
+ # response.content_type = "text/plain"
241
+ #
242
+ # If a character set has been defined for this response (see charset=) then
243
+ # the character set information will also be included in the content type
244
+ # information.
245
+
246
+ def content_type
247
+ parsed_content_type_header.mime_type
248
+ end
249
+
250
+ def sending_file=(v)
251
+ if true == v
252
+ self.charset = false
253
+ end
254
+ end
255
+
256
+ # Sets the HTTP character set. In case of +nil+ parameter
257
+ # it sets the charset to +default_charset+.
258
+ #
259
+ # response.charset = 'utf-16' # => 'utf-16'
260
+ # response.charset = nil # => 'utf-8'
261
+ def charset=(charset)
262
+ content_type = parsed_content_type_header.mime_type
263
+ if false == charset
264
+ set_content_type content_type, nil
265
+ else
266
+ set_content_type content_type, charset || self.class.default_charset
267
+ end
268
+ end
269
+
270
+ # The charset of the response. HTML wants to know the encoding of the
271
+ # content you're giving them, so we need to send that along.
272
+ def charset
273
+ header_info = parsed_content_type_header
274
+ header_info.charset || self.class.default_charset
275
+ end
276
+
277
+ # The response code of the request.
87
278
  def response_code
88
279
  @status
89
280
  end
90
281
 
91
- # Returns a String to ensure compatibility with Net::HTTPResponse
282
+ # Returns a string to ensure compatibility with <tt>Net::HTTPResponse</tt>.
92
283
  def code
93
284
  @status.to_s
94
285
  end
95
286
 
287
+ # Returns the corresponding message for the current HTTP status code:
288
+ #
289
+ # response.status = 200
290
+ # response.message # => "OK"
291
+ #
292
+ # response.status = 404
293
+ # response.message # => "Not Found"
294
+ #
96
295
  def message
97
296
  Rack::Utils::HTTP_STATUS_CODES[@status]
98
297
  end
99
298
  alias_method :status_message, :message
100
299
 
101
- def respond_to?(method)
102
- if method.to_sym == :to_path
103
- @body.respond_to?(:to_path)
104
- else
105
- super
106
- end
300
+ # Returns the content of the response as a string. This contains the contents
301
+ # of any calls to <tt>render</tt>.
302
+ def body
303
+ @stream.body
107
304
  end
108
305
 
109
- def to_path
110
- @body.to_path
306
+ def write(string)
307
+ @stream.write string
111
308
  end
112
309
 
113
- def body
114
- strings = []
115
- each { |part| strings << part.to_s }
116
- strings.join
310
+ # Allows you to manually set or override the response body.
311
+ def body=(body)
312
+ if body.respond_to?(:to_path)
313
+ @stream = body
314
+ else
315
+ synchronize do
316
+ @stream = build_buffer self, munge_body_object(body)
317
+ end
318
+ end
117
319
  end
118
320
 
119
- EMPTY = " "
321
+ # Avoid having to pass an open file handle as the response body.
322
+ # Rack::Sendfile will usually intercept the response and uses
323
+ # the path directly, so there is no reason to open the file.
324
+ class FileBody #:nodoc:
325
+ attr_reader :to_path
120
326
 
121
- def body=(body)
122
- @blank = true if body == EMPTY
327
+ def initialize(path)
328
+ @to_path = path
329
+ end
123
330
 
124
- # Explicitly check for strings. This is *wrong* theoretically
125
- # but if we don't check this, the performance on string bodies
126
- # is bad on Ruby 1.8 (because strings responds to each then).
127
- @body = if body.respond_to?(:to_str) || !body.respond_to?(:each)
128
- [body]
129
- else
130
- body
331
+ def body
332
+ File.binread(to_path)
131
333
  end
132
- end
133
334
 
134
- def body_parts
135
- @body
335
+ # Stream the file's contents if Rack::Sendfile isn't present.
336
+ def each
337
+ File.open(to_path, "rb") do |file|
338
+ while chunk = file.read(16384)
339
+ yield chunk
340
+ end
341
+ end
342
+ end
136
343
  end
137
344
 
138
- def set_cookie(key, value)
139
- ::Rack::Utils.set_cookie_header!(header, key, value)
345
+ # Send the file stored at +path+ as the response body.
346
+ def send_file(path)
347
+ commit!
348
+ @stream = FileBody.new(path)
140
349
  end
141
350
 
142
- def delete_cookie(key, value={})
143
- ::Rack::Utils.delete_cookie_header!(header, key, value)
351
+ def reset_body!
352
+ @stream = build_buffer(self, [])
144
353
  end
145
354
 
146
- def location
147
- headers[LOCATION]
355
+ def body_parts
356
+ parts = []
357
+ @stream.each { |x| parts << x }
358
+ parts
148
359
  end
360
+
361
+ # The location header we'll be responding with.
149
362
  alias_method :redirect_url, :location
150
363
 
151
- def location=(url)
152
- headers[LOCATION] = url
364
+ def close
365
+ stream.close if stream.respond_to?(:close)
153
366
  end
154
367
 
155
- def close
156
- @body.close if @body.respond_to?(:close)
368
+ def abort
369
+ if stream.respond_to?(:abort)
370
+ stream.abort
371
+ elsif stream.respond_to?(:close)
372
+ # `stream.close` should really be reserved for a close from the
373
+ # other direction, but we must fall back to it for
374
+ # compatibility.
375
+ stream.close
376
+ end
157
377
  end
158
378
 
379
+ # Turns the Response into a Rack-compatible array of the status, headers,
380
+ # and body. Allows explicit splatting:
381
+ #
382
+ # status, headers, body = *response
159
383
  def to_a
160
- assign_default_content_type_and_charset!
161
- handle_conditional_get!
162
-
163
- @header[SET_COOKIE] = @header[SET_COOKIE].join("\n") if @header[SET_COOKIE].respond_to?(:join)
164
-
165
- if [204, 304].include?(@status)
166
- @header.delete CONTENT_TYPE
167
- [@status, @header, []]
168
- else
169
- [@status, @header, self]
170
- end
384
+ commit!
385
+ rack_response @status, @header.to_hash
171
386
  end
172
387
  alias prepare! to_a
173
- alias to_ary to_a # For implicit splat on 1.9.2
174
388
 
175
389
  # Returns the response cookies, converted to a Hash of (name => value) pairs
176
390
  #
177
391
  # assert_equal 'AuthorOfNewPage', r.cookies['author']
178
392
  def cookies
179
393
  cookies = {}
180
- if header = self[SET_COOKIE]
394
+ if header = get_header(SET_COOKIE)
181
395
  header = header.split("\n") if header.respond_to?(:to_str)
182
396
  header.each do |cookie|
183
- if pair = cookie.split(';').first
397
+ if pair = cookie.split(";").first
184
398
  key, value = pair.split("=").map { |v| Rack::Utils.unescape(v) }
185
399
  cookies[key] = value
186
400
  end
@@ -191,16 +405,116 @@ module ActionDispatch # :nodoc:
191
405
 
192
406
  private
193
407
 
408
+ ContentTypeHeader = Struct.new :mime_type, :charset
409
+ NullContentTypeHeader = ContentTypeHeader.new nil, nil
410
+
411
+ def parse_content_type(content_type)
412
+ if content_type
413
+ type, charset = content_type.split(/;\s*charset=/)
414
+ type = nil if type && type.empty?
415
+ ContentTypeHeader.new(type, charset)
416
+ else
417
+ NullContentTypeHeader
418
+ end
419
+ end
420
+
421
+ # Small internal convenience method to get the parsed version of the current
422
+ # content type header.
423
+ def parsed_content_type_header
424
+ parse_content_type(get_header(CONTENT_TYPE))
425
+ end
426
+
427
+ def set_content_type(content_type, charset)
428
+ type = (content_type || "").dup
429
+ type << "; charset=#{charset.to_s.downcase}" if charset
430
+ set_header CONTENT_TYPE, type
431
+ end
432
+
433
+ def before_committed
434
+ return if committed?
435
+ assign_default_content_type_and_charset!
436
+ merge_and_normalize_cache_control!(@cache_control)
437
+ handle_conditional_get!
438
+ handle_no_content!
439
+ end
440
+
441
+ def before_sending
442
+ # Normally we've already committed by now, but it's possible
443
+ # (e.g., if the controller action tries to read back its own
444
+ # response) to get here before that. In that case, we must force
445
+ # an "early" commit: we're about to freeze the headers, so this is
446
+ # our last chance.
447
+ commit! unless committed?
448
+
449
+ headers.freeze
450
+ request.commit_cookie_jar! unless committed?
451
+ end
452
+
453
+ def build_buffer(response, body)
454
+ Buffer.new response, body
455
+ end
456
+
457
+ def munge_body_object(body)
458
+ body.respond_to?(:each) ? body : [body]
459
+ end
460
+
194
461
  def assign_default_content_type_and_charset!
195
- return if headers[CONTENT_TYPE].present?
462
+ return if content_type
463
+
464
+ ct = parsed_content_type_header
465
+ set_content_type(ct.mime_type || Mime[:html].to_s,
466
+ ct.charset || self.class.default_charset)
467
+ end
468
+
469
+ class RackBody
470
+ def initialize(response)
471
+ @response = response
472
+ end
196
473
 
197
- @content_type ||= Mime::HTML
198
- @charset ||= self.class.default_charset
474
+ def each(*args, &block)
475
+ @response.each(*args, &block)
476
+ end
199
477
 
200
- type = @content_type.to_s.dup
201
- type << "; charset=#{@charset}" unless @sending_file
478
+ def close
479
+ # Rack "close" maps to Response#abort, and *not* Response#close
480
+ # (which is used when the controller's finished writing)
481
+ @response.abort
482
+ end
202
483
 
203
- headers[CONTENT_TYPE] = type
484
+ def body
485
+ @response.body
486
+ end
487
+
488
+ def respond_to?(method, include_private = false)
489
+ if method.to_s == "to_path"
490
+ @response.stream.respond_to?(method)
491
+ else
492
+ super
493
+ end
494
+ end
495
+
496
+ def to_path
497
+ @response.stream.to_path
498
+ end
499
+
500
+ def to_ary
501
+ nil
502
+ end
503
+ end
504
+
505
+ def handle_no_content!
506
+ if NO_CONTENT_CODES.include?(@status)
507
+ @header.delete CONTENT_TYPE
508
+ @header.delete "Content-Length"
509
+ end
510
+ end
511
+
512
+ def rack_response(status, header)
513
+ if NO_CONTENT_CODES.include?(status)
514
+ [status, header, []]
515
+ else
516
+ [status, header, RackBody.new(self)]
517
+ end
204
518
  end
205
519
  end
206
520
  end
@@ -1,47 +1,84 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActionDispatch
2
4
  module Http
5
+ # Models uploaded files.
6
+ #
7
+ # The actual file is accessible via the +tempfile+ accessor, though some
8
+ # of its interface is available directly for convenience.
9
+ #
10
+ # Uploaded files are temporary files whose lifespan is one request. When
11
+ # the object is finalized Ruby unlinks the file, so there is no need to
12
+ # clean them with a separate maintenance task.
3
13
  class UploadedFile
4
- attr_accessor :original_filename, :content_type, :tempfile, :headers
14
+ # The basename of the file in the client.
15
+ attr_accessor :original_filename
16
+
17
+ # A string with the MIME type of the file.
18
+ attr_accessor :content_type
19
+
20
+ # A +Tempfile+ object with the actual uploaded file. Note that some of
21
+ # its interface is available directly.
22
+ attr_accessor :tempfile
23
+ alias :to_io :tempfile
24
+
25
+ # A string with the headers of the multipart request.
26
+ attr_accessor :headers
27
+
28
+ def initialize(hash) # :nodoc:
29
+ @tempfile = hash[:tempfile]
30
+ raise(ArgumentError, ":tempfile is required") unless @tempfile
31
+
32
+ if hash[:filename]
33
+ @original_filename = hash[:filename].dup
34
+
35
+ begin
36
+ @original_filename.encode!(Encoding::UTF_8)
37
+ rescue EncodingError
38
+ @original_filename.force_encoding(Encoding::UTF_8)
39
+ end
40
+ else
41
+ @original_filename = nil
42
+ end
5
43
 
6
- def initialize(hash)
7
- @original_filename = encode_filename(hash[:filename])
8
44
  @content_type = hash[:type]
9
45
  @headers = hash[:head]
10
- @tempfile = hash[:tempfile]
11
- raise(ArgumentError, ':tempfile is required') unless @tempfile
12
46
  end
13
47
 
14
- def read(*args)
15
- @tempfile.read(*args)
48
+ # Shortcut for +tempfile.read+.
49
+ def read(length = nil, buffer = nil)
50
+ @tempfile.read(length, buffer)
16
51
  end
17
52
 
18
- # Delegate these methods to the tempfile.
19
- [:open, :path, :rewind, :size].each do |method|
20
- class_eval "def #{method}; @tempfile.#{method}; end"
53
+ # Shortcut for +tempfile.open+.
54
+ def open
55
+ @tempfile.open
21
56
  end
22
57
 
23
- private
24
- def encode_filename(filename)
25
- # Encode the filename in the utf8 encoding, unless it is nil or we're in 1.8
26
- if "ruby".encoding_aware? && filename
27
- filename.force_encoding("UTF-8").encode!
28
- else
29
- filename
30
- end
58
+ # Shortcut for +tempfile.close+.
59
+ def close(unlink_now = false)
60
+ @tempfile.close(unlink_now)
31
61
  end
32
- end
33
62
 
34
- module Upload
35
- # Convert nested Hash to HashWithIndifferentAccess and replace
36
- # file upload hash with UploadedFile objects
37
- def normalize_parameters(value)
38
- if Hash === value && value.has_key?(:tempfile)
39
- UploadedFile.new(value)
40
- else
41
- super
42
- end
63
+ # Shortcut for +tempfile.path+.
64
+ def path
65
+ @tempfile.path
66
+ end
67
+
68
+ # Shortcut for +tempfile.rewind+.
69
+ def rewind
70
+ @tempfile.rewind
71
+ end
72
+
73
+ # Shortcut for +tempfile.size+.
74
+ def size
75
+ @tempfile.size
76
+ end
77
+
78
+ # Shortcut for +tempfile.eof?+.
79
+ def eof?
80
+ @tempfile.eof?
43
81
  end
44
- private :normalize_parameters
45
82
  end
46
83
  end
47
84
  end