Skip to content

Commit

Permalink
Added fixed gateway script [Nicholas Seckar]
Browse files Browse the repository at this point in the history
git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@1721 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
  • Loading branch information
dhh committed Jul 6, 2005
1 parent 3170295 commit 707106f
Show file tree
Hide file tree
Showing 5 changed files with 197 additions and 215 deletions.
2 changes: 1 addition & 1 deletion railties/Rakefile
Expand Up @@ -137,7 +137,7 @@ task :copy_dispatches do
chmod 0755, "#{PKG_DESTINATION}/public/dispatch.fcgi"

copy_with_rewritten_ruby_path("dispatches/gateway.cgi", "#{PKG_DESTINATION}/public/gateway.cgi")
chmod 0644, "#{PKG_DESTINATION}/public/gateway.cgi"
chmod 0755, "#{PKG_DESTINATION}/public/gateway.cgi"
end

task :copy_html_files do
Expand Down
147 changes: 65 additions & 82 deletions railties/bin/listener
@@ -1,103 +1,86 @@
require "drb"
ENV["RAILS_ENV"] = 'production'
require "#{File.dirname(__FILE__)}/../config/environment.rb"
#!/usr/local/bin/ruby

require 'stringio'
require 'fileutils'
require 'fcgi_handler'
require 'rbconfig'

VERBOSE = false
def message(s)
$stderr.puts "listener: #{s}" if ENV && ENV["DEBUG_GATEWAY"]
end

class RemoteCGI < CGI
attr_accessor :stdinput, :stdoutput, :env_table
def initialize(env_table, input = nil, output = nil)
self.env_table = env_table
self.stdinput = input || StringIO.new
self.stdoutput = output || StringIO.new
super()
end

def out(stream) # Ignore the requested output stream
super(stdoutput)
end
end

class Listener
include DRbUndumped
attr_accessor :tracker

def initialize(timeout = nil)
@timeout = timeout

def initialize(timeout, socket_path)
@socket = File.expand_path(socket_path)
@mutex = Mutex.new
@active = false

@timeout = timeout

@handler = RailsFCGIHandler.new
@handler.extend DRbUndumped
@output = FakeOut.new
$stdout = @output
end

def inform_up(tracker_uri)
return unless tracker_uri
tracker = DRbObject.new_with_uri(tracker_uri)
tracker.register_listener self
@tracker = tracker
end
def inform_down
@tracker.remove_listener(self) if @tracker
end

def run(on_uri, tracker_uri)
on_uri ||= "drbunix:"
DRb.start_service(on_uri, self) # Start a server for us
inform_up tracker_uri
@handler.process!(self)
end

def die!
inform_down
Kernel.exit 0
end

def process(input)
$stderr.puts "listener: received request -- obtaining lock" if VERBOSE
@mutex.synchronize do
@active = true

$stderr.puts "listener: obtained -- swaping stdin" if VERBOSE
$stdin = input
cgi = CGI.new

$stderr.puts "listener: yielding to FCGI handler..." if VERBOSE
@cgi_block.call cgi
$stderr.puts "listener: handler finished, releasing control" if VERBOSE

return @output.read!
end
message 'opening socket'
DRb.start_service("drbunix:#{@socket}", self)

message 'entering process loop'
@handler.process! self
end

def each_cgi(&block)
@cgi_block = block

def each_cgi(&cgi_block)
@cgi_block = cgi_block
message 'entering idle loop'
loop do
@timeout ? sleep(@timeout) : sleep
sleep @timeout rescue nil
die! unless @active
@active = false
end
end
end

class FakeOut < Struct.new(:contents)
def initialize
super("")
end
def write(str)
contents << str
def process(env, input)
message 'received request'
@mutex.synchronize do
@active = true

message 'creating input stream'
input_stream = StringIO.new(input)
message 'building CGI instance'
cgi = RemoteCGI.new(eval(env), input_stream)

message 'yielding to fcgi handler'
@cgi_block.call cgi
message 'yield finished -- sending output'

cgi.stdoutput.seek(0)
output = cgi.stdoutput.read

return output
end
end
def read!
c = contents
self.contents = ''
return c

def die!
message 'shutting down'
DRb.stop_service
FileUtils.rm_f @socket
Kernel.exit 0
end
end

if ARGV.shift == 'start-listeners'
tracker = ARGV.shift
number = (ARGV.shift || '1').to_i
exit(0) if number.zero?

if number > 1
fork do
exec(
File.join(Config::CONFIG['bin_dir'], Config::CONFIG['RUBY_SO_NAME']),
__FILE__, 'start-listeners', tracker, (number - 1).to_s
)
end
end

l = Listener.new(90)
l.run(nil, tracker)
end
socket_path = ARGV.shift
timeout = (ARGV.shift || 90).to_i

Listener.new(timeout, socket_path)
149 changes: 54 additions & 95 deletions railties/bin/tracker
@@ -1,110 +1,69 @@
require "drb"
require "rbconfig"
#!/usr/local/bin/ruby

VERBOSE = false
require 'drb'
require 'thread'

def message(s)
$stderr.puts "tracker: #{s}" if ENV && ENV["DEBUG_GATEWAY"]
end

class Tracker
include DRbUndumped

def initialize(timeout = 90, uri = nil)
@timeout = timeout
@waiting = []
@working = []

@waiting_mutex = Mutex.new

DRb.start_service(uri, self)
@uri = DRb.uri
end
def run
start_listener 3
sleep 3

background
end

def register_listener(listener)
@waiting.push listener
nil
end
def remove_listener(listener)
@waiting.delete listener
@working.delete listener
nil

def initialize(instances, socket_path)
@instances = instances
@socket = File.expand_path(socket_path)
@active = false

@listeners = []
@instances.times { @listeners << Mutex.new }

message "using #{@listeners.length} listeners"
message "opening socket at #{@socket}"

@service = DRb.start_service("drbunix://#{@socket}", self)
end

def with_listener
listener = @waiting.shift
unless listener
start_listener(2) unless @waiting.length + @working.length > 6
@waiting_mutex.synchronize do
10.times do
sleep 0.5
listener = @waiting.shift
break if listener
end
unless listener
($stderr.puts "Dropping request due to lack of listeners!!!" unless listener) if VERBOSE
return
end
message "listener requested"

mutex = has_lock = index = nil
3.times do
@listeners.each_with_index do |mutex, index|
has_lock = mutex.try_lock
break if has_lock
end
break if has_lock
sleep 0.05
end

@working << listener
yield listener
ensure
if listener
@working.delete listener
@waiting << listener
end
end

def background
loop do
@timeout ? sleep(@timeout) : sleep
unless @processed
$stderr.puts "Idle for #{@timeout} -- shutting down tracker." if VERBOSE
Kernel.exit 0

if has_lock
message "obtained listener #{index}"
@active = true
begin yield index
ensure
mutex.unlock
message "released listener #{index}"
end
@processed = false
else
message "dropping request because no listeners are available!"
end
end

def process(input)
output = nil
$stderr.puts "tracker: received request.. obtaining listener" if VERBOSE
with_listener do |listener|
$stderr.puts "tracker: obtained -- forwarding request to listener.." if VERBOSE
@processed = true
output = listener.process(input)
$stderr.puts "tracker: listener released control." if VERBOSE
end
return output
end

def start_listener(n = 1)
tracker_uri = @uri
listener_path = File.join(File.dirname(__FILE__), 'listener')
fork do
exec(
File.join(Config::CONFIG['bin_dir'], Config::CONFIG['RUBY_SO_NAME']),
listener_path, 'start-listeners', tracker_uri, n.to_s
)

def background(check_interval = nil)
if check_interval
loop do
sleep check_interval
message "Idle for #{check_interval}, shutting down" unless @active
@active = false
Kernel.exit 0
end
else DRb.thread.join
end
end

def ping
true
end
end

if ARGV.first == "start"
tracker = Tracker.new(90, ARGV[1])
socket = (/druby:([^?]*)\?/ =~ ARGV[1]) ? $1 : nil
require 'fileutils' if socket

begin tracker.run
ensure
FileUtils.rm_f(socket) if socket
end
end
socket_path = ARGV.shift
instances = ARGV.shift.to_i
t = Tracker.new(instances, socket_path)
t.background(ARGV.first ? ARGV.shift.to_i : 90)

0 comments on commit 707106f

Please sign in to comment.