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