rack 2.0.9 → 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 +4 -4
- data/CHANGELOG.md +77 -0
- data/{COPYING → MIT-LICENSE} +4 -2
- data/README.rdoc +77 -117
- data/Rakefile +25 -18
- data/SPEC +3 -4
- 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.rb +63 -60
- data/lib/rack/auth/abstract/handler.rb +3 -1
- data/lib/rack/auth/abstract/request.rb +2 -0
- data/lib/rack/auth/basic.rb +4 -1
- data/lib/rack/auth/digest/md5.rb +9 -7
- data/lib/rack/auth/digest/nonce.rb +6 -3
- data/lib/rack/auth/digest/params.rb +4 -2
- data/lib/rack/auth/digest/request.rb +2 -0
- data/lib/rack/body_proxy.rb +3 -6
- data/lib/rack/builder.rb +39 -15
- data/lib/rack/cascade.rb +6 -5
- data/lib/rack/chunked.rb +29 -6
- data/lib/rack/common_logger.rb +9 -8
- data/lib/rack/conditional_get.rb +3 -1
- data/lib/rack/config.rb +2 -0
- data/lib/rack/content_length.rb +3 -1
- data/lib/rack/content_type.rb +3 -1
- data/lib/rack/core_ext/regexp.rb +14 -0
- data/lib/rack/deflater.rb +32 -17
- data/lib/rack/directory.rb +19 -16
- data/lib/rack/etag.rb +3 -1
- data/lib/rack/events.rb +5 -3
- data/lib/rack/file.rb +4 -173
- data/lib/rack/files.rb +178 -0
- data/lib/rack/handler.rb +7 -2
- data/lib/rack/handler/cgi.rb +3 -1
- data/lib/rack/handler/fastcgi.rb +4 -2
- data/lib/rack/handler/lsws.rb +3 -1
- data/lib/rack/handler/scgi.rb +9 -6
- data/lib/rack/handler/thin.rb +3 -1
- data/lib/rack/handler/webrick.rb +4 -2
- data/lib/rack/head.rb +2 -0
- data/lib/rack/lint.rb +14 -11
- data/lib/rack/lobster.rb +7 -5
- data/lib/rack/lock.rb +2 -0
- data/lib/rack/logger.rb +2 -0
- data/lib/rack/media_type.rb +10 -5
- data/lib/rack/method_override.rb +4 -2
- data/lib/rack/mime.rb +9 -1
- data/lib/rack/mock.rb +74 -15
- data/lib/rack/multipart.rb +5 -3
- data/lib/rack/multipart/generator.rb +6 -7
- data/lib/rack/multipart/parser.rb +51 -45
- data/lib/rack/multipart/uploaded_file.rb +2 -0
- data/lib/rack/null_logger.rb +2 -0
- data/lib/rack/query_parser.rb +51 -25
- data/lib/rack/recursive.rb +7 -5
- data/lib/rack/reloader.rb +10 -4
- data/lib/rack/request.rb +79 -26
- data/lib/rack/response.rb +71 -31
- data/lib/rack/rewindable_input.rb +4 -2
- data/lib/rack/runtime.rb +4 -2
- data/lib/rack/sendfile.rb +15 -8
- data/lib/rack/server.rb +88 -18
- data/lib/rack/session/abstract/id.rb +30 -20
- data/lib/rack/session/cookie.rb +10 -9
- data/lib/rack/session/memcache.rb +4 -93
- data/lib/rack/session/pool.rb +4 -2
- data/lib/rack/show_exceptions.rb +15 -9
- data/lib/rack/show_status.rb +4 -2
- data/lib/rack/static.rb +15 -10
- data/lib/rack/tempfile_reaper.rb +2 -0
- data/lib/rack/urlmap.rb +11 -2
- data/lib/rack/utils.rb +59 -72
- data/rack.gemspec +17 -7
- metadata +33 -175
- 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_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 -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 -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 -515
- 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 -722
- data/test/spec_null_logger.rb +0 -21
- data/test/spec_recursive.rb +0 -75
- data/test/spec_request.rb +0 -1407
- 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/recursive.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'uri'
|
2
4
|
|
3
5
|
module Rack
|
@@ -10,14 +12,14 @@ module Rack
|
|
10
12
|
class ForwardRequest < Exception
|
11
13
|
attr_reader :url, :env
|
12
14
|
|
13
|
-
def initialize(url, env={})
|
15
|
+
def initialize(url, env = {})
|
14
16
|
@url = URI(url)
|
15
17
|
@env = env
|
16
18
|
|
17
|
-
@env[PATH_INFO]
|
18
|
-
@env[QUERY_STRING]
|
19
|
-
@env[HTTP_HOST]
|
20
|
-
@env["HTTP_PORT"]
|
19
|
+
@env[PATH_INFO] = @url.path
|
20
|
+
@env[QUERY_STRING] = @url.query if @url.query
|
21
|
+
@env[HTTP_HOST] = @url.host if @url.host
|
22
|
+
@env["HTTP_PORT"] = @url.port if @url.port
|
21
23
|
@env[RACK_URL_SCHEME] = @url.scheme if @url.scheme
|
22
24
|
|
23
25
|
super "forwarding to #{url}"
|
data/lib/rack/reloader.rb
CHANGED
@@ -1,9 +1,13 @@
|
|
1
|
-
#
|
2
|
-
|
3
|
-
#
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Copyright (C) 2009-2018 Michael Fellinger <m.fellinger@gmail.com>
|
4
|
+
# Rack::Reloader is subject to the terms of an MIT-style license.
|
5
|
+
# See MIT-LICENSE or https://opensource.org/licenses/MIT.
|
4
6
|
|
5
7
|
require 'pathname'
|
6
8
|
|
9
|
+
require_relative 'core_ext/regexp'
|
10
|
+
|
7
11
|
module Rack
|
8
12
|
|
9
13
|
# High performant source reloader
|
@@ -20,6 +24,8 @@ module Rack
|
|
20
24
|
# It is performing a check/reload cycle at the start of every request, but
|
21
25
|
# also respects a cool down time, during which nothing will be done.
|
22
26
|
class Reloader
|
27
|
+
using ::Rack::RegexpExtensions
|
28
|
+
|
23
29
|
def initialize(app, cooldown = 10, backend = Stat)
|
24
30
|
@app = app
|
25
31
|
@cooldown = cooldown
|
@@ -69,7 +75,7 @@ module Rack
|
|
69
75
|
paths = ['./', *$LOAD_PATH].uniq
|
70
76
|
|
71
77
|
files.map{|file|
|
72
|
-
next if
|
78
|
+
next if /\.(so|bundle)$/.match?(file) # cannot reload compiled files
|
73
79
|
|
74
80
|
found, stat = figure_path(file, paths)
|
75
81
|
next unless found && stat && mtime = stat.mtime
|
data/lib/rack/request.rb
CHANGED
@@ -1,6 +1,10 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'rack/utils'
|
2
4
|
require 'rack/media_type'
|
3
5
|
|
6
|
+
require_relative 'core_ext/regexp'
|
7
|
+
|
4
8
|
module Rack
|
5
9
|
# Rack::Request provides a convenient interface to a Rack
|
6
10
|
# environment. It is stateless, the environment +env+ passed to the
|
@@ -11,7 +15,18 @@ module Rack
|
|
11
15
|
# req.params["data"]
|
12
16
|
|
13
17
|
class Request
|
14
|
-
|
18
|
+
using ::Rack::RegexpExtensions
|
19
|
+
|
20
|
+
class << self
|
21
|
+
attr_accessor :ip_filter
|
22
|
+
end
|
23
|
+
|
24
|
+
self.ip_filter = lambda { |ip| /\A127\.0\.0\.1\Z|\A(10|172\.(1[6-9]|2[0-9]|30|31)|192\.168)\.|\A::1\Z|\Afd[0-9a-f]{2}:.+|\Alocalhost\Z|\Aunix\Z|\Aunix:/i.match?(ip) }
|
25
|
+
ALLOWED_SCHEMES = %w(https http).freeze
|
26
|
+
SCHEME_WHITELIST = ALLOWED_SCHEMES
|
27
|
+
if Object.respond_to?(:deprecate_constant)
|
28
|
+
deprecate_constant :SCHEME_WHITELIST
|
29
|
+
end
|
15
30
|
|
16
31
|
def initialize(env)
|
17
32
|
@params = nil
|
@@ -100,7 +115,7 @@ module Rack
|
|
100
115
|
|
101
116
|
module Helpers
|
102
117
|
# The set of form-data media-types. Requests that do not indicate
|
103
|
-
# one of the media types
|
118
|
+
# one of the media types present in this list will not be eligible
|
104
119
|
# for form-data / param parsing.
|
105
120
|
FORM_DATA_MEDIA_TYPES = [
|
106
121
|
'application/x-www-form-urlencoded',
|
@@ -108,7 +123,7 @@ module Rack
|
|
108
123
|
]
|
109
124
|
|
110
125
|
# The set of media-types. Requests that do not indicate
|
111
|
-
# one of the media types
|
126
|
+
# one of the media types present in this list will not be eligible
|
112
127
|
# for param parsing like soap attachments or generic multiparts
|
113
128
|
PARSEABLE_DATA_MEDIA_TYPES = [
|
114
129
|
'multipart/related',
|
@@ -119,11 +134,11 @@ module Rack
|
|
119
134
|
# to include the port in a generated URI.
|
120
135
|
DEFAULT_PORTS = { 'http' => 80, 'https' => 443, 'coffee' => 80 }
|
121
136
|
|
122
|
-
HTTP_X_FORWARDED_SCHEME = 'HTTP_X_FORWARDED_SCHEME'
|
123
|
-
HTTP_X_FORWARDED_PROTO = 'HTTP_X_FORWARDED_PROTO'
|
124
|
-
HTTP_X_FORWARDED_HOST = 'HTTP_X_FORWARDED_HOST'
|
125
|
-
HTTP_X_FORWARDED_PORT = 'HTTP_X_FORWARDED_PORT'
|
126
|
-
HTTP_X_FORWARDED_SSL = 'HTTP_X_FORWARDED_SSL'
|
137
|
+
HTTP_X_FORWARDED_SCHEME = 'HTTP_X_FORWARDED_SCHEME'
|
138
|
+
HTTP_X_FORWARDED_PROTO = 'HTTP_X_FORWARDED_PROTO'
|
139
|
+
HTTP_X_FORWARDED_HOST = 'HTTP_X_FORWARDED_HOST'
|
140
|
+
HTTP_X_FORWARDED_PORT = 'HTTP_X_FORWARDED_PORT'
|
141
|
+
HTTP_X_FORWARDED_SSL = 'HTTP_X_FORWARDED_SSL'
|
127
142
|
|
128
143
|
def body; get_header(RACK_INPUT) end
|
129
144
|
def script_name; get_header(SCRIPT_NAME).to_s end
|
@@ -159,10 +174,10 @@ module Rack
|
|
159
174
|
def delete?; request_method == DELETE end
|
160
175
|
|
161
176
|
# Checks the HTTP request method (or verb) to see if it was of type GET
|
162
|
-
def get?; request_method == GET
|
177
|
+
def get?; request_method == GET end
|
163
178
|
|
164
179
|
# Checks the HTTP request method (or verb) to see if it was of type HEAD
|
165
|
-
def head?; request_method == HEAD
|
180
|
+
def head?; request_method == HEAD end
|
166
181
|
|
167
182
|
# Checks the HTTP request method (or verb) to see if it was of type OPTIONS
|
168
183
|
def options?; request_method == OPTIONS end
|
@@ -208,7 +223,7 @@ module Rack
|
|
208
223
|
string = get_header HTTP_COOKIE
|
209
224
|
|
210
225
|
return hash if string == get_header(RACK_REQUEST_COOKIE_STRING)
|
211
|
-
hash.replace Utils.parse_cookies_header
|
226
|
+
hash.replace Utils.parse_cookies_header string
|
212
227
|
set_header(RACK_REQUEST_COOKIE_STRING, string)
|
213
228
|
hash
|
214
229
|
end
|
@@ -232,18 +247,23 @@ module Rack
|
|
232
247
|
|
233
248
|
def host
|
234
249
|
# Remove port number.
|
235
|
-
host_with_port
|
250
|
+
h = host_with_port
|
251
|
+
if colon_index = h.index(":")
|
252
|
+
h[0, colon_index]
|
253
|
+
else
|
254
|
+
h
|
255
|
+
end
|
236
256
|
end
|
237
257
|
|
238
258
|
def port
|
239
|
-
if port = host_with_port
|
259
|
+
if port = extract_port(host_with_port)
|
240
260
|
port.to_i
|
241
261
|
elsif port = get_header(HTTP_X_FORWARDED_PORT)
|
242
262
|
port.to_i
|
243
263
|
elsif has_header?(HTTP_X_FORWARDED_HOST)
|
244
264
|
DEFAULT_PORTS[scheme]
|
245
265
|
elsif has_header?(HTTP_X_FORWARDED_PROTO)
|
246
|
-
DEFAULT_PORTS[get_header(HTTP_X_FORWARDED_PROTO)
|
266
|
+
DEFAULT_PORTS[extract_proto_header(get_header(HTTP_X_FORWARDED_PROTO))]
|
247
267
|
else
|
248
268
|
get_header(SERVER_PORT).to_i
|
249
269
|
end
|
@@ -260,6 +280,7 @@ module Rack
|
|
260
280
|
return remote_addrs.first if remote_addrs.any?
|
261
281
|
|
262
282
|
forwarded_ips = split_ip_addresses(get_header('HTTP_X_FORWARDED_FOR'))
|
283
|
+
.map { |ip| strip_port(ip) }
|
263
284
|
|
264
285
|
return reject_trusted_ip_addresses(forwarded_ips).last || forwarded_ips.first || get_header("REMOTE_ADDR")
|
265
286
|
end
|
@@ -337,7 +358,7 @@ module Rack
|
|
337
358
|
|
338
359
|
# Fix for Safari Ajax postings that always append \0
|
339
360
|
# form_vars.sub!(/\0\z/, '') # performance replacement:
|
340
|
-
form_vars.slice!(-1) if form_vars
|
361
|
+
form_vars.slice!(-1) if form_vars.end_with?("\0")
|
341
362
|
|
342
363
|
set_header RACK_REQUEST_FORM_VARS, form_vars
|
343
364
|
set_header RACK_REQUEST_FORM_HASH, parse_query(form_vars, '&')
|
@@ -386,12 +407,13 @@ module Rack
|
|
386
407
|
#
|
387
408
|
# <tt>env['rack.input']</tt> is not touched.
|
388
409
|
def delete_param(k)
|
389
|
-
|
410
|
+
post_value, get_value = self.POST.delete(k), self.GET.delete(k)
|
411
|
+
post_value || get_value
|
390
412
|
end
|
391
413
|
|
392
414
|
def base_url
|
393
415
|
url = "#{scheme}://#{host}"
|
394
|
-
url
|
416
|
+
url = "#{url}:#{port}" if port != DEFAULT_PORTS[scheme]
|
395
417
|
url
|
396
418
|
end
|
397
419
|
|
@@ -417,7 +439,7 @@ module Rack
|
|
417
439
|
end
|
418
440
|
|
419
441
|
def trusted_proxy?(ip)
|
420
|
-
ip
|
442
|
+
Rack::Request.ip_filter.call(ip)
|
421
443
|
end
|
422
444
|
|
423
445
|
# shortcut for <tt>request.params[key]</tt>
|
@@ -464,7 +486,7 @@ module Rack
|
|
464
486
|
Utils.default_query_parser
|
465
487
|
end
|
466
488
|
|
467
|
-
def parse_query(qs, d='&')
|
489
|
+
def parse_query(qs, d = '&')
|
468
490
|
query_parser.parse_nested_query(qs, d)
|
469
491
|
end
|
470
492
|
|
@@ -476,21 +498,52 @@ module Rack
|
|
476
498
|
ip_addresses ? ip_addresses.strip.split(/[,\s]+/) : []
|
477
499
|
end
|
478
500
|
|
501
|
+
def strip_port(ip_address)
|
502
|
+
# IPv6 format with optional port: "[2001:db8:cafe::17]:47011"
|
503
|
+
# returns: "2001:db8:cafe::17"
|
504
|
+
sep_start = ip_address.index('[')
|
505
|
+
sep_end = ip_address.index(']')
|
506
|
+
if (sep_start && sep_end)
|
507
|
+
return ip_address[sep_start + 1, sep_end - 1]
|
508
|
+
end
|
509
|
+
|
510
|
+
# IPv4 format with optional port: "192.0.2.43:47011"
|
511
|
+
# returns: "192.0.2.43"
|
512
|
+
sep = ip_address.index(':')
|
513
|
+
if (sep && ip_address.count(':') == 1)
|
514
|
+
return ip_address[0, sep]
|
515
|
+
end
|
516
|
+
|
517
|
+
ip_address
|
518
|
+
end
|
519
|
+
|
479
520
|
def reject_trusted_ip_addresses(ip_addresses)
|
480
521
|
ip_addresses.reject { |ip| trusted_proxy?(ip) }
|
481
522
|
end
|
482
523
|
|
483
524
|
def forwarded_scheme
|
484
|
-
|
485
|
-
|
486
|
-
|
487
|
-
]
|
525
|
+
allowed_scheme(get_header(HTTP_X_FORWARDED_SCHEME)) ||
|
526
|
+
allowed_scheme(extract_proto_header(get_header(HTTP_X_FORWARDED_PROTO)))
|
527
|
+
end
|
488
528
|
|
489
|
-
|
490
|
-
|
529
|
+
def allowed_scheme(header)
|
530
|
+
header if ALLOWED_SCHEMES.include?(header)
|
531
|
+
end
|
532
|
+
|
533
|
+
def extract_proto_header(header)
|
534
|
+
if header
|
535
|
+
if (comma_index = header.index(','))
|
536
|
+
header[0, comma_index]
|
537
|
+
else
|
538
|
+
header
|
539
|
+
end
|
491
540
|
end
|
541
|
+
end
|
492
542
|
|
493
|
-
|
543
|
+
def extract_port(uri)
|
544
|
+
if (colon_index = uri.index(':'))
|
545
|
+
uri[colon_index + 1, uri.length]
|
546
|
+
end
|
494
547
|
end
|
495
548
|
end
|
496
549
|
|
data/lib/rack/response.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'rack/request'
|
2
4
|
require 'rack/utils'
|
3
5
|
require 'rack/body_proxy'
|
@@ -23,32 +25,34 @@ module Rack
|
|
23
25
|
attr_reader :header
|
24
26
|
alias headers header
|
25
27
|
|
26
|
-
CHUNKED = 'chunked'
|
28
|
+
CHUNKED = 'chunked'
|
29
|
+
STATUS_WITH_NO_ENTITY_BODY = Utils::STATUS_WITH_NO_ENTITY_BODY
|
27
30
|
|
28
|
-
def initialize(body=
|
31
|
+
def initialize(body = nil, status = 200, header = {})
|
29
32
|
@status = status.to_i
|
30
|
-
@header = Utils::HeaderHash.new
|
33
|
+
@header = Utils::HeaderHash.new(header)
|
31
34
|
|
32
|
-
@writer
|
33
|
-
@block = nil
|
34
|
-
@length = 0
|
35
|
+
@writer = self.method(:append)
|
35
36
|
|
36
|
-
@
|
37
|
+
@block = nil
|
38
|
+
@length = 0
|
37
39
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
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
|
44
47
|
else
|
45
|
-
|
48
|
+
@body = body
|
49
|
+
@buffered = false
|
46
50
|
end
|
47
51
|
|
48
|
-
yield self
|
52
|
+
yield self if block_given?
|
49
53
|
end
|
50
54
|
|
51
|
-
def redirect(target, status=302)
|
55
|
+
def redirect(target, status = 302)
|
52
56
|
self.status = status
|
53
57
|
self.location = target
|
54
58
|
end
|
@@ -58,41 +62,45 @@ module Rack
|
|
58
62
|
end
|
59
63
|
|
60
64
|
def finish(&block)
|
61
|
-
|
62
|
-
|
63
|
-
if [204, 304].include?(status.to_i)
|
65
|
+
if STATUS_WITH_NO_ENTITY_BODY[status.to_i]
|
64
66
|
delete_header CONTENT_TYPE
|
65
67
|
delete_header CONTENT_LENGTH
|
66
68
|
close
|
67
69
|
[status.to_i, header, []]
|
68
70
|
else
|
69
|
-
|
71
|
+
if block_given?
|
72
|
+
@block = block
|
73
|
+
[status.to_i, header, self]
|
74
|
+
else
|
75
|
+
[status.to_i, header, @body]
|
76
|
+
end
|
70
77
|
end
|
71
78
|
end
|
79
|
+
|
72
80
|
alias to_a finish # For *response
|
73
|
-
alias to_ary finish # For implicit-splat on Ruby 1.9.2
|
74
81
|
|
75
82
|
def each(&callback)
|
76
83
|
@body.each(&callback)
|
77
|
-
@
|
78
|
-
|
84
|
+
@buffered = true
|
85
|
+
|
86
|
+
if @block
|
87
|
+
@writer = callback
|
88
|
+
@block.call(self)
|
89
|
+
end
|
79
90
|
end
|
80
91
|
|
81
92
|
# Append to body and update Content-Length.
|
82
93
|
#
|
83
94
|
# NOTE: Do not mix #write and direct #body access!
|
84
95
|
#
|
85
|
-
def write(
|
86
|
-
|
87
|
-
@length += s.bytesize unless chunked?
|
88
|
-
@writer.call s
|
96
|
+
def write(chunk)
|
97
|
+
buffered_body!
|
89
98
|
|
90
|
-
|
91
|
-
str
|
99
|
+
@writer.call(chunk.to_s)
|
92
100
|
end
|
93
101
|
|
94
102
|
def close
|
95
|
-
body.close if body.respond_to?(:close)
|
103
|
+
@body.close if @body.respond_to?(:close)
|
96
104
|
end
|
97
105
|
|
98
106
|
def empty?
|
@@ -184,7 +192,7 @@ module Rack
|
|
184
192
|
set_header SET_COOKIE, ::Rack::Utils.add_cookie_to_header(cookie_header, key, value)
|
185
193
|
end
|
186
194
|
|
187
|
-
def delete_cookie(key, value={})
|
195
|
+
def delete_cookie(key, value = {})
|
188
196
|
set_header SET_COOKIE, ::Rack::Utils.add_remove_cookie_to_header(get_header(SET_COOKIE), key, value)
|
189
197
|
end
|
190
198
|
|
@@ -211,6 +219,38 @@ module Rack
|
|
211
219
|
def etag= v
|
212
220
|
set_header ETAG, v
|
213
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
|
253
|
+
end
|
214
254
|
end
|
215
255
|
|
216
256
|
include Helpers
|
@@ -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.
|
@@ -72,7 +74,7 @@ module Rack
|
|
72
74
|
@unlinked = true
|
73
75
|
end
|
74
76
|
|
75
|
-
buffer = ""
|
77
|
+
buffer = "".dup
|
76
78
|
while @io.read(1024 * 4, buffer)
|
77
79
|
entire_buffer_written_out = false
|
78
80
|
while !entire_buffer_written_out
|