forked from rails/rails
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added fixed gateway script [Nicholas Seckar]
git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@1721 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
- Loading branch information
Showing
5 changed files
with
197 additions
and
215 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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) |
Oops, something went wrong.