rack 2.0.4 → 2.1.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/{HISTORY.md → CHANGELOG.md} +220 -155
- data/{COPYING → MIT-LICENSE} +4 -2
- data/README.rdoc +77 -119
- 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 +38 -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 +28 -17
- data/lib/rack/directory.rb +17 -14
- data/lib/rack/etag.rb +3 -1
- data/lib/rack/events.rb +5 -3
- data/lib/rack/file.rb +5 -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 +9 -3
- 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 +54 -51
- 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 +89 -23
- 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 -16
- data/lib/rack/session/abstract/id.rb +104 -21
- data/lib/rack/session/cookie.rb +21 -11
- data/lib/rack/session/memcache.rb +4 -87
- data/lib/rack/session/pool.rb +17 -8
- data/lib/rack/show_exceptions.rb +16 -10
- 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 +55 -70
- data/rack.gemspec +19 -9
- metadata +32 -173
- 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 -96
- 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 -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 -45
- 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 -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,6 +15,19 @@ module Rack
|
|
11
15
|
# req.params["data"]
|
12
16
|
|
13
17
|
class Request
|
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
|
30
|
+
|
14
31
|
def initialize(env)
|
15
32
|
@params = nil
|
16
33
|
super(env)
|
@@ -98,7 +115,7 @@ module Rack
|
|
98
115
|
|
99
116
|
module Helpers
|
100
117
|
# The set of form-data media-types. Requests that do not indicate
|
101
|
-
# one of the media types
|
118
|
+
# one of the media types present in this list will not be eligible
|
102
119
|
# for form-data / param parsing.
|
103
120
|
FORM_DATA_MEDIA_TYPES = [
|
104
121
|
'application/x-www-form-urlencoded',
|
@@ -106,7 +123,7 @@ module Rack
|
|
106
123
|
]
|
107
124
|
|
108
125
|
# The set of media-types. Requests that do not indicate
|
109
|
-
# one of the media types
|
126
|
+
# one of the media types present in this list will not be eligible
|
110
127
|
# for param parsing like soap attachments or generic multiparts
|
111
128
|
PARSEABLE_DATA_MEDIA_TYPES = [
|
112
129
|
'multipart/related',
|
@@ -117,11 +134,11 @@ module Rack
|
|
117
134
|
# to include the port in a generated URI.
|
118
135
|
DEFAULT_PORTS = { 'http' => 80, 'https' => 443, 'coffee' => 80 }
|
119
136
|
|
120
|
-
HTTP_X_FORWARDED_SCHEME = 'HTTP_X_FORWARDED_SCHEME'
|
121
|
-
HTTP_X_FORWARDED_PROTO = 'HTTP_X_FORWARDED_PROTO'
|
122
|
-
HTTP_X_FORWARDED_HOST = 'HTTP_X_FORWARDED_HOST'
|
123
|
-
HTTP_X_FORWARDED_PORT = 'HTTP_X_FORWARDED_PORT'
|
124
|
-
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'
|
125
142
|
|
126
143
|
def body; get_header(RACK_INPUT) end
|
127
144
|
def script_name; get_header(SCRIPT_NAME).to_s end
|
@@ -157,10 +174,10 @@ module Rack
|
|
157
174
|
def delete?; request_method == DELETE end
|
158
175
|
|
159
176
|
# Checks the HTTP request method (or verb) to see if it was of type GET
|
160
|
-
def get?; request_method == GET
|
177
|
+
def get?; request_method == GET end
|
161
178
|
|
162
179
|
# Checks the HTTP request method (or verb) to see if it was of type HEAD
|
163
|
-
def head?; request_method == HEAD
|
180
|
+
def head?; request_method == HEAD end
|
164
181
|
|
165
182
|
# Checks the HTTP request method (or verb) to see if it was of type OPTIONS
|
166
183
|
def options?; request_method == OPTIONS end
|
@@ -188,10 +205,8 @@ module Rack
|
|
188
205
|
'https'
|
189
206
|
elsif get_header(HTTP_X_FORWARDED_SSL) == 'on'
|
190
207
|
'https'
|
191
|
-
elsif
|
192
|
-
|
193
|
-
elsif get_header(HTTP_X_FORWARDED_PROTO)
|
194
|
-
get_header(HTTP_X_FORWARDED_PROTO).split(',')[0]
|
208
|
+
elsif forwarded_scheme
|
209
|
+
forwarded_scheme
|
195
210
|
else
|
196
211
|
get_header(RACK_URL_SCHEME)
|
197
212
|
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,8 +280,9 @@ 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
|
-
return reject_trusted_ip_addresses(forwarded_ips).last || get_header("REMOTE_ADDR")
|
285
|
+
return reject_trusted_ip_addresses(forwarded_ips).last || forwarded_ips.first || get_header("REMOTE_ADDR")
|
265
286
|
end
|
266
287
|
|
267
288
|
# The media type (type/subtype) portion of the CONTENT_TYPE header
|
@@ -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,9 +498,53 @@ 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
|
523
|
+
|
524
|
+
def forwarded_scheme
|
525
|
+
allowed_scheme(get_header(HTTP_X_FORWARDED_SCHEME)) ||
|
526
|
+
allowed_scheme(extract_proto_header(get_header(HTTP_X_FORWARDED_PROTO)))
|
527
|
+
end
|
528
|
+
|
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
|
540
|
+
end
|
541
|
+
end
|
542
|
+
|
543
|
+
def extract_port(uri)
|
544
|
+
if (colon_index = uri.index(':'))
|
545
|
+
uri[colon_index + 1, uri.length]
|
546
|
+
end
|
547
|
+
end
|
482
548
|
end
|
483
549
|
|
484
550
|
include Env
|
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
|