From 24d37e66f22c8ffe1453d7cb25c8b5edbb272959 Mon Sep 17 00:00:00 2001 From: Graham Batty Date: Thu, 26 Feb 2009 19:09:56 -0700 Subject: [PATCH] Improved rackup's ability to handle handler-specific options. Made it so that you can specify optparse arguments in a handler definition and rackup (and hopefuly any other rack-using frontend) can take handler-specific options by dynamically rewriting the optparse table when you specify a server. In theory, this should help alleviate the situation where both handlers AND frameworks are writing their own tools to start because they each need to have their own custom options. Now handlers can take their custom options through a framework (or rackup's) initializer. Prime example would be rails 2.3's script/server, which I hope will also adopt this convention. --- bin/rackup | 63 ++++++++++++++++++++++++++----------------- lib/rack/handler/mongrel.rb | 17 +++++++++++- 2 files changed, 54 insertions(+), 26 deletions(-) diff --git a/bin/rackup b/bin/rackup index 65855de..382f51b 100755 --- a/bin/rackup +++ b/bin/rackup @@ -9,12 +9,13 @@ env = "development" daemonize = false pid = nil options = {:Port => 9292, :Host => "0.0.0.0", :AccessLog => []} +handler_options = {} # Don't evaluate CGI ISINDEX parameters. # http://hoohoo.ncsa.uiuc.edu/cgi/cl.html ARGV.clear if ENV.include?("REQUEST_METHOD") -opts = OptionParser.new("", 24, ' ') { |opts| +opts = OptionParser.new("", 30, ' ') { |opts| opts.banner = "Usage: rackup [ruby options] [rack options] [rackup config]" opts.separator "" @@ -46,7 +47,18 @@ opts = OptionParser.new("", 24, ' ') { |opts| opts.separator "" opts.separator "Rack options:" opts.on("-s", "--server SERVER", "serve using SERVER (webrick/mongrel)") { |s| - server = s + require 'rack' + server = Rack::Handler.get(s) + + # You only get expanded options if you explicitly choose the server type. Sorry. + if (server.respond_to? :options_parse) + opts.separator "" + opts.separator "Server options (#{server.class.name}):" + + server.options_parse(opts, handler_options) + opts.separator "" + opts.separator "" + end } opts.on("-o", "--host HOST", "listen on HOST (default: 0.0.0.0)") { |host| @@ -69,9 +81,6 @@ opts = OptionParser.new("", 24, ' ') { |opts| pid = File.expand_path(f) } - opts.separator "" - opts.separator "Common options:" - opts.on_tail("-h", "--help", "Show this message") do puts opts exit @@ -88,27 +97,11 @@ opts = OptionParser.new("", 24, ' ') { |opts| require 'pp' if $DEBUG -config = ARGV[0] || "config.ru" -if !File.exist? config - abort "configuration #{config} not found" -end - -if config =~ /\.ru$/ - cfgfile = File.read(config) - if cfgfile[/^#\\(.*)/] - opts.parse! $1.split(/\s+/) - end - require 'rack' - inner_app = eval "Rack::Builder.new {( " + cfgfile + "\n )}.to_app", - nil, config -else +unless server require 'rack' - require config - inner_app = Object.const_get(File.basename(config, '.rb').capitalize) -end -unless server = Rack::Handler.get(server) - # Guess. + # Make an initial stab at what type of server we want. + # The switching code below can override it. if ENV.include?("PHP_FCGI_CHILDREN") server = Rack::Handler::FastCGI @@ -126,7 +119,24 @@ unless server = Rack::Handler.get(server) end end -p server if $DEBUG +config = ARGV[0] || "config.ru" +if !File.exist? config + abort "configuration #{config} not found" +end + +if config =~ /\.ru$/ + cfgfile = File.read(config) + require 'rack' + if cfgfile[/^#\\(.*)/] + opts.parse! $1.split(/\s+/) + end + inner_app = eval "Rack::Builder.new {( " + cfgfile + "\n )}.to_app", + nil, config +else + require config + require 'rack' + inner_app = Object.const_get(File.basename(config, '.rb').capitalize) +end case env when "development" @@ -173,4 +183,7 @@ if daemonize end end +# merge in server-specific options +options = handler_options.merge(options) + server.run app, options diff --git a/lib/rack/handler/mongrel.rb b/lib/rack/handler/mongrel.rb index f0c0d58..1b0329f 100644 --- a/lib/rack/handler/mongrel.rb +++ b/lib/rack/handler/mongrel.rb @@ -8,7 +8,10 @@ module Rack class Mongrel < ::Mongrel::HttpHandler def self.run(app, options={}) server = ::Mongrel::HttpServer.new(options[:Host] || '0.0.0.0', - options[:Port] || 8080) + options[:Port] || 8080, + options[:Processors] || 950, + options[:Throttle] || 0, + options[:Timeout] || 60) # Acts like Rack::URLMap, utilizing Mongrel's own path finding methods. # Use is similar to #run, replacing the app argument with a hash of # { path=>app, ... } or an instance of Rack::URLMap. @@ -33,6 +36,18 @@ module Rack yield server if block_given? server.run.join end + + def self.options_parse(opts, options) + opts.on("-R", "--mongrel-processors NUM", "Number of concurrent processors to accept (default: 950)") do |n| + options[:Processors] = n.to_i + end + opts.on("-T", "--mongrel-timeout SECONDS", "Time before request is dropped for inactivity (default: 60)") do |s| + options[:Timeout] = s.to_i + end + opts.on("-B", "--mongrel-throttle TIME", "Throttle time between socket.accept calls in hundredths of a second (default: 0)") do |t| + options[:Throttle] = t.to_i + end + end def initialize(app) @app = Rack::Chunked.new(Rack::ContentLength.new(app)) -- 1.6.1.2