From 810144e77f073bf16c2d2b8275696fd8cb58069e Mon Sep 17 00:00:00 2001 From: Evan Phoenix Date: Sun, 2 Sep 2012 23:33:09 -0400 Subject: [PATCH] Close kept alive sockets on restart. Fixes #144 --- lib/puma/cli.rb | 5 ++-- lib/puma/events.rb | 3 +++ lib/puma/reactor.rb | 15 ++++++++++++ lib/puma/server.rb | 2 ++ test/test_integration.rb | 49 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 72 insertions(+), 2 deletions(-) diff --git a/lib/puma/cli.rb b/lib/puma/cli.rb index c6425fbc..8ff431a8 100644 --- a/lib/puma/cli.rb +++ b/lib/puma/cli.rb @@ -325,12 +325,13 @@ module Puma @listeners << [str, io] when "unix" + path = "#{uri.host}#{uri.path}" + if fd = @inherited_fds.delete(str) log "* Inherited #{str}" - io = server.inherit_unix_listener uri.path, fd + io = server.inherit_unix_listener path, fd else log "* Listening on #{str}" - path = "#{uri.host}#{uri.path}" umask = nil diff --git a/lib/puma/events.rb b/lib/puma/events.rb index ca1a7783..a6dab4da 100644 --- a/lib/puma/events.rb +++ b/lib/puma/events.rb @@ -16,6 +16,9 @@ module Puma def initialize(stdout, stderr) @stdout = stdout @stderr = stderr + + @stdout.sync = true + @stderr.sync = true end attr_reader :stdout, :stderr diff --git a/lib/puma/reactor.rb b/lib/puma/reactor.rb index f817d176..01a57a3d 100644 --- a/lib/puma/reactor.rb +++ b/lib/puma/reactor.rb @@ -30,6 +30,15 @@ module Puma when "*" sockets += @input @input.clear + when "c" + sockets.delete_if do |s| + if s == @ready + false + else + s.close + true + end + end when "!" return end @@ -121,8 +130,14 @@ module Puma end end + # Close all watched sockets and clear them from being watched + def clear! + @trigger << "c" + end + def shutdown @trigger << "!" + @thread.join end end end diff --git a/lib/puma/server.rb b/lib/puma/server.rb index e4ae4b1b..a79e60a1 100644 --- a/lib/puma/server.rb +++ b/lib/puma/server.rb @@ -261,6 +261,8 @@ module Puma end end + @reactor.clear! if @status == :restart + @reactor.shutdown graceful_shutdown if @status == :stop ensure diff --git a/test/test_integration.rb b/test/test_integration.rb index b56e1873..d9920d51 100644 --- a/test/test_integration.rb +++ b/test/test_integration.rb @@ -1,6 +1,7 @@ require "rbconfig" require 'test/unit' require 'socket' +require 'timeout' require 'puma/cli' require 'puma/control_cli' @@ -10,12 +11,35 @@ class TestIntegration < Test::Unit::TestCase @state_path = "test/test_puma.state" @bind_path = "test/test_server.sock" @control_path = "test/test_control.sock" + @tcp_port = 9998 + + @server = nil end def teardown File.unlink @state_path rescue nil File.unlink @bind_path rescue nil File.unlink @control_path rescue nil + + if @server + Process.kill "INT", @server.pid + Process.wait @server.pid + @server.close + end + end + + def server(opts) + core = "#{Gem.ruby} -rubygems -Ilib bin/puma" + cmd = "#{core} --restart-cmd '#{core}' -b tcp://127.0.0.1:#{@tcp_port} #{opts}" + @server = IO.popen(cmd, "r") + + true until @server.gets =~ /Ctrl-C/ + + @server + end + + def signal(which) + Process.kill which, @server.pid end def test_stop_via_pumactl @@ -45,4 +69,29 @@ class TestIntegration < Test::Unit::TestCase assert_kind_of Thread, t.join(1), "server didn't stop" end + + def test_restart_closes_keepalive_sockets + server("-q test/hello.ru") + + s = TCPSocket.new "localhost", @tcp_port + s << "GET / HTTP/1.1\r\n\r\n" + true until s.gets == "\r\n" + + s.readpartial(20) + signal :USR2 + + true until @server.gets =~ /Ctrl-C/ + + s.write "GET / HTTP/1.1\r\n\r\n" + + assert_raises Errno::ECONNRESET do + Timeout.timeout(2) do + s.read(2) + end + end + + s = TCPSocket.new "localhost", @tcp_port + s << "GET / HTTP/1.0\r\n\r\n" + assert_equal "Hello World", s.read.split("\r\n").last + end end