1 # -*- encoding: binary -*-
4 require 'test/test_helper'
9 class HttpParserNgTest < Test::Unit::TestCase
12 @parser = HttpParser.new
15 def test_identity_byte_headers
17 str = "PUT / HTTP/1.1\r\n"
18 str << "Content-Length: 123\r\n"
21 str.each_byte { |byte|
22 assert_nil @parser.headers(req, hdr << byte.chr)
25 assert_equal req.object_id, @parser.headers(req, hdr).object_id
26 assert_equal '123', req['CONTENT_LENGTH']
27 assert_equal 0, hdr.size
28 assert ! @parser.keepalive?
29 assert @parser.headers?
30 assert 123, @parser.content_length
33 def test_identity_step_headers
35 str = "PUT / HTTP/1.1\r\n"
36 assert ! @parser.headers(req, str)
37 str << "Content-Length: 123\r\n"
38 assert ! @parser.headers(req, str)
40 assert_equal req.object_id, @parser.headers(req, str).object_id
41 assert_equal '123', req['CONTENT_LENGTH']
42 assert_equal 0, str.size
43 assert ! @parser.keepalive?
44 assert @parser.headers?
47 def test_identity_oneshot_header
49 str = "PUT / HTTP/1.1\r\nContent-Length: 123\r\n\r\n"
50 assert_equal req.object_id, @parser.headers(req, str).object_id
51 assert_equal '123', req['CONTENT_LENGTH']
52 assert_equal 0, str.size
53 assert ! @parser.keepalive?
56 def test_identity_oneshot_header_with_body
57 body = ('a' * 123).freeze
59 str = "PUT / HTTP/1.1\r\n" \
60 "Content-Length: #{body.length}\r\n" \
62 assert_equal req.object_id, @parser.headers(req, str).object_id
63 assert_equal '123', req['CONTENT_LENGTH']
64 assert_equal 123, str.size
65 assert_equal body, str
67 assert_nil @parser.filter_body(tmp, str)
68 assert_equal 0, str.size
69 assert_equal tmp, body
70 assert_equal "", @parser.filter_body(tmp, str)
71 assert ! @parser.keepalive?
74 def test_identity_oneshot_header_with_body_partial
75 str = "PUT / HTTP/1.1\r\nContent-Length: 123\r\n\r\na"
76 assert_equal Hash, @parser.headers({}, str).class
77 assert_equal 1, str.size
80 assert_nil @parser.filter_body(tmp, str)
84 rv = @parser.filter_body(tmp, str)
85 assert_equal 122, tmp.size
88 assert_equal str.object_id, @parser.filter_body(tmp, str).object_id
89 assert ! @parser.keepalive?
92 def test_identity_oneshot_header_with_body_slop
93 str = "PUT / HTTP/1.1\r\nContent-Length: 1\r\n\r\naG"
94 assert_equal Hash, @parser.headers({}, str).class
95 assert_equal 2, str.size
96 assert_equal 'aG', str
98 assert_nil @parser.filter_body(tmp, str)
100 assert_equal "G", @parser.filter_body(tmp, str)
101 assert_equal 1, tmp.size
102 assert_equal "a", tmp
103 assert ! @parser.keepalive?
107 str = "PUT / HTTP/1.1\r\ntransfer-Encoding: chunked\r\n\r\n"
109 assert_equal req, @parser.headers(req, str)
110 assert_equal 0, str.size
112 assert_nil @parser.filter_body(tmp, "6")
113 assert_equal 0, tmp.size
114 assert_nil @parser.filter_body(tmp, rv = "\r\n")
115 assert_equal 0, rv.size
116 assert_equal 0, tmp.size
118 assert_nil @parser.filter_body(tmp, "..")
119 assert_equal "..", tmp
120 assert_nil @parser.filter_body(tmp, "abcd\r\n0\r\n")
121 assert_equal "abcd", tmp
123 assert_equal rv.object_id, @parser.filter_body(tmp, rv).object_id
124 assert_equal "PUT", rv
125 assert ! @parser.keepalive?
129 str = "PUT / HTTP/1.1\r\ntransfer-Encoding: chunked\r\n\r\n"
131 assert_equal req, @parser.headers(req, str)
132 assert_equal 0, str.size
134 assert_nil @parser.filter_body(tmp, "6")
135 assert_equal 0, tmp.size
136 assert_nil @parser.filter_body(tmp, rv = "\r\n")
138 assert_equal 0, tmp.size
140 assert_nil @parser.filter_body(tmp, "..")
141 assert_equal 2, tmp.size
142 assert_equal "..", tmp
143 assert_nil @parser.filter_body(tmp, "abcd\r\n1")
144 assert_equal "abcd", tmp
145 assert_nil @parser.filter_body(tmp, "\r")
147 assert_nil @parser.filter_body(tmp, "\n")
149 assert_nil @parser.filter_body(tmp, "z")
150 assert_equal "z", tmp
151 assert_nil @parser.filter_body(tmp, "\r\n")
152 assert_nil @parser.filter_body(tmp, "0")
153 assert_nil @parser.filter_body(tmp, "\r")
154 rv = @parser.filter_body(tmp, buf = "\nGET")
155 assert_equal "GET", rv
156 assert_equal buf.object_id, rv.object_id
157 assert ! @parser.keepalive?
161 str = "PUT / HTTP/1.1\r\ntransfer-Encoding: chunked\r\n\r\n" \
164 assert_equal req, @parser.headers(req, str)
166 assert_nil @parser.filter_body(tmp, str)
169 assert_nil @parser.filter_body(tmp, str)
172 assert_nil @parser.filter_body(tmp, str)
174 assert ! @parser.body_eof?
175 assert_equal "", @parser.filter_body(tmp, "\r\n0\r\n")
176 assert @parser.body_eof?
177 assert ! @parser.keepalive?
180 def test_two_chunks_oneshot
181 str = "PUT / HTTP/1.1\r\ntransfer-Encoding: chunked\r\n\r\n" \
182 "1\r\na\r\n2\r\n..\r\n0\r\n"
184 assert_equal req, @parser.headers(req, str)
186 assert_nil @parser.filter_body(tmp, str)
187 assert_equal 'a..', tmp
188 rv = @parser.filter_body(tmp, str)
189 assert_equal rv.object_id, str.object_id
190 assert ! @parser.keepalive?
194 str = "PUT / HTTP/1.1\r\n" \
195 "Trailer: Content-MD5\r\n" \
196 "transfer-Encoding: chunked\r\n\r\n" \
197 "1\r\na\r\n2\r\n..\r\n0\r\n"
199 assert_equal req, @parser.headers(req, str)
200 assert_equal 'Content-MD5', req['HTTP_TRAILER']
201 assert_nil req['HTTP_CONTENT_MD5']
203 assert_nil @parser.filter_body(tmp, str)
204 assert_equal 'a..', tmp
205 md5_b64 = [ Digest::MD5.digest(tmp) ].pack('m').strip.freeze
206 rv = @parser.filter_body(tmp, str)
207 assert_equal rv.object_id, str.object_id
209 md5_hdr = "Content-MD5: #{md5_b64}\r\n".freeze
211 assert_nil @parser.trailers(req, str)
212 assert_equal md5_b64, req['HTTP_CONTENT_MD5']
213 assert_equal "CONTENT_MD5: #{md5_b64}\r\n", str
214 assert_nil @parser.trailers(req, str << "\r")
215 assert_equal req, @parser.trailers(req, str << "\nGET / ")
216 assert_equal "GET / ", str
217 assert ! @parser.keepalive?
220 def test_trailers_slowly
221 str = "PUT / HTTP/1.1\r\n" \
222 "Trailer: Content-MD5\r\n" \
223 "transfer-Encoding: chunked\r\n\r\n" \
224 "1\r\na\r\n2\r\n..\r\n0\r\n"
226 assert_equal req, @parser.headers(req, str)
227 assert_equal 'Content-MD5', req['HTTP_TRAILER']
228 assert_nil req['HTTP_CONTENT_MD5']
230 assert_nil @parser.filter_body(tmp, str)
231 assert_equal 'a..', tmp
232 md5_b64 = [ Digest::MD5.digest(tmp) ].pack('m').strip.freeze
233 rv = @parser.filter_body(tmp, str)
234 assert_equal rv.object_id, str.object_id
236 assert_nil @parser.trailers(req, str)
237 md5_hdr = "Content-MD5: #{md5_b64}\r\n".freeze
238 md5_hdr.each_byte { |byte|
240 assert_nil @parser.trailers(req, str)
242 assert_equal md5_b64, req['HTTP_CONTENT_MD5']
243 assert_equal "CONTENT_MD5: #{md5_b64}\r\n", str
244 assert_nil @parser.trailers(req, str << "\r")
245 assert_equal req, @parser.trailers(req, str << "\n")
249 str = "PUT / HTTP/1.1\r\n" \
250 "transfer-Encoding: chunked\r\n\r\n" \
251 "#{HttpParser::CHUNK_MAX.to_s(16)}\r\na\r\n2\r\n..\r\n0\r\n"
253 assert_equal req, @parser.headers(req, str)
254 assert_nil @parser.content_length
255 assert_nothing_raised { @parser.filter_body('', str) }
256 assert ! @parser.keepalive?
260 n = HttpParser::LENGTH_MAX
261 str = "PUT / HTTP/1.1\r\nContent-Length: #{n}\r\n\r\n"
263 assert_nothing_raised { @parser.headers(req, str) }
264 assert_equal n, req['CONTENT_LENGTH'].to_i
265 assert ! @parser.keepalive?
268 def test_overflow_chunk
269 n = HttpParser::CHUNK_MAX + 1
270 str = "PUT / HTTP/1.1\r\n" \
271 "transfer-Encoding: chunked\r\n\r\n" \
272 "#{n.to_s(16)}\r\na\r\n2\r\n..\r\n0\r\n"
274 assert_equal req, @parser.headers(req, str)
275 assert_nil @parser.content_length
276 assert_raise(HttpParserError) { @parser.filter_body('', str) }
277 assert ! @parser.keepalive?
280 def test_overflow_content_length
281 n = HttpParser::LENGTH_MAX + 1
282 str = "PUT / HTTP/1.1\r\nContent-Length: #{n}\r\n\r\n"
283 assert_raise(HttpParserError) { @parser.headers({}, str) }
284 assert ! @parser.keepalive?
288 str = "PUT / HTTP/1.1\r\n" \
289 "transfer-Encoding: chunked\r\n\r\n" \
290 "#zzz\r\na\r\n2\r\n..\r\n0\r\n"
292 assert_equal req, @parser.headers(req, str)
293 assert_nil @parser.content_length
294 assert_raise(HttpParserError) { @parser.filter_body('', str) }
295 assert ! @parser.keepalive?
298 def test_bad_content_length
299 str = "PUT / HTTP/1.1\r\nContent-Length: 7ff\r\n\r\n"
300 assert_raise(HttpParserError) { @parser.headers({}, str) }
301 assert ! @parser.keepalive?
304 def test_bad_trailers
305 str = "PUT / HTTP/1.1\r\n" \
306 "Trailer: Transfer-Encoding\r\n" \
307 "transfer-Encoding: chunked\r\n\r\n" \
308 "1\r\na\r\n2\r\n..\r\n0\r\n"
310 assert_equal req, @parser.headers(req, str)
311 assert_equal 'Transfer-Encoding', req['HTTP_TRAILER']
313 assert_nil @parser.filter_body(tmp, str)
314 assert_equal 'a..', tmp
316 str << "Transfer-Encoding: identity\r\n\r\n"
317 assert_raise(HttpParserError) { @parser.trailers(req, str) }
318 assert ! @parser.keepalive?
321 def test_repeat_headers
322 str = "PUT / HTTP/1.1\r\n" \
323 "Trailer: Content-MD5\r\n" \
324 "Trailer: Content-SHA1\r\n" \
325 "transfer-Encoding: chunked\r\n\r\n" \
326 "1\r\na\r\n2\r\n..\r\n0\r\n"
328 assert_equal req, @parser.headers(req, str)
329 assert_equal 'Content-MD5,Content-SHA1', req['HTTP_TRAILER']
330 assert ! @parser.keepalive?
333 def test_parse_simple_request
334 parser = HttpParser.new
336 http = "GET /read-rfc1945-if-you-dont-believe-me\r\n"
337 assert_equal req, parser.headers(req, http)
338 assert_equal '', http
340 "SERVER_NAME"=>"localhost",
341 "rack.url_scheme"=>"http",
342 "REQUEST_PATH"=>"/read-rfc1945-if-you-dont-believe-me",
343 "PATH_INFO"=>"/read-rfc1945-if-you-dont-believe-me",
344 "REQUEST_URI"=>"/read-rfc1945-if-you-dont-believe-me",
346 "SERVER_PROTOCOL"=>"HTTP/0.9",
347 "REQUEST_METHOD"=>"GET",
350 assert_equal expect, req
351 assert ! parser.headers?