diff options
author | Eric Wong <normalperson@yhbt.net> | 2009-12-24 22:34:27 +0000 |
---|---|---|
committer | Eric Wong <normalperson@yhbt.net> | 2009-12-24 22:34:27 +0000 |
commit | 28f19d7d1f1255fc920a42640e2d7f24b37f7eaf (patch) | |
tree | b03e115d99aa27b59bd0b555110e39fd5047d5ae | |
parent | aea2d608fe592a665741087a8d6d31fcf26848ec (diff) | |
download | rack-28f19d7d1f1255fc920a42640e2d7f24b37f7eaf.tar.gz |
Rack::Lint understands "rack.io" env
-rw-r--r-- | lib/rack/lint.rb | 45 | ||||
-rw-r--r-- | test/spec_rack_lint.rb | 40 |
2 files changed, 85 insertions, 0 deletions
diff --git a/lib/rack/lint.rb b/lib/rack/lint.rb index 493982ac..9c10c7d5 100644 --- a/lib/rack/lint.rb +++ b/lib/rack/lint.rb @@ -224,6 +224,8 @@ module Rack check_input env["rack.input"] ## * There must be a valid error stream in <tt>rack.errors</tt>. check_error env["rack.errors"] + ## * There may be a bidirectional IO in <tt>rack.io</tt>. + check_io env["rack.io"] ## * The <tt>REQUEST_METHOD</tt> must be a valid token. assert("REQUEST_METHOD unknown: #{env["REQUEST_METHOD"]}") { @@ -281,6 +283,49 @@ module Rack } end + ## === The IO Stream + ## + ## An optional "raw" IO object which is both read and writable. + ## This may be used to implement alternative/tunneled protocols + ## such as Web Sockets or used to detect client disconnects + ## before performing long/expensive operations. + def check_io(io) + return if io.nil? + + ## When applicable, its external encoding must be "ASCII-8BIT" and it + ## must be opened in binary mode, for Ruby 1.9 compatibility. + assert("rack.io #{io} does not have ASCII-8BIT as its external encoding") { + io.external_encoding.name == "ASCII-8BIT" + } if io.respond_to?(:external_encoding) + assert("rack.io is not opened in binary mode") { + io.binmode? + } if io.respond_to?(:binmode?) + + ## Allow StringIO for testing + return if io.kind_of? StringIO + + ## IO.select() is useful for detecting if a client is still + ## alive before performing long or expensive operations. + ## IO.select() also validates that the IO object is usable + ## by other IO frameworks such as Rev or EventMachine. + assert("rack.io is not a readable IO") { + begin + IO.select([io], nil, nil, 0.0) + true + rescue + false + end + } + assert("rack.io is not a writable IO") { + begin + IO.select(nil, [io], nil, 0.0) + true + rescue + false + end + } + end + class InputWrapper include Assertion diff --git a/test/spec_rack_lint.rb b/test/spec_rack_lint.rb index bbf75c17..3daa5cac 100644 --- a/test/spec_rack_lint.rb +++ b/test/spec_rack_lint.rb @@ -17,6 +17,17 @@ context "Rack::Lint" do }.should.not.raise end + specify "passes valid request with rack.io" do + lambda { + io = StringIO.new + io.binmode if io.respond_to?(:binmode) + io.set_encoding(Encoding::BINARY) if io.respond_to?(:set_encoding) + Rack::Lint.new(lambda { |env| + [200, {"Content-type" => "test/plain", "Content-length" => "3"}, ["foo"]] + }).call(env("rack.io" => io)) + }.should.not.raise + end + specify "notices fatal errors" do lambda { Rack::Lint.new(nil).call }.should.raise(Rack::Lint::LintError). message.should.match(/No env given/) @@ -139,6 +150,35 @@ context "Rack::Lint" do message.should.match(/does not have ASCII-8BIT as its external encoding/) end + specify "notices io errors" do + lambda { + Rack::Lint.new(nil).call(env("rack.io" => true)) + }.should.raise(Rack::Lint::LintError). + message.should.match(/is not a readable IO/) + + lambda { + io = Object.new + def io.binmode? + false + end + Rack::Lint.new(nil).call(env("rack.io" => io)) + }.should.raise(Rack::Lint::LintError). + message.should.match(/is not opened in binary mode/) + + lambda { + io = Object.new + def io.external_encoding + result = Object.new + def result.name + "US-ASCII" + end + result + end + Rack::Lint.new(nil).call(env("rack.io" => io)) + }.should.raise(Rack::Lint::LintError). + message.should.match(/does not have ASCII-8BIT as its external encoding/) + end + specify "notices error errors" do lambda { Rack::Lint.new(nil).call(env("rack.errors" => "")) |