a5ee053802db80ddca0cad5e15eeef0976ca3320
[unicorn.git] / test / unit / test_http_parser_ng.rb
bloba5ee053802db80ddca0cad5e15eeef0976ca3320
1 # -*- encoding: binary -*-
3 require 'test/test_helper'
4 require 'digest/md5'
6 include Unicorn
8 class HttpParserNgTest < Test::Unit::TestCase
10   def setup
11     HttpParser.keepalive_requests = HttpParser::KEEPALIVE_REQUESTS_DEFAULT
12     @parser = HttpParser.new
13   end
15   def test_keepalive_requests_default_constant
16     assert_kind_of Integer, HttpParser::KEEPALIVE_REQUESTS_DEFAULT
17     assert HttpParser::KEEPALIVE_REQUESTS_DEFAULT >= 0
18   end
20   def test_keepalive_requests_setting
21     HttpParser.keepalive_requests = 0
22     assert_equal 0, HttpParser.keepalive_requests
23     HttpParser.keepalive_requests = nil
24     assert HttpParser.keepalive_requests >= 0xffffffff
25     HttpParser.keepalive_requests = 1
26     assert_equal 1, HttpParser.keepalive_requests
27     HttpParser.keepalive_requests = 666
28     assert_equal 666, HttpParser.keepalive_requests
30     assert_raises(TypeError) { HttpParser.keepalive_requests = "666" }
31     assert_raises(TypeError) { HttpParser.keepalive_requests = [] }
32   end
34   def test_connection_TE
35     @parser.buf << "GET / HTTP/1.1\r\nHost: example.com\r\nConnection: TE\r\n"
36     @parser.buf << "TE: trailers\r\n\r\n"
37     assert_nothing_raised { @parser.parse }
38     assert @parser.keepalive?
39     assert @parser.next?
40   end
42   def test_keepalive_requests_with_next?
43     req = "GET / HTTP/1.1\r\nHost: example.com\r\n\r\n".freeze
44     expect = {
45       "SERVER_NAME" => "example.com",
46       "HTTP_HOST" => "example.com",
47       "rack.url_scheme" => "http",
48       "REQUEST_PATH" => "/",
49       "SERVER_PROTOCOL" => "HTTP/1.1",
50       "PATH_INFO" => "/",
51       "HTTP_VERSION" => "HTTP/1.1",
52       "REQUEST_URI" => "/",
53       "SERVER_PORT" => "80",
54       "REQUEST_METHOD" => "GET",
55       "QUERY_STRING" => ""
56     }.freeze
57     HttpParser::KEEPALIVE_REQUESTS_DEFAULT.times do |nr|
58       @parser.buf << req
59       assert_equal expect, @parser.parse
60       assert @parser.next?
61     end
62     @parser.buf << req
63     assert_equal expect, @parser.parse
64     assert ! @parser.next?
65   end
67   def test_fewer_keepalive_requests_with_next?
68     HttpParser.keepalive_requests = 5
69     @parser = HttpParser.new
70     req = "GET / HTTP/1.1\r\nHost: example.com\r\n\r\n".freeze
71     expect = {
72       "SERVER_NAME" => "example.com",
73       "HTTP_HOST" => "example.com",
74       "rack.url_scheme" => "http",
75       "REQUEST_PATH" => "/",
76       "SERVER_PROTOCOL" => "HTTP/1.1",
77       "PATH_INFO" => "/",
78       "HTTP_VERSION" => "HTTP/1.1",
79       "REQUEST_URI" => "/",
80       "SERVER_PORT" => "80",
81       "REQUEST_METHOD" => "GET",
82       "QUERY_STRING" => ""
83     }.freeze
84     5.times do |nr|
85       @parser.buf << req
86       assert_equal expect, @parser.parse
87       assert @parser.next?
88     end
89     @parser.buf << req
90     assert_equal expect, @parser.parse
91     assert ! @parser.next?
92   end
94   def test_default_keepalive_is_off
95     assert ! @parser.keepalive?
96     assert ! @parser.next?
97     assert_nothing_raised do
98       @parser.buf << "GET / HTTP/1.1\r\nHost: example.com\r\n\r\n"
99       @parser.parse
100     end
101     assert @parser.keepalive?
102     @parser.clear
103     assert ! @parser.keepalive?
104     assert ! @parser.next?
105   end
107   def test_identity_byte_headers
108     req = @parser.env
109     str = "PUT / HTTP/1.1\r\n"
110     str << "Content-Length: 123\r\n"
111     str << "\r"
112     hdr = @parser.buf
113     str.each_byte { |byte|
114       hdr << byte.chr
115       assert_nil @parser.parse
116     }
117     hdr << "\n"
118     assert_equal req.object_id, @parser.parse.object_id
119     assert_equal '123', req['CONTENT_LENGTH']
120     assert_equal 0, hdr.size
121     assert ! @parser.keepalive?
122     assert @parser.headers?
123     assert_equal 123, @parser.content_length
124     dst = ""
125     buf = '.' * 123
126     @parser.filter_body(dst, buf)
127     assert_equal '.' * 123, dst
128     assert_equal "", buf
129     assert @parser.keepalive?
130   end
132   def test_identity_step_headers
133     req = @parser.env
134     str = @parser.buf
135     str << "PUT / HTTP/1.1\r\n"
136     assert ! @parser.parse
137     str << "Content-Length: 123\r\n"
138     assert ! @parser.parse
139     str << "\r\n"
140     assert_equal req.object_id, @parser.parse.object_id
141     assert_equal '123', req['CONTENT_LENGTH']
142     assert_equal 0, str.size
143     assert ! @parser.keepalive?
144     assert @parser.headers?
145     dst = ""
146     buf = '.' * 123
147     @parser.filter_body(dst, buf)
148     assert_equal '.' * 123, dst
149     assert_equal "", buf
150     assert @parser.keepalive?
151   end
153   def test_identity_oneshot_header
154     req = @parser.env
155     str = @parser.buf
156     str << "PUT / HTTP/1.1\r\nContent-Length: 123\r\n\r\n"
157     assert_equal req.object_id, @parser.parse.object_id
158     assert_equal '123', req['CONTENT_LENGTH']
159     assert_equal 0, str.size
160     assert ! @parser.keepalive?
161     assert @parser.headers?
162     dst = ""
163     buf = '.' * 123
164     @parser.filter_body(dst, buf)
165     assert_equal '.' * 123, dst
166     assert_equal "", buf
167   end
169   def test_identity_oneshot_header_with_body
170     body = ('a' * 123).freeze
171     req = @parser.env
172     str = @parser.buf
173     str << "PUT / HTTP/1.1\r\n" \
174            "Content-Length: #{body.length}\r\n" \
175            "\r\n#{body}"
176     assert_equal req.object_id, @parser.parse.object_id
177     assert_equal '123', req['CONTENT_LENGTH']
178     assert_equal 123, str.size
179     assert_equal body, str
180     tmp = ''
181     assert_nil @parser.filter_body(tmp, str)
182     assert_equal 0, str.size
183     assert_equal tmp, body
184     assert_equal "", @parser.filter_body(tmp, str)
185     assert @parser.keepalive?
186   end
188   def test_identity_oneshot_header_with_body_partial
189     str = @parser.buf
190     str << "PUT / HTTP/1.1\r\nContent-Length: 123\r\n\r\na"
191     assert_equal Hash, @parser.parse.class
192     assert_equal 1, str.size
193     assert_equal 'a', str
194     tmp = ''
195     assert_nil @parser.filter_body(tmp, str)
196     assert_equal "", str
197     assert_equal "a", tmp
198     str << ' ' * 122
199     rv = @parser.filter_body(tmp, str)
200     assert_equal 122, tmp.size
201     assert_nil rv
202     assert_equal "", str
203     assert_equal str.object_id, @parser.filter_body(tmp, str).object_id
204     assert @parser.keepalive?
205   end
207   def test_identity_oneshot_header_with_body_slop
208     str = @parser.buf
209     str << "PUT / HTTP/1.1\r\nContent-Length: 1\r\n\r\naG"
210     assert_equal Hash, @parser.parse.class
211     assert_equal 2, str.size
212     assert_equal 'aG', str
213     tmp = ''
214     assert_nil @parser.filter_body(tmp, str)
215     assert_equal "G", str
216     assert_equal "G", @parser.filter_body(tmp, str)
217     assert_equal 1, tmp.size
218     assert_equal "a", tmp
219     assert @parser.keepalive?
220   end
222   def test_chunked
223     str = @parser.buf
224     req = @parser.env
225     str << "PUT / HTTP/1.1\r\ntransfer-Encoding: chunked\r\n\r\n"
226     assert_equal req, @parser.parse, "msg=#{str}"
227     assert_equal 0, str.size
228     tmp = ""
229     assert_nil @parser.filter_body(tmp, str << "6")
230     assert_equal 0, tmp.size
231     assert_nil @parser.filter_body(tmp, str << "\r\n")
232     assert_equal 0, str.size
233     assert_equal 0, tmp.size
234     tmp = ""
235     assert_nil @parser.filter_body(tmp, str << "..")
236     assert_equal "..", tmp
237     assert_nil @parser.filter_body(tmp, str << "abcd\r\n0\r\n")
238     assert_equal "abcd", tmp
239     assert_equal str.object_id, @parser.filter_body(tmp, str << "PUT").object_id
240     assert_equal "PUT", str
241     assert ! @parser.keepalive?
242     str << "TY: FOO\r\n\r\n"
243     assert_equal req, @parser.parse
244     assert_equal "FOO", req["HTTP_PUTTY"]
245     assert @parser.keepalive?
246   end
248   def test_chunked_empty
249     str = @parser.buf
250     req = @parser.env
251     str << "PUT / HTTP/1.1\r\ntransfer-Encoding: chunked\r\n\r\n"
252     assert_equal req, @parser.parse, "msg=#{str}"
253     assert_equal 0, str.size
254     tmp = ""
255     assert_equal str, @parser.filter_body(tmp, str << "0\r\n\r\n")
256     assert_equal "", tmp
257   end
259   def test_two_chunks
260     str = @parser.buf
261     str << "PUT / HTTP/1.1\r\ntransfer-Encoding: chunked\r\n\r\n"
262     req = @parser.env
263     assert_equal req, @parser.parse
264     assert_equal 0, str.size
265     tmp = ""
266     assert_nil @parser.filter_body(tmp, str << "6")
267     assert_equal 0, tmp.size
268     assert_nil @parser.filter_body(tmp, str << "\r\n")
269     assert_equal "", str
270     assert_equal 0, tmp.size
271     tmp = ""
272     assert_nil @parser.filter_body(tmp, str << "..")
273     assert_equal 2, tmp.size
274     assert_equal "..", tmp
275     assert_nil @parser.filter_body(tmp, str << "abcd\r\n1")
276     assert_equal "abcd", tmp
277     assert_nil @parser.filter_body(tmp, str << "\r")
278     assert_equal "", tmp
279     assert_nil @parser.filter_body(tmp, str << "\n")
280     assert_equal "", tmp
281     assert_nil @parser.filter_body(tmp, str << "z")
282     assert_equal "z", tmp
283     assert_nil @parser.filter_body(tmp, str << "\r\n")
284     assert_nil @parser.filter_body(tmp, str << "0")
285     assert_nil @parser.filter_body(tmp, str << "\r")
286     rv = @parser.filter_body(tmp, str << "\nGET")
287     assert_equal "GET", rv
288     assert_equal str.object_id, rv.object_id
289     assert ! @parser.keepalive?
290   end
292   def test_big_chunk
293     str = @parser.buf
294     str << "PUT / HTTP/1.1\r\ntransfer-Encoding: chunked\r\n\r\n" \
295            "4000\r\nabcd"
296     req = @parser.env
297     assert_equal req, @parser.parse
298     tmp = ''
299     assert_nil @parser.filter_body(tmp, str)
300     assert_equal '', str
301     str << ' ' * 16300
302     assert_nil @parser.filter_body(tmp, str)
303     assert_equal '', str
304     str << ' ' * 80
305     assert_nil @parser.filter_body(tmp, str)
306     assert_equal '', str
307     assert ! @parser.body_eof?
308     assert_equal "", @parser.filter_body(tmp, str << "\r\n0\r\n")
309     assert_equal "", tmp
310     assert @parser.body_eof?
311     str << "\r\n"
312     assert_equal req, @parser.parse
313     assert_equal "", str
314     assert @parser.body_eof?
315     assert @parser.keepalive?
316   end
318   def test_two_chunks_oneshot
319     str = @parser.buf
320     req = @parser.env
321     str << "PUT / HTTP/1.1\r\ntransfer-Encoding: chunked\r\n\r\n" \
322            "1\r\na\r\n2\r\n..\r\n0\r\n"
323     assert_equal req, @parser.parse
324     tmp = ''
325     assert_nil @parser.filter_body(tmp, str)
326     assert_equal 'a..', tmp
327     rv = @parser.filter_body(tmp, str)
328     assert_equal rv.object_id, str.object_id
329     assert ! @parser.keepalive?
330   end
332   def test_chunks_bytewise
333     chunked = "10\r\nabcdefghijklmnop\r\n11\r\n0123456789abcdefg\r\n0\r\n"
334     str = "PUT / HTTP/1.1\r\ntransfer-Encoding: chunked\r\n\r\n"
335     buf = @parser.buf
336     buf << str
337     req = @parser.env
338     assert_equal req, @parser.parse
339     assert_equal "", buf
340     tmp = ''
341     body = ''
342     str = chunked[0..-2]
343     str.each_byte { |byte|
344       assert_nil @parser.filter_body(tmp, buf << byte.chr)
345       body << tmp
346     }
347     assert_equal 'abcdefghijklmnop0123456789abcdefg', body
348     rv = @parser.filter_body(tmp, buf<< "\n")
349     assert_equal rv.object_id, buf.object_id
350     assert ! @parser.keepalive?
351   end
353   def test_trailers
354     req = @parser.env
355     str = @parser.buf
356     str << "PUT / HTTP/1.1\r\n" \
357            "Trailer: Content-MD5\r\n" \
358            "transfer-Encoding: chunked\r\n\r\n" \
359            "1\r\na\r\n2\r\n..\r\n0\r\n"
360     assert_equal req, @parser.parse
361     assert_equal 'Content-MD5', req['HTTP_TRAILER']
362     assert_nil req['HTTP_CONTENT_MD5']
363     tmp = ''
364     assert_nil @parser.filter_body(tmp, str)
365     assert_equal 'a..', tmp
366     md5_b64 = [ Digest::MD5.digest(tmp) ].pack('m').strip.freeze
367     rv = @parser.filter_body(tmp, str)
368     assert_equal rv.object_id, str.object_id
369     assert_equal '', str
370     md5_hdr = "Content-MD5: #{md5_b64}\r\n".freeze
371     str << md5_hdr
372     assert_nil @parser.trailers(req, str)
373     assert_equal md5_b64, req['HTTP_CONTENT_MD5']
374     assert_equal "CONTENT_MD5: #{md5_b64}\r\n", str
375     str << "\r"
376     assert_nil @parser.parse
377     str << "\nGET / "
378     assert_equal req, @parser.parse
379     assert_equal "GET / ", str
380     assert @parser.keepalive?
381   end
383   def test_trailers_slowly
384     str = @parser.buf
385     str << "PUT / HTTP/1.1\r\n" \
386            "Trailer: Content-MD5\r\n" \
387            "transfer-Encoding: chunked\r\n\r\n" \
388            "1\r\na\r\n2\r\n..\r\n0\r\n"
389     req = @parser.env
390     assert_equal req, @parser.parse
391     assert_equal 'Content-MD5', req['HTTP_TRAILER']
392     assert_nil req['HTTP_CONTENT_MD5']
393     tmp = ''
394     assert_nil @parser.filter_body(tmp, str)
395     assert_equal 'a..', tmp
396     md5_b64 = [ Digest::MD5.digest(tmp) ].pack('m').strip.freeze
397     rv = @parser.filter_body(tmp, str)
398     assert_equal rv.object_id, str.object_id
399     assert_equal '', str
400     assert_nil @parser.trailers(req, str)
401     md5_hdr = "Content-MD5: #{md5_b64}\r\n".freeze
402     md5_hdr.each_byte { |byte|
403       str << byte.chr
404       assert_nil @parser.trailers(req, str)
405     }
406     assert_equal md5_b64, req['HTTP_CONTENT_MD5']
407     assert_equal "CONTENT_MD5: #{md5_b64}\r\n", str
408     str << "\r"
409     assert_nil @parser.parse
410     str << "\n"
411     assert_equal req, @parser.parse
412   end
414   def test_max_chunk
415     str = @parser.buf
416     str << "PUT / HTTP/1.1\r\n" \
417            "transfer-Encoding: chunked\r\n\r\n" \
418            "#{HttpParser::CHUNK_MAX.to_s(16)}\r\na\r\n2\r\n..\r\n0\r\n"
419     req = @parser.env
420     assert_equal req, @parser.parse
421     assert_nil @parser.content_length
422     assert_nothing_raised { @parser.filter_body('', str) }
423     assert ! @parser.keepalive?
424   end
426   def test_max_body
427     n = HttpParser::LENGTH_MAX
428     @parser.buf << "PUT / HTTP/1.1\r\nContent-Length: #{n}\r\n\r\n"
429     req = @parser.env
430     assert_nothing_raised { @parser.headers(req, @parser.buf) }
431     assert_equal n, req['CONTENT_LENGTH'].to_i
432     assert ! @parser.keepalive?
433   end
435   def test_overflow_chunk
436     n = HttpParser::CHUNK_MAX + 1
437     str = @parser.buf
438     req = @parser.env
439     str << "PUT / HTTP/1.1\r\n" \
440            "transfer-Encoding: chunked\r\n\r\n" \
441            "#{n.to_s(16)}\r\na\r\n2\r\n..\r\n0\r\n"
442     assert_equal req, @parser.parse
443     assert_nil @parser.content_length
444     assert_raise(HttpParserError) { @parser.filter_body('', str) }
445   end
447   def test_overflow_content_length
448     n = HttpParser::LENGTH_MAX + 1
449     @parser.buf << "PUT / HTTP/1.1\r\nContent-Length: #{n}\r\n\r\n"
450     assert_raise(HttpParserError) { @parser.parse }
451   end
453   def test_bad_chunk
454     @parser.buf << "PUT / HTTP/1.1\r\n" \
455                    "transfer-Encoding: chunked\r\n\r\n" \
456                    "#zzz\r\na\r\n2\r\n..\r\n0\r\n"
457     req = @parser.env
458     assert_equal req, @parser.parse
459     assert_nil @parser.content_length
460     assert_raise(HttpParserError) { @parser.filter_body("", @parser.buf) }
461   end
463   def test_bad_content_length
464     @parser.buf << "PUT / HTTP/1.1\r\nContent-Length: 7ff\r\n\r\n"
465     assert_raise(HttpParserError) { @parser.parse }
466   end
468   def test_bad_trailers
469     str = @parser.buf
470     req = @parser.env
471     str << "PUT / HTTP/1.1\r\n" \
472            "Trailer: Transfer-Encoding\r\n" \
473            "transfer-Encoding: chunked\r\n\r\n" \
474            "1\r\na\r\n2\r\n..\r\n0\r\n"
475     assert_equal req, @parser.parse
476     assert_equal 'Transfer-Encoding', req['HTTP_TRAILER']
477     tmp = ''
478     assert_nil @parser.filter_body(tmp, str)
479     assert_equal 'a..', tmp
480     assert_equal '', str
481     str << "Transfer-Encoding: identity\r\n\r\n"
482     assert_raise(HttpParserError) { @parser.parse }
483   end
485   def test_repeat_headers
486     str = "PUT / HTTP/1.1\r\n" \
487           "Trailer: Content-MD5\r\n" \
488           "Trailer: Content-SHA1\r\n" \
489           "transfer-Encoding: chunked\r\n\r\n" \
490           "1\r\na\r\n2\r\n..\r\n0\r\n"
491     req = @parser.env
492     @parser.buf << str
493     assert_equal req, @parser.parse
494     assert_equal 'Content-MD5,Content-SHA1', req['HTTP_TRAILER']
495     assert ! @parser.keepalive?
496   end
498   def test_parse_simple_request
499     parser = HttpParser.new
500     req = parser.env
501     parser.buf << "GET /read-rfc1945-if-you-dont-believe-me\r\n"
502     assert_equal req, parser.parse
503     assert_equal '', parser.buf
504     expect = {
505       "SERVER_NAME"=>"localhost",
506       "rack.url_scheme"=>"http",
507       "REQUEST_PATH"=>"/read-rfc1945-if-you-dont-believe-me",
508       "PATH_INFO"=>"/read-rfc1945-if-you-dont-believe-me",
509       "REQUEST_URI"=>"/read-rfc1945-if-you-dont-believe-me",
510       "SERVER_PORT"=>"80",
511       "SERVER_PROTOCOL"=>"HTTP/0.9",
512       "REQUEST_METHOD"=>"GET",
513       "QUERY_STRING"=>""
514     }
515     assert_equal expect, req
516     assert ! parser.headers?
517   end
519   def test_path_info_semicolon
520     qs = "QUERY_STRING"
521     pi = "PATH_INFO"
522     req = {}
523     str = "GET %s HTTP/1.1\r\nHost: example.com\r\n\r\n"
524     {
525       "/1;a=b?c=d&e=f" => { qs => "c=d&e=f", pi => "/1;a=b" },
526       "/1?c=d&e=f" => { qs => "c=d&e=f", pi => "/1" },
527       "/1;a=b" => { qs => "", pi => "/1;a=b" },
528       "/1;a=b?" => { qs => "", pi => "/1;a=b" },
529       "/1?a=b;c=d&e=f" => { qs => "a=b;c=d&e=f", pi => "/1" },
530       "*" => { qs => "", pi => "" },
531     }.each do |uri,expect|
532       assert_equal req, @parser.headers(req.clear, str % [ uri ])
533       req = req.dup
534       @parser.clear
535       assert_equal uri, req["REQUEST_URI"], "REQUEST_URI mismatch"
536       assert_equal expect[qs], req[qs], "#{qs} mismatch"
537       assert_equal expect[pi], req[pi], "#{pi} mismatch"
538       next if uri == "*"
539       uri = URI.parse("http://example.com#{uri}")
540       assert_equal uri.query.to_s, req[qs], "#{qs} mismatch URI.parse disagrees"
541       assert_equal uri.path, req[pi], "#{pi} mismatch URI.parse disagrees"
542     end
543   end
545   def test_path_info_semicolon_absolute
546     qs = "QUERY_STRING"
547     pi = "PATH_INFO"
548     req = {}
549     str = "GET http://example.com%s HTTP/1.1\r\nHost: www.example.com\r\n\r\n"
550     {
551       "/1;a=b?c=d&e=f" => { qs => "c=d&e=f", pi => "/1;a=b" },
552       "/1?c=d&e=f" => { qs => "c=d&e=f", pi => "/1" },
553       "/1;a=b" => { qs => "", pi => "/1;a=b" },
554       "/1;a=b?" => { qs => "", pi => "/1;a=b" },
555       "/1?a=b;c=d&e=f" => { qs => "a=b;c=d&e=f", pi => "/1" },
556     }.each do |uri,expect|
557       assert_equal req, @parser.headers(req.clear, str % [ uri ])
558       req = req.dup
559       @parser.clear
560       assert_equal uri, req["REQUEST_URI"], "REQUEST_URI mismatch"
561       assert_equal "example.com", req["HTTP_HOST"], "Host: mismatch"
562       assert_equal expect[qs], req[qs], "#{qs} mismatch"
563       assert_equal expect[pi], req[pi], "#{pi} mismatch"
564     end
565   end
567   def test_negative_content_length
568     req = {}
569     str = "PUT / HTTP/1.1\r\n" \
570           "Content-Length: -1\r\n" \
571           "\r\n"
572     assert_raises(HttpParserError) do
573       @parser.headers(req, str)
574     end
575   end
577   def test_invalid_content_length
578     req = {}
579     str = "PUT / HTTP/1.1\r\n" \
580           "Content-Length: zzzzz\r\n" \
581           "\r\n"
582     assert_raises(HttpParserError) do
583       @parser.headers(req, str)
584     end
585   end
587   def test_backtrace_is_empty
588     begin
589       @parser.headers({}, "AAADFSFDSFD\r\n\r\n")
590       assert false, "should never get here line:#{__LINE__}"
591     rescue HttpParserError => e
592       assert_equal [], e.backtrace
593       return
594     end
595     assert false, "should never get here line:#{__LINE__}"
596   end
598   def test_ignore_version_header
599     @parser.buf << "GET / HTTP/1.1\r\nVersion: hello\r\n\r\n"
600     req = @parser.env
601     assert_equal req, @parser.parse
602     assert_equal '', @parser.buf
603     expect = {
604       "SERVER_NAME" => "localhost",
605       "rack.url_scheme" => "http",
606       "REQUEST_PATH" => "/",
607       "SERVER_PROTOCOL" => "HTTP/1.1",
608       "PATH_INFO" => "/",
609       "HTTP_VERSION" => "HTTP/1.1",
610       "REQUEST_URI" => "/",
611       "SERVER_PORT" => "80",
612       "REQUEST_METHOD" => "GET",
613       "QUERY_STRING" => ""
614     }
615     assert_equal expect, req
616   end
618   def test_pipelined_requests
619     host = "example.com"
620     expect = {
621       "HTTP_HOST" => host,
622       "SERVER_NAME" => host,
623       "REQUEST_PATH" => "/",
624       "rack.url_scheme" => "http",
625       "SERVER_PROTOCOL" => "HTTP/1.1",
626       "PATH_INFO" => "/",
627       "HTTP_VERSION" => "HTTP/1.1",
628       "REQUEST_URI" => "/",
629       "SERVER_PORT" => "80",
630       "REQUEST_METHOD" => "GET",
631       "QUERY_STRING" => ""
632     }
633     req1 = "GET / HTTP/1.1\r\nHost: example.com\r\n\r\n"
634     req2 = "GET / HTTP/1.1\r\nHost: www.example.com\r\n\r\n"
635     @parser.buf << (req1 + req2)
636     env1 = @parser.parse.dup
637     assert_equal expect, env1
638     assert_equal req2, @parser.buf
639     assert ! @parser.env.empty?
640     assert @parser.next?
641     assert @parser.keepalive?
642     assert @parser.headers?
643     assert_equal expect, @parser.env
644     env2 = @parser.parse.dup
645     host.replace "www.example.com"
646     assert_equal "www.example.com", expect["HTTP_HOST"]
647     assert_equal "www.example.com", expect["SERVER_NAME"]
648     assert_equal expect, env2
649     assert_equal "", @parser.buf
650   end
652   def test_keepalive_requests_disabled
653     req = "GET / HTTP/1.1\r\nHost: example.com\r\n\r\n".freeze
654     expect = {
655       "SERVER_NAME" => "example.com",
656       "HTTP_HOST" => "example.com",
657       "rack.url_scheme" => "http",
658       "REQUEST_PATH" => "/",
659       "SERVER_PROTOCOL" => "HTTP/1.1",
660       "PATH_INFO" => "/",
661       "HTTP_VERSION" => "HTTP/1.1",
662       "REQUEST_URI" => "/",
663       "SERVER_PORT" => "80",
664       "REQUEST_METHOD" => "GET",
665       "QUERY_STRING" => ""
666     }.freeze
667     HttpParser.keepalive_requests = 0
668     @parser = HttpParser.new
669     @parser.buf << req
670     assert_equal expect, @parser.parse
671     assert ! @parser.next?
672   end
674   def test_chunk_only
675     tmp = ""
676     assert_equal @parser, @parser.dechunk!
677     assert_nil @parser.filter_body(tmp, "6\r\n")
678     assert_equal "", tmp
679     assert_nil @parser.filter_body(tmp, "abcdef")
680     assert_equal "abcdef", tmp
681     assert_nil @parser.filter_body(tmp, "\r\n")
682     assert_equal "", tmp
683     src = "0\r\n\r\n"
684     assert_equal src.object_id, @parser.filter_body(tmp, src).object_id
685     assert_equal "", tmp
686   end
688   def test_chunk_only_bad_align
689     tmp = ""
690     assert_equal @parser, @parser.dechunk!
691     assert_nil @parser.filter_body(tmp, "6\r\na")
692     assert_equal "a", tmp
693     assert_nil @parser.filter_body(tmp, "bcde")
694     assert_equal "bcde", tmp
695     assert_nil @parser.filter_body(tmp, "f\r")
696     assert_equal "f", tmp
697     src = "\n0\r\n\r\n"
698     assert_equal src.object_id, @parser.filter_body(tmp, src).object_id
699     assert_equal "", tmp
700   end
702   def test_chunk_only_reset_ok
703     tmp = ""
704     assert_equal @parser, @parser.dechunk!
705     src = "1\r\na\r\n0\r\n\r\n"
706     assert_nil @parser.filter_body(tmp, src)
707     assert_equal "a", tmp
708     assert_equal src.object_id, @parser.filter_body(tmp, src).object_id
710     assert_equal @parser, @parser.dechunk!
711     src = "0\r\n\r\n"
712     assert_equal src.object_id, @parser.filter_body(tmp, src).object_id
713     assert_equal "", tmp
714     assert_equal src, @parser.filter_body(tmp, src)
715   end