thin 1.8.2 → 2.0.0.pre

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 (108) hide show
  1. data/.gitignore +9 -0
  2. data/CHANGELOG +29 -116
  3. data/Gemfile +8 -0
  4. data/README.md +44 -78
  5. data/Rakefile +28 -18
  6. data/bin/thin +4 -4
  7. data/examples/async.ru +21 -0
  8. data/examples/thin.conf.rb +39 -0
  9. data/lib/thin/async.rb +108 -0
  10. data/lib/thin/backends/prefork.rb +44 -0
  11. data/lib/thin/backends/single_process.rb +28 -0
  12. data/lib/thin/chunked_body.rb +28 -0
  13. data/lib/thin/configurator.rb +118 -0
  14. data/lib/thin/connection.rb +246 -172
  15. data/lib/thin/listener.rb +114 -0
  16. data/lib/thin/request.rb +94 -76
  17. data/lib/thin/response.rb +112 -45
  18. data/lib/thin/runner.rb +134 -197
  19. data/lib/thin/server.rb +203 -252
  20. data/lib/thin/system.rb +49 -0
  21. data/lib/thin/version.rb +12 -27
  22. data/lib/thin.rb +2 -44
  23. data/man/index.txt +3 -0
  24. data/man/thin-conf.5.ronn +121 -0
  25. data/man/thin.1.ronn +105 -0
  26. data/site/.gitignore +2 -0
  27. data/site/README.md +21 -0
  28. data/site/Rakefile +20 -0
  29. data/site/config.ru +4 -0
  30. data/site/public/images/grid.png +0 -0
  31. data/site/public/javascripts/dd_belatedpng.js +13 -0
  32. data/site/public/javascripts/modernizr-1.6.min.js +30 -0
  33. data/site/public/man/thin-conf.5.html +220 -0
  34. data/site/public/man/thin.1.html +177 -0
  35. data/site/site/assets/javascripts/main.coffee +2 -0
  36. data/site/site/assets/stylesheets/_config.scss +55 -0
  37. data/site/site/assets/stylesheets/main.scss +24 -0
  38. data/site/site/helpers.rb +17 -0
  39. data/site/site/layouts/base.erb +55 -0
  40. data/site/site/layouts/default.erb +17 -0
  41. data/site/site/pages/about.md +5 -0
  42. data/site/site/pages/index.erb +10 -0
  43. data/site/site/partials/.gitkeep +0 -0
  44. data/test/fixtures/big.txt +1 -0
  45. data/test/fixtures/small.txt +1 -0
  46. data/test/fixtures/thin.conf.rb +15 -0
  47. data/test/integration/async_test.rb +35 -0
  48. data/test/integration/big_request_test.rb +30 -0
  49. data/test/integration/config.ru +57 -0
  50. data/test/integration/daemonize_test.rb +26 -0
  51. data/test/integration/env_test.rb +44 -0
  52. data/test/integration/error_test.rb +37 -0
  53. data/test/integration/file_sending_test.rb +24 -0
  54. data/test/integration/keep_alive_test.rb +35 -0
  55. data/test/integration/robustness_test.rb +37 -0
  56. data/test/integration/single_process_test.rb +15 -0
  57. data/test/integration/socket_family_test.rb +38 -0
  58. data/test/integration/worker_test.rb +22 -0
  59. data/test/test_helper.rb +195 -0
  60. data/test/unit/configurator_test.rb +43 -0
  61. data/test/unit/connection_test.rb +94 -0
  62. data/test/unit/listener_test.rb +74 -0
  63. data/test/unit/request_test.rb +74 -0
  64. data/test/unit/response_test.rb +90 -0
  65. data/test/unit/server_test.rb +29 -0
  66. data/test/unit/system_test.rb +17 -0
  67. data/thin.gemspec +26 -0
  68. data/v2.todo +21 -0
  69. metadata +138 -93
  70. checksums.yaml +0 -7
  71. data/example/adapter.rb +0 -32
  72. data/example/async_app.ru +0 -126
  73. data/example/async_chat.ru +0 -247
  74. data/example/async_tailer.ru +0 -100
  75. data/example/config.ru +0 -22
  76. data/example/monit_sockets +0 -20
  77. data/example/monit_unixsock +0 -20
  78. data/example/myapp.rb +0 -1
  79. data/example/ramaze.ru +0 -12
  80. data/example/thin.god +0 -80
  81. data/example/thin_solaris_smf.erb +0 -36
  82. data/example/thin_solaris_smf.readme.txt +0 -150
  83. data/example/vlad.rake +0 -72
  84. data/ext/thin_parser/common.rl +0 -59
  85. data/ext/thin_parser/ext_help.h +0 -14
  86. data/ext/thin_parser/extconf.rb +0 -6
  87. data/ext/thin_parser/parser.c +0 -1447
  88. data/ext/thin_parser/parser.h +0 -49
  89. data/ext/thin_parser/parser.rl +0 -152
  90. data/ext/thin_parser/thin.c +0 -435
  91. data/lib/rack/adapter/loader.rb +0 -75
  92. data/lib/rack/adapter/rails.rb +0 -178
  93. data/lib/rack/handler/thin.rb +0 -38
  94. data/lib/thin/backends/base.rb +0 -169
  95. data/lib/thin/backends/swiftiply_client.rb +0 -66
  96. data/lib/thin/backends/tcp_server.rb +0 -34
  97. data/lib/thin/backends/unix_server.rb +0 -56
  98. data/lib/thin/command.rb +0 -53
  99. data/lib/thin/controllers/cluster.rb +0 -178
  100. data/lib/thin/controllers/controller.rb +0 -189
  101. data/lib/thin/controllers/service.rb +0 -76
  102. data/lib/thin/controllers/service.sh.erb +0 -39
  103. data/lib/thin/daemonizing.rb +0 -199
  104. data/lib/thin/headers.rb +0 -47
  105. data/lib/thin/logging.rb +0 -174
  106. data/lib/thin/stats.html.erb +0 -216
  107. data/lib/thin/stats.rb +0 -52
  108. data/lib/thin/statuses.rb +0 -48
data/lib/thin/runner.rb CHANGED
@@ -1,238 +1,175 @@
1
- require 'logger'
2
- require 'optparse'
3
- require 'yaml'
4
- require 'erb'
1
+ require "optparse"
2
+ require "rack"
3
+
4
+ require "thin/configurator"
5
5
 
6
6
  module Thin
7
- # CLI runner.
8
- # Parse options and send command to the correct Controller.
7
+ # Command line runner. Mimic Rack's +rackup+.
9
8
  class Runner
10
- COMMANDS = %w(start stop restart config)
11
- LINUX_ONLY_COMMANDS = %w(install)
9
+ class OptionsParser
10
+ def parse!(args)
11
+ options = {}
12
+ opt_parser = OptionParser.new("", 24, ' ') do |opts|
13
+ opts.banner = "Usage: thin [ruby options] [thin options] [rackup config]"
12
14
 
13
- # Commands that wont load options from the config file
14
- CONFIGLESS_COMMANDS = %w(config install)
15
+ opts.separator ""
16
+ opts.separator "Ruby options:"
17
+
18
+ lineno = 1
19
+ opts.on("-e", "--eval LINE", "evaluate a LINE of code") { |line|
20
+ eval line, TOPLEVEL_BINDING, "-e", lineno
21
+ lineno += 1
22
+ }
23
+
24
+ opts.on("-d", "--debug", "set debugging flags (set $DEBUG to true)") {
25
+ $DEBUG = true
26
+ }
27
+ opts.on("-w", "--warn", "turn warnings on for your script") {
28
+ $-w = true
29
+ }
30
+
31
+ opts.on("-I", "--include PATH",
32
+ "specify $LOAD_PATH (may be used more than once)") { |path|
33
+ $LOAD_PATH.unshift *path.split(":")
34
+ }
35
+
36
+ opts.on("-r", "--require LIBRARY",
37
+ "require the library, before executing your script") { |library|
38
+ require library
39
+ }
15
40
 
16
- # Parsed options
17
- attr_accessor :options
41
+ opts.separator ""
42
+ opts.separator "Thin options:"
18
43
 
19
- # Name of the command to be runned.
20
- attr_accessor :command
44
+ opts.on("-o", "--host HOST", "bind to HOST") { |host|
45
+ options[:host] = host
46
+ }
21
47
 
22
- # Arguments to be passed to the command.
23
- attr_accessor :arguments
48
+ opts.on("-p", "--port PORT", "use PORT (default: 9292)") { |port|
49
+ options[:port] = port
50
+ }
24
51
 
25
- # Return all available commands
26
- def self.commands
27
- commands = COMMANDS
28
- commands += LINUX_ONLY_COMMANDS if Thin.linux?
29
- commands
30
- end
52
+ opts.on("-E", "--env ENVIRONMENT", "use ENVIRONMENT for defaults (default: development)") { |e|
53
+ options[:environment] = e
54
+ }
31
55
 
32
- def initialize(argv)
33
- @argv = argv
34
-
35
- # Default options values
36
- @options = {
37
- :chdir => Dir.pwd,
38
- :environment => ENV['RACK_ENV'] || 'development',
39
- :address => '0.0.0.0',
40
- :port => Server::DEFAULT_PORT,
41
- :timeout => Server::DEFAULT_TIMEOUT,
42
- :log => File.join(Dir.pwd, 'log/thin.log'),
43
- :pid => 'tmp/pids/thin.pid',
44
- :max_conns => Server::DEFAULT_MAXIMUM_CONNECTIONS,
45
- :max_persistent_conns => Server::DEFAULT_MAXIMUM_PERSISTENT_CONNECTIONS,
46
- :require => [],
47
- :wait => Controllers::Cluster::DEFAULT_WAIT_TIME,
48
- :threadpool_size => 20
49
- }
56
+ opts.on("-D", "--daemonize", "run daemonized in the background") { |d|
57
+ options[:daemonize] = d ? true : false
58
+ }
50
59
 
51
- parse!
52
- end
60
+ opts.on("-P", "--pid FILE", "file to store PID (default: thin.pid)") { |f|
61
+ options[:pid] = ::File.expand_path(f)
62
+ }
53
63
 
54
- def parser
55
- # NOTE: If you add an option here make sure the key in the +options+ hash is the
56
- # same as the name of the command line option.
57
- # +option+ keys are used to build the command line to launch other processes,
58
- # see <tt>lib/thin/command.rb</tt>.
59
- @parser ||= OptionParser.new do |opts|
60
- opts.banner = "Usage: thin [options] #{self.class.commands.join('|')}"
61
-
62
- opts.separator ""
63
- opts.separator "Server options:"
64
-
65
- opts.on("-a", "--address HOST", "bind to HOST address " +
66
- "(default: #{@options[:address]})") { |host| @options[:address] = host }
67
- opts.on("-p", "--port PORT", "use PORT (default: #{@options[:port]})") { |port| @options[:port] = port.to_i }
68
- opts.on("-S", "--socket FILE", "bind to unix domain socket") { |file| @options[:socket] = file }
69
- opts.on("-y", "--swiftiply [KEY]", "Run using swiftiply") { |key| @options[:swiftiply] = key }
70
- opts.on("-A", "--adapter NAME", "Rack adapter to use (default: autodetect)",
71
- "(#{Rack::ADAPTERS.map{|(a,b)|a}.join(', ')})") { |name| @options[:adapter] = name }
72
- opts.on("-R", "--rackup FILE", "Load a Rack config file instead of " +
73
- "Rack adapter") { |file| @options[:rackup] = file }
74
- opts.on("-c", "--chdir DIR", "Change to dir before starting") { |dir| @options[:chdir] = File.expand_path(dir) }
75
- opts.on( "--stats PATH", "Mount the Stats adapter under PATH") { |path| @options[:stats] = path }
76
-
77
- opts.separator ""
78
- opts.separator "SSL options:"
79
-
80
- opts.on( "--ssl", "Enables SSL") { @options[:ssl] = true }
81
- opts.on( "--ssl-key-file PATH", "Path to private key") { |path| @options[:ssl_key_file] = path }
82
- opts.on( "--ssl-cert-file PATH", "Path to certificate") { |path| @options[:ssl_cert_file] = path }
83
- opts.on( "--ssl-disable-verify", "Disables (optional) client cert requests") { @options[:ssl_disable_verify] = true }
84
- opts.on( "--ssl-version VERSION", "TLSv1, TLSv1_1, TLSv1_2") { |version| @options[:ssl_version] = version }
85
- opts.on( "--ssl-cipher-list STRING", "Example: HIGH:!ADH:!RC4:-MEDIUM:-LOW:-EXP:-CAMELLIA") { |cipher| @options[:ssl_cipher_list] = cipher }
86
-
87
- opts.separator ""
88
- opts.separator "Adapter options:"
89
- opts.on("-e", "--environment ENV", "Framework environment " +
90
- "(default: #{@options[:environment]})") { |env| @options[:environment] = env }
91
- opts.on( "--prefix PATH", "Mount the app under PATH (start with /)") { |path| @options[:prefix] = path }
92
-
93
- unless Thin.win? # Daemonizing not supported on Windows
94
- opts.separator ""
95
- opts.separator "Daemon options:"
64
+ opts.on("-l", "--log FILE", "file to log to (default: stdout)") { |f|
65
+ options[:log] = ::File.expand_path(f)
66
+ }
96
67
 
97
- opts.on("-d", "--daemonize", "Run daemonized in the background") { @options[:daemonize] = true }
98
- opts.on("-l", "--log FILE", "File to redirect output " +
99
- "(default: #{@options[:log]})") { |file| @options[:log] = file }
100
- opts.on("-P", "--pid FILE", "File to store PID " +
101
- "(default: #{@options[:pid]})") { |file| @options[:pid] = file }
102
- opts.on("-u", "--user NAME", "User to run daemon as (use with -g)") { |user| @options[:user] = user }
103
- opts.on("-g", "--group NAME", "Group to run daemon as (use with -u)") { |group| @options[:group] = group }
104
- opts.on( "--tag NAME", "Additional text to display in process listing") { |tag| @options[:tag] = tag }
68
+ opts.on("-c", "--config FILE", "Thin configuration file.") { |file|
69
+ options[:thin_config] = file
70
+ }
105
71
 
106
72
  opts.separator ""
107
- opts.separator "Cluster options:"
108
-
109
- opts.on("-s", "--servers NUM", "Number of servers to start") { |num| @options[:servers] = num.to_i }
110
- opts.on("-o", "--only NUM", "Send command to only one server of the cluster") { |only| @options[:only] = only.to_i }
111
- opts.on("-C", "--config FILE", "Load options from config file") { |file| @options[:config] = file }
112
- opts.on( "--all [DIR]", "Send command to each config files in DIR") { |dir| @options[:all] = dir } if Thin.linux?
113
- opts.on("-O", "--onebyone", "Restart the cluster one by one (only works with restart command)") { @options[:onebyone] = true }
114
- opts.on("-w", "--wait NUM", "Maximum wait time for server to be started in seconds (use with -O)") { |time| @options[:wait] = time.to_i }
73
+ opts.separator "Common options:"
74
+
75
+ opts.on_tail("-h", "-?", "--help", "Show this message") do
76
+ puts opts
77
+ exit
78
+ end
79
+
80
+ opts.on_tail("--version", "Show version") do
81
+ puts Thin::SERVER
82
+ exit
83
+ end
115
84
  end
116
85
 
117
- opts.separator ""
118
- opts.separator "Tuning options:"
119
-
120
- opts.on("-b", "--backend CLASS", "Backend to use, full classname") { |name| @options[:backend] = name }
121
- opts.on("-t", "--timeout SEC", "Request or command timeout in sec " +
122
- "(default: #{@options[:timeout]})") { |sec| @options[:timeout] = sec.to_i }
123
- opts.on("-f", "--force", "Force the execution of the command") { @options[:force] = true }
124
- opts.on( "--max-conns NUM", "Maximum number of open file descriptors " +
125
- "(default: #{@options[:max_conns]})",
126
- "Might require sudo to set higher than 1024") { |num| @options[:max_conns] = num.to_i } unless Thin.win?
127
- opts.on( "--max-persistent-conns NUM",
128
- "Maximum number of persistent connections",
129
- "(default: #{@options[:max_persistent_conns]})") { |num| @options[:max_persistent_conns] = num.to_i }
130
- opts.on( "--threaded", "Call the Rack application in threads " +
131
- "[experimental]") { @options[:threaded] = true }
132
- opts.on( "--threadpool-size NUM", "Sets the size of the EventMachine threadpool.",
133
- "(default: #{@options[:threadpool_size]})") { |num| @options[:threadpool_size] = num.to_i }
134
- opts.on( "--no-epoll", "Disable the use of epoll") { @options[:no_epoll] = true } if Thin.linux?
135
-
136
- opts.separator ""
137
- opts.separator "Common options:"
138
-
139
- opts.on_tail("-r", "--require FILE", "require the library") { |file| @options[:require] << file }
140
- opts.on_tail("-q", "--quiet", "Silence all logging") { @options[:quiet] = true }
141
- opts.on_tail("-D", "--debug", "Enable debug logging") { @options[:debug] = true }
142
- opts.on_tail("-V", "--trace", "Set tracing on (log raw request/response)") { @options[:trace] = true }
143
- opts.on_tail("-h", "--help", "Show this message") { puts opts; exit }
144
- opts.on_tail('-v', '--version', "Show version") { puts Thin::SERVER; exit }
145
- end
146
- end
86
+ begin
87
+ opt_parser.parse! args
88
+ rescue OptionParser::InvalidOption => e
89
+ warn e.message
90
+ abort opt_parser.to_s
91
+ end
147
92
 
148
- # Parse the options.
149
- def parse!
150
- parser.parse! @argv
151
- @command = @argv.shift
152
- @arguments = @argv
153
- end
93
+ options[:config] = args.last if args.last
154
94
 
155
- # Parse the current shell arguments and run the command.
156
- # Exits on error.
157
- def run!
158
- if self.class.commands.include?(@command)
159
- run_command
160
- elsif @command.nil?
161
- puts "Command required"
162
- puts @parser
163
- exit 1
164
- else
165
- abort "Unknown command: #{@command}. Use one of #{self.class.commands.join(', ')}"
95
+ options
166
96
  end
167
97
  end
168
98
 
169
- # Send the command to the controller: single instance or cluster.
170
- def run_command
171
- load_options_from_config_file! unless CONFIGLESS_COMMANDS.include?(@command)
172
-
173
- # PROGRAM_NAME is relative to the current directory, so make sure
174
- # we store and expand it before changing directory.
175
- Command.script = File.expand_path($PROGRAM_NAME)
99
+ def default_options
100
+ {
101
+ :environment => ENV['RACK_ENV'] || "development",
102
+ :port => 9292,
103
+ :config => "config.ru"
104
+ }
105
+ end
176
106
 
177
- # Change the current directory ASAP so that all relative paths are
178
- # relative to this one.
179
- Dir.chdir(@options[:chdir]) unless CONFIGLESS_COMMANDS.include?(@command)
107
+ def run(args)
108
+ options = default_options.dup
180
109
 
181
- @options[:require].each { |r| ruby_require r }
110
+ parser = OptionsParser.new
111
+ options.update parser.parse!(args)
182
112
 
183
- # Setup the logger
184
- if @options[:quiet]
185
- Logging.silent = true
186
- else
187
- Logging.level = Logger::DEBUG if @options[:debug]
113
+ # Parse in file options like rackup
114
+ shebang = File.open(options[:config]) { |f| f.readline }
115
+ if shebang[/^#\\(.*)/]
116
+ options.update parser.parse! $1.split(/\s+/)
188
117
  end
189
118
 
190
- if @options[:trace]
191
- # Trace raw requests/responses
192
- Logging.trace_logger = Logging.logger
119
+ ENV["RACK_ENV"] = options[:environment]
120
+ options[:config] = ::File.expand_path(options[:config])
121
+
122
+ # Configure the server
123
+ server = Server.new do
124
+ # This is passed as a block so it can be loaded inside workers if preload_app disabled.
125
+ build_app(options[:config], options[:environment])
193
126
  end
194
127
 
195
- controller = case
196
- when cluster? then Controllers::Cluster.new(@options)
197
- when service? then Controllers::Service.new(@options)
198
- else Controllers::Controller.new(@options)
128
+ if options[:thin_config]
129
+ Configurator.load(options[:thin_config]).apply(server)
199
130
  end
200
131
 
201
- if controller.respond_to?(@command)
202
- begin
203
- controller.send(@command, *@arguments)
204
- rescue RunnerError => e
205
- abort e.message
206
- end
207
- else
208
- abort "Invalid options for command: #{@command}"
132
+ # If no listeners yet, use the one from the options
133
+ if !options.has_key?(:thin_config) || server.listeners.empty?
134
+ server.listen [options[:host], options[:port]].compact.join(":")
209
135
  end
210
- end
211
136
 
212
- # +true+ if we're controlling a cluster.
213
- def cluster?
214
- @options[:only] || @options[:servers] || @options[:config]
137
+ server.pid_path = options[:pid] if options[:pid]
138
+ server.log_path = options[:log] if options[:log]
139
+ server.worker_processes = options[:workers] if options[:workers]
140
+ server.timeout = options[:timeout] if options[:timeout]
141
+
142
+ # Start the server
143
+ server.start(options[:daemonize])
215
144
  end
216
145
 
217
- # +true+ if we're acting a as system service.
218
- def service?
219
- @options.has_key?(:all) || @command == 'install'
146
+ def self.run(args)
147
+ new.run(args)
220
148
  end
221
149
 
222
150
  private
223
- def load_options_from_config_file!
224
- if file = @options.delete(:config)
225
- YAML.load(ERB.new(File.read(file)).result).each { |key, value| @options[key.to_sym] = value }
226
- end
151
+ def build_app(config, environment)
152
+ inner_app = Rack::Builder.parse_file(config).first
153
+
154
+ Rack::Builder.new do
155
+ case environment
156
+ when "development"
157
+ use Rack::ContentLength
158
+ use Rack::Chunked
159
+ use Rack::CommonLogger
160
+ use Rack::ShowExceptions
161
+ use Rack::Lint
162
+
163
+ when "deployment"
164
+ use Rack::ContentLength
165
+ use Rack::Chunked
166
+ use Rack::CommonLogger
167
+
168
+ end
169
+
170
+ run inner_app
171
+ end.to_app
227
172
  end
228
173
 
229
- def ruby_require(file)
230
- if File.extname(file) == '.ru'
231
- warn 'WARNING: Use the -R option to load a Rack config file'
232
- @options[:rackup] = file
233
- else
234
- require file
235
- end
236
- end
237
174
  end
238
175
  end