rack 2.2.8.1 → 3.1.5

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 +301 -81
  3. data/CONTRIBUTING.md +63 -55
  4. data/MIT-LICENSE +1 -1
  5. data/README.md +328 -0
  6. data/SPEC.rdoc +204 -131
  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 +1 -4
  10. data/lib/rack/bad_request.rb +8 -0
  11. data/lib/rack/body_proxy.rb +21 -3
  12. data/lib/rack/builder.rb +102 -69
  13. data/lib/rack/cascade.rb +2 -3
  14. data/lib/rack/common_logger.rb +23 -18
  15. data/lib/rack/conditional_get.rb +18 -15
  16. data/lib/rack/constants.rb +67 -0
  17. data/lib/rack/content_length.rb +12 -16
  18. data/lib/rack/content_type.rb +8 -5
  19. data/lib/rack/deflater.rb +40 -26
  20. data/lib/rack/directory.rb +9 -3
  21. data/lib/rack/etag.rb +14 -23
  22. data/lib/rack/events.rb +4 -0
  23. data/lib/rack/files.rb +15 -17
  24. data/lib/rack/head.rb +9 -8
  25. data/lib/rack/headers.rb +238 -0
  26. data/lib/rack/lint.rb +838 -644
  27. data/lib/rack/lock.rb +2 -5
  28. data/lib/rack/logger.rb +3 -0
  29. data/lib/rack/method_override.rb +5 -1
  30. data/lib/rack/mime.rb +14 -5
  31. data/lib/rack/mock.rb +1 -271
  32. data/lib/rack/mock_request.rb +171 -0
  33. data/lib/rack/mock_response.rb +124 -0
  34. data/lib/rack/multipart/generator.rb +7 -5
  35. data/lib/rack/multipart/parser.rb +215 -90
  36. data/lib/rack/multipart/uploaded_file.rb +4 -0
  37. data/lib/rack/multipart.rb +53 -40
  38. data/lib/rack/null_logger.rb +9 -0
  39. data/lib/rack/query_parser.rb +81 -102
  40. data/lib/rack/recursive.rb +2 -0
  41. data/lib/rack/reloader.rb +0 -2
  42. data/lib/rack/request.rb +250 -123
  43. data/lib/rack/response.rb +146 -66
  44. data/lib/rack/rewindable_input.rb +24 -5
  45. data/lib/rack/runtime.rb +7 -6
  46. data/lib/rack/sendfile.rb +30 -25
  47. data/lib/rack/show_exceptions.rb +21 -4
  48. data/lib/rack/show_status.rb +17 -7
  49. data/lib/rack/static.rb +8 -8
  50. data/lib/rack/tempfile_reaper.rb +15 -4
  51. data/lib/rack/urlmap.rb +3 -1
  52. data/lib/rack/utils.rb +232 -233
  53. data/lib/rack/version.rb +1 -9
  54. data/lib/rack.rb +13 -89
  55. metadata +15 -41
  56. data/README.rdoc +0 -320
  57. data/Rakefile +0 -130
  58. data/bin/rackup +0 -5
  59. data/contrib/rack.png +0 -0
  60. data/contrib/rack.svg +0 -150
  61. data/contrib/rack_logo.svg +0 -164
  62. data/contrib/rdoc.css +0 -412
  63. data/example/lobster.ru +0 -6
  64. data/example/protectedlobster.rb +0 -16
  65. data/example/protectedlobster.ru +0 -10
  66. data/lib/rack/auth/digest/md5.rb +0 -131
  67. data/lib/rack/auth/digest/nonce.rb +0 -54
  68. data/lib/rack/auth/digest/params.rb +0 -54
  69. data/lib/rack/auth/digest/request.rb +0 -43
  70. data/lib/rack/chunked.rb +0 -117
  71. data/lib/rack/core_ext/regexp.rb +0 -14
  72. data/lib/rack/file.rb +0 -7
  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,38 @@ 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
31
-
32
- # Initialize the response object with the specified body, status
33
- # and headers.
34
+ # Initialize the response object with the specified +body+, +status+
35
+ # and +headers+.
36
+ #
37
+ # If the +body+ is +nil+, construct an empty response object with internal
38
+ # buffering.
39
+ #
40
+ # If the +body+ responds to +to_str+, assume it's a string-like object and
41
+ # construct a buffered response object containing using that string as the
42
+ # initial contents of the buffer.
43
+ #
44
+ # Otherwise it is expected +body+ conforms to the normal requirements of a
45
+ # Rack response body, typically implementing one of +each+ (enumerable
46
+ # body) or +call+ (streaming body).
34
47
  #
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.
48
+ # The +status+ defaults to +200+ which is the "OK" HTTP status code. You
49
+ # can provide any other valid status code.
40
50
  #
41
- # Providing a body which responds to #to_str is legacy behaviour.
51
+ # The +headers+ must be a +Hash+ of key-value header pairs which conform to
52
+ # the Rack specification for response headers. The key must be a +String+
53
+ # instance and the value can be either a +String+ or +Array+ instance.
42
54
  def initialize(body = nil, status = 200, headers = {})
43
55
  @status = status.to_i
44
- @headers = Utils::HeaderHash[headers]
56
+
57
+ unless headers.is_a?(Hash)
58
+ raise ArgumentError, "Headers must be a Hash!"
59
+ end
60
+
61
+ @headers = Headers.new
62
+ # Convert headers input to a plain hash with lowercase keys.
63
+ headers.each do |k, v|
64
+ @headers[k] = v
65
+ end
45
66
 
46
67
  @writer = self.method(:append)
47
68
 
@@ -58,7 +79,7 @@ module Rack
58
79
  @length = body.to_str.bytesize
59
80
  else
60
81
  @body = body
61
- @buffered = false
82
+ @buffered = nil # undetermined as of yet.
62
83
  @length = 0
63
84
  end
64
85
 
@@ -74,16 +95,25 @@ module Rack
74
95
  CHUNKED == get_header(TRANSFER_ENCODING)
75
96
  end
76
97
 
98
+ def no_entity_body?
99
+ # The response body is an enumerable body and it is not allowed to have an entity body.
100
+ @body.respond_to?(:each) && STATUS_WITH_NO_ENTITY_BODY[@status]
101
+ end
102
+
77
103
  # Generate a response array consistent with the requirements of the SPEC.
78
104
  # @return [Array] a 3-tuple suitable of `[status, headers, body]`
79
105
  # which is suitable to be returned from the middleware `#call(env)` method.
80
106
  def finish(&block)
81
- if STATUS_WITH_NO_ENTITY_BODY[status.to_i]
107
+ if no_entity_body?
82
108
  delete_header CONTENT_TYPE
83
109
  delete_header CONTENT_LENGTH
84
110
  close
85
111
  return [@status, @headers, []]
86
112
  else
113
+ if @length && @length > 0 && !chunked?
114
+ set_header CONTENT_LENGTH, @length.to_s
115
+ end
116
+
87
117
  if block_given?
88
118
  @block = block
89
119
  return [@status, @headers, self]
@@ -105,7 +135,7 @@ module Rack
105
135
  end
106
136
  end
107
137
 
108
- # Append to body and update Content-Length.
138
+ # Append to body and update content-length.
109
139
  #
110
140
  # NOTE: Do not mix #write and direct #body access!
111
141
  #
@@ -123,10 +153,22 @@ module Rack
123
153
  @block == nil && @body.empty?
124
154
  end
125
155
 
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
156
+ def has_header?(key)
157
+ raise ArgumentError unless key.is_a?(String)
158
+ @headers.key?(key)
159
+ end
160
+ def get_header(key)
161
+ raise ArgumentError unless key.is_a?(String)
162
+ @headers[key]
163
+ end
164
+ def set_header(key, value)
165
+ raise ArgumentError unless key.is_a?(String)
166
+ @headers[key] = value
167
+ end
168
+ def delete_header(key)
169
+ raise ArgumentError unless key.is_a?(String)
170
+ @headers.delete key
171
+ end
130
172
 
131
173
  alias :[] :get_header
132
174
  alias :[]= :set_header
@@ -150,31 +192,43 @@ module Rack
150
192
  def forbidden?; status == 403; end
151
193
  def not_found?; status == 404; end
152
194
  def method_not_allowed?; status == 405; end
195
+ def not_acceptable?; status == 406; end
196
+ def request_timeout?; status == 408; end
153
197
  def precondition_failed?; status == 412; end
154
198
  def unprocessable?; status == 422; end
155
199
 
156
200
  def redirect?; [301, 302, 303, 307, 308].include? status; end
157
201
 
158
202
  def include?(header)
159
- has_header? header
203
+ has_header?(header)
160
204
  end
161
205
 
162
206
  # Add a header that may have multiple values.
163
207
  #
164
208
  # Example:
165
- # response.add_header 'Vary', 'Accept-Encoding'
166
- # response.add_header 'Vary', 'Cookie'
209
+ # response.add_header 'vary', 'accept-encoding'
210
+ # response.add_header 'vary', 'cookie'
167
211
  #
168
- # assert_equal 'Accept-Encoding,Cookie', response.get_header('Vary')
212
+ # assert_equal 'accept-encoding,cookie', response.get_header('vary')
169
213
  #
170
214
  # 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}"
215
+ def add_header(key, value)
216
+ raise ArgumentError unless key.is_a?(String)
217
+
218
+ if value.nil?
219
+ return get_header(key)
220
+ end
221
+
222
+ value = value.to_s
223
+
224
+ if header = get_header(key)
225
+ if header.is_a?(Array)
226
+ header << value
227
+ else
228
+ set_header(key, [header, value])
229
+ end
176
230
  else
177
- set_header key, v
231
+ set_header(key, value)
178
232
  end
179
233
  end
180
234
 
@@ -202,36 +256,39 @@ module Rack
202
256
  end
203
257
 
204
258
  def location
205
- get_header "Location"
259
+ get_header "location"
206
260
  end
207
261
 
208
262
  def location=(location)
209
- set_header "Location", location
263
+ set_header "location", location
210
264
  end
211
265
 
212
266
  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)
267
+ add_header SET_COOKIE, Utils.set_cookie_header(key, value)
215
268
  end
216
269
 
217
270
  def delete_cookie(key, value = {})
218
- set_header SET_COOKIE, ::Rack::Utils.add_remove_cookie_to_header(get_header(SET_COOKIE), key, value)
271
+ set_header(SET_COOKIE,
272
+ Utils.delete_set_cookie_header!(
273
+ get_header(SET_COOKIE), key, value
274
+ )
275
+ )
219
276
  end
220
277
 
221
278
  def set_cookie_header
222
279
  get_header SET_COOKIE
223
280
  end
224
281
 
225
- def set_cookie_header=(v)
226
- set_header SET_COOKIE, v
282
+ def set_cookie_header=(value)
283
+ set_header SET_COOKIE, value
227
284
  end
228
285
 
229
286
  def cache_control
230
287
  get_header CACHE_CONTROL
231
288
  end
232
289
 
233
- def cache_control=(v)
234
- set_header CACHE_CONTROL, v
290
+ def cache_control=(value)
291
+ set_header CACHE_CONTROL, value
235
292
  end
236
293
 
237
294
  # Specifies that the content shouldn't be cached. Overrides `cache!` if already called.
@@ -254,43 +311,55 @@ module Rack
254
311
  get_header ETAG
255
312
  end
256
313
 
257
- def etag=(v)
258
- set_header ETAG, v
314
+ def etag=(value)
315
+ set_header ETAG, value
259
316
  end
260
317
 
261
318
  protected
262
319
 
320
+ # Convert the body of this response into an internally buffered Array if possible.
321
+ #
322
+ # `@buffered` is a ternary value which indicates whether the body is buffered. It can be:
323
+ # * `nil` - The body has not been buffered yet.
324
+ # * `true` - The body is buffered as an Array instance.
325
+ # * `false` - The body is not buffered and cannot be buffered.
326
+ #
327
+ # @return [Boolean] whether the body is buffered as an Array instance.
263
328
  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
329
+ if @buffered.nil?
330
+ if @body.is_a?(Array)
331
+ # The user supplied body was an array:
332
+ @body = @body.compact
333
+ @length = @body.sum{|part| part.bytesize}
334
+ @buffered = true
335
+ elsif @body.respond_to?(:each)
336
+ # Turn the user supplied body into a buffered array:
337
+ body = @body
338
+ @body = Array.new
339
+ @length = 0
340
+
341
+ body.each do |part|
342
+ @writer.call(part.to_s)
343
+ end
344
+
345
+ body.close if body.respond_to?(:close)
346
+
347
+ # We have converted the body into an Array:
348
+ @buffered = true
349
+ else
350
+ # We don't know how to buffer the user-supplied body:
351
+ @buffered = false
271
352
  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
353
  end
283
354
 
284
- @buffered = true
355
+ return @buffered
285
356
  end
286
357
 
287
358
  def append(chunk)
359
+ chunk = chunk.dup unless chunk.frozen?
288
360
  @body << chunk
289
361
 
290
- unless chunked?
291
- @length += chunk.bytesize
292
- set_header(CONTENT_LENGTH, @length.to_s)
293
- end
362
+ @length += chunk.bytesize
294
363
 
295
364
  return chunk
296
365
  end
@@ -309,10 +378,21 @@ module Rack
309
378
  @headers = headers
310
379
  end
311
380
 
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
381
+ def has_header?(key)
382
+ headers.key?(key)
383
+ end
384
+
385
+ def get_header(key)
386
+ headers[key]
387
+ end
388
+
389
+ def set_header(key, value)
390
+ headers[key] = value
391
+ end
392
+
393
+ def delete_header(key)
394
+ headers.delete(key)
395
+ end
316
396
  end
317
397
  end
318
398
  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
@@ -1,8 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'ostruct'
4
3
  require 'erb'
5
4
 
5
+ require_relative 'constants'
6
+ require_relative 'utils'
7
+ require_relative 'request'
8
+
6
9
  module Rack
7
10
  # Rack::ShowExceptions catches all exceptions raised from the app it
8
11
  # wraps. It shows a useful backtrace with the sourcefile and
@@ -15,6 +18,11 @@ module Rack
15
18
  class ShowExceptions
16
19
  CONTEXT = 7
17
20
 
21
+ Frame = Struct.new(:filename, :lineno, :function,
22
+ :pre_context_lineno, :pre_context,
23
+ :context_line, :post_context_lineno,
24
+ :post_context)
25
+
18
26
  def initialize(app)
19
27
  @app = app
20
28
  end
@@ -55,7 +63,12 @@ module Rack
55
63
  private :accepts_html?
56
64
 
57
65
  def dump_exception(exception)
58
- string = "#{exception.class}: #{exception.message}\n".dup
66
+ if exception.respond_to?(:detailed_message)
67
+ message = exception.detailed_message(highlight: false)
68
+ else
69
+ message = exception.message
70
+ end
71
+ string = "#{exception.class}: #{message}\n".dup
59
72
  string << exception.backtrace.map { |l| "\t#{l}" }.join("\n")
60
73
  string
61
74
  end
@@ -70,7 +83,7 @@ module Rack
70
83
  # This double assignment is to prevent an "unused variable" warning.
71
84
  # Yes, it is dumb, but I don't like Ruby yelling at me.
72
85
  frames = frames = exception.backtrace.map { |line|
73
- frame = OpenStruct.new
86
+ frame = Frame.new
74
87
  if line =~ /(.*?):(\d+)(:in `(.*)')?/
75
88
  frame.filename = $1
76
89
  frame.lineno = $2.to_i
@@ -159,7 +172,7 @@ module Rack
159
172
  div.commands { margin-left: 40px; }
160
173
  div.commands a { color:black; text-decoration:none; }
161
174
  #summary { background: #ffc; }
162
- #summary h2 { font-weight: normal; color: #666; }
175
+ #summary h2 { font-family: monospace; font-weight: normal; color: #666; white-space: pre-wrap; }
163
176
  #summary ul#quicklinks { list-style-type: none; margin-bottom: 2em; }
164
177
  #summary ul#quicklinks li { float: left; padding: 0 1em; }
165
178
  #summary ul#quicklinks>li+li { border-left: 1px #666 solid; }
@@ -227,7 +240,11 @@ module Rack
227
240
 
228
241
  <div id="summary">
229
242
  <h1><%=h exception.class %> at <%=h path %></h1>
243
+ <% if exception.respond_to?(:detailed_message) %>
244
+ <h2><%=h exception.detailed_message(highlight: false) %></h2>
245
+ <% else %>
230
246
  <h2><%=h exception.message %></h2>
247
+ <% end %>
231
248
  <table><tr>
232
249
  <th>Ruby</th>
233
250
  <td>