unicorn-fork 6.1.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 (145) hide show
  1. checksums.yaml +7 -0
  2. data/.CHANGELOG.old +25 -0
  3. data/.document +28 -0
  4. data/.gitattributes +5 -0
  5. data/.gitignore +25 -0
  6. data/.mailmap +26 -0
  7. data/.manifest +144 -0
  8. data/.olddoc.yml +25 -0
  9. data/Application_Timeouts +77 -0
  10. data/CONTRIBUTORS +39 -0
  11. data/COPYING +674 -0
  12. data/DESIGN +99 -0
  13. data/Documentation/.gitignore +3 -0
  14. data/Documentation/unicorn.1 +222 -0
  15. data/Documentation/unicorn_rails.1 +207 -0
  16. data/FAQ +70 -0
  17. data/GIT-VERSION-FILE +1 -0
  18. data/GIT-VERSION-GEN +39 -0
  19. data/GNUmakefile +318 -0
  20. data/HACKING +117 -0
  21. data/ISSUES +102 -0
  22. data/KNOWN_ISSUES +79 -0
  23. data/LICENSE +67 -0
  24. data/Links +58 -0
  25. data/PHILOSOPHY +139 -0
  26. data/README +165 -0
  27. data/Rakefile +17 -0
  28. data/SIGNALS +123 -0
  29. data/Sandbox +104 -0
  30. data/TODO +1 -0
  31. data/TUNING +119 -0
  32. data/archive/.gitignore +3 -0
  33. data/archive/slrnpull.conf +4 -0
  34. data/bin/unicorn +129 -0
  35. data/bin/unicorn_rails +210 -0
  36. data/examples/big_app_gc.rb +3 -0
  37. data/examples/echo.ru +27 -0
  38. data/examples/init.sh +102 -0
  39. data/examples/logger_mp_safe.rb +26 -0
  40. data/examples/logrotate.conf +44 -0
  41. data/examples/nginx.conf +156 -0
  42. data/examples/unicorn.conf.minimal.rb +14 -0
  43. data/examples/unicorn.conf.rb +111 -0
  44. data/examples/unicorn.socket +11 -0
  45. data/examples/unicorn@.service +40 -0
  46. data/ext/unicorn_http/CFLAGS +13 -0
  47. data/ext/unicorn_http/c_util.h +115 -0
  48. data/ext/unicorn_http/common_field_optimization.h +128 -0
  49. data/ext/unicorn_http/epollexclusive.h +128 -0
  50. data/ext/unicorn_http/ext_help.h +38 -0
  51. data/ext/unicorn_http/extconf.rb +40 -0
  52. data/ext/unicorn_http/global_variables.h +97 -0
  53. data/ext/unicorn_http/httpdate.c +91 -0
  54. data/ext/unicorn_http/unicorn_http.c +4348 -0
  55. data/ext/unicorn_http/unicorn_http.rl +1054 -0
  56. data/ext/unicorn_http/unicorn_http_common.rl +76 -0
  57. data/lib/unicorn/app/old_rails/static.rb +60 -0
  58. data/lib/unicorn/app/old_rails.rb +36 -0
  59. data/lib/unicorn/cgi_wrapper.rb +148 -0
  60. data/lib/unicorn/configurator.rb +749 -0
  61. data/lib/unicorn/const.rb +22 -0
  62. data/lib/unicorn/http_request.rb +180 -0
  63. data/lib/unicorn/http_response.rb +95 -0
  64. data/lib/unicorn/http_server.rb +860 -0
  65. data/lib/unicorn/launcher.rb +63 -0
  66. data/lib/unicorn/oob_gc.rb +82 -0
  67. data/lib/unicorn/preread_input.rb +34 -0
  68. data/lib/unicorn/select_waiter.rb +7 -0
  69. data/lib/unicorn/socket_helper.rb +186 -0
  70. data/lib/unicorn/stream_input.rb +152 -0
  71. data/lib/unicorn/tee_input.rb +132 -0
  72. data/lib/unicorn/tmpio.rb +34 -0
  73. data/lib/unicorn/util.rb +91 -0
  74. data/lib/unicorn/version.rb +1 -0
  75. data/lib/unicorn/worker.rb +166 -0
  76. data/lib/unicorn.rb +137 -0
  77. data/man/man1/unicorn.1 +222 -0
  78. data/man/man1/unicorn_rails.1 +207 -0
  79. data/setup.rb +1587 -0
  80. data/t/.gitignore +4 -0
  81. data/t/GNUmakefile +5 -0
  82. data/t/README +49 -0
  83. data/t/active-unix-socket.t +110 -0
  84. data/t/back-out-of-upgrade.t +44 -0
  85. data/t/bin/unused_listen +40 -0
  86. data/t/client_body_buffer_size.ru +15 -0
  87. data/t/client_body_buffer_size.t +79 -0
  88. data/t/detach.ru +12 -0
  89. data/t/env.ru +4 -0
  90. data/t/fails-rack-lint.ru +6 -0
  91. data/t/heartbeat-timeout.ru +13 -0
  92. data/t/heartbeat-timeout.t +60 -0
  93. data/t/integration.ru +129 -0
  94. data/t/integration.t +509 -0
  95. data/t/lib.perl +309 -0
  96. data/t/listener_names.ru +5 -0
  97. data/t/my-tap-lib.sh +201 -0
  98. data/t/oob_gc.ru +18 -0
  99. data/t/oob_gc_path.ru +18 -0
  100. data/t/pid.ru +4 -0
  101. data/t/preread_input.ru +23 -0
  102. data/t/reload-bad-config.t +49 -0
  103. data/t/reopen-logs.ru +14 -0
  104. data/t/reopen-logs.t +36 -0
  105. data/t/t0010-reap-logging.sh +55 -0
  106. data/t/t0012-reload-empty-config.sh +86 -0
  107. data/t/t0013-rewindable-input-false.sh +24 -0
  108. data/t/t0013.ru +13 -0
  109. data/t/t0014-rewindable-input-true.sh +24 -0
  110. data/t/t0014.ru +13 -0
  111. data/t/t0015-configurator-internals.sh +25 -0
  112. data/t/t0020-at_exit-handler.sh +49 -0
  113. data/t/t0021-process_detach.sh +29 -0
  114. data/t/t0022-listener_names-preload_app.sh +32 -0
  115. data/t/t0300-no-default-middleware.sh +20 -0
  116. data/t/t0301-no-default-middleware-ignored-in-config.sh +25 -0
  117. data/t/t0301.ru +14 -0
  118. data/t/t9001-oob_gc.sh +47 -0
  119. data/t/t9002-oob_gc-path.sh +75 -0
  120. data/t/test-lib.sh +125 -0
  121. data/t/winch_ttin.t +64 -0
  122. data/t/working_directory.t +86 -0
  123. data/test/aggregate.rb +16 -0
  124. data/test/benchmark/README +60 -0
  125. data/test/benchmark/dd.ru +19 -0
  126. data/test/benchmark/ddstream.ru +51 -0
  127. data/test/benchmark/readinput.ru +41 -0
  128. data/test/benchmark/stack.ru +9 -0
  129. data/test/benchmark/uconnect.perl +66 -0
  130. data/test/exec/README +5 -0
  131. data/test/exec/test_exec.rb +1030 -0
  132. data/test/test_helper.rb +307 -0
  133. data/test/unit/test_configurator.rb +176 -0
  134. data/test/unit/test_droplet.rb +29 -0
  135. data/test/unit/test_http_parser.rb +885 -0
  136. data/test/unit/test_http_parser_ng.rb +715 -0
  137. data/test/unit/test_server.rb +245 -0
  138. data/test/unit/test_signals.rb +189 -0
  139. data/test/unit/test_socket_helper.rb +160 -0
  140. data/test/unit/test_stream_input.rb +211 -0
  141. data/test/unit/test_tee_input.rb +304 -0
  142. data/test/unit/test_util.rb +132 -0
  143. data/test/unit/test_waiter.rb +35 -0
  144. data/unicorn.gemspec +49 -0
  145. metadata +266 -0
@@ -0,0 +1,211 @@
1
+ # -*- encoding: binary -*-
2
+ # frozen_string_literal: false
3
+
4
+ require 'test/unit'
5
+ require 'digest/sha1'
6
+ require 'unicorn'
7
+
8
+ class TestStreamInput < Test::Unit::TestCase
9
+ def setup
10
+ @rs = "\n"
11
+ $/ == "\n" or abort %q{test broken if \$/ != "\\n"}
12
+ @env = {}
13
+ @rd, @wr = UNIXSocket.pair
14
+ @rd.sync = @wr.sync = true
15
+ @start_pid = $$
16
+ end
17
+
18
+ def teardown
19
+ return if $$ != @start_pid
20
+ @rd.close rescue nil
21
+ @wr.close rescue nil
22
+ Process.waitall
23
+ end
24
+
25
+ def test_read_negative
26
+ r = init_request('hello')
27
+ si = Unicorn::StreamInput.new(@rd, r)
28
+ assert_raises(ArgumentError) { si.read(-1) }
29
+ assert_equal 'hello', si.read
30
+ end
31
+
32
+ def test_read_small
33
+ r = init_request('hello')
34
+ si = Unicorn::StreamInput.new(@rd, r)
35
+ assert_equal 'hello', si.read
36
+ assert_equal '', si.read
37
+ assert_nil si.read(5)
38
+ assert_nil si.gets
39
+ end
40
+
41
+ def test_gets_oneliner
42
+ r = init_request('hello')
43
+ si = Unicorn::StreamInput.new(@rd, r)
44
+ assert_equal 'hello', si.gets
45
+ assert_nil si.gets
46
+ end
47
+
48
+ def test_gets_multiline
49
+ r = init_request("a\nb\n\n")
50
+ si = Unicorn::StreamInput.new(@rd, r)
51
+ assert_equal "a\n", si.gets
52
+ assert_equal "b\n", si.gets
53
+ assert_equal "\n", si.gets
54
+ assert_nil si.gets
55
+ end
56
+
57
+ def test_gets_empty_rs
58
+ r = init_request("a\nb\n\n")
59
+ si = Unicorn::StreamInput.new(@rd, r)
60
+ pid = fork do # to avoid $/ warning (hopefully)
61
+ $/ = nil
62
+ @rd.close
63
+ @wr.write(si.gets)
64
+ @wr.close
65
+ end
66
+ @wr.close
67
+ assert_equal "a\nb\n\n", @rd.read
68
+ pid, status = Process.waitpid2(pid)
69
+ assert_predicate status, :success?
70
+ end
71
+
72
+ def test_read_with_equal_len
73
+ r = init_request("abcde")
74
+ si = Unicorn::StreamInput.new(@rd, r)
75
+ assert_equal "abcde", si.read(5)
76
+ assert_nil si.read(5)
77
+ end
78
+
79
+ def test_big_body_multi
80
+ r = init_request('.', Unicorn::Const::MAX_BODY + 1)
81
+ si = Unicorn::StreamInput.new(@rd, r)
82
+ assert_equal Unicorn::Const::MAX_BODY, @parser.content_length
83
+ assert ! @parser.body_eof?
84
+ nr = Unicorn::Const::MAX_BODY / 4
85
+ pid = fork {
86
+ @rd.close
87
+ nr.times { @wr.write('....') }
88
+ @wr.close
89
+ }
90
+ @wr.close
91
+ assert_equal '.', si.read(1)
92
+ nr.times { |x|
93
+ assert_equal '....', si.read(4), "nr=#{x}"
94
+ }
95
+ assert_nil si.read(1)
96
+ pid, status = Process.waitpid2(pid)
97
+ assert status.success?
98
+ end
99
+
100
+ def test_gets_long
101
+ r = init_request("hello", 5 + (4096 * 4 * 3) + "#{@rs}foo#{@rs}".size)
102
+ si = Unicorn::StreamInput.new(@rd, r)
103
+ status = line = nil
104
+ pid = fork {
105
+ @rd.close
106
+ 3.times { @wr.write("ffff" * 4096) }
107
+ @wr.write "#{@rs}foo#{@rs}"
108
+ @wr.close
109
+ }
110
+ @wr.close
111
+ line = si.gets
112
+ assert_equal(4096 * 4 * 3 + 5 + $/.size, line.size)
113
+ assert_equal("hello" << ("ffff" * 4096 * 3) << "#{@rs}", line)
114
+ line = si.gets
115
+ assert_equal "foo#{@rs}", line
116
+ assert_nil si.gets
117
+ pid, status = Process.waitpid2(pid)
118
+ assert status.success?
119
+ end
120
+
121
+ def test_read_with_buffer
122
+ r = init_request('hello')
123
+ si = Unicorn::StreamInput.new(@rd, r)
124
+ buf = ''
125
+ rv = si.read(4, buf)
126
+ assert_equal 'hell', rv
127
+ assert_equal 'hell', buf
128
+ assert_equal rv.object_id, buf.object_id
129
+ assert_equal 'o', si.read
130
+ assert_equal nil, si.read(5, buf)
131
+ end
132
+
133
+ def test_read_with_buffer_clobbers
134
+ r = init_request('hello')
135
+ si = Unicorn::StreamInput.new(@rd, r)
136
+ buf = 'foo'
137
+ assert_equal 'hello', si.read(nil, buf)
138
+ assert_equal 'hello', buf
139
+ assert_equal '', si.read(nil, buf)
140
+ assert_equal '', buf
141
+ buf = 'asdf'
142
+ assert_nil si.read(5, buf)
143
+ assert_equal '', buf
144
+ end
145
+
146
+ def test_read_zero
147
+ r = init_request('hello')
148
+ si = Unicorn::StreamInput.new(@rd, r)
149
+ assert_equal '', si.read(0)
150
+ buf = 'asdf'
151
+ rv = si.read(0, buf)
152
+ assert_equal rv.object_id, buf.object_id
153
+ assert_equal '', buf
154
+ assert_equal 'hello', si.read
155
+ assert_nil si.read(5)
156
+ assert_equal '', si.read(0)
157
+ buf = 'hello'
158
+ rv = si.read(0, buf)
159
+ assert_equal rv.object_id, buf.object_id
160
+ assert_equal '', rv
161
+ end
162
+
163
+ def test_gets_read_mix
164
+ r = init_request("hello\nasdfasdf")
165
+ si = Unicorn::StreamInput.new(@rd, r)
166
+ assert_equal "hello\n", si.gets
167
+ assert_equal "asdfasdf", si.read(9)
168
+ assert_nil si.read(9)
169
+ end
170
+
171
+ def test_gets_read_mix_chunked
172
+ r = @parser = Unicorn::HttpParser.new
173
+ body = "6\r\nhello"
174
+ @buf = "POST / HTTP/1.1\r\n" \
175
+ "Host: localhost\r\n" \
176
+ "Transfer-Encoding: chunked\r\n" \
177
+ "\r\n#{body}"
178
+ assert_equal @env, @parser.headers(@env, @buf)
179
+ assert_equal body, @buf
180
+ si = Unicorn::StreamInput.new(@rd, r)
181
+ @wr.syswrite "\n\r\n"
182
+ assert_equal "hello\n", si.gets
183
+ @wr.syswrite "8\r\nasdfasdf\r\n"
184
+ assert_equal"asdfasdf", si.read(9) + si.read(9)
185
+ @wr.syswrite "0\r\n\r\n"
186
+ assert_nil si.read(9)
187
+ end
188
+
189
+ def test_gets_read_mix_big
190
+ r = init_request("hello\n#{'.' * 65536}")
191
+ si = Unicorn::StreamInput.new(@rd, r)
192
+ assert_equal "hello\n", si.gets
193
+ assert_equal '.' * 16384, si.read(16384)
194
+ assert_equal '.' * 16383, si.read(16383)
195
+ assert_equal '.' * 16384, si.read(16384)
196
+ assert_equal '.' * 16385, si.read(16385)
197
+ assert_nil si.gets
198
+ end
199
+
200
+ def init_request(body, size = nil)
201
+ @parser = Unicorn::HttpParser.new
202
+ body = body.to_s.freeze
203
+ @buf = "POST / HTTP/1.1\r\n" \
204
+ "Host: localhost\r\n" \
205
+ "Content-Length: #{size || body.size}\r\n" \
206
+ "\r\n#{body}"
207
+ assert_equal @env, @parser.headers(@env, @buf)
208
+ assert_equal body, @buf
209
+ @parser
210
+ end
211
+ end
@@ -0,0 +1,304 @@
1
+ # -*- encoding: binary -*-
2
+ # frozen_string_literal: false
3
+
4
+ require 'test/unit'
5
+ require 'digest/sha1'
6
+ require 'unicorn'
7
+
8
+ class TeeInput < Unicorn::TeeInput
9
+ attr_accessor :tmp, :len
10
+ end
11
+
12
+ class TestTeeInput < Test::Unit::TestCase
13
+ def setup
14
+ @rd, @wr = UNIXSocket.pair
15
+ @rd.sync = @wr.sync = true
16
+ @start_pid = $$
17
+ @rs = "\n"
18
+ $/ == "\n" or abort %q{test broken if \$/ != "\\n"}
19
+ end
20
+
21
+ def teardown
22
+ return if $$ != @start_pid
23
+ @rd.close rescue nil
24
+ @wr.close rescue nil
25
+ begin
26
+ Process.wait
27
+ rescue Errno::ECHILD
28
+ break
29
+ end while true
30
+ end
31
+
32
+ def check_tempfiles
33
+ tmp = @parser.env["rack.tempfiles"]
34
+ assert_instance_of Array, tmp
35
+ assert_operator tmp.size, :>=, 1
36
+ assert_instance_of Unicorn::TmpIO, tmp[0]
37
+ end
38
+
39
+ def test_gets_long
40
+ r = init_request("hello", 5 + (4096 * 4 * 3) + "#{@rs}foo#{@rs}".size)
41
+ ti = TeeInput.new(@rd, r)
42
+ status = line = nil
43
+ pid = fork {
44
+ @rd.close
45
+ 3.times { @wr.write("ffff" * 4096) }
46
+ @wr.write "#{@rs}foo#{@rs}"
47
+ @wr.close
48
+ }
49
+ @wr.close
50
+ line = ti.gets
51
+ assert_equal(4096 * 4 * 3 + 5 + $/.size, line.size)
52
+ assert_equal("hello" << ("ffff" * 4096 * 3) << "#{@rs}", line)
53
+ line = ti.gets
54
+ assert_equal "foo#{@rs}", line
55
+ assert_nil ti.gets
56
+ pid, status = Process.waitpid2(pid)
57
+ assert status.success?
58
+ end
59
+
60
+ def test_gets_short
61
+ r = init_request("hello", 5 + "#{@rs}foo".size)
62
+ ti = TeeInput.new(@rd, r)
63
+ status = line = nil
64
+ pid = fork {
65
+ @rd.close
66
+ @wr.write "#{@rs}foo"
67
+ @wr.close
68
+ }
69
+ @wr.close
70
+ line = ti.gets
71
+ assert_equal("hello#{@rs}", line)
72
+ line = ti.gets
73
+ assert_equal "foo", line
74
+ assert_nil ti.gets
75
+ pid, status = Process.waitpid2(pid)
76
+ assert status.success?
77
+ end
78
+
79
+ def test_small_body
80
+ r = init_request('hello')
81
+ ti = TeeInput.new(@rd, r)
82
+ assert_equal 0, @parser.content_length
83
+ assert @parser.body_eof?
84
+ assert_equal StringIO, ti.tmp.class
85
+ assert_equal 0, ti.tmp.pos
86
+ assert_equal 5, ti.size
87
+ assert_equal 'hello', ti.read
88
+ assert_equal '', ti.read
89
+ assert_nil ti.read(4096)
90
+ assert_equal 5, ti.size
91
+ end
92
+
93
+ def test_read_with_buffer
94
+ r = init_request('hello')
95
+ ti = TeeInput.new(@rd, r)
96
+ buf = ''
97
+ rv = ti.read(4, buf)
98
+ assert_equal 'hell', rv
99
+ assert_equal 'hell', buf
100
+ assert_equal rv.object_id, buf.object_id
101
+ assert_equal 'o', ti.read
102
+ assert_equal nil, ti.read(5, buf)
103
+ assert_equal 0, ti.rewind
104
+ assert_equal 'hello', ti.read(5, buf)
105
+ assert_equal 'hello', buf
106
+ end
107
+
108
+ def test_big_body
109
+ r = init_request('.' * Unicorn::Const::MAX_BODY << 'a')
110
+ ti = TeeInput.new(@rd, r)
111
+ assert_equal 0, @parser.content_length
112
+ assert @parser.body_eof?
113
+ assert_kind_of File, ti.tmp
114
+ assert_equal 0, ti.tmp.pos
115
+ assert_equal Unicorn::Const::MAX_BODY + 1, ti.size
116
+ check_tempfiles
117
+ end
118
+
119
+ def test_read_in_full_if_content_length
120
+ a, b = 300, 3
121
+ r = init_request('.' * b, 300)
122
+ assert_equal 300, @parser.content_length
123
+ ti = TeeInput.new(@rd, r)
124
+ pid = fork {
125
+ @wr.write('.' * 197)
126
+ sleep 1 # still a *potential* race here that would make the test moot...
127
+ @wr.write('.' * 100)
128
+ }
129
+ assert_equal a, ti.read(a).size
130
+ _, status = Process.waitpid2(pid)
131
+ assert status.success?
132
+ @wr.close
133
+ end
134
+
135
+ def test_big_body_multi
136
+ r = init_request('.', Unicorn::Const::MAX_BODY + 1)
137
+ ti = TeeInput.new(@rd, r)
138
+ assert_equal Unicorn::Const::MAX_BODY, @parser.content_length
139
+ assert ! @parser.body_eof?
140
+ assert_kind_of File, ti.tmp
141
+ assert_equal 0, ti.tmp.pos
142
+ assert_equal Unicorn::Const::MAX_BODY + 1, ti.size
143
+ nr = Unicorn::Const::MAX_BODY / 4
144
+ pid = fork {
145
+ @rd.close
146
+ nr.times { @wr.write('....') }
147
+ @wr.close
148
+ }
149
+ @wr.close
150
+ assert_equal '.', ti.read(1)
151
+ assert_equal Unicorn::Const::MAX_BODY + 1, ti.size
152
+ nr.times { |x|
153
+ assert_equal '....', ti.read(4), "nr=#{x}"
154
+ assert_equal Unicorn::Const::MAX_BODY + 1, ti.size
155
+ }
156
+ assert_nil ti.read(1)
157
+ pid, status = Process.waitpid2(pid)
158
+ assert status.success?
159
+ check_tempfiles
160
+ end
161
+
162
+ def test_chunked
163
+ @parser = Unicorn::HttpParser.new
164
+ @parser.buf << "POST / HTTP/1.1\r\n" \
165
+ "Host: localhost\r\n" \
166
+ "Transfer-Encoding: chunked\r\n" \
167
+ "\r\n"
168
+ assert @parser.parse
169
+ assert_equal "", @parser.buf
170
+
171
+ pid = fork {
172
+ @rd.close
173
+ 5.times { @wr.write("5\r\nabcde\r\n") }
174
+ @wr.write("0\r\n\r\n")
175
+ }
176
+ @wr.close
177
+ ti = TeeInput.new(@rd, @parser)
178
+ assert_nil @parser.content_length
179
+ assert_nil ti.len
180
+ assert ! @parser.body_eof?
181
+ assert_equal 25, ti.size
182
+ assert @parser.body_eof?
183
+ assert_equal 25, ti.len
184
+ assert_equal 0, ti.tmp.pos
185
+ ti.rewind
186
+ assert_equal 0, ti.tmp.pos
187
+ assert_equal 'abcdeabcdeabcdeabcde', ti.read(20)
188
+ assert_equal 20, ti.tmp.pos
189
+ ti.rewind
190
+ assert_equal 0, ti.tmp.pos
191
+ assert_kind_of File, ti.tmp
192
+ status = nil
193
+ pid, status = Process.waitpid2(pid)
194
+ assert status.success?
195
+ check_tempfiles
196
+ end
197
+
198
+ def test_chunked_ping_pong
199
+ @parser = Unicorn::HttpParser.new
200
+ buf = @parser.buf
201
+ buf << "POST / HTTP/1.1\r\n" \
202
+ "Host: localhost\r\n" \
203
+ "Transfer-Encoding: chunked\r\n" \
204
+ "\r\n"
205
+ assert @parser.parse
206
+ assert_equal "", buf
207
+ chunks = %w(aa bbb cccc dddd eeee)
208
+ rd, wr = IO.pipe
209
+
210
+ pid = fork {
211
+ chunks.each do |chunk|
212
+ rd.read(1) == "." and
213
+ @wr.write("#{'%x' % [ chunk.size]}\r\n#{chunk}\r\n")
214
+ end
215
+ @wr.write("0\r\n\r\n")
216
+ }
217
+ ti = TeeInput.new(@rd, @parser)
218
+ assert_nil @parser.content_length
219
+ assert_nil ti.len
220
+ assert ! @parser.body_eof?
221
+ chunks.each do |chunk|
222
+ wr.write('.')
223
+ assert_equal chunk, ti.read(16384)
224
+ end
225
+ _, status = Process.waitpid2(pid)
226
+ assert status.success?
227
+ end
228
+
229
+ def test_chunked_with_trailer
230
+ @parser = Unicorn::HttpParser.new
231
+ buf = @parser.buf
232
+ buf << "POST / HTTP/1.1\r\n" \
233
+ "Host: localhost\r\n" \
234
+ "Trailer: Hello\r\n" \
235
+ "Transfer-Encoding: chunked\r\n" \
236
+ "\r\n"
237
+ assert @parser.parse
238
+ assert_equal "", buf
239
+
240
+ pid = fork {
241
+ @rd.close
242
+ 5.times { @wr.write("5\r\nabcde\r\n") }
243
+ @wr.write("0\r\n")
244
+ @wr.write("Hello: World\r\n\r\n")
245
+ }
246
+ @wr.close
247
+ ti = TeeInput.new(@rd, @parser)
248
+ assert_nil @parser.content_length
249
+ assert_nil ti.len
250
+ assert ! @parser.body_eof?
251
+ assert_equal 25, ti.size
252
+ assert_equal "World", @parser.env['HTTP_HELLO']
253
+ pid, status = Process.waitpid2(pid)
254
+ assert status.success?
255
+ end
256
+
257
+ def test_chunked_and_size_slow
258
+ @parser = Unicorn::HttpParser.new
259
+ buf = @parser.buf
260
+ buf << "POST / HTTP/1.1\r\n" \
261
+ "Host: localhost\r\n" \
262
+ "Trailer: Hello\r\n" \
263
+ "Transfer-Encoding: chunked\r\n" \
264
+ "\r\n"
265
+ assert @parser.parse
266
+ assert_equal "", buf
267
+
268
+ @wr.write("9\r\nabcde")
269
+ ti = TeeInput.new(@rd, @parser)
270
+ assert_nil @parser.content_length
271
+ assert_equal "abcde", ti.read(9)
272
+ assert ! @parser.body_eof?
273
+ @wr.write("fghi\r\n0\r\nHello: World\r\n\r\n")
274
+ assert_equal 9, ti.size
275
+ assert_equal "fghi", ti.read(9)
276
+ assert_equal nil, ti.read(9)
277
+ assert_equal "World", @parser.env['HTTP_HELLO']
278
+ end
279
+
280
+ def test_gets_read_mix
281
+ r = init_request("hello\nasdfasdf")
282
+ ti = Unicorn::TeeInput.new(@rd, r)
283
+ assert_equal "hello\n", ti.gets
284
+ assert_equal "asdfasdf", ti.read(9)
285
+ assert_nil ti.read(9)
286
+ end
287
+
288
+ private
289
+
290
+ def init_request(body, size = nil)
291
+ @parser = Unicorn::HttpParser.new
292
+ body = body.to_s.freeze
293
+ buf = @parser.buf
294
+ buf << "POST / HTTP/1.1\r\n" \
295
+ "Host: localhost\r\n" \
296
+ "Content-Length: #{size || body.size}\r\n" \
297
+ "\r\n#{body}"
298
+ assert @parser.parse
299
+ assert_equal body, buf
300
+ @buf = buf
301
+ @parser
302
+ end
303
+
304
+ end
@@ -0,0 +1,132 @@
1
+ # -*- encoding: binary -*-
2
+ # frozen_string_literal: false
3
+
4
+ require './test/test_helper'
5
+ require 'tempfile'
6
+
7
+ class TestUtil < Test::Unit::TestCase
8
+
9
+ EXPECT_FLAGS = File::WRONLY | File::APPEND
10
+ def test_reopen_logs_noop
11
+ tmp = Tempfile.new('')
12
+ fp = File.open(tmp.path, 'ab')
13
+ fp.sync = true
14
+ ext = fp.external_encoding rescue nil
15
+ int = fp.internal_encoding rescue nil
16
+ before = fp.stat.inspect
17
+ Unicorn::Util.reopen_logs
18
+ assert_equal before, File.stat(fp.path).inspect
19
+ assert_equal ext, (fp.external_encoding rescue nil)
20
+ assert_equal int, (fp.internal_encoding rescue nil)
21
+ assert_equal(EXPECT_FLAGS, EXPECT_FLAGS & fp.fcntl(Fcntl::F_GETFL))
22
+ tmp.close!
23
+ fp.close
24
+ end
25
+
26
+ def test_reopen_logs_renamed
27
+ tmp = Tempfile.new('')
28
+ tmp_path = tmp.path.freeze
29
+ fp = File.open(tmp_path, 'ab')
30
+ fp.sync = true
31
+
32
+ ext = fp.external_encoding rescue nil
33
+ int = fp.internal_encoding rescue nil
34
+ before = fp.stat.inspect
35
+ to = Tempfile.new('')
36
+ File.rename(tmp_path, to.path)
37
+ assert ! File.exist?(tmp_path)
38
+ Unicorn::Util.reopen_logs
39
+ assert_equal tmp_path, tmp.path
40
+ assert File.exist?(tmp_path)
41
+ assert before != File.stat(tmp_path).inspect
42
+ assert_equal fp.stat.inspect, File.stat(tmp_path).inspect
43
+ assert_equal ext, (fp.external_encoding rescue nil)
44
+ assert_equal int, (fp.internal_encoding rescue nil)
45
+ assert_equal(EXPECT_FLAGS, EXPECT_FLAGS & fp.fcntl(Fcntl::F_GETFL))
46
+ assert fp.sync
47
+ tmp.close!
48
+ to.close!
49
+ fp.close
50
+ end
51
+
52
+ def test_reopen_logs_renamed_with_encoding
53
+ tmp = Tempfile.new('')
54
+ tmp_path = tmp.path.dup.freeze
55
+ Encoding.list.sample(5).each { |encoding|
56
+ File.open(tmp_path, "a:#{encoding.to_s}") { |fp|
57
+ fp.sync = true
58
+ assert_equal encoding, fp.external_encoding
59
+ assert_nil fp.internal_encoding
60
+ File.unlink(tmp_path)
61
+ assert ! File.exist?(tmp_path)
62
+ Unicorn::Util.reopen_logs
63
+ assert_equal tmp_path, fp.path
64
+ assert File.exist?(tmp_path)
65
+ assert_equal fp.stat.inspect, File.stat(tmp_path).inspect
66
+ assert_equal encoding, fp.external_encoding
67
+ assert_nil fp.internal_encoding
68
+ assert_equal(EXPECT_FLAGS, EXPECT_FLAGS & fp.fcntl(Fcntl::F_GETFL))
69
+ assert fp.sync
70
+ }
71
+ }
72
+ tmp.close!
73
+ end
74
+
75
+ def test_reopen_logs_renamed_with_internal_encoding
76
+ tmp = Tempfile.new('')
77
+ tmp_path = tmp.path.dup.freeze
78
+ full = Encoding.list
79
+ full.sample(2).each { |ext|
80
+ full.sample(2).each { |int|
81
+ next if ext == int
82
+ File.open(tmp_path, "a:#{ext.to_s}:#{int.to_s}") { |fp|
83
+ fp.sync = true
84
+ assert_equal ext, fp.external_encoding
85
+
86
+ if ext != Encoding::BINARY
87
+ assert_equal int, fp.internal_encoding
88
+ end
89
+
90
+ File.unlink(tmp_path)
91
+ assert ! File.exist?(tmp_path)
92
+ Unicorn::Util.reopen_logs
93
+ assert_equal tmp_path, fp.path
94
+ assert File.exist?(tmp_path)
95
+ assert_equal fp.stat.inspect, File.stat(tmp_path).inspect
96
+ assert_equal ext, fp.external_encoding
97
+ if ext != Encoding::BINARY
98
+ assert_equal int, fp.internal_encoding
99
+ end
100
+ assert_equal(EXPECT_FLAGS, EXPECT_FLAGS & fp.fcntl(Fcntl::F_GETFL))
101
+ assert fp.sync
102
+ }
103
+ }
104
+ }
105
+ tmp.close!
106
+ end
107
+
108
+ def test_pipe
109
+ r, w = Unicorn.pipe
110
+ assert r
111
+ assert w
112
+
113
+ return if RUBY_PLATFORM !~ /linux/
114
+
115
+ begin
116
+ f_getpipe_sz = 1032
117
+ IO.pipe do |a, b|
118
+ a_sz = a.fcntl(f_getpipe_sz)
119
+ b.fcntl(f_getpipe_sz)
120
+ assert_kind_of Integer, a_sz
121
+ r_sz = r.fcntl(f_getpipe_sz)
122
+ assert_equal Raindrops::PAGE_SIZE, r_sz
123
+ assert_operator a_sz, :>=, r_sz
124
+ end
125
+ rescue Errno::EINVAL
126
+ # Linux <= 2.6.34
127
+ end
128
+ ensure
129
+ w.close
130
+ r.close
131
+ end
132
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: false
2
+ require 'test/unit'
3
+ require 'unicorn'
4
+ require 'unicorn/select_waiter'
5
+ class TestSelectWaiter < Test::Unit::TestCase
6
+
7
+ def test_select_timeout # n.b. this is level-triggered
8
+ sw = Unicorn::SelectWaiter.new
9
+ IO.pipe do |r,w|
10
+ sw.get_readers(ready = [], [r], 0)
11
+ assert_equal [], ready
12
+ w.syswrite '.'
13
+ sw.get_readers(ready, [r], 1000)
14
+ assert_equal [r], ready
15
+ sw.get_readers(ready, [r], 0)
16
+ assert_equal [r], ready
17
+ end
18
+ end
19
+
20
+ def test_linux # ugh, also level-triggered, unlikely to change
21
+ IO.pipe do |r,w|
22
+ wtr = Unicorn::Waiter.prep_readers([r])
23
+ wtr.get_readers(ready = [], [r], 0)
24
+ assert_equal [], ready
25
+ w.syswrite '.'
26
+ wtr.get_readers(ready = [], [r], 1000)
27
+ assert_equal [r], ready
28
+ wtr.get_readers(ready = [], [r], 1000)
29
+ assert_equal [r], ready, 'still ready (level-triggered :<)'
30
+ assert_nil wtr.close
31
+ end
32
+ rescue SystemCallError => e
33
+ warn "#{e.message} (#{e.class})"
34
+ end if Unicorn.const_defined?(:Waiter)
35
+ end