wendell-puma 2.9.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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