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,102 @@
1
+ require 'puma/runner'
2
+
3
+ module Puma
4
+ class Single < Runner
5
+ def stats
6
+ b = @server.backlog
7
+ r = @server.running
8
+ %Q!{ "backlog": #{b}, "running": #{r} }!
9
+ end
10
+
11
+ def restart
12
+ @server.begin_restart
13
+ end
14
+
15
+ def stop
16
+ @server.stop false
17
+ end
18
+
19
+ def halt
20
+ @server.halt
21
+ end
22
+
23
+ def stop_blocked
24
+ log "- Gracefully stopping, waiting for requests to finish"
25
+ @control.stop(true) if @control
26
+ @server.stop(true)
27
+ end
28
+
29
+ def jruby_daemon?
30
+ daemon? and @cli.jruby?
31
+ end
32
+
33
+ def run
34
+ already_daemon = false
35
+
36
+ if jruby_daemon?
37
+ require 'puma/jruby_restart'
38
+
39
+ if JRubyRestart.daemon?
40
+ # load and bind before redirecting IO so errors show up on stdout/stderr
41
+ load_and_bind
42
+ end
43
+
44
+ already_daemon = JRubyRestart.daemon_init
45
+ end
46
+
47
+ output_header "single"
48
+
49
+ if jruby_daemon?
50
+ if already_daemon
51
+ JRubyRestart.perm_daemonize
52
+ else
53
+ pid = nil
54
+
55
+ Signal.trap "SIGUSR2" do
56
+ log "* Started new process #{pid} as daemon..."
57
+
58
+ # Must use exit! so we don't unwind and run the ensures
59
+ # that will be run by the new child (such as deleting the
60
+ # pidfile)
61
+ exit!(true)
62
+ end
63
+
64
+ Signal.trap "SIGCHLD" do
65
+ log "! Error starting new process as daemon, exitting"
66
+ exit 1
67
+ end
68
+
69
+ pid = @cli.jruby_daemon_start
70
+ sleep
71
+ end
72
+ else
73
+ load_and_bind
74
+
75
+ if daemon?
76
+ log "* Daemonizing..."
77
+ Process.daemon(true)
78
+ end
79
+ end
80
+
81
+ @cli.write_state
82
+
83
+ start_control
84
+
85
+ @server = server = start_server
86
+
87
+ unless @options[:daemon]
88
+ log "Use Ctrl-C to stop"
89
+ end
90
+
91
+ redirect_io
92
+
93
+ @cli.events.fire_on_booted!
94
+
95
+ begin
96
+ server.run.join
97
+ rescue Interrupt
98
+ # Swallow it
99
+ end
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,32 @@
1
+ module Puma
2
+ class TCPLogger
3
+ def initialize(logger, app, quiet=false)
4
+ @logger = logger
5
+ @app = app
6
+ @quiet = quiet
7
+ end
8
+
9
+ FORMAT = "%s - %s"
10
+
11
+ def log(who, str)
12
+ now = Time.now.strftime("%d/%b/%Y %H:%M:%S")
13
+
14
+ @logger.puts "#{now} - #{who} - #{str}"
15
+ end
16
+
17
+ def call(env, socket)
18
+ who = env[Const::REMOTE_ADDR]
19
+ log who, "connected" unless @quiet
20
+
21
+ env['log'] = lambda { |str| log(who, str) }
22
+
23
+ begin
24
+ @app.call env, socket
25
+ rescue Object => e
26
+ log who, "exception: #{e.message} (#{e.class})"
27
+ else
28
+ log who, "disconnected" unless @quiet
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,185 @@
1
+ require 'thread'
2
+
3
+ module Puma
4
+ # A simple thread pool management object.
5
+ #
6
+ class ThreadPool
7
+
8
+ # Maintain a minimum of +min+ and maximum of +max+ threads
9
+ # in the pool.
10
+ #
11
+ # The block passed is the work that will be performed in each
12
+ # thread.
13
+ #
14
+ def initialize(min, max, *extra, &block)
15
+ @cond = ConditionVariable.new
16
+ @mutex = Mutex.new
17
+
18
+ @todo = []
19
+
20
+ @spawned = 0
21
+ @waiting = 0
22
+
23
+ @min = Integer(min)
24
+ @max = Integer(max)
25
+ @block = block
26
+ @extra = extra
27
+
28
+ @shutdown = false
29
+
30
+ @trim_requested = 0
31
+
32
+ @workers = []
33
+
34
+ @auto_trim = nil
35
+
36
+ @mutex.synchronize do
37
+ @min.times { spawn_thread }
38
+ end
39
+ end
40
+
41
+ attr_reader :spawned, :trim_requested
42
+
43
+ # How many objects have yet to be processed by the pool?
44
+ #
45
+ def backlog
46
+ @mutex.synchronize { @todo.size }
47
+ end
48
+
49
+ # :nodoc:
50
+ #
51
+ # Must be called with @mutex held!
52
+ #
53
+ def spawn_thread
54
+ @spawned += 1
55
+
56
+ th = Thread.new do
57
+ todo = @todo
58
+ block = @block
59
+ mutex = @mutex
60
+ cond = @cond
61
+
62
+ extra = @extra.map { |i| i.new }
63
+
64
+ while true
65
+ work = nil
66
+
67
+ continue = true
68
+
69
+ mutex.synchronize do
70
+ while todo.empty?
71
+ if @trim_requested > 0
72
+ @trim_requested -= 1
73
+ continue = false
74
+ break
75
+ end
76
+
77
+ if @shutdown
78
+ continue = false
79
+ break
80
+ end
81
+
82
+ @waiting += 1
83
+ cond.wait mutex
84
+ @waiting -= 1
85
+ end
86
+
87
+ work = todo.shift if continue
88
+ end
89
+
90
+ break unless continue
91
+
92
+ block.call(work, *extra)
93
+ end
94
+
95
+ mutex.synchronize do
96
+ @spawned -= 1
97
+ @workers.delete th
98
+ end
99
+ end
100
+
101
+ @workers << th
102
+
103
+ th
104
+ end
105
+
106
+ private :spawn_thread
107
+
108
+ # Add +work+ to the todo list for a Thread to pickup and process.
109
+ def <<(work)
110
+ @mutex.synchronize do
111
+ if @shutdown
112
+ raise "Unable to add work while shutting down"
113
+ end
114
+
115
+ @todo << work
116
+
117
+ if @waiting < @todo.size and @spawned < @max
118
+ spawn_thread
119
+ end
120
+
121
+ @cond.signal
122
+ end
123
+ end
124
+
125
+ # If too many threads are in the pool, tell one to finish go ahead
126
+ # and exit. If +force+ is true, then a trim request is requested
127
+ # even if all threads are being utilized.
128
+ #
129
+ def trim(force=false)
130
+ @mutex.synchronize do
131
+ if (force or @waiting > 0) and @spawned - @trim_requested > @min
132
+ @trim_requested += 1
133
+ @cond.signal
134
+ end
135
+ end
136
+ end
137
+
138
+ class AutoTrim
139
+ def initialize(pool, timeout)
140
+ @pool = pool
141
+ @timeout = timeout
142
+ @running = false
143
+ end
144
+
145
+ def start!
146
+ @running = true
147
+
148
+ @thread = Thread.new do
149
+ while @running
150
+ @pool.trim
151
+ sleep @timeout
152
+ end
153
+ end
154
+ end
155
+
156
+ def stop
157
+ @running = false
158
+ @thread.wakeup
159
+ end
160
+ end
161
+
162
+ def auto_trim!(timeout=5)
163
+ @auto_trim = AutoTrim.new(self, timeout)
164
+ @auto_trim.start!
165
+ end
166
+
167
+ # Tell all threads in the pool to exit and wait for them to finish.
168
+ #
169
+ def shutdown
170
+ @mutex.synchronize do
171
+ @shutdown = true
172
+ @cond.broadcast
173
+
174
+ @auto_trim.stop if @auto_trim
175
+ end
176
+
177
+ # Use this instead of #each so that we don't stop in the middle
178
+ # of each and see a mutated object mid #each
179
+ @workers.first.join until @workers.empty?
180
+
181
+ @spawned = 0
182
+ @workers = []
183
+ end
184
+ end
185
+ end
@@ -0,0 +1,9 @@
1
+ module Puma
2
+ module Util
3
+ module_function
4
+
5
+ def pipe
6
+ IO.pipe
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,66 @@
1
+ require 'rack/handler'
2
+ require 'puma'
3
+
4
+ module Rack
5
+ module Handler
6
+ module Puma
7
+ DEFAULT_OPTIONS = {
8
+ :Host => '0.0.0.0',
9
+ :Port => 8080,
10
+ :Threads => '0:16',
11
+ :Verbose => false
12
+ }
13
+
14
+ def self.run(app, options = {})
15
+ options = DEFAULT_OPTIONS.merge(options)
16
+
17
+ if options[:Verbose]
18
+ app = Rack::CommonLogger.new(app, STDOUT)
19
+ end
20
+
21
+ if options[:environment]
22
+ ENV['RACK_ENV'] = options[:environment].to_s
23
+ end
24
+
25
+ server = ::Puma::Server.new(app)
26
+ min, max = options[:Threads].split(':', 2)
27
+
28
+ puts "Puma #{::Puma::Const::PUMA_VERSION} starting..."
29
+ puts "* Min threads: #{min}, max threads: #{max}"
30
+ puts "* Environment: #{ENV['RACK_ENV']}"
31
+ puts "* Listening on tcp://#{options[:Host]}:#{options[:Port]}"
32
+
33
+ server.add_tcp_listener options[:Host], options[:Port]
34
+ server.min_threads = min
35
+ server.max_threads = max
36
+ yield server if block_given?
37
+
38
+ begin
39
+ server.run.join
40
+ rescue Interrupt
41
+ puts "* Gracefully stopping, waiting for requests to finish"
42
+ server.stop(true)
43
+ puts "* Goodbye!"
44
+ end
45
+
46
+ end
47
+
48
+ def self.valid_options
49
+ {
50
+ "Host=HOST" => "Hostname to listen on (default: localhost)",
51
+ "Port=PORT" => "Port to listen on (default: 8080)",
52
+ "Threads=MIN:MAX" => "min:max threads to use (default 0:16)",
53
+ "Quiet" => "Don't report each request"
54
+ }
55
+ end
56
+ end
57
+
58
+ register :puma, Puma
59
+ end
60
+ end
61
+
62
+ # This is to trick newrelic into enabling the agent automatically.
63
+ module Mongrel
64
+ class HttpServer
65
+ end
66
+ end
@@ -0,0 +1,92 @@
1
+ require 'test/unit'
2
+ require 'rack'
3
+ require 'puma/app/status'
4
+
5
+ class TestAppStatus < Test::Unit::TestCase
6
+ class FakeServer
7
+ def initialize
8
+ @status = :running
9
+ @backlog = 0
10
+ @running = 0
11
+ end
12
+
13
+ attr_reader :status
14
+ attr_accessor :backlog, :running
15
+
16
+ def stop
17
+ @status = :stop
18
+ end
19
+
20
+ def halt
21
+ @status = :halt
22
+ end
23
+
24
+ def stats
25
+ "{}"
26
+ end
27
+ end
28
+
29
+ def setup
30
+ @server = FakeServer.new
31
+ @app = Puma::App::Status.new(@server)
32
+ @app.auth_token = nil
33
+ end
34
+
35
+ def lint(uri)
36
+ app = Rack::Lint.new @app
37
+ mock_env = Rack::MockRequest.env_for uri
38
+ app.call mock_env
39
+ end
40
+
41
+ def test_bad_token
42
+ @app.auth_token = "abcdef"
43
+
44
+ status, _, _ = lint('/whatever')
45
+
46
+ assert_equal 403, status
47
+ end
48
+
49
+ def test_good_token
50
+ @app.auth_token = "abcdef"
51
+
52
+ status, _, _ = lint('/whatever?token=abcdef')
53
+
54
+ assert_equal 404, status
55
+ end
56
+
57
+ def test_unsupported
58
+ status, _, _ = lint('/not-real')
59
+
60
+ assert_equal 404, status
61
+ end
62
+
63
+ def test_stop
64
+ status, _ , app = lint('/stop')
65
+
66
+ assert_equal :stop, @server.status
67
+ assert_equal 200, status
68
+ assert_equal ['{ "status": "ok" }'], app.enum_for.to_a
69
+ end
70
+
71
+ def test_halt
72
+ status, _ , app = lint('/halt')
73
+
74
+ assert_equal :halt, @server.status
75
+ assert_equal 200, status
76
+ assert_equal ['{ "status": "ok" }'], app.enum_for.to_a
77
+ end
78
+
79
+ def test_stats
80
+ @server.backlog = 1
81
+ @server.running = 9
82
+ status, _ , app = lint('/stats')
83
+
84
+ assert_equal 200, status
85
+ assert_equal ['{}'], app.enum_for.to_a
86
+ end
87
+
88
+ def test_alternate_location
89
+ status, _ , _ = lint('__alternatE_location_/stats')
90
+ assert_equal 200, status
91
+ end
92
+ end