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.
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