kcar 0.1.0
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.
- data/.document +7 -0
- data/.gitignore +20 -0
- data/COPYING +339 -0
- data/GIT-VERSION-GEN +40 -0
- data/GNUmakefile +197 -0
- data/LICENSE +55 -0
- data/README +86 -0
- data/Rakefile +149 -0
- data/TODO +6 -0
- data/ext/kcar/c_util.h +105 -0
- data/ext/kcar/ext_help.h +82 -0
- data/ext/kcar/extconf.rb +14 -0
- data/ext/kcar/kcar.rl +656 -0
- data/ext/kcar/kcar_http_common.rl +56 -0
- data/kcar.gemspec +40 -0
- data/lib/kcar.rb +11 -0
- data/lib/kcar/parser.rb +39 -0
- data/lib/kcar/response.rb +168 -0
- data/setup.rb +1586 -0
- data/test/test_parser.rb +257 -0
- data/test/test_response.rb +415 -0
- metadata +96 -0
data/test/test_parser.rb
ADDED
@@ -0,0 +1,257 @@
|
|
1
|
+
# -*- encoding: binary -*-
|
2
|
+
require 'test/unit'
|
3
|
+
require 'pp'
|
4
|
+
require 'kcar'
|
5
|
+
require 'rack'
|
6
|
+
|
7
|
+
class TestParser < Test::Unit::TestCase
|
8
|
+
|
9
|
+
def setup
|
10
|
+
@hp = Kcar::Parser.new
|
11
|
+
end
|
12
|
+
|
13
|
+
def test_reset
|
14
|
+
assert_nothing_raised { @hp.reset }
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_parser_status_eof
|
18
|
+
buf = "HTTP/1.0 200 OK\r\n\r\n"
|
19
|
+
hdr = []
|
20
|
+
hdr_object_id = hdr.object_id
|
21
|
+
response = @hp.headers(hdr, buf)
|
22
|
+
assert_equal(["200 OK", hdr], response)
|
23
|
+
assert hdr.empty?
|
24
|
+
assert ! @hp.keepalive?
|
25
|
+
assert_equal hdr_object_id, hdr.object_id
|
26
|
+
assert_equal "", buf
|
27
|
+
end
|
28
|
+
|
29
|
+
def test_parser_status_eof_one_one
|
30
|
+
buf = "HTTP/1.1 200 OK\r\n\r\n"
|
31
|
+
hdr = []
|
32
|
+
response = @hp.headers(hdr, buf)
|
33
|
+
assert_equal(["200 OK", hdr], response)
|
34
|
+
assert hdr.empty?
|
35
|
+
assert ! @hp.keepalive? # no content-length
|
36
|
+
end
|
37
|
+
|
38
|
+
def test_parser_status_with_content_length
|
39
|
+
buf = "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"
|
40
|
+
hdr = []
|
41
|
+
response = @hp.headers(hdr, buf)
|
42
|
+
assert_equal(["200 OK", hdr], response)
|
43
|
+
assert_equal([%w(Content-Length 0)], hdr)
|
44
|
+
assert @hp.keepalive?
|
45
|
+
end
|
46
|
+
|
47
|
+
def test_parser_content_length
|
48
|
+
buf = "HTTP/1.1 200 OK\r\nContent-Length: 5\r\n\r\n"
|
49
|
+
rv = @hp.headers([], buf)
|
50
|
+
assert_equal "200 OK", rv[0]
|
51
|
+
assert_equal([ %w(Content-Length 5) ], rv[1])
|
52
|
+
assert_equal 2, rv.size
|
53
|
+
assert_equal "", buf
|
54
|
+
assert_equal 5, @hp.body_bytes_left
|
55
|
+
end
|
56
|
+
|
57
|
+
def test_parser_content_length_with_body
|
58
|
+
buf = "HTTP/1.1 200 OK\r\nContent-Length: 5\r\n\r\nabcde"
|
59
|
+
rv = @hp.headers([], buf)
|
60
|
+
assert_equal "200 OK", rv[0]
|
61
|
+
assert_equal([ %w(Content-Length 5) ], rv[1])
|
62
|
+
assert_equal "abcde", buf
|
63
|
+
assert_equal 5, @hp.body_bytes_left
|
64
|
+
end
|
65
|
+
|
66
|
+
def test_bad_crlf
|
67
|
+
buf = "HTTP/1.1 200 OK\nContent-Length: 5\n\r\nabcde"
|
68
|
+
rv = @hp.headers([], buf)
|
69
|
+
assert_equal "200 OK", rv[0]
|
70
|
+
assert_equal([ %w(Content-Length 5) ], rv[1])
|
71
|
+
assert_equal "abcde", buf
|
72
|
+
assert_equal 5, @hp.body_bytes_left
|
73
|
+
assert ! @hp.chunked?
|
74
|
+
end
|
75
|
+
|
76
|
+
def test_chunky_bad_crlf
|
77
|
+
buf = "HTTP/1.1 200 OK\n" \
|
78
|
+
"Transfer-Encoding: chunked\n\n" \
|
79
|
+
"6\nabcdef\n0\n\n"
|
80
|
+
rv = @hp.headers([], buf)
|
81
|
+
assert_equal "200 OK", rv[0]
|
82
|
+
assert_equal([ %w(Transfer-Encoding chunked) ], rv[1])
|
83
|
+
assert_equal "6\nabcdef\n0\n\n", buf
|
84
|
+
assert_nil @hp.body_bytes_left
|
85
|
+
assert @hp.chunked?
|
86
|
+
assert_nil @hp.filter_body(tmp = "", buf)
|
87
|
+
assert_equal "abcdef", tmp
|
88
|
+
assert @hp.keepalive?
|
89
|
+
end
|
90
|
+
|
91
|
+
def test_chunky
|
92
|
+
buf = "HTTP/1.1 200 OK\r\n" \
|
93
|
+
"Transfer-Encoding: chunked\r\n\r\n" \
|
94
|
+
"6\r\nabcdef\r\n0\r\n\r\n"
|
95
|
+
rv = @hp.headers([], buf)
|
96
|
+
assert_equal "200 OK", rv[0]
|
97
|
+
assert_equal([ %w(Transfer-Encoding chunked) ], rv[1])
|
98
|
+
assert_equal "6\r\nabcdef\r\n0\r\n\r\n", buf
|
99
|
+
assert_nil @hp.body_bytes_left
|
100
|
+
assert @hp.chunked?
|
101
|
+
assert_nil @hp.filter_body(tmp = "", buf)
|
102
|
+
assert_equal "abcdef", tmp
|
103
|
+
assert @hp.body_eof?
|
104
|
+
assert @hp.keepalive?
|
105
|
+
end
|
106
|
+
|
107
|
+
def test_chunky_two_step
|
108
|
+
buf = "HTTP/1.1 200 OK\r\n" \
|
109
|
+
"Transfer-Encoding: chunked\r\n\r\n" \
|
110
|
+
"6\r\nabcd"
|
111
|
+
buf2 = "ef\r\n0\r\n\r\n"
|
112
|
+
rv = @hp.headers([], buf)
|
113
|
+
assert_equal "200 OK", rv[0]
|
114
|
+
assert_equal([ %w(Transfer-Encoding chunked) ], rv[1])
|
115
|
+
assert_equal "6\r\nabcd", buf
|
116
|
+
assert_nil @hp.body_bytes_left
|
117
|
+
assert @hp.chunked?
|
118
|
+
assert_nil @hp.filter_body(tmp = "", buf)
|
119
|
+
assert_equal "abcd", tmp
|
120
|
+
assert_equal "", buf
|
121
|
+
assert ! @hp.body_eof?
|
122
|
+
assert_nil @hp.filter_body(tmp = "", buf2)
|
123
|
+
assert_equal "ef", tmp
|
124
|
+
assert @hp.body_eof?
|
125
|
+
assert_equal({}, @hp.trailers(tmp = {}, buf2))
|
126
|
+
assert @hp.keepalive?
|
127
|
+
assert_nothing_raised { @hp.reset }
|
128
|
+
end
|
129
|
+
|
130
|
+
def test_trailers_ary
|
131
|
+
buf = "HTTP/1.1 200 OK\r\n" \
|
132
|
+
"Trailer: Foo\r\n" \
|
133
|
+
"Transfer-Encoding: chunked\r\n\r\n" \
|
134
|
+
"6\r\nabcdef\r\n0\r\nFoo: bar\r\n\r\n"
|
135
|
+
rv = @hp.headers([], buf)
|
136
|
+
assert_equal "200 OK", rv[0]
|
137
|
+
assert_equal([ %w(Trailer Foo), %w(Transfer-Encoding chunked) ], rv[1])
|
138
|
+
assert_equal "6\r\nabcdef\r\n0\r\nFoo: bar\r\n\r\n", buf
|
139
|
+
assert_nil @hp.body_bytes_left
|
140
|
+
assert @hp.chunked?
|
141
|
+
assert_nil @hp.filter_body(tmp = "", buf)
|
142
|
+
assert_equal "abcdef", tmp
|
143
|
+
assert @hp.body_eof?
|
144
|
+
expect = [ %w(Trailer Foo),
|
145
|
+
%w(Transfer-Encoding chunked),
|
146
|
+
%w(Foo bar) ]
|
147
|
+
assert_equal(expect, @hp.trailers(rv[1], buf))
|
148
|
+
assert @hp.keepalive?
|
149
|
+
assert_nothing_raised { @hp.reset }
|
150
|
+
end
|
151
|
+
|
152
|
+
def test_extract_trailers_ary
|
153
|
+
tmp = [ %w(Trailer Foo), %w(Transfer-Encoding chunked), %w(Foo bar) ]
|
154
|
+
assert_equal [ %w(Foo bar) ], @hp.extract_trailers(tmp)
|
155
|
+
end
|
156
|
+
|
157
|
+
def test_extract_trailers_hash
|
158
|
+
tmp = {
|
159
|
+
'Trailer' => 'Foo',
|
160
|
+
'Transfer-Encoding' => 'chunked',
|
161
|
+
'Foo' => 'bar'
|
162
|
+
}
|
163
|
+
assert_equal [ %w(Foo bar) ], @hp.extract_trailers(tmp)
|
164
|
+
end
|
165
|
+
|
166
|
+
def test_extract_trailers_header_hash
|
167
|
+
tmp = Rack::Utils::HeaderHash.new(
|
168
|
+
'Trailer' => 'foo',
|
169
|
+
'Transfer-Encoding' => 'chunked',
|
170
|
+
'Foo' => 'bar'
|
171
|
+
)
|
172
|
+
assert_equal [ %w(foo bar) ], @hp.extract_trailers(tmp)
|
173
|
+
end
|
174
|
+
|
175
|
+
def test_repeated_headers_rack_hash
|
176
|
+
hdr = Rack::Utils::HeaderHash.new
|
177
|
+
buf = "HTTP/1.1 200 OK\r\nSet-Cookie: a=b\r\n"
|
178
|
+
assert_nil @hp.headers(hdr, buf)
|
179
|
+
assert_equal({ 'Set-Cookie' => 'a=b' }, hdr.to_hash)
|
180
|
+
assert_nil @hp.headers(hdr, buf << "set-cookie: c=d\r\n")
|
181
|
+
assert_equal([ "200 OK", hdr ], @hp.headers(hdr, buf << "\r\n"))
|
182
|
+
assert_equal "", buf
|
183
|
+
assert_equal({ 'Set-Cookie' => "a=b\nc=d" }, hdr.to_hash)
|
184
|
+
end
|
185
|
+
|
186
|
+
def test_repeated_headers_plain_hash
|
187
|
+
hdr = {}
|
188
|
+
buf = "HTTP/1.1 200 OK\r\nSet-Cookie: a=b\r\n"
|
189
|
+
assert_nil @hp.headers(hdr, buf)
|
190
|
+
assert_equal({ 'Set-Cookie' => 'a=b' }, hdr)
|
191
|
+
assert_nil @hp.headers(hdr, buf << "set-cookie: c=d\r\n")
|
192
|
+
assert_equal([ "200 OK", hdr ], @hp.headers(hdr, buf << "\r\n"))
|
193
|
+
assert_equal "", buf
|
194
|
+
assert_equal({ 'Set-Cookie' => 'a=b', 'set-cookie' => 'c=d' }, hdr)
|
195
|
+
end
|
196
|
+
|
197
|
+
def test_repeated_headers_array
|
198
|
+
hdr = []
|
199
|
+
buf = "HTTP/1.1 200 OK\r\nSet-Cookie: a=b\r\n"
|
200
|
+
assert_nil @hp.headers(hdr, buf)
|
201
|
+
assert_equal([ %w(Set-Cookie a=b) ] , hdr)
|
202
|
+
assert_nil @hp.headers(hdr, buf << "set-cookie: c=d\r\n")
|
203
|
+
assert_equal([ "200 OK", hdr ], @hp.headers(hdr, buf << "\r\n"))
|
204
|
+
assert_equal "", buf
|
205
|
+
assert_equal([ %w(Set-Cookie a=b), %w(set-cookie c=d) ], hdr)
|
206
|
+
end
|
207
|
+
|
208
|
+
def test_long_line_headers_array
|
209
|
+
hdr = []
|
210
|
+
buf = "HTTP/1.1 200 OK\r\na: b\r\n"
|
211
|
+
assert_nil @hp.headers(hdr, buf)
|
212
|
+
assert_equal([ %w(a b) ] , hdr)
|
213
|
+
assert_nil @hp.headers(hdr, buf << " c\r\n")
|
214
|
+
assert_equal([ [ 'a', 'b c'] ], hdr)
|
215
|
+
assert_nil @hp.headers(hdr, buf << " d\n")
|
216
|
+
assert_equal([ [ 'a', 'b c d'] ], hdr)
|
217
|
+
assert_equal([ "200 OK", hdr ], @hp.headers(hdr, buf << "\r\n"))
|
218
|
+
assert_equal([ [ 'a', 'b c d'] ], hdr)
|
219
|
+
end
|
220
|
+
|
221
|
+
def test_long_line_headers_plain_hash
|
222
|
+
hdr = {}
|
223
|
+
buf = "HTTP/1.1 200 OK\r\na: b\r\n"
|
224
|
+
assert_nil @hp.headers(hdr, buf)
|
225
|
+
assert_equal({ 'a' => 'b' }, hdr)
|
226
|
+
assert_nil @hp.headers(hdr, buf << " c\r\n")
|
227
|
+
assert_equal({ 'a' => 'b c' }, hdr)
|
228
|
+
assert_nil @hp.headers(hdr, buf << " d\r\n")
|
229
|
+
assert_equal({ 'a' => 'b c d' }, hdr)
|
230
|
+
assert_equal([ "200 OK", hdr ], @hp.headers(hdr, buf << "\r\n"))
|
231
|
+
assert_equal({ 'a' => 'b c d' }, hdr)
|
232
|
+
end
|
233
|
+
|
234
|
+
def test_long_line_headers_rack_hash
|
235
|
+
hdr = Rack::Utils::HeaderHash.new
|
236
|
+
buf = "HTTP/1.1 200 OK\r\na: b\r\n"
|
237
|
+
assert_nil @hp.headers(hdr, buf)
|
238
|
+
assert_equal({ 'a' => 'b' }, hdr.to_hash)
|
239
|
+
assert_nil @hp.headers(hdr, buf << " c\r\n")
|
240
|
+
assert_equal({ 'a' => 'b c' }, hdr)
|
241
|
+
assert_nil @hp.headers(hdr, buf << " d\r\n")
|
242
|
+
assert_equal({ 'a' => 'b c d' }, hdr)
|
243
|
+
assert_nil @hp.headers(hdr, buf << "A: e\r\n")
|
244
|
+
assert_equal([ "200 OK", hdr ], @hp.headers(hdr, buf << "\r\n"))
|
245
|
+
assert_equal({ 'a' => "b c d\ne"}, hdr.to_hash)
|
246
|
+
end
|
247
|
+
|
248
|
+
def test_content_length_invalid
|
249
|
+
assert_raises(Kcar::ParserError) do
|
250
|
+
@hp.headers([], "HTTP/1.1 200 OK\r\nContent-Length: 5a\r\n\r\n")
|
251
|
+
end
|
252
|
+
assert_raises(Kcar::ParserError) do
|
253
|
+
@hp.headers([], "HTTP/1.1 200 OK\r\nContent-Length: -1\r\n\r\n")
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
257
|
+
end
|
@@ -0,0 +1,415 @@
|
|
1
|
+
# -*- encoding: binary -*-
|
2
|
+
require 'test/unit'
|
3
|
+
require 'pp'
|
4
|
+
require 'socket'
|
5
|
+
require 'kcar'
|
6
|
+
|
7
|
+
class TestSession < Test::Unit::TestCase
|
8
|
+
def setup
|
9
|
+
@s, @c = UNIXSocket.pair
|
10
|
+
end
|
11
|
+
|
12
|
+
def test_http_one_zero
|
13
|
+
pid = fork do
|
14
|
+
@s << "HTTP/1.0 200 OK\r\n\r\nHI"
|
15
|
+
@s.close
|
16
|
+
end
|
17
|
+
@s.close
|
18
|
+
@response = Kcar::Response.new(@c)
|
19
|
+
status, headers, body = @response.read
|
20
|
+
assert_equal status, "200 OK"
|
21
|
+
assert headers.empty?
|
22
|
+
tmp = []
|
23
|
+
assert ! body.parser.keepalive?
|
24
|
+
assert_nothing_raised { body.each { |chunk| tmp << chunk.dup } }
|
25
|
+
assert_equal [ "HI" ], tmp
|
26
|
+
_, status = Process.waitpid2(pid)
|
27
|
+
assert status.success?
|
28
|
+
body.close
|
29
|
+
assert @c.closed?
|
30
|
+
end
|
31
|
+
|
32
|
+
def test_http_keepalive
|
33
|
+
pid = fork do
|
34
|
+
@s << "HTTP/1.1 200 OK\r\nContent-Length: 2\r\n\r\nHI"
|
35
|
+
end
|
36
|
+
@response = Kcar::Response.new(@c)
|
37
|
+
status, headers, body = @response.read
|
38
|
+
assert_equal status, "200 OK"
|
39
|
+
assert_equal({"Content-Length" => "2" }, headers)
|
40
|
+
tmp = []
|
41
|
+
assert body.parser.keepalive?
|
42
|
+
assert_nothing_raised { body.each { |chunk| tmp << chunk.dup } }
|
43
|
+
assert_equal [ "HI" ], tmp
|
44
|
+
_, status = Process.waitpid2(pid)
|
45
|
+
assert status.success?
|
46
|
+
body.close
|
47
|
+
assert ! @c.closed?
|
48
|
+
|
49
|
+
pid = fork do
|
50
|
+
@s << "HTTP/1.1 200 OK\r\nContent-Length: 3\r\n"
|
51
|
+
@s << "Connection: close\r\n\r\nBYE"
|
52
|
+
end
|
53
|
+
status, headers, body = @response.read
|
54
|
+
assert_equal status, "200 OK"
|
55
|
+
assert_equal({ "Content-Length" => "3", "Connection" => "close" }, headers)
|
56
|
+
tmp = []
|
57
|
+
assert ! body.parser.keepalive?
|
58
|
+
assert_nothing_raised { body.each { |chunk| tmp << chunk.dup } }
|
59
|
+
assert_equal [ "BYE" ], tmp
|
60
|
+
_, status = Process.waitpid2(pid)
|
61
|
+
assert status.success?
|
62
|
+
body.close
|
63
|
+
assert @c.closed?
|
64
|
+
end
|
65
|
+
|
66
|
+
def test_http_keepalive_chunky
|
67
|
+
@response = Kcar::Response.new(@c)
|
68
|
+
pid = fork do
|
69
|
+
@s << "HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n"
|
70
|
+
@s << "5\r\nabcde\r\n"
|
71
|
+
@s << "0\r\n\r\nHTTP/1.1 " # partial response
|
72
|
+
end
|
73
|
+
status, headers, body = @response.read
|
74
|
+
assert_equal status, "200 OK"
|
75
|
+
assert_equal({"Transfer-Encoding" => "chunked" }, headers)
|
76
|
+
tmp = []
|
77
|
+
assert body.parser.keepalive?
|
78
|
+
assert_nothing_raised { body.each { |chunk| tmp << chunk.dup } }
|
79
|
+
assert_equal [ "abcde" ], tmp
|
80
|
+
_, status = Process.waitpid2(pid)
|
81
|
+
assert status.success?
|
82
|
+
body.close
|
83
|
+
assert ! @c.closed?
|
84
|
+
assert_equal "HTTP/1.1 ", @response.buf
|
85
|
+
|
86
|
+
pid = fork do
|
87
|
+
@s << "200 OK\r\nContent-Length: 3\r\n"
|
88
|
+
@s << "Connection: close\r\n\r\nBYE"
|
89
|
+
end
|
90
|
+
status, headers, body = @response.read
|
91
|
+
assert_equal status, "200 OK"
|
92
|
+
assert_equal({ "Content-Length" => "3", "Connection" => "close" }, headers)
|
93
|
+
tmp = []
|
94
|
+
assert ! body.parser.keepalive?
|
95
|
+
assert_nothing_raised { body.each { |chunk| tmp << chunk.dup } }
|
96
|
+
assert_equal [ "BYE" ], tmp
|
97
|
+
_, status = Process.waitpid2(pid)
|
98
|
+
assert status.success?
|
99
|
+
body.close
|
100
|
+
assert @c.closed?
|
101
|
+
end
|
102
|
+
|
103
|
+
def test_http_no_body_keepalive
|
104
|
+
pid = fork { @s << "HTTP/1.1 100 Continue\r\n\r\n" }
|
105
|
+
@response = Kcar::Response.new(@c)
|
106
|
+
status, headers, body = @response.read
|
107
|
+
assert_equal status, "100 Continue"
|
108
|
+
assert_equal({}, headers)
|
109
|
+
tmp = []
|
110
|
+
assert body.parser.keepalive?
|
111
|
+
assert_nothing_raised { body.each { |chunk| tmp << chunk.dup } }
|
112
|
+
assert tmp.empty?
|
113
|
+
_, status = Process.waitpid2(pid)
|
114
|
+
assert status.success?
|
115
|
+
body.close
|
116
|
+
assert ! @c.closed?
|
117
|
+
|
118
|
+
pid = fork { @s << "HTTP/1.1 200 OK\r\nConnection: close\r\n\r\nhello" }
|
119
|
+
@s.close
|
120
|
+
status, headers, body = @response.read
|
121
|
+
assert_equal status, "200 OK"
|
122
|
+
assert_equal({'Connection' => 'close'}, headers)
|
123
|
+
tmp = []
|
124
|
+
assert ! body.parser.keepalive?
|
125
|
+
assert_nothing_raised { body.each { |chunk| tmp << chunk.dup } }
|
126
|
+
assert_equal(%w(hello), tmp)
|
127
|
+
_, status = Process.waitpid2(pid)
|
128
|
+
assert status.success?
|
129
|
+
body.close
|
130
|
+
assert @c.closed?
|
131
|
+
end
|
132
|
+
|
133
|
+
def test_trailers
|
134
|
+
pid = fork do
|
135
|
+
@s << "HTTP/1.1 200 OK\r\nTrailer: Foo\r\n"
|
136
|
+
@s << "Transfer-Encoding: chunked\r\n\r\n"
|
137
|
+
end
|
138
|
+
@response = Kcar::Response.new(@c)
|
139
|
+
status, headers, body = @response.read
|
140
|
+
assert_equal status, "200 OK"
|
141
|
+
expect = {
|
142
|
+
"Trailer" => "Foo",
|
143
|
+
"Transfer-Encoding" => "chunked",
|
144
|
+
}
|
145
|
+
assert_equal(expect, headers)
|
146
|
+
assert body.parser.keepalive?
|
147
|
+
_, status = Process.waitpid2(pid)
|
148
|
+
assert status.success?
|
149
|
+
tmp = []
|
150
|
+
pid = fork { @s << "5\r\nhello\r\n0\r\nFoo: bar\r\n\r\n" }
|
151
|
+
assert_nothing_raised { body.each { |chunk| tmp << chunk.dup } }
|
152
|
+
assert_equal %w(hello), tmp
|
153
|
+
expect['Foo'] = 'bar'
|
154
|
+
assert_equal(expect, headers)
|
155
|
+
_, status = Process.waitpid2(pid)
|
156
|
+
assert status.success?
|
157
|
+
body.close
|
158
|
+
assert ! @c.closed?
|
159
|
+
end
|
160
|
+
|
161
|
+
def test_trailers_pass_through
|
162
|
+
pid = fork do
|
163
|
+
@s << "HTTP/1.1 200 OK\r\nTrailer: Foo\r\n"
|
164
|
+
@s << "Transfer-Encoding: chunked\r\n\r\n"
|
165
|
+
end
|
166
|
+
@response = Kcar::Response.new(@c, {}, false)
|
167
|
+
status, headers, body = @response.read
|
168
|
+
assert_equal status, "200 OK"
|
169
|
+
expect = {
|
170
|
+
"Trailer" => "Foo",
|
171
|
+
"Transfer-Encoding" => "chunked",
|
172
|
+
}
|
173
|
+
assert_equal(expect, headers)
|
174
|
+
assert body.parser.keepalive?
|
175
|
+
_, status = Process.waitpid2(pid)
|
176
|
+
assert status.success?
|
177
|
+
tmp = []
|
178
|
+
pid = fork { @s << "5\r\nhello\r\n0\r\nFoo: bar\r\n\r\n" }
|
179
|
+
assert_nothing_raised { body.each { |chunk| tmp << chunk.dup } }
|
180
|
+
assert_equal ["5\r\n", "hello\r\n", "0\r\n", "Foo: bar\r\n\r\n"], tmp
|
181
|
+
expect['Foo'] = 'bar'
|
182
|
+
assert_equal(expect, headers)
|
183
|
+
_, status = Process.waitpid2(pid)
|
184
|
+
assert status.success?
|
185
|
+
body.close
|
186
|
+
assert ! @c.closed?
|
187
|
+
end
|
188
|
+
|
189
|
+
def test_pass_through_one_oh
|
190
|
+
pid = fork do
|
191
|
+
@s << "HTTP/1.0 200 OK\r\n"
|
192
|
+
@s << "Content-Type: text/plain\r\n\r\n"
|
193
|
+
end
|
194
|
+
@response = Kcar::Response.new(@c, {}, false)
|
195
|
+
status, headers, body = @response.read
|
196
|
+
assert_equal status, "200 OK"
|
197
|
+
expect = { "Content-Type" => "text/plain", }
|
198
|
+
assert_equal(expect, headers)
|
199
|
+
assert ! body.parser.keepalive?
|
200
|
+
_, status = Process.waitpid2(pid)
|
201
|
+
assert status.success?
|
202
|
+
tmp = []
|
203
|
+
pid = fork { @s << "hello" }
|
204
|
+
@s.close
|
205
|
+
assert_nothing_raised { body.each { |chunk| tmp << chunk.dup } }
|
206
|
+
assert_equal %w(hello), tmp
|
207
|
+
assert_equal(expect, headers)
|
208
|
+
_, status = Process.waitpid2(pid)
|
209
|
+
assert status.success?
|
210
|
+
body.close
|
211
|
+
assert @c.closed?
|
212
|
+
end
|
213
|
+
|
214
|
+
def test_trailers_burpy
|
215
|
+
pid = fork do
|
216
|
+
@s << "HTTP/1.1 200 OK\r\nTrailer: Foo\r\n"
|
217
|
+
@s << "Transfer-Encoding: chunked\r\n\r\n"
|
218
|
+
end
|
219
|
+
@response = Kcar::Response.new(@c)
|
220
|
+
status, headers, body = @response.read
|
221
|
+
assert_equal status, "200 OK"
|
222
|
+
expect = {
|
223
|
+
"Trailer" => "Foo",
|
224
|
+
"Transfer-Encoding" => "chunked",
|
225
|
+
}
|
226
|
+
assert_equal(expect, headers)
|
227
|
+
assert body.parser.keepalive?
|
228
|
+
_, status = Process.waitpid2(pid)
|
229
|
+
assert status.success?
|
230
|
+
tmp = []
|
231
|
+
pid = fork { @s << "5\r\nhello\r\n0\r\nFoo: bar" }
|
232
|
+
rd, wr = IO.pipe
|
233
|
+
crlf_pid = fork do
|
234
|
+
wr.close
|
235
|
+
@s << rd.sysread(4)
|
236
|
+
end
|
237
|
+
rd.close
|
238
|
+
assert_nothing_raised do
|
239
|
+
first = true
|
240
|
+
body.each do |chunk|
|
241
|
+
tmp << chunk.dup
|
242
|
+
if first
|
243
|
+
first = false
|
244
|
+
wr.syswrite "\r\n\r\n"
|
245
|
+
end
|
246
|
+
end
|
247
|
+
end
|
248
|
+
assert_equal %w(hello), tmp
|
249
|
+
_, status = Process.waitpid2(pid)
|
250
|
+
assert status.success?
|
251
|
+
_, status = Process.waitpid2(crlf_pid)
|
252
|
+
assert status.success?
|
253
|
+
expect['Foo'] = 'bar'
|
254
|
+
assert_equal(expect, headers)
|
255
|
+
body.close
|
256
|
+
assert ! @c.closed?
|
257
|
+
end
|
258
|
+
|
259
|
+
def test_pass_through_trailers_burpy
|
260
|
+
pid = fork do
|
261
|
+
@s << "HTTP/1.1 200 OK\r\nTrailer: Foo\r\n"
|
262
|
+
@s << "Transfer-Encoding: chunked\r\n\r\n"
|
263
|
+
end
|
264
|
+
@response = Kcar::Response.new(@c, {}, false)
|
265
|
+
status, headers, body = @response.read
|
266
|
+
assert_equal status, "200 OK"
|
267
|
+
expect = {
|
268
|
+
"Trailer" => "Foo",
|
269
|
+
"Transfer-Encoding" => "chunked",
|
270
|
+
}
|
271
|
+
assert_equal(expect, headers)
|
272
|
+
assert body.parser.keepalive?
|
273
|
+
_, status = Process.waitpid2(pid)
|
274
|
+
assert status.success?
|
275
|
+
tmp = []
|
276
|
+
pid = fork { @s << "5\r\nhello\r\n0\r\nFoo: bar" }
|
277
|
+
rd, wr = IO.pipe
|
278
|
+
crlf_pid = fork do
|
279
|
+
wr.close
|
280
|
+
@s << rd.sysread(4)
|
281
|
+
end
|
282
|
+
rd.close
|
283
|
+
assert_nothing_raised do
|
284
|
+
first = true
|
285
|
+
body.each do |chunk|
|
286
|
+
tmp << chunk.dup
|
287
|
+
if first
|
288
|
+
first = false
|
289
|
+
wr.syswrite "\r\n\r\n"
|
290
|
+
end
|
291
|
+
end
|
292
|
+
end
|
293
|
+
assert_equal ["5\r\n", "hello\r\n", "0\r\n", "Foo: bar\r\n\r\n"], tmp
|
294
|
+
_, status = Process.waitpid2(pid)
|
295
|
+
assert status.success?
|
296
|
+
_, status = Process.waitpid2(crlf_pid)
|
297
|
+
assert status.success?
|
298
|
+
expect['Foo'] = 'bar'
|
299
|
+
assert_equal(expect, headers)
|
300
|
+
body.close
|
301
|
+
assert ! @c.closed?
|
302
|
+
end
|
303
|
+
|
304
|
+
def test_identity_burpy
|
305
|
+
pid = fork { @s << "HTTP/1.1 200 OK\r\nContent-Length: 5\r\n\r\n" }
|
306
|
+
@response = Kcar::Response.new(@c)
|
307
|
+
status, headers, body = @response.read
|
308
|
+
assert_equal status, "200 OK"
|
309
|
+
expect = { "Content-Length" => '5' }
|
310
|
+
assert_equal(expect, headers)
|
311
|
+
assert body.parser.keepalive?
|
312
|
+
_, status = Process.waitpid2(pid)
|
313
|
+
assert status.success?
|
314
|
+
tmp = []
|
315
|
+
pid = fork { @s << "h" }
|
316
|
+
rd, wr = IO.pipe
|
317
|
+
crlf_pid = fork do
|
318
|
+
wr.close
|
319
|
+
@s << rd.sysread(4)
|
320
|
+
end
|
321
|
+
rd.close
|
322
|
+
assert_nothing_raised do
|
323
|
+
first = true
|
324
|
+
body.each do |chunk|
|
325
|
+
tmp << chunk.dup
|
326
|
+
if first
|
327
|
+
first = false
|
328
|
+
wr.syswrite "ello"
|
329
|
+
end
|
330
|
+
end
|
331
|
+
end
|
332
|
+
assert_equal %w(h ello), tmp
|
333
|
+
_, status = Process.waitpid2(pid)
|
334
|
+
assert status.success?
|
335
|
+
_, status = Process.waitpid2(crlf_pid)
|
336
|
+
assert status.success?
|
337
|
+
assert_equal(expect, headers)
|
338
|
+
body.close
|
339
|
+
assert ! @c.closed?
|
340
|
+
end
|
341
|
+
|
342
|
+
def test_rack_preserve_chunk_hash
|
343
|
+
pid = fork do
|
344
|
+
@s << "HTTP/1.1 200 OK\r\n"
|
345
|
+
@s << "Trailer: Foo\r\n"
|
346
|
+
@s << "Transfer-Encoding: chunked\r\n\r\n"
|
347
|
+
@s << "5\r\nhello\r\n0\r\nFoo: bar\r\n\r\n"
|
348
|
+
end
|
349
|
+
@response = Kcar::Response.new(@c)
|
350
|
+
status, headers, body = @response.rack
|
351
|
+
assert_equal status, "200 OK"
|
352
|
+
expect = {
|
353
|
+
"Trailer" => "Foo",
|
354
|
+
"Transfer-Encoding" => "chunked",
|
355
|
+
}
|
356
|
+
assert_equal expect, headers
|
357
|
+
tmp = []
|
358
|
+
assert body.parser.keepalive?
|
359
|
+
assert_nothing_raised { body.each { |chunk| tmp << chunk.dup } }
|
360
|
+
assert_equal ["5\r\n", "hello\r\n", "0\r\n", "Foo: bar\r\n\r\n"], tmp
|
361
|
+
expect["Foo"] = "bar"
|
362
|
+
assert_equal expect, headers
|
363
|
+
_, status = Process.waitpid2(pid)
|
364
|
+
assert status.success?
|
365
|
+
body.close
|
366
|
+
assert ! @c.closed?
|
367
|
+
end
|
368
|
+
|
369
|
+
def test_rack_preserve_chunk_ary
|
370
|
+
pid = fork do
|
371
|
+
@s << "HTTP/1.1 200 OK\r\n"
|
372
|
+
@s << "Trailer: Foo\r\n"
|
373
|
+
@s << "Transfer-Encoding: chunked\r\n\r\n"
|
374
|
+
@s << "5\r\nhello\r\n0\r\nFoo: bar\r\n\r\n"
|
375
|
+
end
|
376
|
+
@response = Kcar::Response.new(@c, [])
|
377
|
+
status, headers, body = @response.rack
|
378
|
+
assert_equal status, "200 OK"
|
379
|
+
expect = [ %w(Trailer Foo), %w(Transfer-Encoding chunked) ]
|
380
|
+
assert_equal expect, headers
|
381
|
+
tmp = []
|
382
|
+
assert body.parser.keepalive?
|
383
|
+
assert_nothing_raised { body.each { |chunk| tmp << chunk.dup } }
|
384
|
+
assert_equal ["5\r\n", "hello\r\n", "0\r\n", "Foo: bar\r\n\r\n"], tmp
|
385
|
+
expect << %w(Foo bar)
|
386
|
+
assert_equal expect, headers
|
387
|
+
_, status = Process.waitpid2(pid)
|
388
|
+
assert status.success?
|
389
|
+
body.close
|
390
|
+
assert ! @c.closed?
|
391
|
+
end
|
392
|
+
|
393
|
+
def test_rack_preserve_chunk_ary_no_keepalive
|
394
|
+
pid = fork do
|
395
|
+
@s << "HTTP/1.1 200 OK\r\n"
|
396
|
+
@s << "Connection: close\r\n"
|
397
|
+
@s << "Trailer: Foo\r\n"
|
398
|
+
@s << "Transfer-Encoding: chunked\r\n\r\n"
|
399
|
+
@s << "5\r\nhello\r\n0\r\nFoo: bar\r\n\r\n"
|
400
|
+
end
|
401
|
+
@s.close
|
402
|
+
@response = Kcar::Response.new(@c, [])
|
403
|
+
status, headers, body = @response.rack
|
404
|
+
assert_equal status, "200 OK"
|
405
|
+
tmp = []
|
406
|
+
assert ! body.parser.keepalive?
|
407
|
+
assert_nothing_raised { body.each { |chunk| tmp << chunk.dup } }
|
408
|
+
assert_equal ["5\r\nhello\r\n0\r\nFoo: bar\r\n\r\n"], tmp
|
409
|
+
_, status = Process.waitpid2(pid)
|
410
|
+
assert status.success?
|
411
|
+
body.close
|
412
|
+
assert @c.closed?
|
413
|
+
end
|
414
|
+
|
415
|
+
end
|