require 'cgi'
require 'posixlock'
STDOUT.sync = STDERR.sync = true
$VERBOSE = nil
require 'nitro/context'
require 'nitro/dispatcher'
require 'nitro/cgi'
require 'glue/flexob'
# Speeds things up, more comaptible with OSX.
Socket.do_not_reverse_lookup = true
# No multi-threading.
Og.thread_safe = false
module Nitro
# ACGI Adaptor. ACGI is a language independent,
# scalable, open extension to CGI that provides high
# performance without the limitations of server
# specific APIs.
#
# This code is directly from the author. Please go to
# http://codeforpeople.com/lib/ruby/acgi/ for more
# information about ACGI.
# or email: ara [dot] t [dot] howard [at] noaa [dot] gov
#
# It uses a compiled C cgi program to handle
# connections compile the acgi.c by executing
# "make -f Makefile.acgi" in your public/ folder.
# then make your .htaccess redirect all calls to acgi.cgi
# instead of fcgi.rb / cgi.rb
#
#--
# gmosx: not tested! Use at your own risk!
#++
class ACGI < ::CGI
THIS = File::expand_path $0
COMMAND_LINE = [THIS, ARGV].join ' '
LOADED = Time::now
IPC_DIR = "/tmp/acgi_ipc"
CLIENT_LOCK_PATH = File::join IPC_DIR, "client.lock"
SERVER_LOCK_PATH = File::join IPC_DIR, "server.lock"
SERVER_ENVIRONMENT_PATH = File::join IPC_DIR, "server.environment"
SERVER_STDIN_PATH = File::join IPC_DIR, "server.stdin"
SERVER_STDOUT_PATH = File::join IPC_DIR, "server.stdout"
SERVER_STDERR_PATH = File::join IPC_DIR, "server.stderr"
SERVER_PID_PATH = File::join IPC_DIR, "server.pid"
RE_ENV = %r/^ ([^=]+) = (.*) $/ox
RE_NUL = %r/#{ Regexp::escape "\0" }/ox
SERVER = ENV['ACGI_SERVER'] || ARGV.delete('--server') || ARGV.delete('-s')
if SERVER
trap('HUP'){ exec ::ACGI::COMMAND_LINE }
%w( INT KILL TERM ).each{|s| trap(s){ exit }}
end
class << self
def each_cgi(*a, &b)
if SERVER
start_server(*a, &b)
else
munge_environment
@cgi = new(*a)
catching_errors{ b[ @cgi ] }
end
end
def start_server(*a, &b)
daemon do
ENV.clear # __very__ important for speed!
aquire_server_lock
record_pid
loop do
load_environment
munge_environment
@cgi = redirecting_stdin{ new(*a) }
redirecting_stdout_and_stderr{ catching_errors{ b[ @cgi ] }}
restart_if_updated
end
end
end
def restart_if_updated
# put the full path to your run.rb here if it doesn't work
#th = "/path/to/app/run.rb"
if File::stat(THIS).mtime > LOADED
#if File::stat(th).mtime > LOADED
exec COMMAND_LINE
end
end
def aquire_server_lock
lockfile = File.open(SERVER_LOCK_PATH)
lockfile.reopen(SERVER_LOCK_PATH, 'w')
at_exit{ lockfile.close }
ret = lockfile.posixlock(File::LOCK_EX|File::LOCK_NB)
raise("#{ File::basename $0 } already running..") if(!ret or !ret.zero?)
end
def record_pid
begin
f = File::open(SERVER_PID_PATH)
f.reopen(SERVER_PID_PATH, 'w')
f.print Process::pid
f.chmod 0777 rescue nil
ensure
f.close
end
end
def env_table
SERVER ? (@environment ||= {}) : ENV
end
def env
env_table
end
def load_environment
begin
f = File::open(SERVER_ENVIRONMENT_PATH)
f.reopen(SERVER_ENVIRONMENT_PATH, "r")
env_table.clear
kvs = f.read.chop.split RE_NUL
kvs.each do |kv|
m = RE_ENV.match(kv)
k, v = m[1], m[2]
env_table[k] = v
end
ensure
f.close
end
end
def munge_environment # because CGI::Cookie::parse is broken!
env_table['HTTP_COOKIE'] =
munge_http_cookie env_table['HTTP_COOKIE']
end
def munge_http_cookie http_cookie
return unless http_cookie
kvs = http_cookie.split %r/[;,]\s+/o
h = Hash::new{|h,k| h[k] = []}
kvs.each do |kv|
k, v = kv.split %r/=/o, 2
next unless k
k = CGI::unescape k
vs = v.to_s.split(%r/&/).map{|v| CGI::unescape v}
h[k].push(*vs)
end
h.sort.map{|k,vs| [k, vs.join('&')].join('=')}.join('; ')
end
def redirecting_stdin
stdin = $stdin
begin
@stdin = File::open(SERVER_STDIN_PATH)
@stdin.reopen(SERVER_STDIN_PATH, "r")
$stdin = @stdin
yield
ensure
@stdin.close if @stdin
$stdin = stdin
end
end
def redirecting_stdout_and_stderr
stdout, stderr = $stdout, $stderr
begin
@stdout = File::open(SERVER_STDOUT_PATH)
@stdout.reopen(SERVER_STDOUT_PATH, "w")
@stderr = File::open(SERVER_STDERR_PATH)
@stderr.reopen(SERVER_STDERR_PATH, "w")
$stdout, $stderr = @stdout, @stderr
yield
ensure
@stdout.close if @stdout
@stderr.close if @stderr
$stdout, $stderr = stdout, stderr
end
end
def catching_errors
@cgi ||= CGI::new
begin
yield
rescue Exception => e
m, c, b = e.message, e.class, e.backtrace.join("
\n")
@cgi.out{ "