rack 1.6.13 → 2.1.4.3

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