rack 1.6.0.beta → 1.6.0.beta2
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/SPEC +3 -3
- data/lib/rack.rb +10 -0
- data/lib/rack/auth/abstract/handler.rb +4 -4
- data/lib/rack/auth/digest/request.rb +1 -1
- data/lib/rack/cascade.rb +1 -1
- data/lib/rack/chunked.rb +2 -2
- data/lib/rack/commonlogger.rb +4 -4
- data/lib/rack/conditionalget.rb +3 -3
- data/lib/rack/content_length.rb +2 -2
- data/lib/rack/content_type.rb +1 -1
- data/lib/rack/deflater.rb +4 -5
- data/lib/rack/directory.rb +5 -5
- data/lib/rack/etag.rb +7 -6
- data/lib/rack/file.rb +32 -14
- data/lib/rack/handler.rb +1 -1
- data/lib/rack/handler/cgi.rb +1 -1
- data/lib/rack/handler/fastcgi.rb +1 -1
- data/lib/rack/handler/lsws.rb +1 -1
- data/lib/rack/handler/mongrel.rb +1 -1
- data/lib/rack/handler/scgi.rb +2 -2
- data/lib/rack/handler/webrick.rb +6 -4
- data/lib/rack/head.rb +1 -1
- data/lib/rack/lint.rb +30 -16
- data/lib/rack/lobster.rb +2 -2
- data/lib/rack/methodoverride.rb +3 -3
- data/lib/rack/mock.rb +7 -7
- data/lib/rack/multipart/parser.rb +21 -7
- data/lib/rack/recursive.rb +6 -5
- data/lib/rack/request.rb +6 -6
- data/lib/rack/response.rb +8 -6
- data/lib/rack/runtime.rb +2 -1
- data/lib/rack/sendfile.rb +2 -2
- data/lib/rack/server.rb +7 -10
- data/lib/rack/showexceptions.rb +2 -2
- data/lib/rack/showstatus.rb +3 -3
- data/lib/rack/static.rb +1 -1
- data/lib/rack/urlmap.rb +2 -2
- data/lib/rack/utils.rb +14 -10
- data/rack.gemspec +1 -1
- data/test/spec_body_proxy.rb +16 -0
- data/test/spec_lint.rb +20 -9
- data/test/spec_multipart.rb +38 -16
- data/test/spec_request.rb +17 -0
- data/test/spec_server.rb +22 -13
- metadata +2 -2
data/lib/rack/showexceptions.rb
CHANGED
data/lib/rack/showstatus.rb
CHANGED
@@ -3,7 +3,7 @@ require 'rack/request'
|
|
3
3
|
require 'rack/utils'
|
4
4
|
|
5
5
|
module Rack
|
6
|
-
# Rack::ShowStatus catches all empty responses and replaces them
|
6
|
+
# Rack::ShowStatus catches all empty responses and replaces them
|
7
7
|
# with a site explaining the error.
|
8
8
|
#
|
9
9
|
# Additional details can be put into <tt>rack.showstatus.detail</tt>
|
@@ -19,7 +19,7 @@ module Rack
|
|
19
19
|
def call(env)
|
20
20
|
status, headers, body = @app.call(env)
|
21
21
|
headers = Utils::HeaderHash.new(headers)
|
22
|
-
empty = headers[
|
22
|
+
empty = headers[CONTENT_LENGTH].to_i <= 0
|
23
23
|
|
24
24
|
# client or server error, or explicit message
|
25
25
|
if (status.to_i >= 400 && empty) || env["rack.showstatus.detail"]
|
@@ -35,7 +35,7 @@ module Rack
|
|
35
35
|
|
36
36
|
body = @template.result(binding)
|
37
37
|
size = Rack::Utils.bytesize(body)
|
38
|
-
[status, headers.merge(
|
38
|
+
[status, headers.merge(CONTENT_TYPE => "text/html", CONTENT_LENGTH => size.to_s), [body]]
|
39
39
|
else
|
40
40
|
[status, headers, body]
|
41
41
|
end
|
data/lib/rack/static.rb
CHANGED
data/lib/rack/urlmap.rb
CHANGED
@@ -41,7 +41,7 @@ module Rack
|
|
41
41
|
end
|
42
42
|
|
43
43
|
def call(env)
|
44
|
-
path = env[
|
44
|
+
path = env[PATH_INFO]
|
45
45
|
script_name = env['SCRIPT_NAME']
|
46
46
|
hHost = env['HTTP_HOST']
|
47
47
|
sName = env['SERVER_NAME']
|
@@ -66,7 +66,7 @@ module Rack
|
|
66
66
|
return app.call(env)
|
67
67
|
end
|
68
68
|
|
69
|
-
[404, {
|
69
|
+
[404, {CONTENT_TYPE => "text/plain", "X-Cascade" => "pass"}, ["Not Found: #{path}"]]
|
70
70
|
|
71
71
|
ensure
|
72
72
|
env['PATH_INFO'] = path
|
data/lib/rack/utils.rb
CHANGED
@@ -61,12 +61,18 @@ module Rack
|
|
61
61
|
|
62
62
|
class << self
|
63
63
|
attr_accessor :key_space_limit
|
64
|
+
attr_accessor :multipart_part_limit
|
64
65
|
end
|
65
66
|
|
66
67
|
# The default number of bytes to allow parameter keys to take up.
|
67
68
|
# This helps prevent a rogue client from flooding a Request.
|
68
69
|
self.key_space_limit = 65536
|
69
70
|
|
71
|
+
# The maximum number of parts a request can contain. Accepting to many part
|
72
|
+
# can lead to the server running out of file handles.
|
73
|
+
# Set to `0` for no limit.
|
74
|
+
self.multipart_part_limit = (ENV['RACK_MULTIPART_LIMIT'] || 128).to_i
|
75
|
+
|
70
76
|
# Stolen from Mongrel, with some small modifications:
|
71
77
|
# Parses a query string by breaking it up at the '&'
|
72
78
|
# and ';' characters. You can also use this to parse
|
@@ -418,7 +424,7 @@ module Rack
|
|
418
424
|
# Constant time string comparison.
|
419
425
|
#
|
420
426
|
# NOTE: the values compared should be of fixed length, such as strings
|
421
|
-
# that have
|
427
|
+
# that have already been processed by HMAC. This should not be used
|
422
428
|
# on variable length plaintext strings because it could leak length info
|
423
429
|
# via timing attacks.
|
424
430
|
def secure_compare(a, b)
|
@@ -567,9 +573,9 @@ module Rack
|
|
567
573
|
|
568
574
|
# Every standard HTTP code mapped to the appropriate message.
|
569
575
|
# Generated with:
|
570
|
-
#
|
571
|
-
#
|
572
|
-
#
|
576
|
+
# curl -s https://www.iana.org/assignments/http-status-codes/http-status-codes-1.csv | \
|
577
|
+
# ruby -ne 'm = /^(\d{3}),(?!Unassigned|\(Unused\))([^,]+)/.match($_) and \
|
578
|
+
# puts "#{m[1]} => \x27#{m[2].strip}\x27,"'
|
573
579
|
HTTP_STATUS_CODES = {
|
574
580
|
100 => 'Continue',
|
575
581
|
101 => 'Switching Protocols',
|
@@ -590,7 +596,6 @@ module Rack
|
|
590
596
|
303 => 'See Other',
|
591
597
|
304 => 'Not Modified',
|
592
598
|
305 => 'Use Proxy',
|
593
|
-
306 => 'Reserved',
|
594
599
|
307 => 'Temporary Redirect',
|
595
600
|
308 => 'Permanent Redirect',
|
596
601
|
400 => 'Bad Request',
|
@@ -606,12 +611,11 @@ module Rack
|
|
606
611
|
410 => 'Gone',
|
607
612
|
411 => 'Length Required',
|
608
613
|
412 => 'Precondition Failed',
|
609
|
-
413 => '
|
610
|
-
414 => '
|
614
|
+
413 => 'Payload Too Large',
|
615
|
+
414 => 'URI Too Long',
|
611
616
|
415 => 'Unsupported Media Type',
|
612
|
-
416 => '
|
617
|
+
416 => 'Range Not Satisfiable',
|
613
618
|
417 => 'Expectation Failed',
|
614
|
-
418 => 'I\'m a teapot',
|
615
619
|
422 => 'Unprocessable Entity',
|
616
620
|
423 => 'Locked',
|
617
621
|
424 => 'Failed Dependency',
|
@@ -625,7 +629,7 @@ module Rack
|
|
625
629
|
503 => 'Service Unavailable',
|
626
630
|
504 => 'Gateway Timeout',
|
627
631
|
505 => 'HTTP Version Not Supported',
|
628
|
-
506 => 'Variant Also Negotiates
|
632
|
+
506 => 'Variant Also Negotiates',
|
629
633
|
507 => 'Insufficient Storage',
|
630
634
|
508 => 'Loop Detected',
|
631
635
|
510 => 'Not Extended',
|
data/rack.gemspec
CHANGED
data/test/spec_body_proxy.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'rack/body_proxy'
|
2
2
|
require 'stringio'
|
3
|
+
require 'ostruct'
|
3
4
|
|
4
5
|
describe Rack::BodyProxy do
|
5
6
|
should 'call each on the wrapped body' do
|
@@ -49,6 +50,21 @@ describe Rack::BodyProxy do
|
|
49
50
|
called.should.equal true
|
50
51
|
end
|
51
52
|
|
53
|
+
should 'allow multiple arguments in respond_to?' do
|
54
|
+
body = []
|
55
|
+
proxy = Rack::BodyProxy.new(body) { }
|
56
|
+
proc { proxy.respond_to?(:foo, false) }.should.not.raise
|
57
|
+
end
|
58
|
+
|
59
|
+
should 'not respond to :to_ary' do
|
60
|
+
body = OpenStruct.new(:to_ary => true)
|
61
|
+
body.respond_to?(:to_ary).should.equal true
|
62
|
+
|
63
|
+
proxy = Rack::BodyProxy.new(body) { }
|
64
|
+
proxy.respond_to?(:to_ary).should.equal false
|
65
|
+
proxy.respond_to?("to_ary").should.equal false
|
66
|
+
end
|
67
|
+
|
52
68
|
should 'not close more than one time' do
|
53
69
|
count = 0
|
54
70
|
proxy = Rack::BodyProxy.new([]) { count += 1; raise "Block invoked more than 1 time!" if count > 1 }
|
data/test/spec_lint.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'stringio'
|
2
|
+
require 'tempfile'
|
2
3
|
require 'rack/lint'
|
3
4
|
require 'rack/mock'
|
4
5
|
|
@@ -74,6 +75,23 @@ describe Rack::Lint do
|
|
74
75
|
}.should.raise(Rack::Lint::LintError).
|
75
76
|
message.should.equal("logger [] must respond to info")
|
76
77
|
|
78
|
+
lambda {
|
79
|
+
Rack::Lint.new(nil).call(env("rack.multipart.buffer_size" => 0))
|
80
|
+
}.should.raise(Rack::Lint::LintError).
|
81
|
+
message.should.equal("rack.multipart.buffer_size must be an Integer > 0 if specified")
|
82
|
+
|
83
|
+
lambda {
|
84
|
+
Rack::Lint.new(nil).call(env("rack.multipart.tempfile_factory" => Tempfile))
|
85
|
+
}.should.raise(Rack::Lint::LintError).
|
86
|
+
message.should.equal("rack.multipart.tempfile_factory must respond to #call")
|
87
|
+
|
88
|
+
lambda {
|
89
|
+
Rack::Lint.new(lambda { |env|
|
90
|
+
env['rack.multipart.tempfile_factory'].call("testfile", "text/plain")
|
91
|
+
}).call(env("rack.multipart.tempfile_factory" => lambda { |filename, content_type| Object.new }))
|
92
|
+
}.should.raise(Rack::Lint::LintError).
|
93
|
+
message.should.equal("rack.multipart.tempfile_factory return value must respond to #<<")
|
94
|
+
|
77
95
|
lambda {
|
78
96
|
Rack::Lint.new(nil).call(env("REQUEST_METHOD" => "FUCKUP?"))
|
79
97
|
}.should.raise(Rack::Lint::LintError).
|
@@ -191,17 +209,10 @@ describe Rack::Lint do
|
|
191
209
|
|
192
210
|
lambda {
|
193
211
|
Rack::Lint.new(lambda { |env|
|
194
|
-
[200, {"
|
195
|
-
}).call(env({}))
|
196
|
-
}.should.raise(Rack::Lint::LintError).
|
197
|
-
message.should.match(/must not end/)
|
198
|
-
|
199
|
-
lambda {
|
200
|
-
Rack::Lint.new(lambda { |env|
|
201
|
-
[200, {"..%%quark%%.." => "text/plain"}, []]
|
212
|
+
[200, {"([{<quark>}])?" => "text/plain"}, []]
|
202
213
|
}).call(env({}))
|
203
214
|
}.should.raise(Rack::Lint::LintError).
|
204
|
-
message.should.equal("invalid header name:
|
215
|
+
message.should.equal("invalid header name: ([{<quark>}])?")
|
205
216
|
|
206
217
|
lambda {
|
207
218
|
Rack::Lint.new(lambda { |env|
|
data/test/spec_multipart.rb
CHANGED
@@ -153,6 +153,12 @@ describe Rack::Multipart do
|
|
153
153
|
params["files"][:tempfile].read.should.equal "contents"
|
154
154
|
end
|
155
155
|
|
156
|
+
should "preserve extension in the created tempfile" do
|
157
|
+
env = Rack::MockRequest.env_for("/", multipart_fixture(:text))
|
158
|
+
params = Rack::Multipart.parse_multipart(env)
|
159
|
+
File.extname(params["files"][:tempfile].path).should.equal ".txt"
|
160
|
+
end
|
161
|
+
|
156
162
|
should "parse multipart upload with text file with no name field" do
|
157
163
|
env = Rack::MockRequest.env_for("/", multipart_fixture(:filename_and_no_name))
|
158
164
|
params = Rack::Multipart.parse_multipart(env)
|
@@ -165,6 +171,15 @@ describe Rack::Multipart do
|
|
165
171
|
params["file1.txt"][:tempfile].read.should.equal "contents"
|
166
172
|
end
|
167
173
|
|
174
|
+
should "parse multipart upload file using custom tempfile class" do
|
175
|
+
env = Rack::MockRequest.env_for("/", multipart_fixture(:text))
|
176
|
+
my_tempfile = ""
|
177
|
+
env['rack.multipart.tempfile_factory'] = lambda { |filename, content_type| my_tempfile }
|
178
|
+
params = Rack::Multipart.parse_multipart(env)
|
179
|
+
params["files"][:tempfile].object_id.should.equal my_tempfile.object_id
|
180
|
+
my_tempfile.should.equal "contents"
|
181
|
+
end
|
182
|
+
|
168
183
|
should "parse multipart upload with nested parameters" do
|
169
184
|
env = Rack::MockRequest.env_for("/", multipart_fixture(:nested))
|
170
185
|
params = Rack::Multipart.parse_multipart(env)
|
@@ -436,22 +451,29 @@ Content-Type: image/jpeg\r
|
|
436
451
|
end
|
437
452
|
|
438
453
|
it "builds complete params with the chunk size of 16384 slicing exactly on boundary" do
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
454
|
+
begin
|
455
|
+
previous_limit = Rack::Utils.multipart_part_limit
|
456
|
+
Rack::Utils.multipart_part_limit = 256
|
457
|
+
|
458
|
+
data = File.open(multipart_file("fail_16384_nofile"), 'rb') { |f| f.read }.gsub(/\n/, "\r\n")
|
459
|
+
options = {
|
460
|
+
"CONTENT_TYPE" => "multipart/form-data; boundary=----WebKitFormBoundaryWsY0GnpbI5U7ztzo",
|
461
|
+
"CONTENT_LENGTH" => data.length.to_s,
|
462
|
+
:input => StringIO.new(data)
|
463
|
+
}
|
464
|
+
env = Rack::MockRequest.env_for("/", options)
|
465
|
+
params = Rack::Multipart.parse_multipart(env)
|
466
|
+
|
467
|
+
params.should.not.equal nil
|
468
|
+
params.keys.should.include "AAAAAAAAAAAAAAAAAAA"
|
469
|
+
params["AAAAAAAAAAAAAAAAAAA"].keys.should.include "PLAPLAPLA_MEMMEMMEMM_ATTRATTRER"
|
470
|
+
params["AAAAAAAAAAAAAAAAAAA"]["PLAPLAPLA_MEMMEMMEMM_ATTRATTRER"].keys.should.include "new"
|
471
|
+
params["AAAAAAAAAAAAAAAAAAA"]["PLAPLAPLA_MEMMEMMEMM_ATTRATTRER"]["new"].keys.should.include "-2"
|
472
|
+
params["AAAAAAAAAAAAAAAAAAA"]["PLAPLAPLA_MEMMEMMEMM_ATTRATTRER"]["new"]["-2"].keys.should.include "ba_unit_id"
|
473
|
+
params["AAAAAAAAAAAAAAAAAAA"]["PLAPLAPLA_MEMMEMMEMM_ATTRATTRER"]["new"]["-2"]["ba_unit_id"].should.equal "1017"
|
474
|
+
ensure
|
475
|
+
Rack::Utils.multipart_part_limit = previous_limit
|
476
|
+
end
|
455
477
|
end
|
456
478
|
|
457
479
|
should "return nil if no UploadedFiles were used" do
|
data/test/spec_request.rb
CHANGED
@@ -2,6 +2,7 @@ require 'stringio'
|
|
2
2
|
require 'cgi'
|
3
3
|
require 'rack/request'
|
4
4
|
require 'rack/mock'
|
5
|
+
require 'securerandom'
|
5
6
|
|
6
7
|
describe Rack::Request do
|
7
8
|
should "wrap the rack variables" do
|
@@ -744,6 +745,22 @@ EOF
|
|
744
745
|
f[:tempfile].size.should.equal 76
|
745
746
|
end
|
746
747
|
|
748
|
+
should "MultipartPartLimitError when request has too many multipart parts if limit set" do
|
749
|
+
begin
|
750
|
+
data = 10000.times.map { "--AaB03x\r\nContent-Type: text/plain\r\nContent-Disposition: attachment; name=#{SecureRandom.hex(10)}; filename=#{SecureRandom.hex(10)}\r\n\r\ncontents\r\n" }.join("\r\n")
|
751
|
+
data += "--AaB03x--\r"
|
752
|
+
|
753
|
+
options = {
|
754
|
+
"CONTENT_TYPE" => "multipart/form-data; boundary=AaB03x",
|
755
|
+
"CONTENT_LENGTH" => data.length.to_s,
|
756
|
+
:input => StringIO.new(data)
|
757
|
+
}
|
758
|
+
|
759
|
+
request = Rack::Request.new Rack::MockRequest.env_for("/", options)
|
760
|
+
lambda { request.POST }.should.raise(Rack::Multipart::MultipartPartLimitError)
|
761
|
+
end
|
762
|
+
end
|
763
|
+
|
747
764
|
should "parse big multipart form data" do
|
748
765
|
input = <<EOF
|
749
766
|
--AaB03x\r
|
data/test/spec_server.rb
CHANGED
@@ -28,21 +28,35 @@ describe Rack::Server do
|
|
28
28
|
Rack::MockRequest.new(server.app).get("/").body.to_s.should.equal 'success'
|
29
29
|
end
|
30
30
|
|
31
|
-
should "
|
31
|
+
should "allow subclasses to override middleware" do
|
32
|
+
server = Class.new(Rack::Server).class_eval { def middleware; Hash.new [] end; self }
|
33
|
+
server.middleware['deployment'].should.not.equal []
|
34
|
+
server.new(:app => 'foo').middleware['deployment'].should.equal []
|
35
|
+
end
|
36
|
+
|
37
|
+
should "allow subclasses to override default middleware" do
|
38
|
+
server = Class.new(Rack::Server).instance_eval { def default_middleware_by_environment; Hash.new [] end; self }
|
39
|
+
server.middleware['deployment'].should.equal []
|
40
|
+
server.new(:app => 'foo').middleware['deployment'].should.equal []
|
41
|
+
end
|
42
|
+
|
43
|
+
should "only provide default middleware for development and deployment environments" do
|
44
|
+
Rack::Server.default_middleware_by_environment.keys.sort.should.equal %w(deployment development)
|
45
|
+
end
|
46
|
+
|
47
|
+
should "always return an empty array for unknown environments" do
|
32
48
|
server = Rack::Server.new(:app => 'foo')
|
33
|
-
server.
|
34
|
-
server.default_middleware_by_environment['none'].flatten.should.not.include(Rack::Lint)
|
49
|
+
server.middleware['production'].should.equal []
|
35
50
|
end
|
36
51
|
|
37
|
-
should "not include Rack::
|
52
|
+
should "not include Rack::Lint in deployment environment" do
|
38
53
|
server = Rack::Server.new(:app => 'foo')
|
39
|
-
server.
|
40
|
-
server.default_middleware_by_environment['none'].flatten.should.not.include(Rack::ShowExceptions)
|
54
|
+
server.middleware['deployment'].flatten.should.not.include(Rack::Lint)
|
41
55
|
end
|
42
56
|
|
43
|
-
should "
|
57
|
+
should "not include Rack::ShowExceptions in deployment environment" do
|
44
58
|
server = Rack::Server.new(:app => 'foo')
|
45
|
-
server.
|
59
|
+
server.middleware['deployment'].flatten.should.not.include(Rack::ShowExceptions)
|
46
60
|
end
|
47
61
|
|
48
62
|
should "include Rack::TempfileReaper in deployment environment" do
|
@@ -66,11 +80,6 @@ describe Rack::Server do
|
|
66
80
|
Rack::Server.logging_middleware.call(server).should.eql(nil)
|
67
81
|
end
|
68
82
|
|
69
|
-
should "not force any middleware under the none configuration" do
|
70
|
-
server = Rack::Server.new(:app => 'foo')
|
71
|
-
server.default_middleware_by_environment['none'].should.be.empty
|
72
|
-
end
|
73
|
-
|
74
83
|
should "use a full path to the pidfile" do
|
75
84
|
# avoids issues with daemonize chdir
|
76
85
|
opts = Rack::Server.new.send(:parse_options, %w[--pid testing.pid])
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rack
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.6.0.
|
4
|
+
version: 1.6.0.beta2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Christian Neukirchen
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-
|
11
|
+
date: 2014-11-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bacon
|