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