puma-simon 3.7.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.github/issue_template.md +20 -0
- data/.gitignore +18 -0
- data/.hoeignore +12 -0
- data/.travis.yml +29 -0
- data/DEPLOYMENT.md +91 -0
- data/Gemfile +12 -0
- data/History.md +1254 -0
- data/LICENSE +26 -0
- data/Manifest.txt +78 -0
- data/README.md +353 -0
- data/Rakefile +158 -0
- data/Release.md +9 -0
- data/bin/puma +10 -0
- data/bin/puma-wild +31 -0
- data/bin/pumactl +12 -0
- data/docs/nginx.md +80 -0
- data/docs/signals.md +43 -0
- data/docs/systemd.md +197 -0
- data/examples/CA/cacert.pem +23 -0
- data/examples/CA/newcerts/cert_1.pem +19 -0
- data/examples/CA/newcerts/cert_2.pem +19 -0
- data/examples/CA/private/cakeypair.pem +30 -0
- data/examples/CA/serial +1 -0
- data/examples/config.rb +200 -0
- data/examples/plugins/redis_stop_puma.rb +46 -0
- data/examples/puma/cert_puma.pem +19 -0
- data/examples/puma/client-certs/ca.crt +19 -0
- data/examples/puma/client-certs/ca.key +27 -0
- data/examples/puma/client-certs/client.crt +19 -0
- data/examples/puma/client-certs/client.key +27 -0
- data/examples/puma/client-certs/client_expired.crt +19 -0
- data/examples/puma/client-certs/client_expired.key +27 -0
- data/examples/puma/client-certs/client_unknown.crt +19 -0
- data/examples/puma/client-certs/client_unknown.key +27 -0
- data/examples/puma/client-certs/generate.rb +78 -0
- data/examples/puma/client-certs/keystore.jks +0 -0
- data/examples/puma/client-certs/server.crt +19 -0
- data/examples/puma/client-certs/server.key +27 -0
- data/examples/puma/client-certs/server.p12 +0 -0
- data/examples/puma/client-certs/unknown_ca.crt +19 -0
- data/examples/puma/client-certs/unknown_ca.key +27 -0
- data/examples/puma/csr_puma.pem +11 -0
- data/examples/puma/keystore.jks +0 -0
- data/examples/puma/puma_keypair.pem +15 -0
- data/examples/qc_config.rb +13 -0
- data/ext/puma_http11/PumaHttp11Service.java +17 -0
- data/ext/puma_http11/ext_help.h +15 -0
- data/ext/puma_http11/extconf.rb +15 -0
- data/ext/puma_http11/http11_parser.c +1069 -0
- data/ext/puma_http11/http11_parser.h +65 -0
- data/ext/puma_http11/http11_parser.java.rl +161 -0
- data/ext/puma_http11/http11_parser.rl +147 -0
- data/ext/puma_http11/http11_parser_common.rl +54 -0
- data/ext/puma_http11/io_buffer.c +155 -0
- data/ext/puma_http11/mini_ssl.c +457 -0
- data/ext/puma_http11/org/jruby/puma/Http11.java +234 -0
- data/ext/puma_http11/org/jruby/puma/Http11Parser.java +473 -0
- data/ext/puma_http11/org/jruby/puma/MiniSSL.java +339 -0
- data/ext/puma_http11/puma_http11.c +500 -0
- data/gemfiles/2.1-Gemfile +12 -0
- data/lib/puma.rb +15 -0
- data/lib/puma/accept_nonblock.rb +23 -0
- data/lib/puma/app/status.rb +66 -0
- data/lib/puma/binder.rb +402 -0
- data/lib/puma/cli.rb +220 -0
- data/lib/puma/client.rb +434 -0
- data/lib/puma/cluster.rb +510 -0
- data/lib/puma/commonlogger.rb +106 -0
- data/lib/puma/compat.rb +14 -0
- data/lib/puma/configuration.rb +364 -0
- data/lib/puma/const.rb +224 -0
- data/lib/puma/control_cli.rb +259 -0
- data/lib/puma/convenient.rb +23 -0
- data/lib/puma/daemon_ext.rb +31 -0
- data/lib/puma/delegation.rb +11 -0
- data/lib/puma/detect.rb +13 -0
- data/lib/puma/dsl.rb +486 -0
- data/lib/puma/events.rb +152 -0
- data/lib/puma/io_buffer.rb +7 -0
- data/lib/puma/java_io_buffer.rb +45 -0
- data/lib/puma/jruby_restart.rb +83 -0
- data/lib/puma/launcher.rb +410 -0
- data/lib/puma/minissl.rb +221 -0
- data/lib/puma/null_io.rb +42 -0
- data/lib/puma/plugin.rb +115 -0
- data/lib/puma/plugin/tmp_restart.rb +35 -0
- data/lib/puma/rack/backports/uri/common_193.rb +33 -0
- data/lib/puma/rack/builder.rb +298 -0
- data/lib/puma/rack/urlmap.rb +91 -0
- data/lib/puma/rack_default.rb +7 -0
- data/lib/puma/reactor.rb +210 -0
- data/lib/puma/runner.rb +171 -0
- data/lib/puma/server.rb +949 -0
- data/lib/puma/single.rb +112 -0
- data/lib/puma/state_file.rb +29 -0
- data/lib/puma/tcp_logger.rb +39 -0
- data/lib/puma/thread_pool.rb +297 -0
- data/lib/puma/util.rb +128 -0
- data/lib/rack/handler/puma.rb +78 -0
- data/puma.gemspec +52 -0
- data/test/ab_rs.rb +22 -0
- data/test/config.rb +2 -0
- data/test/config/app.rb +9 -0
- data/test/config/plugin.rb +1 -0
- data/test/config/settings.rb +2 -0
- data/test/config/state_file_testing_config.rb +14 -0
- data/test/hello-bind.ru +2 -0
- data/test/hello-delay.ru +3 -0
- data/test/hello-map.ru +3 -0
- data/test/hello-post.ru +4 -0
- data/test/hello-stuck.ru +1 -0
- data/test/hello-tcp.ru +5 -0
- data/test/hello.ru +1 -0
- data/test/hijack.ru +6 -0
- data/test/hijack2.ru +5 -0
- data/test/lobster.ru +4 -0
- data/test/shell/run.sh +24 -0
- data/test/shell/t1.rb +19 -0
- data/test/shell/t1_conf.rb +3 -0
- data/test/shell/t2.rb +17 -0
- data/test/shell/t2_conf.rb +6 -0
- data/test/shell/t3.rb +25 -0
- data/test/shell/t3_conf.rb +5 -0
- data/test/slow.ru +4 -0
- data/test/ssl_config.rb +4 -0
- data/test/test_app_status.rb +93 -0
- data/test/test_binder.rb +31 -0
- data/test/test_cli.rb +209 -0
- data/test/test_config.rb +95 -0
- data/test/test_events.rb +161 -0
- data/test/test_helper.rb +50 -0
- data/test/test_http10.rb +27 -0
- data/test/test_http11.rb +186 -0
- data/test/test_integration.rb +247 -0
- data/test/test_iobuffer.rb +39 -0
- data/test/test_minissl.rb +29 -0
- data/test/test_null_io.rb +49 -0
- data/test/test_persistent.rb +245 -0
- data/test/test_puma_server.rb +626 -0
- data/test/test_puma_server_ssl.rb +222 -0
- data/test/test_rack_handler.rb +57 -0
- data/test/test_rack_server.rb +138 -0
- data/test/test_tcp_logger.rb +39 -0
- data/test/test_tcp_rack.rb +36 -0
- data/test/test_thread_pool.rb +250 -0
- data/test/test_unix_socket.rb +35 -0
- data/test/test_web_server.rb +88 -0
- data/tools/jungle/README.md +9 -0
- data/tools/jungle/init.d/README.md +59 -0
- data/tools/jungle/init.d/puma +421 -0
- data/tools/jungle/init.d/run-puma +18 -0
- data/tools/jungle/upstart/README.md +61 -0
- data/tools/jungle/upstart/puma-manager.conf +31 -0
- data/tools/jungle/upstart/puma.conf +69 -0
- data/tools/trickletest.rb +45 -0
- metadata +297 -0
data/test/test_events.rb
ADDED
@@ -0,0 +1,161 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
|
3
|
+
require "puma/events"
|
4
|
+
|
5
|
+
class TestEvents < Minitest::Test
|
6
|
+
def test_null
|
7
|
+
events = Puma::Events.null
|
8
|
+
|
9
|
+
assert_instance_of Puma::NullIO, events.stdout
|
10
|
+
assert_instance_of Puma::NullIO, events.stderr
|
11
|
+
assert_equal events.stdout, events.stderr
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_strings
|
15
|
+
events = Puma::Events.strings
|
16
|
+
|
17
|
+
assert_instance_of StringIO, events.stdout
|
18
|
+
assert_instance_of StringIO, events.stderr
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_stdio
|
22
|
+
events = Puma::Events.stdio
|
23
|
+
|
24
|
+
assert_equal STDOUT, events.stdout
|
25
|
+
assert_equal STDERR, events.stderr
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_register_callback_with_block
|
29
|
+
res = false
|
30
|
+
|
31
|
+
events = Puma::Events.null
|
32
|
+
|
33
|
+
events.register(:exec) { res = true }
|
34
|
+
|
35
|
+
events.fire(:exec)
|
36
|
+
|
37
|
+
assert_equal true, res
|
38
|
+
end
|
39
|
+
|
40
|
+
def test_register_callback_with_object
|
41
|
+
obj = Object.new
|
42
|
+
|
43
|
+
def obj.res
|
44
|
+
@res || false
|
45
|
+
end
|
46
|
+
|
47
|
+
def obj.call
|
48
|
+
@res = true
|
49
|
+
end
|
50
|
+
|
51
|
+
events = Puma::Events.null
|
52
|
+
|
53
|
+
events.register(:exec, obj)
|
54
|
+
|
55
|
+
events.fire(:exec)
|
56
|
+
|
57
|
+
assert_equal true, obj.res
|
58
|
+
end
|
59
|
+
|
60
|
+
def test_fire_callback_with_multiple_arguments
|
61
|
+
res = []
|
62
|
+
|
63
|
+
events = Puma::Events.null
|
64
|
+
|
65
|
+
events.register(:exec) { |*args| res.concat(args) }
|
66
|
+
|
67
|
+
events.fire(:exec, :foo, :bar, :baz)
|
68
|
+
|
69
|
+
assert_equal [:foo, :bar, :baz], res
|
70
|
+
end
|
71
|
+
|
72
|
+
def test_on_booted_callback
|
73
|
+
res = false
|
74
|
+
|
75
|
+
events = Puma::Events.null
|
76
|
+
|
77
|
+
events.on_booted { res = true }
|
78
|
+
|
79
|
+
events.fire_on_booted!
|
80
|
+
|
81
|
+
assert res
|
82
|
+
end
|
83
|
+
|
84
|
+
def test_log_writes_to_stdout
|
85
|
+
out, _ = capture_io do
|
86
|
+
Puma::Events.stdio.log("ready")
|
87
|
+
end
|
88
|
+
|
89
|
+
assert_equal "ready\n", out
|
90
|
+
end
|
91
|
+
|
92
|
+
def test_write_writes_to_stdout
|
93
|
+
out, _ = capture_io do
|
94
|
+
Puma::Events.stdio.write("ready")
|
95
|
+
end
|
96
|
+
|
97
|
+
assert_equal "ready", out
|
98
|
+
end
|
99
|
+
|
100
|
+
def test_debug_writes_to_stdout_if_env_is_present
|
101
|
+
original_debug, ENV["PUMA_DEBUG"] = ENV["PUMA_DEBUG"], "1"
|
102
|
+
|
103
|
+
out, _ = capture_io do
|
104
|
+
Puma::Events.stdio.debug("ready")
|
105
|
+
end
|
106
|
+
|
107
|
+
assert_equal "% ready\n", out
|
108
|
+
ensure
|
109
|
+
ENV["PUMA_DEBUG"] = original_debug
|
110
|
+
end
|
111
|
+
|
112
|
+
def test_debug_not_write_to_stdout_if_env_is_not_present
|
113
|
+
out, _ = capture_io do
|
114
|
+
Puma::Events.stdio.debug("ready")
|
115
|
+
end
|
116
|
+
|
117
|
+
assert_empty out
|
118
|
+
end
|
119
|
+
|
120
|
+
def test_error_writes_to_stderr_and_exits
|
121
|
+
did_exit = false
|
122
|
+
|
123
|
+
_, err = capture_io do
|
124
|
+
Puma::Events.stdio.error("interrupted")
|
125
|
+
end
|
126
|
+
|
127
|
+
assert_equal "ERROR: interrupted", err
|
128
|
+
rescue SystemExit
|
129
|
+
did_exit = true
|
130
|
+
ensure
|
131
|
+
assert did_exit
|
132
|
+
end
|
133
|
+
|
134
|
+
def test_pid_formatter
|
135
|
+
pid = Process.pid
|
136
|
+
|
137
|
+
out, _ = capture_io do
|
138
|
+
events = Puma::Events.stdio
|
139
|
+
|
140
|
+
events.formatter = Puma::Events::PidFormatter.new
|
141
|
+
|
142
|
+
events.write("ready")
|
143
|
+
end
|
144
|
+
|
145
|
+
assert_equal "[#{ pid }] ready", out
|
146
|
+
end
|
147
|
+
|
148
|
+
def test_custom_log_formatter
|
149
|
+
custom_formatter = proc { |str| "-> #{ str }" }
|
150
|
+
|
151
|
+
out, _ = capture_io do
|
152
|
+
events = Puma::Events.stdio
|
153
|
+
|
154
|
+
events.formatter = custom_formatter
|
155
|
+
|
156
|
+
events.write("ready")
|
157
|
+
end
|
158
|
+
|
159
|
+
assert_equal "-> ready", out
|
160
|
+
end
|
161
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
# Copyright (c) 2011 Evan Phoenix
|
2
|
+
# Copyright (c) 2005 Zed A. Shaw
|
3
|
+
|
4
|
+
require "bundler/setup"
|
5
|
+
require "minitest/autorun"
|
6
|
+
require "minitest/pride"
|
7
|
+
require "puma"
|
8
|
+
require "puma/detect"
|
9
|
+
|
10
|
+
# Either takes a string to do a get request against, or a tuple of [URI, HTTP] where
|
11
|
+
# HTTP is some kind of Net::HTTP request object (POST, HEAD, etc.)
|
12
|
+
def hit(uris)
|
13
|
+
results = []
|
14
|
+
|
15
|
+
uris.each do |u|
|
16
|
+
res = nil
|
17
|
+
|
18
|
+
if u.kind_of? String
|
19
|
+
res = Net::HTTP.get(URI.parse(u))
|
20
|
+
else
|
21
|
+
url = URI.parse(u[0])
|
22
|
+
res = Net::HTTP.new(url.host, url.port).start {|h| h.request(u[1]) }
|
23
|
+
end
|
24
|
+
|
25
|
+
assert res != nil, "Didn't get a response: #{u}"
|
26
|
+
results << res
|
27
|
+
end
|
28
|
+
|
29
|
+
return results
|
30
|
+
end
|
31
|
+
|
32
|
+
module TimeoutEveryTestCase
|
33
|
+
def run(*)
|
34
|
+
if !!ENV['CI']
|
35
|
+
Timeout.timeout(60) { super }
|
36
|
+
else
|
37
|
+
super
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
Minitest::Test.prepend TimeoutEveryTestCase
|
43
|
+
|
44
|
+
module SkipTestsBasedOnRubyEngine
|
45
|
+
def skip_on_jruby
|
46
|
+
skip "Skipped on JRuby" if Puma.jruby?
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
Minitest::Test.include SkipTestsBasedOnRubyEngine
|
data/test/test_http10.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
|
3
|
+
require "puma/puma_http11"
|
4
|
+
|
5
|
+
class Http10ParserTest < Minitest::Test
|
6
|
+
def test_parse_simple
|
7
|
+
parser = Puma::HttpParser.new
|
8
|
+
req = {}
|
9
|
+
http = "GET / HTTP/1.0\r\n\r\n"
|
10
|
+
nread = parser.execute(req, http, 0)
|
11
|
+
|
12
|
+
assert nread == http.length, "Failed to parse the full HTTP request"
|
13
|
+
assert parser.finished?, "Parser didn't finish"
|
14
|
+
assert !parser.error?, "Parser had error"
|
15
|
+
assert nread == parser.nread, "Number read returned from execute does not match"
|
16
|
+
|
17
|
+
assert_equal '/', req['REQUEST_PATH']
|
18
|
+
assert_equal 'HTTP/1.0', req['HTTP_VERSION']
|
19
|
+
assert_equal '/', req['REQUEST_URI']
|
20
|
+
assert_equal 'GET', req['REQUEST_METHOD']
|
21
|
+
assert_nil req['FRAGMENT']
|
22
|
+
assert_nil req['QUERY_STRING']
|
23
|
+
|
24
|
+
parser.reset
|
25
|
+
assert parser.nread == 0, "Number read after reset should be 0"
|
26
|
+
end
|
27
|
+
end
|
data/test/test_http11.rb
ADDED
@@ -0,0 +1,186 @@
|
|
1
|
+
# Copyright (c) 2011 Evan Phoenix
|
2
|
+
# Copyright (c) 2005 Zed A. Shaw
|
3
|
+
|
4
|
+
require "test_helper"
|
5
|
+
|
6
|
+
require "puma/puma_http11"
|
7
|
+
|
8
|
+
class Http11ParserTest < Minitest::Test
|
9
|
+
|
10
|
+
def test_parse_simple
|
11
|
+
parser = Puma::HttpParser.new
|
12
|
+
req = {}
|
13
|
+
http = "GET /?a=1 HTTP/1.1\r\n\r\n"
|
14
|
+
nread = parser.execute(req, http, 0)
|
15
|
+
|
16
|
+
assert nread == http.length, "Failed to parse the full HTTP request"
|
17
|
+
assert parser.finished?, "Parser didn't finish"
|
18
|
+
assert !parser.error?, "Parser had error"
|
19
|
+
assert nread == parser.nread, "Number read returned from execute does not match"
|
20
|
+
|
21
|
+
assert_equal '/', req['REQUEST_PATH']
|
22
|
+
assert_equal 'HTTP/1.1', req['HTTP_VERSION']
|
23
|
+
assert_equal '/?a=1', req['REQUEST_URI']
|
24
|
+
assert_equal 'GET', req['REQUEST_METHOD']
|
25
|
+
assert_nil req['FRAGMENT']
|
26
|
+
assert_equal "a=1", req['QUERY_STRING']
|
27
|
+
|
28
|
+
parser.reset
|
29
|
+
assert parser.nread == 0, "Number read after reset should be 0"
|
30
|
+
end
|
31
|
+
|
32
|
+
def test_parse_escaping_in_query
|
33
|
+
parser = Puma::HttpParser.new
|
34
|
+
req = {}
|
35
|
+
http = "GET /admin/users?search=%27%%27 HTTP/1.1\r\n\r\n"
|
36
|
+
nread = parser.execute(req, http, 0)
|
37
|
+
|
38
|
+
assert nread == http.length, "Failed to parse the full HTTP request"
|
39
|
+
assert parser.finished?, "Parser didn't finish"
|
40
|
+
assert !parser.error?, "Parser had error"
|
41
|
+
assert nread == parser.nread, "Number read returned from execute does not match"
|
42
|
+
|
43
|
+
assert_equal '/admin/users?search=%27%%27', req['REQUEST_URI']
|
44
|
+
assert_equal "search=%27%%27", req['QUERY_STRING']
|
45
|
+
|
46
|
+
parser.reset
|
47
|
+
assert parser.nread == 0, "Number read after reset should be 0"
|
48
|
+
end
|
49
|
+
|
50
|
+
def test_parse_absolute_uri
|
51
|
+
parser = Puma::HttpParser.new
|
52
|
+
req = {}
|
53
|
+
http = "GET http://192.168.1.96:3000/api/v1/matches/test?1=1 HTTP/1.1\r\n\r\n"
|
54
|
+
nread = parser.execute(req, http, 0)
|
55
|
+
|
56
|
+
assert nread == http.length, "Failed to parse the full HTTP request"
|
57
|
+
assert parser.finished?, "Parser didn't finish"
|
58
|
+
assert !parser.error?, "Parser had error"
|
59
|
+
assert nread == parser.nread, "Number read returned from execute does not match"
|
60
|
+
|
61
|
+
assert_equal "GET", req['REQUEST_METHOD']
|
62
|
+
assert_equal 'http://192.168.1.96:3000/api/v1/matches/test?1=1', req['REQUEST_URI']
|
63
|
+
assert_equal 'HTTP/1.1', req['HTTP_VERSION']
|
64
|
+
|
65
|
+
assert_nil req['REQUEST_PATH']
|
66
|
+
assert_nil req['FRAGMENT']
|
67
|
+
assert_nil req['QUERY_STRING']
|
68
|
+
|
69
|
+
parser.reset
|
70
|
+
assert parser.nread == 0, "Number read after reset should be 0"
|
71
|
+
|
72
|
+
end
|
73
|
+
|
74
|
+
def test_parse_dumbfuck_headers
|
75
|
+
parser = Puma::HttpParser.new
|
76
|
+
req = {}
|
77
|
+
should_be_good = "GET / HTTP/1.1\r\naaaaaaaaaaaaa:++++++++++\r\n\r\n"
|
78
|
+
nread = parser.execute(req, should_be_good, 0)
|
79
|
+
assert_equal should_be_good.length, nread
|
80
|
+
assert parser.finished?
|
81
|
+
assert !parser.error?
|
82
|
+
end
|
83
|
+
|
84
|
+
def test_parse_error
|
85
|
+
parser = Puma::HttpParser.new
|
86
|
+
req = {}
|
87
|
+
bad_http = "GET / SsUTF/1.1"
|
88
|
+
|
89
|
+
error = false
|
90
|
+
begin
|
91
|
+
parser.execute(req, bad_http, 0)
|
92
|
+
rescue
|
93
|
+
error = true
|
94
|
+
end
|
95
|
+
|
96
|
+
assert error, "failed to throw exception"
|
97
|
+
assert !parser.finished?, "Parser shouldn't be finished"
|
98
|
+
assert parser.error?, "Parser SHOULD have error"
|
99
|
+
end
|
100
|
+
|
101
|
+
def test_fragment_in_uri
|
102
|
+
parser = Puma::HttpParser.new
|
103
|
+
req = {}
|
104
|
+
get = "GET /forums/1/topics/2375?page=1#posts-17408 HTTP/1.1\r\n\r\n"
|
105
|
+
|
106
|
+
parser.execute(req, get, 0)
|
107
|
+
|
108
|
+
assert parser.finished?
|
109
|
+
assert_equal '/forums/1/topics/2375?page=1', req['REQUEST_URI']
|
110
|
+
assert_equal 'posts-17408', req['FRAGMENT']
|
111
|
+
end
|
112
|
+
|
113
|
+
# lame random garbage maker
|
114
|
+
def rand_data(min, max, readable=true)
|
115
|
+
count = min + ((rand(max)+1) *10).to_i
|
116
|
+
res = count.to_s + "/"
|
117
|
+
|
118
|
+
if readable
|
119
|
+
res << Digest::SHA1.hexdigest(rand(count * 100).to_s) * (count / 40)
|
120
|
+
else
|
121
|
+
res << Digest::SHA1.digest(rand(count * 100).to_s) * (count / 20)
|
122
|
+
end
|
123
|
+
|
124
|
+
return res
|
125
|
+
end
|
126
|
+
|
127
|
+
def test_max_uri_path_length
|
128
|
+
parser = Puma::HttpParser.new
|
129
|
+
req = {}
|
130
|
+
|
131
|
+
# Support URI path length to a max of 2048
|
132
|
+
path = "/" + rand_data(1000, 100)
|
133
|
+
http = "GET #{path} HTTP/1.1\r\n\r\n"
|
134
|
+
parser.execute(req, http, 0)
|
135
|
+
assert_equal path, req['REQUEST_PATH']
|
136
|
+
parser.reset
|
137
|
+
|
138
|
+
# Raise exception if URI path length > 2048
|
139
|
+
path = "/" + rand_data(3000, 100)
|
140
|
+
http = "GET #{path} HTTP/1.1\r\n\r\n"
|
141
|
+
assert_raises Puma::HttpParserError do
|
142
|
+
parser.execute(req, http, 0)
|
143
|
+
parser.reset
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
def test_horrible_queries
|
148
|
+
parser = Puma::HttpParser.new
|
149
|
+
|
150
|
+
# then that large header names are caught
|
151
|
+
10.times do |c|
|
152
|
+
get = "GET /#{rand_data(10,120)} HTTP/1.1\r\nX-#{rand_data(1024, 1024+(c*1024))}: Test\r\n\r\n"
|
153
|
+
assert_raises Puma::HttpParserError do
|
154
|
+
parser.execute({}, get, 0)
|
155
|
+
parser.reset
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
# then that large mangled field values are caught
|
160
|
+
10.times do |c|
|
161
|
+
get = "GET /#{rand_data(10,120)} HTTP/1.1\r\nX-Test: #{rand_data(1024, 1024+(c*1024), false)}\r\n\r\n"
|
162
|
+
assert_raises Puma::HttpParserError do
|
163
|
+
parser.execute({}, get, 0)
|
164
|
+
parser.reset
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
# then large headers are rejected too
|
169
|
+
get = "GET /#{rand_data(10,120)} HTTP/1.1\r\n"
|
170
|
+
get << "X-Test: test\r\n" * (80 * 1024)
|
171
|
+
assert_raises Puma::HttpParserError do
|
172
|
+
parser.execute({}, get, 0)
|
173
|
+
parser.reset
|
174
|
+
end
|
175
|
+
|
176
|
+
# finally just that random garbage gets blocked all the time
|
177
|
+
10.times do |c|
|
178
|
+
get = "GET #{rand_data(1024, 1024+(c*1024), false)} #{rand_data(1024, 1024+(c*1024), false)}\r\n\r\n"
|
179
|
+
assert_raises Puma::HttpParserError do
|
180
|
+
parser.execute({}, get, 0)
|
181
|
+
parser.reset
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
end
|
186
|
+
end
|
@@ -0,0 +1,247 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
|
3
|
+
require "puma/cli"
|
4
|
+
require "puma/control_cli"
|
5
|
+
|
6
|
+
# These don't run on travis because they're too fragile
|
7
|
+
|
8
|
+
class TestIntegration < Minitest::Test
|
9
|
+
def setup
|
10
|
+
@state_path = "test/test_puma.state"
|
11
|
+
@bind_path = "test/test_server.sock"
|
12
|
+
@control_path = "test/test_control.sock"
|
13
|
+
@token = "xxyyzz"
|
14
|
+
@tcp_port = 9998
|
15
|
+
|
16
|
+
@server = nil
|
17
|
+
@script = nil
|
18
|
+
|
19
|
+
@wait, @ready = IO.pipe
|
20
|
+
|
21
|
+
@events = Puma::Events.strings
|
22
|
+
@events.on_booted { @ready << "!" }
|
23
|
+
end
|
24
|
+
|
25
|
+
def teardown
|
26
|
+
File.unlink @state_path rescue nil
|
27
|
+
File.unlink @bind_path rescue nil
|
28
|
+
File.unlink @control_path rescue nil
|
29
|
+
|
30
|
+
@wait.close
|
31
|
+
@ready.close
|
32
|
+
|
33
|
+
if @server
|
34
|
+
Process.kill "INT", @server.pid
|
35
|
+
begin
|
36
|
+
Process.wait @server.pid
|
37
|
+
rescue Errno::ECHILD
|
38
|
+
end
|
39
|
+
|
40
|
+
@server.close
|
41
|
+
end
|
42
|
+
|
43
|
+
if @script
|
44
|
+
@script.close!
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def server(opts)
|
49
|
+
core = "#{Gem.ruby} -rubygems -Ilib bin/puma"
|
50
|
+
cmd = "#{core} --restart-cmd '#{core}' -b tcp://127.0.0.1:#{@tcp_port} #{opts}"
|
51
|
+
tf = Tempfile.new "puma-test"
|
52
|
+
tf.puts "exec #{cmd}"
|
53
|
+
tf.close
|
54
|
+
|
55
|
+
@script = tf
|
56
|
+
|
57
|
+
@server = IO.popen("sh #{tf.path}", "r")
|
58
|
+
|
59
|
+
while (@server.gets) !~ /Ctrl-C/
|
60
|
+
# nothing
|
61
|
+
end
|
62
|
+
|
63
|
+
sleep 1
|
64
|
+
|
65
|
+
@server
|
66
|
+
end
|
67
|
+
|
68
|
+
def signal(which)
|
69
|
+
Process.kill which, @server.pid
|
70
|
+
end
|
71
|
+
|
72
|
+
def wait_booted
|
73
|
+
@wait.sysread 1
|
74
|
+
end
|
75
|
+
|
76
|
+
def test_stop_via_pumactl
|
77
|
+
if Puma.jruby? || Puma.windows?
|
78
|
+
assert true
|
79
|
+
return
|
80
|
+
end
|
81
|
+
|
82
|
+
conf = Puma::Configuration.new do |c|
|
83
|
+
c.quiet
|
84
|
+
c.state_path @state_path
|
85
|
+
c.bind "unix://#{@bind_path}"
|
86
|
+
c.activate_control_app "unix://#{@control_path}", :auth_token => @token
|
87
|
+
c.rackup "test/hello.ru"
|
88
|
+
end
|
89
|
+
|
90
|
+
l = Puma::Launcher.new conf, :events => @events
|
91
|
+
|
92
|
+
t = Thread.new do
|
93
|
+
Thread.current.abort_on_exception = true
|
94
|
+
l.run
|
95
|
+
end
|
96
|
+
|
97
|
+
wait_booted
|
98
|
+
|
99
|
+
s = UNIXSocket.new @bind_path
|
100
|
+
s << "GET / HTTP/1.0\r\n\r\n"
|
101
|
+
assert_equal "Hello World", s.read.split("\r\n").last
|
102
|
+
|
103
|
+
sout = StringIO.new
|
104
|
+
|
105
|
+
ccli = Puma::ControlCLI.new %W!-S #{@state_path} stop!, sout
|
106
|
+
|
107
|
+
ccli.run
|
108
|
+
|
109
|
+
assert_kind_of Thread, t.join(1), "server didn't stop"
|
110
|
+
end
|
111
|
+
|
112
|
+
def test_phased_restart_via_pumactl
|
113
|
+
skip("Too finicky, fails 50% of the time on CI.")
|
114
|
+
|
115
|
+
if Puma.jruby? || Puma.windows?
|
116
|
+
assert true
|
117
|
+
return
|
118
|
+
end
|
119
|
+
|
120
|
+
conf = Puma::Configuration.new do |c|
|
121
|
+
c.quiet
|
122
|
+
c.state_path @state_path
|
123
|
+
c.bind "unix://#{@bind_path}"
|
124
|
+
c.activate_control_app "unix://#{@control_path}", :auth_token => @token
|
125
|
+
c.workers 2
|
126
|
+
c.worker_shutdown_timeout 1
|
127
|
+
c.rackup "test/hello-stuck.ru"
|
128
|
+
end
|
129
|
+
|
130
|
+
l = Puma::Launcher.new conf, :events => @events
|
131
|
+
|
132
|
+
Thread.abort_on_exception = true
|
133
|
+
|
134
|
+
t = Thread.new do
|
135
|
+
Thread.current.abort_on_exception = true
|
136
|
+
l.run
|
137
|
+
end
|
138
|
+
|
139
|
+
wait_booted
|
140
|
+
|
141
|
+
# Make both workers stuck
|
142
|
+
s1 = UNIXSocket.new @bind_path
|
143
|
+
s1 << "GET / HTTP/1.0\r\n\r\n"
|
144
|
+
|
145
|
+
sout = StringIO.new
|
146
|
+
|
147
|
+
# Phased restart
|
148
|
+
ccli = Puma::ControlCLI.new %W!-S #{@state_path} phased-restart!, sout
|
149
|
+
ccli.run
|
150
|
+
sleep 20
|
151
|
+
@events.stdout.rewind
|
152
|
+
log = @events.stdout.readlines.join("")
|
153
|
+
assert_match(/TERM sent/, log)
|
154
|
+
assert_match(/- Worker \d \(pid: \d+\) booted, phase: 1/, log)
|
155
|
+
|
156
|
+
# Stop
|
157
|
+
ccli = Puma::ControlCLI.new %W!-S #{@state_path} stop!, sout
|
158
|
+
ccli.run
|
159
|
+
|
160
|
+
assert_kind_of Thread, t.join(5), "server didn't stop"
|
161
|
+
end
|
162
|
+
|
163
|
+
def test_kill_unknown_via_pumactl
|
164
|
+
if Puma.jruby? || Puma.windows?
|
165
|
+
assert true
|
166
|
+
return
|
167
|
+
end
|
168
|
+
|
169
|
+
# we run ls to get a 'safe' pid to pass off as puma in cli stop
|
170
|
+
# do not want to accidently kill a valid other process
|
171
|
+
io = IO.popen("ls")
|
172
|
+
safe_pid = io.pid
|
173
|
+
Process.wait safe_pid
|
174
|
+
|
175
|
+
sout = StringIO.new
|
176
|
+
|
177
|
+
e = assert_raises SystemExit do
|
178
|
+
ccli = Puma::ControlCLI.new %W!-p #{safe_pid} stop!, sout
|
179
|
+
ccli.run
|
180
|
+
end
|
181
|
+
sout.rewind
|
182
|
+
assert_match(/No pid '\d+' found/, sout.readlines.join(""))
|
183
|
+
assert_equal(1, e.status)
|
184
|
+
end
|
185
|
+
|
186
|
+
def test_restart_closes_keepalive_sockets
|
187
|
+
server("-q test/hello.ru")
|
188
|
+
|
189
|
+
s = TCPSocket.new "localhost", @tcp_port
|
190
|
+
s << "GET / HTTP/1.1\r\n\r\n"
|
191
|
+
true until s.gets == "\r\n"
|
192
|
+
|
193
|
+
s.readpartial(20)
|
194
|
+
signal :USR2
|
195
|
+
|
196
|
+
sleep 1
|
197
|
+
|
198
|
+
s.write "GET / HTTP/1.1\r\n\r\n"
|
199
|
+
|
200
|
+
assert_raises Errno::ECONNRESET do
|
201
|
+
Timeout.timeout(2) do
|
202
|
+
raise Errno::ECONNRESET unless s.read(2)
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
while (@server.gets) !~ /Ctrl-C/
|
207
|
+
# nothing
|
208
|
+
end
|
209
|
+
|
210
|
+
sleep 5 if Puma.jruby?
|
211
|
+
|
212
|
+
s = TCPSocket.new "127.0.0.1", @tcp_port
|
213
|
+
s << "GET / HTTP/1.0\r\n\r\n"
|
214
|
+
assert_equal "Hello World", s.read.split("\r\n").last
|
215
|
+
end
|
216
|
+
|
217
|
+
def test_restart_closes_keepalive_sockets_workers
|
218
|
+
if Puma.jruby?
|
219
|
+
assert true
|
220
|
+
return
|
221
|
+
end
|
222
|
+
|
223
|
+
server("-q -w 2 test/hello.ru")
|
224
|
+
|
225
|
+
s = TCPSocket.new "localhost", @tcp_port
|
226
|
+
s << "GET / HTTP/1.1\r\n\r\n"
|
227
|
+
true until s.gets == "\r\n"
|
228
|
+
|
229
|
+
s.readpartial(20)
|
230
|
+
signal :USR2
|
231
|
+
|
232
|
+
true while @server.gets =~ /Ctrl-C/
|
233
|
+
sleep 1
|
234
|
+
|
235
|
+
s.write "GET / HTTP/1.1\r\n\r\n"
|
236
|
+
|
237
|
+
assert_raises Errno::ECONNRESET do
|
238
|
+
Timeout.timeout(2) do
|
239
|
+
raise Errno::ECONNRESET unless s.read(2)
|
240
|
+
end
|
241
|
+
end
|
242
|
+
|
243
|
+
s = TCPSocket.new "localhost", @tcp_port
|
244
|
+
s << "GET / HTTP/1.0\r\n\r\n"
|
245
|
+
assert_equal "Hello World", s.read.split("\r\n").last
|
246
|
+
end
|
247
|
+
end
|