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