From 62621af7c8724f5cc5cc3a6dfcd47eb6654286a5 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Sat, 24 Jul 2010 23:18:14 +0000 Subject: [PATCH] response: fix handling of pipelined identity responses 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. --- lib/kcar/response.rb | 10 +++++--- 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" -- 2.11.4.GIT