puma 0.9.3 → 0.9.4

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.

data/Rakefile CHANGED
@@ -62,7 +62,7 @@ Rake::ExtensionTask.new("puma_http11", HOE.spec) do |ext|
62
62
  # place extension inside namespace
63
63
  ext.lib_dir = "lib/puma"
64
64
 
65
- ext.cross_compile = !!ENV['CROSS']
65
+ ext.cross_compile = true
66
66
  ext.cross_platform = ['i386-mswin32-60', 'i386-mingw32']
67
67
  ext.cross_compiling do |spec|
68
68
  # add fat-binary stub only when cross compiling
@@ -74,10 +74,8 @@ Rake::ExtensionTask.new("puma_http11", HOE.spec) do |ext|
74
74
  end
75
75
 
76
76
  # Java (JRuby)
77
- if defined? JRUBY_VERSION
78
- Rake::JavaExtensionTask.new("puma_http11", HOE.spec) do |ext|
79
- ext.lib_dir = "lib/puma"
80
- end
77
+ Rake::JavaExtensionTask.new("puma_http11", HOE.spec) do |ext|
78
+ ext.lib_dir = "lib/puma"
81
79
  end
82
80
 
83
81
  # the following is a fat-binary stub that will be used when
@@ -1,3 +1,5 @@
1
+ package puma;
2
+
1
3
  import java.io.IOException;
2
4
 
3
5
  import org.jruby.Ruby;
@@ -6,6 +6,7 @@ require 'puma/const'
6
6
  require 'puma/configuration'
7
7
 
8
8
  require 'rack/commonlogger'
9
+ require 'rack/utils'
9
10
 
10
11
  module Puma
11
12
  # Handles invoke a Puma::Server in a command line style.
@@ -120,6 +121,11 @@ module Puma
120
121
  @options[:config_file] = arg
121
122
  end
122
123
 
124
+ o.on "-p", "--port PORT", "Define what port TCP port to bind to",
125
+ "Use -b for more advanced options" do |arg|
126
+ @options[:binds] << "tcp://#{Configuration::DefaultTCPHost}:#{arg}"
127
+ end
128
+
123
129
  o.on "--pidfile PATH", "Use PATH as a pidfile" do |arg|
124
130
  @options[:pidfile] = arg
125
131
  end
@@ -238,6 +244,27 @@ module Puma
238
244
  path = "#{uri.host}#{uri.path}"
239
245
 
240
246
  server.add_unix_listener path
247
+ when "ssl"
248
+ log "* Listening on #{str}"
249
+ params = Rack::Utils.parse_query uri.query
250
+ require 'openssl'
251
+
252
+ ctx = OpenSSL::SSL::SSLContext.new
253
+ unless params['key']
254
+ error "Please specify the SSL key via 'key='"
255
+ end
256
+
257
+ ctx.key = OpenSSL::PKey::RSA.new File.read(params['key'])
258
+
259
+ unless params['cert']
260
+ error "Please specify the SSL cert via 'cert='"
261
+ end
262
+
263
+ ctx.cert = OpenSSL::X509::Certificate.new File.read(params['cert'])
264
+
265
+ ctx.verify_mode = OpenSSL::SSL::VERIFY_NONE
266
+
267
+ server.add_ssl_listener uri.host, uri.port, ctx
241
268
  else
242
269
  error "Invalid URI: #{str}"
243
270
  end
@@ -179,6 +179,15 @@ module Puma
179
179
  @options[:max_threads] = max
180
180
  end
181
181
 
182
+ def ssl_bind(host, port, opts)
183
+ o = [
184
+ "cert=#{opts[:cert]}",
185
+ "key=#{opts[:key]}"
186
+ ]
187
+
188
+ @options[:binds] << "ssl://#{host}:#{port}?#{o.join('&')}"
189
+ end
190
+
182
191
  # Use +path+ as the file to store the server info state. This is
183
192
  # used by pumactl to query and control the server.
184
193
  #
@@ -75,7 +75,7 @@ module Puma
75
75
 
76
76
  PATH_INFO = 'PATH_INFO'.freeze
77
77
 
78
- PUMA_VERSION = VERSION = "0.9.3".freeze
78
+ PUMA_VERSION = VERSION = "0.9.4".freeze
79
79
 
80
80
  PUMA_TMP_BASE = "puma".freeze
81
81
 
@@ -33,7 +33,7 @@ module Puma
33
33
  # +server+ is the Server object, +env+ the request, +error+ an exception
34
34
  # object, and +kind+ some additional info.
35
35
  #
36
- def unknown_error(server, env, error, kind="Unknown")
36
+ def unknown_error(server, error, kind="Unknown")
37
37
  if error.respond_to? :render
38
38
  error.render "#{Time.now}: #{kind} error", @stderr
39
39
  else
@@ -66,6 +66,8 @@ module Puma
66
66
  SERVER_SOFTWARE => PUMA_VERSION,
67
67
  GATEWAY_INTERFACE => CGI_VER
68
68
  }
69
+
70
+ ENV['RACK_ENV'] ||= "development"
69
71
  end
70
72
 
71
73
  # On Linux, use TCP_CORK to better control how the TCP stack
@@ -106,6 +108,16 @@ module Puma
106
108
  @ios << s
107
109
  end
108
110
 
111
+ def add_ssl_listener(host, port, ctx, optimize_for_latency=true, backlog=1024)
112
+ s = TCPServer.new(host, port)
113
+ if optimize_for_latency
114
+ s.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
115
+ end
116
+ s.listen backlog
117
+ @proto_env[HTTPS_KEY] = HTTPS
118
+ @ios << OpenSSL::SSL::SSLServer.new(s, ctx)
119
+ end
120
+
109
121
  # Tell the server to listen on +path+ as a UNIX domain socket.
110
122
  #
111
123
  def add_unix_listener(path)
@@ -156,7 +168,7 @@ module Puma
156
168
  # client closed the socket even before accept
157
169
  client.close rescue nil
158
170
  rescue Object => e
159
- @events.unknown_error self, env, e, "Listen loop"
171
+ @events.unknown_error self, e, "Listen loop"
160
172
  end
161
173
  end
162
174
 
@@ -12,21 +12,29 @@ module Puma
12
12
  # thread.
13
13
  #
14
14
  def initialize(min, max, &blk)
15
- @todo = Queue.new
15
+ @cond = ConditionVariable.new
16
16
  @mutex = Mutex.new
17
17
 
18
+ @todo = []
19
+
18
20
  @spawned = 0
21
+ @waiting = 0
22
+
19
23
  @min = min
20
24
  @max = max
21
25
  @block = blk
22
26
 
27
+ @shutdown = false
28
+
23
29
  @trim_requested = 0
24
30
 
25
31
  @workers = []
26
32
 
27
33
  @auto_trim = nil
28
34
 
29
- min.times { spawn_thread }
35
+ @mutex.synchronize do
36
+ min.times { spawn_thread }
37
+ end
30
38
  end
31
39
 
32
40
  attr_reader :spawned
@@ -34,37 +42,54 @@ module Puma
34
42
  # How many objects have yet to be processed by the pool?
35
43
  #
36
44
  def backlog
37
- @todo.size
45
+ @mutex.synchronize { @todo.size }
38
46
  end
39
47
 
40
- Stop = Object.new
41
- Trim = Object.new
42
-
43
48
  # :nodoc:
49
+ #
50
+ # Must be called with @mutex held!
51
+ #
44
52
  def spawn_thread
45
- @mutex.synchronize do
46
- @spawned += 1
47
- end
53
+ @spawned += 1
48
54
 
49
55
  th = Thread.new do
50
56
  todo = @todo
51
57
  block = @block
52
58
 
53
59
  while true
54
- work = todo.pop
55
-
56
- case work
57
- when Stop
58
- break
59
- when Trim
60
- @mutex.synchronize do
61
- @trim_requested -= 1
60
+ work = nil
61
+
62
+ continue = true
63
+
64
+ @mutex.synchronize do
65
+ while todo.empty?
66
+ if @trim_requested > 0
67
+ @trim_requested -= 1
68
+ continue = false
69
+ break
70
+ end
71
+
72
+ if @shutdown
73
+ continue = false
74
+ break
75
+ end
76
+
77
+ @waiting += 1
78
+ @cond.wait @mutex
79
+ @waiting -= 1
80
+
81
+ if @shutdown
82
+ continue = false
83
+ break
84
+ end
62
85
  end
63
86
 
64
- break
65
- else
66
- block.call work
87
+ work = todo.pop if continue
67
88
  end
89
+
90
+ break unless continue
91
+
92
+ block.call work
68
93
  end
69
94
 
70
95
  @mutex.synchronize do
@@ -73,28 +98,39 @@ module Puma
73
98
  end
74
99
  end
75
100
 
76
- @mutex.synchronize { @workers << th }
101
+ @workers << th
77
102
 
78
103
  th
79
104
  end
80
105
 
106
+ private :spawn_thread
107
+
81
108
  # Add +work+ to the todo list for a Thread to pickup and process.
82
109
  def <<(work)
83
- if @todo.num_waiting == 0 and @spawned < @max
84
- spawn_thread
85
- end
110
+ @mutex.synchronize do
111
+ if @shutdown
112
+ raise "Unable to add work while shutting down"
113
+ end
86
114
 
87
- @todo << work
115
+ @todo << work
116
+
117
+ if @waiting == 0 and @spawned < @max
118
+ spawn_thread
119
+ end
120
+
121
+ @cond.signal
122
+ end
88
123
  end
89
124
 
90
125
  # If too many threads are in the pool, tell one to finish go ahead
91
- # and exit.
126
+ # and exit. If +force+ is true, then a trim request is requested
127
+ # even if all threads are being utilized.
92
128
  #
93
- def trim
129
+ def trim(force=false)
94
130
  @mutex.synchronize do
95
- if @spawned - @trim_requested > @min
131
+ if (force or @waiting > 0) and @spawned - @trim_requested > @min
96
132
  @trim_requested += 1
97
- @todo << Trim
133
+ @cond.signal
98
134
  end
99
135
  end
100
136
  end
@@ -131,10 +167,11 @@ module Puma
131
167
  # Tell all threads in the pool to exit and wait for them to finish.
132
168
  #
133
169
  def shutdown
134
- @auto_trim.stop if @auto_trim
170
+ @mutex.synchronize do
171
+ @shutdown = true
172
+ @cond.broadcast
135
173
 
136
- @spawned.times do
137
- @todo << Stop
174
+ @auto_trim.stop if @auto_trim
138
175
  end
139
176
 
140
177
  # Use this instead of #each so that we don't stop in the middle
@@ -2,11 +2,11 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = "puma"
5
- s.version = "0.9.3"
5
+ s.version = "0.9.4"
6
6
 
7
7
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
8
8
  s.authors = ["Evan Phoenix"]
9
- s.date = "2012-01-09"
9
+ s.date = "2012-03-02"
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"]
@@ -17,7 +17,7 @@ Gem::Specification.new do |s|
17
17
  s.require_paths = ["lib"]
18
18
  s.required_ruby_version = Gem::Requirement.new(">= 1.8.7")
19
19
  s.rubyforge_project = "puma"
20
- s.rubygems_version = "1.8.15"
20
+ s.rubygems_version = "1.8.17"
21
21
  s.summary = "Puma is a simple, fast, and highly concurrent HTTP 1.1 server for Ruby web applications"
22
22
  s.test_files = ["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_persistent.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"]
23
23
 
@@ -27,15 +27,18 @@ Gem::Specification.new do |s|
27
27
  if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
28
28
  s.add_runtime_dependency(%q<rack>, ["~> 1.2"])
29
29
  s.add_development_dependency(%q<rake-compiler>, ["~> 0.8.0"])
30
+ s.add_development_dependency(%q<rdoc>, ["~> 3.10"])
30
31
  s.add_development_dependency(%q<hoe>, ["~> 2.12"])
31
32
  else
32
33
  s.add_dependency(%q<rack>, ["~> 1.2"])
33
34
  s.add_dependency(%q<rake-compiler>, ["~> 0.8.0"])
35
+ s.add_dependency(%q<rdoc>, ["~> 3.10"])
34
36
  s.add_dependency(%q<hoe>, ["~> 2.12"])
35
37
  end
36
38
  else
37
39
  s.add_dependency(%q<rack>, ["~> 1.2"])
38
40
  s.add_dependency(%q<rake-compiler>, ["~> 0.8.0"])
41
+ s.add_dependency(%q<rdoc>, ["~> 3.10"])
39
42
  s.add_dependency(%q<hoe>, ["~> 2.12"])
40
43
  end
41
44
  end
@@ -70,6 +70,8 @@ class TestThreadPool < Test::Unit::TestCase
70
70
 
71
71
  finish = true
72
72
 
73
+ pause
74
+
73
75
  assert_equal 2, pool.spawned
74
76
  pool.trim
75
77
  pause
@@ -82,7 +84,25 @@ class TestThreadPool < Test::Unit::TestCase
82
84
 
83
85
  end
84
86
 
85
- def test_trim_doesnt_overtrim
87
+ def test_force_trim_doesnt_overtrim
88
+ finish = false
89
+ pool = new_pool(1, 2) { Thread.pass until finish }
90
+
91
+ pool << 1
92
+ pool << 2
93
+
94
+ assert_equal 2, pool.spawned
95
+ pool.trim true
96
+ pool.trim true
97
+
98
+ finish = true
99
+
100
+ pause
101
+
102
+ assert_equal 1, pool.spawned
103
+ end
104
+
105
+ def test_trim_is_ignored_if_no_waiting_threads
86
106
  finish = false
87
107
  pool = new_pool(1, 2) { Thread.pass until finish }
88
108
 
@@ -97,7 +117,7 @@ class TestThreadPool < Test::Unit::TestCase
97
117
 
98
118
  pause
99
119
 
100
- assert_equal 1, pool.spawned
120
+ assert_equal 2, pool.spawned
101
121
  end
102
122
 
103
123
  def test_autotrim
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: puma
3
3
  version: !ruby/object:Gem::Version
4
- hash: 61
4
+ hash: 51
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
8
  - 9
9
- - 3
10
- version: 0.9.3
9
+ - 4
10
+ version: 0.9.4
11
11
  platform: ruby
12
12
  authors:
13
13
  - Evan Phoenix
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2012-01-09 00:00:00 Z
18
+ date: 2012-03-02 00:00:00 Z
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
21
21
  name: rack
@@ -49,9 +49,24 @@ dependencies:
49
49
  type: :development
50
50
  version_requirements: *id002
51
51
  - !ruby/object:Gem::Dependency
52
- name: hoe
52
+ name: rdoc
53
53
  prerelease: false
54
54
  requirement: &id003 !ruby/object:Gem::Requirement
55
+ none: false
56
+ requirements:
57
+ - - ~>
58
+ - !ruby/object:Gem::Version
59
+ hash: 19
60
+ segments:
61
+ - 3
62
+ - 10
63
+ version: "3.10"
64
+ type: :development
65
+ version_requirements: *id003
66
+ - !ruby/object:Gem::Dependency
67
+ name: hoe
68
+ prerelease: false
69
+ requirement: &id004 !ruby/object:Gem::Requirement
55
70
  none: false
56
71
  requirements:
57
72
  - - ~>
@@ -62,7 +77,7 @@ dependencies:
62
77
  - 12
63
78
  version: "2.12"
64
79
  type: :development
65
- version_requirements: *id003
80
+ version_requirements: *id004
66
81
  description: |-
67
82
  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.
68
83
 
@@ -170,7 +185,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
170
185
  requirements: []
171
186
 
172
187
  rubyforge_project: puma
173
- rubygems_version: 1.8.15
188
+ rubygems_version: 1.8.17
174
189
  signing_key:
175
190
  specification_version: 3
176
191
  summary: Puma is a simple, fast, and highly concurrent HTTP 1.1 server for Ruby web applications