diff --git a/SPEC.rdoc b/SPEC.rdoc
index f97f69d08..da9728a05 100644
--- a/SPEC.rdoc
+++ b/SPEC.rdoc
@@ -132,6 +132,9 @@ 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.
+Invoked with (env, status, headers, error) arguments. If there is no error sending the response,
+the error argument will be nil.
=== The Input Stream
diff --git a/lib/rack/constants.rb b/lib/rack/constants.rb
index 26fe9d14b..d99b63673 100644
--- a/lib/rack/constants.rb
+++ b/lib/rack/constants.rb
@@ -51,6 +51,7 @@ module Rack
RACK_RECURSIVE_INCLUDE = 'rack.recursive.include'
RACK_MULTIPART_BUFFER_SIZE = 'rack.multipart.buffer_size'
RACK_MULTIPART_TEMPFILE_FACTORY = 'rack.multipart.tempfile_factory'
+ RACK_RESPONSE_FINISHED = 'rack.response_finished'
RACK_REQUEST_FORM_INPUT = 'rack.request.form_input'
RACK_REQUEST_FORM_HASH = 'rack.request.form_hash'
RACK_REQUEST_FORM_VARS = 'rack.request.form_vars'
diff --git a/lib/rack/lint.rb b/lib/rack/lint.rb
index 9ac3e2bca..9ea1b8c60 100755
--- a/lib/rack/lint.rb
+++ b/lib/rack/lint.rb
@@ -363,6 +363,17 @@ 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.
+ ## Invoked with (env, status, headers, error) arguments. If there is no error sending the response,
+ ## the error argument will be nil.
+ 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(env, status, headers, error)" unless callable.respond_to?(:call)
+ end
+ end
end
##
@@ -582,7 +593,7 @@ def check_hijack_response(headers, env)
## ignore the +body+ part of the response tuple when the
## +rack.hijack+ response header is present. Using an empty +Array+
## instance is recommended.
- else
+ else
##
## The special response header +rack.hijack+ must only be set
## if the request +env+ has a truthy +rack.hijack?+.
diff --git a/test/spec_lint.rb b/test/spec_lint.rb
index 6ad2ca78b..877aee371 100755
--- a/test/spec_lint.rb
+++ b/test/spec_lint.rb
@@ -253,6 +253,16 @@ def obj.fatal(*) 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" => [-> (env) {}, "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
@@ -811,4 +821,15 @@ def assert_lint(*args)
}.must_raise(Rack::Lint::LintError).
message.must_equal 'rack.hijack header must respond to #call'
end
+
+ it "pass valid rack.response_finished" do
+ callable_object = Class.new do
+ def call(env)
+ end
+ end.new
+
+ Rack::Lint.new(lambda { |env|
+ [200, {}, ["foo"]]
+ }).call(env({ "rack.response_finished" => [-> (env) {}, lambda { |env| }, callable_object], "content-length" => "3" })).first.must_equal 200
+ end
end