rack 2.0.9.3 → 3.0.0
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 +4 -4
- data/CHANGELOG.md +808 -0
- data/CONTRIBUTING.md +142 -0
- data/{COPYING → MIT-LICENSE} +4 -2
- data/README.md +293 -0
- data/SPEC.rdoc +340 -0
- data/lib/rack/auth/abstract/handler.rb +6 -2
- data/lib/rack/auth/abstract/request.rb +4 -2
- data/lib/rack/auth/basic.rb +7 -4
- data/lib/rack/auth/digest/md5.rb +1 -129
- data/lib/rack/auth/digest/nonce.rb +1 -51
- data/lib/rack/auth/digest/params.rb +1 -52
- data/lib/rack/auth/digest/request.rb +1 -41
- data/lib/rack/auth/digest.rb +256 -0
- data/lib/rack/body_proxy.rb +18 -15
- data/lib/rack/builder.rb +151 -40
- data/lib/rack/cascade.rb +30 -12
- data/lib/rack/chunked.rb +74 -23
- data/lib/rack/common_logger.rb +49 -36
- data/lib/rack/conditional_get.rb +33 -26
- data/lib/rack/config.rb +2 -0
- data/lib/rack/constants.rb +63 -0
- data/lib/rack/content_length.rb +13 -16
- data/lib/rack/content_type.rb +12 -8
- data/lib/rack/deflater.rb +84 -45
- data/lib/rack/directory.rb +90 -64
- data/lib/rack/etag.rb +17 -23
- data/lib/rack/events.rb +23 -20
- data/lib/rack/file.rb +5 -172
- data/lib/rack/files.rb +216 -0
- data/lib/rack/head.rb +10 -9
- data/lib/rack/headers.rb +154 -0
- data/lib/rack/lint.rb +786 -645
- data/lib/rack/lock.rb +4 -6
- data/lib/rack/logger.rb +4 -0
- data/lib/rack/media_type.rb +10 -5
- data/lib/rack/method_override.rb +8 -2
- data/lib/rack/mime.rb +17 -1
- data/lib/rack/mock.rb +2 -195
- data/lib/rack/mock_request.rb +166 -0
- data/lib/rack/mock_response.rb +126 -0
- data/lib/rack/multipart/generator.rb +21 -15
- data/lib/rack/multipart/parser.rb +161 -118
- data/lib/rack/multipart/uploaded_file.rb +19 -7
- data/lib/rack/multipart.rb +23 -41
- data/lib/rack/null_logger.rb +11 -0
- data/lib/rack/query_parser.rb +126 -65
- data/lib/rack/recursive.rb +9 -5
- data/lib/rack/reloader.rb +6 -4
- data/lib/rack/request.rb +331 -74
- data/lib/rack/response.rb +223 -70
- data/lib/rack/rewindable_input.rb +28 -8
- data/lib/rack/runtime.rb +11 -8
- data/lib/rack/sendfile.rb +42 -33
- data/lib/rack/show_exceptions.rb +35 -18
- data/lib/rack/show_status.rb +25 -15
- data/lib/rack/static.rb +30 -18
- data/lib/rack/tempfile_reaper.rb +16 -5
- data/lib/rack/urlmap.rb +14 -6
- data/lib/rack/utils.rb +268 -260
- data/lib/rack/version.rb +34 -0
- data/lib/rack.rb +15 -92
- metadata +44 -207
- data/HISTORY.md +0 -520
- data/README.rdoc +0 -316
- data/Rakefile +0 -116
- data/SPEC +0 -263
- data/bin/rackup +0 -4
- 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 -4
- data/example/protectedlobster.rb +0 -14
- data/example/protectedlobster.ru +0 -8
- data/lib/rack/handler/cgi.rb +0 -60
- data/lib/rack/handler/fastcgi.rb +0 -100
- data/lib/rack/handler/lsws.rb +0 -61
- data/lib/rack/handler/scgi.rb +0 -70
- data/lib/rack/handler/thin.rb +0 -36
- data/lib/rack/handler/webrick.rb +0 -120
- data/lib/rack/handler.rb +0 -99
- data/lib/rack/lobster.rb +0 -70
- data/lib/rack/server.rb +0 -395
- data/lib/rack/session/abstract/id.rb +0 -510
- data/lib/rack/session/cookie.rb +0 -204
- data/lib/rack/session/memcache.rb +0 -99
- data/lib/rack/session/pool.rb +0 -83
- data/rack.gemspec +0 -34
- 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_null_byte +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 -107
- 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 -375
- 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 -264
- data/test/spec_handler.rb +0 -57
- data/test/spec_head.rb +0 -46
- data/test/spec_lint.rb +0 -520
- data/test/spec_lobster.rb +0 -59
- data/test/spec_lock.rb +0 -204
- data/test/spec_logger.rb +0 -24
- data/test/spec_media_type.rb +0 -42
- data/test/spec_method_override.rb +0 -110
- data/test/spec_mime.rb +0 -51
- data/test/spec_mock.rb +0 -359
- data/test/spec_multipart.rb +0 -721
- data/test/spec_null_logger.rb +0 -21
- data/test/spec_recursive.rb +0 -75
- data/test/spec_request.rb +0 -1423
- data/test/spec_response.rb +0 -528
- 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 -45
- data/test/spec_session_cookie.rb +0 -442
- data/test/spec_session_memcache.rb +0 -357
- data/test/spec_session_persisted_secure_secure_session_hash.rb +0 -73
- data/test/spec_session_pool.rb +0 -247
- data/test/spec_show_exceptions.rb +0 -93
- 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 -206
- 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,9 +1,12 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
require 'rack/body_proxy'
|
4
|
-
require 'rack/media_type'
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
5
3
|
require 'time'
|
6
4
|
|
5
|
+
require_relative 'constants'
|
6
|
+
require_relative 'utils'
|
7
|
+
require_relative 'media_type'
|
8
|
+
require_relative 'headers'
|
9
|
+
|
7
10
|
module Rack
|
8
11
|
# Rack::Response provides a convenient interface to create a Rack
|
9
12
|
# response.
|
@@ -17,38 +20,80 @@ module Rack
|
|
17
20
|
# +write+ are synchronous with the Rack response.
|
18
21
|
#
|
19
22
|
# Your application's +call+ should end returning Response#finish.
|
20
|
-
|
21
23
|
class Response
|
24
|
+
def self.[](status, headers, body)
|
25
|
+
self.new(body, status, headers)
|
26
|
+
end
|
27
|
+
|
28
|
+
CHUNKED = 'chunked'
|
29
|
+
STATUS_WITH_NO_ENTITY_BODY = Utils::STATUS_WITH_NO_ENTITY_BODY
|
30
|
+
|
22
31
|
attr_accessor :length, :status, :body
|
23
|
-
attr_reader :
|
24
|
-
|
32
|
+
attr_reader :headers
|
33
|
+
|
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
|
25
37
|
|
26
|
-
|
38
|
+
headers
|
39
|
+
end
|
27
40
|
|
28
|
-
|
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.
|
50
|
+
#
|
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).
|
54
|
+
#
|
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.
|
61
|
+
def initialize(body = nil, status = 200, headers = {})
|
29
62
|
@status = status.to_i
|
30
|
-
@header = Utils::HeaderHash.new.merge(header)
|
31
63
|
|
32
|
-
|
33
|
-
|
34
|
-
|
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
|
35
67
|
|
36
|
-
@
|
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
|
37
73
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
74
|
+
@writer = self.method(:append)
|
75
|
+
|
76
|
+
@block = nil
|
77
|
+
|
78
|
+
# Keep track of whether we have expanded the user supplied body.
|
79
|
+
if body.nil?
|
80
|
+
@body = []
|
81
|
+
@buffered = true
|
82
|
+
@length = 0
|
83
|
+
elsif body.respond_to?(:to_str)
|
84
|
+
@body = [body]
|
85
|
+
@buffered = true
|
86
|
+
@length = body.to_str.bytesize
|
44
87
|
else
|
45
|
-
|
88
|
+
@body = body
|
89
|
+
@buffered = nil # undetermined as of yet.
|
90
|
+
@length = 0
|
46
91
|
end
|
47
92
|
|
48
|
-
yield self
|
93
|
+
yield self if block_given?
|
49
94
|
end
|
50
95
|
|
51
|
-
def redirect(target, status=302)
|
96
|
+
def redirect(target, status = 302)
|
52
97
|
self.status = status
|
53
98
|
self.location = target
|
54
99
|
end
|
@@ -57,52 +102,71 @@ module Rack
|
|
57
102
|
CHUNKED == get_header(TRANSFER_ENCODING)
|
58
103
|
end
|
59
104
|
|
105
|
+
# Generate a response array consistent with the requirements of the SPEC.
|
106
|
+
# @return [Array] a 3-tuple suitable of `[status, headers, body]`
|
107
|
+
# which is suitable to be returned from the middleware `#call(env)` method.
|
60
108
|
def finish(&block)
|
61
|
-
@
|
62
|
-
|
63
|
-
if [204, 304].include?(status.to_i)
|
109
|
+
if STATUS_WITH_NO_ENTITY_BODY[@status]
|
64
110
|
delete_header CONTENT_TYPE
|
65
111
|
delete_header CONTENT_LENGTH
|
66
112
|
close
|
67
|
-
[status
|
113
|
+
return [@status, @headers, []]
|
68
114
|
else
|
69
|
-
|
115
|
+
if block_given?
|
116
|
+
@block = block
|
117
|
+
return [@status, @headers, self]
|
118
|
+
else
|
119
|
+
return [@status, @headers, @body]
|
120
|
+
end
|
70
121
|
end
|
71
122
|
end
|
123
|
+
|
72
124
|
alias to_a finish # For *response
|
73
|
-
alias to_ary finish # For implicit-splat on Ruby 1.9.2
|
74
125
|
|
75
126
|
def each(&callback)
|
76
127
|
@body.each(&callback)
|
77
|
-
@
|
78
|
-
|
128
|
+
@buffered = true
|
129
|
+
|
130
|
+
if @block
|
131
|
+
@writer = callback
|
132
|
+
@block.call(self)
|
133
|
+
end
|
79
134
|
end
|
80
135
|
|
81
|
-
# Append to body and update
|
136
|
+
# Append to body and update content-length.
|
82
137
|
#
|
83
138
|
# NOTE: Do not mix #write and direct #body access!
|
84
139
|
#
|
85
|
-
def write(
|
86
|
-
|
87
|
-
@length += s.bytesize unless chunked?
|
88
|
-
@writer.call s
|
140
|
+
def write(chunk)
|
141
|
+
buffered_body!
|
89
142
|
|
90
|
-
|
91
|
-
str
|
143
|
+
@writer.call(chunk.to_s)
|
92
144
|
end
|
93
145
|
|
94
146
|
def close
|
95
|
-
body.close if body.respond_to?(:close)
|
147
|
+
@body.close if @body.respond_to?(:close)
|
96
148
|
end
|
97
149
|
|
98
150
|
def empty?
|
99
151
|
@block == nil && @body.empty?
|
100
152
|
end
|
101
153
|
|
102
|
-
def has_header?(key)
|
103
|
-
|
104
|
-
|
105
|
-
|
154
|
+
def has_header?(key)
|
155
|
+
raise ArgumentError unless key.is_a?(String)
|
156
|
+
@headers.key?(key)
|
157
|
+
end
|
158
|
+
def get_header(key)
|
159
|
+
raise ArgumentError unless key.is_a?(String)
|
160
|
+
@headers[key]
|
161
|
+
end
|
162
|
+
def set_header(key, value)
|
163
|
+
raise ArgumentError unless key.is_a?(String)
|
164
|
+
@headers[key] = value
|
165
|
+
end
|
166
|
+
def delete_header(key)
|
167
|
+
raise ArgumentError unless key.is_a?(String)
|
168
|
+
@headers.delete key
|
169
|
+
end
|
106
170
|
|
107
171
|
alias :[] :get_header
|
108
172
|
alias :[]= :set_header
|
@@ -126,38 +190,56 @@ module Rack
|
|
126
190
|
def forbidden?; status == 403; end
|
127
191
|
def not_found?; status == 404; end
|
128
192
|
def method_not_allowed?; status == 405; end
|
193
|
+
def not_acceptable?; status == 406; end
|
194
|
+
def request_timeout?; status == 408; end
|
129
195
|
def precondition_failed?; status == 412; end
|
130
196
|
def unprocessable?; status == 422; end
|
131
197
|
|
132
198
|
def redirect?; [301, 302, 303, 307, 308].include? status; end
|
133
199
|
|
134
200
|
def include?(header)
|
135
|
-
has_header?
|
201
|
+
has_header?(header)
|
136
202
|
end
|
137
203
|
|
138
204
|
# Add a header that may have multiple values.
|
139
205
|
#
|
140
206
|
# Example:
|
141
|
-
# response.add_header '
|
142
|
-
# response.add_header '
|
207
|
+
# response.add_header 'vary', 'accept-encoding'
|
208
|
+
# response.add_header 'vary', 'cookie'
|
143
209
|
#
|
144
|
-
# assert_equal '
|
210
|
+
# assert_equal 'accept-encoding,cookie', response.get_header('vary')
|
145
211
|
#
|
146
212
|
# http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2
|
147
|
-
def add_header
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
213
|
+
def add_header(key, value)
|
214
|
+
raise ArgumentError unless key.is_a?(String)
|
215
|
+
|
216
|
+
if value.nil?
|
217
|
+
return get_header(key)
|
218
|
+
end
|
219
|
+
|
220
|
+
value = value.to_s
|
221
|
+
|
222
|
+
if header = get_header(key)
|
223
|
+
if header.is_a?(Array)
|
224
|
+
header << value
|
225
|
+
else
|
226
|
+
set_header(key, [header, value])
|
227
|
+
end
|
152
228
|
else
|
153
|
-
set_header
|
229
|
+
set_header(key, value)
|
154
230
|
end
|
155
231
|
end
|
156
232
|
|
233
|
+
# Get the content type of the response.
|
157
234
|
def content_type
|
158
235
|
get_header CONTENT_TYPE
|
159
236
|
end
|
160
237
|
|
238
|
+
# Set the content type of the response.
|
239
|
+
def content_type=(content_type)
|
240
|
+
set_header CONTENT_TYPE, content_type
|
241
|
+
end
|
242
|
+
|
161
243
|
def media_type
|
162
244
|
MediaType.type(content_type)
|
163
245
|
end
|
@@ -172,44 +254,104 @@ module Rack
|
|
172
254
|
end
|
173
255
|
|
174
256
|
def location
|
175
|
-
get_header "
|
257
|
+
get_header "location"
|
176
258
|
end
|
177
259
|
|
178
260
|
def location=(location)
|
179
|
-
set_header "
|
261
|
+
set_header "location", location
|
180
262
|
end
|
181
263
|
|
182
264
|
def set_cookie(key, value)
|
183
|
-
|
184
|
-
set_header SET_COOKIE, ::Rack::Utils.add_cookie_to_header(cookie_header, key, value)
|
265
|
+
add_header SET_COOKIE, Utils.set_cookie_header(key, value)
|
185
266
|
end
|
186
267
|
|
187
|
-
def delete_cookie(key, value={})
|
188
|
-
set_header
|
268
|
+
def delete_cookie(key, value = {})
|
269
|
+
set_header(SET_COOKIE,
|
270
|
+
Utils.delete_set_cookie_header!(
|
271
|
+
get_header(SET_COOKIE), key, value
|
272
|
+
)
|
273
|
+
)
|
189
274
|
end
|
190
275
|
|
191
276
|
def set_cookie_header
|
192
277
|
get_header SET_COOKIE
|
193
278
|
end
|
194
279
|
|
195
|
-
def set_cookie_header=
|
196
|
-
set_header SET_COOKIE,
|
280
|
+
def set_cookie_header=(value)
|
281
|
+
set_header SET_COOKIE, value
|
197
282
|
end
|
198
283
|
|
199
284
|
def cache_control
|
200
285
|
get_header CACHE_CONTROL
|
201
286
|
end
|
202
287
|
|
203
|
-
def cache_control=
|
204
|
-
set_header CACHE_CONTROL,
|
288
|
+
def cache_control=(value)
|
289
|
+
set_header CACHE_CONTROL, value
|
290
|
+
end
|
291
|
+
|
292
|
+
# Specifies that the content shouldn't be cached. Overrides `cache!` if already called.
|
293
|
+
def do_not_cache!
|
294
|
+
set_header CACHE_CONTROL, "no-cache, must-revalidate"
|
295
|
+
set_header EXPIRES, Time.now.httpdate
|
296
|
+
end
|
297
|
+
|
298
|
+
# Specify that the content should be cached.
|
299
|
+
# @param duration [Integer] The number of seconds until the cache expires.
|
300
|
+
# @option directive [String] The cache control directive, one of "public", "private", "no-cache" or "no-store".
|
301
|
+
def cache!(duration = 3600, directive: "public")
|
302
|
+
unless headers[CACHE_CONTROL] =~ /no-cache/
|
303
|
+
set_header CACHE_CONTROL, "#{directive}, max-age=#{duration}"
|
304
|
+
set_header EXPIRES, (Time.now + duration).httpdate
|
305
|
+
end
|
205
306
|
end
|
206
307
|
|
207
308
|
def etag
|
208
309
|
get_header ETAG
|
209
310
|
end
|
210
311
|
|
211
|
-
def etag=
|
212
|
-
set_header ETAG,
|
312
|
+
def etag=(value)
|
313
|
+
set_header ETAG, value
|
314
|
+
end
|
315
|
+
|
316
|
+
protected
|
317
|
+
|
318
|
+
def buffered_body!
|
319
|
+
if @buffered.nil?
|
320
|
+
if @body.is_a?(Array)
|
321
|
+
# The user supplied body was an array:
|
322
|
+
@body = @body.compact
|
323
|
+
@body.each do |part|
|
324
|
+
@length += part.to_s.bytesize
|
325
|
+
end
|
326
|
+
elsif @body.respond_to?(:each)
|
327
|
+
# Turn the user supplied body into a buffered array:
|
328
|
+
body = @body
|
329
|
+
@body = Array.new
|
330
|
+
|
331
|
+
body.each do |part|
|
332
|
+
@writer.call(part.to_s)
|
333
|
+
end
|
334
|
+
|
335
|
+
body.close if body.respond_to?(:close)
|
336
|
+
|
337
|
+
@buffered = true
|
338
|
+
else
|
339
|
+
@buffered = false
|
340
|
+
end
|
341
|
+
end
|
342
|
+
|
343
|
+
return @buffered
|
344
|
+
end
|
345
|
+
|
346
|
+
def append(chunk)
|
347
|
+
@body << chunk
|
348
|
+
|
349
|
+
unless chunked?
|
350
|
+
@length += chunk.bytesize
|
351
|
+
set_header(CONTENT_LENGTH, @length.to_s)
|
352
|
+
end
|
353
|
+
|
354
|
+
return chunk
|
213
355
|
end
|
214
356
|
end
|
215
357
|
|
@@ -221,15 +363,26 @@ module Rack
|
|
221
363
|
attr_reader :headers
|
222
364
|
attr_accessor :status
|
223
365
|
|
224
|
-
def initialize
|
366
|
+
def initialize(status, headers)
|
225
367
|
@status = status
|
226
368
|
@headers = headers
|
227
369
|
end
|
228
370
|
|
229
|
-
def has_header?(key)
|
230
|
-
|
231
|
-
|
232
|
-
|
371
|
+
def has_header?(key)
|
372
|
+
headers.key?(key)
|
373
|
+
end
|
374
|
+
|
375
|
+
def get_header(key)
|
376
|
+
headers[key]
|
377
|
+
end
|
378
|
+
|
379
|
+
def set_header(key, value)
|
380
|
+
headers[key] = value
|
381
|
+
end
|
382
|
+
|
383
|
+
def delete_header(key)
|
384
|
+
headers.delete(key)
|
385
|
+
end
|
233
386
|
end
|
234
387
|
end
|
235
388
|
end
|
@@ -1,18 +1,31 @@
|
|
1
1
|
# -*- encoding: binary -*-
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
2
4
|
require 'tempfile'
|
3
|
-
|
5
|
+
|
6
|
+
require_relative 'constants'
|
4
7
|
|
5
8
|
module Rack
|
6
9
|
# Class which can make any IO object rewindable, including non-rewindable ones. It does
|
7
10
|
# this by buffering the data into a tempfile, which is rewindable.
|
8
11
|
#
|
9
|
-
# rack.input is required to be rewindable, so if your input stream IO is non-rewindable
|
10
|
-
# by nature (e.g. a pipe or a socket) then you can wrap it in an object of this class
|
11
|
-
# to easily make it rewindable.
|
12
|
-
#
|
13
12
|
# Don't forget to call #close when you're done. This frees up temporary resources that
|
14
13
|
# RewindableInput uses, though it does *not* close the original IO object.
|
15
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
|
+
|
16
29
|
def initialize(io)
|
17
30
|
@io = io
|
18
31
|
@rewindable_io = nil
|
@@ -39,8 +52,13 @@ module Rack
|
|
39
52
|
@rewindable_io.rewind
|
40
53
|
end
|
41
54
|
|
55
|
+
def size
|
56
|
+
make_rewindable unless @rewindable_io
|
57
|
+
@rewindable_io.size
|
58
|
+
end
|
59
|
+
|
42
60
|
# Closes this RewindableInput object without closing the originally
|
43
|
-
# wrapped IO
|
61
|
+
# wrapped IO object. Cleans up any temporary resources that this RewindableInput
|
44
62
|
# has created.
|
45
63
|
#
|
46
64
|
# This method may be called multiple times. It does nothing on subsequent calls.
|
@@ -65,14 +83,16 @@ module Rack
|
|
65
83
|
# access it because we have the file handle open.
|
66
84
|
@rewindable_io = Tempfile.new('RackRewindableInput')
|
67
85
|
@rewindable_io.chmod(0000)
|
68
|
-
@rewindable_io.set_encoding(Encoding::BINARY)
|
86
|
+
@rewindable_io.set_encoding(Encoding::BINARY)
|
69
87
|
@rewindable_io.binmode
|
88
|
+
# :nocov:
|
70
89
|
if filesystem_has_posix_semantics?
|
71
90
|
raise 'Unlink failed. IO closed.' if @rewindable_io.closed?
|
72
91
|
@unlinked = true
|
73
92
|
end
|
93
|
+
# :nocov:
|
74
94
|
|
75
|
-
buffer = ""
|
95
|
+
buffer = "".dup
|
76
96
|
while @io.read(1024 * 4, buffer)
|
77
97
|
entire_buffer_written_out = false
|
78
98
|
while !entire_buffer_written_out
|
data/lib/rack/runtime.rb
CHANGED
@@ -1,32 +1,35 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'utils'
|
2
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
|
8
10
|
# time, or before all the other middlewares to include time for them,
|
9
11
|
# too.
|
10
12
|
class Runtime
|
11
|
-
FORMAT_STRING = "%0.6f"
|
12
|
-
HEADER_NAME = "
|
13
|
+
FORMAT_STRING = "%0.6f" # :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
|
-
|
24
|
+
_, headers, _ = response = @app.call(env)
|
25
|
+
|
23
26
|
request_time = Utils.clock_time - start_time
|
24
27
|
|
25
|
-
unless headers.
|
28
|
+
unless headers.key?(@header_name)
|
26
29
|
headers[@header_name] = FORMAT_STRING % request_time
|
27
30
|
end
|
28
31
|
|
29
|
-
|
32
|
+
response
|
30
33
|
end
|
31
34
|
end
|
32
35
|
end
|