1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
| | require 'test/unit'
require 'unicorn'
require 'unicorn/http11'
require 'tempfile'
require 'io/nonblock'
require 'digest/sha1'
class TestChunkedReader < Test::Unit::TestCase
def setup
@env = {}
@rd, @wr = IO.pipe
@rd.binmode
@wr.binmode
@rd.sync = @wr.sync = true
@start_pid = $$
end
def teardown
return if $$ != @start_pid
@rd.close rescue nil
@wr.close rescue nil
begin
Process.wait
rescue Errno::ECHILD
break
end while true
end
def test_error
cr = bin_reader("8\r\nasdfasdf\r\n8\r\nasdfasdfa#{'a' * 1024}")
a = nil
assert_nothing_raised { a = cr.readpartial(8192) }
assert_equal 'asdfasdf', a
assert_nothing_raised { a = cr.readpartial(8192) }
assert_equal 'asdfasdf', a
assert_raises(Unicorn::HttpParserError) { cr.readpartial(8192) }
end
def test_eof1
cr = bin_reader("0\r\n")
assert_raises(EOFError) { cr.readpartial(8192) }
end
def test_eof2
cr = bin_reader("0\r\n\r\n")
assert_raises(EOFError) { cr.readpartial(8192) }
end
def test_readpartial1
cr = bin_reader("4\r\nasdf\r\n0\r\n")
assert_equal 'asdf', cr.readpartial(8192)
assert_raises(EOFError) { cr.readpartial(8192) }
end
def test_gets1
cr = bin_reader("4\r\nasdf\r\n0\r\n")
STDOUT.sync = true
assert_equal 'asdf', cr.gets
assert_raises(EOFError) { cr.readpartial(8192) }
end
def test_gets2
cr = bin_reader("4\r\nasd\n\r\n0\r\n\r\n")
assert_equal "asd\n", cr.gets
assert_nil cr.gets
end
def test_gets3
max = Unicorn::Const::CHUNK_SIZE * 2
str = ('a' * max).freeze
first = 5
last = str.size - first
cr = bin_reader(
"#{'%x' % first}\r\n#{str[0, first]}\r\n" \
"#{'%x' % last}\r\n#{str[-last, last]}\r\n" \
"0\r\n")
assert_equal str, cr.gets
assert_nil cr.gets
end
def test_readpartial_gets_mixed1
max = Unicorn::Const::CHUNK_SIZE * 2
str = ('a' * max).freeze
first = 5
last = str.size - first
cr = bin_reader(
"#{'%x' % first}\r\n#{str[0, first]}\r\n" \
"#{'%x' % last}\r\n#{str[-last, last]}\r\n" \
"0\r\n")
partial = cr.readpartial(16384)
assert String === partial
len = max - partial.size
assert_equal(str[-len, len], cr.gets)
assert_raises(EOFError) { cr.readpartial(1) }
assert_nil cr.gets
end
def test_gets_mixed_readpartial
max = 10
str = ("z\n" * max).freeze
first = 5
last = str.size - first
cr = bin_reader(
"#{'%x' % first}\r\n#{str[0, first]}\r\n" \
"#{'%x' % last}\r\n#{str[-last, last]}\r\n" \
"0\r\n")
assert_equal("z\n", cr.gets)
assert_equal("z\n", cr.gets)
end
def test_dd
cr = bin_reader("6\r\nhello\n\r\n")
tmp = Tempfile.new('test_dd')
tmp.sync = true
pid = fork {
crd, cwr = IO.pipe
crd.binmode
cwr.binmode
crd.sync = cwr.sync = true
pid = fork {
STDOUT.reopen(cwr)
crd.close
cwr.close
exec('dd', 'if=/dev/urandom', 'bs=93390', 'count=16')
}
cwr.close
begin
buf = crd.readpartial(16384)
tmp.write(buf)
@wr.write("#{'%x' % buf.size}\r\n#{buf}\r\n")
rescue EOFError
@wr.write("0\r\n\r\n")
Process.waitpid(pid)
exit 0
end while true
}
assert_equal "hello\n", cr.gets
sha1 = Digest::SHA1.new
buf = Unicorn::Z.dup
begin
cr.readpartial(16384, buf)
sha1.update(buf)
rescue EOFError
break
end while true
assert_nothing_raised { Process.waitpid(pid) }
sha1_file = Digest::SHA1.new
File.open(tmp.path, 'rb') { |fp|
while fp.read(16384, buf)
sha1_file.update(buf)
end
}
assert_equal sha1_file.hexdigest, sha1.hexdigest
end
def test_trailer
@env['HTTP_TRAILER'] = 'Content-MD5'
pid = fork { @wr.syswrite("Content-MD5: asdf\r\n") }
cr = bin_reader("8\r\nasdfasdf\r\n8\r\nasdfasdf\r\n0\r\n")
assert_equal 'asdfasdf', cr.readpartial(4096)
assert_equal 'asdfasdf', cr.readpartial(4096)
assert_raises(EOFError) { cr.readpartial(4096) }
pid, status = Process.waitpid2(pid)
assert status.success?
assert_equal 'asdf', @env['HTTP_CONTENT_MD5']
end
private
def bin_reader(buf)
buf.force_encoding(Encoding::BINARY) if buf.respond_to?(:force_encoding)
Unicorn::ChunkedReader.new(@env, @rd, buf)
end
end
|