about summary refs log tree commit homepage
diff options
context:
space:
mode:
authorEric Wong <normalperson@yhbt.net>2009-11-28 11:43:45 -0800
committerEric Wong <normalperson@yhbt.net>2009-11-28 12:19:21 -0800
commitf824f4e13a13daf56439e16ecb3a62469a8c9bf0 (patch)
tree162c80491bdf7ad7d258479af092c23a5557c2be
parent2489368a624cff50a330238cf3c3f16eb0bd743c (diff)
downloadrainbows-f824f4e13a13daf56439e16ecb3a62469a8c9bf0.tar.gz
Some people fork processes, so it avoid hanging a connection
open because of that...
-rw-r--r--lib/rainbows.rb16
-rw-r--r--lib/rainbows/revactor.rb2
-rw-r--r--t/fork-sleep.ru10
-rwxr-xr-xt/t0011-close-on-exec-set.sh54
4 files changed, 79 insertions, 3 deletions
diff --git a/lib/rainbows.rb b/lib/rainbows.rb
index a252ba6..5521e81 100644
--- a/lib/rainbows.rb
+++ b/lib/rainbows.rb
@@ -1,6 +1,7 @@
 # -*- encoding: binary -*-
 require 'unicorn'
 require 'rainbows/error'
+require 'fcntl'
 
 module Rainbows
 
@@ -87,9 +88,18 @@ module Rainbows
   autoload :Fiber, 'rainbows/fiber' # core class
 
   # returns nil if accept fails
-  def self.accept(sock)
-    sock.accept_nonblock
-  rescue Errno::EAGAIN, Errno::ECONNABORTED
+  if defined?(Fcntl::FD_CLOEXEC)
+    def self.accept(sock)
+      rv = sock.accept_nonblock
+      rv.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)
+      rv
+    rescue Errno::EAGAIN, Errno::ECONNABORTED
+    end
+  else
+    def self.accept(sock)
+      sock.accept_nonblock
+    rescue Errno::EAGAIN, Errno::ECONNABORTED
+    end
   end
 
 end
diff --git a/lib/rainbows/revactor.rb b/lib/rainbows/revactor.rb
index 4e4b381..9a18157 100644
--- a/lib/rainbows/revactor.rb
+++ b/lib/rainbows/revactor.rb
@@ -30,6 +30,8 @@ module Rainbows
     # once a client is accepted, it is processed in its entirety here
     # in 3 easy steps: read request, call app, write app response
     def process_client(client)
+      defined?(Fcntl::FD_CLOEXEC) and
+        client.instance_eval { @_io.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC) }
       rd_args = [ nil ]
       remote_addr = if ::Revactor::TCP::Socket === client
         rd_args << RD_ARGS
diff --git a/t/fork-sleep.ru b/t/fork-sleep.ru
new file mode 100644
index 0000000..747a06d
--- /dev/null
+++ b/t/fork-sleep.ru
@@ -0,0 +1,10 @@
+#\-E none
+# we do not want Rack::Lint or anything to protect us
+use Rack::ContentLength
+use Rack::ContentType, "text/plain"
+trap(:CHLD) { $stderr.puts Process.waitpid2(-1).inspect }
+map "/" do
+  time = ENV["nr"] || '15'
+  pid = fork { exec('sleep', time) }
+  run lambda { |env| [ 200, {}, [ "#{pid}\n" ] ] }
+end
diff --git a/t/t0011-close-on-exec-set.sh b/t/t0011-close-on-exec-set.sh
new file mode 100755
index 0000000..0429851
--- /dev/null
+++ b/t/t0011-close-on-exec-set.sh
@@ -0,0 +1,54 @@
+#!/bin/sh
+nr=${nr-"5"}
+. ./test-lib.sh
+
+t_plan 7 "ensure close-on-exec flag is set for $model"
+
+t_begin "setup and start" && {
+        rainbows_setup $model 1 1
+        nr=$nr rainbows -D fork-sleep.ru -c $unicorn_config
+        rainbows_wait_start
+}
+
+t_begin "send keepalive req expect it to timeout in ~1s" && {
+        req='GET / HTTP/1.1\r\nHost: example.com\r\n\r\n'
+        t0=$(date +%s)
+        (
+                cat $fifo > $tmp &
+                printf "$req"
+                wait
+                date +%s > $ok
+        ) | socat - TCP:$listen > $fifo
+        now="$(cat $ok)"
+        elapsed=$(( $now - $t0 ))
+        t_info "elapsed=$elapsed (expecting >=1s)"
+        test $elapsed -ge 1
+}
+
+t_begin 'sleep process is still running' && {
+        sleep_pid="$(tail -1 $tmp)"
+        kill -0 $sleep_pid
+}
+
+t_begin 'keepalive not unreasonably long' && {
+        test $elapsed -lt $nr
+}
+
+t_begin "killing succeeds" && {
+        kill $rainbows_pid
+}
+
+t_begin "check stderr" && {
+        t_info "about to start waiting $nr seconds..."
+        sleep $nr
+        check_stderr
+}
+
+t_begin 'sleep process is NOT running' && {
+        if kill -0 $sleep_pid
+        then
+                die "sleep process should've died"
+        fi
+}
+
+t_done