puma 2.0.0.b6 → 2.0.0.b7

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

Potentially problematic release.


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

@@ -1,3 +1,28 @@
1
+ === 2.0.0.b7 / 2013-03-18
2
+
3
+ * 5 minor enhancements:
4
+
5
+ * Add -q option for :start
6
+ * Add -V, --version
7
+ * Add default Rack handler helper
8
+ * Upstart support
9
+ * Set worker directory from configuration file
10
+
11
+ * 12 bug fixes:
12
+
13
+ * Close the binder in the right place. Fixes #192
14
+ * Handle early term in workers. Fixes #206
15
+ * Make sure that the default port is 80 when the request doesn't include HTTP_X_FORWARDED_PROTO.
16
+ * Prevent Errno::EBADF errors on restart when running ruby 2.0
17
+ * Record the proper @master_pid
18
+ * Respect the header HTTP_X_FORWARDED_PROTO when the host doesn't include a port number.
19
+ * Retry EAGAIN/EWOULDBLOCK during syswrite
20
+ * Run exec properly to restart. Fixes #154
21
+ * Set Rack run_once to false
22
+ * Syncronize all access to @timeouts. Fixes #208
23
+ * Write out the state post-daemonize. Fixes #189
24
+ * Prevent crash when all workers are gone
25
+
1
26
  === 2.0.0.b6 / 2013-02-06
2
27
 
3
28
  * 2 minor enhancements:
@@ -44,6 +44,7 @@ lib/puma/java_io_buffer.rb
44
44
  lib/puma/jruby_restart.rb
45
45
  lib/puma/minissl.rb
46
46
  lib/puma/null_io.rb
47
+ lib/puma/rack_default.rb
47
48
  lib/puma/rack_patch.rb
48
49
  lib/puma/reactor.rb
49
50
  lib/puma/server.rb
@@ -51,6 +52,9 @@ lib/puma/thread_pool.rb
51
52
  lib/puma/util.rb
52
53
  lib/rack/handler/puma.rb
53
54
  puma.gemspec
54
- tools/jungle/README.md
55
- tools/jungle/puma
56
- tools/jungle/run-puma
55
+ tools/jungle/init.d/README.md
56
+ tools/jungle/init.d/puma
57
+ tools/jungle/init.d/run-puma
58
+ tools/jungle/upstart/README.md
59
+ tools/jungle/upstart/puma-manager.conf
60
+ tools/jungle/upstart/puma.conf
data/README.md CHANGED
@@ -66,7 +66,7 @@ Puma provides numerous options for controlling the operation of the server. Cons
66
66
  Puma utilizes a dynamic thread pool which you can modify. You can set the minimum and maximum number of threads that are available in the pool with the `-t` (or `--threads`) flag:
67
67
 
68
68
  $ puma -t 8:32
69
-
69
+
70
70
  Puma will automatically scale the number of threads based on how much traffic is present. The current default is `0:16`. Feel free to experiment, but be careful not to set the number of maximum threads to a very large number, as you may exhaust resources on the system (or hit resource limits).
71
71
 
72
72
  ### Binding TCP / Sockets
@@ -95,9 +95,21 @@ Puma comes with a builtin status/control app that can be used query and control
95
95
 
96
96
  This directs puma to start the control server on localhost port 9293. Additionally, all requests to the control server will need to include `token=foo` as a query parameter. This allows for simple authentication. Check out https://github.com/puma/puma/blob/master/lib/puma/app/status.rb to see what the app has available.
97
97
 
98
+ ### Configuration file
99
+
100
+ You can also provide a configuration file which puma will use:
101
+
102
+ $ puma --config /path/to/config
103
+
104
+ or
105
+
106
+ $ puma -C /path/to/config
107
+
108
+ Take the following [sample configuration](https://github.com/puma/puma/blob/master/examples/config.rb) as inspiration or check out [configuration.rb](https://github.com/puma/puma/blob/master/lib/puma/configuration.rb#L138) to see all available options.
109
+
98
110
  ## Restart
99
111
 
100
- Puma includes the ability to restart itself, allowing for new versions to be easily upgraded to. When available (currently anywhere but JRuby), puma performs a "hot restart". This is the same functionality available in *unicorn* and *nginx* which keep the server sockets open between restarts. This makes sure that no pending requests are dropped while the restart is taking place.
112
+ Puma includes the ability to restart itself, allowing for new versions to be easily upgraded to. When available (MRI, Rubinius, JRuby), puma performs a "hot restart". This is the same functionality available in *unicorn* and *nginx* which keep the server sockets open between restarts. This makes sure that no pending requests are dropped while the restart is taking place.
101
113
 
102
114
  To perform a restart, there are 2 builtin mechanism:
103
115
 
@@ -120,7 +132,7 @@ If you start puma with `-S some/path` then you can pass that same path to the `p
120
132
 
121
133
  $ pumactl -S some/path command
122
134
 
123
- or
135
+ or
124
136
 
125
137
  $ pumactl -C url -T token command
126
138
 
@@ -128,9 +140,28 @@ will cause the server to perform a restart. `pumactl` is a simple CLI frontend t
128
140
 
129
141
  Allowed commands: status, restart, halt, stop
130
142
 
131
- ## Managing multiple Pumas / init.d script
143
+ ## Managing multiple Pumas / init.d / upstart scripts
144
+
145
+ If you want an easy way to manage multiple scripts at once check [tools/jungle](https://github.com/puma/puma/tree/master/tools/jungle) for init.d and upstart scripts.
146
+
147
+ ## Capistrano deployment
148
+
149
+ Puma has included Capistrano [deploy script](https://github.com/plentz/puma/blob/master/lib/puma/capistrano.rb), you just need require that:
150
+
151
+ config/deploy.rb
152
+
153
+ ```ruby
154
+ require 'puma/capistrano'
155
+ ```
156
+
157
+ and then
158
+
159
+ ```bash
160
+ $ bunde exec cap puma:start
161
+ $ bunde exec cap puma:restart
162
+ $ bunde exec cap puma:stop
163
+ ```
132
164
 
133
- If you want an easy way to manage multiple scripts at once check [tools/jungle](https://github.com/puma/puma/tree/master/tools/jungle) for an init.d script.
134
165
 
135
166
  ## License
136
167
 
@@ -15,7 +15,7 @@ module Puma
15
15
  "rack.errors".freeze => events.stderr,
16
16
  "rack.multithread".freeze => true,
17
17
  "rack.multiprocess".freeze => false,
18
- "rack.run_once".freeze => true,
18
+ "rack.run_once".freeze => false,
19
19
  "SCRIPT_NAME".freeze => ENV['SCRIPT_NAME'] || "",
20
20
 
21
21
  # Rack blows up if this is an empty string, and Rack::Lint
@@ -1,26 +1,28 @@
1
1
  Capistrano::Configuration.instance.load do
2
- after "deploy:stop", "puma:stop"
3
- after "deploy:start", "puma:start"
4
- after "deploy:restart", "puma:restart"
2
+ after 'deploy:stop', 'puma:stop'
3
+ after 'deploy:start', 'puma:start'
4
+ after 'deploy:restart', 'puma:restart'
5
5
 
6
- _cset(:puma_role) { :app }
6
+ _cset(:puma_cmd) { "#{fetch(:bundle_cmd, 'bundle')} exec puma" }
7
+ _cset(:pumactl_cmd) { "#{fetch(:bundle_cmd, 'bundle')} exec pumactl" }
8
+ _cset(:puma_state) { "#{shared_path}/sockets/puma.state" }
9
+ _cset(:puma_role) { :app }
7
10
 
8
11
  namespace :puma do
9
- desc "Start puma"
12
+ desc 'Start puma'
10
13
  task :start, :roles => lambda { fetch(:puma_role) }, :on_no_matching_servers => :continue do
11
- puma_env = fetch(:rack_env, fetch(:rails_env, "production"))
12
- run "cd #{current_path} && #{fetch(:bundle_cmd, "bundle")} exec puma -d -e #{puma_env} -b 'unix://#{shared_path}/sockets/puma.sock' -S #{shared_path}/sockets/puma.state --control 'unix://#{shared_path}/sockets/pumactl.sock'", :pty => false
14
+ puma_env = fetch(:rack_env, fetch(:rails_env, 'production'))
15
+ run "cd #{current_path} && #{fetch(:puma_cmd)} -q -d -e #{puma_env} -b 'unix://#{shared_path}/sockets/puma.sock' -S #{fetch(:puma_state)} --control 'unix://#{shared_path}/sockets/pumactl.sock'", :pty => false
13
16
  end
14
17
 
15
- desc "Stop puma"
18
+ desc 'Stop puma'
16
19
  task :stop, :roles => lambda { fetch(:puma_role) }, :on_no_matching_servers => :continue do
17
- run "cd #{current_path} && #{fetch(:bundle_cmd, "bundle")} exec pumactl -S #{shared_path}/sockets/puma.state stop"
20
+ run "cd #{current_path} && #{fetch(:pumactl_cmd)} -S fetch(:puma_state) stop"
18
21
  end
19
22
 
20
- desc "Restart puma"
23
+ desc 'Restart puma'
21
24
  task :restart, :roles => lambda { fetch(:puma_role) }, :on_no_matching_servers => :continue do
22
- run "cd #{current_path} && #{fetch(:bundle_cmd, "bundle")} exec pumactl -S #{shared_path}/sockets/puma.state restart"
25
+ run "cd #{current_path} && #{fetch(:pumactl_cmd)} -S fetch(:puma_state) restart"
23
26
  end
24
-
25
27
  end
26
28
  end
@@ -39,6 +39,9 @@ module Puma
39
39
  @restart = false
40
40
  @phased_state = :idle
41
41
 
42
+ @io_redirected = false
43
+
44
+
42
45
  ENV['NEWRELIC_DISPATCHER'] ||= "puma"
43
46
 
44
47
  setup_options
@@ -112,10 +115,12 @@ module Puma
112
115
  end
113
116
 
114
117
  require 'puma/jruby_restart'
115
- JRubyRestart.chdir_exec(@restart_dir, Gem.ruby, *@restart_argv)
118
+ JRubyRestart.chdir_exec(@restart_dir, @restart_argv)
116
119
  else
120
+ redirects = {}
117
121
  @binder.listeners.each_with_index do |(l,io),i|
118
122
  ENV["PUMA_INHERIT_#{i}"] = "#{io.to_i}:#{l}"
123
+ redirects[io.to_i] = io.to_i
119
124
  end
120
125
 
121
126
  if cmd = @options[:restart_cmd]
@@ -125,6 +130,8 @@ module Puma
125
130
  end
126
131
 
127
132
  Dir.chdir @restart_dir
133
+
134
+ argv += [redirects] unless RUBY_VERSION < '1.9'
128
135
  Kernel.exec(*argv)
129
136
  end
130
137
  end
@@ -256,6 +263,11 @@ module Puma
256
263
  end
257
264
  end
258
265
 
266
+ o.on "-V", "--version", "Print the version information" do
267
+ puts "puma version #{Puma::Const::VERSION}"
268
+ exit 1
269
+ end
270
+
259
271
  o.on "-w", "--workers COUNT",
260
272
  "Activate cluster mode: How many worker processes to create" do |arg|
261
273
  unsupported "-w not supported on JRuby and Windows",
@@ -309,7 +321,8 @@ module Puma
309
321
  state = { "pid" => Process.pid }
310
322
 
311
323
  cfg = @config.dup
312
- cfg.options.delete :on_restart
324
+
325
+ [ :logger, :worker_boot, :on_restart ].each { |o| cfg.options.delete o }
313
326
 
314
327
  state["config"] = cfg
315
328
 
@@ -349,6 +362,7 @@ module Puma
349
362
  append = @options[:redirect_append]
350
363
 
351
364
  if stdout
365
+ @io_redirected = true
352
366
  STDOUT.reopen stdout, (append ? "a" : "w")
353
367
  STDOUT.puts "=== puma startup: #{Time.now} ==="
354
368
  end
@@ -390,8 +404,6 @@ module Puma
390
404
  end
391
405
 
392
406
  def run_single
393
- write_state
394
-
395
407
  min_t = @options[:min_threads]
396
408
  max_t = @options[:max_threads]
397
409
 
@@ -402,9 +414,11 @@ module Puma
402
414
  @binder.parse @options[:binds], self
403
415
 
404
416
  if @options[:daemon]
405
- Process.daemon(true, true)
417
+ Process.daemon(true, @io_redirected)
406
418
  end
407
419
 
420
+ write_state
421
+
408
422
  server = Puma::Server.new @config.app, @events
409
423
  server.binder = @binder
410
424
  server.min_threads = min_t
@@ -521,7 +535,7 @@ module Puma
521
535
  server = Puma::Server.new @config.app, @events
522
536
  server.min_threads = min_t
523
537
  server.max_threads = max_t
524
- server.binder = @binder
538
+ server.inherit_binder @binder
525
539
 
526
540
  Signal.trap "SIGTERM" do
527
541
  server.stop
@@ -599,7 +613,7 @@ module Puma
599
613
  end
600
614
 
601
615
  def check_workers
602
- while true
616
+ while @workers.any?
603
617
  pid = Process.waitpid(-1, Process::WNOHANG)
604
618
  break unless pid
605
619
 
@@ -631,8 +645,6 @@ module Puma
631
645
 
632
646
  @binder.parse @options[:binds], self
633
647
 
634
- @master_pid = Process.pid
635
-
636
648
  read, write = Puma::Util.pipe
637
649
 
638
650
  Signal.trap "SIGCHLD" do
@@ -650,10 +662,20 @@ module Puma
650
662
  rescue Exception
651
663
  end
652
664
 
665
+ master_pid = Process.pid
666
+
653
667
  begin
654
668
  Signal.trap "SIGTERM" do
655
- stop = true
656
- write.write "!"
669
+ # The worker installs there own SIGTERM when booted.
670
+ # Until then, this is run by the worker and the worker
671
+ # should just exit if they get it.
672
+ if Process.pid != master_pid
673
+ log "Early termination of worker"
674
+ exit! 0
675
+ else
676
+ stop = true
677
+ write.write "!"
678
+ end
657
679
  end
658
680
  rescue Exception
659
681
  end
@@ -676,11 +698,13 @@ module Puma
676
698
  @check_pipe, @suicide_pipe = Puma::Util.pipe
677
699
 
678
700
  if @options[:daemon]
679
- Process.daemon(true, true)
701
+ Process.daemon(true, @io_redirected)
680
702
  else
681
703
  log "Use Ctrl-C to stop"
682
704
  end
683
705
 
706
+ @master_pid = Process.pid
707
+
684
708
  redirect_io
685
709
 
686
710
  write_state
@@ -277,6 +277,7 @@ module Puma
277
277
  # The directory to operate out of.
278
278
  def directory(dir)
279
279
  @options[:directory] = dir.to_s
280
+ @options[:worker_directory] = dir.to_s
280
281
  end
281
282
  end
282
283
  end
@@ -28,7 +28,7 @@ module Puma
28
28
  # too taxing on performance.
29
29
  module Const
30
30
 
31
- PUMA_VERSION = VERSION = "2.0.0.b6".freeze
31
+ PUMA_VERSION = VERSION = "2.0.0.b7".freeze
32
32
 
33
33
  FAST_TRACK_KA_TIMEOUT = 0.2
34
34
 
@@ -101,6 +101,7 @@ module Puma
101
101
  SERVER_PORT = "SERVER_PORT".freeze
102
102
  HTTP_HOST = "HTTP_HOST".freeze
103
103
  PORT_80 = "80".freeze
104
+ PORT_443 = "443".freeze
104
105
  LOCALHOST = "localhost".freeze
105
106
 
106
107
  SERVER_PROTOCOL = "SERVER_PROTOCOL".freeze
@@ -8,12 +8,12 @@ module Puma
8
8
  attach_function :execlp, [:string, :varargs], :int
9
9
  attach_function :chdir, [:string], :int
10
10
 
11
- def self.chdir_exec(dir, cmd, *argv)
11
+ def self.chdir_exec(dir, argv)
12
12
  chdir(dir)
13
- argv.unshift(cmd)
13
+ cmd = argv.first
14
14
  argv = ([:string] * argv.size).zip(argv).flatten
15
- argv <<:int
16
- argv << 0
15
+ argv << :string
16
+ argv << nil
17
17
  execlp(cmd, *argv)
18
18
  raise SystemCallError.new(FFI.errno)
19
19
  end
@@ -1,137 +1,139 @@
1
- module Puma::MiniSSL
2
- class Socket
3
- def initialize(socket, engine)
4
- @socket = socket
5
- @engine = engine
6
- end
1
+ module Puma
2
+ module MiniSSL
3
+ class Socket
4
+ def initialize(socket, engine)
5
+ @socket = socket
6
+ @engine = engine
7
+ end
7
8
 
8
- def to_io
9
- @socket
10
- end
9
+ def to_io
10
+ @socket
11
+ end
11
12
 
12
- def readpartial(size)
13
- while true
14
- output = @engine.read
15
- return output if output
13
+ def readpartial(size)
14
+ while true
15
+ output = @engine.read
16
+ return output if output
16
17
 
17
- data = @socket.readpartial(size)
18
- @engine.inject(data)
19
- output = @engine.read
18
+ data = @socket.readpartial(size)
19
+ @engine.inject(data)
20
+ output = @engine.read
20
21
 
21
- return output if output
22
+ return output if output
22
23
 
23
- while neg_data = @engine.extract
24
- @socket.write neg_data
24
+ while neg_data = @engine.extract
25
+ @socket.write neg_data
26
+ end
25
27
  end
26
28
  end
27
- end
28
29
 
29
- def read_nonblock(size)
30
- while true
31
- output = @engine.read
32
- return output if output
30
+ def read_nonblock(size)
31
+ while true
32
+ output = @engine.read
33
+ return output if output
33
34
 
34
- data = @socket.read_nonblock(size)
35
+ data = @socket.read_nonblock(size)
35
36
 
36
- @engine.inject(data)
37
- output = @engine.read
37
+ @engine.inject(data)
38
+ output = @engine.read
38
39
 
39
- return output if output
40
+ return output if output
40
41
 
41
- while neg_data = @engine.extract
42
- @socket.write neg_data
42
+ while neg_data = @engine.extract
43
+ @socket.write neg_data
44
+ end
43
45
  end
44
46
  end
45
- end
46
47
 
47
- def write(data)
48
- need = data.size
48
+ def write(data)
49
+ need = data.size
49
50
 
50
- while true
51
- wrote = @engine.write data
52
- enc = @engine.extract
51
+ while true
52
+ wrote = @engine.write data
53
+ enc = @engine.extract
53
54
 
54
- if enc
55
- @socket.syswrite enc
56
- end
55
+ if enc
56
+ @socket.syswrite enc
57
+ end
57
58
 
58
- need -= wrote
59
+ need -= wrote
59
60
 
60
- return data.size if need == 0
61
+ return data.size if need == 0
61
62
 
62
- data = data[need..-1]
63
+ data = data[need..-1]
64
+ end
63
65
  end
64
- end
65
66
 
66
- alias_method :syswrite, :write
67
+ alias_method :syswrite, :write
67
68
 
68
- def flush
69
- @socket.flush
70
- end
69
+ def flush
70
+ @socket.flush
71
+ end
71
72
 
72
- def close
73
- @socket.close
74
- end
73
+ def close
74
+ @socket.close
75
+ end
75
76
 
76
- def peeraddr
77
- @socket.peeraddr
77
+ def peeraddr
78
+ @socket.peeraddr
79
+ end
78
80
  end
79
- end
80
81
 
81
- class Context
82
- attr_accessor :verify_mode
82
+ class Context
83
+ attr_accessor :verify_mode
83
84
 
84
- attr_reader :key
85
- attr_reader :cert
85
+ attr_reader :key
86
+ attr_reader :cert
86
87
 
87
- def key=(key)
88
- raise ArgumentError, "No such key file '#{key}'" unless File.exist? key
89
- @key = key
90
- end
88
+ def key=(key)
89
+ raise ArgumentError, "No such key file '#{key}'" unless File.exist? key
90
+ @key = key
91
+ end
91
92
 
92
- def cert=(cert)
93
- raise ArgumentError, "No such cert file '#{cert}'" unless File.exist? cert
94
- @cert = cert
93
+ def cert=(cert)
94
+ raise ArgumentError, "No such cert file '#{cert}'" unless File.exist? cert
95
+ @cert = cert
96
+ end
95
97
  end
96
- end
97
98
 
98
- VERIFY_NONE = 0
99
- VERIFY_PEER = 1
99
+ VERIFY_NONE = 0
100
+ VERIFY_PEER = 1
100
101
 
101
- #if defined?(JRUBY_VERSION)
102
+ #if defined?(JRUBY_VERSION)
102
103
  #class Engine
103
- #def self.server(key, cert)
104
- #new(key, cert)
105
- #end
104
+ #def self.server(key, cert)
105
+ #new(key, cert)
106
+ #end
107
+ #end
106
108
  #end
107
- #end
108
109
 
109
- class Server
110
- def initialize(socket, ctx)
111
- @socket = socket
112
- @ctx = ctx
113
- end
110
+ class Server
111
+ def initialize(socket, ctx)
112
+ @socket = socket
113
+ @ctx = ctx
114
+ end
114
115
 
115
- def to_io
116
- @socket
117
- end
116
+ def to_io
117
+ @socket
118
+ end
118
119
 
119
- def accept
120
- io = @socket.accept
121
- engine = Engine.server @ctx.key, @ctx.cert
120
+ def accept
121
+ io = @socket.accept
122
+ engine = Engine.server @ctx.key, @ctx.cert
122
123
 
123
- Socket.new io, engine
124
- end
124
+ Socket.new io, engine
125
+ end
125
126
 
126
- def accept_nonblock
127
- io = @socket.accept_nonblock
128
- engine = Engine.server @ctx.key, @ctx.cert
127
+ def accept_nonblock
128
+ io = @socket.accept_nonblock
129
+ engine = Engine.server @ctx.key, @ctx.cert
129
130
 
130
- Socket.new io, engine
131
- end
131
+ Socket.new io, engine
132
+ end
132
133
 
133
- def close
134
- @socket.close
134
+ def close
135
+ @socket.close
136
+ end
135
137
  end
136
138
  end
137
139
  end
@@ -0,0 +1,7 @@
1
+ require 'rack/handler/puma'
2
+
3
+ module Rack::Handler
4
+ def self.default(options = {})
5
+ Rack::Handler::Puma
6
+ end
7
+ end
@@ -50,7 +50,9 @@ module Puma
50
50
  # list or we'll accidentally close the socket when
51
51
  # it's in use!
52
52
  if c.timeout_at
53
- @timeouts.delete c
53
+ @mutex.synchronize do
54
+ @timeouts.delete c
55
+ end
54
56
  end
55
57
 
56
58
  begin
@@ -78,17 +80,19 @@ module Puma
78
80
  end
79
81
 
80
82
  unless @timeouts.empty?
81
- now = Time.now
83
+ @mutex.synchronize do
84
+ now = Time.now
82
85
 
83
- while @timeouts.first.timeout_at < now
84
- c = @timeouts.shift
85
- sockets.delete c
86
- c.close
86
+ while @timeouts.first.timeout_at < now
87
+ c = @timeouts.shift
88
+ sockets.delete c
89
+ c.close
87
90
 
88
- break if @timeouts.empty?
89
- end
91
+ break if @timeouts.empty?
92
+ end
90
93
 
91
- calculate_sleep
94
+ calculate_sleep
95
+ end
92
96
  end
93
97
  end
94
98
  ensure
@@ -64,6 +64,8 @@ module Puma
64
64
  @persistent_timeout = PERSISTENT_TIMEOUT
65
65
 
66
66
  @binder = Binder.new(events)
67
+ @own_binder = true
68
+
67
69
  @first_data_timeout = FIRST_DATA_TIMEOUT
68
70
 
69
71
  ENV['RACK_ENV'] ||= "development"
@@ -75,6 +77,11 @@ module Puma
75
77
  forward :add_ssl_listener, :@binder
76
78
  forward :add_unix_listener, :@binder
77
79
 
80
+ def inherit_binder(bind)
81
+ @binder = bind
82
+ @own_binder = false
83
+ end
84
+
78
85
  # On Linux, use TCP_CORK to better control how the TCP stack
79
86
  # packetizes our stream. This improves both latency and throughput.
80
87
  #
@@ -190,11 +197,14 @@ module Puma
190
197
  @reactor.clear! if @status == :restart
191
198
 
192
199
  @reactor.shutdown
200
+ rescue Exception => e
201
+ STDERR.puts "Exception handling servers: #{e.message} (#{e.class})"
202
+ STDERR.puts e.backtrace
193
203
  ensure
194
204
  @check.close
195
205
  @notify.close
196
206
 
197
- unless @status == :restart
207
+ if @status != :restart and @own_binder
198
208
  @binder.close
199
209
  end
200
210
  end
@@ -287,11 +297,11 @@ module Puma
287
297
  env[SERVER_PORT] = host[colon+1, host.bytesize]
288
298
  else
289
299
  env[SERVER_NAME] = host
290
- env[SERVER_PORT] = PORT_80
300
+ env[SERVER_PORT] = default_server_port(env)
291
301
  end
292
302
  else
293
303
  env[SERVER_NAME] = LOCALHOST
294
- env[SERVER_PORT] = PORT_80
304
+ env[SERVER_PORT] = default_server_port(env)
295
305
  end
296
306
 
297
307
  unless env[REQUEST_PATH]
@@ -315,6 +325,10 @@ module Puma
315
325
  env[REMOTE_ADDR] = client.peeraddr.last
316
326
  end
317
327
 
328
+ def default_server_port(env)
329
+ env['HTTP_X_FORWARDED_PROTO'] == 'https' ? PORT_443 : PORT_80
330
+ end
331
+
318
332
  # Given the request +env+ from +client+ and a partial request body
319
333
  # in +body+, finish reading the body if there is one and invoke
320
334
  # the rack app. Then construct the response and write it back to
@@ -581,19 +595,17 @@ module Puma
581
595
  end
582
596
 
583
597
  def fast_write(io, str)
584
- n = io.syswrite str
585
-
586
- # Fast path.
587
- return if n == str.bytesize
588
-
589
- pos = n
590
- left = str.bytesize - n
591
-
592
- until left == 0
593
- n = io.syswrite str.byteslice(pos..-1)
598
+ n = 0
599
+ while true
600
+ begin
601
+ n = io.syswrite str
602
+ rescue Errno::EAGAIN, Errno::EWOULDBLOCK
603
+ IO.select(nil, [io], nil, 1)
604
+ retry
605
+ end
594
606
 
595
- pos += n
596
- left -= n
607
+ return if n == str.bytesize
608
+ str = str.byteslice(n..-1)
597
609
  end
598
610
  end
599
611
  private :fast_write
@@ -2,17 +2,17 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = "puma"
5
- s.version = "2.0.0.b6"
5
+ s.version = "2.0.0.b7"
6
6
 
7
7
  s.required_rubygems_version = Gem::Requirement.new("> 1.3.1") if s.respond_to? :required_rubygems_version=
8
8
  s.authors = ["Evan Phoenix"]
9
- s.date = "2013-02-07"
9
+ s.date = "2013-03-19"
10
10
  s.description = "Puma is a simple, fast, and highly concurrent HTTP 1.1 server for Ruby web applications. It can be used with any application that supports Rack, and is considered the replacement for Webrick and Mongrel. It was designed to be the go-to server for [Rubinius](http://rubini.us), but also works well with JRuby and MRI. Puma is intended for use in both development and production environments.\n\nUnder the hood, Puma processes requests using a C-optimized Ragel extension (inherited from Mongrel) that provides fast, accurate HTTP 1.1 protocol parsing in a portable way. Puma then serves the request in a thread from an internal thread pool (which you can control). This allows Puma to provide real concurrency for your web application!\n\nWith Rubinius 2.0, Puma will utilize all cores on your CPU with real threads, meaning you won't have to spawn multiple processes to increase throughput. You can expect to see a similar benefit from JRuby.\n\nOn MRI, there is a Global Interpreter Lock (GIL) that ensures only one thread can be run at a time. But if you're doing a lot of blocking IO (such as HTTP calls to external APIs like Twitter), Puma still improves MRI's throughput by allowing blocking IO to be run concurrently (EventMachine-based servers such as Thin turn off this ability, requiring you to use special libraries). Your mileage may vary. In order to get the best throughput, it is highly recommended that you use a Ruby implementation with real threads like [Rubinius](http://rubini.us) or [JRuby](http://jruby.org)."
11
11
  s.email = ["evan@phx.io"]
12
12
  s.executables = ["puma", "pumactl"]
13
13
  s.extensions = ["ext/puma_http11/extconf.rb"]
14
14
  s.extra_rdoc_files = ["History.txt", "Manifest.txt"]
15
- s.files = ["COPYING", "Gemfile", "History.txt", "LICENSE", "Manifest.txt", "README.md", "Rakefile", "TODO", "bin/puma", "bin/pumactl", "docs/config.md", "docs/nginx.md", "ext/puma_http11/PumaHttp11Service.java", "ext/puma_http11/ext_help.h", "ext/puma_http11/extconf.rb", "ext/puma_http11/http11_parser.c", "ext/puma_http11/http11_parser.h", "ext/puma_http11/http11_parser.java.rl", "ext/puma_http11/http11_parser.rl", "ext/puma_http11/http11_parser_common.rl", "ext/puma_http11/io_buffer.c", "ext/puma_http11/mini_ssl.c", "ext/puma_http11/org/jruby/puma/Http11.java", "ext/puma_http11/org/jruby/puma/Http11Parser.java", "ext/puma_http11/org/jruby/puma/MiniSSL.java", "ext/puma_http11/puma_http11.c", "lib/puma.rb", "lib/puma/accept_nonblock.rb", "lib/puma/app/status.rb", "lib/puma/binder.rb", "lib/puma/capistrano.rb", "lib/puma/cli.rb", "lib/puma/client.rb", "lib/puma/compat.rb", "lib/puma/configuration.rb", "lib/puma/const.rb", "lib/puma/control_cli.rb", "lib/puma/daemon_ext.rb", "lib/puma/delegation.rb", "lib/puma/detect.rb", "lib/puma/events.rb", "lib/puma/io_buffer.rb", "lib/puma/java_io_buffer.rb", "lib/puma/jruby_restart.rb", "lib/puma/minissl.rb", "lib/puma/null_io.rb", "lib/puma/rack_patch.rb", "lib/puma/reactor.rb", "lib/puma/server.rb", "lib/puma/thread_pool.rb", "lib/puma/util.rb", "lib/rack/handler/puma.rb", "puma.gemspec", "tools/jungle/README.md", "tools/jungle/puma", "tools/jungle/run-puma", "test/test_app_status.rb", "test/test_cli.rb", "test/test_config.rb", "test/test_http10.rb", "test/test_http11.rb", "test/test_integration.rb", "test/test_iobuffer.rb", "test/test_minissl.rb", "test/test_null_io.rb", "test/test_persistent.rb", "test/test_puma_server.rb", "test/test_rack_handler.rb", "test/test_rack_server.rb", "test/test_thread_pool.rb", "test/test_unix_socket.rb", "test/test_ws.rb"]
15
+ s.files = ["COPYING", "Gemfile", "History.txt", "LICENSE", "Manifest.txt", "README.md", "Rakefile", "TODO", "bin/puma", "bin/pumactl", "docs/config.md", "docs/nginx.md", "ext/puma_http11/PumaHttp11Service.java", "ext/puma_http11/ext_help.h", "ext/puma_http11/extconf.rb", "ext/puma_http11/http11_parser.c", "ext/puma_http11/http11_parser.h", "ext/puma_http11/http11_parser.java.rl", "ext/puma_http11/http11_parser.rl", "ext/puma_http11/http11_parser_common.rl", "ext/puma_http11/io_buffer.c", "ext/puma_http11/mini_ssl.c", "ext/puma_http11/org/jruby/puma/Http11.java", "ext/puma_http11/org/jruby/puma/Http11Parser.java", "ext/puma_http11/org/jruby/puma/MiniSSL.java", "ext/puma_http11/puma_http11.c", "lib/puma.rb", "lib/puma/accept_nonblock.rb", "lib/puma/app/status.rb", "lib/puma/binder.rb", "lib/puma/capistrano.rb", "lib/puma/cli.rb", "lib/puma/client.rb", "lib/puma/compat.rb", "lib/puma/configuration.rb", "lib/puma/const.rb", "lib/puma/control_cli.rb", "lib/puma/daemon_ext.rb", "lib/puma/delegation.rb", "lib/puma/detect.rb", "lib/puma/events.rb", "lib/puma/io_buffer.rb", "lib/puma/java_io_buffer.rb", "lib/puma/jruby_restart.rb", "lib/puma/minissl.rb", "lib/puma/null_io.rb", "lib/puma/rack_default.rb", "lib/puma/rack_patch.rb", "lib/puma/reactor.rb", "lib/puma/server.rb", "lib/puma/thread_pool.rb", "lib/puma/util.rb", "lib/rack/handler/puma.rb", "puma.gemspec", "tools/jungle/init.d/README.md", "tools/jungle/init.d/puma", "tools/jungle/init.d/run-puma", "tools/jungle/upstart/README.md", "tools/jungle/upstart/puma-manager.conf", "tools/jungle/upstart/puma.conf", "test/test_app_status.rb", "test/test_cli.rb", "test/test_config.rb", "test/test_http10.rb", "test/test_http11.rb", "test/test_integration.rb", "test/test_iobuffer.rb", "test/test_minissl.rb", "test/test_null_io.rb", "test/test_persistent.rb", "test/test_puma_server.rb", "test/test_rack_handler.rb", "test/test_rack_server.rb", "test/test_thread_pool.rb", "test/test_unix_socket.rb", "test/test_ws.rb"]
16
16
  s.homepage = "http://puma.io"
17
17
  s.rdoc_options = ["--main", "README.md"]
18
18
  s.require_paths = ["lib"]
@@ -29,17 +29,17 @@ Gem::Specification.new do |s|
29
29
  s.add_runtime_dependency(%q<rack>, ["< 2.0", ">= 1.1"])
30
30
  s.add_development_dependency(%q<rdoc>, ["~> 3.10"])
31
31
  s.add_development_dependency(%q<rake-compiler>, ["~> 0.8.0"])
32
- s.add_development_dependency(%q<hoe>, ["~> 3.0"])
32
+ s.add_development_dependency(%q<hoe>, ["~> 3.5"])
33
33
  else
34
34
  s.add_dependency(%q<rack>, ["< 2.0", ">= 1.1"])
35
35
  s.add_dependency(%q<rdoc>, ["~> 3.10"])
36
36
  s.add_dependency(%q<rake-compiler>, ["~> 0.8.0"])
37
- s.add_dependency(%q<hoe>, ["~> 3.0"])
37
+ s.add_dependency(%q<hoe>, ["~> 3.5"])
38
38
  end
39
39
  else
40
40
  s.add_dependency(%q<rack>, ["< 2.0", ">= 1.1"])
41
41
  s.add_dependency(%q<rdoc>, ["~> 3.10"])
42
42
  s.add_dependency(%q<rake-compiler>, ["~> 0.8.0"])
43
- s.add_dependency(%q<hoe>, ["~> 3.0"])
43
+ s.add_dependency(%q<hoe>, ["~> 3.5"])
44
44
  end
45
45
  end
@@ -1,5 +1,7 @@
1
1
  require 'test/unit'
2
2
 
3
+ unless defined? JRUBY_VERSION
4
+
3
5
  require 'puma'
4
6
  require 'puma/minissl'
5
7
 
@@ -10,13 +12,14 @@ class TestMiniSSL < Test::Unit::TestCase
10
12
 
11
13
  exception = assert_raise(ArgumentError) { ctx.key = "/no/such/key" }
12
14
  assert_equal("No such key file '/no/such/key'", exception.message)
13
- end unless defined? JRUBY_VERSION
15
+ end
14
16
 
15
17
  def test_raises_with_invalid_cert_file
16
18
  ctx = Puma::MiniSSL::Context.new
17
19
 
18
20
  exception = assert_raise(ArgumentError) { ctx.cert = "/no/such/cert" }
19
21
  assert_equal("No such cert file '/no/such/cert'", exception.message)
20
- end unless defined? JRUBY_VERSION
22
+ end
23
+ end
21
24
 
22
25
  end
@@ -125,4 +125,41 @@ class TestPumaServer < Test::Unit::TestCase
125
125
 
126
126
  assert_equal giant.bytesize, out.bytesize
127
127
  end
128
+
129
+ def test_respect_x_forwarded_proto
130
+ @server.app = proc do |env|
131
+ [200, {}, [env['SERVER_PORT']]]
132
+ end
133
+
134
+ @server.add_tcp_listener @host, @port
135
+ @server.run
136
+
137
+ req = Net::HTTP::Get.new("/")
138
+ req['HOST'] = "example.com"
139
+ req['X_FORWARDED_PROTO'] = "https"
140
+
141
+ res = Net::HTTP.start @host, @port do |http|
142
+ http.request(req)
143
+ end
144
+
145
+ assert_equal "443", res.body
146
+ end
147
+
148
+ def test_default_server_port
149
+ @server.app = proc do |env|
150
+ [200, {}, [env['SERVER_PORT']]]
151
+ end
152
+
153
+ @server.add_tcp_listener @host, @port
154
+ @server.run
155
+
156
+ req = Net::HTTP::Get.new("/")
157
+ req['HOST'] = "example.com"
158
+
159
+ res = Net::HTTP.start @host, @port do |http|
160
+ http.request(req)
161
+ end
162
+
163
+ assert_equal "80", res.body
164
+ end
128
165
  end
File without changes
@@ -0,0 +1,61 @@
1
+ # Puma as a service using Upstart
2
+
3
+ Manage multiple Puma servers as services on the same box using Ubuntu upstart.
4
+
5
+ ## Installation
6
+
7
+ # Copy the scripts to services directory
8
+ sudo cp puma.conf puma-manager.conf /etc/init
9
+
10
+ # Create an empty configuration file
11
+ sudo touch /etc/puma.conf
12
+
13
+ ## Managing the jungle
14
+
15
+ Puma apps are referenced in /etc/puma.conf by default. Add each app's path as a new line, e.g.:
16
+
17
+ ```
18
+ /home/apps/my-cool-ruby-app
19
+ /home/apps/another-app/current
20
+ ```
21
+
22
+ Start the jungle running:
23
+
24
+ `sudo start puma-manager`
25
+
26
+ This script will run at boot time.
27
+
28
+ Start a single puma like this:
29
+
30
+ `sudo start puma app=/path/to/app`
31
+
32
+ ## Logs
33
+
34
+ Everything is logged by upstart, defaulting to `/var/log/upstart`.
35
+
36
+ Each puma instance is named after its directory, so for an app called `/home/apps/my-app` the log file would be `/var/log/upstart/puma-_home_apps_my-app.log`.
37
+
38
+ ## Conventions
39
+
40
+ * The script expects:
41
+ * a config file to exist under `config/puma.rb` in your app. E.g.: `/home/apps/my-app/config/puma.rb`.
42
+ * a temporary folder to put the PID, socket and state files to exist called `tmp/puma`. E.g.: `/home/apps/my-app/tmp/puma`. Puma will take care of the files for you.
43
+
44
+ You can always change those defaults by editing the scripts.
45
+
46
+ ## Here's what a minimal app's config file should have
47
+
48
+ ```
49
+ pidfile "/path/to/app/tmp/puma/pid"
50
+ state_path "/path/to/app/tmp/puma/state"
51
+ activate_control_app
52
+ ```
53
+
54
+ ## Before starting...
55
+
56
+ You need to customise `puma.conf` to:
57
+
58
+ * Set the right user your app should be running on unless you want root to execute it!
59
+ * Look for `setuid apps` and `setgid apps`, uncomment those lines and replace `apps` to whatever your deployment user is.
60
+ * Replace `apps` on the paths (or set the right paths to your user's home) everywhere else.
61
+ * Uncomment the source lines for `rbenv` or `rvm` support unless you use a system wide installation of Ruby.
@@ -0,0 +1,31 @@
1
+ # /etc/init/puma-manager.conf - manage a set of Pumas
2
+
3
+ # This example config should work with Ubuntu 12.04+. It
4
+ # allows you to manage multiple Puma instances with
5
+ # Upstart, Ubuntu's native service management tool.
6
+ #
7
+ # See puma.conf for how to manage a single Puma instance.
8
+ #
9
+ # Use "stop workers" to stop all Puma instances.
10
+ # Use "start workers" to start all instances.
11
+ # Use "restart workers" to restart all instances.
12
+ # Crazy, right?
13
+ #
14
+
15
+ description "Manages the set of puma processes"
16
+
17
+ # This starts upon bootup and stops on shutdown
18
+ start on runlevel [2345]
19
+ stop on runlevel [06]
20
+
21
+ # Set this to the number of Puma processes you want
22
+ # to run on this machine
23
+ env PUMA_CONF=/etc/puma.conf
24
+
25
+ pre-start script
26
+ for i in `cat $PUMA_CONF`; do
27
+ app=`echo $i | cut -d , -f 1`
28
+ logger -t "puma-manager" "Starting $app"
29
+ start puma app=$app
30
+ done
31
+ end script
@@ -0,0 +1,52 @@
1
+ # /etc/init/puma.conf - Puma config
2
+
3
+ # This example config should work with Ubuntu 12.04+. It
4
+ # allows you to manage multiple Puma instances with
5
+ # Upstart, Ubuntu's native service management tool.
6
+ #
7
+ # See workers.conf for how to manage all Puma instances at once.
8
+ #
9
+ # Save this config as /etc/init/puma.conf then mange puma with:
10
+ # sudo start puma index=0
11
+ # sudo stop puma index=0
12
+ # sudo status puma index=0
13
+ #
14
+ # or use the service command:
15
+ # sudo service puma {start,stop,restart,status}
16
+ #
17
+
18
+ description "Puma Background Worker"
19
+
20
+ # no "start on", we don't want to automatically start
21
+ stop on (stopping puma-manager or runlevel [06])
22
+
23
+ # change apps to match your deployment user if you want to use this as a less privileged user (recommended!)
24
+ # setuid apps
25
+ # setgid apps
26
+
27
+ respawn
28
+ respawn limit 3 30
29
+
30
+ instance ${app}
31
+
32
+ script
33
+ # this script runs in /bin/sh by default
34
+ # respawn as bash so we can source in rbenv/rvm
35
+ exec /bin/bash <<EOT
36
+ export HOME=/home/apps
37
+
38
+ # Pick your poison :) Or none if you're using a system wide installed Ruby.
39
+ # rbenv
40
+ # source /home/apps/.bash_profile
41
+ # OR
42
+ # source /home/apps/.profile
43
+ #
44
+ # rvm
45
+ # source /home/apps/.rvm/scripts/rvm
46
+
47
+ logger -t puma "Starting server: $app"
48
+
49
+ cd $app
50
+ exec bundle exec puma -C config/puma.rb
51
+ EOT
52
+ end script
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: puma
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.0.b6
4
+ version: 2.0.0.b7
5
5
  prerelease: 6
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-02-07 00:00:00.000000000 Z
12
+ date: 2013-03-19 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rack
@@ -72,7 +72,7 @@ dependencies:
72
72
  requirements:
73
73
  - - ~>
74
74
  - !ruby/object:Gem::Version
75
- version: '3.0'
75
+ version: '3.5'
76
76
  type: :development
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
@@ -80,7 +80,7 @@ dependencies:
80
80
  requirements:
81
81
  - - ~>
82
82
  - !ruby/object:Gem::Version
83
- version: '3.0'
83
+ version: '3.5'
84
84
  description: ! 'Puma is a simple, fast, and highly concurrent HTTP 1.1 server for
85
85
  Ruby web applications. It can be used with any application that supports Rack, and
86
86
  is considered the replacement for Webrick and Mongrel. It was designed to be the
@@ -163,6 +163,7 @@ files:
163
163
  - lib/puma/jruby_restart.rb
164
164
  - lib/puma/minissl.rb
165
165
  - lib/puma/null_io.rb
166
+ - lib/puma/rack_default.rb
166
167
  - lib/puma/rack_patch.rb
167
168
  - lib/puma/reactor.rb
168
169
  - lib/puma/server.rb
@@ -170,9 +171,12 @@ files:
170
171
  - lib/puma/util.rb
171
172
  - lib/rack/handler/puma.rb
172
173
  - puma.gemspec
173
- - tools/jungle/README.md
174
- - tools/jungle/puma
175
- - tools/jungle/run-puma
174
+ - tools/jungle/init.d/README.md
175
+ - tools/jungle/init.d/puma
176
+ - tools/jungle/init.d/run-puma
177
+ - tools/jungle/upstart/README.md
178
+ - tools/jungle/upstart/puma-manager.conf
179
+ - tools/jungle/upstart/puma.conf
176
180
  - test/test_app_status.rb
177
181
  - test/test_cli.rb
178
182
  - test/test_config.rb