server.rb 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323
  1. require 'optparse'
  2. module Rack
  3. class Server
  4. class Options
  5. def parse!(args)
  6. options = {}
  7. opt_parser = OptionParser.new("", 24, ' ') do |opts|
  8. opts.banner = "Usage: rackup [ruby options] [rack options] [rackup config]"
  9. opts.separator ""
  10. opts.separator "Ruby options:"
  11. lineno = 1
  12. opts.on("-e", "--eval LINE", "evaluate a LINE of code") { |line|
  13. eval line, TOPLEVEL_BINDING, "-e", lineno
  14. lineno += 1
  15. }
  16. opts.on("-d", "--debug", "set debugging flags (set $DEBUG to true)") {
  17. options[:debug] = true
  18. }
  19. opts.on("-w", "--warn", "turn warnings on for your script") {
  20. options[:warn] = true
  21. }
  22. opts.on("-I", "--include PATH",
  23. "specify $LOAD_PATH (may be used more than once)") { |path|
  24. options[:include] = path.split(":")
  25. }
  26. opts.on("-r", "--require LIBRARY",
  27. "require the library, before executing your script") { |library|
  28. options[:require] = library
  29. }
  30. opts.separator ""
  31. opts.separator "Rack options:"
  32. opts.on("-s", "--server SERVER", "serve using SERVER (webrick/mongrel)") { |s|
  33. options[:server] = s
  34. }
  35. opts.on("-o", "--host HOST", "listen on HOST (default: 0.0.0.0)") { |host|
  36. options[:Host] = host
  37. }
  38. opts.on("-p", "--port PORT", "use PORT (default: 9292)") { |port|
  39. options[:Port] = port
  40. }
  41. opts.on("-O", "--option NAME[=VALUE]", "pass VALUE to the server as option NAME. If no VALUE, sets it to true. Run '#{$0} -s SERVER -h' to get a list of options for SERVER") { |name|
  42. name, value = name.split('=', 2)
  43. value = true if value.nil?
  44. options[name.to_sym] = value
  45. }
  46. opts.on("-E", "--env ENVIRONMENT", "use ENVIRONMENT for defaults (default: development)") { |e|
  47. options[:environment] = e
  48. }
  49. opts.on("-D", "--daemonize", "run daemonized in the background") { |d|
  50. options[:daemonize] = d ? true : false
  51. }
  52. opts.on("-P", "--pid FILE", "file to store PID (default: rack.pid)") { |f|
  53. options[:pid] = ::File.expand_path(f)
  54. }
  55. opts.separator ""
  56. opts.separator "Common options:"
  57. opts.on_tail("-h", "-?", "--help", "Show this message") do
  58. puts opts
  59. puts handler_opts(options)
  60. exit
  61. end
  62. opts.on_tail("--version", "Show version") do
  63. puts "Rack #{Rack.version} (Release: #{Rack.release})"
  64. exit
  65. end
  66. end
  67. begin
  68. opt_parser.parse! args
  69. rescue OptionParser::InvalidOption => e
  70. warn e.message
  71. abort opt_parser.to_s
  72. end
  73. options[:config] = args.last if args.last
  74. options
  75. end
  76. def handler_opts(options)
  77. begin
  78. info = []
  79. server = Rack::Handler.get(options[:server]) || Rack::Handler.default(options)
  80. if server && server.respond_to?(:valid_options)
  81. info << ""
  82. info << "Server-specific options for #{server.name}:"
  83. has_options = false
  84. server.valid_options.each do |name, description|
  85. next if name.to_s.match(/^(Host|Port)[^a-zA-Z]/) # ignore handler's host and port options, we do our own.
  86. info << " -O %-21s %s" % [name, description]
  87. has_options = true
  88. end
  89. return "" if !has_options
  90. end
  91. info.join("\n")
  92. rescue NameError
  93. return "Warning: Could not find handler specified (#{options[:server] || 'default'}) to determine handler-specific options"
  94. end
  95. end
  96. end
  97. # Start a new rack server (like running rackup). This will parse ARGV and
  98. # provide standard ARGV rackup options, defaulting to load 'config.ru'.
  99. #
  100. # Providing an options hash will prevent ARGV parsing and will not include
  101. # any default options.
  102. #
  103. # This method can be used to very easily launch a CGI application, for
  104. # example:
  105. #
  106. # Rack::Server.start(
  107. # :app => lambda do |e|
  108. # [200, {'Content-Type' => 'text/html'}, ['hello world']]
  109. # end,
  110. # :server => 'cgi'
  111. # )
  112. #
  113. # Further options available here are documented on Rack::Server#initialize
  114. def self.start(options = nil)
  115. new(options).start
  116. end
  117. attr_writer :options
  118. # Options may include:
  119. # * :app
  120. # a rack application to run (overrides :config)
  121. # * :config
  122. # a rackup configuration file path to load (.ru)
  123. # * :environment
  124. # this selects the middleware that will be wrapped around
  125. # your application. Default options available are:
  126. # - development: CommonLogger, ShowExceptions, and Lint
  127. # - deployment: CommonLogger
  128. # - none: no extra middleware
  129. # note: when the server is a cgi server, CommonLogger is not included.
  130. # * :server
  131. # choose a specific Rack::Handler, e.g. cgi, fcgi, webrick
  132. # * :daemonize
  133. # if true, the server will daemonize itself (fork, detach, etc)
  134. # * :pid
  135. # path to write a pid file after daemonize
  136. # * :Host
  137. # the host address to bind to (used by supporting Rack::Handler)
  138. # * :Port
  139. # the port to bind to (used by supporting Rack::Handler)
  140. # * :AccessLog
  141. # webrick acess log options (or supporting Rack::Handler)
  142. # * :debug
  143. # turn on debug output ($DEBUG = true)
  144. # * :warn
  145. # turn on warnings ($-w = true)
  146. # * :include
  147. # add given paths to $LOAD_PATH
  148. # * :require
  149. # require the given libraries
  150. def initialize(options = nil)
  151. @options = options
  152. @app = options[:app] if options && options[:app]
  153. end
  154. def options
  155. @options ||= parse_options(ARGV)
  156. end
  157. def default_options
  158. {
  159. :environment => ENV['RACK_ENV'] || "development",
  160. :pid => nil,
  161. :Port => 9292,
  162. :Host => "0.0.0.0",
  163. :AccessLog => [],
  164. :config => "config.ru"
  165. }
  166. end
  167. def app
  168. @app ||= begin
  169. if !::File.exist? options[:config]
  170. abort "configuration #{options[:config]} not found"
  171. end
  172. app, options = Rack::Builder.parse_file(self.options[:config], opt_parser)
  173. self.options.merge! options
  174. app
  175. end
  176. end
  177. def self.logging_middleware
  178. lambda { |server|
  179. server.server.name =~ /CGI/ ? nil : [Rack::CommonLogger, $stderr]
  180. }
  181. end
  182. def self.middleware
  183. @middleware ||= begin
  184. m = Hash.new {|h,k| h[k] = []}
  185. m["deployment"].concat [
  186. [Rack::ContentLength],
  187. [Rack::Chunked],
  188. logging_middleware
  189. ]
  190. m["development"].concat m["deployment"] + [[Rack::ShowExceptions], [Rack::Lint]]
  191. m
  192. end
  193. end
  194. def middleware
  195. self.class.middleware
  196. end
  197. def start &blk
  198. if options[:warn]
  199. $-w = true
  200. end
  201. if includes = options[:include]
  202. $LOAD_PATH.unshift(*includes)
  203. end
  204. if library = options[:require]
  205. require library
  206. end
  207. if options[:debug]
  208. $DEBUG = true
  209. require 'pp'
  210. p options[:server]
  211. pp wrapped_app
  212. pp app
  213. end
  214. # Touch the wrapped app, so that the config.ru is loaded before
  215. # daemonization (i.e. before chdir, etc).
  216. wrapped_app
  217. daemonize_app if options[:daemonize]
  218. write_pid if options[:pid]
  219. trap(:INT) do
  220. if server.respond_to?(:shutdown)
  221. server.shutdown
  222. else
  223. exit
  224. end
  225. end
  226. server.run wrapped_app, options, &blk
  227. end
  228. def server
  229. @_server ||= Rack::Handler.get(options[:server]) || Rack::Handler.default(options)
  230. end
  231. private
  232. def parse_options(args)
  233. options = default_options
  234. # Don't evaluate CGI ISINDEX parameters.
  235. # http://hoohoo.ncsa.uiuc.edu/cgi/cl.html
  236. args.clear if ENV.include?("REQUEST_METHOD")
  237. options.merge! opt_parser.parse!(args)
  238. options[:config] = ::File.expand_path(options[:config])
  239. ENV["RACK_ENV"] = options[:environment]
  240. options
  241. end
  242. def opt_parser
  243. Options.new
  244. end
  245. def build_app(app)
  246. middleware[options[:environment]].reverse_each do |middleware|
  247. middleware = middleware.call(self) if middleware.respond_to?(:call)
  248. next unless middleware
  249. klass = middleware.shift
  250. app = klass.new(app, *middleware)
  251. end
  252. app
  253. end
  254. def wrapped_app
  255. @wrapped_app ||= build_app app
  256. end
  257. def daemonize_app
  258. if RUBY_VERSION < "1.9"
  259. exit if fork
  260. Process.setsid
  261. exit if fork
  262. Dir.chdir "/"
  263. STDIN.reopen "/dev/null"
  264. STDOUT.reopen "/dev/null", "a"
  265. STDERR.reopen "/dev/null", "a"
  266. else
  267. Process.daemon
  268. end
  269. end
  270. def write_pid
  271. ::File.open(options[:pid], 'w'){ |f| f.write("#{Process.pid}") }
  272. at_exit { ::File.delete(options[:pid]) if ::File.exist?(options[:pid]) }
  273. end
  274. end
  275. end