rack 2.2.8.1 → 3.0.9.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (86) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +213 -83
  3. data/CONTRIBUTING.md +53 -47
  4. data/MIT-LICENSE +1 -1
  5. data/README.md +309 -0
  6. data/SPEC.rdoc +174 -126
  7. data/lib/rack/auth/abstract/handler.rb +3 -1
  8. data/lib/rack/auth/abstract/request.rb +3 -1
  9. data/lib/rack/auth/basic.rb +0 -2
  10. data/lib/rack/auth/digest/md5.rb +1 -131
  11. data/lib/rack/auth/digest/nonce.rb +1 -54
  12. data/lib/rack/auth/digest/params.rb +1 -54
  13. data/lib/rack/auth/digest/request.rb +1 -43
  14. data/lib/rack/auth/digest.rb +256 -0
  15. data/lib/rack/body_proxy.rb +3 -1
  16. data/lib/rack/builder.rb +83 -63
  17. data/lib/rack/cascade.rb +2 -0
  18. data/lib/rack/chunked.rb +16 -13
  19. data/lib/rack/common_logger.rb +23 -18
  20. data/lib/rack/conditional_get.rb +18 -15
  21. data/lib/rack/constants.rb +64 -0
  22. data/lib/rack/content_length.rb +12 -16
  23. data/lib/rack/content_type.rb +8 -5
  24. data/lib/rack/deflater.rb +40 -26
  25. data/lib/rack/directory.rb +9 -3
  26. data/lib/rack/etag.rb +14 -23
  27. data/lib/rack/events.rb +4 -0
  28. data/lib/rack/file.rb +2 -0
  29. data/lib/rack/files.rb +15 -17
  30. data/lib/rack/head.rb +9 -8
  31. data/lib/rack/headers.rb +154 -0
  32. data/lib/rack/lint.rb +758 -646
  33. data/lib/rack/lock.rb +2 -5
  34. data/lib/rack/logger.rb +2 -0
  35. data/lib/rack/method_override.rb +5 -1
  36. data/lib/rack/mime.rb +8 -0
  37. data/lib/rack/mock.rb +1 -271
  38. data/lib/rack/mock_request.rb +166 -0
  39. data/lib/rack/mock_response.rb +126 -0
  40. data/lib/rack/multipart/generator.rb +7 -5
  41. data/lib/rack/multipart/parser.rb +120 -64
  42. data/lib/rack/multipart/uploaded_file.rb +4 -0
  43. data/lib/rack/multipart.rb +20 -40
  44. data/lib/rack/null_logger.rb +9 -0
  45. data/lib/rack/query_parser.rb +78 -46
  46. data/lib/rack/recursive.rb +2 -0
  47. data/lib/rack/reloader.rb +0 -2
  48. data/lib/rack/request.rb +224 -106
  49. data/lib/rack/response.rb +138 -61
  50. data/lib/rack/rewindable_input.rb +24 -5
  51. data/lib/rack/runtime.rb +7 -6
  52. data/lib/rack/sendfile.rb +30 -25
  53. data/lib/rack/show_exceptions.rb +15 -2
  54. data/lib/rack/show_status.rb +17 -7
  55. data/lib/rack/static.rb +8 -8
  56. data/lib/rack/tempfile_reaper.rb +15 -4
  57. data/lib/rack/urlmap.rb +3 -1
  58. data/lib/rack/utils.rb +203 -176
  59. data/lib/rack/version.rb +9 -4
  60. data/lib/rack.rb +6 -76
  61. metadata +13 -33
  62. data/README.rdoc +0 -320
  63. data/Rakefile +0 -130
  64. data/bin/rackup +0 -5
  65. data/contrib/rack.png +0 -0
  66. data/contrib/rack.svg +0 -150
  67. data/contrib/rack_logo.svg +0 -164
  68. data/contrib/rdoc.css +0 -412
  69. data/example/lobster.ru +0 -6
  70. data/example/protectedlobster.rb +0 -16
  71. data/example/protectedlobster.ru +0 -10
  72. data/lib/rack/core_ext/regexp.rb +0 -14
  73. data/lib/rack/handler/cgi.rb +0 -59
  74. data/lib/rack/handler/fastcgi.rb +0 -100
  75. data/lib/rack/handler/lsws.rb +0 -61
  76. data/lib/rack/handler/scgi.rb +0 -71
  77. data/lib/rack/handler/thin.rb +0 -36
  78. data/lib/rack/handler/webrick.rb +0 -129
  79. data/lib/rack/handler.rb +0 -104
  80. data/lib/rack/lobster.rb +0 -70
  81. data/lib/rack/server.rb +0 -466
  82. data/lib/rack/session/abstract/id.rb +0 -523
  83. data/lib/rack/session/cookie.rb +0 -204
  84. data/lib/rack/session/memcache.rb +0 -10
  85. data/lib/rack/session/pool.rb +0 -85
  86. data/rack.gemspec +0 -46
data/lib/rack/response.rb CHANGED
@@ -2,6 +2,11 @@
2
2
 
3
3
  require 'time'
4
4
 
5
+ require_relative 'constants'
6
+ require_relative 'utils'
7
+ require_relative 'media_type'
8
+ require_relative 'headers'
9
+
5
10
  module Rack
6
11
  # Rack::Response provides a convenient interface to create a Rack
7
12
  # response.
@@ -26,22 +31,45 @@ module Rack
26
31
  attr_accessor :length, :status, :body
27
32
  attr_reader :headers
28
33
 
29
- # @deprecated Use {#headers} instead.
30
- alias header headers
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
37
+
38
+ headers
39
+ end
31
40
 
32
- # Initialize the response object with the specified body, status
33
- # and headers.
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.
34
50
  #
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.
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).
40
54
  #
41
- # Providing a body which responds to #to_str is legacy behaviour.
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.
42
61
  def initialize(body = nil, status = 200, headers = {})
43
62
  @status = status.to_i
44
- @headers = Utils::HeaderHash[headers]
63
+
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
67
+
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
45
73
 
46
74
  @writer = self.method(:append)
47
75
 
@@ -58,7 +86,7 @@ module Rack
58
86
  @length = body.to_str.bytesize
59
87
  else
60
88
  @body = body
61
- @buffered = false
89
+ @buffered = nil # undetermined as of yet.
62
90
  @length = 0
63
91
  end
64
92
 
@@ -74,11 +102,16 @@ module Rack
74
102
  CHUNKED == get_header(TRANSFER_ENCODING)
75
103
  end
76
104
 
105
+ def no_entity_body?
106
+ # The response body is an enumerable body and it is not allowed to have an entity body.
107
+ @body.respond_to?(:each) && STATUS_WITH_NO_ENTITY_BODY[@status]
108
+ end
109
+
77
110
  # Generate a response array consistent with the requirements of the SPEC.
78
111
  # @return [Array] a 3-tuple suitable of `[status, headers, body]`
79
112
  # which is suitable to be returned from the middleware `#call(env)` method.
80
113
  def finish(&block)
81
- if STATUS_WITH_NO_ENTITY_BODY[status.to_i]
114
+ if no_entity_body?
82
115
  delete_header CONTENT_TYPE
83
116
  delete_header CONTENT_LENGTH
84
117
  close
@@ -105,7 +138,7 @@ module Rack
105
138
  end
106
139
  end
107
140
 
108
- # Append to body and update Content-Length.
141
+ # Append to body and update content-length.
109
142
  #
110
143
  # NOTE: Do not mix #write and direct #body access!
111
144
  #
@@ -123,10 +156,22 @@ module Rack
123
156
  @block == nil && @body.empty?
124
157
  end
125
158
 
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
159
+ def has_header?(key)
160
+ raise ArgumentError unless key.is_a?(String)
161
+ @headers.key?(key)
162
+ end
163
+ def get_header(key)
164
+ raise ArgumentError unless key.is_a?(String)
165
+ @headers[key]
166
+ end
167
+ def set_header(key, value)
168
+ raise ArgumentError unless key.is_a?(String)
169
+ @headers[key] = value
170
+ end
171
+ def delete_header(key)
172
+ raise ArgumentError unless key.is_a?(String)
173
+ @headers.delete key
174
+ end
130
175
 
131
176
  alias :[] :get_header
132
177
  alias :[]= :set_header
@@ -150,31 +195,43 @@ module Rack
150
195
  def forbidden?; status == 403; end
151
196
  def not_found?; status == 404; end
152
197
  def method_not_allowed?; status == 405; end
198
+ def not_acceptable?; status == 406; end
199
+ def request_timeout?; status == 408; end
153
200
  def precondition_failed?; status == 412; end
154
201
  def unprocessable?; status == 422; end
155
202
 
156
203
  def redirect?; [301, 302, 303, 307, 308].include? status; end
157
204
 
158
205
  def include?(header)
159
- has_header? header
206
+ has_header?(header)
160
207
  end
161
208
 
162
209
  # Add a header that may have multiple values.
163
210
  #
164
211
  # Example:
165
- # response.add_header 'Vary', 'Accept-Encoding'
166
- # response.add_header 'Vary', 'Cookie'
212
+ # response.add_header 'vary', 'accept-encoding'
213
+ # response.add_header 'vary', 'cookie'
167
214
  #
168
- # assert_equal 'Accept-Encoding,Cookie', response.get_header('Vary')
215
+ # assert_equal 'accept-encoding,cookie', response.get_header('vary')
169
216
  #
170
217
  # 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}"
218
+ def add_header(key, value)
219
+ raise ArgumentError unless key.is_a?(String)
220
+
221
+ if value.nil?
222
+ return get_header(key)
223
+ end
224
+
225
+ value = value.to_s
226
+
227
+ if header = get_header(key)
228
+ if header.is_a?(Array)
229
+ header << value
230
+ else
231
+ set_header(key, [header, value])
232
+ end
176
233
  else
177
- set_header key, v
234
+ set_header(key, value)
178
235
  end
179
236
  end
180
237
 
@@ -202,36 +259,39 @@ module Rack
202
259
  end
203
260
 
204
261
  def location
205
- get_header "Location"
262
+ get_header "location"
206
263
  end
207
264
 
208
265
  def location=(location)
209
- set_header "Location", location
266
+ set_header "location", location
210
267
  end
211
268
 
212
269
  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)
270
+ add_header SET_COOKIE, Utils.set_cookie_header(key, value)
215
271
  end
216
272
 
217
273
  def delete_cookie(key, value = {})
218
- set_header SET_COOKIE, ::Rack::Utils.add_remove_cookie_to_header(get_header(SET_COOKIE), key, value)
274
+ set_header(SET_COOKIE,
275
+ Utils.delete_set_cookie_header!(
276
+ get_header(SET_COOKIE), key, value
277
+ )
278
+ )
219
279
  end
220
280
 
221
281
  def set_cookie_header
222
282
  get_header SET_COOKIE
223
283
  end
224
284
 
225
- def set_cookie_header=(v)
226
- set_header SET_COOKIE, v
285
+ def set_cookie_header=(value)
286
+ set_header SET_COOKIE, value
227
287
  end
228
288
 
229
289
  def cache_control
230
290
  get_header CACHE_CONTROL
231
291
  end
232
292
 
233
- def cache_control=(v)
234
- set_header CACHE_CONTROL, v
293
+ def cache_control=(value)
294
+ set_header CACHE_CONTROL, value
235
295
  end
236
296
 
237
297
  # Specifies that the content shouldn't be cached. Overrides `cache!` if already called.
@@ -254,34 +314,40 @@ module Rack
254
314
  get_header ETAG
255
315
  end
256
316
 
257
- def etag=(v)
258
- set_header ETAG, v
317
+ def etag=(value)
318
+ set_header ETAG, value
259
319
  end
260
320
 
261
321
  protected
262
322
 
263
323
  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
324
+ if @buffered.nil?
325
+ if @body.is_a?(Array)
326
+ # The user supplied body was an array:
327
+ @body = @body.compact
328
+ @body.each do |part|
329
+ @length += part.to_s.bytesize
330
+ end
331
+
332
+ @buffered = true
333
+ elsif @body.respond_to?(:each)
334
+ # Turn the user supplied body into a buffered array:
335
+ body = @body
336
+ @body = Array.new
337
+
338
+ body.each do |part|
339
+ @writer.call(part.to_s)
340
+ end
341
+
342
+ body.close if body.respond_to?(:close)
343
+
344
+ @buffered = true
345
+ else
346
+ @buffered = false
271
347
  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
348
  end
283
349
 
284
- @buffered = true
350
+ return @buffered
285
351
  end
286
352
 
287
353
  def append(chunk)
@@ -309,10 +375,21 @@ module Rack
309
375
  @headers = headers
310
376
  end
311
377
 
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
378
+ def has_header?(key)
379
+ headers.key?(key)
380
+ end
381
+
382
+ def get_header(key)
383
+ headers[key]
384
+ end
385
+
386
+ def set_header(key, value)
387
+ headers[key] = value
388
+ end
389
+
390
+ def delete_header(key)
391
+ headers.delete(key)
392
+ end
316
393
  end
317
394
  end
318
395
  end
@@ -3,17 +3,29 @@
3
3
 
4
4
  require 'tempfile'
5
5
 
6
+ require_relative 'constants'
7
+
6
8
  module Rack
7
9
  # Class which can make any IO object rewindable, including non-rewindable ones. It does
8
10
  # this by buffering the data into a tempfile, which is rewindable.
9
11
  #
10
- # rack.input is required to be rewindable, so if your input stream IO is non-rewindable
11
- # by nature (e.g. a pipe or a socket) then you can wrap it in an object of this class
12
- # to easily make it rewindable.
13
- #
14
12
  # Don't forget to call #close when you're done. This frees up temporary resources that
15
13
  # RewindableInput uses, though it does *not* close the original IO object.
16
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
+
17
29
  def initialize(io)
18
30
  @io = io
19
31
  @rewindable_io = nil
@@ -40,6 +52,11 @@ module Rack
40
52
  @rewindable_io.rewind
41
53
  end
42
54
 
55
+ def size
56
+ make_rewindable unless @rewindable_io
57
+ @rewindable_io.size
58
+ end
59
+
43
60
  # Closes this RewindableInput object without closing the originally
44
61
  # wrapped IO object. Cleans up any temporary resources that this RewindableInput
45
62
  # has created.
@@ -66,12 +83,14 @@ module Rack
66
83
  # access it because we have the file handle open.
67
84
  @rewindable_io = Tempfile.new('RackRewindableInput')
68
85
  @rewindable_io.chmod(0000)
69
- @rewindable_io.set_encoding(Encoding::BINARY) if @rewindable_io.respond_to?(:set_encoding)
86
+ @rewindable_io.set_encoding(Encoding::BINARY)
70
87
  @rewindable_io.binmode
88
+ # :nocov:
71
89
  if filesystem_has_posix_semantics?
72
90
  raise 'Unlink failed. IO closed.' if @rewindable_io.closed?
73
91
  @unlinked = true
74
92
  end
93
+ # :nocov:
75
94
 
76
95
  buffer = "".dup
77
96
  while @io.read(1024 * 4, buffer)
data/lib/rack/runtime.rb CHANGED
@@ -1,7 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative 'utils'
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
@@ -9,18 +11,17 @@ module Rack
9
11
  # too.
10
12
  class Runtime
11
13
  FORMAT_STRING = "%0.6f" # :nodoc:
12
- HEADER_NAME = "X-Runtime" # :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)
23
- headers = Utils::HeaderHash[headers]
24
+ _, headers, _ = response = @app.call(env)
24
25
 
25
26
  request_time = Utils.clock_time - start_time
26
27
 
@@ -28,7 +29,7 @@ module Rack
28
29
  headers[@header_name] = FORMAT_STRING % request_time
29
30
  end
30
31
 
31
- [status, headers, body]
32
+ response
32
33
  end
33
34
  end
34
35
  end
data/lib/rack/sendfile.rb CHANGED
@@ -1,32 +1,36 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative 'constants'
4
+ require_relative 'utils'
5
+ require_relative 'body_proxy'
6
+
3
7
  module Rack
4
8
 
5
9
  # = Sendfile
6
10
  #
7
11
  # The Sendfile middleware intercepts responses whose body is being
8
- # served from a file and replaces it with a server specific X-Sendfile
12
+ # served from a file and replaces it with a server specific x-sendfile
9
13
  # header. The web server is then responsible for writing the file contents
10
14
  # to the client. This can dramatically reduce the amount of work required
11
15
  # by the Ruby backend and takes advantage of the web server's optimized file
12
16
  # delivery code.
13
17
  #
14
18
  # In order to take advantage of this middleware, the response body must
15
- # respond to +to_path+ and the request must include an X-Sendfile-Type
19
+ # respond to +to_path+ and the request must include an x-sendfile-type
16
20
  # header. Rack::Files and other components implement +to_path+ so there's
17
- # rarely anything you need to do in your application. The X-Sendfile-Type
21
+ # rarely anything you need to do in your application. The x-sendfile-type
18
22
  # header is typically set in your web servers configuration. The following
19
23
  # sections attempt to document
20
24
  #
21
25
  # === Nginx
22
26
  #
23
- # Nginx supports the X-Accel-Redirect header. This is similar to X-Sendfile
27
+ # Nginx supports the x-accel-redirect header. This is similar to x-sendfile
24
28
  # but requires parts of the filesystem to be mapped into a private URL
25
29
  # hierarchy.
26
30
  #
27
31
  # The following example shows the Nginx configuration required to create
28
- # a private "/files/" area, enable X-Accel-Redirect, and pass the special
29
- # X-Sendfile-Type and X-Accel-Mapping headers to the backend:
32
+ # a private "/files/" area, enable x-accel-redirect, and pass the special
33
+ # x-sendfile-type and x-accel-mapping headers to the backend:
30
34
  #
31
35
  # location ~ /files/(.*) {
32
36
  # internal;
@@ -40,14 +44,14 @@ module Rack
40
44
  # proxy_set_header X-Real-IP $remote_addr;
41
45
  # proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
42
46
  #
43
- # proxy_set_header X-Sendfile-Type X-Accel-Redirect;
44
- # proxy_set_header X-Accel-Mapping /var/www/=/files/;
47
+ # proxy_set_header x-sendfile-type x-accel-redirect;
48
+ # proxy_set_header x-accel-mapping /var/www/=/files/;
45
49
  #
46
50
  # proxy_pass http://127.0.0.1:8080/;
47
51
  # }
48
52
  #
49
- # Note that the X-Sendfile-Type header must be set exactly as shown above.
50
- # The X-Accel-Mapping header should specify the location on the file system,
53
+ # Note that the x-sendfile-type header must be set exactly as shown above.
54
+ # The x-accel-mapping header should specify the location on the file system,
51
55
  # followed by an equals sign (=), followed name of the private URL pattern
52
56
  # that it maps to. The middleware performs a simple substitution on the
53
57
  # resulting path.
@@ -56,8 +60,8 @@ module Rack
56
60
  #
57
61
  # === lighttpd
58
62
  #
59
- # Lighttpd has supported some variation of the X-Sendfile header for some
60
- # time, although only recent version support X-Sendfile in a reverse proxy
63
+ # Lighttpd has supported some variation of the x-sendfile header for some
64
+ # time, although only recent version support x-sendfile in a reverse proxy
61
65
  # configuration.
62
66
  #
63
67
  # $HTTP["host"] == "example.com" {
@@ -71,7 +75,7 @@ module Rack
71
75
  #
72
76
  # proxy-core.allow-x-sendfile = "enable"
73
77
  # proxy-core.rewrite-request = (
74
- # "X-Sendfile-Type" => (".*" => "X-Sendfile")
78
+ # "x-sendfile-type" => (".*" => "x-sendfile")
75
79
  # )
76
80
  # }
77
81
  #
@@ -79,21 +83,21 @@ module Rack
79
83
  #
80
84
  # === Apache
81
85
  #
82
- # X-Sendfile is supported under Apache 2.x using a separate module:
86
+ # x-sendfile is supported under Apache 2.x using a separate module:
83
87
  #
84
88
  # https://tn123.org/mod_xsendfile/
85
89
  #
86
90
  # Once the module is compiled and installed, you can enable it using
87
91
  # XSendFile config directive:
88
92
  #
89
- # RequestHeader Set X-Sendfile-Type X-Sendfile
93
+ # RequestHeader Set x-sendfile-type x-sendfile
90
94
  # ProxyPassReverse / http://localhost:8001/
91
95
  # XSendFile on
92
96
  #
93
97
  # === Mapping parameter
94
98
  #
95
99
  # The third parameter allows for an overriding extension of the
96
- # X-Accel-Mapping header. Mappings should be provided in tuples of internal to
100
+ # x-accel-mapping header. Mappings should be provided in tuples of internal to
97
101
  # external. The internal values may contain regular expression syntax, they
98
102
  # will be matched with case indifference.
99
103
 
@@ -107,28 +111,29 @@ module Rack
107
111
  end
108
112
 
109
113
  def call(env)
110
- status, headers, body = @app.call(env)
114
+ _, headers, body = response = @app.call(env)
115
+
111
116
  if body.respond_to?(:to_path)
112
117
  case type = variation(env)
113
- when 'X-Accel-Redirect'
118
+ when /x-accel-redirect/i
114
119
  path = ::File.expand_path(body.to_path)
115
120
  if url = map_accel_path(env, path)
116
121
  headers[CONTENT_LENGTH] = '0'
117
122
  # '?' 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')
123
+ headers[type.downcase] = ::Rack::Utils.escape_path(url).gsub('?', '%3F')
119
124
  obody = body
120
- body = Rack::BodyProxy.new([]) do
125
+ response[2] = Rack::BodyProxy.new([]) do
121
126
  obody.close if obody.respond_to?(:close)
122
127
  end
123
128
  else
124
- env[RACK_ERRORS].puts "X-Accel-Mapping header missing"
129
+ env[RACK_ERRORS].puts "x-accel-mapping header missing"
125
130
  end
126
- when 'X-Sendfile', 'X-Lighttpd-Send-File'
131
+ when /x-sendfile|x-lighttpd-send-file/i
127
132
  path = ::File.expand_path(body.to_path)
128
133
  headers[CONTENT_LENGTH] = '0'
129
- headers[type] = path
134
+ headers[type.downcase] = path
130
135
  obody = body
131
- body = Rack::BodyProxy.new([]) do
136
+ response[2] = Rack::BodyProxy.new([]) do
132
137
  obody.close if obody.respond_to?(:close)
133
138
  end
134
139
  when '', nil
@@ -136,7 +141,7 @@ module Rack
136
141
  env[RACK_ERRORS].puts "Unknown x-sendfile variation: '#{type}'.\n"
137
142
  end
138
143
  end
139
- [status, headers, body]
144
+ response
140
145
  end
141
146
 
142
147
  private
@@ -3,6 +3,10 @@
3
3
  require 'ostruct'
4
4
  require 'erb'
5
5
 
6
+ require_relative 'constants'
7
+ require_relative 'utils'
8
+ require_relative 'request'
9
+
6
10
  module Rack
7
11
  # Rack::ShowExceptions catches all exceptions raised from the app it
8
12
  # wraps. It shows a useful backtrace with the sourcefile and
@@ -55,7 +59,12 @@ module Rack
55
59
  private :accepts_html?
56
60
 
57
61
  def dump_exception(exception)
58
- string = "#{exception.class}: #{exception.message}\n".dup
62
+ if exception.respond_to?(:detailed_message)
63
+ message = exception.detailed_message(highlight: false)
64
+ else
65
+ message = exception.message
66
+ end
67
+ string = "#{exception.class}: #{message}\n".dup
59
68
  string << exception.backtrace.map { |l| "\t#{l}" }.join("\n")
60
69
  string
61
70
  end
@@ -159,7 +168,7 @@ module Rack
159
168
  div.commands { margin-left: 40px; }
160
169
  div.commands a { color:black; text-decoration:none; }
161
170
  #summary { background: #ffc; }
162
- #summary h2 { font-weight: normal; color: #666; }
171
+ #summary h2 { font-family: monospace; font-weight: normal; color: #666; white-space: pre-wrap; }
163
172
  #summary ul#quicklinks { list-style-type: none; margin-bottom: 2em; }
164
173
  #summary ul#quicklinks li { float: left; padding: 0 1em; }
165
174
  #summary ul#quicklinks>li+li { border-left: 1px #666 solid; }
@@ -227,7 +236,11 @@ module Rack
227
236
 
228
237
  <div id="summary">
229
238
  <h1><%=h exception.class %> at <%=h path %></h1>
239
+ <% if exception.respond_to?(:detailed_message) %>
240
+ <h2><%=h exception.detailed_message(highlight: false) %></h2>
241
+ <% else %>
230
242
  <h2><%=h exception.message %></h2>
243
+ <% end %>
231
244
  <table><tr>
232
245
  <th>Ruby</th>
233
246
  <td>
@@ -2,6 +2,11 @@
2
2
 
3
3
  require 'erb'
4
4
 
5
+ require_relative 'constants'
6
+ require_relative 'utils'
7
+ require_relative 'request'
8
+ require_relative 'body_proxy'
9
+
5
10
  module Rack
6
11
  # Rack::ShowStatus catches all empty responses and replaces them
7
12
  # with a site explaining the error.
@@ -17,8 +22,7 @@ module Rack
17
22
  end
18
23
 
19
24
  def call(env)
20
- status, headers, body = @app.call(env)
21
- headers = Utils::HeaderHash[headers]
25
+ status, headers, body = response = @app.call(env)
22
26
  empty = headers[CONTENT_LENGTH].to_i <= 0
23
27
 
24
28
  # client or server error, or explicit message
@@ -33,12 +37,18 @@ module Rack
33
37
  # Yes, it is dumb, but I don't like Ruby yelling at me.
34
38
  detail = detail = env[RACK_SHOWSTATUS_DETAIL] || message
35
39
 
36
- body = @template.result(binding)
37
- size = body.bytesize
38
- [status, headers.merge(CONTENT_TYPE => "text/html", CONTENT_LENGTH => size.to_s), [body]]
39
- else
40
- [status, headers, body]
40
+ html = @template.result(binding)
41
+ size = html.bytesize
42
+
43
+ response[2] = Rack::BodyProxy.new([html]) do
44
+ body.close if body.respond_to?(:close)
45
+ end
46
+
47
+ headers[CONTENT_TYPE] = "text/html"
48
+ headers[CONTENT_LENGTH] = size.to_s
41
49
  end
50
+
51
+ response
42
52
  end
43
53
 
44
54
  def h(obj) # :nodoc: