rack 2.2.8 → 3.0.9.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +213 -83
- data/CONTRIBUTING.md +53 -47
- data/MIT-LICENSE +1 -1
- data/README.md +309 -0
- data/SPEC.rdoc +174 -126
- data/lib/rack/auth/abstract/handler.rb +3 -1
- data/lib/rack/auth/abstract/request.rb +3 -1
- data/lib/rack/auth/basic.rb +0 -2
- data/lib/rack/auth/digest/md5.rb +1 -131
- data/lib/rack/auth/digest/nonce.rb +1 -54
- data/lib/rack/auth/digest/params.rb +1 -54
- data/lib/rack/auth/digest/request.rb +1 -43
- data/lib/rack/auth/digest.rb +256 -0
- data/lib/rack/body_proxy.rb +3 -1
- data/lib/rack/builder.rb +83 -63
- data/lib/rack/cascade.rb +2 -0
- data/lib/rack/chunked.rb +16 -13
- data/lib/rack/common_logger.rb +23 -18
- data/lib/rack/conditional_get.rb +18 -15
- data/lib/rack/constants.rb +64 -0
- data/lib/rack/content_length.rb +12 -16
- data/lib/rack/content_type.rb +8 -5
- data/lib/rack/deflater.rb +40 -26
- data/lib/rack/directory.rb +9 -3
- data/lib/rack/etag.rb +14 -23
- data/lib/rack/events.rb +4 -0
- data/lib/rack/file.rb +2 -0
- data/lib/rack/files.rb +15 -17
- data/lib/rack/head.rb +9 -8
- data/lib/rack/headers.rb +154 -0
- data/lib/rack/lint.rb +758 -646
- data/lib/rack/lock.rb +2 -5
- data/lib/rack/logger.rb +2 -0
- data/lib/rack/media_type.rb +9 -4
- data/lib/rack/method_override.rb +5 -1
- data/lib/rack/mime.rb +8 -0
- data/lib/rack/mock.rb +1 -271
- data/lib/rack/mock_request.rb +166 -0
- data/lib/rack/mock_response.rb +126 -0
- data/lib/rack/multipart/generator.rb +7 -5
- data/lib/rack/multipart/parser.rb +120 -64
- data/lib/rack/multipart/uploaded_file.rb +4 -0
- data/lib/rack/multipart.rb +20 -40
- data/lib/rack/null_logger.rb +9 -0
- data/lib/rack/query_parser.rb +78 -46
- data/lib/rack/recursive.rb +2 -0
- data/lib/rack/reloader.rb +0 -2
- data/lib/rack/request.rb +224 -106
- data/lib/rack/response.rb +138 -61
- data/lib/rack/rewindable_input.rb +24 -5
- data/lib/rack/runtime.rb +7 -6
- data/lib/rack/sendfile.rb +30 -25
- data/lib/rack/show_exceptions.rb +15 -2
- data/lib/rack/show_status.rb +17 -7
- data/lib/rack/static.rb +8 -8
- data/lib/rack/tempfile_reaper.rb +15 -4
- data/lib/rack/urlmap.rb +3 -1
- data/lib/rack/utils.rb +208 -178
- data/lib/rack/version.rb +9 -4
- data/lib/rack.rb +6 -76
- metadata +14 -34
- data/README.rdoc +0 -320
- data/Rakefile +0 -130
- data/bin/rackup +0 -5
- data/contrib/rack.png +0 -0
- data/contrib/rack.svg +0 -150
- data/contrib/rack_logo.svg +0 -164
- data/contrib/rdoc.css +0 -412
- data/example/lobster.ru +0 -6
- data/example/protectedlobster.rb +0 -16
- data/example/protectedlobster.ru +0 -10
- data/lib/rack/core_ext/regexp.rb +0 -14
- data/lib/rack/handler/cgi.rb +0 -59
- data/lib/rack/handler/fastcgi.rb +0 -100
- data/lib/rack/handler/lsws.rb +0 -61
- data/lib/rack/handler/scgi.rb +0 -71
- data/lib/rack/handler/thin.rb +0 -36
- data/lib/rack/handler/webrick.rb +0 -129
- data/lib/rack/handler.rb +0 -104
- data/lib/rack/lobster.rb +0 -70
- data/lib/rack/server.rb +0 -466
- data/lib/rack/session/abstract/id.rb +0 -523
- data/lib/rack/session/cookie.rb +0 -204
- data/lib/rack/session/memcache.rb +0 -10
- data/lib/rack/session/pool.rb +0 -85
- 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
|
-
#
|
30
|
-
|
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
|
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
|
-
#
|
36
|
-
#
|
37
|
-
#
|
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
|
-
#
|
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
|
-
|
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 =
|
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
|
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
|
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)
|
127
|
-
|
128
|
-
|
129
|
-
|
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?
|
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 '
|
166
|
-
# response.add_header '
|
212
|
+
# response.add_header 'vary', 'accept-encoding'
|
213
|
+
# response.add_header 'vary', 'cookie'
|
167
214
|
#
|
168
|
-
# assert_equal '
|
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,
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
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
|
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 "
|
262
|
+
get_header "location"
|
206
263
|
end
|
207
264
|
|
208
265
|
def location=(location)
|
209
|
-
set_header "
|
266
|
+
set_header "location", location
|
210
267
|
end
|
211
268
|
|
212
269
|
def set_cookie(key, value)
|
213
|
-
|
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
|
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=(
|
226
|
-
set_header SET_COOKIE,
|
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=(
|
234
|
-
set_header CACHE_CONTROL,
|
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=(
|
258
|
-
set_header ETAG,
|
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
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
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
|
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)
|
313
|
-
|
314
|
-
|
315
|
-
|
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)
|
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 "
|
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 = "
|
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
|
-
|
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
|
-
|
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
|
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
|
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
|
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
|
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
|
29
|
-
#
|
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
|
44
|
-
# proxy_set_header
|
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
|
50
|
-
# The
|
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
|
60
|
-
# time, although only recent version support
|
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
|
-
# "
|
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
|
-
#
|
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
|
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
|
-
#
|
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
|
-
|
114
|
+
_, headers, body = response = @app.call(env)
|
115
|
+
|
111
116
|
if body.respond_to?(:to_path)
|
112
117
|
case type = variation(env)
|
113
|
-
when
|
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
|
-
|
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 "
|
129
|
+
env[RACK_ERRORS].puts "x-accel-mapping header missing"
|
125
130
|
end
|
126
|
-
when
|
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
|
-
|
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
|
-
|
144
|
+
response
|
140
145
|
end
|
141
146
|
|
142
147
|
private
|
data/lib/rack/show_exceptions.rb
CHANGED
@@ -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
|
-
|
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>
|
data/lib/rack/show_status.rb
CHANGED
@@ -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
|
-
|
37
|
-
size =
|
38
|
-
|
39
|
-
|
40
|
-
|
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:
|