diff --git a/SPEC.rdoc b/SPEC.rdoc index 6562b2ce8..f3a517a69 100644 --- a/SPEC.rdoc +++ b/SPEC.rdoc @@ -137,6 +137,7 @@ There are the following restrictions: set. PATH_INFO should be / if SCRIPT_NAME is empty. SCRIPT_NAME never should be /, but instead be empty. +rack.response_finished:: An array of callables run after the HTTP response has been sent. === The Input Stream diff --git a/lib/rack.rb b/lib/rack.rb index ad3efec68..54def2443 100644 --- a/lib/rack.rb +++ b/lib/rack.rb @@ -59,6 +59,7 @@ module Rack RACK_HIJACK = 'rack.hijack' RACK_IS_HIJACK = 'rack.hijack?' RACK_HIJACK_IO = 'rack.hijack_io' + RACK_RESPONSE_FINISHED = 'rack.response_finished' RACK_RECURSIVE_INCLUDE = 'rack.recursive.include' RACK_MULTIPART_BUFFER_SIZE = 'rack.multipart.buffer_size' RACK_MULTIPART_TEMPFILE_FACTORY = 'rack.multipart.tempfile_factory' diff --git a/lib/rack/lint.rb b/lib/rack/lint.rb index fe6161387..dceb3a08c 100755 --- a/lib/rack/lint.rb +++ b/lib/rack/lint.rb @@ -356,6 +356,15 @@ def check_environment(env) unless env[SCRIPT_NAME] != "/" raise LintError, "SCRIPT_NAME cannot be '/', make it '' and PATH_INFO '/'" end + + ## rack.response_finished:: An array of callables run after the HTTP response has been sent. + if callables = env[RACK_RESPONSE_FINISHED] + raise LintError, "rack.response_finished must be an array of callable objects" unless callables.is_a?(Array) + + callables.each do |callable| + raise LintError, "rack.response_finished values must respond to call" unless callable.respond_to?(:call) + end + end end ## diff --git a/test/spec_lint.rb b/test/spec_lint.rb index d822f9851..ee0df6db3 100755 --- a/test/spec_lint.rb +++ b/test/spec_lint.rb @@ -204,6 +204,16 @@ def obj.error(*) end Rack::Lint.new(nil).call(env("SCRIPT_NAME" => "/")) }.must_raise(Rack::Lint::LintError). message.must_match(/cannot be .* make it ''/) + + lambda { + Rack::Lint.new(nil).call(env("rack.response_finished" => "not a callable")) + }.must_raise(Rack::Lint::LintError). + message.must_match(/rack.response_finished must be an array of callable objects/) + + lambda { + Rack::Lint.new(nil).call(env("rack.response_finished" => [->{}, "not a callable"])) + }.must_raise(Rack::Lint::LintError). + message.must_match(/rack.response_finished values must respond to call/) end it "notice input errors" do @@ -731,6 +741,12 @@ def assert_lint(*args) }).call(env({}))[1]['rack.hijack'].call(StringIO.new).read.must_equal '' end + it "pass valid rack.response_finished" do + Rack::Lint.new(lambda { |env| + [200, { "rack.response_finished" => [-> {}, lambda {}], "Content-length" => "3" }, ["foo"]] + }).call(env({})).first.must_equal 200 + end + end describe "Rack::Lint::Wrapper::InputWrapper" do