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 +3 -5
- data/ext/puma_http11/PumaHttp11Service.java +2 -0
- data/lib/puma/cli.rb +27 -0
- data/lib/puma/configuration.rb +9 -0
- data/lib/puma/const.rb +1 -1
- data/lib/puma/events.rb +1 -1
- data/lib/puma/server.rb +13 -1
- data/lib/puma/thread_pool.rb +69 -32
- data/puma.gemspec +6 -3
- data/test/test_thread_pool.rb +22 -2
- metadata +22 -7
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 =
|
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
|
-
|
78
|
-
|
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
|
data/lib/puma/cli.rb
CHANGED
@@ -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
|
data/lib/puma/configuration.rb
CHANGED
@@ -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
|
#
|
data/lib/puma/const.rb
CHANGED
data/lib/puma/events.rb
CHANGED
@@ -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,
|
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
|
data/lib/puma/server.rb
CHANGED
@@ -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,
|
171
|
+
@events.unknown_error self, e, "Listen loop"
|
160
172
|
end
|
161
173
|
end
|
162
174
|
|
data/lib/puma/thread_pool.rb
CHANGED
@@ -12,21 +12,29 @@ module Puma
|
|
12
12
|
# thread.
|
13
13
|
#
|
14
14
|
def initialize(min, max, &blk)
|
15
|
-
@
|
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
|
-
|
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
|
-
@
|
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 =
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
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
|
-
|
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
|
-
@
|
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
|
-
|
84
|
-
|
85
|
-
|
110
|
+
@mutex.synchronize do
|
111
|
+
if @shutdown
|
112
|
+
raise "Unable to add work while shutting down"
|
113
|
+
end
|
86
114
|
|
87
|
-
|
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
|
-
@
|
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
|
-
@
|
170
|
+
@mutex.synchronize do
|
171
|
+
@shutdown = true
|
172
|
+
@cond.broadcast
|
135
173
|
|
136
|
-
|
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
|
data/puma.gemspec
CHANGED
@@ -2,11 +2,11 @@
|
|
2
2
|
|
3
3
|
Gem::Specification.new do |s|
|
4
4
|
s.name = "puma"
|
5
|
-
s.version = "0.9.
|
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-
|
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.
|
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
|
data/test/test_thread_pool.rb
CHANGED
@@ -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
|
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
|
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:
|
4
|
+
hash: 51
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 9
|
9
|
-
-
|
10
|
-
version: 0.9.
|
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-
|
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:
|
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: *
|
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.
|
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
|