puma 2.7.0 → 3.1.1

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.

Files changed (79) hide show
  1. checksums.yaml +5 -13
  2. data/DEPLOYMENT.md +91 -0
  3. data/Gemfile +3 -2
  4. data/History.txt +624 -1
  5. data/Manifest.txt +15 -3
  6. data/README.md +129 -14
  7. data/Rakefile +3 -3
  8. data/bin/puma-wild +31 -0
  9. data/bin/pumactl +1 -1
  10. data/docs/nginx.md +1 -1
  11. data/docs/signals.md +43 -0
  12. data/ext/puma_http11/extconf.rb +7 -2
  13. data/ext/puma_http11/http11_parser.java.rl +5 -5
  14. data/ext/puma_http11/io_buffer.c +1 -1
  15. data/ext/puma_http11/mini_ssl.c +233 -18
  16. data/ext/puma_http11/org/jruby/puma/Http11.java +12 -3
  17. data/ext/puma_http11/org/jruby/puma/Http11Parser.java +39 -39
  18. data/ext/puma_http11/org/jruby/puma/MiniSSL.java +245 -195
  19. data/ext/puma_http11/puma_http11.c +12 -4
  20. data/lib/puma.rb +1 -0
  21. data/lib/puma/app/status.rb +7 -0
  22. data/lib/puma/binder.rb +108 -39
  23. data/lib/puma/capistrano.rb +23 -6
  24. data/lib/puma/cli.rb +141 -446
  25. data/lib/puma/client.rb +48 -1
  26. data/lib/puma/cluster.rb +207 -58
  27. data/lib/puma/commonlogger.rb +107 -0
  28. data/lib/puma/configuration.rb +262 -235
  29. data/lib/puma/const.rb +97 -14
  30. data/lib/puma/control_cli.rb +85 -77
  31. data/lib/puma/convenient.rb +23 -0
  32. data/lib/puma/daemon_ext.rb +11 -4
  33. data/lib/puma/detect.rb +8 -1
  34. data/lib/puma/dsl.rb +456 -0
  35. data/lib/puma/events.rb +35 -18
  36. data/lib/puma/jruby_restart.rb +1 -1
  37. data/lib/puma/launcher.rb +399 -0
  38. data/lib/puma/minissl.rb +49 -20
  39. data/lib/puma/null_io.rb +15 -0
  40. data/lib/puma/plugin.rb +104 -0
  41. data/lib/puma/plugin/tmp_restart.rb +35 -0
  42. data/lib/puma/rack/backports/uri/common_18.rb +56 -0
  43. data/lib/puma/rack/backports/uri/common_192.rb +52 -0
  44. data/lib/puma/rack/backports/uri/common_193.rb +29 -0
  45. data/lib/puma/rack/builder.rb +295 -0
  46. data/lib/puma/rack/urlmap.rb +90 -0
  47. data/lib/puma/reactor.rb +14 -1
  48. data/lib/puma/runner.rb +35 -17
  49. data/lib/puma/server.rb +161 -58
  50. data/lib/puma/single.rb +15 -10
  51. data/lib/puma/state_file.rb +29 -0
  52. data/lib/puma/thread_pool.rb +88 -13
  53. data/lib/puma/util.rb +123 -0
  54. data/lib/rack/handler/puma.rb +35 -29
  55. data/puma.gemspec +2 -4
  56. data/tools/jungle/init.d/README.md +2 -2
  57. data/tools/jungle/init.d/puma +69 -7
  58. data/tools/jungle/upstart/puma.conf +8 -2
  59. metadata +51 -71
  60. data/COPYING +0 -55
  61. data/TODO +0 -5
  62. data/lib/puma/rack_patch.rb +0 -45
  63. data/test/test_app_status.rb +0 -92
  64. data/test/test_cli.rb +0 -173
  65. data/test/test_config.rb +0 -16
  66. data/test/test_http10.rb +0 -27
  67. data/test/test_http11.rb +0 -145
  68. data/test/test_integration.rb +0 -165
  69. data/test/test_iobuffer.rb +0 -38
  70. data/test/test_minissl.rb +0 -25
  71. data/test/test_null_io.rb +0 -31
  72. data/test/test_persistent.rb +0 -238
  73. data/test/test_puma_server.rb +0 -292
  74. data/test/test_rack_handler.rb +0 -10
  75. data/test/test_rack_server.rb +0 -141
  76. data/test/test_tcp_rack.rb +0 -42
  77. data/test/test_thread_pool.rb +0 -156
  78. data/test/test_unix_socket.rb +0 -39
  79. data/test/test_ws.rb +0 -89
@@ -1,165 +0,0 @@
1
- require "rbconfig"
2
- require 'test/unit'
3
- require 'socket'
4
- require 'timeout'
5
- require 'net/http'
6
- require 'tempfile'
7
-
8
- require 'puma/cli'
9
- require 'puma/control_cli'
10
-
11
- # These don't run on travis because they're too fragile
12
-
13
- class TestIntegration < Test::Unit::TestCase
14
- def setup
15
- @state_path = "test/test_puma.state"
16
- @bind_path = "test/test_server.sock"
17
- @control_path = "test/test_control.sock"
18
- @tcp_port = 9998
19
-
20
- @server = nil
21
- @script = nil
22
-
23
- @wait, @ready = IO.pipe
24
-
25
- @events = Puma::Events.strings
26
- @events.on_booted { @ready << "!" }
27
- end
28
-
29
- def teardown
30
- File.unlink @state_path rescue nil
31
- File.unlink @bind_path rescue nil
32
- File.unlink @control_path rescue nil
33
-
34
- @wait.close
35
- @ready.close
36
-
37
- if @server
38
- Process.kill "INT", @server.pid
39
- begin
40
- Process.wait @server.pid
41
- rescue Errno::ECHILD
42
- end
43
-
44
- @server.close
45
- end
46
-
47
- if @script
48
- @script.close!
49
- end
50
- end
51
-
52
- def server(opts)
53
- core = "#{Gem.ruby} -rubygems -Ilib bin/puma"
54
- cmd = "#{core} --restart-cmd '#{core}' -b tcp://127.0.0.1:#{@tcp_port} #{opts}"
55
- tf = Tempfile.new "puma-test"
56
- tf.puts "exec #{cmd}"
57
- tf.close
58
-
59
- @script = tf
60
-
61
- @server = IO.popen("sh #{tf.path}", "r")
62
-
63
- true while @server.gets =~ /Ctrl-C/
64
-
65
- sleep 1
66
-
67
- @server
68
- end
69
-
70
- def signal(which)
71
- Process.kill which, @server.pid
72
- end
73
-
74
- def wait_booted
75
- @wait.sysread 1
76
- end
77
-
78
- def test_stop_via_pumactl
79
- if defined?(JRUBY_VERSION) || RbConfig::CONFIG["host_os"] =~ /mingw|mswin/
80
- assert true
81
- return
82
- end
83
-
84
- cli = Puma::CLI.new %W!-q -S #{@state_path} -b unix://#{@bind_path} --control unix://#{@control_path} test/hello.ru!, @events
85
-
86
- t = Thread.new do
87
- cli.run
88
- end
89
-
90
- wait_booted
91
-
92
- s = UNIXSocket.new @bind_path
93
- s << "GET / HTTP/1.0\r\n\r\n"
94
- assert_equal "Hello World", s.read.split("\r\n").last
95
-
96
- sout = StringIO.new
97
-
98
- ccli = Puma::ControlCLI.new %W!-S #{@state_path} stop!, sout
99
-
100
- ccli.run
101
-
102
- assert_kind_of Thread, t.join(1), "server didn't stop"
103
- end
104
-
105
- def notest_restart_closes_keepalive_sockets
106
- server("-q test/hello.ru")
107
-
108
- s = TCPSocket.new "localhost", @tcp_port
109
- s << "GET / HTTP/1.1\r\n\r\n"
110
- true until s.gets == "\r\n"
111
-
112
- s.readpartial(20)
113
- signal :USR2
114
-
115
- true while @server.gets =~ /Ctrl-C/
116
- sleep 1
117
-
118
- s.write "GET / HTTP/1.1\r\n\r\n"
119
-
120
- assert_raises Errno::ECONNRESET do
121
- Timeout.timeout(2) do
122
- raise Errno::ECONNRESET unless s.read(2)
123
- end
124
- end
125
-
126
- s = TCPSocket.new "localhost", @tcp_port
127
- s << "GET / HTTP/1.0\r\n\r\n"
128
- assert_equal "Hello World", s.read.split("\r\n").last
129
- end
130
-
131
- def notest_restart_closes_keepalive_sockets_workers
132
- server("-q -w 2 test/hello.ru")
133
-
134
- s = TCPSocket.new "localhost", @tcp_port
135
- s << "GET / HTTP/1.1\r\n\r\n"
136
- true until s.gets == "\r\n"
137
-
138
- s.readpartial(20)
139
- signal :USR2
140
-
141
- true while @server.gets =~ /Ctrl-C/
142
- sleep 1
143
-
144
- s.write "GET / HTTP/1.1\r\n\r\n"
145
-
146
- assert_raises Errno::ECONNRESET do
147
- Timeout.timeout(2) do
148
- raise Errno::ECONNRESET unless s.read(2)
149
- end
150
- end
151
-
152
- s = TCPSocket.new "localhost", @tcp_port
153
- s << "GET / HTTP/1.0\r\n\r\n"
154
- assert_equal "Hello World", s.read.split("\r\n").last
155
- end
156
-
157
- def test_bad_query_string_outputs_400
158
- server "-q test/hello.ru 2>&1"
159
-
160
- s = TCPSocket.new "localhost", @tcp_port
161
- s << "GET /?h=% HTTP/1.0\r\n\r\n"
162
- data = s.read
163
- assert_equal "HTTP/1.1 400 Bad Request\r\n\r\n", data
164
- end
165
- end unless ENV['TRAVIS']
@@ -1,38 +0,0 @@
1
- require 'puma/io_buffer'
2
- require 'test/unit'
3
-
4
- class TestIOBuffer < Test::Unit::TestCase
5
- attr_accessor :iobuf
6
- def setup
7
- self.iobuf = Puma::IOBuffer.new
8
- end
9
-
10
- def test_initial_size
11
- assert_equal 0, iobuf.used
12
- assert iobuf.capacity > 0
13
- end
14
-
15
- def test_append_op
16
- iobuf << "abc"
17
- assert_equal "abc", iobuf.to_s
18
- iobuf << "123"
19
- assert_equal "abc123", iobuf.to_s
20
- assert_equal 6, iobuf.used
21
- end
22
-
23
- def test_append
24
- expected = "mary had a little lamb"
25
- iobuf.append("mary", " ", "had ", "a little", " lamb")
26
- assert_equal expected, iobuf.to_s
27
- assert_equal expected.length, iobuf.used
28
- end
29
-
30
- def test_reset
31
- iobuf << "content"
32
- assert_equal "content", iobuf.to_s
33
- iobuf.reset
34
- assert_equal 0, iobuf.used
35
- assert_equal "", iobuf.to_s
36
- end
37
-
38
- end
data/test/test_minissl.rb DELETED
@@ -1,25 +0,0 @@
1
- require 'test/unit'
2
-
3
- unless defined? JRUBY_VERSION
4
-
5
- require 'puma'
6
- require 'puma/minissl'
7
-
8
- class TestMiniSSL < Test::Unit::TestCase
9
-
10
- def test_raises_with_invalid_key_file
11
- ctx = Puma::MiniSSL::Context.new
12
-
13
- exception = assert_raise(ArgumentError) { ctx.key = "/no/such/key" }
14
- assert_equal("No such key file '/no/such/key'", exception.message)
15
- end
16
-
17
- def test_raises_with_invalid_cert_file
18
- ctx = Puma::MiniSSL::Context.new
19
-
20
- exception = assert_raise(ArgumentError) { ctx.cert = "/no/such/cert" }
21
- assert_equal("No such cert file '/no/such/cert'", exception.message)
22
- end
23
- end
24
-
25
- end
data/test/test_null_io.rb DELETED
@@ -1,31 +0,0 @@
1
- require 'puma/null_io'
2
- require 'test/unit'
3
-
4
- class TestNullIO < Test::Unit::TestCase
5
- attr_accessor :nio
6
- def setup
7
- self.nio = Puma::NullIO.new
8
- end
9
-
10
- def test_read_with_no_arguments
11
- assert_equal "", nio.read
12
- end
13
-
14
- def test_read_with_nil_length
15
- assert_equal "", nio.read(nil)
16
- end
17
-
18
- def test_read_with_zero_length
19
- assert_equal "", nio.read(0)
20
- end
21
-
22
- def test_read_with_positive_integer_length
23
- assert_nil nio.read(1)
24
- end
25
-
26
- def test_read_with_length_and_buffer
27
- buf = ""
28
- assert_nil nio.read(1,buf)
29
- assert_equal "", buf
30
- end
31
- end
@@ -1,238 +0,0 @@
1
- require 'puma'
2
- require 'test/unit'
3
- require 'timeout'
4
-
5
- class TestPersistent < Test::Unit::TestCase
6
- def setup
7
- @valid_request = "GET / HTTP/1.1\r\nHost: test.com\r\nContent-Type: text/plain\r\n\r\n"
8
- @close_request = "GET / HTTP/1.1\r\nHost: test.com\r\nContent-Type: text/plain\r\nConnection: close\r\n\r\n"
9
- @http10_request = "GET / HTTP/1.0\r\nHost: test.com\r\nContent-Type: text/plain\r\n\r\n"
10
- @keep_request = "GET / HTTP/1.0\r\nHost: test.com\r\nContent-Type: text/plain\r\nConnection: Keep-Alive\r\n\r\n"
11
-
12
- @valid_post = "POST / HTTP/1.1\r\nHost: test.com\r\nContent-Type: text/plain\r\nContent-Length: 5\r\n\r\nhello"
13
- @valid_no_body = "GET / HTTP/1.1\r\nHost: test.com\r\nX-Status: 204\r\nContent-Type: text/plain\r\n\r\n"
14
-
15
- @headers = { "X-Header" => "Works" }
16
- @body = ["Hello"]
17
- @inputs = []
18
-
19
- @simple = lambda do |env|
20
- @inputs << env['rack.input']
21
- status = Integer(env['HTTP_X_STATUS'] || 200)
22
- [status, @headers, @body]
23
- end
24
-
25
- @host = "127.0.0.1"
26
- @port = 9988
27
-
28
- @server = Puma::Server.new @simple
29
- @server.add_tcp_listener "127.0.0.1", 9988
30
- @server.max_threads = 1
31
- @server.run
32
-
33
- @client = TCPSocket.new "127.0.0.1", 9988
34
- end
35
-
36
- def teardown
37
- @client.close
38
- @server.stop(true)
39
- end
40
-
41
- def lines(count, s=@client)
42
- str = ""
43
- timeout(5) do
44
- count.times { str << s.gets }
45
- end
46
- str
47
- end
48
-
49
- def test_one_with_content_length
50
- @client << @valid_request
51
- sz = @body[0].size.to_s
52
-
53
- assert_equal "HTTP/1.1 200 OK\r\nX-Header: Works\r\nContent-Length: #{sz}\r\n\r\n", lines(4)
54
- assert_equal "Hello", @client.read(5)
55
- end
56
-
57
- def test_two_back_to_back
58
- @client << @valid_request
59
- sz = @body[0].size.to_s
60
-
61
- assert_equal "HTTP/1.1 200 OK\r\nX-Header: Works\r\nContent-Length: #{sz}\r\n\r\n", lines(4)
62
- assert_equal "Hello", @client.read(5)
63
-
64
- @client << @valid_request
65
- sz = @body[0].size.to_s
66
-
67
- assert_equal "HTTP/1.1 200 OK\r\nX-Header: Works\r\nContent-Length: #{sz}\r\n\r\n", lines(4)
68
- assert_equal "Hello", @client.read(5)
69
- end
70
-
71
- def test_post_then_get
72
- @client << @valid_post
73
- sz = @body[0].size.to_s
74
-
75
- assert_equal "HTTP/1.1 200 OK\r\nX-Header: Works\r\nContent-Length: #{sz}\r\n\r\n", lines(4)
76
- assert_equal "Hello", @client.read(5)
77
-
78
- @client << @valid_request
79
- sz = @body[0].size.to_s
80
-
81
- assert_equal "HTTP/1.1 200 OK\r\nX-Header: Works\r\nContent-Length: #{sz}\r\n\r\n", lines(4)
82
- assert_equal "Hello", @client.read(5)
83
- end
84
-
85
- def test_no_body_then_get
86
- @client << @valid_no_body
87
- assert_equal "HTTP/1.1 204 No Content\r\nX-Header: Works\r\n\r\n", lines(3)
88
-
89
- @client << @valid_request
90
- sz = @body[0].size.to_s
91
-
92
- assert_equal "HTTP/1.1 200 OK\r\nX-Header: Works\r\nContent-Length: #{sz}\r\n\r\n", lines(4)
93
- assert_equal "Hello", @client.read(5)
94
- end
95
-
96
- def test_chunked
97
- @body << "Chunked"
98
-
99
- @client << @valid_request
100
-
101
- assert_equal "HTTP/1.1 200 OK\r\nX-Header: Works\r\nTransfer-Encoding: chunked\r\n\r\n5\r\nHello\r\n7\r\nChunked\r\n0\r\n\r\n", lines(10)
102
- end
103
-
104
- def test_no_chunked_in_http10
105
- @body << "Chunked"
106
-
107
- @client << @http10_request
108
-
109
- assert_equal "HTTP/1.0 200 OK\r\nX-Header: Works\r\nConnection: close\r\n\r\n", lines(4)
110
- assert_equal "HelloChunked", @client.read
111
- end
112
-
113
- def test_hex
114
- str = "This is longer and will be in hex"
115
- @body << str
116
-
117
- @client << @valid_request
118
-
119
- assert_equal "HTTP/1.1 200 OK\r\nX-Header: Works\r\nTransfer-Encoding: chunked\r\n\r\n5\r\nHello\r\n#{str.size.to_s(16)}\r\n#{str}\r\n0\r\n\r\n", lines(10)
120
-
121
- end
122
-
123
- def test_client11_close
124
- @client << @close_request
125
- sz = @body[0].size.to_s
126
-
127
- assert_equal "HTTP/1.1 200 OK\r\nX-Header: Works\r\nConnection: close\r\nContent-Length: #{sz}\r\n\r\n", lines(5)
128
- assert_equal "Hello", @client.read(5)
129
- end
130
-
131
- def test_client10_close
132
- @client << @http10_request
133
- sz = @body[0].size.to_s
134
-
135
- assert_equal "HTTP/1.0 200 OK\r\nX-Header: Works\r\nConnection: close\r\nContent-Length: #{sz}\r\n\r\n", lines(5)
136
- assert_equal "Hello", @client.read(5)
137
- end
138
-
139
- def test_one_with_keep_alive_header
140
- @client << @keep_request
141
- sz = @body[0].size.to_s
142
-
143
- assert_equal "HTTP/1.0 200 OK\r\nX-Header: Works\r\nConnection: Keep-Alive\r\nContent-Length: #{sz}\r\n\r\n", lines(5)
144
- assert_equal "Hello", @client.read(5)
145
- end
146
-
147
- def test_persistent_timeout
148
- @server.persistent_timeout = 2
149
- @client << @valid_request
150
- sz = @body[0].size.to_s
151
-
152
- assert_equal "HTTP/1.1 200 OK\r\nX-Header: Works\r\nContent-Length: #{sz}\r\n\r\n", lines(4)
153
- assert_equal "Hello", @client.read(5)
154
-
155
- sleep 3
156
-
157
- assert_raises EOFError do
158
- @client.read_nonblock(1)
159
- end
160
- end
161
-
162
- def test_app_sets_content_length
163
- @body = ["hello", " world"]
164
- @headers['Content-Length'] = "11"
165
-
166
- @client << @valid_request
167
-
168
- assert_equal "HTTP/1.1 200 OK\r\nX-Header: Works\r\nContent-Length: 11\r\n\r\n",
169
- lines(4)
170
- assert_equal "hello world", @client.read(11)
171
- end
172
-
173
- def test_allow_app_to_chunk_itself
174
- @headers = {'Transfer-Encoding' => "chunked" }
175
-
176
- @body = ["5\r\nhello\r\n0\r\n\r\n"]
177
-
178
- @client << @valid_request
179
-
180
- assert_equal "HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n5\r\nhello\r\n0\r\n\r\n", lines(7)
181
- end
182
-
183
-
184
- def test_two_requests_in_one_chunk
185
- @server.persistent_timeout = 3
186
-
187
- req = @valid_request.to_s
188
- req << "GET /second HTTP/1.1\r\nHost: test.com\r\nContent-Type: text/plain\r\n\r\n"
189
-
190
- @client << req
191
-
192
- sz = @body[0].size.to_s
193
-
194
- assert_equal "HTTP/1.1 200 OK\r\nX-Header: Works\r\nContent-Length: #{sz}\r\n\r\n", lines(4)
195
- assert_equal "Hello", @client.read(5)
196
-
197
- assert_equal "HTTP/1.1 200 OK\r\nX-Header: Works\r\nContent-Length: #{sz}\r\n\r\n", lines(4)
198
- assert_equal "Hello", @client.read(5)
199
- end
200
-
201
- def test_second_request_not_in_first_req_body
202
- @server.persistent_timeout = 3
203
-
204
- req = @valid_request.to_s
205
- req << "GET /second HTTP/1.1\r\nHost: test.com\r\nContent-Type: text/plain\r\n\r\n"
206
-
207
- @client << req
208
-
209
- sz = @body[0].size.to_s
210
-
211
- assert_equal "HTTP/1.1 200 OK\r\nX-Header: Works\r\nContent-Length: #{sz}\r\n\r\n", lines(4)
212
- assert_equal "Hello", @client.read(5)
213
-
214
- assert_equal "HTTP/1.1 200 OK\r\nX-Header: Works\r\nContent-Length: #{sz}\r\n\r\n", lines(4)
215
- assert_equal "Hello", @client.read(5)
216
-
217
- assert_kind_of Puma::NullIO, @inputs[0]
218
- assert_kind_of Puma::NullIO, @inputs[1]
219
- end
220
-
221
- def test_keepalive_doesnt_starve_clients
222
- sz = @body[0].size.to_s
223
-
224
- @client << @valid_request
225
-
226
- c2 = TCPSocket.new @host, @port
227
- c2 << @valid_request
228
-
229
- out = IO.select([c2], nil, nil, 1)
230
-
231
- assert out, "select returned nil"
232
- assert_equal c2, out.first.first
233
-
234
- assert_equal "HTTP/1.1 200 OK\r\nX-Header: Works\r\nContent-Length: #{sz}\r\n\r\n", lines(4, c2)
235
- assert_equal "Hello", c2.read(5)
236
- end
237
-
238
- end