about summary refs log tree commit homepage
diff options
context:
space:
mode:
authorEric Wong <e@80x24.org>2015-03-09 04:35:18 +0000
committerEric Wong <e@80x24.org>2015-03-09 04:35:18 +0000
commite413325737f23c5ec27a02246f95077bc1fb038d (patch)
treecff1568e5326cde3921b84531702aca8eb3d679c
parent94538f003c4031f33eb6545032bd234e483e65c6 (diff)
downloadyahns-e413325737f23c5ec27a02246f95077bc1fb038d.tar.gz
When inheriting sockets from the parent via YAHNS_FD, we must close
sockets ASAP if they are unconfigured in the child.  This bug exists
in yahns (and not unicorn) because of the trickier shutdown routine
we do for blocking accept system calls to work reliably with the
threading support in mainline Ruby 2.x.  This bug would not exist
in a purely C server using blocking accept, either.
-rw-r--r--lib/yahns/acceptor.rb5
-rw-r--r--test/test_server.rb39
2 files changed, 43 insertions, 1 deletions
diff --git a/lib/yahns/acceptor.rb b/lib/yahns/acceptor.rb
index cd9e055..0cebea2 100644
--- a/lib/yahns/acceptor.rb
+++ b/lib/yahns/acceptor.rb
@@ -19,7 +19,10 @@ module Yahns::Acceptor # :nodoc:
 
   # just keep looping this on every acceptor until the associated thread dies
   def ac_quit
-    return true unless defined?(@thrs)
+    unless defined?(@thrs) # acceptor has not started yet, freshly inherited
+      close
+      return true
+    end
     @thrs.each { |t| t[:yahns_quit] = true }
     return true if __ac_quit_done?
 
diff --git a/test/test_server.rb b/test/test_server.rb
index 69babb3..b7cb3e6 100644
--- a/test/test_server.rb
+++ b/test/test_server.rb
@@ -804,4 +804,43 @@ class TestServer < Testcase
   ensure
     quit_wait(pid)
   end
+
+  def test_inherit_too_many
+    err = @err
+    s2 = TCPServer.new(ENV["TEST_HOST"] || "127.0.0.1", 0)
+    cfg = Yahns::Config.new
+    host, port = @srv.addr[3], @srv.addr[1]
+    cfg.instance_eval do
+      ru = lambda { |_| [ 200, {'Content-Length'=>'2'}, ['HI'] ] }
+      GTL.synchronize { app(:rack, ru) { listen "#{host}:#{port}" } }
+      logger(Logger.new(err.path))
+    end
+    mkserver(cfg, @srv) do
+      s2.autoclose = false
+      ENV["YAHNS_FD"] = "#{@srv.fileno},#{s2.fileno}"
+    end
+    run_client(host, port) { |res| assert_equal "HI", res.body }
+    th = Thread.new do
+      c = s2.accept
+      c.readpartial(1234)
+      c.write "HTTP/1.0 666 OK\r\n\r\nGO AWAY"
+      c.close
+      :OK
+    end
+    Thread.pass
+    s2host, s2port = s2.addr[3], s2.addr[1]
+    Net::HTTP.start(s2host, s2port) do |http|
+      res = http.request(Net::HTTP::Get.new("/"))
+      assert_equal 666, res.code.to_i
+      assert_equal "GO AWAY", res.body
+    end
+    assert_equal :OK, th.value
+    tmpc = TCPSocket.new(s2host, s2port)
+    a2 = s2.accept
+    assert_nil IO.select([a2], nil, nil, 0.05)
+    tmpc.close
+    assert_nil a2.read(1)
+    a2.close
+    s2.close
+  end
 end