diff options
author | Eric Wong <normalperson@yhbt.net> | 2010-07-24 23:18:14 +0000 |
---|---|---|
committer | Eric Wong <normalperson@yhbt.net> | 2010-07-25 00:00:25 +0000 |
commit | 62621af7c8724f5cc5cc3a6dfcd47eb6654286a5 (patch) | |
tree | 62a16915f33891bb82afd6bd930f67eb7f8136e8 | |
parent | 35e3ad8134c39902b502e20d88246be2751767e1 (diff) | |
download | kcar-62621af7c8724f5cc5cc3a6dfcd47eb6654286a5.tar.gz |
When parsing a stream of multiple responses (when HTTP pipelining is enabled), we did not properly reset our internal buffer, causing failures when parsing the second response.
-rw-r--r-- | lib/kcar/response.rb | 10 | ||||
-rw-r--r-- | test/test_response.rb | 63 |
2 files changed, 70 insertions, 3 deletions
diff --git a/lib/kcar/response.rb b/lib/kcar/response.rb index 265c98a..4b90883 100644 --- a/lib/kcar/response.rb +++ b/lib/kcar/response.rb @@ -140,9 +140,13 @@ class Response < Struct.new(:sock, :hdr, :unchunk, :buf, :parser) len -= dst.size yield dst end - while len > 0 - len -= sock.readpartial(len > READ_SIZE ? READ_SIZE : len, dst).size - yield dst + + if len > 0 + begin + len -= sock.readpartial(len > READ_SIZE ? READ_SIZE : len, dst).size + yield dst + end while len > 0 + dst.respond_to?(:clear) ? dst.clear : self.buf = '' end end end diff --git a/test/test_response.rb b/test/test_response.rb index ab1c30c..8eb0736 100644 --- a/test/test_response.rb +++ b/test/test_response.rb @@ -3,12 +3,75 @@ require 'test/unit' require 'pp' require 'socket' require 'kcar' +require 'digest/sha1' class TestSession < Test::Unit::TestCase def setup @s, @c = UNIXSocket.pair end + def test_http_small_pipelined_identity + resp = "HTTP/1.1 200 OK\r\nContent-Length: 12\r\n\r\nhello world\n" \ + "HTTP/1.1 200 OK\r\nContent-Length: 14\r\n\r\ngoodbye world\n" + pid = fork do + @s << resp + @s.close + end + @s.close + @response = Kcar::Response.new(@c) + status, headers, body = @response.rack + assert_equal status, "200 OK" + assert_equal({'Content-Length'=>'12'},headers) + tmp = [] + assert_nothing_raised { body.each { |chunk| tmp << chunk.dup } } + assert_equal [ "hello world\n" ], tmp + body.close + assert ! @c.closed? + + status, headers, body = @response.rack + assert_equal status, "200 OK" + assert_equal({'Content-Length'=>'14'},headers) + tmp = [] + assert_nothing_raised { body.each { |chunk| tmp << chunk.dup } } + assert_equal [ "goodbye world\n" ], tmp + + _, status = Process.waitpid2(pid) + assert status.success? + body.close + end + + def test_http_big_pipelined_identity + nr = 1024 * 512 + width = 80 + length = nr * width + template = "%0#{width - 1}x\n" + expect = Digest::SHA1.new + nr.times { |i| expect << sprintf(template, i) } + pid = fork do + @s.sync = false + @s << "HTTP/1.1 200 OK\r\nContent-Length: #{length}\r\n\r\n" + nr.times { |i| @s.printf(template, i) } + @s << "HTTP/1.1 200 OK\r\nContent-Length: #{length}\r\n\r\n" + nr.times { |i| @s.printf(template, i) } + @s.close + end + @s.close + @response = Kcar::Response.new(@c) + + 2.times do |i| + status, headers, body = @response.rack + assert_equal status, "200 OK" + assert_equal({'Content-Length'=>length.to_s}, headers) + sha1 = Digest::SHA1.new + assert_nothing_raised { body.each { |chunk| sha1 << chunk } } + assert_equal expect, sha1, "#{expect.hexdigest} != #{sha1.hexdigest}" + body.close + assert ! @c.closed? + end + _, status = Process.waitpid2(pid) + assert status.success? + end + def test_http_one_zero pid = fork do @s << "HTTP/1.0 200 OK\r\n\r\nHI" |