wendell-puma 2.9.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (88) hide show
  1. checksums.yaml +7 -0
  2. data/COPYING +55 -0
  3. data/DEPLOYMENT.md +92 -0
  4. data/Gemfile +17 -0
  5. data/History.txt +588 -0
  6. data/LICENSE +26 -0
  7. data/Manifest.txt +68 -0
  8. data/README.md +251 -0
  9. data/Rakefile +158 -0
  10. data/bin/puma +10 -0
  11. data/bin/puma-wild +31 -0
  12. data/bin/pumactl +12 -0
  13. data/docs/config.md +0 -0
  14. data/docs/nginx.md +80 -0
  15. data/docs/signals.md +43 -0
  16. data/ext/puma_http11/PumaHttp11Service.java +17 -0
  17. data/ext/puma_http11/ext_help.h +15 -0
  18. data/ext/puma_http11/extconf.rb +9 -0
  19. data/ext/puma_http11/http11_parser.c +1225 -0
  20. data/ext/puma_http11/http11_parser.h +64 -0
  21. data/ext/puma_http11/http11_parser.java.rl +161 -0
  22. data/ext/puma_http11/http11_parser.rl +146 -0
  23. data/ext/puma_http11/http11_parser_common.rl +54 -0
  24. data/ext/puma_http11/io_buffer.c +155 -0
  25. data/ext/puma_http11/mini_ssl.c +198 -0
  26. data/ext/puma_http11/org/jruby/puma/Http11.java +225 -0
  27. data/ext/puma_http11/org/jruby/puma/Http11Parser.java +488 -0
  28. data/ext/puma_http11/org/jruby/puma/MiniSSL.java +391 -0
  29. data/ext/puma_http11/puma_http11.c +491 -0
  30. data/lib/puma.rb +14 -0
  31. data/lib/puma/accept_nonblock.rb +23 -0
  32. data/lib/puma/app/status.rb +59 -0
  33. data/lib/puma/binder.rb +298 -0
  34. data/lib/puma/capistrano.rb +86 -0
  35. data/lib/puma/cli.rb +606 -0
  36. data/lib/puma/client.rb +289 -0
  37. data/lib/puma/cluster.rb +404 -0
  38. data/lib/puma/compat.rb +18 -0
  39. data/lib/puma/configuration.rb +377 -0
  40. data/lib/puma/const.rb +165 -0
  41. data/lib/puma/control_cli.rb +251 -0
  42. data/lib/puma/daemon_ext.rb +25 -0
  43. data/lib/puma/delegation.rb +11 -0
  44. data/lib/puma/detect.rb +4 -0
  45. data/lib/puma/events.rb +130 -0
  46. data/lib/puma/io_buffer.rb +7 -0
  47. data/lib/puma/java_io_buffer.rb +45 -0
  48. data/lib/puma/jruby_restart.rb +83 -0
  49. data/lib/puma/minissl.rb +187 -0
  50. data/lib/puma/null_io.rb +34 -0
  51. data/lib/puma/rack_default.rb +7 -0
  52. data/lib/puma/rack_patch.rb +45 -0
  53. data/lib/puma/reactor.rb +183 -0
  54. data/lib/puma/runner.rb +146 -0
  55. data/lib/puma/server.rb +801 -0
  56. data/lib/puma/single.rb +102 -0
  57. data/lib/puma/tcp_logger.rb +32 -0
  58. data/lib/puma/thread_pool.rb +185 -0
  59. data/lib/puma/util.rb +9 -0
  60. data/lib/rack/handler/puma.rb +66 -0
  61. data/test/test_app_status.rb +92 -0
  62. data/test/test_cli.rb +173 -0
  63. data/test/test_config.rb +26 -0
  64. data/test/test_http10.rb +27 -0
  65. data/test/test_http11.rb +144 -0
  66. data/test/test_integration.rb +165 -0
  67. data/test/test_iobuffer.rb +38 -0
  68. data/test/test_minissl.rb +29 -0
  69. data/test/test_null_io.rb +31 -0
  70. data/test/test_persistent.rb +238 -0
  71. data/test/test_puma_server.rb +288 -0
  72. data/test/test_puma_server_ssl.rb +137 -0
  73. data/test/test_rack_handler.rb +10 -0
  74. data/test/test_rack_server.rb +141 -0
  75. data/test/test_tcp_rack.rb +42 -0
  76. data/test/test_thread_pool.rb +156 -0
  77. data/test/test_unix_socket.rb +39 -0
  78. data/test/test_ws.rb +89 -0
  79. data/tools/jungle/README.md +9 -0
  80. data/tools/jungle/init.d/README.md +54 -0
  81. data/tools/jungle/init.d/puma +332 -0
  82. data/tools/jungle/init.d/run-puma +3 -0
  83. data/tools/jungle/upstart/README.md +61 -0
  84. data/tools/jungle/upstart/puma-manager.conf +31 -0
  85. data/tools/jungle/upstart/puma.conf +63 -0
  86. data/tools/trickletest.rb +45 -0
  87. data/wendell-puma.gemspec +55 -0
  88. metadata +225 -0
@@ -0,0 +1,606 @@
1
+ require 'optparse'
2
+ require 'uri'
3
+
4
+ require 'puma/server'
5
+ require 'puma/const'
6
+ require 'puma/configuration'
7
+ require 'puma/binder'
8
+ require 'puma/detect'
9
+ require 'puma/daemon_ext'
10
+ require 'puma/util'
11
+ require 'puma/single'
12
+ require 'puma/cluster'
13
+
14
+ require 'rack/commonlogger'
15
+ require 'rack/utils'
16
+
17
+ module Puma
18
+ # Handles invoke a Puma::Server in a command line style.
19
+ #
20
+ class CLI
21
+ # Create a new CLI object using +argv+ as the command line
22
+ # arguments.
23
+ #
24
+ # +stdout+ and +stderr+ can be set to IO-like objects which
25
+ # this object will report status on.
26
+ #
27
+ def initialize(argv, events=Events.stdio)
28
+ @debug = false
29
+ @argv = argv
30
+
31
+ @events = events
32
+
33
+ @status = nil
34
+ @runner = nil
35
+
36
+ @config = nil
37
+
38
+ ENV['NEWRELIC_DISPATCHER'] ||= "puma"
39
+
40
+ setup_options
41
+ generate_restart_data
42
+
43
+ @binder = Binder.new(@events)
44
+ @binder.import_from_env
45
+ end
46
+
47
+ # The Binder object containing the sockets bound to.
48
+ attr_reader :binder
49
+
50
+ # The Configuration object used.
51
+ attr_reader :config
52
+
53
+ # The Hash of options used to configure puma.
54
+ attr_reader :options
55
+
56
+ # The Events object used to output information.
57
+ attr_reader :events
58
+
59
+ # Delegate +log+ to +@events+
60
+ #
61
+ def log(str)
62
+ @events.log str
63
+ end
64
+
65
+ # Delegate +error+ to +@events+
66
+ #
67
+ def error(str)
68
+ @events.error str
69
+ end
70
+
71
+ def debug(str)
72
+ if @options[:debug]
73
+ @events.log "- #{str}"
74
+ end
75
+ end
76
+
77
+ def jruby?
78
+ IS_JRUBY
79
+ end
80
+
81
+ def windows?
82
+ RUBY_PLATFORM =~ /mswin32|ming32/
83
+ end
84
+
85
+ def unsupported(str, cond=true)
86
+ return unless cond
87
+ @events.error str
88
+ raise UnsupportedOption
89
+ end
90
+
91
+ # Build the OptionParser object to handle the available options.
92
+ #
93
+ def setup_options
94
+ @options = {
95
+ :min_threads => 0,
96
+ :max_threads => 16,
97
+ :quiet => false,
98
+ :debug => false,
99
+ :binds => [],
100
+ :workers => 0,
101
+ :daemon => false,
102
+ :before_worker_boot => [],
103
+ :after_worker_boot => []
104
+ }
105
+
106
+ @parser = OptionParser.new do |o|
107
+ o.on "-b", "--bind URI", "URI to bind to (tcp://, unix://, ssl://)" do |arg|
108
+ @options[:binds] << arg
109
+ end
110
+
111
+ o.on "-C", "--config PATH", "Load PATH as a config file" do |arg|
112
+ @options[:config_file] = arg
113
+ end
114
+
115
+ o.on "--control URL", "The bind url to use for the control server",
116
+ "Use 'auto' to use temp unix server" do |arg|
117
+ if arg
118
+ @options[:control_url] = arg
119
+ elsif jruby?
120
+ unsupported "No default url available on JRuby"
121
+ end
122
+ end
123
+
124
+ o.on "--control-token TOKEN",
125
+ "The token to use as authentication for the control server" do |arg|
126
+ @options[:control_auth_token] = arg
127
+ end
128
+
129
+ o.on "-d", "--daemon", "Daemonize the server into the background" do
130
+ @options[:daemon] = true
131
+ @options[:quiet] = true
132
+ end
133
+
134
+ o.on "--debug", "Log lowlevel debugging information" do
135
+ @options[:debug] = true
136
+ end
137
+
138
+ o.on "--dir DIR", "Change to DIR before starting" do |d|
139
+ @options[:directory] = d.to_s
140
+ @options[:worker_directory] = d.to_s
141
+ end
142
+
143
+ o.on "-e", "--environment ENVIRONMENT",
144
+ "The environment to run the Rack app on (default development)" do |arg|
145
+ @options[:environment] = arg
146
+ end
147
+
148
+ o.on "-I", "--include PATH", "Specify $LOAD_PATH directories" do |arg|
149
+ $LOAD_PATH.unshift(*arg.split(':'))
150
+ end
151
+
152
+ o.on "-p", "--port PORT", "Define the TCP port to bind to",
153
+ "Use -b for more advanced options" do |arg|
154
+ @options[:binds] << "tcp://#{Configuration::DefaultTCPHost}:#{arg}"
155
+ end
156
+
157
+ o.on "--pidfile PATH", "Use PATH as a pidfile" do |arg|
158
+ @options[:pidfile] = arg
159
+ end
160
+
161
+ o.on "--preload", "Preload the app. Cluster mode only" do
162
+ @options[:preload_app] = true
163
+ end
164
+
165
+ o.on "--prune-bundler", "Prune out the bundler env if possible" do
166
+ @options[:prune_bundler] = true
167
+ end
168
+
169
+ o.on "-q", "--quiet", "Quiet down the output" do
170
+ @options[:quiet] = true
171
+ end
172
+
173
+ o.on "-R", "--restart-cmd CMD",
174
+ "The puma command to run during a hot restart",
175
+ "Default: inferred" do |cmd|
176
+ @options[:restart_cmd] = cmd
177
+ end
178
+
179
+ o.on "-S", "--state PATH", "Where to store the state details" do |arg|
180
+ @options[:state] = arg
181
+ end
182
+
183
+ o.on '-t', '--threads INT', "min:max threads to use (default 0:16)" do |arg|
184
+ min, max = arg.split(":")
185
+ if max
186
+ @options[:min_threads] = min
187
+ @options[:max_threads] = max
188
+ else
189
+ @options[:min_threads] = 0
190
+ @options[:max_threads] = arg
191
+ end
192
+ end
193
+
194
+ o.on "--tcp-mode", "Run the app in raw TCP mode instead of HTTP mode" do
195
+ @options[:mode] = :tcp
196
+ end
197
+
198
+ o.on "-V", "--version", "Print the version information" do
199
+ puts "puma version #{Puma::Const::VERSION}"
200
+ exit 1
201
+ end
202
+
203
+ o.on "-w", "--workers COUNT",
204
+ "Activate cluster mode: How many worker processes to create" do |arg|
205
+ @options[:workers] = arg.to_i
206
+ end
207
+
208
+ o.on "--tag NAME", "Additional text to display in process listing" do |arg|
209
+ @options[:tag] = arg
210
+ end
211
+ end
212
+
213
+ @parser.banner = "puma <options> <rackup file>"
214
+
215
+ @parser.on_tail "-h", "--help", "Show help" do
216
+ log @parser
217
+ exit 1
218
+ end
219
+ end
220
+
221
+ def write_state
222
+ write_pid
223
+
224
+ require 'yaml'
225
+
226
+ if path = @options[:state]
227
+ state = { "pid" => Process.pid }
228
+
229
+ cfg = @config.dup
230
+
231
+ [ :logger, :before_worker_boot, :after_worker_boot, :on_restart ].each { |o| cfg.options.delete o }
232
+
233
+ state["config"] = cfg
234
+
235
+ File.open(path, "w") do |f|
236
+ f.write state.to_yaml
237
+ end
238
+ end
239
+ end
240
+
241
+ # If configured, write the pid of the current process out
242
+ # to a file.
243
+ #
244
+ def write_pid
245
+ if path = @options[:pidfile]
246
+ File.open(path, "w") do |f|
247
+ f.puts Process.pid
248
+ end
249
+
250
+ cur = Process.pid
251
+
252
+ at_exit do
253
+ if cur == Process.pid
254
+ delete_pidfile
255
+ end
256
+ end
257
+ end
258
+ end
259
+
260
+ def env
261
+ # Try the user option first, then the environment variable,
262
+ # finally default to development
263
+ @options[:environment] || ENV['RACK_ENV'] || 'development'
264
+ end
265
+
266
+ def set_rack_environment
267
+ @options[:environment] = env
268
+ ENV['RACK_ENV'] = env
269
+ end
270
+
271
+ def delete_pidfile
272
+ if path = @options[:pidfile]
273
+ File.unlink path if File.exist? path
274
+ end
275
+ end
276
+
277
+ def find_config
278
+ if cfg = @options[:config_file]
279
+ # Allow - to disable config finding
280
+ if cfg == "-"
281
+ @options[:config_file] = nil
282
+ return
283
+ end
284
+
285
+ return
286
+ end
287
+
288
+ pos = ["config/puma/#{env}.rb", "config/puma.rb"]
289
+ @options[:config_file] = pos.find { |f| File.exist? f }
290
+ end
291
+
292
+ # :nodoc:
293
+ def parse_options
294
+ @parser.parse! @argv
295
+
296
+ if @argv.last
297
+ @options[:rackup] = @argv.shift
298
+ end
299
+
300
+ find_config
301
+
302
+ @config = Puma::Configuration.new @options
303
+
304
+ # Advertise the Configuration
305
+ Puma.cli_config = @config
306
+
307
+ @config.load
308
+
309
+ if clustered?
310
+ unsupported "worker mode not supported on JRuby or Windows",
311
+ jruby? || windows?
312
+ end
313
+
314
+ if @options[:daemon] and windows?
315
+ unsupported "daemon mode not supported on Windows"
316
+ end
317
+ end
318
+
319
+ def clustered?
320
+ @options[:workers] > 0
321
+ end
322
+
323
+ def graceful_stop
324
+ @runner.stop_blocked
325
+ log "=== puma shutdown: #{Time.now} ==="
326
+ log "- Goodbye!"
327
+ end
328
+
329
+ def generate_restart_data
330
+ # Use the same trick as unicorn, namely favor PWD because
331
+ # it will contain an unresolved symlink, useful for when
332
+ # the pwd is /data/releases/current.
333
+ if dir = ENV['PWD']
334
+ s_env = File.stat(dir)
335
+ s_pwd = File.stat(Dir.pwd)
336
+
337
+ if s_env.ino == s_pwd.ino and s_env.dev == s_pwd.dev
338
+ @restart_dir = dir
339
+ @options[:worker_directory] = dir
340
+ end
341
+ end
342
+
343
+ @restart_dir ||= Dir.pwd
344
+
345
+ @original_argv = ARGV.dup
346
+
347
+ if defined? Rubinius::OS_ARGV
348
+ @restart_argv = Rubinius::OS_ARGV
349
+ else
350
+ require 'rubygems'
351
+
352
+ # if $0 is a file in the current directory, then restart
353
+ # it the same, otherwise add -S on there because it was
354
+ # picked up in PATH.
355
+ #
356
+ if File.exist?($0)
357
+ arg0 = [Gem.ruby, $0]
358
+ else
359
+ arg0 = [Gem.ruby, "-S", $0]
360
+ end
361
+
362
+ # Detect and reinject -Ilib from the command line
363
+ lib = File.expand_path "lib"
364
+ arg0[1,0] = ["-I", lib] if $:[0] == lib
365
+
366
+ if defined? Puma::WILD_ARGS
367
+ @restart_argv = arg0 + Puma::WILD_ARGS + ARGV
368
+ else
369
+ @restart_argv = arg0 + ARGV
370
+ end
371
+ end
372
+ end
373
+
374
+ def restart_args
375
+ if cmd = @options[:restart_cmd]
376
+ cmd.split(' ') + @original_argv
377
+ else
378
+ @restart_argv
379
+ end
380
+ end
381
+
382
+ def jruby_daemon_start
383
+ require 'puma/jruby_restart'
384
+ JRubyRestart.daemon_start(@restart_dir, restart_args)
385
+ end
386
+
387
+ def restart!
388
+ @options[:on_restart].each do |block|
389
+ block.call self
390
+ end
391
+
392
+ if jruby?
393
+ @binder.listeners.each_with_index do |(str,io),i|
394
+ io.close
395
+
396
+ # We have to unlink a unix socket path that's not being used
397
+ uri = URI.parse str
398
+ if uri.scheme == "unix"
399
+ path = "#{uri.host}#{uri.path}"
400
+ File.unlink path
401
+ end
402
+ end
403
+
404
+ require 'puma/jruby_restart'
405
+ JRubyRestart.chdir_exec(@restart_dir, restart_args)
406
+
407
+ elsif windows?
408
+ @binder.listeners.each_with_index do |(str,io),i|
409
+ io.close
410
+
411
+ # We have to unlink a unix socket path that's not being used
412
+ uri = URI.parse str
413
+ if uri.scheme == "unix"
414
+ path = "#{uri.host}#{uri.path}"
415
+ File.unlink path
416
+ end
417
+ end
418
+
419
+ argv = restart_args
420
+
421
+ Dir.chdir @restart_dir
422
+
423
+ argv += [redirects] unless RUBY_VERSION < '1.9'
424
+ Kernel.exec(*argv)
425
+
426
+ else
427
+ redirects = {:close_others => true}
428
+ @binder.listeners.each_with_index do |(l,io),i|
429
+ ENV["PUMA_INHERIT_#{i}"] = "#{io.to_i}:#{l}"
430
+ redirects[io.to_i] = io.to_i
431
+ end
432
+
433
+ argv = restart_args
434
+
435
+ Dir.chdir @restart_dir
436
+
437
+ argv += [redirects] unless RUBY_VERSION < '1.9'
438
+
439
+ Kernel.exec(*argv)
440
+ end
441
+ end
442
+
443
+ def prune_bundler?
444
+ @options[:prune_bundler] && clustered? && !@options[:preload_app]
445
+ end
446
+
447
+ # Parse the options, load the rackup, start the server and wait
448
+ # for it to finish.
449
+ #
450
+ def run
451
+ begin
452
+ parse_options
453
+ rescue UnsupportedOption
454
+ exit 1
455
+ end
456
+
457
+ if prune_bundler? && defined?(Bundler)
458
+ puma = Bundler.rubygems.loaded_specs("puma")
459
+
460
+ dirs = puma.require_paths.map { |x| File.join(puma.full_gem_path, x) }
461
+
462
+ puma_lib_dir = dirs.detect { |x| File.exist? File.join(x, "../bin/puma-wild") }
463
+
464
+ deps = puma.runtime_dependencies.map { |d|
465
+ spec = Bundler.rubygems.loaded_specs(d.name)
466
+ "#{d.name}:#{spec.version.to_s}"
467
+ }.join(",")
468
+
469
+ if puma_lib_dir
470
+ log "* Pruning Bundler environment"
471
+ Bundler.with_clean_env do
472
+
473
+ wild = File.expand_path(File.join(puma_lib_dir, "../bin/puma-wild"))
474
+
475
+ wild_loadpath = dirs.join(":")
476
+
477
+ args = [Gem.ruby] + [wild, "-I", wild_loadpath, deps] + @original_argv
478
+
479
+ Kernel.exec(*args)
480
+ end
481
+ end
482
+
483
+ log "! Unable to prune Bundler environment, continuing"
484
+ end
485
+
486
+ if dir = @options[:directory]
487
+ Dir.chdir dir
488
+ end
489
+
490
+ set_rack_environment
491
+
492
+ if clustered?
493
+ @events = PidEvents.new STDOUT, STDERR
494
+ @options[:logger] = @events
495
+
496
+ @runner = Cluster.new(self)
497
+ else
498
+ @runner = Single.new(self)
499
+ end
500
+
501
+ setup_signals
502
+ set_process_title
503
+
504
+ @status = :run
505
+
506
+ @runner.run
507
+
508
+ case @status
509
+ when :halt
510
+ log "* Stopping immediately!"
511
+ when :run, :stop
512
+ graceful_stop
513
+ when :restart
514
+ log "* Restarting..."
515
+ @runner.before_restart
516
+ restart!
517
+ when :exit
518
+ # nothing
519
+ end
520
+ end
521
+
522
+ def setup_signals
523
+ begin
524
+ Signal.trap "SIGUSR2" do
525
+ restart
526
+ end
527
+ rescue Exception
528
+ log "*** SIGUSR2 not implemented, signal based restart unavailable!"
529
+ end
530
+
531
+ begin
532
+ Signal.trap "SIGUSR1" do
533
+ phased_restart
534
+ end
535
+ rescue Exception
536
+ log "*** SIGUSR1 not implemented, signal based restart unavailable!"
537
+ end
538
+
539
+ begin
540
+ Signal.trap "SIGTERM" do
541
+ stop
542
+ end
543
+ rescue Exception
544
+ log "*** SIGTERM not implemented, signal based gracefully stopping unavailable!"
545
+ end
546
+
547
+ begin
548
+ Signal.trap "SIGHUP" do
549
+ redirect_io
550
+ end
551
+ rescue Exception
552
+ log "*** SIGHUP not implemented, signal based logs reopening unavailable!"
553
+ end
554
+
555
+ if jruby?
556
+ Signal.trap("INT") do
557
+ @status = :exit
558
+ graceful_stop
559
+ exit
560
+ end
561
+ end
562
+ end
563
+
564
+ def stop
565
+ @status = :stop
566
+ @runner.stop
567
+ end
568
+
569
+ def restart
570
+ @status = :restart
571
+ @runner.restart
572
+ end
573
+
574
+ def phased_restart
575
+ unless @runner.respond_to?(:phased_restart) and @runner.phased_restart
576
+ log "* phased-restart called but not available, restarting normally."
577
+ return restart
578
+ end
579
+ true
580
+ end
581
+
582
+ def redirect_io
583
+ @runner.redirect_io
584
+ end
585
+
586
+ def stats
587
+ @runner.stats
588
+ end
589
+
590
+ def halt
591
+ @status = :halt
592
+ @runner.halt
593
+ end
594
+
595
+ private
596
+ def title
597
+ buffer = "puma #{Puma::Const::VERSION} (#{@options[:binds].join(',')})"
598
+ buffer << " [#{@options[:tag]}]" if @options[:tag]
599
+ buffer
600
+ end
601
+
602
+ def set_process_title
603
+ Process.respond_to?(:setproctitle) ? Process.setproctitle(title) : $0 = title
604
+ end
605
+ end
606
+ end