Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Specify rack.input#read to behave the same like IO#read.

  • Loading branch information...
commit 385f621683f0be8fdce820bd900f39f5c904f89f 1 parent b95cec5
@FooBarWidget FooBarWidget authored
Showing with 133 additions and 9 deletions.
  1. +30 −7 lib/rack/lint.rb
  2. +103 −2 test/spec_rack_lint.rb
View
37 lib/rack/lint.rb
@@ -236,21 +236,44 @@ def gets(*args)
v
end
- ## * +read+ must be called without or with one integer argument
- ## and return a string, or +nil+ on EOF.
+ ## * +read+ behaves like IO#read. Its signature is <tt>read([length, [buffer]])</tt>.
+ ## If given, +length+ must be an non-negative Integer (>= 0) or +nil+, and +buffer+ must
+ ## be a String and may not be nil. If +length+ is given and not nil, then this method
+ ## reads at most +length+ bytes from the input stream. If +length+ is not given or nil,
+ ## then this method reads all data until EOF.
+ ## When EOF is reached, this method returns nil if +length+ is given and not nil, or ""
+ ## if +length+ is not given or is nil.
+ ## If +buffer+ is given, then the read data will be placed into +buffer+ instead of a
+ ## newly created String object.
def read(*args)
assert("rack.input#read called with too many arguments") {
- args.size <= 1
+ args.size <= 2
}
- if args.size == 1
- assert("rack.input#read called with non-integer argument") {
- args.first.kind_of? Integer
+ if args.size >= 1
+ assert("rack.input#read called with non-integer and non-nil length") {
+ args.first.kind_of?(Integer) || args.first.nil?
+ }
+ assert("rack.input#read called with a negative length") {
+ args.first.nil? || args.first >= 0
+ }
+ end
+ if args.size >= 2
+ assert("rack.input#read called with non-String buffer") {
+ args[1].kind_of?(String)
}
end
+
v = @input.read(*args)
- assert("rack.input#read didn't return a String") {
+
+ assert("rack.input#read didn't return nil or a String") {
v.nil? or v.instance_of? String
}
+ if args[0].nil?
+ assert("rack.input#read(nil) returned nil on EOF") {
+ !v.nil?
+ }
+ end
+
v
end
View
105 test/spec_rack_lint.rb
@@ -260,11 +260,43 @@ def env(*args)
lambda {
Rack::Lint.new(lambda { |env|
+ env["rack.input"].read(1, 2, 3)
+ [201, {"Content-type" => "text/plain", "Content-length" => "0"}, []]
+ }).call(env({}))
+ }.should.raise(Rack::Lint::LintError).
+ message.should.match(/read called with too many arguments/)
+
+ lambda {
+ Rack::Lint.new(lambda { |env|
env["rack.input"].read("foo")
[201, {"Content-type" => "text/plain", "Content-length" => "0"}, []]
}).call(env({}))
}.should.raise(Rack::Lint::LintError).
- message.should.match(/read called with non-integer argument/)
+ message.should.match(/read called with non-integer and non-nil length/)
+
+ lambda {
+ Rack::Lint.new(lambda { |env|
+ env["rack.input"].read(-1)
+ [201, {"Content-type" => "text/plain", "Content-length" => "0"}, []]
+ }).call(env({}))
+ }.should.raise(Rack::Lint::LintError).
+ message.should.match(/read called with a negative length/)
+
+ lambda {
+ Rack::Lint.new(lambda { |env|
+ env["rack.input"].read(nil, nil)
+ [201, {"Content-type" => "text/plain", "Content-length" => "0"}, []]
+ }).call(env({}))
+ }.should.raise(Rack::Lint::LintError).
+ message.should.match(/read called with non-String buffer/)
+
+ lambda {
+ Rack::Lint.new(lambda { |env|
+ env["rack.input"].read(nil, 1)
+ [201, {"Content-type" => "text/plain", "Content-length" => "0"}, []]
+ }).call(env({}))
+ }.should.raise(Rack::Lint::LintError).
+ message.should.match(/read called with non-String buffer/)
lambda {
Rack::Lint.new(lambda { |env|
@@ -293,6 +325,23 @@ def rewind
raise Errno::ESPIPE, "Errno::ESPIPE"
end
end
+
+ eof_weirdio = Object.new
+ class << eof_weirdio
+ def gets
+ nil
+ end
+
+ def read(*args)
+ nil
+ end
+
+ def each
+ end
+
+ def rewind
+ end
+ end
lambda {
Rack::Lint.new(lambda { |env|
@@ -316,7 +365,15 @@ def rewind
[201, {"Content-type" => "text/plain", "Content-length" => "0"}, []]
}).call(env("rack.input" => weirdio))
}.should.raise(Rack::Lint::LintError).
- message.should.match(/read didn't return a String/)
+ message.should.match(/read didn't return nil or a String/)
+
+ lambda {
+ Rack::Lint.new(lambda { |env|
+ env["rack.input"].read
+ [201, {"Content-type" => "text/plain", "Content-length" => "0"}, []]
+ }).call(env("rack.input" => eof_weirdio))
+ }.should.raise(Rack::Lint::LintError).
+ message.should.match(/read\(nil\) returned nil on EOF/)
lambda {
Rack::Lint.new(lambda { |env|
@@ -368,6 +425,50 @@ def rewind
}.should.raise(Rack::Lint::LintError).
message.should.match(/body was given for HEAD/)
end
+
+ specify "passes valid read calls" do
+ lambda {
+ Rack::Lint.new(lambda { |env|
+ env["rack.input"].read
+ [201, {"Content-type" => "text/plain", "Content-length" => "0"}, []]
+ }).call(env({"rack.input" => StringIO.new("hello world")}))
+ }.should.not.raise(Rack::Lint::LintError)
+
+ lambda {
+ Rack::Lint.new(lambda { |env|
+ env["rack.input"].read(0)
+ [201, {"Content-type" => "text/plain", "Content-length" => "0"}, []]
+ }).call(env({"rack.input" => StringIO.new("hello world")}))
+ }.should.not.raise(Rack::Lint::LintError)
+
+ lambda {
+ Rack::Lint.new(lambda { |env|
+ env["rack.input"].read(1)
+ [201, {"Content-type" => "text/plain", "Content-length" => "0"}, []]
+ }).call(env({"rack.input" => StringIO.new("hello world")}))
+ }.should.not.raise(Rack::Lint::LintError)
+
+ lambda {
+ Rack::Lint.new(lambda { |env|
+ env["rack.input"].read(nil)
+ [201, {"Content-type" => "text/plain", "Content-length" => "0"}, []]
+ }).call(env({"rack.input" => StringIO.new("hello world")}))
+ }.should.not.raise(Rack::Lint::LintError)
+
+ lambda {
+ Rack::Lint.new(lambda { |env|
+ env["rack.input"].read(nil, '')
+ [201, {"Content-type" => "text/plain", "Content-length" => "0"}, []]
+ }).call(env({"rack.input" => StringIO.new("hello world")}))
+ }.should.not.raise(Rack::Lint::LintError)
+
+ lambda {
+ Rack::Lint.new(lambda { |env|
+ env["rack.input"].read(1, '')
+ [201, {"Content-type" => "text/plain", "Content-length" => "0"}, []]
+ }).call(env({"rack.input" => StringIO.new("hello world")}))
+ }.should.not.raise(Rack::Lint::LintError)
+ end
end
context "Rack::Lint::InputWrapper" do
Please sign in to comment.
Something went wrong with that request. Please try again.