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