about summary refs log tree commit homepage
diff options
context:
space:
mode:
authorEric Wong <normalperson@yhbt.net>2010-07-24 23:18:14 +0000
committerEric Wong <normalperson@yhbt.net>2010-07-25 00:00:25 +0000
commit62621af7c8724f5cc5cc3a6dfcd47eb6654286a5 (patch)
tree62a16915f33891bb82afd6bd930f67eb7f8136e8
parent35e3ad8134c39902b502e20d88246be2751767e1 (diff)
downloadkcar-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.rb10
-rw-r--r--test/test_response.rb63
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"