rack 2.0.9.3 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of rack might be problematic. Click here for more details.

Files changed (201) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +808 -0
  3. data/CONTRIBUTING.md +142 -0
  4. data/{COPYING → MIT-LICENSE} +4 -2
  5. data/README.md +293 -0
  6. data/SPEC.rdoc +340 -0
  7. data/lib/rack/auth/abstract/handler.rb +6 -2
  8. data/lib/rack/auth/abstract/request.rb +4 -2
  9. data/lib/rack/auth/basic.rb +7 -4
  10. data/lib/rack/auth/digest/md5.rb +1 -129
  11. data/lib/rack/auth/digest/nonce.rb +1 -51
  12. data/lib/rack/auth/digest/params.rb +1 -52
  13. data/lib/rack/auth/digest/request.rb +1 -41
  14. data/lib/rack/auth/digest.rb +256 -0
  15. data/lib/rack/body_proxy.rb +18 -15
  16. data/lib/rack/builder.rb +151 -40
  17. data/lib/rack/cascade.rb +30 -12
  18. data/lib/rack/chunked.rb +74 -23
  19. data/lib/rack/common_logger.rb +49 -36
  20. data/lib/rack/conditional_get.rb +33 -26
  21. data/lib/rack/config.rb +2 -0
  22. data/lib/rack/constants.rb +63 -0
  23. data/lib/rack/content_length.rb +13 -16
  24. data/lib/rack/content_type.rb +12 -8
  25. data/lib/rack/deflater.rb +84 -45
  26. data/lib/rack/directory.rb +90 -64
  27. data/lib/rack/etag.rb +17 -23
  28. data/lib/rack/events.rb +23 -20
  29. data/lib/rack/file.rb +5 -172
  30. data/lib/rack/files.rb +216 -0
  31. data/lib/rack/head.rb +10 -9
  32. data/lib/rack/headers.rb +154 -0
  33. data/lib/rack/lint.rb +786 -645
  34. data/lib/rack/lock.rb +4 -6
  35. data/lib/rack/logger.rb +4 -0
  36. data/lib/rack/media_type.rb +10 -5
  37. data/lib/rack/method_override.rb +8 -2
  38. data/lib/rack/mime.rb +17 -1
  39. data/lib/rack/mock.rb +2 -195
  40. data/lib/rack/mock_request.rb +166 -0
  41. data/lib/rack/mock_response.rb +126 -0
  42. data/lib/rack/multipart/generator.rb +21 -15
  43. data/lib/rack/multipart/parser.rb +161 -118
  44. data/lib/rack/multipart/uploaded_file.rb +19 -7
  45. data/lib/rack/multipart.rb +23 -41
  46. data/lib/rack/null_logger.rb +11 -0
  47. data/lib/rack/query_parser.rb +126 -65
  48. data/lib/rack/recursive.rb +9 -5
  49. data/lib/rack/reloader.rb +6 -4
  50. data/lib/rack/request.rb +331 -74
  51. data/lib/rack/response.rb +223 -70
  52. data/lib/rack/rewindable_input.rb +28 -8
  53. data/lib/rack/runtime.rb +11 -8
  54. data/lib/rack/sendfile.rb +42 -33
  55. data/lib/rack/show_exceptions.rb +35 -18
  56. data/lib/rack/show_status.rb +25 -15
  57. data/lib/rack/static.rb +30 -18
  58. data/lib/rack/tempfile_reaper.rb +16 -5
  59. data/lib/rack/urlmap.rb +14 -6
  60. data/lib/rack/utils.rb +268 -260
  61. data/lib/rack/version.rb +34 -0
  62. data/lib/rack.rb +15 -92
  63. metadata +44 -207
  64. data/HISTORY.md +0 -520
  65. data/README.rdoc +0 -316
  66. data/Rakefile +0 -116
  67. data/SPEC +0 -263
  68. data/bin/rackup +0 -4
  69. data/contrib/rack.png +0 -0
  70. data/contrib/rack.svg +0 -150
  71. data/contrib/rack_logo.svg +0 -164
  72. data/contrib/rdoc.css +0 -412
  73. data/example/lobster.ru +0 -4
  74. data/example/protectedlobster.rb +0 -14
  75. data/example/protectedlobster.ru +0 -8
  76. data/lib/rack/handler/cgi.rb +0 -60
  77. data/lib/rack/handler/fastcgi.rb +0 -100
  78. data/lib/rack/handler/lsws.rb +0 -61
  79. data/lib/rack/handler/scgi.rb +0 -70
  80. data/lib/rack/handler/thin.rb +0 -36
  81. data/lib/rack/handler/webrick.rb +0 -120
  82. data/lib/rack/handler.rb +0 -99
  83. data/lib/rack/lobster.rb +0 -70
  84. data/lib/rack/server.rb +0 -395
  85. data/lib/rack/session/abstract/id.rb +0 -510
  86. data/lib/rack/session/cookie.rb +0 -204
  87. data/lib/rack/session/memcache.rb +0 -99
  88. data/lib/rack/session/pool.rb +0 -83
  89. data/rack.gemspec +0 -34
  90. data/test/builder/an_underscore_app.rb +0 -5
  91. data/test/builder/anything.rb +0 -5
  92. data/test/builder/comment.ru +0 -4
  93. data/test/builder/end.ru +0 -5
  94. data/test/builder/line.ru +0 -1
  95. data/test/builder/options.ru +0 -2
  96. data/test/cgi/assets/folder/test.js +0 -1
  97. data/test/cgi/assets/fonts/font.eot +0 -1
  98. data/test/cgi/assets/images/image.png +0 -1
  99. data/test/cgi/assets/index.html +0 -1
  100. data/test/cgi/assets/javascripts/app.js +0 -1
  101. data/test/cgi/assets/stylesheets/app.css +0 -1
  102. data/test/cgi/lighttpd.conf +0 -26
  103. data/test/cgi/rackup_stub.rb +0 -6
  104. data/test/cgi/sample_rackup.ru +0 -5
  105. data/test/cgi/test +0 -9
  106. data/test/cgi/test+directory/test+file +0 -1
  107. data/test/cgi/test.fcgi +0 -9
  108. data/test/cgi/test.gz +0 -0
  109. data/test/cgi/test.ru +0 -5
  110. data/test/gemloader.rb +0 -10
  111. data/test/helper.rb +0 -34
  112. data/test/multipart/bad_robots +0 -259
  113. data/test/multipart/binary +0 -0
  114. data/test/multipart/content_type_and_no_filename +0 -6
  115. data/test/multipart/empty +0 -10
  116. data/test/multipart/fail_16384_nofile +0 -814
  117. data/test/multipart/file1.txt +0 -1
  118. data/test/multipart/filename_and_modification_param +0 -7
  119. data/test/multipart/filename_and_no_name +0 -6
  120. data/test/multipart/filename_with_encoded_words +0 -7
  121. data/test/multipart/filename_with_escaped_quotes +0 -6
  122. data/test/multipart/filename_with_escaped_quotes_and_modification_param +0 -7
  123. data/test/multipart/filename_with_null_byte +0 -7
  124. data/test/multipart/filename_with_percent_escaped_quotes +0 -6
  125. data/test/multipart/filename_with_single_quote +0 -7
  126. data/test/multipart/filename_with_unescaped_percentages +0 -6
  127. data/test/multipart/filename_with_unescaped_percentages2 +0 -6
  128. data/test/multipart/filename_with_unescaped_percentages3 +0 -6
  129. data/test/multipart/filename_with_unescaped_quotes +0 -6
  130. data/test/multipart/ie +0 -6
  131. data/test/multipart/invalid_character +0 -6
  132. data/test/multipart/mixed_files +0 -21
  133. data/test/multipart/nested +0 -10
  134. data/test/multipart/none +0 -9
  135. data/test/multipart/quoted +0 -15
  136. data/test/multipart/rack-logo.png +0 -0
  137. data/test/multipart/semicolon +0 -6
  138. data/test/multipart/text +0 -15
  139. data/test/multipart/three_files_three_fields +0 -31
  140. data/test/multipart/unity3d_wwwform +0 -11
  141. data/test/multipart/webkit +0 -32
  142. data/test/rackup/config.ru +0 -31
  143. data/test/registering_handler/rack/handler/registering_myself.rb +0 -8
  144. data/test/spec_auth_basic.rb +0 -89
  145. data/test/spec_auth_digest.rb +0 -260
  146. data/test/spec_body_proxy.rb +0 -85
  147. data/test/spec_builder.rb +0 -233
  148. data/test/spec_cascade.rb +0 -63
  149. data/test/spec_cgi.rb +0 -84
  150. data/test/spec_chunked.rb +0 -103
  151. data/test/spec_common_logger.rb +0 -107
  152. data/test/spec_conditional_get.rb +0 -103
  153. data/test/spec_config.rb +0 -23
  154. data/test/spec_content_length.rb +0 -86
  155. data/test/spec_content_type.rb +0 -46
  156. data/test/spec_deflater.rb +0 -375
  157. data/test/spec_directory.rb +0 -148
  158. data/test/spec_etag.rb +0 -108
  159. data/test/spec_events.rb +0 -133
  160. data/test/spec_fastcgi.rb +0 -85
  161. data/test/spec_file.rb +0 -264
  162. data/test/spec_handler.rb +0 -57
  163. data/test/spec_head.rb +0 -46
  164. data/test/spec_lint.rb +0 -520
  165. data/test/spec_lobster.rb +0 -59
  166. data/test/spec_lock.rb +0 -204
  167. data/test/spec_logger.rb +0 -24
  168. data/test/spec_media_type.rb +0 -42
  169. data/test/spec_method_override.rb +0 -110
  170. data/test/spec_mime.rb +0 -51
  171. data/test/spec_mock.rb +0 -359
  172. data/test/spec_multipart.rb +0 -721
  173. data/test/spec_null_logger.rb +0 -21
  174. data/test/spec_recursive.rb +0 -75
  175. data/test/spec_request.rb +0 -1423
  176. data/test/spec_response.rb +0 -528
  177. data/test/spec_rewindable_input.rb +0 -128
  178. data/test/spec_runtime.rb +0 -50
  179. data/test/spec_sendfile.rb +0 -125
  180. data/test/spec_server.rb +0 -193
  181. data/test/spec_session_abstract_id.rb +0 -31
  182. data/test/spec_session_abstract_session_hash.rb +0 -45
  183. data/test/spec_session_cookie.rb +0 -442
  184. data/test/spec_session_memcache.rb +0 -357
  185. data/test/spec_session_persisted_secure_secure_session_hash.rb +0 -73
  186. data/test/spec_session_pool.rb +0 -247
  187. data/test/spec_show_exceptions.rb +0 -93
  188. data/test/spec_show_status.rb +0 -104
  189. data/test/spec_static.rb +0 -184
  190. data/test/spec_tempfile_reaper.rb +0 -64
  191. data/test/spec_thin.rb +0 -96
  192. data/test/spec_urlmap.rb +0 -237
  193. data/test/spec_utils.rb +0 -742
  194. data/test/spec_version.rb +0 -11
  195. data/test/spec_webrick.rb +0 -206
  196. data/test/static/another/index.html +0 -1
  197. data/test/static/foo.html +0 -1
  198. data/test/static/index.html +0 -1
  199. data/test/testrequest.rb +0 -78
  200. data/test/unregistered_handler/rack/handler/unregistered.rb +0 -7
  201. data/test/unregistered_handler/rack/handler/unregistered_long_one.rb +0 -7
data/lib/rack/response.rb CHANGED
@@ -1,9 +1,12 @@
1
- require 'rack/request'
2
- require 'rack/utils'
3
- require 'rack/body_proxy'
4
- require 'rack/media_type'
1
+ # frozen_string_literal: true
2
+
5
3
  require 'time'
6
4
 
5
+ require_relative 'constants'
6
+ require_relative 'utils'
7
+ require_relative 'media_type'
8
+ require_relative 'headers'
9
+
7
10
  module Rack
8
11
  # Rack::Response provides a convenient interface to create a Rack
9
12
  # response.
@@ -17,38 +20,80 @@ module Rack
17
20
  # +write+ are synchronous with the Rack response.
18
21
  #
19
22
  # Your application's +call+ should end returning Response#finish.
20
-
21
23
  class Response
24
+ def self.[](status, headers, body)
25
+ self.new(body, status, headers)
26
+ end
27
+
28
+ CHUNKED = 'chunked'
29
+ STATUS_WITH_NO_ENTITY_BODY = Utils::STATUS_WITH_NO_ENTITY_BODY
30
+
22
31
  attr_accessor :length, :status, :body
23
- attr_reader :header
24
- alias headers header
32
+ attr_reader :headers
33
+
34
+ # Deprecated, use headers instead.
35
+ def header
36
+ warn 'Rack::Response#header is deprecated and will be removed in Rack 3.1', uplevel: 1
25
37
 
26
- CHUNKED = 'chunked'.freeze
38
+ headers
39
+ end
27
40
 
28
- def initialize(body=[], status=200, header={})
41
+ # Initialize the response object with the specified +body+, +status+
42
+ # and +headers+.
43
+ #
44
+ # If the +body+ is +nil+, construct an empty response object with internal
45
+ # buffering.
46
+ #
47
+ # If the +body+ responds to +to_str+, assume it's a string-like object and
48
+ # construct a buffered response object containing using that string as the
49
+ # initial contents of the buffer.
50
+ #
51
+ # Otherwise it is expected +body+ conforms to the normal requirements of a
52
+ # Rack response body, typically implementing one of +each+ (enumerable
53
+ # body) or +call+ (streaming body).
54
+ #
55
+ # The +status+ defaults to +200+ which is the "OK" HTTP status code. You
56
+ # can provide any other valid status code.
57
+ #
58
+ # The +headers+ must be a +Hash+ of key-value header pairs which conform to
59
+ # the Rack specification for response headers. The key must be a +String+
60
+ # instance and the value can be either a +String+ or +Array+ instance.
61
+ def initialize(body = nil, status = 200, headers = {})
29
62
  @status = status.to_i
30
- @header = Utils::HeaderHash.new.merge(header)
31
63
 
32
- @writer = lambda { |x| @body << x }
33
- @block = nil
34
- @length = 0
64
+ unless headers.is_a?(Hash)
65
+ warn "Providing non-hash headers to Rack::Response is deprecated and will be removed in Rack 3.1", uplevel: 1
66
+ end
35
67
 
36
- @body = []
68
+ @headers = Headers.new
69
+ # Convert headers input to a plain hash with lowercase keys.
70
+ headers.each do |k, v|
71
+ @headers[k] = v
72
+ end
37
73
 
38
- if body.respond_to? :to_str
39
- write body.to_str
40
- elsif body.respond_to?(:each)
41
- body.each { |part|
42
- write part.to_s
43
- }
74
+ @writer = self.method(:append)
75
+
76
+ @block = nil
77
+
78
+ # Keep track of whether we have expanded the user supplied body.
79
+ if body.nil?
80
+ @body = []
81
+ @buffered = true
82
+ @length = 0
83
+ elsif body.respond_to?(:to_str)
84
+ @body = [body]
85
+ @buffered = true
86
+ @length = body.to_str.bytesize
44
87
  else
45
- raise TypeError, "stringable or iterable required"
88
+ @body = body
89
+ @buffered = nil # undetermined as of yet.
90
+ @length = 0
46
91
  end
47
92
 
48
- yield self if block_given?
93
+ yield self if block_given?
49
94
  end
50
95
 
51
- def redirect(target, status=302)
96
+ def redirect(target, status = 302)
52
97
  self.status = status
53
98
  self.location = target
54
99
  end
@@ -57,52 +102,71 @@ module Rack
57
102
  CHUNKED == get_header(TRANSFER_ENCODING)
58
103
  end
59
104
 
105
+ # Generate a response array consistent with the requirements of the SPEC.
106
+ # @return [Array] a 3-tuple suitable of `[status, headers, body]`
107
+ # which is suitable to be returned from the middleware `#call(env)` method.
60
108
  def finish(&block)
61
- @block = block
62
-
63
- if [204, 304].include?(status.to_i)
109
+ if STATUS_WITH_NO_ENTITY_BODY[@status]
64
110
  delete_header CONTENT_TYPE
65
111
  delete_header CONTENT_LENGTH
66
112
  close
67
- [status.to_i, header, []]
113
+ return [@status, @headers, []]
68
114
  else
69
- [status.to_i, header, BodyProxy.new(self){}]
115
+ if block_given?
116
+ @block = block
117
+ return [@status, @headers, self]
118
+ else
119
+ return [@status, @headers, @body]
120
+ end
70
121
  end
71
122
  end
123
+
72
124
  alias to_a finish # For *response
73
- alias to_ary finish # For implicit-splat on Ruby 1.9.2
74
125
 
75
126
  def each(&callback)
76
127
  @body.each(&callback)
77
- @writer = callback
78
- @block.call(self) if @block
128
+ @buffered = true
129
+
130
+ if @block
131
+ @writer = callback
132
+ @block.call(self)
133
+ end
79
134
  end
80
135
 
81
- # Append to body and update Content-Length.
136
+ # Append to body and update content-length.
82
137
  #
83
138
  # NOTE: Do not mix #write and direct #body access!
84
139
  #
85
- def write(str)
86
- s = str.to_s
87
- @length += s.bytesize unless chunked?
88
- @writer.call s
140
+ def write(chunk)
141
+ buffered_body!
89
142
 
90
- set_header(CONTENT_LENGTH, @length.to_s) unless chunked?
91
- str
143
+ @writer.call(chunk.to_s)
92
144
  end
93
145
 
94
146
  def close
95
- body.close if body.respond_to?(:close)
147
+ @body.close if @body.respond_to?(:close)
96
148
  end
97
149
 
98
150
  def empty?
99
151
  @block == nil && @body.empty?
100
152
  end
101
153
 
102
- def has_header?(key); headers.key? key; end
103
- def get_header(key); headers[key]; end
104
- def set_header(key, v); headers[key] = v; end
105
- def delete_header(key); headers.delete key; end
154
+ def has_header?(key)
155
+ raise ArgumentError unless key.is_a?(String)
156
+ @headers.key?(key)
157
+ end
158
+ def get_header(key)
159
+ raise ArgumentError unless key.is_a?(String)
160
+ @headers[key]
161
+ end
162
+ def set_header(key, value)
163
+ raise ArgumentError unless key.is_a?(String)
164
+ @headers[key] = value
165
+ end
166
+ def delete_header(key)
167
+ raise ArgumentError unless key.is_a?(String)
168
+ @headers.delete key
169
+ end
106
170
 
107
171
  alias :[] :get_header
108
172
  alias :[]= :set_header
@@ -126,38 +190,56 @@ module Rack
126
190
  def forbidden?; status == 403; end
127
191
  def not_found?; status == 404; end
128
192
  def method_not_allowed?; status == 405; end
193
+ def not_acceptable?; status == 406; end
194
+ def request_timeout?; status == 408; end
129
195
  def precondition_failed?; status == 412; end
130
196
  def unprocessable?; status == 422; end
131
197
 
132
198
  def redirect?; [301, 302, 303, 307, 308].include? status; end
133
199
 
134
200
  def include?(header)
135
- has_header? header
201
+ has_header?(header)
136
202
  end
137
203
 
138
204
  # Add a header that may have multiple values.
139
205
  #
140
206
  # Example:
141
- # response.add_header 'Vary', 'Accept-Encoding'
142
- # response.add_header 'Vary', 'Cookie'
207
+ # response.add_header 'vary', 'accept-encoding'
208
+ # response.add_header 'vary', 'cookie'
143
209
  #
144
- # assert_equal 'Accept-Encoding,Cookie', response.get_header('Vary')
210
+ # assert_equal 'accept-encoding,cookie', response.get_header('vary')
145
211
  #
146
212
  # http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2
147
- def add_header key, v
148
- if v.nil?
149
- get_header key
150
- elsif has_header? key
151
- set_header key, "#{get_header key},#{v}"
213
+ def add_header(key, value)
214
+ raise ArgumentError unless key.is_a?(String)
215
+
216
+ if value.nil?
217
+ return get_header(key)
218
+ end
219
+
220
+ value = value.to_s
221
+
222
+ if header = get_header(key)
223
+ if header.is_a?(Array)
224
+ header << value
225
+ else
226
+ set_header(key, [header, value])
227
+ end
152
228
  else
153
- set_header key, v
229
+ set_header(key, value)
154
230
  end
155
231
  end
156
232
 
233
+ # Get the content type of the response.
157
234
  def content_type
158
235
  get_header CONTENT_TYPE
159
236
  end
160
237
 
238
+ # Set the content type of the response.
239
+ def content_type=(content_type)
240
+ set_header CONTENT_TYPE, content_type
241
+ end
242
+
161
243
  def media_type
162
244
  MediaType.type(content_type)
163
245
  end
@@ -172,44 +254,104 @@ module Rack
172
254
  end
173
255
 
174
256
  def location
175
- get_header "Location"
257
+ get_header "location"
176
258
  end
177
259
 
178
260
  def location=(location)
179
- set_header "Location", location
261
+ set_header "location", location
180
262
  end
181
263
 
182
264
  def set_cookie(key, value)
183
- cookie_header = get_header SET_COOKIE
184
- set_header SET_COOKIE, ::Rack::Utils.add_cookie_to_header(cookie_header, key, value)
265
+ add_header SET_COOKIE, Utils.set_cookie_header(key, value)
185
266
  end
186
267
 
187
- def delete_cookie(key, value={})
188
- set_header SET_COOKIE, ::Rack::Utils.add_remove_cookie_to_header(get_header(SET_COOKIE), key, value)
268
+ def delete_cookie(key, value = {})
269
+ set_header(SET_COOKIE,
270
+ Utils.delete_set_cookie_header!(
271
+ get_header(SET_COOKIE), key, value
272
+ )
273
+ )
189
274
  end
190
275
 
191
276
  def set_cookie_header
192
277
  get_header SET_COOKIE
193
278
  end
194
279
 
195
- def set_cookie_header= v
196
- set_header SET_COOKIE, v
280
+ def set_cookie_header=(value)
281
+ set_header SET_COOKIE, value
197
282
  end
198
283
 
199
284
  def cache_control
200
285
  get_header CACHE_CONTROL
201
286
  end
202
287
 
203
- def cache_control= v
204
- set_header CACHE_CONTROL, v
288
+ def cache_control=(value)
289
+ set_header CACHE_CONTROL, value
290
+ end
291
+
292
+ # Specifies that the content shouldn't be cached. Overrides `cache!` if already called.
293
+ def do_not_cache!
294
+ set_header CACHE_CONTROL, "no-cache, must-revalidate"
295
+ set_header EXPIRES, Time.now.httpdate
296
+ end
297
+
298
+ # Specify that the content should be cached.
299
+ # @param duration [Integer] The number of seconds until the cache expires.
300
+ # @option directive [String] The cache control directive, one of "public", "private", "no-cache" or "no-store".
301
+ def cache!(duration = 3600, directive: "public")
302
+ unless headers[CACHE_CONTROL] =~ /no-cache/
303
+ set_header CACHE_CONTROL, "#{directive}, max-age=#{duration}"
304
+ set_header EXPIRES, (Time.now + duration).httpdate
305
+ end
205
306
  end
206
307
 
207
308
  def etag
208
309
  get_header ETAG
209
310
  end
210
311
 
211
- def etag= v
212
- set_header ETAG, v
312
+ def etag=(value)
313
+ set_header ETAG, value
314
+ end
315
+
316
+ protected
317
+
318
+ def buffered_body!
319
+ if @buffered.nil?
320
+ if @body.is_a?(Array)
321
+ # The user supplied body was an array:
322
+ @body = @body.compact
323
+ @body.each do |part|
324
+ @length += part.to_s.bytesize
325
+ end
326
+ elsif @body.respond_to?(:each)
327
+ # Turn the user supplied body into a buffered array:
328
+ body = @body
329
+ @body = Array.new
330
+
331
+ body.each do |part|
332
+ @writer.call(part.to_s)
333
+ end
334
+
335
+ body.close if body.respond_to?(:close)
336
+
337
+ @buffered = true
338
+ else
339
+ @buffered = false
340
+ end
341
+ end
342
+
343
+ return @buffered
344
+ end
345
+
346
+ def append(chunk)
347
+ @body << chunk
348
+
349
+ unless chunked?
350
+ @length += chunk.bytesize
351
+ set_header(CONTENT_LENGTH, @length.to_s)
352
+ end
353
+
354
+ return chunk
213
355
  end
214
356
  end
215
357
 
@@ -221,15 +363,26 @@ module Rack
221
363
  attr_reader :headers
222
364
  attr_accessor :status
223
365
 
224
- def initialize status, headers
366
+ def initialize(status, headers)
225
367
  @status = status
226
368
  @headers = headers
227
369
  end
228
370
 
229
- def has_header?(key); headers.key? key; end
230
- def get_header(key); headers[key]; end
231
- def set_header(key, v); headers[key] = v; end
232
- def delete_header(key); headers.delete key; end
371
+ def has_header?(key)
372
+ headers.key?(key)
373
+ end
374
+
375
+ def get_header(key)
376
+ headers[key]
377
+ end
378
+
379
+ def set_header(key, value)
380
+ headers[key] = value
381
+ end
382
+
383
+ def delete_header(key)
384
+ headers.delete(key)
385
+ end
233
386
  end
234
387
  end
235
388
  end
@@ -1,18 +1,31 @@
1
1
  # -*- encoding: binary -*-
2
+ # frozen_string_literal: true
3
+
2
4
  require 'tempfile'
3
- require 'rack/utils'
5
+
6
+ require_relative 'constants'
4
7
 
5
8
  module Rack
6
9
  # Class which can make any IO object rewindable, including non-rewindable ones. It does
7
10
  # this by buffering the data into a tempfile, which is rewindable.
8
11
  #
9
- # rack.input is required to be rewindable, so if your input stream IO is non-rewindable
10
- # by nature (e.g. a pipe or a socket) then you can wrap it in an object of this class
11
- # to easily make it rewindable.
12
- #
13
12
  # Don't forget to call #close when you're done. This frees up temporary resources that
14
13
  # RewindableInput uses, though it does *not* close the original IO object.
15
14
  class RewindableInput
15
+ # Makes rack.input rewindable, for compatibility with applications and middleware
16
+ # designed for earlier versions of Rack (where rack.input was required to be
17
+ # rewindable).
18
+ class Middleware
19
+ def initialize(app)
20
+ @app = app
21
+ end
22
+
23
+ def call(env)
24
+ env[RACK_INPUT] = RewindableInput.new(env[RACK_INPUT])
25
+ @app.call(env)
26
+ end
27
+ end
28
+
16
29
  def initialize(io)
17
30
  @io = io
18
31
  @rewindable_io = nil
@@ -39,8 +52,13 @@ module Rack
39
52
  @rewindable_io.rewind
40
53
  end
41
54
 
55
+ def size
56
+ make_rewindable unless @rewindable_io
57
+ @rewindable_io.size
58
+ end
59
+
42
60
  # Closes this RewindableInput object without closing the originally
43
- # wrapped IO oject. Cleans up any temporary resources that this RewindableInput
61
+ # wrapped IO object. Cleans up any temporary resources that this RewindableInput
44
62
  # has created.
45
63
  #
46
64
  # This method may be called multiple times. It does nothing on subsequent calls.
@@ -65,14 +83,16 @@ module Rack
65
83
  # access it because we have the file handle open.
66
84
  @rewindable_io = Tempfile.new('RackRewindableInput')
67
85
  @rewindable_io.chmod(0000)
68
- @rewindable_io.set_encoding(Encoding::BINARY) if @rewindable_io.respond_to?(:set_encoding)
86
+ @rewindable_io.set_encoding(Encoding::BINARY)
69
87
  @rewindable_io.binmode
88
+ # :nocov:
70
89
  if filesystem_has_posix_semantics?
71
90
  raise 'Unlink failed. IO closed.' if @rewindable_io.closed?
72
91
  @unlinked = true
73
92
  end
93
+ # :nocov:
74
94
 
75
- buffer = ""
95
+ buffer = "".dup
76
96
  while @io.read(1024 * 4, buffer)
77
97
  entire_buffer_written_out = false
78
98
  while !entire_buffer_written_out
data/lib/rack/runtime.rb CHANGED
@@ -1,32 +1,35 @@
1
- require 'rack/utils'
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'utils'
2
4
 
3
5
  module Rack
4
- # Sets an "X-Runtime" response header, indicating the response
6
+ # Sets an "x-runtime" response header, indicating the response
5
7
  # time of the request, in seconds
6
8
  #
7
9
  # You can put it right before the application to see the processing
8
10
  # time, or before all the other middlewares to include time for them,
9
11
  # too.
10
12
  class Runtime
11
- FORMAT_STRING = "%0.6f".freeze # :nodoc:
12
- HEADER_NAME = "X-Runtime".freeze # :nodoc:
13
+ FORMAT_STRING = "%0.6f" # :nodoc:
14
+ HEADER_NAME = "x-runtime" # :nodoc:
13
15
 
14
16
  def initialize(app, name = nil)
15
17
  @app = app
16
18
  @header_name = HEADER_NAME
17
- @header_name += "-#{name}" if name
19
+ @header_name += "-#{name.to_s.downcase}" if name
18
20
  end
19
21
 
20
22
  def call(env)
21
23
  start_time = Utils.clock_time
22
- status, headers, body = @app.call(env)
24
+ _, headers, _ = response = @app.call(env)
25
+
23
26
  request_time = Utils.clock_time - start_time
24
27
 
25
- unless headers.has_key?(@header_name)
28
+ unless headers.key?(@header_name)
26
29
  headers[@header_name] = FORMAT_STRING % request_time
27
30
  end
28
31
 
29
- [status, headers, body]
32
+ response
30
33
  end
31
34
  end
32
35
  end