rack 1.6.11 → 2.2.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 (190) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +675 -0
  3. data/CONTRIBUTING.md +136 -0
  4. data/{COPYING → MIT-LICENSE} +4 -2
  5. data/README.rdoc +157 -163
  6. data/Rakefile +38 -32
  7. data/{SPEC → SPEC.rdoc} +41 -13
  8. data/bin/rackup +1 -0
  9. data/contrib/rack_logo.svg +164 -111
  10. data/example/lobster.ru +2 -0
  11. data/example/protectedlobster.rb +4 -2
  12. data/example/protectedlobster.ru +3 -1
  13. data/lib/rack/auth/abstract/handler.rb +3 -1
  14. data/lib/rack/auth/abstract/request.rb +6 -2
  15. data/lib/rack/auth/basic.rb +7 -4
  16. data/lib/rack/auth/digest/md5.rb +13 -11
  17. data/lib/rack/auth/digest/nonce.rb +6 -3
  18. data/lib/rack/auth/digest/params.rb +5 -4
  19. data/lib/rack/auth/digest/request.rb +6 -4
  20. data/lib/rack/body_proxy.rb +21 -15
  21. data/lib/rack/builder.rb +119 -26
  22. data/lib/rack/cascade.rb +28 -12
  23. data/lib/rack/chunked.rb +70 -22
  24. data/lib/rack/common_logger.rb +80 -0
  25. data/lib/rack/{conditionalget.rb → conditional_get.rb} +20 -16
  26. data/lib/rack/config.rb +2 -0
  27. data/lib/rack/content_length.rb +9 -8
  28. data/lib/rack/content_type.rb +5 -4
  29. data/lib/rack/core_ext/regexp.rb +14 -0
  30. data/lib/rack/deflater.rb +60 -70
  31. data/lib/rack/directory.rb +117 -85
  32. data/lib/rack/etag.rb +9 -7
  33. data/lib/rack/events.rb +153 -0
  34. data/lib/rack/file.rb +4 -149
  35. data/lib/rack/files.rb +218 -0
  36. data/lib/rack/handler/cgi.rb +17 -19
  37. data/lib/rack/handler/fastcgi.rb +17 -18
  38. data/lib/rack/handler/lsws.rb +14 -14
  39. data/lib/rack/handler/scgi.rb +22 -21
  40. data/lib/rack/handler/thin.rb +20 -11
  41. data/lib/rack/handler/webrick.rb +39 -32
  42. data/lib/rack/handler.rb +9 -26
  43. data/lib/rack/head.rb +16 -18
  44. data/lib/rack/lint.rb +110 -64
  45. data/lib/rack/lobster.rb +10 -10
  46. data/lib/rack/lock.rb +17 -11
  47. data/lib/rack/logger.rb +4 -2
  48. data/lib/rack/media_type.rb +43 -0
  49. data/lib/rack/{methodoverride.rb → method_override.rb} +10 -8
  50. data/lib/rack/mime.rb +27 -6
  51. data/lib/rack/mock.rb +124 -65
  52. data/lib/rack/multipart/generator.rb +20 -16
  53. data/lib/rack/multipart/parser.rb +273 -162
  54. data/lib/rack/multipart/uploaded_file.rb +15 -8
  55. data/lib/rack/multipart.rb +39 -8
  56. data/lib/rack/{nulllogger.rb → null_logger.rb} +3 -1
  57. data/lib/rack/query_parser.rb +217 -0
  58. data/lib/rack/recursive.rb +11 -9
  59. data/lib/rack/reloader.rb +8 -4
  60. data/lib/rack/request.rb +543 -305
  61. data/lib/rack/response.rb +244 -88
  62. data/lib/rack/rewindable_input.rb +5 -15
  63. data/lib/rack/runtime.rb +12 -18
  64. data/lib/rack/sendfile.rb +17 -15
  65. data/lib/rack/server.rb +125 -47
  66. data/lib/rack/session/abstract/id.rb +216 -93
  67. data/lib/rack/session/cookie.rb +47 -31
  68. data/lib/rack/session/memcache.rb +4 -87
  69. data/lib/rack/session/pool.rb +26 -17
  70. data/lib/rack/show_exceptions.rb +390 -0
  71. data/lib/rack/{showstatus.rb → show_status.rb} +8 -8
  72. data/lib/rack/static.rb +48 -11
  73. data/lib/rack/tempfile_reaper.rb +3 -3
  74. data/lib/rack/urlmap.rb +26 -19
  75. data/lib/rack/utils.rb +208 -294
  76. data/lib/rack/version.rb +29 -0
  77. data/lib/rack.rb +76 -33
  78. data/rack.gemspec +43 -30
  79. metadata +62 -183
  80. data/HISTORY.md +0 -375
  81. data/KNOWN-ISSUES +0 -44
  82. data/lib/rack/backports/uri/common_18.rb +0 -56
  83. data/lib/rack/backports/uri/common_192.rb +0 -52
  84. data/lib/rack/backports/uri/common_193.rb +0 -29
  85. data/lib/rack/commonlogger.rb +0 -72
  86. data/lib/rack/handler/evented_mongrel.rb +0 -8
  87. data/lib/rack/handler/mongrel.rb +0 -106
  88. data/lib/rack/handler/swiftiplied_mongrel.rb +0 -8
  89. data/lib/rack/showexceptions.rb +0 -387
  90. data/lib/rack/utils/okjson.rb +0 -600
  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 -8
  108. data/test/cgi/test.ru +0 -5
  109. data/test/gemloader.rb +0 -10
  110. data/test/multipart/bad_robots +0 -259
  111. data/test/multipart/binary +0 -0
  112. data/test/multipart/content_type_and_no_filename +0 -6
  113. data/test/multipart/empty +0 -10
  114. data/test/multipart/fail_16384_nofile +0 -814
  115. data/test/multipart/file1.txt +0 -1
  116. data/test/multipart/filename_and_modification_param +0 -7
  117. data/test/multipart/filename_and_no_name +0 -6
  118. data/test/multipart/filename_with_escaped_quotes +0 -6
  119. data/test/multipart/filename_with_escaped_quotes_and_modification_param +0 -7
  120. data/test/multipart/filename_with_null_byte +0 -7
  121. data/test/multipart/filename_with_percent_escaped_quotes +0 -6
  122. data/test/multipart/filename_with_unescaped_percentages +0 -6
  123. data/test/multipart/filename_with_unescaped_percentages2 +0 -6
  124. data/test/multipart/filename_with_unescaped_percentages3 +0 -6
  125. data/test/multipart/filename_with_unescaped_quotes +0 -6
  126. data/test/multipart/ie +0 -6
  127. data/test/multipart/invalid_character +0 -6
  128. data/test/multipart/mixed_files +0 -21
  129. data/test/multipart/nested +0 -10
  130. data/test/multipart/none +0 -9
  131. data/test/multipart/semicolon +0 -6
  132. data/test/multipart/text +0 -15
  133. data/test/multipart/three_files_three_fields +0 -31
  134. data/test/multipart/webkit +0 -32
  135. data/test/rackup/config.ru +0 -31
  136. data/test/registering_handler/rack/handler/registering_myself.rb +0 -8
  137. data/test/spec_auth_basic.rb +0 -81
  138. data/test/spec_auth_digest.rb +0 -259
  139. data/test/spec_body_proxy.rb +0 -85
  140. data/test/spec_builder.rb +0 -223
  141. data/test/spec_cascade.rb +0 -61
  142. data/test/spec_cgi.rb +0 -102
  143. data/test/spec_chunked.rb +0 -101
  144. data/test/spec_commonlogger.rb +0 -93
  145. data/test/spec_conditionalget.rb +0 -102
  146. data/test/spec_config.rb +0 -22
  147. data/test/spec_content_length.rb +0 -85
  148. data/test/spec_content_type.rb +0 -45
  149. data/test/spec_deflater.rb +0 -339
  150. data/test/spec_directory.rb +0 -88
  151. data/test/spec_etag.rb +0 -107
  152. data/test/spec_fastcgi.rb +0 -107
  153. data/test/spec_file.rb +0 -221
  154. data/test/spec_handler.rb +0 -72
  155. data/test/spec_head.rb +0 -45
  156. data/test/spec_lint.rb +0 -550
  157. data/test/spec_lobster.rb +0 -58
  158. data/test/spec_lock.rb +0 -164
  159. data/test/spec_logger.rb +0 -23
  160. data/test/spec_methodoverride.rb +0 -111
  161. data/test/spec_mime.rb +0 -51
  162. data/test/spec_mock.rb +0 -297
  163. data/test/spec_mongrel.rb +0 -182
  164. data/test/spec_multipart.rb +0 -600
  165. data/test/spec_nulllogger.rb +0 -20
  166. data/test/spec_recursive.rb +0 -72
  167. data/test/spec_request.rb +0 -1232
  168. data/test/spec_response.rb +0 -407
  169. data/test/spec_rewindable_input.rb +0 -118
  170. data/test/spec_runtime.rb +0 -49
  171. data/test/spec_sendfile.rb +0 -130
  172. data/test/spec_server.rb +0 -167
  173. data/test/spec_session_abstract_id.rb +0 -53
  174. data/test/spec_session_cookie.rb +0 -410
  175. data/test/spec_session_memcache.rb +0 -321
  176. data/test/spec_session_pool.rb +0 -209
  177. data/test/spec_showexceptions.rb +0 -98
  178. data/test/spec_showstatus.rb +0 -103
  179. data/test/spec_static.rb +0 -145
  180. data/test/spec_tempfile_reaper.rb +0 -63
  181. data/test/spec_thin.rb +0 -91
  182. data/test/spec_urlmap.rb +0 -236
  183. data/test/spec_utils.rb +0 -647
  184. data/test/spec_version.rb +0 -17
  185. data/test/spec_webrick.rb +0 -184
  186. data/test/static/another/index.html +0 -1
  187. data/test/static/index.html +0 -1
  188. data/test/testrequest.rb +0 -78
  189. data/test/unregistered_handler/rack/handler/unregistered.rb +0 -7
  190. data/test/unregistered_handler/rack/handler/unregistered_long_one.rb +0 -7
data/lib/rack/response.rb CHANGED
@@ -1,6 +1,5 @@
1
- require 'rack/request'
2
- require 'rack/utils'
3
- require 'rack/body_proxy'
1
+ # frozen_string_literal: true
2
+
4
3
  require 'time'
5
4
 
6
5
  module Rack
@@ -8,7 +7,7 @@ module Rack
8
7
  # response.
9
8
  #
10
9
  # It allows setting of headers and cookies, and provides useful
11
- # defaults (a OK response containing HTML).
10
+ # defaults (an OK response with empty headers and body).
12
11
  #
13
12
  # You can use Response#write to iteratively generate your response,
14
13
  # but note that this is buffered by Rack::Response until you call
@@ -16,147 +15,304 @@ module Rack
16
15
  # +write+ are synchronous with the Rack response.
17
16
  #
18
17
  # Your application's +call+ should end returning Response#finish.
19
-
20
18
  class Response
21
- attr_accessor :length
19
+ def self.[](status, headers, body)
20
+ self.new(body, status, headers)
21
+ end
22
22
 
23
- CHUNKED = 'chunked'.freeze
24
- TRANSFER_ENCODING = 'Transfer-Encoding'.freeze
25
- def initialize(body=[], status=200, header={})
26
- @status = status.to_i
27
- @header = Utils::HeaderHash.new.merge(header)
23
+ CHUNKED = 'chunked'
24
+ STATUS_WITH_NO_ENTITY_BODY = Utils::STATUS_WITH_NO_ENTITY_BODY
28
25
 
29
- @chunked = CHUNKED == @header[TRANSFER_ENCODING]
30
- @writer = lambda { |x| @body << x }
31
- @block = nil
32
- @length = 0
26
+ attr_accessor :length, :status, :body
27
+ attr_reader :headers
33
28
 
34
- @body = []
29
+ # @deprecated Use {#headers} instead.
30
+ alias header headers
35
31
 
36
- if body.respond_to? :to_str
37
- write body.to_str
38
- elsif body.respond_to?(:each)
39
- body.each { |part|
40
- write part.to_s
41
- }
42
- else
43
- raise TypeError, "stringable or iterable required"
44
- end
45
-
46
- yield self if block_given?
47
- end
32
+ # Initialize the response object with the specified body, status
33
+ # and headers.
34
+ #
35
+ # @param body [nil, #each, #to_str] the response body.
36
+ # @param status [Integer] the integer status as defined by the
37
+ # HTTP protocol RFCs.
38
+ # @param headers [#each] a list of key-value header pairs which
39
+ # conform to the HTTP protocol RFCs.
40
+ #
41
+ # Providing a body which responds to #to_str is legacy behaviour.
42
+ def initialize(body = nil, status = 200, headers = {})
43
+ @status = status.to_i
44
+ @headers = Utils::HeaderHash[headers]
48
45
 
49
- attr_reader :header
50
- attr_accessor :status, :body
46
+ @writer = self.method(:append)
51
47
 
52
- def [](key)
53
- header[key]
54
- end
48
+ @block = nil
55
49
 
56
- def []=(key, value)
57
- header[key] = value
58
- end
50
+ # Keep track of whether we have expanded the user supplied body.
51
+ if body.nil?
52
+ @body = []
53
+ @buffered = true
54
+ @length = 0
55
+ elsif body.respond_to?(:to_str)
56
+ @body = [body]
57
+ @buffered = true
58
+ @length = body.to_str.bytesize
59
+ else
60
+ @body = body
61
+ @buffered = false
62
+ @length = 0
63
+ end
59
64
 
60
- def set_cookie(key, value)
61
- Utils.set_cookie_header!(header, key, value)
65
+ yield self if block_given?
62
66
  end
63
67
 
64
- def delete_cookie(key, value={})
65
- Utils.delete_cookie_header!(header, key, value)
68
+ def redirect(target, status = 302)
69
+ self.status = status
70
+ self.location = target
66
71
  end
67
72
 
68
- def redirect(target, status=302)
69
- self.status = status
70
- self["Location"] = target
73
+ def chunked?
74
+ CHUNKED == get_header(TRANSFER_ENCODING)
71
75
  end
72
76
 
77
+ # Generate a response array consistent with the requirements of the SPEC.
78
+ # @return [Array] a 3-tuple suitable of `[status, headers, body]`
79
+ # which is suitable to be returned from the middleware `#call(env)` method.
73
80
  def finish(&block)
74
- @block = block
75
-
76
- if [204, 205, 304].include?(status.to_i)
77
- header.delete CONTENT_TYPE
78
- header.delete CONTENT_LENGTH
81
+ if STATUS_WITH_NO_ENTITY_BODY[status.to_i]
82
+ delete_header CONTENT_TYPE
83
+ delete_header CONTENT_LENGTH
79
84
  close
80
- [status.to_i, header, []]
85
+ return [@status, @headers, []]
81
86
  else
82
- [status.to_i, header, BodyProxy.new(self){}]
87
+ if block_given?
88
+ @block = block
89
+ return [@status, @headers, self]
90
+ else
91
+ return [@status, @headers, @body]
92
+ end
83
93
  end
84
94
  end
95
+
85
96
  alias to_a finish # For *response
86
- alias to_ary finish # For implicit-splat on Ruby 1.9.2
87
97
 
88
98
  def each(&callback)
89
99
  @body.each(&callback)
90
- @writer = callback
91
- @block.call(self) if @block
100
+ @buffered = true
101
+
102
+ if @block
103
+ @writer = callback
104
+ @block.call(self)
105
+ end
92
106
  end
93
107
 
94
108
  # Append to body and update Content-Length.
95
109
  #
96
110
  # NOTE: Do not mix #write and direct #body access!
97
111
  #
98
- def write(str)
99
- s = str.to_s
100
- @length += Rack::Utils.bytesize(s) unless @chunked
101
- @writer.call s
112
+ def write(chunk)
113
+ buffered_body!
102
114
 
103
- header[CONTENT_LENGTH] = @length.to_s unless @chunked
104
- str
115
+ @writer.call(chunk.to_s)
105
116
  end
106
117
 
107
118
  def close
108
- body.close if body.respond_to?(:close)
119
+ @body.close if @body.respond_to?(:close)
109
120
  end
110
121
 
111
122
  def empty?
112
123
  @block == nil && @body.empty?
113
124
  end
114
125
 
115
- alias headers header
126
+ def has_header?(key); headers.key? key; end
127
+ def get_header(key); headers[key]; end
128
+ def set_header(key, v); headers[key] = v; end
129
+ def delete_header(key); headers.delete key; end
130
+
131
+ alias :[] :get_header
132
+ alias :[]= :set_header
116
133
 
117
134
  module Helpers
118
- def invalid?; status < 100 || status >= 600; end
119
-
120
- def informational?; status >= 100 && status < 200; end
121
- def successful?; status >= 200 && status < 300; end
122
- def redirection?; status >= 300 && status < 400; end
123
- def client_error?; status >= 400 && status < 500; end
124
- def server_error?; status >= 500 && status < 600; end
125
-
126
- def ok?; status == 200; end
127
- def created?; status == 201; end
128
- def accepted?; status == 202; end
129
- def bad_request?; status == 400; end
130
- def unauthorized?; status == 401; end
131
- def forbidden?; status == 403; end
132
- def not_found?; status == 404; end
133
- def method_not_allowed?; status == 405; end
134
- def i_m_a_teapot?; status == 418; end
135
- def unprocessable?; status == 422; end
136
-
137
- def redirect?; [301, 302, 303, 307].include? status; end
138
-
139
- # Headers
140
- attr_reader :headers, :original_headers
135
+ def invalid?; status < 100 || status >= 600; end
136
+
137
+ def informational?; status >= 100 && status < 200; end
138
+ def successful?; status >= 200 && status < 300; end
139
+ def redirection?; status >= 300 && status < 400; end
140
+ def client_error?; status >= 400 && status < 500; end
141
+ def server_error?; status >= 500 && status < 600; end
142
+
143
+ def ok?; status == 200; end
144
+ def created?; status == 201; end
145
+ def accepted?; status == 202; end
146
+ def no_content?; status == 204; end
147
+ def moved_permanently?; status == 301; end
148
+ def bad_request?; status == 400; end
149
+ def unauthorized?; status == 401; end
150
+ def forbidden?; status == 403; end
151
+ def not_found?; status == 404; end
152
+ def method_not_allowed?; status == 405; end
153
+ def precondition_failed?; status == 412; end
154
+ def unprocessable?; status == 422; end
155
+
156
+ def redirect?; [301, 302, 303, 307, 308].include? status; end
141
157
 
142
158
  def include?(header)
143
- !!headers[header]
159
+ has_header? header
160
+ end
161
+
162
+ # Add a header that may have multiple values.
163
+ #
164
+ # Example:
165
+ # response.add_header 'Vary', 'Accept-Encoding'
166
+ # response.add_header 'Vary', 'Cookie'
167
+ #
168
+ # assert_equal 'Accept-Encoding,Cookie', response.get_header('Vary')
169
+ #
170
+ # http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2
171
+ def add_header(key, v)
172
+ if v.nil?
173
+ get_header key
174
+ elsif has_header? key
175
+ set_header key, "#{get_header key},#{v}"
176
+ else
177
+ set_header key, v
178
+ end
144
179
  end
145
180
 
181
+ # Get the content type of the response.
146
182
  def content_type
147
- headers[CONTENT_TYPE]
183
+ get_header CONTENT_TYPE
184
+ end
185
+
186
+ # Set the content type of the response.
187
+ def content_type=(content_type)
188
+ set_header CONTENT_TYPE, content_type
189
+ end
190
+
191
+ def media_type
192
+ MediaType.type(content_type)
193
+ end
194
+
195
+ def media_type_params
196
+ MediaType.params(content_type)
148
197
  end
149
198
 
150
199
  def content_length
151
- cl = headers[CONTENT_LENGTH]
200
+ cl = get_header CONTENT_LENGTH
152
201
  cl ? cl.to_i : cl
153
202
  end
154
203
 
155
204
  def location
156
- headers["Location"]
205
+ get_header "Location"
206
+ end
207
+
208
+ def location=(location)
209
+ set_header "Location", location
210
+ end
211
+
212
+ def set_cookie(key, value)
213
+ cookie_header = get_header SET_COOKIE
214
+ set_header SET_COOKIE, ::Rack::Utils.add_cookie_to_header(cookie_header, key, value)
215
+ end
216
+
217
+ def delete_cookie(key, value = {})
218
+ set_header SET_COOKIE, ::Rack::Utils.add_remove_cookie_to_header(get_header(SET_COOKIE), key, value)
219
+ end
220
+
221
+ def set_cookie_header
222
+ get_header SET_COOKIE
223
+ end
224
+
225
+ def set_cookie_header=(v)
226
+ set_header SET_COOKIE, v
227
+ end
228
+
229
+ def cache_control
230
+ get_header CACHE_CONTROL
231
+ end
232
+
233
+ def cache_control=(v)
234
+ set_header CACHE_CONTROL, v
235
+ end
236
+
237
+ # Specifies that the content shouldn't be cached. Overrides `cache!` if already called.
238
+ def do_not_cache!
239
+ set_header CACHE_CONTROL, "no-cache, must-revalidate"
240
+ set_header EXPIRES, Time.now.httpdate
241
+ end
242
+
243
+ # Specify that the content should be cached.
244
+ # @param duration [Integer] The number of seconds until the cache expires.
245
+ # @option directive [String] The cache control directive, one of "public", "private", "no-cache" or "no-store".
246
+ def cache!(duration = 3600, directive: "public")
247
+ unless headers[CACHE_CONTROL] =~ /no-cache/
248
+ set_header CACHE_CONTROL, "#{directive}, max-age=#{duration}"
249
+ set_header EXPIRES, (Time.now + duration).httpdate
250
+ end
251
+ end
252
+
253
+ def etag
254
+ get_header ETAG
255
+ end
256
+
257
+ def etag=(v)
258
+ set_header ETAG, v
259
+ end
260
+
261
+ protected
262
+
263
+ def buffered_body!
264
+ return if @buffered
265
+
266
+ if @body.is_a?(Array)
267
+ # The user supplied body was an array:
268
+ @body = @body.compact
269
+ @body.each do |part|
270
+ @length += part.to_s.bytesize
271
+ end
272
+ else
273
+ # Turn the user supplied body into a buffered array:
274
+ body = @body
275
+ @body = Array.new
276
+
277
+ body.each do |part|
278
+ @writer.call(part.to_s)
279
+ end
280
+
281
+ body.close if body.respond_to?(:close)
282
+ end
283
+
284
+ @buffered = true
285
+ end
286
+
287
+ def append(chunk)
288
+ @body << chunk
289
+
290
+ unless chunked?
291
+ @length += chunk.bytesize
292
+ set_header(CONTENT_LENGTH, @length.to_s)
293
+ end
294
+
295
+ return chunk
157
296
  end
158
297
  end
159
298
 
160
299
  include Helpers
300
+
301
+ class Raw
302
+ include Helpers
303
+
304
+ attr_reader :headers
305
+ attr_accessor :status
306
+
307
+ def initialize(status, headers)
308
+ @status = status
309
+ @headers = headers
310
+ end
311
+
312
+ def has_header?(key); headers.key? key; end
313
+ def get_header(key); headers[key]; end
314
+ def set_header(key, v); headers[key] = v; end
315
+ def delete_header(key); headers.delete key; end
316
+ end
161
317
  end
162
318
  end
@@ -1,6 +1,7 @@
1
1
  # -*- encoding: binary -*-
2
+ # frozen_string_literal: true
3
+
2
4
  require 'tempfile'
3
- require 'rack/utils'
4
5
 
5
6
  module Rack
6
7
  # Class which can make any IO object rewindable, including non-rewindable ones. It does
@@ -40,7 +41,7 @@ module Rack
40
41
  end
41
42
 
42
43
  # Closes this RewindableInput object without closing the originally
43
- # wrapped IO oject. Cleans up any temporary resources that this RewindableInput
44
+ # wrapped IO object. Cleans up any temporary resources that this RewindableInput
44
45
  # has created.
45
46
  #
46
47
  # This method may be called multiple times. It does nothing on subsequent calls.
@@ -57,15 +58,6 @@ module Rack
57
58
 
58
59
  private
59
60
 
60
- # Ruby's Tempfile class has a bug. Subclass it and fix it.
61
- class Tempfile < ::Tempfile
62
- def _close
63
- @tmpfile.close if @tmpfile
64
- @data[1] = nil if @data
65
- @tmpfile = nil
66
- end
67
- end
68
-
69
61
  def make_rewindable
70
62
  # Buffer all data into a tempfile. Since this tempfile is private to this
71
63
  # RewindableInput object, we chmod it so that nobody else can read or write
@@ -77,18 +69,16 @@ module Rack
77
69
  @rewindable_io.set_encoding(Encoding::BINARY) if @rewindable_io.respond_to?(:set_encoding)
78
70
  @rewindable_io.binmode
79
71
  if filesystem_has_posix_semantics?
80
- # Use ::File.unlink as 1.9.1 Tempfile has a bug where unlink closes the file!
81
- ::File.unlink @rewindable_io.path
82
72
  raise 'Unlink failed. IO closed.' if @rewindable_io.closed?
83
73
  @unlinked = true
84
74
  end
85
75
 
86
- buffer = ""
76
+ buffer = "".dup
87
77
  while @io.read(1024 * 4, buffer)
88
78
  entire_buffer_written_out = false
89
79
  while !entire_buffer_written_out
90
80
  written = @rewindable_io.write(buffer)
91
- entire_buffer_written_out = written == Rack::Utils.bytesize(buffer)
81
+ entire_buffer_written_out = written == buffer.bytesize
92
82
  if !entire_buffer_written_out
93
83
  buffer.slice!(0 .. written - 1)
94
84
  end
data/lib/rack/runtime.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Rack
2
4
  # Sets an "X-Runtime" response header, indicating the response
3
5
  # time of the request, in seconds
@@ -6,35 +8,27 @@ module Rack
6
8
  # time, or before all the other middlewares to include time for them,
7
9
  # too.
8
10
  class Runtime
11
+ FORMAT_STRING = "%0.6f" # :nodoc:
12
+ HEADER_NAME = "X-Runtime" # :nodoc:
13
+
9
14
  def initialize(app, name = nil)
10
15
  @app = app
11
- @header_name = "X-Runtime"
12
- @header_name << "-#{name}" if name
16
+ @header_name = HEADER_NAME
17
+ @header_name += "-#{name}" if name
13
18
  end
14
19
 
15
- FORMAT_STRING = "%0.6f"
16
20
  def call(env)
17
- start_time = clock_time
21
+ start_time = Utils.clock_time
18
22
  status, headers, body = @app.call(env)
19
- request_time = clock_time - start_time
23
+ headers = Utils::HeaderHash[headers]
20
24
 
21
- if !headers.has_key?(@header_name)
25
+ request_time = Utils.clock_time - start_time
26
+
27
+ unless headers.key?(@header_name)
22
28
  headers[@header_name] = FORMAT_STRING % request_time
23
29
  end
24
30
 
25
31
  [status, headers, body]
26
32
  end
27
-
28
- private
29
-
30
- if defined?(Process::CLOCK_MONOTONIC)
31
- def clock_time
32
- Process.clock_gettime(Process::CLOCK_MONOTONIC)
33
- end
34
- else
35
- def clock_time
36
- Time.now.to_f
37
- end
38
- end
39
33
  end
40
34
  end
data/lib/rack/sendfile.rb CHANGED
@@ -1,5 +1,4 @@
1
- require 'rack/file'
2
- require 'rack/body_proxy'
1
+ # frozen_string_literal: true
3
2
 
4
3
  module Rack
5
4
 
@@ -14,7 +13,7 @@ module Rack
14
13
  #
15
14
  # In order to take advantage of this middleware, the response body must
16
15
  # respond to +to_path+ and the request must include an X-Sendfile-Type
17
- # header. Rack::File and other components implement +to_path+ so there's
16
+ # header. Rack::Files and other components implement +to_path+ so there's
18
17
  # rarely anything you need to do in your application. The X-Sendfile-Type
19
18
  # header is typically set in your web servers configuration. The following
20
19
  # sections attempt to document
@@ -53,7 +52,7 @@ module Rack
53
52
  # that it maps to. The middleware performs a simple substitution on the
54
53
  # resulting path.
55
54
  #
56
- # See Also: http://wiki.codemongers.com/NginxXSendfile
55
+ # See Also: https://www.nginx.com/resources/wiki/start/topics/examples/xsendfile
57
56
  #
58
57
  # === lighttpd
59
58
  #
@@ -99,9 +98,7 @@ module Rack
99
98
  # will be matched with case indifference.
100
99
 
101
100
  class Sendfile
102
- F = ::File
103
-
104
- def initialize(app, variation=nil, mappings=[])
101
+ def initialize(app, variation = nil, mappings = [])
105
102
  @app = app
106
103
  @variation = variation
107
104
  @mappings = mappings.map do |internal, external|
@@ -114,19 +111,20 @@ module Rack
114
111
  if body.respond_to?(:to_path)
115
112
  case type = variation(env)
116
113
  when 'X-Accel-Redirect'
117
- path = F.expand_path(body.to_path)
114
+ path = ::File.expand_path(body.to_path)
118
115
  if url = map_accel_path(env, path)
119
116
  headers[CONTENT_LENGTH] = '0'
120
- headers[type] = url
117
+ # '?' must be percent-encoded because it is not query string but a part of path
118
+ headers[type] = ::Rack::Utils.escape_path(url).gsub('?', '%3F')
121
119
  obody = body
122
120
  body = Rack::BodyProxy.new([]) do
123
121
  obody.close if obody.respond_to?(:close)
124
122
  end
125
123
  else
126
- env['rack.errors'].puts "X-Accel-Mapping header missing"
124
+ env[RACK_ERRORS].puts "X-Accel-Mapping header missing"
127
125
  end
128
126
  when 'X-Sendfile', 'X-Lighttpd-Send-File'
129
- path = F.expand_path(body.to_path)
127
+ path = ::File.expand_path(body.to_path)
130
128
  headers[CONTENT_LENGTH] = '0'
131
129
  headers[type] = path
132
130
  obody = body
@@ -135,7 +133,7 @@ module Rack
135
133
  end
136
134
  when '', nil
137
135
  else
138
- env['rack.errors'].puts "Unknown x-sendfile variation: '#{type}'.\n"
136
+ env[RACK_ERRORS].puts "Unknown x-sendfile variation: '#{type}'.\n"
139
137
  end
140
138
  end
141
139
  [status, headers, body]
@@ -149,11 +147,15 @@ module Rack
149
147
  end
150
148
 
151
149
  def map_accel_path(env, path)
152
- if mapping = @mappings.find { |internal,_| internal =~ path }
150
+ if mapping = @mappings.find { |internal, _| internal =~ path }
153
151
  path.sub(*mapping)
154
152
  elsif mapping = env['HTTP_X_ACCEL_MAPPING']
155
- internal, external = mapping.split('=', 2).map{ |p| p.strip }
156
- path.sub(/^#{internal}/i, external)
153
+ mapping.split(',').map(&:strip).each do |m|
154
+ internal, external = m.split('=', 2).map(&:strip)
155
+ new_path = path.sub(/^#{internal}/i, external)
156
+ return new_path unless path == new_path
157
+ end
158
+ path
157
159
  end
158
160
  end
159
161
  end