puma-simon 3.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|