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