puma-simon 3.7.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (157) hide show
  1. checksums.yaml +7 -0
  2. data/.github/issue_template.md +20 -0
  3. data/.gitignore +18 -0
  4. data/.hoeignore +12 -0
  5. data/.travis.yml +29 -0
  6. data/DEPLOYMENT.md +91 -0
  7. data/Gemfile +12 -0
  8. data/History.md +1254 -0
  9. data/LICENSE +26 -0
  10. data/Manifest.txt +78 -0
  11. data/README.md +353 -0
  12. data/Rakefile +158 -0
  13. data/Release.md +9 -0
  14. data/bin/puma +10 -0
  15. data/bin/puma-wild +31 -0
  16. data/bin/pumactl +12 -0
  17. data/docs/nginx.md +80 -0
  18. data/docs/signals.md +43 -0
  19. data/docs/systemd.md +197 -0
  20. data/examples/CA/cacert.pem +23 -0
  21. data/examples/CA/newcerts/cert_1.pem +19 -0
  22. data/examples/CA/newcerts/cert_2.pem +19 -0
  23. data/examples/CA/private/cakeypair.pem +30 -0
  24. data/examples/CA/serial +1 -0
  25. data/examples/config.rb +200 -0
  26. data/examples/plugins/redis_stop_puma.rb +46 -0
  27. data/examples/puma/cert_puma.pem +19 -0
  28. data/examples/puma/client-certs/ca.crt +19 -0
  29. data/examples/puma/client-certs/ca.key +27 -0
  30. data/examples/puma/client-certs/client.crt +19 -0
  31. data/examples/puma/client-certs/client.key +27 -0
  32. data/examples/puma/client-certs/client_expired.crt +19 -0
  33. data/examples/puma/client-certs/client_expired.key +27 -0
  34. data/examples/puma/client-certs/client_unknown.crt +19 -0
  35. data/examples/puma/client-certs/client_unknown.key +27 -0
  36. data/examples/puma/client-certs/generate.rb +78 -0
  37. data/examples/puma/client-certs/keystore.jks +0 -0
  38. data/examples/puma/client-certs/server.crt +19 -0
  39. data/examples/puma/client-certs/server.key +27 -0
  40. data/examples/puma/client-certs/server.p12 +0 -0
  41. data/examples/puma/client-certs/unknown_ca.crt +19 -0
  42. data/examples/puma/client-certs/unknown_ca.key +27 -0
  43. data/examples/puma/csr_puma.pem +11 -0
  44. data/examples/puma/keystore.jks +0 -0
  45. data/examples/puma/puma_keypair.pem +15 -0
  46. data/examples/qc_config.rb +13 -0
  47. data/ext/puma_http11/PumaHttp11Service.java +17 -0
  48. data/ext/puma_http11/ext_help.h +15 -0
  49. data/ext/puma_http11/extconf.rb +15 -0
  50. data/ext/puma_http11/http11_parser.c +1069 -0
  51. data/ext/puma_http11/http11_parser.h +65 -0
  52. data/ext/puma_http11/http11_parser.java.rl +161 -0
  53. data/ext/puma_http11/http11_parser.rl +147 -0
  54. data/ext/puma_http11/http11_parser_common.rl +54 -0
  55. data/ext/puma_http11/io_buffer.c +155 -0
  56. data/ext/puma_http11/mini_ssl.c +457 -0
  57. data/ext/puma_http11/org/jruby/puma/Http11.java +234 -0
  58. data/ext/puma_http11/org/jruby/puma/Http11Parser.java +473 -0
  59. data/ext/puma_http11/org/jruby/puma/MiniSSL.java +339 -0
  60. data/ext/puma_http11/puma_http11.c +500 -0
  61. data/gemfiles/2.1-Gemfile +12 -0
  62. data/lib/puma.rb +15 -0
  63. data/lib/puma/accept_nonblock.rb +23 -0
  64. data/lib/puma/app/status.rb +66 -0
  65. data/lib/puma/binder.rb +402 -0
  66. data/lib/puma/cli.rb +220 -0
  67. data/lib/puma/client.rb +434 -0
  68. data/lib/puma/cluster.rb +510 -0
  69. data/lib/puma/commonlogger.rb +106 -0
  70. data/lib/puma/compat.rb +14 -0
  71. data/lib/puma/configuration.rb +364 -0
  72. data/lib/puma/const.rb +224 -0
  73. data/lib/puma/control_cli.rb +259 -0
  74. data/lib/puma/convenient.rb +23 -0
  75. data/lib/puma/daemon_ext.rb +31 -0
  76. data/lib/puma/delegation.rb +11 -0
  77. data/lib/puma/detect.rb +13 -0
  78. data/lib/puma/dsl.rb +486 -0
  79. data/lib/puma/events.rb +152 -0
  80. data/lib/puma/io_buffer.rb +7 -0
  81. data/lib/puma/java_io_buffer.rb +45 -0
  82. data/lib/puma/jruby_restart.rb +83 -0
  83. data/lib/puma/launcher.rb +410 -0
  84. data/lib/puma/minissl.rb +221 -0
  85. data/lib/puma/null_io.rb +42 -0
  86. data/lib/puma/plugin.rb +115 -0
  87. data/lib/puma/plugin/tmp_restart.rb +35 -0
  88. data/lib/puma/rack/backports/uri/common_193.rb +33 -0
  89. data/lib/puma/rack/builder.rb +298 -0
  90. data/lib/puma/rack/urlmap.rb +91 -0
  91. data/lib/puma/rack_default.rb +7 -0
  92. data/lib/puma/reactor.rb +210 -0
  93. data/lib/puma/runner.rb +171 -0
  94. data/lib/puma/server.rb +949 -0
  95. data/lib/puma/single.rb +112 -0
  96. data/lib/puma/state_file.rb +29 -0
  97. data/lib/puma/tcp_logger.rb +39 -0
  98. data/lib/puma/thread_pool.rb +297 -0
  99. data/lib/puma/util.rb +128 -0
  100. data/lib/rack/handler/puma.rb +78 -0
  101. data/puma.gemspec +52 -0
  102. data/test/ab_rs.rb +22 -0
  103. data/test/config.rb +2 -0
  104. data/test/config/app.rb +9 -0
  105. data/test/config/plugin.rb +1 -0
  106. data/test/config/settings.rb +2 -0
  107. data/test/config/state_file_testing_config.rb +14 -0
  108. data/test/hello-bind.ru +2 -0
  109. data/test/hello-delay.ru +3 -0
  110. data/test/hello-map.ru +3 -0
  111. data/test/hello-post.ru +4 -0
  112. data/test/hello-stuck.ru +1 -0
  113. data/test/hello-tcp.ru +5 -0
  114. data/test/hello.ru +1 -0
  115. data/test/hijack.ru +6 -0
  116. data/test/hijack2.ru +5 -0
  117. data/test/lobster.ru +4 -0
  118. data/test/shell/run.sh +24 -0
  119. data/test/shell/t1.rb +19 -0
  120. data/test/shell/t1_conf.rb +3 -0
  121. data/test/shell/t2.rb +17 -0
  122. data/test/shell/t2_conf.rb +6 -0
  123. data/test/shell/t3.rb +25 -0
  124. data/test/shell/t3_conf.rb +5 -0
  125. data/test/slow.ru +4 -0
  126. data/test/ssl_config.rb +4 -0
  127. data/test/test_app_status.rb +93 -0
  128. data/test/test_binder.rb +31 -0
  129. data/test/test_cli.rb +209 -0
  130. data/test/test_config.rb +95 -0
  131. data/test/test_events.rb +161 -0
  132. data/test/test_helper.rb +50 -0
  133. data/test/test_http10.rb +27 -0
  134. data/test/test_http11.rb +186 -0
  135. data/test/test_integration.rb +247 -0
  136. data/test/test_iobuffer.rb +39 -0
  137. data/test/test_minissl.rb +29 -0
  138. data/test/test_null_io.rb +49 -0
  139. data/test/test_persistent.rb +245 -0
  140. data/test/test_puma_server.rb +626 -0
  141. data/test/test_puma_server_ssl.rb +222 -0
  142. data/test/test_rack_handler.rb +57 -0
  143. data/test/test_rack_server.rb +138 -0
  144. data/test/test_tcp_logger.rb +39 -0
  145. data/test/test_tcp_rack.rb +36 -0
  146. data/test/test_thread_pool.rb +250 -0
  147. data/test/test_unix_socket.rb +35 -0
  148. data/test/test_web_server.rb +88 -0
  149. data/tools/jungle/README.md +9 -0
  150. data/tools/jungle/init.d/README.md +59 -0
  151. data/tools/jungle/init.d/puma +421 -0
  152. data/tools/jungle/init.d/run-puma +18 -0
  153. data/tools/jungle/upstart/README.md +61 -0
  154. data/tools/jungle/upstart/puma-manager.conf +31 -0
  155. data/tools/jungle/upstart/puma.conf +69 -0
  156. data/tools/trickletest.rb +45 -0
  157. metadata +297 -0
@@ -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
@@ -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
@@ -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
@@ -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