93c44bb45ba189df55220c4ccc675cce51af39f2
[unicorn.git] / test / unit / test_http_parser_ng.rb
blob93c44bb45ba189df55220c4ccc675cce51af39f2
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     @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     @parser.buf << "GET / HTTP/1.1\r\nHost: example.com\r\n\r\n"
98     @parser.parse
99     assert @parser.keepalive?
100     @parser.clear
101     assert ! @parser.keepalive?
102     assert ! @parser.next?
103   end
105   def test_identity_byte_headers
106     req = @parser.env
107     str = "PUT / HTTP/1.1\r\n"
108     str << "Content-Length: 123\r\n"
109     str << "\r"
110     hdr = @parser.buf
111     str.each_byte { |byte|
112       hdr << byte.chr
113       assert_nil @parser.parse
114     }
115     hdr << "\n"
116     assert_equal req.object_id, @parser.parse.object_id
117     assert_equal '123', req['CONTENT_LENGTH']
118     assert_equal 0, hdr.size
119     assert ! @parser.keepalive?
120     assert @parser.headers?
121     assert_equal 123, @parser.content_length
122     dst = ""
123     buf = '.' * 123
124     @parser.filter_body(dst, buf)
125     assert_equal '.' * 123, dst
126     assert_equal "", buf
127     assert @parser.keepalive?
128   end
130   def test_identity_step_headers
131     req = @parser.env
132     str = @parser.buf
133     str << "PUT / HTTP/1.1\r\n"
134     assert ! @parser.parse
135     str << "Content-Length: 123\r\n"
136     assert ! @parser.parse
137     str << "\r\n"
138     assert_equal req.object_id, @parser.parse.object_id
139     assert_equal '123', req['CONTENT_LENGTH']
140     assert_equal 0, str.size
141     assert ! @parser.keepalive?
142     assert @parser.headers?
143     dst = ""
144     buf = '.' * 123
145     @parser.filter_body(dst, buf)
146     assert_equal '.' * 123, dst
147     assert_equal "", buf
148     assert @parser.keepalive?
149   end
151   def test_identity_oneshot_header
152     req = @parser.env
153     str = @parser.buf
154     str << "PUT / HTTP/1.1\r\nContent-Length: 123\r\n\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   end
167   def test_identity_oneshot_header_with_body
168     body = ('a' * 123).freeze
169     req = @parser.env
170     str = @parser.buf
171     str << "PUT / HTTP/1.1\r\n" \
172            "Content-Length: #{body.length}\r\n" \
173            "\r\n#{body}"
174     assert_equal req.object_id, @parser.parse.object_id
175     assert_equal '123', req['CONTENT_LENGTH']
176     assert_equal 123, str.size
177     assert_equal body, str
178     tmp = ''
179     assert_nil @parser.filter_body(tmp, str)
180     assert_equal 0, str.size
181     assert_equal tmp, body
182     assert_equal "", @parser.filter_body(tmp, str)
183     assert @parser.keepalive?
184   end
186   def test_identity_oneshot_header_with_body_partial
187     str = @parser.buf
188     str << "PUT / HTTP/1.1\r\nContent-Length: 123\r\n\r\na"
189     assert_equal Hash, @parser.parse.class
190     assert_equal 1, str.size
191     assert_equal 'a', str
192     tmp = ''
193     assert_nil @parser.filter_body(tmp, str)
194     assert_equal "", str
195     assert_equal "a", tmp
196     str << ' ' * 122
197     rv = @parser.filter_body(tmp, str)
198     assert_equal 122, tmp.size
199     assert_nil rv
200     assert_equal "", str
201     assert_equal str.object_id, @parser.filter_body(tmp, str).object_id
202     assert @parser.keepalive?
203   end
205   def test_identity_oneshot_header_with_body_slop
206     str = @parser.buf
207     str << "PUT / HTTP/1.1\r\nContent-Length: 1\r\n\r\naG"
208     assert_equal Hash, @parser.parse.class
209     assert_equal 2, str.size
210     assert_equal 'aG', str
211     tmp = ''
212     assert_nil @parser.filter_body(tmp, str)
213     assert_equal "G", str
214     assert_equal "G", @parser.filter_body(tmp, str)
215     assert_equal 1, tmp.size
216     assert_equal "a", tmp
217     assert @parser.keepalive?
218   end
220   def test_chunked
221     str = @parser.buf
222     req = @parser.env
223     str << "PUT / HTTP/1.1\r\ntransfer-Encoding: chunked\r\n\r\n"
224     assert_equal req, @parser.parse, "msg=#{str}"
225     assert_equal 0, str.size
226     tmp = ""
227     assert_nil @parser.filter_body(tmp, str << "6")
228     assert_equal 0, tmp.size
229     assert_nil @parser.filter_body(tmp, str << "\r\n")
230     assert_equal 0, str.size
231     assert_equal 0, tmp.size
232     tmp = ""
233     assert_nil @parser.filter_body(tmp, str << "..")
234     assert_equal "..", tmp
235     assert_nil @parser.filter_body(tmp, str << "abcd\r\n0\r\n")
236     assert_equal "abcd", tmp
237     assert_equal str.object_id, @parser.filter_body(tmp, str << "PUT").object_id
238     assert_equal "PUT", str
239     assert ! @parser.keepalive?
240     str << "TY: FOO\r\n\r\n"
241     assert_equal req, @parser.parse
242     assert_equal "FOO", req["HTTP_PUTTY"]
243     assert @parser.keepalive?
244   end
246   def test_chunked_empty
247     str = @parser.buf
248     req = @parser.env
249     str << "PUT / HTTP/1.1\r\ntransfer-Encoding: chunked\r\n\r\n"
250     assert_equal req, @parser.parse, "msg=#{str}"
251     assert_equal 0, str.size
252     tmp = ""
253     assert_equal str, @parser.filter_body(tmp, str << "0\r\n\r\n")
254     assert_equal "", tmp
255   end
257   def test_two_chunks
258     str = @parser.buf
259     str << "PUT / HTTP/1.1\r\ntransfer-Encoding: chunked\r\n\r\n"
260     req = @parser.env
261     assert_equal req, @parser.parse
262     assert_equal 0, str.size
263     tmp = ""
264     assert_nil @parser.filter_body(tmp, str << "6")
265     assert_equal 0, tmp.size
266     assert_nil @parser.filter_body(tmp, str << "\r\n")
267     assert_equal "", str
268     assert_equal 0, tmp.size
269     tmp = ""
270     assert_nil @parser.filter_body(tmp, str << "..")
271     assert_equal 2, tmp.size
272     assert_equal "..", tmp
273     assert_nil @parser.filter_body(tmp, str << "abcd\r\n1")
274     assert_equal "abcd", tmp
275     assert_nil @parser.filter_body(tmp, str << "\r")
276     assert_equal "", tmp
277     assert_nil @parser.filter_body(tmp, str << "\n")
278     assert_equal "", tmp
279     assert_nil @parser.filter_body(tmp, str << "z")
280     assert_equal "z", tmp
281     assert_nil @parser.filter_body(tmp, str << "\r\n")
282     assert_nil @parser.filter_body(tmp, str << "0")
283     assert_nil @parser.filter_body(tmp, str << "\r")
284     rv = @parser.filter_body(tmp, str << "\nGET")
285     assert_equal "GET", rv
286     assert_equal str.object_id, rv.object_id
287     assert ! @parser.keepalive?
288   end
290   def test_big_chunk
291     str = @parser.buf
292     str << "PUT / HTTP/1.1\r\ntransfer-Encoding: chunked\r\n\r\n" \
293            "4000\r\nabcd"
294     req = @parser.env
295     assert_equal req, @parser.parse
296     tmp = ''
297     assert_nil @parser.filter_body(tmp, str)
298     assert_equal '', str
299     str << ' ' * 16300
300     assert_nil @parser.filter_body(tmp, str)
301     assert_equal '', str
302     str << ' ' * 80
303     assert_nil @parser.filter_body(tmp, str)
304     assert_equal '', str
305     assert ! @parser.body_eof?
306     assert_equal "", @parser.filter_body(tmp, str << "\r\n0\r\n")
307     assert_equal "", tmp
308     assert @parser.body_eof?
309     str << "\r\n"
310     assert_equal req, @parser.parse
311     assert_equal "", str
312     assert @parser.body_eof?
313     assert @parser.keepalive?
314   end
316   def test_two_chunks_oneshot
317     str = @parser.buf
318     req = @parser.env
319     str << "PUT / HTTP/1.1\r\ntransfer-Encoding: chunked\r\n\r\n" \
320            "1\r\na\r\n2\r\n..\r\n0\r\n"
321     assert_equal req, @parser.parse
322     tmp = ''
323     assert_nil @parser.filter_body(tmp, str)
324     assert_equal 'a..', tmp
325     rv = @parser.filter_body(tmp, str)
326     assert_equal rv.object_id, str.object_id
327     assert ! @parser.keepalive?
328   end
330   def test_chunks_bytewise
331     chunked = "10\r\nabcdefghijklmnop\r\n11\r\n0123456789abcdefg\r\n0\r\n"
332     str = "PUT / HTTP/1.1\r\ntransfer-Encoding: chunked\r\n\r\n"
333     buf = @parser.buf
334     buf << str
335     req = @parser.env
336     assert_equal req, @parser.parse
337     assert_equal "", buf
338     tmp = ''
339     body = ''
340     str = chunked[0..-2]
341     str.each_byte { |byte|
342       assert_nil @parser.filter_body(tmp, buf << byte.chr)
343       body << tmp
344     }
345     assert_equal 'abcdefghijklmnop0123456789abcdefg', body
346     rv = @parser.filter_body(tmp, buf<< "\n")
347     assert_equal rv.object_id, buf.object_id
348     assert ! @parser.keepalive?
349   end
351   def test_trailers
352     req = @parser.env
353     str = @parser.buf
354     str << "PUT / HTTP/1.1\r\n" \
355            "Trailer: Content-MD5\r\n" \
356            "transfer-Encoding: chunked\r\n\r\n" \
357            "1\r\na\r\n2\r\n..\r\n0\r\n"
358     assert_equal req, @parser.parse
359     assert_equal 'Content-MD5', req['HTTP_TRAILER']
360     assert_nil req['HTTP_CONTENT_MD5']
361     tmp = ''
362     assert_nil @parser.filter_body(tmp, str)
363     assert_equal 'a..', tmp
364     md5_b64 = [ Digest::MD5.digest(tmp) ].pack('m').strip.freeze
365     rv = @parser.filter_body(tmp, str)
366     assert_equal rv.object_id, str.object_id
367     assert_equal '', str
368     md5_hdr = "Content-MD5: #{md5_b64}\r\n".freeze
369     str << md5_hdr
370     assert_nil @parser.trailers(req, str)
371     assert_equal md5_b64, req['HTTP_CONTENT_MD5']
372     assert_equal "CONTENT_MD5: #{md5_b64}\r\n", str
373     str << "\r"
374     assert_nil @parser.parse
375     str << "\nGET / "
376     assert_equal req, @parser.parse
377     assert_equal "GET / ", str
378     assert @parser.keepalive?
379   end
381   def test_trailers_slowly
382     str = @parser.buf
383     str << "PUT / HTTP/1.1\r\n" \
384            "Trailer: Content-MD5\r\n" \
385            "transfer-Encoding: chunked\r\n\r\n" \
386            "1\r\na\r\n2\r\n..\r\n0\r\n"
387     req = @parser.env
388     assert_equal req, @parser.parse
389     assert_equal 'Content-MD5', req['HTTP_TRAILER']
390     assert_nil req['HTTP_CONTENT_MD5']
391     tmp = ''
392     assert_nil @parser.filter_body(tmp, str)
393     assert_equal 'a..', tmp
394     md5_b64 = [ Digest::MD5.digest(tmp) ].pack('m').strip.freeze
395     rv = @parser.filter_body(tmp, str)
396     assert_equal rv.object_id, str.object_id
397     assert_equal '', str
398     assert_nil @parser.trailers(req, str)
399     md5_hdr = "Content-MD5: #{md5_b64}\r\n".freeze
400     md5_hdr.each_byte { |byte|
401       str << byte.chr
402       assert_nil @parser.trailers(req, str)
403     }
404     assert_equal md5_b64, req['HTTP_CONTENT_MD5']
405     assert_equal "CONTENT_MD5: #{md5_b64}\r\n", str
406     str << "\r"
407     assert_nil @parser.parse
408     str << "\n"
409     assert_equal req, @parser.parse
410   end
412   def test_max_chunk
413     str = @parser.buf
414     str << "PUT / HTTP/1.1\r\n" \
415            "transfer-Encoding: chunked\r\n\r\n" \
416            "#{HttpParser::CHUNK_MAX.to_s(16)}\r\na\r\n2\r\n..\r\n0\r\n"
417     req = @parser.env
418     assert_equal req, @parser.parse
419     assert_nil @parser.content_length
420     @parser.filter_body('', str)
421     assert ! @parser.keepalive?
422   end
424   def test_max_body
425     n = HttpParser::LENGTH_MAX
426     @parser.buf << "PUT / HTTP/1.1\r\nContent-Length: #{n}\r\n\r\n"
427     req = @parser.env
428     @parser.headers(req, @parser.buf)
429     assert_equal n, req['CONTENT_LENGTH'].to_i
430     assert ! @parser.keepalive?
431   end
433   def test_overflow_chunk
434     n = HttpParser::CHUNK_MAX + 1
435     str = @parser.buf
436     req = @parser.env
437     str << "PUT / HTTP/1.1\r\n" \
438            "transfer-Encoding: chunked\r\n\r\n" \
439            "#{n.to_s(16)}\r\na\r\n2\r\n..\r\n0\r\n"
440     assert_equal req, @parser.parse
441     assert_nil @parser.content_length
442     assert_raise(HttpParserError) { @parser.filter_body('', str) }
443   end
445   def test_overflow_content_length
446     n = HttpParser::LENGTH_MAX + 1
447     @parser.buf << "PUT / HTTP/1.1\r\nContent-Length: #{n}\r\n\r\n"
448     assert_raise(HttpParserError) { @parser.parse }
449   end
451   def test_bad_chunk
452     @parser.buf << "PUT / HTTP/1.1\r\n" \
453                    "transfer-Encoding: chunked\r\n\r\n" \
454                    "#zzz\r\na\r\n2\r\n..\r\n0\r\n"
455     req = @parser.env
456     assert_equal req, @parser.parse
457     assert_nil @parser.content_length
458     assert_raise(HttpParserError) { @parser.filter_body("", @parser.buf) }
459   end
461   def test_bad_content_length
462     @parser.buf << "PUT / HTTP/1.1\r\nContent-Length: 7ff\r\n\r\n"
463     assert_raise(HttpParserError) { @parser.parse }
464   end
466   def test_bad_trailers
467     str = @parser.buf
468     req = @parser.env
469     str << "PUT / HTTP/1.1\r\n" \
470            "Trailer: Transfer-Encoding\r\n" \
471            "transfer-Encoding: chunked\r\n\r\n" \
472            "1\r\na\r\n2\r\n..\r\n0\r\n"
473     assert_equal req, @parser.parse
474     assert_equal 'Transfer-Encoding', req['HTTP_TRAILER']
475     tmp = ''
476     assert_nil @parser.filter_body(tmp, str)
477     assert_equal 'a..', tmp
478     assert_equal '', str
479     str << "Transfer-Encoding: identity\r\n\r\n"
480     assert_raise(HttpParserError) { @parser.parse }
481   end
483   def test_repeat_headers
484     str = "PUT / HTTP/1.1\r\n" \
485           "Trailer: Content-MD5\r\n" \
486           "Trailer: Content-SHA1\r\n" \
487           "transfer-Encoding: chunked\r\n\r\n" \
488           "1\r\na\r\n2\r\n..\r\n0\r\n"
489     req = @parser.env
490     @parser.buf << str
491     assert_equal req, @parser.parse
492     assert_equal 'Content-MD5,Content-SHA1', req['HTTP_TRAILER']
493     assert ! @parser.keepalive?
494   end
496   def test_parse_simple_request
497     parser = HttpParser.new
498     req = parser.env
499     parser.buf << "GET /read-rfc1945-if-you-dont-believe-me\r\n"
500     assert_equal req, parser.parse
501     assert_equal '', parser.buf
502     expect = {
503       "SERVER_NAME"=>"localhost",
504       "rack.url_scheme"=>"http",
505       "REQUEST_PATH"=>"/read-rfc1945-if-you-dont-believe-me",
506       "PATH_INFO"=>"/read-rfc1945-if-you-dont-believe-me",
507       "REQUEST_URI"=>"/read-rfc1945-if-you-dont-believe-me",
508       "SERVER_PORT"=>"80",
509       "SERVER_PROTOCOL"=>"HTTP/0.9",
510       "REQUEST_METHOD"=>"GET",
511       "QUERY_STRING"=>""
512     }
513     assert_equal expect, req
514     assert ! parser.headers?
515   end
517   def test_path_info_semicolon
518     qs = "QUERY_STRING"
519     pi = "PATH_INFO"
520     req = {}
521     str = "GET %s HTTP/1.1\r\nHost: example.com\r\n\r\n"
522     {
523       "/1;a=b?c=d&e=f" => { qs => "c=d&e=f", pi => "/1;a=b" },
524       "/1?c=d&e=f" => { qs => "c=d&e=f", pi => "/1" },
525       "/1;a=b" => { qs => "", pi => "/1;a=b" },
526       "/1;a=b?" => { qs => "", pi => "/1;a=b" },
527       "/1?a=b;c=d&e=f" => { qs => "a=b;c=d&e=f", pi => "/1" },
528       "*" => { qs => "", pi => "" },
529     }.each do |uri,expect|
530       assert_equal req, @parser.headers(req.clear, str % [ uri ])
531       req = req.dup
532       @parser.clear
533       assert_equal uri, req["REQUEST_URI"], "REQUEST_URI mismatch"
534       assert_equal expect[qs], req[qs], "#{qs} mismatch"
535       assert_equal expect[pi], req[pi], "#{pi} mismatch"
536       next if uri == "*"
537       uri = URI.parse("http://example.com#{uri}")
538       assert_equal uri.query.to_s, req[qs], "#{qs} mismatch URI.parse disagrees"
539       assert_equal uri.path, req[pi], "#{pi} mismatch URI.parse disagrees"
540     end
541   end
543   def test_path_info_semicolon_absolute
544     qs = "QUERY_STRING"
545     pi = "PATH_INFO"
546     req = {}
547     str = "GET http://example.com%s HTTP/1.1\r\nHost: www.example.com\r\n\r\n"
548     {
549       "/1;a=b?c=d&e=f" => { qs => "c=d&e=f", pi => "/1;a=b" },
550       "/1?c=d&e=f" => { qs => "c=d&e=f", pi => "/1" },
551       "/1;a=b" => { qs => "", pi => "/1;a=b" },
552       "/1;a=b?" => { qs => "", pi => "/1;a=b" },
553       "/1?a=b;c=d&e=f" => { qs => "a=b;c=d&e=f", pi => "/1" },
554     }.each do |uri,expect|
555       assert_equal req, @parser.headers(req.clear, str % [ uri ])
556       req = req.dup
557       @parser.clear
558       assert_equal uri, req["REQUEST_URI"], "REQUEST_URI mismatch"
559       assert_equal "example.com", req["HTTP_HOST"], "Host: mismatch"
560       assert_equal expect[qs], req[qs], "#{qs} mismatch"
561       assert_equal expect[pi], req[pi], "#{pi} mismatch"
562     end
563   end
565   def test_negative_content_length
566     req = {}
567     str = "PUT / HTTP/1.1\r\n" \
568           "Content-Length: -1\r\n" \
569           "\r\n"
570     assert_raises(HttpParserError) do
571       @parser.headers(req, str)
572     end
573   end
575   def test_invalid_content_length
576     req = {}
577     str = "PUT / HTTP/1.1\r\n" \
578           "Content-Length: zzzzz\r\n" \
579           "\r\n"
580     assert_raises(HttpParserError) do
581       @parser.headers(req, str)
582     end
583   end
585   def test_backtrace_is_empty
586     begin
587       @parser.headers({}, "AAADFSFDSFD\r\n\r\n")
588       assert false, "should never get here line:#{__LINE__}"
589     rescue HttpParserError => e
590       assert_equal [], e.backtrace
591       return
592     end
593     assert false, "should never get here line:#{__LINE__}"
594   end
596   def test_ignore_version_header
597     @parser.buf << "GET / HTTP/1.1\r\nVersion: hello\r\n\r\n"
598     req = @parser.env
599     assert_equal req, @parser.parse
600     assert_equal '', @parser.buf
601     expect = {
602       "SERVER_NAME" => "localhost",
603       "rack.url_scheme" => "http",
604       "REQUEST_PATH" => "/",
605       "SERVER_PROTOCOL" => "HTTP/1.1",
606       "PATH_INFO" => "/",
607       "HTTP_VERSION" => "HTTP/1.1",
608       "REQUEST_URI" => "/",
609       "SERVER_PORT" => "80",
610       "REQUEST_METHOD" => "GET",
611       "QUERY_STRING" => ""
612     }
613     assert_equal expect, req
614   end
616   def test_pipelined_requests
617     host = "example.com"
618     expect = {
619       "HTTP_HOST" => host,
620       "SERVER_NAME" => host,
621       "REQUEST_PATH" => "/",
622       "rack.url_scheme" => "http",
623       "SERVER_PROTOCOL" => "HTTP/1.1",
624       "PATH_INFO" => "/",
625       "HTTP_VERSION" => "HTTP/1.1",
626       "REQUEST_URI" => "/",
627       "SERVER_PORT" => "80",
628       "REQUEST_METHOD" => "GET",
629       "QUERY_STRING" => ""
630     }
631     req1 = "GET / HTTP/1.1\r\nHost: example.com\r\n\r\n"
632     req2 = "GET / HTTP/1.1\r\nHost: www.example.com\r\n\r\n"
633     @parser.buf << (req1 + req2)
634     env1 = @parser.parse.dup
635     assert_equal expect, env1
636     assert_equal req2, @parser.buf
637     assert ! @parser.env.empty?
638     assert @parser.next?
639     assert @parser.keepalive?
640     assert @parser.headers?
641     assert_equal expect, @parser.env
642     env2 = @parser.parse.dup
643     host.replace "www.example.com"
644     assert_equal "www.example.com", expect["HTTP_HOST"]
645     assert_equal "www.example.com", expect["SERVER_NAME"]
646     assert_equal expect, env2
647     assert_equal "", @parser.buf
648   end
650   def test_keepalive_requests_disabled
651     req = "GET / HTTP/1.1\r\nHost: example.com\r\n\r\n".freeze
652     expect = {
653       "SERVER_NAME" => "example.com",
654       "HTTP_HOST" => "example.com",
655       "rack.url_scheme" => "http",
656       "REQUEST_PATH" => "/",
657       "SERVER_PROTOCOL" => "HTTP/1.1",
658       "PATH_INFO" => "/",
659       "HTTP_VERSION" => "HTTP/1.1",
660       "REQUEST_URI" => "/",
661       "SERVER_PORT" => "80",
662       "REQUEST_METHOD" => "GET",
663       "QUERY_STRING" => ""
664     }.freeze
665     HttpParser.keepalive_requests = 0
666     @parser = HttpParser.new
667     @parser.buf << req
668     assert_equal expect, @parser.parse
669     assert ! @parser.next?
670   end
672   def test_chunk_only
673     tmp = ""
674     assert_equal @parser, @parser.dechunk!
675     assert_nil @parser.filter_body(tmp, "6\r\n")
676     assert_equal "", tmp
677     assert_nil @parser.filter_body(tmp, "abcdef")
678     assert_equal "abcdef", tmp
679     assert_nil @parser.filter_body(tmp, "\r\n")
680     assert_equal "", tmp
681     src = "0\r\n\r\n"
682     assert_equal src.object_id, @parser.filter_body(tmp, src).object_id
683     assert_equal "", tmp
684   end
686   def test_chunk_only_bad_align
687     tmp = ""
688     assert_equal @parser, @parser.dechunk!
689     assert_nil @parser.filter_body(tmp, "6\r\na")
690     assert_equal "a", tmp
691     assert_nil @parser.filter_body(tmp, "bcde")
692     assert_equal "bcde", tmp
693     assert_nil @parser.filter_body(tmp, "f\r")
694     assert_equal "f", tmp
695     src = "\n0\r\n\r\n"
696     assert_equal src.object_id, @parser.filter_body(tmp, src).object_id
697     assert_equal "", tmp
698   end
700   def test_chunk_only_reset_ok
701     tmp = ""
702     assert_equal @parser, @parser.dechunk!
703     src = "1\r\na\r\n0\r\n\r\n"
704     assert_nil @parser.filter_body(tmp, src)
705     assert_equal "a", tmp
706     assert_equal src.object_id, @parser.filter_body(tmp, src).object_id
708     assert_equal @parser, @parser.dechunk!
709     src = "0\r\n\r\n"
710     assert_equal src.object_id, @parser.filter_body(tmp, src).object_id
711     assert_equal "", tmp
712     assert_equal src, @parser.filter_body(tmp, src)
713   end