rack 2.0.1 → 2.2.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/CHANGELOG.md +795 -0
- data/CONTRIBUTING.md +136 -0
- data/{COPYING → MIT-LICENSE} +4 -2
- data/README.rdoc +188 -145
- data/Rakefile +37 -23
- data/{SPEC → SPEC.rdoc} +46 -17
- data/bin/rackup +1 -0
- data/example/lobster.ru +2 -0
- data/example/protectedlobster.rb +3 -1
- data/example/protectedlobster.ru +2 -0
- data/lib/rack/auth/abstract/handler.rb +3 -1
- data/lib/rack/auth/abstract/request.rb +1 -1
- data/lib/rack/auth/basic.rb +6 -4
- data/lib/rack/auth/digest/md5.rb +13 -11
- data/lib/rack/auth/digest/nonce.rb +5 -3
- data/lib/rack/auth/digest/params.rb +4 -2
- data/lib/rack/auth/digest/request.rb +5 -3
- data/lib/rack/body_proxy.rb +15 -14
- data/lib/rack/builder.rb +116 -23
- data/lib/rack/cascade.rb +28 -12
- data/lib/rack/chunked.rb +68 -20
- data/lib/rack/common_logger.rb +37 -25
- data/lib/rack/conditional_get.rb +20 -16
- data/lib/rack/config.rb +2 -0
- data/lib/rack/content_length.rb +8 -7
- data/lib/rack/content_type.rb +5 -4
- data/lib/rack/core_ext/regexp.rb +14 -0
- data/lib/rack/deflater.rb +60 -70
- data/lib/rack/directory.rb +84 -64
- data/lib/rack/etag.rb +8 -5
- data/lib/rack/events.rb +19 -20
- data/lib/rack/file.rb +4 -173
- data/lib/rack/files.rb +218 -0
- data/lib/rack/handler/cgi.rb +2 -3
- data/lib/rack/handler/fastcgi.rb +4 -4
- data/lib/rack/handler/lsws.rb +3 -3
- data/lib/rack/handler/scgi.rb +9 -8
- data/lib/rack/handler/thin.rb +3 -3
- data/lib/rack/handler/webrick.rb +19 -10
- data/lib/rack/handler.rb +7 -2
- data/lib/rack/head.rb +1 -1
- data/lib/rack/lint.rb +221 -186
- data/lib/rack/lobster.rb +10 -10
- data/lib/rack/lock.rb +14 -4
- data/lib/rack/logger.rb +2 -0
- data/lib/rack/media_type.rb +23 -8
- data/lib/rack/method_override.rb +13 -4
- data/lib/rack/mime.rb +9 -1
- data/lib/rack/mock.rb +135 -29
- data/lib/rack/multipart/generator.rb +17 -13
- data/lib/rack/multipart/parser.rb +85 -68
- data/lib/rack/multipart/uploaded_file.rb +15 -7
- data/lib/rack/multipart.rb +6 -5
- data/lib/rack/null_logger.rb +2 -0
- data/lib/rack/query_parser.rb +108 -36
- data/lib/rack/recursive.rb +7 -5
- data/lib/rack/reloader.rb +8 -4
- data/lib/rack/request.rb +232 -60
- data/lib/rack/response.rb +127 -44
- data/lib/rack/rewindable_input.rb +4 -3
- data/lib/rack/runtime.rb +6 -4
- data/lib/rack/sendfile.rb +14 -10
- data/lib/rack/server.rb +97 -25
- data/lib/rack/session/abstract/id.rb +113 -25
- data/lib/rack/session/cookie.rb +22 -14
- data/lib/rack/session/memcache.rb +4 -87
- data/lib/rack/session/pool.rb +24 -10
- data/lib/rack/show_exceptions.rb +22 -18
- data/lib/rack/show_status.rb +9 -9
- data/lib/rack/static.rb +25 -12
- data/lib/rack/tempfile_reaper.rb +1 -1
- data/lib/rack/urlmap.rb +13 -7
- data/lib/rack/utils.rb +135 -123
- data/lib/rack/version.rb +29 -0
- data/lib/rack.rb +67 -73
- data/rack.gemspec +40 -29
- metadata +25 -184
- data/HISTORY.md +0 -505
- data/test/builder/an_underscore_app.rb +0 -5
- data/test/builder/anything.rb +0 -5
- data/test/builder/comment.ru +0 -4
- data/test/builder/end.ru +0 -5
- data/test/builder/line.ru +0 -1
- data/test/builder/options.ru +0 -2
- data/test/cgi/assets/folder/test.js +0 -1
- data/test/cgi/assets/fonts/font.eot +0 -1
- data/test/cgi/assets/images/image.png +0 -1
- data/test/cgi/assets/index.html +0 -1
- data/test/cgi/assets/javascripts/app.js +0 -1
- data/test/cgi/assets/stylesheets/app.css +0 -1
- data/test/cgi/lighttpd.conf +0 -26
- data/test/cgi/rackup_stub.rb +0 -6
- data/test/cgi/sample_rackup.ru +0 -5
- data/test/cgi/test +0 -9
- data/test/cgi/test+directory/test+file +0 -1
- data/test/cgi/test.fcgi +0 -9
- data/test/cgi/test.gz +0 -0
- data/test/cgi/test.ru +0 -5
- data/test/gemloader.rb +0 -10
- data/test/helper.rb +0 -34
- data/test/multipart/bad_robots +0 -259
- data/test/multipart/binary +0 -0
- data/test/multipart/content_type_and_no_filename +0 -6
- data/test/multipart/empty +0 -10
- data/test/multipart/fail_16384_nofile +0 -814
- data/test/multipart/file1.txt +0 -1
- data/test/multipart/filename_and_modification_param +0 -7
- data/test/multipart/filename_and_no_name +0 -6
- data/test/multipart/filename_with_encoded_words +0 -7
- data/test/multipart/filename_with_escaped_quotes +0 -6
- data/test/multipart/filename_with_escaped_quotes_and_modification_param +0 -7
- data/test/multipart/filename_with_percent_escaped_quotes +0 -6
- data/test/multipart/filename_with_single_quote +0 -7
- data/test/multipart/filename_with_unescaped_percentages +0 -6
- data/test/multipart/filename_with_unescaped_percentages2 +0 -6
- data/test/multipart/filename_with_unescaped_percentages3 +0 -6
- data/test/multipart/filename_with_unescaped_quotes +0 -6
- data/test/multipart/ie +0 -6
- data/test/multipart/invalid_character +0 -6
- data/test/multipart/mixed_files +0 -21
- data/test/multipart/nested +0 -10
- data/test/multipart/none +0 -9
- data/test/multipart/quoted +0 -15
- data/test/multipart/rack-logo.png +0 -0
- data/test/multipart/semicolon +0 -6
- data/test/multipart/text +0 -15
- data/test/multipart/three_files_three_fields +0 -31
- data/test/multipart/unity3d_wwwform +0 -11
- data/test/multipart/webkit +0 -32
- data/test/rackup/config.ru +0 -31
- data/test/registering_handler/rack/handler/registering_myself.rb +0 -8
- data/test/spec_auth_basic.rb +0 -89
- data/test/spec_auth_digest.rb +0 -260
- data/test/spec_body_proxy.rb +0 -85
- data/test/spec_builder.rb +0 -233
- data/test/spec_cascade.rb +0 -63
- data/test/spec_cgi.rb +0 -84
- data/test/spec_chunked.rb +0 -103
- data/test/spec_common_logger.rb +0 -95
- data/test/spec_conditional_get.rb +0 -103
- data/test/spec_config.rb +0 -23
- data/test/spec_content_length.rb +0 -86
- data/test/spec_content_type.rb +0 -46
- data/test/spec_deflater.rb +0 -365
- data/test/spec_directory.rb +0 -148
- data/test/spec_etag.rb +0 -108
- data/test/spec_events.rb +0 -133
- data/test/spec_fastcgi.rb +0 -85
- data/test/spec_file.rb +0 -251
- data/test/spec_handler.rb +0 -57
- data/test/spec_head.rb +0 -46
- data/test/spec_lint.rb +0 -515
- data/test/spec_lobster.rb +0 -59
- data/test/spec_lock.rb +0 -194
- data/test/spec_logger.rb +0 -24
- data/test/spec_media_type.rb +0 -42
- data/test/spec_method_override.rb +0 -83
- data/test/spec_mime.rb +0 -51
- data/test/spec_mock.rb +0 -342
- data/test/spec_multipart.rb +0 -716
- data/test/spec_null_logger.rb +0 -21
- data/test/spec_recursive.rb +0 -75
- data/test/spec_request.rb +0 -1393
- data/test/spec_response.rb +0 -510
- data/test/spec_rewindable_input.rb +0 -128
- data/test/spec_runtime.rb +0 -50
- data/test/spec_sendfile.rb +0 -125
- data/test/spec_server.rb +0 -193
- data/test/spec_session_abstract_id.rb +0 -31
- data/test/spec_session_abstract_session_hash.rb +0 -28
- data/test/spec_session_cookie.rb +0 -442
- data/test/spec_session_memcache.rb +0 -320
- data/test/spec_session_pool.rb +0 -210
- data/test/spec_show_exceptions.rb +0 -80
- data/test/spec_show_status.rb +0 -104
- data/test/spec_static.rb +0 -184
- data/test/spec_tempfile_reaper.rb +0 -64
- data/test/spec_thin.rb +0 -96
- data/test/spec_urlmap.rb +0 -237
- data/test/spec_utils.rb +0 -742
- data/test/spec_version.rb +0 -11
- data/test/spec_webrick.rb +0 -208
- data/test/static/another/index.html +0 -1
- data/test/static/foo.html +0 -1
- data/test/static/index.html +0 -1
- data/test/testrequest.rb +0 -78
- data/test/unregistered_handler/rack/handler/unregistered.rb +0 -7
- data/test/unregistered_handler/rack/handler/unregistered_long_one.rb +0 -7
data/lib/rack/response.rb
CHANGED
@@ -1,7 +1,5 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
require 'rack/body_proxy'
|
4
|
-
require 'rack/media_type'
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
5
3
|
require 'time'
|
6
4
|
|
7
5
|
module Rack
|
@@ -17,38 +15,57 @@ module Rack
|
|
17
15
|
# +write+ are synchronous with the Rack response.
|
18
16
|
#
|
19
17
|
# Your application's +call+ should end returning Response#finish.
|
20
|
-
|
21
18
|
class Response
|
19
|
+
def self.[](status, headers, body)
|
20
|
+
self.new(body, status, headers)
|
21
|
+
end
|
22
|
+
|
23
|
+
CHUNKED = 'chunked'
|
24
|
+
STATUS_WITH_NO_ENTITY_BODY = Utils::STATUS_WITH_NO_ENTITY_BODY
|
25
|
+
|
22
26
|
attr_accessor :length, :status, :body
|
23
|
-
attr_reader :
|
24
|
-
alias headers header
|
27
|
+
attr_reader :headers
|
25
28
|
|
26
|
-
|
29
|
+
# @deprecated Use {#headers} instead.
|
30
|
+
alias header headers
|
27
31
|
|
28
|
-
|
32
|
+
# Initialize the response object with the specified body, status
|
33
|
+
# and headers.
|
34
|
+
#
|
35
|
+
# @param body [nil, #each, #to_str] the response body.
|
36
|
+
# @param status [Integer] the integer status as defined by the
|
37
|
+
# HTTP protocol RFCs.
|
38
|
+
# @param headers [#each] a list of key-value header pairs which
|
39
|
+
# conform to the HTTP protocol RFCs.
|
40
|
+
#
|
41
|
+
# Providing a body which responds to #to_str is legacy behaviour.
|
42
|
+
def initialize(body = nil, status = 200, headers = {})
|
29
43
|
@status = status.to_i
|
30
|
-
@
|
44
|
+
@headers = Utils::HeaderHash[headers]
|
31
45
|
|
32
|
-
@writer
|
33
|
-
@block = nil
|
34
|
-
@length = 0
|
46
|
+
@writer = self.method(:append)
|
35
47
|
|
36
|
-
@
|
48
|
+
@block = nil
|
37
49
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
50
|
+
# Keep track of whether we have expanded the user supplied body.
|
51
|
+
if body.nil?
|
52
|
+
@body = []
|
53
|
+
@buffered = true
|
54
|
+
@length = 0
|
55
|
+
elsif body.respond_to?(:to_str)
|
56
|
+
@body = [body]
|
57
|
+
@buffered = true
|
58
|
+
@length = body.to_str.bytesize
|
44
59
|
else
|
45
|
-
|
60
|
+
@body = body
|
61
|
+
@buffered = false
|
62
|
+
@length = 0
|
46
63
|
end
|
47
64
|
|
48
|
-
yield self
|
65
|
+
yield self if block_given?
|
49
66
|
end
|
50
67
|
|
51
|
-
def redirect(target, status=302)
|
68
|
+
def redirect(target, status = 302)
|
52
69
|
self.status = status
|
53
70
|
self.location = target
|
54
71
|
end
|
@@ -57,42 +74,49 @@ module Rack
|
|
57
74
|
CHUNKED == get_header(TRANSFER_ENCODING)
|
58
75
|
end
|
59
76
|
|
77
|
+
# Generate a response array consistent with the requirements of the SPEC.
|
78
|
+
# @return [Array] a 3-tuple suitable of `[status, headers, body]`
|
79
|
+
# which is suitable to be returned from the middleware `#call(env)` method.
|
60
80
|
def finish(&block)
|
61
|
-
|
62
|
-
|
63
|
-
if [204, 205, 304].include?(status.to_i)
|
81
|
+
if STATUS_WITH_NO_ENTITY_BODY[status.to_i]
|
64
82
|
delete_header CONTENT_TYPE
|
65
83
|
delete_header CONTENT_LENGTH
|
66
84
|
close
|
67
|
-
[status
|
85
|
+
return [@status, @headers, []]
|
68
86
|
else
|
69
|
-
|
87
|
+
if block_given?
|
88
|
+
@block = block
|
89
|
+
return [@status, @headers, self]
|
90
|
+
else
|
91
|
+
return [@status, @headers, @body]
|
92
|
+
end
|
70
93
|
end
|
71
94
|
end
|
95
|
+
|
72
96
|
alias to_a finish # For *response
|
73
|
-
alias to_ary finish # For implicit-splat on Ruby 1.9.2
|
74
97
|
|
75
98
|
def each(&callback)
|
76
99
|
@body.each(&callback)
|
77
|
-
@
|
78
|
-
|
100
|
+
@buffered = true
|
101
|
+
|
102
|
+
if @block
|
103
|
+
@writer = callback
|
104
|
+
@block.call(self)
|
105
|
+
end
|
79
106
|
end
|
80
107
|
|
81
108
|
# Append to body and update Content-Length.
|
82
109
|
#
|
83
110
|
# NOTE: Do not mix #write and direct #body access!
|
84
111
|
#
|
85
|
-
def write(
|
86
|
-
|
87
|
-
@length += s.bytesize unless chunked?
|
88
|
-
@writer.call s
|
112
|
+
def write(chunk)
|
113
|
+
buffered_body!
|
89
114
|
|
90
|
-
|
91
|
-
str
|
115
|
+
@writer.call(chunk.to_s)
|
92
116
|
end
|
93
117
|
|
94
118
|
def close
|
95
|
-
body.close if body.respond_to?(:close)
|
119
|
+
@body.close if @body.respond_to?(:close)
|
96
120
|
end
|
97
121
|
|
98
122
|
def empty?
|
@@ -144,7 +168,7 @@ module Rack
|
|
144
168
|
# assert_equal 'Accept-Encoding,Cookie', response.get_header('Vary')
|
145
169
|
#
|
146
170
|
# http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2
|
147
|
-
def add_header
|
171
|
+
def add_header(key, v)
|
148
172
|
if v.nil?
|
149
173
|
get_header key
|
150
174
|
elsif has_header? key
|
@@ -154,10 +178,16 @@ module Rack
|
|
154
178
|
end
|
155
179
|
end
|
156
180
|
|
181
|
+
# Get the content type of the response.
|
157
182
|
def content_type
|
158
183
|
get_header CONTENT_TYPE
|
159
184
|
end
|
160
185
|
|
186
|
+
# Set the content type of the response.
|
187
|
+
def content_type=(content_type)
|
188
|
+
set_header CONTENT_TYPE, content_type
|
189
|
+
end
|
190
|
+
|
161
191
|
def media_type
|
162
192
|
MediaType.type(content_type)
|
163
193
|
end
|
@@ -184,7 +214,7 @@ module Rack
|
|
184
214
|
set_header SET_COOKIE, ::Rack::Utils.add_cookie_to_header(cookie_header, key, value)
|
185
215
|
end
|
186
216
|
|
187
|
-
def delete_cookie(key, value={})
|
217
|
+
def delete_cookie(key, value = {})
|
188
218
|
set_header SET_COOKIE, ::Rack::Utils.add_remove_cookie_to_header(get_header(SET_COOKIE), key, value)
|
189
219
|
end
|
190
220
|
|
@@ -192,7 +222,7 @@ module Rack
|
|
192
222
|
get_header SET_COOKIE
|
193
223
|
end
|
194
224
|
|
195
|
-
def set_cookie_header=
|
225
|
+
def set_cookie_header=(v)
|
196
226
|
set_header SET_COOKIE, v
|
197
227
|
end
|
198
228
|
|
@@ -200,17 +230,70 @@ module Rack
|
|
200
230
|
get_header CACHE_CONTROL
|
201
231
|
end
|
202
232
|
|
203
|
-
def cache_control=
|
233
|
+
def cache_control=(v)
|
204
234
|
set_header CACHE_CONTROL, v
|
205
235
|
end
|
206
236
|
|
237
|
+
# Specifies that the content shouldn't be cached. Overrides `cache!` if already called.
|
238
|
+
def do_not_cache!
|
239
|
+
set_header CACHE_CONTROL, "no-cache, must-revalidate"
|
240
|
+
set_header EXPIRES, Time.now.httpdate
|
241
|
+
end
|
242
|
+
|
243
|
+
# Specify that the content should be cached.
|
244
|
+
# @param duration [Integer] The number of seconds until the cache expires.
|
245
|
+
# @option directive [String] The cache control directive, one of "public", "private", "no-cache" or "no-store".
|
246
|
+
def cache!(duration = 3600, directive: "public")
|
247
|
+
unless headers[CACHE_CONTROL] =~ /no-cache/
|
248
|
+
set_header CACHE_CONTROL, "#{directive}, max-age=#{duration}"
|
249
|
+
set_header EXPIRES, (Time.now + duration).httpdate
|
250
|
+
end
|
251
|
+
end
|
252
|
+
|
207
253
|
def etag
|
208
254
|
get_header ETAG
|
209
255
|
end
|
210
256
|
|
211
|
-
def etag=
|
257
|
+
def etag=(v)
|
212
258
|
set_header ETAG, v
|
213
259
|
end
|
260
|
+
|
261
|
+
protected
|
262
|
+
|
263
|
+
def buffered_body!
|
264
|
+
return if @buffered
|
265
|
+
|
266
|
+
if @body.is_a?(Array)
|
267
|
+
# The user supplied body was an array:
|
268
|
+
@body = @body.compact
|
269
|
+
@body.each do |part|
|
270
|
+
@length += part.to_s.bytesize
|
271
|
+
end
|
272
|
+
else
|
273
|
+
# Turn the user supplied body into a buffered array:
|
274
|
+
body = @body
|
275
|
+
@body = Array.new
|
276
|
+
|
277
|
+
body.each do |part|
|
278
|
+
@writer.call(part.to_s)
|
279
|
+
end
|
280
|
+
|
281
|
+
body.close if body.respond_to?(:close)
|
282
|
+
end
|
283
|
+
|
284
|
+
@buffered = true
|
285
|
+
end
|
286
|
+
|
287
|
+
def append(chunk)
|
288
|
+
@body << chunk
|
289
|
+
|
290
|
+
unless chunked?
|
291
|
+
@length += chunk.bytesize
|
292
|
+
set_header(CONTENT_LENGTH, @length.to_s)
|
293
|
+
end
|
294
|
+
|
295
|
+
return chunk
|
296
|
+
end
|
214
297
|
end
|
215
298
|
|
216
299
|
include Helpers
|
@@ -221,7 +304,7 @@ module Rack
|
|
221
304
|
attr_reader :headers
|
222
305
|
attr_accessor :status
|
223
306
|
|
224
|
-
def initialize
|
307
|
+
def initialize(status, headers)
|
225
308
|
@status = status
|
226
309
|
@headers = headers
|
227
310
|
end
|
@@ -1,6 +1,7 @@
|
|
1
1
|
# -*- encoding: binary -*-
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
2
4
|
require 'tempfile'
|
3
|
-
require 'rack/utils'
|
4
5
|
|
5
6
|
module Rack
|
6
7
|
# Class which can make any IO object rewindable, including non-rewindable ones. It does
|
@@ -40,7 +41,7 @@ module Rack
|
|
40
41
|
end
|
41
42
|
|
42
43
|
# Closes this RewindableInput object without closing the originally
|
43
|
-
# wrapped IO
|
44
|
+
# wrapped IO object. Cleans up any temporary resources that this RewindableInput
|
44
45
|
# has created.
|
45
46
|
#
|
46
47
|
# This method may be called multiple times. It does nothing on subsequent calls.
|
@@ -72,7 +73,7 @@ module Rack
|
|
72
73
|
@unlinked = true
|
73
74
|
end
|
74
75
|
|
75
|
-
buffer = ""
|
76
|
+
buffer = "".dup
|
76
77
|
while @io.read(1024 * 4, buffer)
|
77
78
|
entire_buffer_written_out = false
|
78
79
|
while !entire_buffer_written_out
|
data/lib/rack/runtime.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Rack
|
4
4
|
# Sets an "X-Runtime" response header, indicating the response
|
@@ -8,8 +8,8 @@ module Rack
|
|
8
8
|
# time, or before all the other middlewares to include time for them,
|
9
9
|
# too.
|
10
10
|
class Runtime
|
11
|
-
FORMAT_STRING = "%0.6f"
|
12
|
-
HEADER_NAME = "X-Runtime"
|
11
|
+
FORMAT_STRING = "%0.6f" # :nodoc:
|
12
|
+
HEADER_NAME = "X-Runtime" # :nodoc:
|
13
13
|
|
14
14
|
def initialize(app, name = nil)
|
15
15
|
@app = app
|
@@ -20,9 +20,11 @@ module Rack
|
|
20
20
|
def call(env)
|
21
21
|
start_time = Utils.clock_time
|
22
22
|
status, headers, body = @app.call(env)
|
23
|
+
headers = Utils::HeaderHash[headers]
|
24
|
+
|
23
25
|
request_time = Utils.clock_time - start_time
|
24
26
|
|
25
|
-
unless headers.
|
27
|
+
unless headers.key?(@header_name)
|
26
28
|
headers[@header_name] = FORMAT_STRING % request_time
|
27
29
|
end
|
28
30
|
|
data/lib/rack/sendfile.rb
CHANGED
@@ -1,5 +1,4 @@
|
|
1
|
-
|
2
|
-
require 'rack/body_proxy'
|
1
|
+
# frozen_string_literal: true
|
3
2
|
|
4
3
|
module Rack
|
5
4
|
|
@@ -14,7 +13,7 @@ module Rack
|
|
14
13
|
#
|
15
14
|
# In order to take advantage of this middleware, the response body must
|
16
15
|
# respond to +to_path+ and the request must include an X-Sendfile-Type
|
17
|
-
# header. Rack::
|
16
|
+
# header. Rack::Files and other components implement +to_path+ so there's
|
18
17
|
# rarely anything you need to do in your application. The X-Sendfile-Type
|
19
18
|
# header is typically set in your web servers configuration. The following
|
20
19
|
# sections attempt to document
|
@@ -53,7 +52,7 @@ module Rack
|
|
53
52
|
# that it maps to. The middleware performs a simple substitution on the
|
54
53
|
# resulting path.
|
55
54
|
#
|
56
|
-
# See Also:
|
55
|
+
# See Also: https://www.nginx.com/resources/wiki/start/topics/examples/xsendfile
|
57
56
|
#
|
58
57
|
# === lighttpd
|
59
58
|
#
|
@@ -99,7 +98,7 @@ module Rack
|
|
99
98
|
# will be matched with case indifference.
|
100
99
|
|
101
100
|
class Sendfile
|
102
|
-
def initialize(app, variation=nil, mappings=[])
|
101
|
+
def initialize(app, variation = nil, mappings = [])
|
103
102
|
@app = app
|
104
103
|
@variation = variation
|
105
104
|
@mappings = mappings.map do |internal, external|
|
@@ -115,7 +114,8 @@ module Rack
|
|
115
114
|
path = ::File.expand_path(body.to_path)
|
116
115
|
if url = map_accel_path(env, path)
|
117
116
|
headers[CONTENT_LENGTH] = '0'
|
118
|
-
|
117
|
+
# '?' must be percent-encoded because it is not query string but a part of path
|
118
|
+
headers[type] = ::Rack::Utils.escape_path(url).gsub('?', '%3F')
|
119
119
|
obody = body
|
120
120
|
body = Rack::BodyProxy.new([]) do
|
121
121
|
obody.close if obody.respond_to?(:close)
|
@@ -133,7 +133,7 @@ module Rack
|
|
133
133
|
end
|
134
134
|
when '', nil
|
135
135
|
else
|
136
|
-
env[RACK_ERRORS].puts "Unknown x-sendfile variation:
|
136
|
+
env[RACK_ERRORS].puts "Unknown x-sendfile variation: #{type.inspect}"
|
137
137
|
end
|
138
138
|
end
|
139
139
|
[status, headers, body]
|
@@ -147,11 +147,15 @@ module Rack
|
|
147
147
|
end
|
148
148
|
|
149
149
|
def map_accel_path(env, path)
|
150
|
-
if mapping = @mappings.find { |internal,_| internal =~ path }
|
150
|
+
if mapping = @mappings.find { |internal, _| internal =~ path }
|
151
151
|
path.sub(*mapping)
|
152
152
|
elsif mapping = env['HTTP_X_ACCEL_MAPPING']
|
153
|
-
|
154
|
-
|
153
|
+
mapping.split(',').map(&:strip).each do |m|
|
154
|
+
internal, external = m.split('=', 2).map(&:strip)
|
155
|
+
new_path = path.sub(/^#{internal}/i, external)
|
156
|
+
return new_path unless path == new_path
|
157
|
+
end
|
158
|
+
path
|
155
159
|
end
|
156
160
|
end
|
157
161
|
end
|
data/lib/rack/server.rb
CHANGED
@@ -1,9 +1,12 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'optparse'
|
4
|
+
require 'fileutils'
|
3
5
|
|
4
6
|
module Rack
|
5
7
|
|
6
8
|
class Server
|
9
|
+
(require_relative 'core_ext/regexp'; using ::Rack::RegexpExtensions) if RUBY_VERSION < '2.4'
|
7
10
|
|
8
11
|
class Options
|
9
12
|
def parse!(args)
|
@@ -20,10 +23,6 @@ module Rack
|
|
20
23
|
lineno += 1
|
21
24
|
}
|
22
25
|
|
23
|
-
opts.on("-b", "--builder BUILDER_LINE", "evaluate a BUILDER_LINE of code as a builder script") { |line|
|
24
|
-
options[:builder] = line
|
25
|
-
}
|
26
|
-
|
27
26
|
opts.on("-d", "--debug", "set debugging flags (set $DEBUG to true)") {
|
28
27
|
options[:debug] = true
|
29
28
|
}
|
@@ -41,12 +40,16 @@ module Rack
|
|
41
40
|
|
42
41
|
opts.on("-r", "--require LIBRARY",
|
43
42
|
"require the library, before executing your script") { |library|
|
44
|
-
options[:require]
|
43
|
+
(options[:require] ||= []) << library
|
45
44
|
}
|
46
45
|
|
47
46
|
opts.separator ""
|
48
47
|
opts.separator "Rack options:"
|
49
|
-
opts.on("-
|
48
|
+
opts.on("-b", "--builder BUILDER_LINE", "evaluate a BUILDER_LINE of code as a builder script") { |line|
|
49
|
+
options[:builder] = line
|
50
|
+
}
|
51
|
+
|
52
|
+
opts.on("-s", "--server SERVER", "serve using SERVER (thin/puma/webrick)") { |s|
|
50
53
|
options[:server] = s
|
51
54
|
}
|
52
55
|
|
@@ -76,6 +79,24 @@ module Rack
|
|
76
79
|
options[:pid] = ::File.expand_path(f)
|
77
80
|
}
|
78
81
|
|
82
|
+
opts.separator ""
|
83
|
+
opts.separator "Profiling options:"
|
84
|
+
|
85
|
+
opts.on("--heap HEAPFILE", "Build the application, then dump the heap to HEAPFILE") do |e|
|
86
|
+
options[:heapfile] = e
|
87
|
+
end
|
88
|
+
|
89
|
+
opts.on("--profile PROFILE", "Dump CPU or Memory profile to PROFILE (defaults to a tempfile)") do |e|
|
90
|
+
options[:profile_file] = e
|
91
|
+
end
|
92
|
+
|
93
|
+
opts.on("--profile-mode MODE", "Profile mode (cpu|wall|object)") do |e|
|
94
|
+
{ cpu: true, wall: true, object: true }.fetch(e.to_sym) do
|
95
|
+
raise OptionParser::InvalidOption, "unknown profile mode: #{e}"
|
96
|
+
end
|
97
|
+
options[:profile_mode] = e.to_sym
|
98
|
+
end
|
99
|
+
|
79
100
|
opts.separator ""
|
80
101
|
opts.separator "Common options:"
|
81
102
|
|
@@ -113,14 +134,14 @@ module Rack
|
|
113
134
|
|
114
135
|
has_options = false
|
115
136
|
server.valid_options.each do |name, description|
|
116
|
-
next if
|
137
|
+
next if /^(Host|Port)[^a-zA-Z]/.match?(name.to_s) # ignore handler's host and port options, we do our own.
|
117
138
|
info << " -O %-21s %s" % [name, description]
|
118
139
|
has_options = true
|
119
140
|
end
|
120
141
|
return "" if !has_options
|
121
142
|
end
|
122
143
|
info.join("\n")
|
123
|
-
rescue NameError
|
144
|
+
rescue NameError, LoadError
|
124
145
|
return "Warning: Could not find handler specified (#{options[:server] || 'default'}) to determine handler-specific options"
|
125
146
|
end
|
126
147
|
end
|
@@ -151,7 +172,9 @@ module Rack
|
|
151
172
|
|
152
173
|
# Options may include:
|
153
174
|
# * :app
|
154
|
-
# a rack application to run (overrides :config)
|
175
|
+
# a rack application to run (overrides :config and :builder)
|
176
|
+
# * :builder
|
177
|
+
# a string to evaluate a Rack::Builder from
|
155
178
|
# * :config
|
156
179
|
# a rackup configuration file path to load (.ru)
|
157
180
|
# * :environment
|
@@ -181,6 +204,14 @@ module Rack
|
|
181
204
|
# add given paths to $LOAD_PATH
|
182
205
|
# * :require
|
183
206
|
# require the given libraries
|
207
|
+
#
|
208
|
+
# Additional options for profiling app initialization include:
|
209
|
+
# * :heapfile
|
210
|
+
# location for ObjectSpace.dump_all to write the output to
|
211
|
+
# * :profile_file
|
212
|
+
# location for CPU/Memory (StackProf) profile output (defaults to a tempfile)
|
213
|
+
# * :profile_mode
|
214
|
+
# StackProf profile mode (cpu|wall|object)
|
184
215
|
def initialize(options = nil)
|
185
216
|
@ignore_options = []
|
186
217
|
|
@@ -205,12 +236,12 @@ module Rack
|
|
205
236
|
default_host = environment == 'development' ? 'localhost' : '0.0.0.0'
|
206
237
|
|
207
238
|
{
|
208
|
-
:
|
209
|
-
:
|
210
|
-
:
|
211
|
-
:
|
212
|
-
:
|
213
|
-
:
|
239
|
+
environment: environment,
|
240
|
+
pid: nil,
|
241
|
+
Port: 9292,
|
242
|
+
Host: default_host,
|
243
|
+
AccessLog: [],
|
244
|
+
config: "config.ru"
|
214
245
|
}
|
215
246
|
end
|
216
247
|
|
@@ -221,21 +252,19 @@ module Rack
|
|
221
252
|
class << self
|
222
253
|
def logging_middleware
|
223
254
|
lambda { |server|
|
224
|
-
server.server.name
|
255
|
+
/CGI/.match?(server.server.name) || server.options[:quiet] ? nil : [Rack::CommonLogger, $stderr]
|
225
256
|
}
|
226
257
|
end
|
227
258
|
|
228
259
|
def default_middleware_by_environment
|
229
|
-
m = Hash.new {|h,k| h[k] = []}
|
260
|
+
m = Hash.new {|h, k| h[k] = []}
|
230
261
|
m["deployment"] = [
|
231
262
|
[Rack::ContentLength],
|
232
|
-
[Rack::Chunked],
|
233
263
|
logging_middleware,
|
234
264
|
[Rack::TempfileReaper]
|
235
265
|
]
|
236
266
|
m["development"] = [
|
237
267
|
[Rack::ContentLength],
|
238
|
-
[Rack::Chunked],
|
239
268
|
logging_middleware,
|
240
269
|
[Rack::ShowExceptions],
|
241
270
|
[Rack::Lint],
|
@@ -254,7 +283,7 @@ module Rack
|
|
254
283
|
self.class.middleware
|
255
284
|
end
|
256
285
|
|
257
|
-
def start
|
286
|
+
def start(&block)
|
258
287
|
if options[:warn]
|
259
288
|
$-w = true
|
260
289
|
end
|
@@ -263,7 +292,7 @@ module Rack
|
|
263
292
|
$LOAD_PATH.unshift(*includes)
|
264
293
|
end
|
265
294
|
|
266
|
-
|
295
|
+
Array(options[:require]).each do |library|
|
267
296
|
require library
|
268
297
|
end
|
269
298
|
|
@@ -279,7 +308,9 @@ module Rack
|
|
279
308
|
|
280
309
|
# Touch the wrapped app, so that the config.ru is loaded before
|
281
310
|
# daemonization (i.e. before chdir, etc).
|
282
|
-
|
311
|
+
handle_profiling(options[:heapfile], options[:profile_mode], options[:profile_file]) do
|
312
|
+
wrapped_app
|
313
|
+
end
|
283
314
|
|
284
315
|
daemonize_app if options[:daemonize]
|
285
316
|
|
@@ -293,7 +324,7 @@ module Rack
|
|
293
324
|
end
|
294
325
|
end
|
295
326
|
|
296
|
-
server.run
|
327
|
+
server.run(wrapped_app, **options, &block)
|
297
328
|
end
|
298
329
|
|
299
330
|
def server
|
@@ -320,6 +351,44 @@ module Rack
|
|
320
351
|
app
|
321
352
|
end
|
322
353
|
|
354
|
+
def handle_profiling(heapfile, profile_mode, filename)
|
355
|
+
if heapfile
|
356
|
+
require "objspace"
|
357
|
+
ObjectSpace.trace_object_allocations_start
|
358
|
+
yield
|
359
|
+
GC.start
|
360
|
+
::File.open(heapfile, "w") { |f| ObjectSpace.dump_all(output: f) }
|
361
|
+
exit
|
362
|
+
end
|
363
|
+
|
364
|
+
if profile_mode
|
365
|
+
require "stackprof"
|
366
|
+
require "tempfile"
|
367
|
+
|
368
|
+
make_profile_name(filename) do |filename|
|
369
|
+
::File.open(filename, "w") do |f|
|
370
|
+
StackProf.run(mode: profile_mode, out: f) do
|
371
|
+
yield
|
372
|
+
end
|
373
|
+
puts "Profile written to: #{filename}"
|
374
|
+
end
|
375
|
+
end
|
376
|
+
exit
|
377
|
+
end
|
378
|
+
|
379
|
+
yield
|
380
|
+
end
|
381
|
+
|
382
|
+
def make_profile_name(filename)
|
383
|
+
if filename
|
384
|
+
yield filename
|
385
|
+
else
|
386
|
+
::Dir::Tmpname.create("profile.dump") do |tmpname, _, _|
|
387
|
+
yield tmpname
|
388
|
+
end
|
389
|
+
end
|
390
|
+
end
|
391
|
+
|
323
392
|
def build_app_from_string
|
324
393
|
Rack::Builder.new_from_string(self.options[:builder])
|
325
394
|
end
|
@@ -354,12 +423,15 @@ module Rack
|
|
354
423
|
end
|
355
424
|
|
356
425
|
def daemonize_app
|
426
|
+
# Cannot be covered as it forks
|
427
|
+
# :nocov:
|
357
428
|
Process.daemon
|
429
|
+
# :nocov:
|
358
430
|
end
|
359
431
|
|
360
432
|
def write_pid
|
361
433
|
::File.open(options[:pid], ::File::CREAT | ::File::EXCL | ::File::WRONLY ){ |f| f.write("#{Process.pid}") }
|
362
|
-
at_exit { ::
|
434
|
+
at_exit { ::FileUtils.rm_f(options[:pid]) }
|
363
435
|
rescue Errno::EEXIST
|
364
436
|
check_pid!
|
365
437
|
retry
|