public
Description: A very fast & simple Ruby web server
Homepage: http://code.macournoyer.com/thin/
Clone URL: git://github.com/macournoyer/thin.git
Search Repo:
Merge branch 'master' into keepalive

Conflicts:

  CHANGELOG
  lib/thin/connection.rb
  lib/thin/request.rb
  lib/thin/response.rb
  lib/thin/server.rb
  lib/thin/version.rb
  spec/server_spec.rb
macournoyer (author)
Sun Feb 03 19:49:51 -0800 2008
commit  c2475e64f14fdfad1248c69edda3ae50a11eb870
tree    8f3f5fd04cd2115b22e27425aa7fff365f28609a
parent  02b5468eb7eb02c1ceb267bcd77f2be4c708211d parent  31c97b8758a9529714f302ededb8664f17d8604f
...
5
6
7
 
8
9
10
...
5
6
7
8
9
10
11
0
@@ -5,6 +5,7 @@
0
 ext/thin_parser/*.bundle
0
 lib/*.bundle
0
 log/*.log
0
+spec/rails_app/log
0
 doc/rdoc/*
0
 tmp/*
0
 pkg/*
...
1
2
3
 
 
 
 
 
 
 
 
 
 
 
 
4
 
 
 
 
 
5
6
7
...
12
13
14
15
 
16
17
18
...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
...
29
30
31
 
32
33
34
35
0
@@ -1,7 +1,24 @@
0
 == 0.7.0 Bionic Pickle release
0
  * Persistent connection (keep-alive) & HTTP pipelining support
0
 
0
+== 0.6.3 Ninja Cookie release
0
+ * Add a script to run thin as a system service that can start at startup, closes #31 [Gump]
0
+ Setup the service like this:
0
+
0
+ sudo thin install /etc/thin
0
+
0
+ This will install the boot script under /etc/init.d/thin. Then copy your config files to
0
+ /etc/thin. Works only under Linux.
0
+ * Set process name to 'thin server (0.0.0.0:3000)' when running as a daemon, closes #32.
0
+ * Make sure chdir option from config file is used when present.
0
+ * Raise an error when starting a server as a daemon and pid file already exist, fixes #27.
0
+
0
 == 0.6.2 Rambo release
0
+ * Server now let current connections finish before stopping, fixes #18
0
+ * Fix uploading hanging bug when body is moved to a tempfile,
0
+ also delete the tempfile properly upon completion, fixes #25
0
+ * 'thin restart' now sends HUP signals rather then stopping & starting, closes #17
0
+ * HUP signal now launches a new process with the same options.
0
  * Add PID and more info from the last request to the Stats adapter
0
    mostly taken from Rack::ShowException.
0
  * pid and log files in cluster are no longer required to be relative to the
0
@@ -12,7 +29,7 @@
0
  * Add --only (-o) option to control only one server of a cluster.
0
  * Stylize stats page and make the url configurable from the thin script.
0
  * Raise error if attempting to use unix sockets on windows.
0
- * Add example config files for http://www.tildeslash.com/monit useage.
0
+ * Add example config files for http://www.tildeslash.com/monit usage.
0
    Include the example file using "include /path/to/thin/monit/file" in your monitrc file.
0
    The group settings let you do this to manage your clusters:
0
    
...
3
4
5
6
 
7
8
9
10
11
 
12
 
13
14
 
...
3
4
5
 
6
7
8
9
10
 
11
12
13
14
 
15
0
@@ -3,13 +3,14 @@
0
 #
0
 # Run with:
0
 #
0
-# ruby simple.rb [num of request]]
0
+# ruby simple.rb [num of request] [print|graph] [concurrency levels]
0
 #
0
 require File.dirname(__FILE__) + '/../lib/thin'
0
 require File.dirname(__FILE__) + '/utils'
0
 
0
-request = (ARGV[0] || 1000).to_i # Number of request to send (ab -n option)
0
+request = (ARGV[0] || 1000).to_i # Number of request to send (ab -n option)
0
 output_type = (ARGV[1] || 'print')
0
+levels = eval(ARGV[2] || '[1, 10, 100]').to_a
0
 
0
-benchmark output_type, %w(WEBrick Mongrel EMongrel Thin), request, [1, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100]
0
+benchmark output_type, %w(WEBrick Mongrel EMongrel Thin), request, levels
...
29
30
31
32
 
33
34
35
...
29
30
31
 
32
33
34
35
0
@@ -29,7 +29,7 @@
0
 
0
   sleep 2
0
 
0
- out = `nice -n20 ab -c #{c} -n #{n} http://127.0.0.1:port/ 2> /dev/null`
0
+ out = `nice -n20 ab -c #{c} -n #{n} http://127.0.0.1:#{port}/ 2> /dev/null`
0
 
0
   Process.kill('SIGKILL', server)
0
   Process.wait
...
1
2
 
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
 
...
1
 
2
3
4
 
 
5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6
0
@@ -1,170 +1,7 @@
0
 #!/usr/bin/env ruby
0
-# <tt>thin start</tt>: Starts the Rails app in the current directory.
0
+# Thin command line interface script.
0
 # Run <tt>thin -h</tt> to get more usage.
0
 require File.dirname(__FILE__) + '/../lib/thin'
0
-require 'optparse'
0
-require 'yaml'
0
 
0
-COMMANDS = %w(start stop restart config)
0
-
0
-# Default options values
0
-options = {
0
- :chdir => Dir.pwd,
0
- :environment => 'development',
0
- :address => '0.0.0.0',
0
- :port => 3000,
0
- :timeout => 60,
0
- :log => 'log/thin.log',
0
- :pid => 'tmp/pids/thin.pid',
0
- :servers => 1 # no cluster
0
-}
0
-
0
-# NOTE: If you add an option here make sure the key in the +options+ hash is the
0
-# same as the name of the command line option.
0
-# +option+ keys are use to build the command line to launch a cluster,
0
-# see <tt>lib/thin/cluster.rb</tt>.
0
-opts = OptionParser.new do |opts|
0
- opts.banner = "Usage: thin [options] #{COMMANDS.join('|')}"
0
-
0
- opts.separator ""
0
- opts.separator "Server options:"
0
-
0
- opts.on("-a", "--address HOST", "bind to HOST address (default: 0.0.0.0)") { |host| options[:address] = host }
0
- opts.on("-p", "--port PORT", "use PORT (default: 3000)") { |port| options[:port] = port.to_i }
0
- opts.on("-S", "--socket PATH", "bind to unix domain socket") { |file| options[:socket] = file }
0
- opts.on("-e", "--environment ENV", "Rails environment (default: development)") { |env| options[:environment] = env }
0
- opts.on("-c", "--chdir PATH", "Change to dir before starting") { |dir| options[:chdir] = File.expand_path(dir) }
0
- opts.on("-t", "--timeout SEC", "Request or command timeout in sec",
0
- "(default: #{options[:timeout]})") { |sec| options[:timeout] = sec.to_i }
0
- opts.on( "--prefix PATH", "Mount the app under PATH (start with /)") { |path| options[:prefix] = path }
0
- opts.on( "--stats PATH", "Mount the Stats adapter under PATH") { |path| options[:stats] = path }
0
-
0
- opts.separator ""
0
- opts.separator "Daemon options:"
0
-
0
- opts.on("-d", "--daemonize", "Run daemonized in the background") { options[:daemonize] = true }
0
- opts.on("-l", "--log FILE", "File to redirect output",
0
- "(default: #{options[:log]})") { |file| options[:log] = file }
0
- opts.on("-P", "--pid FILE", "File to store PID",
0
- "(default: #{options[:pid]})") { |file| options[:pid] = file }
0
- opts.on("-u", "--user NAME", "User to run daemon as (use with -g)") { |user| options[:user] = user }
0
- opts.on("-g", "--group NAME", "Group to run daemon as (use with -u)") { |group| options[:group] = group }
0
-
0
- opts.separator ""
0
- opts.separator "Cluster options:"
0
-
0
- opts.on("-s", "--servers NUM", "Number of servers to start",
0
- "set a value >1 to start a cluster") { |num| options[:servers] = num.to_i }
0
- opts.on("-o", "--only NUM", "Send command to only one server of the cluster") { |only| options[:only] = only }
0
- opts.on("-C", "--config PATH", "Load options from a config file") { |file| options[:config] = file }
0
-
0
- opts.separator ""
0
- opts.separator "Common options:"
0
-
0
- opts.on_tail("-D", "--debug", "Set debbuging on") { $DEBUG = true }
0
- opts.on_tail("-V", "--trace", "Set tracing on") { $TRACE = true }
0
- opts.on_tail("-h", "--help", "Show this message") { puts opts; exit }
0
- opts.on_tail('-v', '--version', "Show version") { puts Thin::SERVER; exit }
0
-end
0
-
0
-
0
-# == Utilities
0
-
0
-def cluster?(options)
0
- options[:only] || (options[:servers] && options[:servers] > 1)
0
-end
0
-
0
-def load_options_from_config_file!(options)
0
- if file = options.delete(:config)
0
- YAML.load_file(file).each { |key, value| options[key.to_sym] = value }
0
- end
0
-end
0
-
0
-
0
-# == Commands definitions
0
-
0
-def start(options)
0
- load_options_from_config_file! options
0
-
0
- if cluster?(options)
0
- Thin::Cluster.new(options).start
0
- else
0
- if options[:socket]
0
- server = Thin::Server.new(options[:socket])
0
- else
0
- server = Thin::Server.new(options[:address], options[:port])
0
- end
0
-
0
- server.pid_file = options[:pid]
0
- server.log_file = options[:log]
0
- server.timeout = options[:timeout]
0
-
0
- if options[:daemonize]
0
- server.daemonize
0
- server.change_privilege options[:user], options[:group] if options[:user] && options[:group]
0
- end
0
-
0
- server.app = Rack::Adapter::Rails.new(options.merge(:root => options[:chdir]))
0
-
0
- # If a prefix is required, wrap in Rack URL mapper
0
- server.app = Rack::URLMap.new(options[:prefix] => server.app) if options[:prefix]
0
-
0
- # If a stats are required, wrap in Stats adapter
0
- server.app = Thin::Stats::Adapter.new(server.app, options[:stats]) if options[:stats]
0
-
0
- server.start!
0
- end
0
-end
0
-
0
-def stop(options)
0
- load_options_from_config_file! options
0
-
0
- if cluster?(options)
0
- Thin::Cluster.new(options).stop
0
- else
0
- Thin::Server.kill options[:pid], options[:timeout]
0
- end
0
-end
0
-
0
-def restart(options)
0
- load_options_from_config_file! options
0
-
0
- if cluster?(options)
0
- Thin::Cluster.new(options).restart
0
- else
0
- # Restart only make sense when running as a daemon
0
- options.update :daemonize => true
0
-
0
- stop(options)
0
- start(options)
0
- end
0
-end
0
-
0
-def config(options)
0
- config_file = options.delete(:config) || abort('config option required')
0
-
0
- # Stringify keys
0
- options.keys.each { |o| options[o.to_s] = options.delete(o) }
0
-
0
- File.open(config_file, 'w') { |f| f << options.to_yaml }
0
- puts "Wrote configuration to #{config_file}"
0
-end
0
-
0
-
0
-# == Runs the command
0
-
0
-opts.parse! ARGV
0
-command = ARGV[0]
0
-
0
-Dir.chdir(options[:chdir])
0
-
0
-if COMMANDS.include?(command)
0
- send(command, options)
0
-elsif command.nil?
0
- puts "Command required"
0
- puts opts
0
- exit 1
0
-else
0
- abort "Invalid command : #{command}"
0
-end
0
+Thin::Runner.new(ARGV).run!
...
11
12
13
14
15
16
17
 
18
 
19
20
21
22
23
 
24
 
25
26
27
...
11
12
13
 
 
 
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
0
@@ -11,17 +11,18 @@
0
 require 'thin/statuses'
0
 
0
 module Thin
0
- NAME = 'thin'.freeze
0
- SERVER = "#{NAME} #{VERSION::STRING} codename #{VERSION::CODENAME}".freeze
0
-
0
   autoload :Cluster, 'thin/cluster'
0
+ autoload :Command, 'thin/command'
0
   autoload :Connection, 'thin/connection'
0
+ autoload :Controller, 'thin/controller'
0
   autoload :Daemonizable, 'thin/daemonizing'
0
   autoload :Logging, 'thin/logging'
0
   autoload :Headers, 'thin/headers'
0
   autoload :Request, 'thin/request'
0
   autoload :Response, 'thin/response'
0
+ autoload :Runner, 'thin/runner'
0
   autoload :Server, 'thin/server'
0
+ autoload :Service, 'thin/service'
0
   autoload :Stats, 'thin/stats'
0
 end
0
 
...
3
4
5
6
7
8
9
10
11
12
 
13
14
15
16
17
18
 
19
20
21
22
23
24
25
26
27
...
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
 
119
120
121
...
3
4
5
 
 
 
 
 
 
 
6
7
8
 
 
 
 
9
10
11
12
13
14
 
15
16
17
...
90
91
92
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
93
94
95
96
0
@@ -3,25 +3,15 @@
0
   # * Generate start and stop commands and run them.
0
   # * Inject the port or socket number in the pid and log filenames.
0
   # Servers are started throught the +thin+ command-line script.
0
- class Cluster
0
- include Logging
0
-
0
- # Path to the +thin+ script used to control the servers.
0
- # Leave this to default to use the one in the path.
0
- attr_accessor :script
0
-
0
+ class Cluster < Controller
0
     # Number of servers in the cluster.
0
     attr_accessor :size
0
-
0
- # Command line options passed to the thin script
0
- attr_accessor :options
0
-
0
+
0
     # Create a new cluster of servers launched using +options+.
0
     def initialize(options)
0
       @options = options.merge(:daemonize => true)
0
       @size = @options.delete(:servers)
0
       @only = @options.delete(:only)
0
- @script = 'thin'
0
       
0
       if socket
0
         @options.delete(:address)
0
@@ -100,22 +90,7 @@
0
         else
0
           cmd_options.merge!(:port => number)
0
         end
0
- shell_cmd = shellify(cmd, cmd_options)
0
- trace shell_cmd
0
- ouput = `#{shell_cmd}`.chomp
0
- log " " + ouput.gsub("\n", " \n") unless ouput.empty?
0
- end
0
-
0
- # Turn into a runnable shell command
0
- def shellify(cmd, options)
0
- shellified_options = options.inject([]) do |args, (name, value)|
0
- args << case value
0
- when NilClass
0
- when TrueClass then "--#{name}"
0
- else "--#{name.to_s.tr('_', '-')}=#{value.inspect}"
0
- end
0
- end
0
- "#{@script} #{cmd} #{shellified_options.compact.join(' ')}"
0
+ Command.run(cmd, cmd_options)
0
       end
0
       
0
       def with_each_server
...
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
0
@@ -1 +1,45 @@
0
+module Thin
0
+ # Run a command though the +thin+ command-line script.
0
+ class Command
0
+ include Logging
0
+
0
+ class << self
0
+ # Path to the +thin+ script used to control the servers.
0
+ # Leave this to default to use the one in the path.
0
+ attr_accessor :script
0
+ end
0
+
0
+ def initialize(name, options={})
0
+ @name = name
0
+ @options = options
0
+ end
0
+
0
+ def self.run(*args)
0
+ new(*args).run
0
+ end
0
+
0
+ # Send the command to the +thin+ script
0
+ def run
0
+ shell_cmd = shellify
0
+ trace shell_cmd
0
+ ouput = `#{shell_cmd}`.chomp
0
+ log " " + ouput.gsub("\n", " \n") unless ouput.empty?
0
+ end
0
+
0
+ # Turn into a runnable shell command
0
+ def shellify
0
+ shellified_options = @options.inject([]) do |args, (name, value)|
0
+ args << case value
0
+ when NilClass
0
+ when TrueClass then "--#{name}"
0
+ else "--#{name.to_s.tr('_', '-')}=#{value.inspect}"
0
+ end
0
+ end
0
+
0
+ raise ArgumentError, "Path to thin script can't be found, set Command.script" unless self.class.script
0
+
0
+ "#{self.class.script} #{@name} #{shellified_options.compact.join(' ')}"
0
+ end
0
+ end
0
+end
...
10
11
12
 
 
 
13
14
15
...
55
56
57
 
 
 
 
58
59
60
...
10
11
12
13
14
15
16
17
18
...
58
59
60
61
62
63
64
65
66
67
0
@@ -10,6 +10,9 @@
0
     # +true+ if the connection is on a UNIX domain socket.
0
     attr_accessor :unix_socket
0
     
0
+ # Server owning the connection
0
+ attr_accessor :server
0
+
0
     def post_init
0
       @request = Request.new
0
       @response = Response.new
0
@@ -55,6 +58,10 @@
0
       # supports HTTP pipelining (persistent connection).
0
       post_init if @response.persistent?
0
     end
0
+
0
+ def unbind
0
+ @server.connection_finished(self)
0
+ end
0
     
0
     protected
0
       def remote_address
...
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
0
@@ -1 +1,76 @@
0
+require 'yaml'
0
+
0
+module Thin
0
+ # Raised when a mandatory option is missing to run a command.
0
+ class OptionRequired < RuntimeError
0
+ def initialize(option)
0
+ super("#{option} option required")
0
+ end
0
+ end
0
+
0
+ # Control a Thin server.
0
+ # Allow to start, stop, restart and configure a single thin server.
0
+ class Controller
0
+ include Logging
0
+
0
+ # Command line options passed to the thin script
0
+ attr_accessor :options
0
+
0
+ def initialize(options)
0
+ @options = options
0
+ end
0
+
0
+ def start
0
+ if @options[:socket]
0
+ server = Server.new(@options[:socket])
0
+ else
0
+ server = Server.new(@options[:address], @options[:port])
0
+ end
0
+
0
+ server.pid_file = @options[:pid]
0
+ server.log_file = @options[:log]
0
+ server.timeout = @options[:timeout]
0
+
0
+ if @options[:daemonize]
0
+ server.daemonize
0
+ server.change_privilege @options[:user], @options[:group] if @options[:user] && @options[:group]
0
+ end
0
+
0
+ server.app = Rack::Adapter::Rails.new(@options.merge(:root => @options[:chdir]))
0
+
0
+ # If a prefix is required, wrap in Rack URL mapper
0
+ server.app = Rack::URLMap.new(@options[:prefix] => server.app) if @options[:prefix]
0
+
0
+ # If a stats are required, wrap in Stats adapter
0
+ server.app = Stats::Adapter.new(server.app, @options[:stats]) if @options[:stats]
0
+
0
+ # Register restart procedure
0
+ server.on_restart { Command.run(:start, @options) }
0
+
0
+ server.start
0
+ end
0
+
0
+ def stop
0
+ raise OptionRequired, :pid unless @options[:pid]
0
+
0
+ Server.kill(@options[:pid], @options[:timeout] || 60)
0
+ end
0
+
0
+ def restart
0
+ raise OptionRequired, :pid unless @options[:pid]
0
+
0
+ Server.restart(@options[:pid])
0
+ end
0
+
0
+ def config
0
+ config_file = @options.delete(:config) || raise(OptionRequired, :config)
0
+
0
+ # Stringify keys
0
+ @options.keys.each { |o| @options[o.to_s] = @options.delete(o) }
0
+
0
+ File.open(config_file, 'w') { |f| f << @options.to_yaml }
0
+ log ">> Wrote configuration to #{config_file}"
0
+ end
0
+ end
0
+end
...
12
13
14
 
 
 
15
16
17
18
19
...
31
32
33
34
35
 
 
 
 
36
37
38
39
 
40
41
42
43
 
 
44
45
46
47
48
49
50
51
52
53
54
55
...
66
67
68
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
69
70
 
 
 
71
72
73
74
 
75
76
77
78
79
80
81
82
 
 
83
84
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
85
86
 
 
87
88
89
90
91
 
92
93
94
95
 
96
97
98
99
 
100
101
102
...
12
13
14
15
16
17
18
19
20
21
22
...
34
35
36
 
 
37
38
39
40
41
42
43
 
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
...
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
 
92
93
94
95
 
 
 
96
97
 
98
99
100
101
 
 
102
103
104
 
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
 
123
124
125
126
127
 
 
128
129
130
131
 
132
133
134
135
 
136
137
138
139
0
@@ -12,6 +12,9 @@
0
 end
0
 
0
 module Thin
0
+ # Raised when the pid file already exist starting as a daemon.
0
+ class PidFileExist < RuntimeError; end
0
+
0
   # Module included in classes that can be turned into a daemon.
0
   # Handle stuff like:
0
   # * storing the PID in a file
0
0
0
@@ -31,16 +34,20 @@
0
     
0
     # Turns the current script into a daemon process that detaches from the console.
0
     def daemonize
0
- raise PlatformNotSupported, 'Daemonizing not supported on Windows' if Thin.win?
0
- raise ArgumentError, 'You must specify a pid_file to deamonize' unless @pid_file
0
+ raise PlatformNotSupported, 'Daemonizing not supported on Windows' if Thin.win?
0
+ raise ArgumentError, 'You must specify a pid_file to daemonize' unless @pid_file
0
+ raise PidFileExist, "#{@pid_file} already exist, seems like it's already running. " +
0
+ "Stop the process or delete #{@pid_file}." if File.exist?(@pid_file)
0
       
0
       pwd = Dir.pwd # Current directory is changed during daemonization, so store it
0
       
0
- Daemonize.daemonize(File.expand_path(@log_file))
0
+ Daemonize.daemonize(File.expand_path(@log_file), name)
0
       
0
       Dir.chdir(pwd)
0
       
0
       write_pid_file
0
+
0
+ trap('HUP') { restart }
0
       at_exit do
0
         log ">> Exiting!"
0
         remove_pid_file
0
0
0
0
0
0
0
0
0
0
@@ -66,37 +73,67 @@
0
       log "Couldn't change user and group to #{user}:#{group}: #{e}"
0
     end
0
     
0
+ # Registerer a proc to be called to restart the server.
0
+ def on_restart(&block)
0
+ @on_restart = block
0
+ end
0
+
0
+ # Restart the server
0
+ def restart
0
+ raise ArgumentError, "Can't restart, no 'on_restart' proc specified" unless @on_restart
0
+ log '>> Restarting ...'
0
+ stop
0
+ remove_pid_file
0
+ @on_restart.call
0
+ exit!
0
+ end
0
+
0
     module ClassMethods
0
- # Kill the process which PID is stored in +pid_file+.
0
+ # Send a INT signal the process which PID is stored in +pid_file+.
0
+ # If the process is still running after +timeout+, KILL signal is
0
+ # sent.
0
       def kill(pid_file, timeout=60)
0
- if pid = open(pid_file).read
0
- pid = pid.to_i
0
- print "Sending INT signal to process #{pid} ... "
0
+ if pid = send_signal('INT', pid_file)
0
           begin
0
- Process.kill('INT', pid)
0
             Timeout.timeout(timeout) do
0
               sleep 0.1 while Process.running?(pid)
0
             end
0
           rescue Timeout::Error
0
- print "timeout, Sending KILL signal ... "
0
- Process.kill('KILL', pid)
0
+ print "timeout! "
0
+ send_signal('KILL', pid_file)
0
           end
0
- puts "stopped!"
0
+ end
0
+ File.delete(pid_file) if File.exist?(pid_file)
0
+ end
0
+
0
+ # Restart the server by sending HUP signal
0
+ def restart(pid_file)
0
+ send_signal('HUP', pid_file)
0
+ end
0
+
0
+ # Send a +signal+ to the process which PID is stored in +pid_file+.
0
+ def send_signal(signal, pid_file)
0
+ if File.exist?(pid_file) && pid = open(pid_file).read
0
+ pid = pid.to_i
0
+ print "Sending #{signal} signal to process #{pid} ... "
0
+ Process.kill(signal, pid)
0
+ puts
0
+ pid
0
         else
0
- puts "Can't stop process, no PID found in #{@pid_file}"
0
+ puts "Can't stop process, no PID found in #{pid_file}"
0
+ nil
0
         end
0
       rescue Errno::ESRCH # No such process
0
         puts "process not found!"
0
- ensure
0
- File.delete(pid_file) rescue nil
0
+ nil
0
       end
0
     end
0
     
0
- private
0
+ protected
0
       def remove_pid_file
0
         File.delete(@pid_file) if @pid_file && File.exists?(@pid_file)
0
       end
0
-
0
+
0
       def write_pid_file
0
         log ">> Writing PID to #{@pid_file}"
0
         FileUtils.mkdir_p File.dirname(@pid_file)
...
10
11
12
 
 
 
13
14
 
15
16
17
...
10
11
12
13
14
15
16
 
17
18
19
20
0
@@ -10,8 +10,11 @@
0
       @out = ''
0
     end
0
     
0
+ # Add <tt>key: value</tt> pair to the headers.
0
+ # Ignore if already sent and no duplicates are allowed
0
+ # for this +key+.
0
     def []=(key, value)
0
- if !@sent[key] || ALLOWED_DUPLICATES.include?(key)
0
+ if !@sent.has_key?(key) || ALLOWED_DUPLICATES.include?(key)
0
         @sent[key] = true
0
         @out << HEADER_FORMAT % [key, value]
0
       end
...
16
17
18
 
19
20
21
...
16
17
18
19
20
21
22
0
@@ -16,6 +16,7 @@
0
         puts msg || yield if ($DEBUG || $TRACE) && !@silent
0
       end
0
       
0
+ # Log an error backtrace if tracing is activated
0
       def log_error(e)
0
         trace { "#{e}\n\t" + e.backtrace.join("\n\t") }
0
       end