diff --git a/CHANGELOG.md b/CHANGELOG.md index 8c408eba..b7853cac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ # Unreleased +- Implement `rack.response_finished` (#97). - Don't break the `rack.after_reply` callback chain if one callback raises (#97). # 0.11.1 diff --git a/lib/pitchfork/http_server.rb b/lib/pitchfork/http_server.rb index 6ad39df7..4b50255b 100644 --- a/lib/pitchfork/http_server.rb +++ b/lib/pitchfork/http_server.rb @@ -732,7 +732,7 @@ def process_client(client, worker, timeout_handler) end end - env["rack.after_reply"] = [] + env["rack.response_finished"] = env["rack.after_reply"] = [] status, headers, body = @app.call(env) @@ -758,15 +758,19 @@ def process_client(client, worker, timeout_handler) client.close # flush and uncork socket immediately, no keepalive end env - rescue => e - handle_error(client, e) + rescue => application_error + handle_error(client, application_error) env ensure if env - env["rack.after_reply"].each do |callback| - callback.call - rescue => error - Pitchfork.log_error(@logger, "rack.after_reply error", error) + env["rack.response_finished"].each do |callback| + if callback.arity == 0 + callback.call + else + callback.call(env, status, headers, application_error) + end + rescue => callback_error + Pitchfork.log_error(@logger, "rack.after_reply error", callback_error) end end timeout_handler.finished diff --git a/test/slow/test_server.rb b/test/slow/test_server.rb index 7118bb7e..8644411a 100644 --- a/test/slow/test_server.rb +++ b/test/slow/test_server.rb @@ -35,6 +35,7 @@ def call(env) class TestRackAfterReply def initialize @called = false + @valid_arguments = false end def call(env) @@ -42,13 +43,23 @@ def call(env) end env["rack.after_reply"] << -> { raise "oops" } + env["rack.response_finished"] << method(:check_arguments) env["rack.after_reply"] << -> { @called = true } - [200, { 'content-type' => 'text/plain' }, ["after_reply_called: #{@called}"]] + [200, { 'content-type' => 'text/plain' }, ["after_reply_called: #{@called}\nresponse_finished_called: #{@valid_arguments}\n"]] rescue Pitchfork::ClientShutdown, Pitchfork::HttpParserError => e $stderr.syswrite("#{e.class}: #{e.message} #{e.backtrace.empty?}\n") raise e end + + def check_arguments(env, status, headers, application_error) + return unless env.is_a?(Hash) && env["SERVER_PROTOCOL"] + return unless status == 200 + return unless headers.is_a?(Hash) && headers.key?("content-type") + return unless application_error.nil? + + @valid_arguments = true + end end def setup @@ -142,6 +153,7 @@ def test_after_reply responses = sock.read(4096) assert_match %r{\AHTTP/1.[01] 200\b}, responses assert_match %r{^after_reply_called: false}, responses + assert_match %r{^response_finished_called: false}, responses sock = tcp_socket('127.0.0.1', @port) sock.syswrite("GET / HTTP/1.0\r\n\r\n") @@ -149,6 +161,7 @@ def test_after_reply responses = sock.read(4096) assert_match %r{\AHTTP/1.[01] 200\b}, responses assert_match %r{^after_reply_called: true}, responses + assert_match %r{^response_finished_called: true}, responses sock.close end