thin 0.6.2 → 0.6.3

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of thin might be problematic. Click here for more details.

Files changed (49) hide show
  1. data/CHANGELOG +34 -0
  2. data/bin/thin +2 -164
  3. data/example/config.ru +4 -1
  4. data/example/ramaze.ru +12 -0
  5. data/example/thin.god +70 -66
  6. data/example/vlad.rake +61 -0
  7. data/lib/rack/adapter/rails.rb +0 -3
  8. data/lib/rack/handler/thin.rb +6 -1
  9. data/lib/thin.rb +13 -4
  10. data/lib/thin/command.rb +9 -5
  11. data/lib/thin/connection.rb +5 -14
  12. data/lib/thin/connectors/connector.rb +61 -0
  13. data/lib/thin/connectors/tcp_server.rb +29 -0
  14. data/lib/thin/connectors/unix_server.rb +48 -0
  15. data/lib/thin/controllers/cluster.rb +115 -0
  16. data/lib/thin/controllers/controller.rb +85 -0
  17. data/lib/thin/controllers/service.rb +73 -0
  18. data/lib/thin/controllers/service.sh.erb +39 -0
  19. data/lib/thin/daemonizing.rb +9 -4
  20. data/lib/thin/headers.rb +2 -2
  21. data/lib/thin/runner.rb +166 -0
  22. data/lib/thin/server.rb +109 -89
  23. data/lib/thin/stats.html.erb +216 -0
  24. data/lib/thin/stats.rb +1 -249
  25. data/lib/thin/version.rb +10 -3
  26. data/spec/command_spec.rb +0 -1
  27. data/spec/configs/cluster.yml +9 -0
  28. data/spec/configs/single.yml +9 -0
  29. data/spec/{cluster_spec.rb → controllers/cluster_spec.rb} +22 -10
  30. data/spec/controllers/controller_spec.rb +85 -0
  31. data/spec/controllers/service_spec.rb +51 -0
  32. data/spec/daemonizing_spec.rb +73 -9
  33. data/spec/request/mongrel_spec.rb +39 -0
  34. data/spec/{request_spec.rb → request/parser_spec.rb} +11 -143
  35. data/spec/request/perf_spec.rb +50 -0
  36. data/spec/request/processing_spec.rb +46 -0
  37. data/spec/runner_spec.rb +135 -0
  38. data/spec/server/builder_spec.rb +38 -0
  39. data/spec/server/stopping_spec.rb +45 -0
  40. data/spec/server/tcp_spec.rb +54 -0
  41. data/spec/server/unix_socket_spec.rb +30 -0
  42. data/spec/spec_helper.rb +49 -16
  43. data/tasks/announce.rake +7 -3
  44. data/tasks/email.erb +8 -18
  45. data/tasks/gem.rake +8 -1
  46. data/tasks/stats.rake +21 -8
  47. metadata +33 -6
  48. data/lib/thin/cluster.rb +0 -123
  49. data/spec/server_spec.rb +0 -200
data/CHANGELOG CHANGED
@@ -1,3 +1,37 @@
1
+ == 0.6.3 Ninja Cookie release
2
+ * Add tasks for Vlad the Deployer in example/vlad.rake [cnantais]
3
+ * Add Ramaze Rackup config file in example dir [tmm1]
4
+ Use like this from you Ramaze app dir:
5
+
6
+ thin start -r /path/to/thin/example/ramaze.ru
7
+
8
+ * Add the --rackup option to load a Rack config file instead of the Rails adapter.
9
+ So you can use any framework with the thin script and start cluster and stuff like that.
10
+ A Rack config file is one that is usable through the rackup command and looks like this:
11
+
12
+ use Rack::CommonLogger
13
+ run MyCrazyRackAdapter.new(:uterly, 'cool')
14
+
15
+ Then use it with thin like this:
16
+
17
+ thin start --rackup config.ru
18
+
19
+ * thin config --chrdir ... -C thin/yml do not change current directory anymore, fixes #33.
20
+ * Add a better sample god config file in example/thin.god that loads all info from config
21
+ files in /etc/thin. Drop-in replacement for the thin runlevel service [Gump].
22
+ * Add support for specifying a custom Connector to the server and add more doc about Server
23
+ configuration.
24
+ * Add a script to run thin as a runlevel service that can start at startup, closes #31 [Gump]
25
+ Setup the service like this:
26
+
27
+ sudo thin install /etc/thin
28
+
29
+ This will install the boot script under /etc/init.d/thin. Then copy your config files to
30
+ /etc/thin. Works only under Linux.
31
+ * Set process name to 'thin server (0.0.0.0:3000)' when running as a daemon, closes #32.
32
+ * Make sure chdir option from config file is used when present.
33
+ * Raise an error when starting a server as a daemon and pid file already exist, fixes #27.
34
+
1
35
  == 0.6.2 Rambo release
2
36
  * Server now let current connections finish before stopping, fixes #18
3
37
  * Fix uploading hanging bug when body is moved to a tempfile,
data/bin/thin CHANGED
@@ -1,168 +1,6 @@
1
1
  #!/usr/bin/env ruby
2
- # <tt>thin start</tt>: Starts the Rails app in the current directory.
2
+ # Thin command line interface script.
3
3
  # Run <tt>thin -h</tt> to get more usage.
4
4
  require File.dirname(__FILE__) + '/../lib/thin'
5
- require 'optparse'
6
- require 'yaml'
7
5
 
8
- COMMANDS = %w(start stop restart config)
9
-
10
- # Default options values
11
- options = {
12
- :chdir => Dir.pwd,
13
- :environment => 'development',
14
- :address => '0.0.0.0',
15
- :port => 3000,
16
- :timeout => 60,
17
- :log => 'log/thin.log',
18
- :pid => 'tmp/pids/thin.pid',
19
- :servers => 1 # no cluster
20
- }
21
-
22
- # NOTE: If you add an option here make sure the key in the +options+ hash is the
23
- # same as the name of the command line option.
24
- # +option+ keys are use to build the command line to launch a cluster,
25
- # see <tt>lib/thin/cluster.rb</tt>.
26
- opts = OptionParser.new do |opts|
27
- opts.banner = "Usage: thin [options] #{COMMANDS.join('|')}"
28
-
29
- opts.separator ""
30
- opts.separator "Server options:"
31
-
32
- opts.on("-a", "--address HOST", "bind to HOST address (default: 0.0.0.0)") { |host| options[:address] = host }
33
- opts.on("-p", "--port PORT", "use PORT (default: 3000)") { |port| options[:port] = port.to_i }
34
- opts.on("-S", "--socket PATH", "bind to unix domain socket") { |file| options[:socket] = file }
35
- opts.on("-e", "--environment ENV", "Rails environment (default: development)") { |env| options[:environment] = env }
36
- opts.on("-c", "--chdir PATH", "Change to dir before starting") { |dir| options[:chdir] = File.expand_path(dir) }
37
- opts.on("-t", "--timeout SEC", "Request or command timeout in sec",
38
- "(default: #{options[:timeout]})") { |sec| options[:timeout] = sec.to_i }
39
- opts.on( "--prefix PATH", "Mount the app under PATH (start with /)") { |path| options[:prefix] = path }
40
- opts.on( "--stats PATH", "Mount the Stats adapter under PATH") { |path| options[:stats] = path }
41
-
42
- opts.separator ""
43
- opts.separator "Daemon options:"
44
-
45
- opts.on("-d", "--daemonize", "Run daemonized in the background") { options[:daemonize] = true }
46
- opts.on("-l", "--log FILE", "File to redirect output",
47
- "(default: #{options[:log]})") { |file| options[:log] = file }
48
- opts.on("-P", "--pid FILE", "File to store PID",
49
- "(default: #{options[:pid]})") { |file| options[:pid] = file }
50
- opts.on("-u", "--user NAME", "User to run daemon as (use with -g)") { |user| options[:user] = user }
51
- opts.on("-g", "--group NAME", "Group to run daemon as (use with -u)") { |group| options[:group] = group }
52
-
53
- opts.separator ""
54
- opts.separator "Cluster options:"
55
-
56
- opts.on("-s", "--servers NUM", "Number of servers to start",
57
- "set a value >1 to start a cluster") { |num| options[:servers] = num.to_i }
58
- opts.on("-o", "--only NUM", "Send command to only one server of the cluster") { |only| options[:only] = only }
59
- opts.on("-C", "--config PATH", "Load options from a config file") { |file| options[:config] = file }
60
-
61
- opts.separator ""
62
- opts.separator "Common options:"
63
-
64
- opts.on_tail("-D", "--debug", "Set debbuging on") { $DEBUG = true }
65
- opts.on_tail("-V", "--trace", "Set tracing on") { $TRACE = true }
66
- opts.on_tail("-h", "--help", "Show this message") { puts opts; exit }
67
- opts.on_tail('-v', '--version', "Show version") { puts Thin::SERVER; exit }
68
- end
69
-
70
-
71
- # == Utilities
72
-
73
- def cluster?(options)
74
- options[:only] || (options[:servers] && options[:servers] > 1)
75
- end
76
-
77
- def load_options_from_config_file!(options)
78
- if file = options.delete(:config)
79
- YAML.load_file(file).each { |key, value| options[key.to_sym] = value }
80
- end
81
- end
82
-
83
-
84
- # == Commands definitions
85
-
86
- def start(options)
87
- load_options_from_config_file! options
88
-
89
- if cluster?(options)
90
- Thin::Cluster.new(options).start
91
- else
92
- if options[:socket]
93
- server = Thin::Server.new(options[:socket])
94
- else
95
- server = Thin::Server.new(options[:address], options[:port])
96
- end
97
-
98
- server.pid_file = options[:pid]
99
- server.log_file = options[:log]
100
- server.timeout = options[:timeout]
101
-
102
- if options[:daemonize]
103
- server.daemonize
104
- server.change_privilege options[:user], options[:group] if options[:user] && options[:group]
105
- end
106
-
107
- server.app = Rack::Adapter::Rails.new(options.merge(:root => options[:chdir]))
108
-
109
- # If a prefix is required, wrap in Rack URL mapper
110
- server.app = Rack::URLMap.new(options[:prefix] => server.app) if options[:prefix]
111
-
112
- # If a stats are required, wrap in Stats adapter
113
- server.app = Thin::Stats::Adapter.new(server.app, options[:stats]) if options[:stats]
114
-
115
- # Register restart procedure
116
- server.on_restart { Thin::Command.run(:start, options) }
117
-
118
- server.start
119
- end
120
- end
121
-
122
- def stop(options)
123
- load_options_from_config_file! options
124
-
125
- if cluster?(options)
126
- Thin::Cluster.new(options).stop
127
- else
128
- Thin::Server.kill(options[:pid], options[:timeout])
129
- end
130
- end
131
-
132
- def restart(options)
133
- load_options_from_config_file! options
134
-
135
- if cluster?(options)
136
- Thin::Cluster.new(options).restart
137
- else
138
- Thin::Server.restart(options[:pid])
139
- end
140
- end
141
-
142
- def config(options)
143
- config_file = options.delete(:config) || abort('config option required')
144
-
145
- # Stringify keys
146
- options.keys.each { |o| options[o.to_s] = options.delete(o) }
147
-
148
- File.open(config_file, 'w') { |f| f << options.to_yaml }
149
- puts "Wrote configuration to #{config_file}"
150
- end
151
-
152
-
153
- # == Runs the command
154
-
155
- opts.parse! ARGV
156
- command = ARGV[0]
157
-
158
- Dir.chdir(options[:chdir])
159
-
160
- if COMMANDS.include?(command)
161
- send(command, options)
162
- elsif command.nil?
163
- puts "Command required"
164
- puts opts
165
- exit 1
166
- else
167
- abort "Invalid command : #{command}"
168
- end
6
+ Thin::Runner.new(ARGV).run!
data/example/config.ru CHANGED
@@ -1,5 +1,8 @@
1
1
  # Run with: rackup -s thin
2
- # Then browse to http://localhost:9292
2
+ # then browse to http://localhost:9292
3
+ # Or with: thin start -r config.ru
4
+ # then browse to http://localhost:3000
5
+ #
3
6
  # Check Rack::Builder doc for more details on this file format:
4
7
  # http://rack.rubyforge.org/doc/classes/Rack/Builder.html
5
8
 
data/example/ramaze.ru ADDED
@@ -0,0 +1,12 @@
1
+ # Ramaze Rackup config file.
2
+ # by tmm1
3
+ # Use with --rackup option:
4
+ #
5
+ # thin start -r ramaze.ru
6
+ #
7
+ require 'start'
8
+
9
+ Ramaze.trait[:essentials].delete Ramaze::Adapter
10
+ Ramaze.start :force => true
11
+
12
+ run Ramaze::Adapter::Base
data/example/thin.god CHANGED
@@ -1,72 +1,76 @@
1
- # WARNING: this config file has not been tested yet.
2
- # If you know God better then I, feel free to tweak it and send it to me.
3
- # Thanks!
4
- # -- Marc
1
+ # == God config file
2
+ # http://god.rubyforge.org/
3
+ # Author: Gump
4
+ #
5
+ # Config file for god that configures watches for each instance of a thin server for
6
+ # each thin configuration file found in /etc/thin.
7
+ # In order to get it working on Ubuntu, I had to make a change to god as noted at
8
+ # the following blog:
9
+ # http://blog.alexgirard.com/2007/10/25/ruby-one-line-to-save-god/
10
+ #
11
+ require 'yaml'
5
12
 
6
- RAILS_ROOT = "/Users/marc/projects/refactormycode"
13
+ config_path = "/etc/thin"
7
14
 
8
- God.watch do |w|
9
- w.name = "thin-3000"
10
- w.group = 'thins'
11
- w.interval = 5.seconds # default
12
- w.start = "thin start -c #{RAILS_ROOT} -P #{RAILS_ROOT}/tmp/pids/thin.3000.pid -p 3000 -d"
13
- w.stop = "thin stop -P #{RAILS_ROOT}/tmp/pids/thin.3000.pid"
14
- w.restart = "thin restart -P #{RAILS_ROOT}/tmp/pids/thin.3000.pid -p 3000"
15
- w.pid_file = File.join(RAILS_ROOT, "tmp/pids/thin.3000.pid")
16
-
17
- # clean pid files before start if necessary
18
- w.behavior(:clean_pid_file)
19
-
20
- # determine the state on startup
21
- w.transition(:init, { true => :up, false => :start }) do |on|
22
- on.condition(:process_running) do |c|
23
- c.running = true
24
- end
25
- end
26
-
27
- # determine when process has finished starting
28
- w.transition([:start, :restart], :up) do |on|
29
- on.condition(:process_running) do |c|
30
- c.running = true
31
- end
32
-
33
- # failsafe
34
- on.condition(:tries) do |c|
35
- c.times = 5
36
- c.transition = :start
37
- end
38
- end
15
+ Dir[config_path + "/*.yml"].each do |file|
16
+ config = YAML.load_file(file)
17
+ num_servers = config["servers"] ||= 1
39
18
 
40
- # start if process is not running
41
- w.transition(:up, :start) do |on|
42
- on.condition(:process_exits)
43
- end
44
-
45
- # restart if memory or cpu is too high
46
- w.transition(:up, :restart) do |on|
47
- on.condition(:memory_usage) do |c|
48
- c.interval = 20
49
- c.above = 50.megabytes
50
- c.times = [3, 5]
51
- end
52
-
53
- on.condition(:cpu_usage) do |c|
54
- c.interval = 10
55
- c.above = 10.percent
56
- c.times = [3, 5]
57
- end
58
- end
59
-
60
- # lifecycle
61
- w.lifecycle do |on|
62
- on.condition(:flapping) do |c|
63
- c.to_state = [:start, :restart]
64
- c.times = 5
65
- c.within = 5.minute
66
- c.transition = :unmonitored
67
- c.retry_in = 10.minutes
68
- c.retry_times = 5
69
- c.retry_within = 2.hours
19
+ for i in 0...num_servers
20
+ God.watch do |w|
21
+ w.group = "thin-" + File.basename(file, ".yml")
22
+ w.name = w.group + "-#{i}"
23
+
24
+ w.interval = 30.seconds
25
+
26
+ w.uid = config["user"]
27
+ w.gid = config["group"]
28
+
29
+ w.start = "thin start -C #{file} -o #{i}"
30
+ w.start_grace = 10.seconds
31
+
32
+ w.stop = "thin stop -C #{file} -o #{i}"
33
+ w.stop_grace = 10.seconds
34
+
35
+ w.restart = "thin restart -C #{file} -o #{i}"
36
+
37
+ pid_path = config["chdir"] + "/" + config["pid"]
38
+ ext = File.extname(pid_path)
39
+
40
+ w.pid_file = pid_path.gsub(/#{ext}$/, ".#{i}#{ext}")
41
+
42
+ w.behavior(:clean_pid_file)
43
+
44
+ w.start_if do |start|
45
+ start.condition(:process_running) do |c|
46
+ c.interval = 5.seconds
47
+ c.running = false
48
+ end
49
+ end
50
+
51
+ w.restart_if do |restart|
52
+ restart.condition(:memory_usage) do |c|
53
+ c.above = 150.megabytes
54
+ c.times = [3,5] # 3 out of 5 intervals
55
+ end
56
+
57
+ restart.condition(:cpu_usage) do |c|
58
+ c.above = 50.percent
59
+ c.times = 5
60
+ end
61
+ end
62
+
63
+ w.lifecycle do |on|
64
+ on.condition(:flapping) do |c|
65
+ c.to_state = [:start, :restart]
66
+ c.times = 5
67
+ c.within = 5.minutes
68
+ c.transition = :unmonitored
69
+ c.retry_in = 10.minutes
70
+ c.retry_times = 5
71
+ c.retry_within = 2.hours
72
+ end
73
+ end
70
74
  end
71
75
  end
72
76
  end
data/example/vlad.rake ADDED
@@ -0,0 +1,61 @@
1
+ # $GEM_HOME/gems/vlad-1.2.0/lib/vlad/thin.rb
2
+ # Thin tasks for Vlad the Deployer
3
+ # By cnantais
4
+ require 'vlad'
5
+
6
+ namespace :vlad do
7
+ ##
8
+ # Thin app server
9
+
10
+ set :thin_address, "127.0.0.1"
11
+ set :thin_command, 'thin'
12
+ set(:thin_conf) { "#{shared_path}/thin_cluster.conf" }
13
+ set :thin_environment, "production"
14
+ set :thin_group, nil
15
+ set :thin_log_file, nil
16
+ set :thin_pid_file, nil
17
+ set :thin_port, nil
18
+ set :thin_socket, "/tmp/thin.sock"
19
+ set :thin_prefix, nil
20
+ set :thin_servers, 2
21
+ set :thin_user, nil
22
+
23
+ desc "Prepares application servers for deployment. thin
24
+ configuration is set via the thin_* variables.".cleanup
25
+
26
+ remote_task :setup_app, :roles => :app do
27
+ cmd = [
28
+ "#{thin_command} config",
29
+ "-s #{thin_servers}",
30
+ "-S #{thin_socket}",
31
+ "-e #{thin_environment}",
32
+ "-a #{thin_address}",
33
+ "-c #{current_path}",
34
+ "-C #{thin_conf}",
35
+ ("-P #{thin_pid_file}" if thin_pid_file),
36
+ ("-l #{thin_log_file}" if thin_log_file),
37
+ ("--user #{thin_user}" if thin_user),
38
+ ("--group #{thin_group}" if thin_group),
39
+ ("--prefix #{thin_prefix}" if thin_prefix),
40
+ ("-p #{thin_port}" if thin_port),
41
+ ].compact.join ' '
42
+
43
+ run cmd
44
+ end
45
+
46
+ def thin(cmd) # :nodoc:
47
+ "#{thin_command} #{cmd} -C #{thin_conf}"
48
+ end
49
+
50
+ desc "Restart the app servers"
51
+
52
+ remote_task :start_app, :roles => :app do
53
+ run thin("restart -s #{thin_servers}")
54
+ end
55
+
56
+ desc "Stop the app servers"
57
+
58
+ remote_task :stop_app, :roles => :app do
59
+ run thin("stop -s #{thin_servers}")
60
+ end
61
+ end
@@ -1,6 +1,3 @@
1
- # This as been submitted to Rack as a patch, tested and everything.
2
- # Bug Christian Neukirchen at chneukirchen@gmail.com to apply the patch!
3
-
4
1
  require 'cgi'
5
2
 
6
3
  # Adapter to run a Rails app with any supported Rack handler.
@@ -1,12 +1,17 @@
1
1
  module Rack
2
2
  module Handler
3
+ # Rack Handler stricly to be able to use Thin through the rackup command.
4
+ # To do so, simply require 'thin' in your Rack config file and run like this
5
+ #
6
+ # rackup --server thin
7
+ #
3
8
  class Thin
4
9
  def self.run(app, options={})
5
10
  server = ::Thin::Server.new(options[:Host] || '0.0.0.0',
6
11
  options[:Port] || 8080,
7
12
  app)
8
13
  yield server if block_given?
9
- server.start!
14
+ server.start
10
15
  end
11
16
  end
12
17
  end