filter exception messages with control characters
[unicorn.git] / test / unit / test_http_parser_ng.rb
blob2b2fe41181cc5d775393dde0c65ca47369f81b50
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_chunked_empty
241     str = @parser.buf
242     req = @parser.env
243     str << "PUT / HTTP/1.1\r\ntransfer-Encoding: chunked\r\n\r\n"
244     assert_equal req, @parser.parse, "msg=#{str}"
245     assert_equal 0, str.size
246     tmp = ""
247     assert_equal str, @parser.filter_body(tmp, str << "0\r\n\r\n")
248     assert_equal "", tmp
249   end
251   def test_two_chunks
252     str = @parser.buf
253     str << "PUT / HTTP/1.1\r\ntransfer-Encoding: chunked\r\n\r\n"
254     req = @parser.env
255     assert_equal req, @parser.parse
256     assert_equal 0, str.size
257     tmp = ""
258     assert_nil @parser.filter_body(tmp, str << "6")
259     assert_equal 0, tmp.size
260     assert_nil @parser.filter_body(tmp, str << "\r\n")
261     assert_equal "", str
262     assert_equal 0, tmp.size
263     tmp = ""
264     assert_nil @parser.filter_body(tmp, str << "..")
265     assert_equal 2, tmp.size
266     assert_equal "..", tmp
267     assert_nil @parser.filter_body(tmp, str << "abcd\r\n1")
268     assert_equal "abcd", tmp
269     assert_nil @parser.filter_body(tmp, str << "\r")
270     assert_equal "", tmp
271     assert_nil @parser.filter_body(tmp, str << "\n")
272     assert_equal "", tmp
273     assert_nil @parser.filter_body(tmp, str << "z")
274     assert_equal "z", tmp
275     assert_nil @parser.filter_body(tmp, str << "\r\n")
276     assert_nil @parser.filter_body(tmp, str << "0")
277     assert_nil @parser.filter_body(tmp, str << "\r")
278     rv = @parser.filter_body(tmp, str << "\nGET")
279     assert_equal "GET", rv
280     assert_equal str.object_id, rv.object_id
281     assert ! @parser.keepalive?
282   end
284   def test_big_chunk
285     str = @parser.buf
286     str << "PUT / HTTP/1.1\r\ntransfer-Encoding: chunked\r\n\r\n" \
287            "4000\r\nabcd"
288     req = @parser.env
289     assert_equal req, @parser.parse
290     tmp = ''
291     assert_nil @parser.filter_body(tmp, str)
292     assert_equal '', str
293     str << ' ' * 16300
294     assert_nil @parser.filter_body(tmp, str)
295     assert_equal '', str
296     str << ' ' * 80
297     assert_nil @parser.filter_body(tmp, str)
298     assert_equal '', str
299     assert ! @parser.body_eof?
300     assert_equal "", @parser.filter_body(tmp, str << "\r\n0\r\n")
301     assert_equal "", tmp
302     assert @parser.body_eof?
303     str << "\r\n"
304     assert_equal req, @parser.parse
305     assert_equal "", str
306     assert @parser.body_eof?
307     assert @parser.keepalive?
308   end
310   def test_two_chunks_oneshot
311     str = @parser.buf
312     req = @parser.env
313     str << "PUT / HTTP/1.1\r\ntransfer-Encoding: chunked\r\n\r\n" \
314            "1\r\na\r\n2\r\n..\r\n0\r\n"
315     assert_equal req, @parser.parse
316     tmp = ''
317     assert_nil @parser.filter_body(tmp, str)
318     assert_equal 'a..', tmp
319     rv = @parser.filter_body(tmp, str)
320     assert_equal rv.object_id, str.object_id
321     assert ! @parser.keepalive?
322   end
324   def test_chunks_bytewise
325     chunked = "10\r\nabcdefghijklmnop\r\n11\r\n0123456789abcdefg\r\n0\r\n"
326     str = "PUT / HTTP/1.1\r\ntransfer-Encoding: chunked\r\n\r\n"
327     buf = @parser.buf
328     buf << str
329     req = @parser.env
330     assert_equal req, @parser.parse
331     assert_equal "", buf
332     tmp = ''
333     body = ''
334     str = chunked[0..-2]
335     str.each_byte { |byte|
336       assert_nil @parser.filter_body(tmp, buf << byte.chr)
337       body << tmp
338     }
339     assert_equal 'abcdefghijklmnop0123456789abcdefg', body
340     rv = @parser.filter_body(tmp, buf<< "\n")
341     assert_equal rv.object_id, buf.object_id
342     assert ! @parser.keepalive?
343   end
345   def test_trailers
346     req = @parser.env
347     str = @parser.buf
348     str << "PUT / HTTP/1.1\r\n" \
349            "Trailer: Content-MD5\r\n" \
350            "transfer-Encoding: chunked\r\n\r\n" \
351            "1\r\na\r\n2\r\n..\r\n0\r\n"
352     assert_equal req, @parser.parse
353     assert_equal 'Content-MD5', req['HTTP_TRAILER']
354     assert_nil req['HTTP_CONTENT_MD5']
355     tmp = ''
356     assert_nil @parser.filter_body(tmp, str)
357     assert_equal 'a..', tmp
358     md5_b64 = [ Digest::MD5.digest(tmp) ].pack('m').strip.freeze
359     rv = @parser.filter_body(tmp, str)
360     assert_equal rv.object_id, str.object_id
361     assert_equal '', str
362     md5_hdr = "Content-MD5: #{md5_b64}\r\n".freeze
363     str << md5_hdr
364     assert_nil @parser.trailers(req, str)
365     assert_equal md5_b64, req['HTTP_CONTENT_MD5']
366     assert_equal "CONTENT_MD5: #{md5_b64}\r\n", str
367     str << "\r"
368     assert_nil @parser.parse
369     str << "\nGET / "
370     assert_equal req, @parser.parse
371     assert_equal "GET / ", str
372     assert @parser.keepalive?
373   end
375   def test_trailers_slowly
376     str = @parser.buf
377     str << "PUT / HTTP/1.1\r\n" \
378            "Trailer: Content-MD5\r\n" \
379            "transfer-Encoding: chunked\r\n\r\n" \
380            "1\r\na\r\n2\r\n..\r\n0\r\n"
381     req = @parser.env
382     assert_equal req, @parser.parse
383     assert_equal 'Content-MD5', req['HTTP_TRAILER']
384     assert_nil req['HTTP_CONTENT_MD5']
385     tmp = ''
386     assert_nil @parser.filter_body(tmp, str)
387     assert_equal 'a..', tmp
388     md5_b64 = [ Digest::MD5.digest(tmp) ].pack('m').strip.freeze
389     rv = @parser.filter_body(tmp, str)
390     assert_equal rv.object_id, str.object_id
391     assert_equal '', str
392     assert_nil @parser.trailers(req, str)
393     md5_hdr = "Content-MD5: #{md5_b64}\r\n".freeze
394     md5_hdr.each_byte { |byte|
395       str << byte.chr
396       assert_nil @parser.trailers(req, str)
397     }
398     assert_equal md5_b64, req['HTTP_CONTENT_MD5']
399     assert_equal "CONTENT_MD5: #{md5_b64}\r\n", str
400     str << "\r"
401     assert_nil @parser.parse
402     str << "\n"
403     assert_equal req, @parser.parse
404   end
406   def test_max_chunk
407     str = @parser.buf
408     str << "PUT / HTTP/1.1\r\n" \
409            "transfer-Encoding: chunked\r\n\r\n" \
410            "#{HttpParser::CHUNK_MAX.to_s(16)}\r\na\r\n2\r\n..\r\n0\r\n"
411     req = @parser.env
412     assert_equal req, @parser.parse
413     assert_nil @parser.content_length
414     assert_nothing_raised { @parser.filter_body('', str) }
415     assert ! @parser.keepalive?
416   end
418   def test_max_body
419     n = HttpParser::LENGTH_MAX
420     @parser.buf << "PUT / HTTP/1.1\r\nContent-Length: #{n}\r\n\r\n"
421     req = @parser.env
422     assert_nothing_raised { @parser.headers(req, @parser.buf) }
423     assert_equal n, req['CONTENT_LENGTH'].to_i
424     assert ! @parser.keepalive?
425   end
427   def test_overflow_chunk
428     n = HttpParser::CHUNK_MAX + 1
429     str = @parser.buf
430     req = @parser.env
431     str << "PUT / HTTP/1.1\r\n" \
432            "transfer-Encoding: chunked\r\n\r\n" \
433            "#{n.to_s(16)}\r\na\r\n2\r\n..\r\n0\r\n"
434     assert_equal req, @parser.parse
435     assert_nil @parser.content_length
436     assert_raise(HttpParserError) { @parser.filter_body('', str) }
437   end
439   def test_overflow_content_length
440     n = HttpParser::LENGTH_MAX + 1
441     @parser.buf << "PUT / HTTP/1.1\r\nContent-Length: #{n}\r\n\r\n"
442     assert_raise(HttpParserError) { @parser.parse }
443   end
445   def test_bad_chunk
446     @parser.buf << "PUT / HTTP/1.1\r\n" \
447                    "transfer-Encoding: chunked\r\n\r\n" \
448                    "#zzz\r\na\r\n2\r\n..\r\n0\r\n"
449     req = @parser.env
450     assert_equal req, @parser.parse
451     assert_nil @parser.content_length
452     assert_raise(HttpParserError) { @parser.filter_body("", @parser.buf) }
453   end
455   def test_bad_content_length
456     @parser.buf << "PUT / HTTP/1.1\r\nContent-Length: 7ff\r\n\r\n"
457     assert_raise(HttpParserError) { @parser.parse }
458   end
460   def test_bad_trailers
461     str = @parser.buf
462     req = @parser.env
463     str << "PUT / HTTP/1.1\r\n" \
464            "Trailer: Transfer-Encoding\r\n" \
465            "transfer-Encoding: chunked\r\n\r\n" \
466            "1\r\na\r\n2\r\n..\r\n0\r\n"
467     assert_equal req, @parser.parse
468     assert_equal 'Transfer-Encoding', req['HTTP_TRAILER']
469     tmp = ''
470     assert_nil @parser.filter_body(tmp, str)
471     assert_equal 'a..', tmp
472     assert_equal '', str
473     str << "Transfer-Encoding: identity\r\n\r\n"
474     assert_raise(HttpParserError) { @parser.parse }
475   end
477   def test_repeat_headers
478     str = "PUT / HTTP/1.1\r\n" \
479           "Trailer: Content-MD5\r\n" \
480           "Trailer: Content-SHA1\r\n" \
481           "transfer-Encoding: chunked\r\n\r\n" \
482           "1\r\na\r\n2\r\n..\r\n0\r\n"
483     req = @parser.env
484     @parser.buf << str
485     assert_equal req, @parser.parse
486     assert_equal 'Content-MD5,Content-SHA1', req['HTTP_TRAILER']
487     assert ! @parser.keepalive?
488   end
490   def test_parse_simple_request
491     parser = HttpParser.new
492     req = parser.env
493     parser.buf << "GET /read-rfc1945-if-you-dont-believe-me\r\n"
494     assert_equal req, parser.parse
495     assert_equal '', parser.buf
496     expect = {
497       "SERVER_NAME"=>"localhost",
498       "rack.url_scheme"=>"http",
499       "REQUEST_PATH"=>"/read-rfc1945-if-you-dont-believe-me",
500       "PATH_INFO"=>"/read-rfc1945-if-you-dont-believe-me",
501       "REQUEST_URI"=>"/read-rfc1945-if-you-dont-believe-me",
502       "SERVER_PORT"=>"80",
503       "SERVER_PROTOCOL"=>"HTTP/0.9",
504       "REQUEST_METHOD"=>"GET",
505       "QUERY_STRING"=>""
506     }
507     assert_equal expect, req
508     assert ! parser.headers?
509   end
511   def test_path_info_semicolon
512     qs = "QUERY_STRING"
513     pi = "PATH_INFO"
514     req = {}
515     str = "GET %s HTTP/1.1\r\nHost: example.com\r\n\r\n"
516     {
517       "/1;a=b?c=d&e=f" => { qs => "c=d&e=f", pi => "/1;a=b" },
518       "/1?c=d&e=f" => { qs => "c=d&e=f", pi => "/1" },
519       "/1;a=b" => { qs => "", pi => "/1;a=b" },
520       "/1;a=b?" => { qs => "", pi => "/1;a=b" },
521       "/1?a=b;c=d&e=f" => { qs => "a=b;c=d&e=f", pi => "/1" },
522       "*" => { qs => "", pi => "" },
523     }.each do |uri,expect|
524       assert_equal req, @parser.headers(req.clear, str % [ uri ])
525       req = req.dup
526       @parser.clear
527       assert_equal uri, req["REQUEST_URI"], "REQUEST_URI mismatch"
528       assert_equal expect[qs], req[qs], "#{qs} mismatch"
529       assert_equal expect[pi], req[pi], "#{pi} mismatch"
530       next if uri == "*"
531       uri = URI.parse("http://example.com#{uri}")
532       assert_equal uri.query.to_s, req[qs], "#{qs} mismatch URI.parse disagrees"
533       assert_equal uri.path, req[pi], "#{pi} mismatch URI.parse disagrees"
534     end
535   end
537   def test_path_info_semicolon_absolute
538     qs = "QUERY_STRING"
539     pi = "PATH_INFO"
540     req = {}
541     str = "GET http://example.com%s HTTP/1.1\r\nHost: www.example.com\r\n\r\n"
542     {
543       "/1;a=b?c=d&e=f" => { qs => "c=d&e=f", pi => "/1;a=b" },
544       "/1?c=d&e=f" => { qs => "c=d&e=f", pi => "/1" },
545       "/1;a=b" => { qs => "", pi => "/1;a=b" },
546       "/1;a=b?" => { qs => "", pi => "/1;a=b" },
547       "/1?a=b;c=d&e=f" => { qs => "a=b;c=d&e=f", pi => "/1" },
548     }.each do |uri,expect|
549       assert_equal req, @parser.headers(req.clear, str % [ uri ])
550       req = req.dup
551       @parser.clear
552       assert_equal uri, req["REQUEST_URI"], "REQUEST_URI mismatch"
553       assert_equal "example.com", req["HTTP_HOST"], "Host: mismatch"
554       assert_equal expect[qs], req[qs], "#{qs} mismatch"
555       assert_equal expect[pi], req[pi], "#{pi} mismatch"
556     end
557   end
559   def test_negative_content_length
560     req = {}
561     str = "PUT / HTTP/1.1\r\n" \
562           "Content-Length: -1\r\n" \
563           "\r\n"
564     assert_raises(HttpParserError) do
565       @parser.headers(req, str)
566     end
567   end
569   def test_invalid_content_length
570     req = {}
571     str = "PUT / HTTP/1.1\r\n" \
572           "Content-Length: zzzzz\r\n" \
573           "\r\n"
574     assert_raises(HttpParserError) do
575       @parser.headers(req, str)
576     end
577   end
579   def test_backtrace_is_empty
580     begin
581       @parser.headers({}, "AAADFSFDSFD\r\n\r\n")
582       assert false, "should never get here line:#{__LINE__}"
583     rescue HttpParserError => e
584       assert_equal [], e.backtrace
585       return
586     end
587     assert false, "should never get here line:#{__LINE__}"
588   end
590   def test_ignore_version_header
591     @parser.buf << "GET / HTTP/1.1\r\nVersion: hello\r\n\r\n"
592     req = @parser.env
593     assert_equal req, @parser.parse
594     assert_equal '', @parser.buf
595     expect = {
596       "SERVER_NAME" => "localhost",
597       "rack.url_scheme" => "http",
598       "REQUEST_PATH" => "/",
599       "SERVER_PROTOCOL" => "HTTP/1.1",
600       "PATH_INFO" => "/",
601       "HTTP_VERSION" => "HTTP/1.1",
602       "REQUEST_URI" => "/",
603       "SERVER_PORT" => "80",
604       "REQUEST_METHOD" => "GET",
605       "QUERY_STRING" => ""
606     }
607     assert_equal expect, req
608   end
610   def test_pipelined_requests
611     host = "example.com"
612     expect = {
613       "HTTP_HOST" => host,
614       "SERVER_NAME" => host,
615       "REQUEST_PATH" => "/",
616       "rack.url_scheme" => "http",
617       "SERVER_PROTOCOL" => "HTTP/1.1",
618       "PATH_INFO" => "/",
619       "HTTP_VERSION" => "HTTP/1.1",
620       "REQUEST_URI" => "/",
621       "SERVER_PORT" => "80",
622       "REQUEST_METHOD" => "GET",
623       "QUERY_STRING" => ""
624     }
625     req1 = "GET / HTTP/1.1\r\nHost: example.com\r\n\r\n"
626     req2 = "GET / HTTP/1.1\r\nHost: www.example.com\r\n\r\n"
627     @parser.buf << (req1 + req2)
628     env1 = @parser.parse.dup
629     assert_equal expect, env1
630     assert_equal req2, @parser.buf
631     assert ! @parser.env.empty?
632     assert @parser.next?
633     assert @parser.keepalive?
634     assert @parser.headers?
635     assert_equal expect, @parser.env
636     env2 = @parser.parse.dup
637     host.replace "www.example.com"
638     assert_equal "www.example.com", expect["HTTP_HOST"]
639     assert_equal "www.example.com", expect["SERVER_NAME"]
640     assert_equal expect, env2
641     assert_equal "", @parser.buf
642   end
644   def test_keepalive_requests_disabled
645     req = "GET / HTTP/1.1\r\nHost: example.com\r\n\r\n".freeze
646     expect = {
647       "SERVER_NAME" => "example.com",
648       "HTTP_HOST" => "example.com",
649       "rack.url_scheme" => "http",
650       "REQUEST_PATH" => "/",
651       "SERVER_PROTOCOL" => "HTTP/1.1",
652       "PATH_INFO" => "/",
653       "HTTP_VERSION" => "HTTP/1.1",
654       "REQUEST_URI" => "/",
655       "SERVER_PORT" => "80",
656       "REQUEST_METHOD" => "GET",
657       "QUERY_STRING" => ""
658     }.freeze
659     HttpParser.keepalive_requests = 0
660     @parser = HttpParser.new
661     @parser.buf << req
662     assert_equal expect, @parser.parse
663     assert ! @parser.next?
664   end
666   def test_chunk_only
667     tmp = ""
668     assert_equal @parser, @parser.dechunk!
669     assert_nil @parser.filter_body(tmp, "6\r\n")
670     assert_equal "", tmp
671     assert_nil @parser.filter_body(tmp, "abcdef")
672     assert_equal "abcdef", tmp
673     assert_nil @parser.filter_body(tmp, "\r\n")
674     assert_equal "", tmp
675     src = "0\r\n\r\n"
676     assert_equal src.object_id, @parser.filter_body(tmp, src).object_id
677     assert_equal "", tmp
678   end
680   def test_chunk_only_bad_align
681     tmp = ""
682     assert_equal @parser, @parser.dechunk!
683     assert_nil @parser.filter_body(tmp, "6\r\na")
684     assert_equal "a", tmp
685     assert_nil @parser.filter_body(tmp, "bcde")
686     assert_equal "bcde", tmp
687     assert_nil @parser.filter_body(tmp, "f\r")
688     assert_equal "f", tmp
689     src = "\n0\r\n\r\n"
690     assert_equal src.object_id, @parser.filter_body(tmp, src).object_id
691     assert_equal "", tmp
692   end
694   def test_chunk_only_reset_ok
695     tmp = ""
696     assert_equal @parser, @parser.dechunk!
697     src = "1\r\na\r\n0\r\n\r\n"
698     assert_nil @parser.filter_body(tmp, src)
699     assert_equal "a", tmp
700     assert_equal src.object_id, @parser.filter_body(tmp, src).object_id
702     assert_equal @parser, @parser.dechunk!
703     src = "0\r\n\r\n"
704     assert_equal src.object_id, @parser.filter_body(tmp, src).object_id
705     assert_equal "", tmp
706     assert_equal src, @parser.filter_body(tmp, src)
707   end