From f824f4e13a13daf56439e16ecb3a62469a8c9bf0 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Sat, 28 Nov 2009 11:43:45 -0800 Subject: always set FD_CLOEXEC if available Some people fork processes, so it avoid hanging a connection open because of that... --- lib/rainbows.rb | 16 ++++++++++--- lib/rainbows/revactor.rb | 2 ++ t/fork-sleep.ru | 10 ++++++++ t/t0011-close-on-exec-set.sh | 54 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 79 insertions(+), 3 deletions(-) create mode 100644 t/fork-sleep.ru create mode 100755 t/t0011-close-on-exec-set.sh 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 -- cgit v1.2.3-24-ge0c7