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.
- checksums.yaml +7 -0
- data/.CHANGELOG.old +25 -0
- data/.document +28 -0
- data/.gitattributes +5 -0
- data/.gitignore +25 -0
- data/.mailmap +26 -0
- data/.manifest +144 -0
- data/.olddoc.yml +25 -0
- data/Application_Timeouts +77 -0
- data/CONTRIBUTORS +39 -0
- data/COPYING +674 -0
- data/DESIGN +99 -0
- data/Documentation/.gitignore +3 -0
- data/Documentation/unicorn.1 +222 -0
- data/Documentation/unicorn_rails.1 +207 -0
- data/FAQ +70 -0
- data/GIT-VERSION-FILE +1 -0
- data/GIT-VERSION-GEN +39 -0
- data/GNUmakefile +318 -0
- data/HACKING +117 -0
- data/ISSUES +102 -0
- data/KNOWN_ISSUES +79 -0
- data/LICENSE +67 -0
- data/Links +58 -0
- data/PHILOSOPHY +139 -0
- data/README +165 -0
- data/Rakefile +17 -0
- data/SIGNALS +123 -0
- data/Sandbox +104 -0
- data/TODO +1 -0
- data/TUNING +119 -0
- data/archive/.gitignore +3 -0
- data/archive/slrnpull.conf +4 -0
- data/bin/unicorn +129 -0
- data/bin/unicorn_rails +210 -0
- data/examples/big_app_gc.rb +3 -0
- data/examples/echo.ru +27 -0
- data/examples/init.sh +102 -0
- data/examples/logger_mp_safe.rb +26 -0
- data/examples/logrotate.conf +44 -0
- data/examples/nginx.conf +156 -0
- data/examples/unicorn.conf.minimal.rb +14 -0
- data/examples/unicorn.conf.rb +111 -0
- data/examples/unicorn.socket +11 -0
- data/examples/unicorn@.service +40 -0
- data/ext/unicorn_http/CFLAGS +13 -0
- data/ext/unicorn_http/c_util.h +115 -0
- data/ext/unicorn_http/common_field_optimization.h +128 -0
- data/ext/unicorn_http/epollexclusive.h +128 -0
- data/ext/unicorn_http/ext_help.h +38 -0
- data/ext/unicorn_http/extconf.rb +40 -0
- data/ext/unicorn_http/global_variables.h +97 -0
- data/ext/unicorn_http/httpdate.c +91 -0
- data/ext/unicorn_http/unicorn_http.c +4348 -0
- data/ext/unicorn_http/unicorn_http.rl +1054 -0
- data/ext/unicorn_http/unicorn_http_common.rl +76 -0
- data/lib/unicorn/app/old_rails/static.rb +60 -0
- data/lib/unicorn/app/old_rails.rb +36 -0
- data/lib/unicorn/cgi_wrapper.rb +148 -0
- data/lib/unicorn/configurator.rb +749 -0
- data/lib/unicorn/const.rb +22 -0
- data/lib/unicorn/http_request.rb +180 -0
- data/lib/unicorn/http_response.rb +95 -0
- data/lib/unicorn/http_server.rb +860 -0
- data/lib/unicorn/launcher.rb +63 -0
- data/lib/unicorn/oob_gc.rb +82 -0
- data/lib/unicorn/preread_input.rb +34 -0
- data/lib/unicorn/select_waiter.rb +7 -0
- data/lib/unicorn/socket_helper.rb +186 -0
- data/lib/unicorn/stream_input.rb +152 -0
- data/lib/unicorn/tee_input.rb +132 -0
- data/lib/unicorn/tmpio.rb +34 -0
- data/lib/unicorn/util.rb +91 -0
- data/lib/unicorn/version.rb +1 -0
- data/lib/unicorn/worker.rb +166 -0
- data/lib/unicorn.rb +137 -0
- data/man/man1/unicorn.1 +222 -0
- data/man/man1/unicorn_rails.1 +207 -0
- data/setup.rb +1587 -0
- data/t/.gitignore +4 -0
- data/t/GNUmakefile +5 -0
- data/t/README +49 -0
- data/t/active-unix-socket.t +110 -0
- data/t/back-out-of-upgrade.t +44 -0
- data/t/bin/unused_listen +40 -0
- data/t/client_body_buffer_size.ru +15 -0
- data/t/client_body_buffer_size.t +79 -0
- data/t/detach.ru +12 -0
- data/t/env.ru +4 -0
- data/t/fails-rack-lint.ru +6 -0
- data/t/heartbeat-timeout.ru +13 -0
- data/t/heartbeat-timeout.t +60 -0
- data/t/integration.ru +129 -0
- data/t/integration.t +509 -0
- data/t/lib.perl +309 -0
- data/t/listener_names.ru +5 -0
- data/t/my-tap-lib.sh +201 -0
- data/t/oob_gc.ru +18 -0
- data/t/oob_gc_path.ru +18 -0
- data/t/pid.ru +4 -0
- data/t/preread_input.ru +23 -0
- data/t/reload-bad-config.t +49 -0
- data/t/reopen-logs.ru +14 -0
- data/t/reopen-logs.t +36 -0
- data/t/t0010-reap-logging.sh +55 -0
- data/t/t0012-reload-empty-config.sh +86 -0
- data/t/t0013-rewindable-input-false.sh +24 -0
- data/t/t0013.ru +13 -0
- data/t/t0014-rewindable-input-true.sh +24 -0
- data/t/t0014.ru +13 -0
- data/t/t0015-configurator-internals.sh +25 -0
- data/t/t0020-at_exit-handler.sh +49 -0
- data/t/t0021-process_detach.sh +29 -0
- data/t/t0022-listener_names-preload_app.sh +32 -0
- data/t/t0300-no-default-middleware.sh +20 -0
- data/t/t0301-no-default-middleware-ignored-in-config.sh +25 -0
- data/t/t0301.ru +14 -0
- data/t/t9001-oob_gc.sh +47 -0
- data/t/t9002-oob_gc-path.sh +75 -0
- data/t/test-lib.sh +125 -0
- data/t/winch_ttin.t +64 -0
- data/t/working_directory.t +86 -0
- data/test/aggregate.rb +16 -0
- data/test/benchmark/README +60 -0
- data/test/benchmark/dd.ru +19 -0
- data/test/benchmark/ddstream.ru +51 -0
- data/test/benchmark/readinput.ru +41 -0
- data/test/benchmark/stack.ru +9 -0
- data/test/benchmark/uconnect.perl +66 -0
- data/test/exec/README +5 -0
- data/test/exec/test_exec.rb +1030 -0
- data/test/test_helper.rb +307 -0
- data/test/unit/test_configurator.rb +176 -0
- data/test/unit/test_droplet.rb +29 -0
- data/test/unit/test_http_parser.rb +885 -0
- data/test/unit/test_http_parser_ng.rb +715 -0
- data/test/unit/test_server.rb +245 -0
- data/test/unit/test_signals.rb +189 -0
- data/test/unit/test_socket_helper.rb +160 -0
- data/test/unit/test_stream_input.rb +211 -0
- data/test/unit/test_tee_input.rb +304 -0
- data/test/unit/test_util.rb +132 -0
- data/test/unit/test_waiter.rb +35 -0
- data/unicorn.gemspec +49 -0
- 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
|