puma 0.8.2-java
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/.gemtest +0 -0
- data/COPYING +55 -0
- data/Gemfile +6 -0
- data/History.txt +69 -0
- data/LICENSE +26 -0
- data/Manifest.txt +60 -0
- data/README.md +60 -0
- data/Rakefile +12 -0
- data/TODO +5 -0
- data/bin/puma +15 -0
- data/examples/builder.rb +29 -0
- data/examples/camping/README +3 -0
- data/examples/camping/blog.rb +294 -0
- data/examples/camping/tepee.rb +149 -0
- data/examples/httpd.conf +474 -0
- data/examples/mime.yaml +3 -0
- data/examples/mongrel.conf +9 -0
- data/examples/monitrc +57 -0
- data/examples/random_thrash.rb +19 -0
- data/examples/simpletest.rb +52 -0
- data/examples/webrick_compare.rb +20 -0
- data/ext/puma_http11/PumaHttp11Service.java +13 -0
- data/ext/puma_http11/ext_help.h +15 -0
- data/ext/puma_http11/extconf.rb +5 -0
- data/ext/puma_http11/http11_parser.c +1225 -0
- data/ext/puma_http11/http11_parser.h +63 -0
- data/ext/puma_http11/http11_parser.java.rl +161 -0
- data/ext/puma_http11/http11_parser.rl +146 -0
- data/ext/puma_http11/http11_parser_common.rl +54 -0
- data/ext/puma_http11/org/jruby/puma/Http11.java +225 -0
- data/ext/puma_http11/org/jruby/puma/Http11Parser.java +488 -0
- data/ext/puma_http11/puma_http11.c +482 -0
- data/lib/puma.rb +18 -0
- data/lib/puma/cli.rb +164 -0
- data/lib/puma/const.rb +132 -0
- data/lib/puma/events.rb +36 -0
- data/lib/puma/gems.rb +20 -0
- data/lib/puma/mime_types.yml +616 -0
- data/lib/puma/rack_patch.rb +22 -0
- data/lib/puma/server.rb +429 -0
- data/lib/puma/thread_pool.rb +95 -0
- data/lib/puma/utils.rb +44 -0
- data/lib/puma_http11.jar +0 -0
- data/lib/rack/handler/puma.rb +48 -0
- data/puma.gemspec +40 -0
- data/tasks/gem.rake +24 -0
- data/tasks/java.rake +12 -0
- data/tasks/native.rake +36 -0
- data/tasks/ragel.rake +24 -0
- data/test/lobster.ru +4 -0
- data/test/mime.yaml +3 -0
- data/test/test_cli.rb +19 -0
- data/test/test_http10.rb +27 -0
- data/test/test_http11.rb +151 -0
- data/test/test_persistent.rb +205 -0
- data/test/test_rack_handler.rb +10 -0
- data/test/test_rack_server.rb +122 -0
- data/test/test_thread_pool.rb +102 -0
- data/test/test_unix_socket.rb +37 -0
- data/test/test_ws.rb +97 -0
- data/test/testhelp.rb +41 -0
- data/tools/trickletest.rb +45 -0
- metadata +163 -0
@@ -0,0 +1,205 @@
|
|
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
|
+
|
14
|
+
@headers = { "X-Header" => "Works" }
|
15
|
+
@body = ["Hello"]
|
16
|
+
@inputs = []
|
17
|
+
|
18
|
+
@simple = lambda do |env|
|
19
|
+
@inputs << env['rack.input']
|
20
|
+
[200, @headers, @body]
|
21
|
+
end
|
22
|
+
|
23
|
+
@server = Puma::Server.new @simple
|
24
|
+
@server.add_tcp_listener "127.0.0.1", 9988
|
25
|
+
@server.run
|
26
|
+
|
27
|
+
@client = TCPSocket.new "127.0.0.1", 9988
|
28
|
+
end
|
29
|
+
|
30
|
+
def teardown
|
31
|
+
@client.close
|
32
|
+
@server.stop(true)
|
33
|
+
end
|
34
|
+
|
35
|
+
def lines(count, s=@client)
|
36
|
+
str = ""
|
37
|
+
timeout(5) do
|
38
|
+
count.times { str << s.gets }
|
39
|
+
end
|
40
|
+
str
|
41
|
+
end
|
42
|
+
|
43
|
+
def test_one_with_content_length
|
44
|
+
@client << @valid_request
|
45
|
+
sz = @body[0].size.to_s
|
46
|
+
|
47
|
+
assert_equal "HTTP/1.1 200 OK\r\nX-Header: Works\r\nContent-Length: #{sz}\r\n\r\n", lines(4)
|
48
|
+
assert_equal "Hello", @client.read(5)
|
49
|
+
end
|
50
|
+
|
51
|
+
def test_two_back_to_back
|
52
|
+
@client << @valid_request
|
53
|
+
sz = @body[0].size.to_s
|
54
|
+
|
55
|
+
assert_equal "HTTP/1.1 200 OK\r\nX-Header: Works\r\nContent-Length: #{sz}\r\n\r\n", lines(4)
|
56
|
+
assert_equal "Hello", @client.read(5)
|
57
|
+
|
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
|
+
end
|
64
|
+
|
65
|
+
def test_post_then_get
|
66
|
+
@client << @valid_post
|
67
|
+
sz = @body[0].size.to_s
|
68
|
+
|
69
|
+
assert_equal "HTTP/1.1 200 OK\r\nX-Header: Works\r\nContent-Length: #{sz}\r\n\r\n", lines(4)
|
70
|
+
assert_equal "Hello", @client.read(5)
|
71
|
+
|
72
|
+
@client << @valid_request
|
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
|
+
end
|
78
|
+
|
79
|
+
def test_chunked
|
80
|
+
@body << "Chunked"
|
81
|
+
|
82
|
+
@client << @valid_request
|
83
|
+
|
84
|
+
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)
|
85
|
+
end
|
86
|
+
|
87
|
+
def test_no_chunked_in_http10
|
88
|
+
@body << "Chunked"
|
89
|
+
|
90
|
+
@client << @http10_request
|
91
|
+
|
92
|
+
assert_equal "HTTP/1.0 200 OK\r\nX-Header: Works\r\nConnection: close\r\n\r\n", lines(4)
|
93
|
+
assert_equal "HelloChunked", @client.read
|
94
|
+
end
|
95
|
+
|
96
|
+
def test_hex
|
97
|
+
str = "This is longer and will be in hex"
|
98
|
+
@body << str
|
99
|
+
|
100
|
+
@client << @valid_request
|
101
|
+
|
102
|
+
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)
|
103
|
+
|
104
|
+
end
|
105
|
+
|
106
|
+
def test_client11_close
|
107
|
+
@client << @close_request
|
108
|
+
sz = @body[0].size.to_s
|
109
|
+
|
110
|
+
assert_equal "HTTP/1.1 200 OK\r\nX-Header: Works\r\nConnection: close\r\nContent-Length: #{sz}\r\n\r\n", lines(5)
|
111
|
+
assert_equal "Hello", @client.read(5)
|
112
|
+
end
|
113
|
+
|
114
|
+
def test_client10_close
|
115
|
+
@client << @http10_request
|
116
|
+
sz = @body[0].size.to_s
|
117
|
+
|
118
|
+
assert_equal "HTTP/1.0 200 OK\r\nX-Header: Works\r\nConnection: close\r\nContent-Length: #{sz}\r\n\r\n", lines(5)
|
119
|
+
assert_equal "Hello", @client.read(5)
|
120
|
+
end
|
121
|
+
|
122
|
+
def test_one_with_keep_alive_header
|
123
|
+
@client << @keep_request
|
124
|
+
sz = @body[0].size.to_s
|
125
|
+
|
126
|
+
assert_equal "HTTP/1.0 200 OK\r\nX-Header: Works\r\nContent-Length: #{sz}\r\n\r\n", lines(4)
|
127
|
+
assert_equal "Hello", @client.read(5)
|
128
|
+
end
|
129
|
+
|
130
|
+
def test_persistent_timeout
|
131
|
+
@server.persistent_timeout = 2
|
132
|
+
@client << @valid_request
|
133
|
+
sz = @body[0].size.to_s
|
134
|
+
|
135
|
+
assert_equal "HTTP/1.1 200 OK\r\nX-Header: Works\r\nContent-Length: #{sz}\r\n\r\n", lines(4)
|
136
|
+
assert_equal "Hello", @client.read(5)
|
137
|
+
|
138
|
+
sleep 3
|
139
|
+
|
140
|
+
assert_raises EOFError do
|
141
|
+
@client.read_nonblock(1)
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
def test_app_sets_content_length
|
146
|
+
@body = ["hello", " world"]
|
147
|
+
@headers['Content-Length'] = "11"
|
148
|
+
|
149
|
+
@client << @valid_request
|
150
|
+
|
151
|
+
assert_equal "HTTP/1.1 200 OK\r\nX-Header: Works\r\nContent-Length: 11\r\n\r\n",
|
152
|
+
lines(4)
|
153
|
+
assert_equal "hello world", @client.read(11)
|
154
|
+
end
|
155
|
+
|
156
|
+
def test_allow_app_to_chunk_itself
|
157
|
+
@headers = {'Transfer-Encoding' => "chunked" }
|
158
|
+
|
159
|
+
@body = ["5\r\nhello\r\n0\r\n\r\n"]
|
160
|
+
|
161
|
+
@client << @valid_request
|
162
|
+
|
163
|
+
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)
|
164
|
+
end
|
165
|
+
|
166
|
+
|
167
|
+
def test_two_requests_in_one_chunk
|
168
|
+
@server.persistent_timeout = 3
|
169
|
+
|
170
|
+
req = @valid_request.to_s
|
171
|
+
req << "GET /second HTTP/1.1\r\nHost: test.com\r\nContent-Type: text/plain\r\n\r\n"
|
172
|
+
|
173
|
+
@client << req
|
174
|
+
|
175
|
+
sz = @body[0].size.to_s
|
176
|
+
|
177
|
+
assert_equal "HTTP/1.1 200 OK\r\nX-Header: Works\r\nContent-Length: #{sz}\r\n\r\n", lines(4)
|
178
|
+
assert_equal "Hello", @client.read(5)
|
179
|
+
|
180
|
+
assert_equal "HTTP/1.1 200 OK\r\nX-Header: Works\r\nContent-Length: #{sz}\r\n\r\n", lines(4)
|
181
|
+
assert_equal "Hello", @client.read(5)
|
182
|
+
end
|
183
|
+
|
184
|
+
def test_second_request_not_in_first_req_body
|
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
|
+
|
200
|
+
assert_equal "", @inputs[0].string
|
201
|
+
assert_equal "", @inputs[1].string
|
202
|
+
|
203
|
+
end
|
204
|
+
|
205
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
|
3
|
+
class TestPumaUnixSocket < Test::Unit::TestCase
|
4
|
+
def test_handler
|
5
|
+
handler = Rack::Handler.get(:puma)
|
6
|
+
assert_equal Rack::Handler::Puma, handler
|
7
|
+
handler = Rack::Handler.get('Puma')
|
8
|
+
assert_equal Rack::Handler::Puma, handler
|
9
|
+
end
|
10
|
+
end
|
@@ -0,0 +1,122 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'puma'
|
3
|
+
require 'rack/lint'
|
4
|
+
require 'test/testhelp'
|
5
|
+
require 'rack/commonlogger'
|
6
|
+
require 'puma/rack_patch'
|
7
|
+
|
8
|
+
class TestRackServer < Test::Unit::TestCase
|
9
|
+
|
10
|
+
class ErrorChecker
|
11
|
+
def initialize(app)
|
12
|
+
@app = app
|
13
|
+
@exception = nil
|
14
|
+
@env = nil
|
15
|
+
end
|
16
|
+
|
17
|
+
attr_reader :exception, :env
|
18
|
+
|
19
|
+
def call(env)
|
20
|
+
begin
|
21
|
+
@env = env
|
22
|
+
return @app.call(env)
|
23
|
+
rescue Exception => e
|
24
|
+
@exception = e
|
25
|
+
|
26
|
+
[
|
27
|
+
500,
|
28
|
+
{ "X-Exception" => e.message, "X-Exception-Class" => e.class.to_s },
|
29
|
+
["Error detected"]
|
30
|
+
]
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
class ServerLint < Rack::Lint
|
36
|
+
def call(env)
|
37
|
+
assert("No env given") { env }
|
38
|
+
check_env env
|
39
|
+
|
40
|
+
@app.call(env)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def setup
|
45
|
+
@valid_request = "GET / HTTP/1.1\r\nHost: test.com\r\nContent-Type: text/plain\r\n\r\n"
|
46
|
+
|
47
|
+
@simple = lambda { |env| [200, { "X-Header" => "Works" }, ["Hello"]] }
|
48
|
+
@server = Puma::Server.new @simple
|
49
|
+
@server.add_tcp_listener "127.0.0.1", 9998
|
50
|
+
|
51
|
+
@stopped = false
|
52
|
+
end
|
53
|
+
|
54
|
+
def stop
|
55
|
+
@server.stop(true)
|
56
|
+
@stopped = true
|
57
|
+
end
|
58
|
+
|
59
|
+
def teardown
|
60
|
+
@server.stop(true) unless @stopped
|
61
|
+
end
|
62
|
+
|
63
|
+
def test_lint
|
64
|
+
@checker = ErrorChecker.new ServerLint.new(@simple)
|
65
|
+
@server.app = @checker
|
66
|
+
|
67
|
+
@server.run
|
68
|
+
|
69
|
+
hit(['http://localhost:9998/test'])
|
70
|
+
|
71
|
+
stop
|
72
|
+
|
73
|
+
if exc = @checker.exception
|
74
|
+
raise exc
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def test_path_info
|
79
|
+
input = nil
|
80
|
+
@server.app = lambda { |env| input = env; @simple.call(env) }
|
81
|
+
@server.run
|
82
|
+
|
83
|
+
hit(['http://localhost:9998/test/a/b/c'])
|
84
|
+
|
85
|
+
stop
|
86
|
+
|
87
|
+
assert_equal "/test/a/b/c", input['PATH_INFO']
|
88
|
+
end
|
89
|
+
|
90
|
+
def test_after_reply
|
91
|
+
closed = false
|
92
|
+
|
93
|
+
@server.app = lambda do |env|
|
94
|
+
env['rack.after_reply'] << lambda { closed = true }
|
95
|
+
@simple.call(env)
|
96
|
+
end
|
97
|
+
|
98
|
+
@server.run
|
99
|
+
|
100
|
+
hit(['http://localhost:9998/test'])
|
101
|
+
|
102
|
+
stop
|
103
|
+
|
104
|
+
assert_equal true, closed
|
105
|
+
end
|
106
|
+
|
107
|
+
def test_common_logger
|
108
|
+
log = StringIO.new
|
109
|
+
|
110
|
+
logger = Rack::CommonLogger.new(@simple, log)
|
111
|
+
|
112
|
+
@server.app = logger
|
113
|
+
|
114
|
+
@server.run
|
115
|
+
|
116
|
+
hit(['http://localhost:9998/test'])
|
117
|
+
|
118
|
+
stop
|
119
|
+
|
120
|
+
assert_match %r!GET /test HTTP/1\.1!, log.string
|
121
|
+
end
|
122
|
+
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
|
3
|
+
require 'puma/thread_pool'
|
4
|
+
|
5
|
+
class TestThreadPool < Test::Unit::TestCase
|
6
|
+
|
7
|
+
def teardown
|
8
|
+
@pool.shutdown if @pool
|
9
|
+
end
|
10
|
+
|
11
|
+
def new_pool(min, max, &blk)
|
12
|
+
blk = proc { } unless blk
|
13
|
+
@pool = Puma::ThreadPool.new(min, max, &blk)
|
14
|
+
end
|
15
|
+
|
16
|
+
def pause
|
17
|
+
sleep 0.2
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_append_spawns
|
21
|
+
saw = []
|
22
|
+
|
23
|
+
pool = new_pool(0, 1) do |work|
|
24
|
+
saw << work
|
25
|
+
end
|
26
|
+
|
27
|
+
pool << 1
|
28
|
+
|
29
|
+
pause
|
30
|
+
|
31
|
+
assert_equal [1], saw
|
32
|
+
assert_equal 1, pool.spawned
|
33
|
+
end
|
34
|
+
|
35
|
+
def test_append_queues_on_max
|
36
|
+
finish = false
|
37
|
+
pool = new_pool(0, 1) { Thread.pass until finish }
|
38
|
+
|
39
|
+
pool << 1
|
40
|
+
pool << 2
|
41
|
+
pool << 3
|
42
|
+
|
43
|
+
pause
|
44
|
+
|
45
|
+
assert_equal 2, pool.backlog
|
46
|
+
|
47
|
+
finish = true
|
48
|
+
end
|
49
|
+
|
50
|
+
def test_trim
|
51
|
+
pool = new_pool(0, 1)
|
52
|
+
|
53
|
+
pool << 1
|
54
|
+
|
55
|
+
pause
|
56
|
+
|
57
|
+
assert_equal 1, pool.spawned
|
58
|
+
pool.trim
|
59
|
+
|
60
|
+
pause
|
61
|
+
assert_equal 0, pool.spawned
|
62
|
+
end
|
63
|
+
|
64
|
+
def test_trim_leaves_min
|
65
|
+
finish = false
|
66
|
+
pool = new_pool(1, 2) { Thread.pass until finish }
|
67
|
+
|
68
|
+
pool << 1
|
69
|
+
pool << 2
|
70
|
+
|
71
|
+
finish = true
|
72
|
+
|
73
|
+
assert_equal 2, pool.spawned
|
74
|
+
pool.trim
|
75
|
+
pause
|
76
|
+
|
77
|
+
assert_equal 1, pool.spawned
|
78
|
+
pool.trim
|
79
|
+
pause
|
80
|
+
|
81
|
+
assert_equal 1, pool.spawned
|
82
|
+
|
83
|
+
end
|
84
|
+
|
85
|
+
def test_trim_doesnt_overtrim
|
86
|
+
finish = false
|
87
|
+
pool = new_pool(1, 2) { Thread.pass until finish }
|
88
|
+
|
89
|
+
pool << 1
|
90
|
+
pool << 2
|
91
|
+
|
92
|
+
assert_equal 2, pool.spawned
|
93
|
+
pool.trim
|
94
|
+
pool.trim
|
95
|
+
|
96
|
+
finish = true
|
97
|
+
|
98
|
+
pause
|
99
|
+
|
100
|
+
assert_equal 1, pool.spawned
|
101
|
+
end
|
102
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'puma/server'
|
3
|
+
|
4
|
+
require 'socket'
|
5
|
+
|
6
|
+
# UNIX sockets are not recommended on JRuby
|
7
|
+
unless defined?(JRUBY_VERSION)
|
8
|
+
class TestPumaUnixSocket < Test::Unit::TestCase
|
9
|
+
|
10
|
+
App = lambda { |env| [200, {}, ["Works"]] }
|
11
|
+
|
12
|
+
Path = "test/puma.sock"
|
13
|
+
|
14
|
+
def setup
|
15
|
+
@server = Puma::Server.new App
|
16
|
+
@server.add_unix_listener Path
|
17
|
+
@server.run
|
18
|
+
end
|
19
|
+
|
20
|
+
def teardown
|
21
|
+
@server.stop(true)
|
22
|
+
File.unlink Path if File.exists? Path
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_server
|
26
|
+
sock = UNIXSocket.new Path
|
27
|
+
|
28
|
+
sock << "GET / HTTP/1.0\r\nHost: blah.com\r\n\r\n"
|
29
|
+
|
30
|
+
expected = "HTTP/1.0 200 OK\r\nConnection: close\r\nContent-Length: 5\r\n\r\nWorks"
|
31
|
+
|
32
|
+
assert_equal expected, sock.read(expected.size)
|
33
|
+
|
34
|
+
sock.close
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|