about summary refs log tree commit homepage
diff options
context:
space:
mode:
-rw-r--r--lib/yahns/wbuf_common.rb10
-rw-r--r--test/server_helper.rb14
-rw-r--r--test/test_output_buffering.rb14
-rw-r--r--test/test_serve_static.rb61
-rw-r--r--test/test_wbuf.rb7
5 files changed, 85 insertions, 21 deletions
diff --git a/lib/yahns/wbuf_common.rb b/lib/yahns/wbuf_common.rb
index cfafaa2..b3a4502 100644
--- a/lib/yahns/wbuf_common.rb
+++ b/lib/yahns/wbuf_common.rb
@@ -14,8 +14,16 @@ module Yahns::WbufCommon # :nodoc:
       @sf_offset += rv # keep going otherwise
     when :wait_writable, :wait_readable
       return rv
+    when nil
+      # response got truncated, drop the connection
+      # this may happens when using Rack::File or similar, we can't
+      # keep the connection alive because we already sent our Content-Length
+      # header the client would be confused.
+      @wbuf_persist = false
+      return wbuf_close(client)
     else
-      raise "BUG: #{rv.nil? ? "EOF" : rv.inspect} on tmpio=#{@tmpio.inspect} " \
+      raise "BUG: rv=#{rv.inspect} " \
+            "on tmpio=#{@tmpio.inspect} " \
             "sf_offset=#@sf_offset sf_count=#@sf_count"
     end while true
   end
diff --git a/test/server_helper.rb b/test/server_helper.rb
index 52b08c7..2f9266a 100644
--- a/test/server_helper.rb
+++ b/test/server_helper.rb
@@ -78,6 +78,20 @@ module ServerHelper
       Yahns::Server.new(cfg).start.join
     end
   end
+
+  def wait_for_full(c)
+    prev = 0
+    prev_time = Time.now
+    begin
+      nr = c.nread
+      break if nr > 0 && nr == prev && (Time.now - prev_time) > 0.5
+      if nr != prev
+        prev = nr
+        prev_time = Time.now
+      end
+      Thread.pass
+    end while sleep(0.1)
+  end
 end
 
 module TrywriteBlocked
diff --git a/test/test_output_buffering.rb b/test/test_output_buffering.rb
index a448062..7b99418 100644
--- a/test/test_output_buffering.rb
+++ b/test/test_output_buffering.rb
@@ -86,20 +86,6 @@ class TestOutputBuffering < Testcase
     quit_wait(pid)
   end
 
-  def wait_for_full(c)
-    prev = 0
-    prev_time = Time.now
-    begin
-      nr = c.nread
-      break if nr > 0 && nr == prev && (Time.now - prev_time) > 0.5
-      if nr != prev
-        prev = nr
-        prev_time = Time.now
-      end
-      Thread.pass
-    end while sleep(0.1)
-  end
-
   def md5sum(c)
     dig = Digest::MD5.new
     buf = ""
diff --git a/test/test_serve_static.rb b/test/test_serve_static.rb
index b1817f7..4f33b6f 100644
--- a/test/test_serve_static.rb
+++ b/test/test_serve_static.rb
@@ -73,4 +73,65 @@ class TestServeStatic < Testcase
   ensure
     quit_wait(pid)
   end
+
+  def mksparse(tmpdir)
+    sparse = "#{tmpdir}/sparse"
+    off = 100 * 1024 * 1024
+    File.open(sparse, "w") do |fp|
+      fp.sysseek(off)
+      fp.syswrite '.'
+    end
+    [ off + 1, sparse ]
+  end
+
+  def test_truncated_sendfile
+    tmpdir = Dir.mktmpdir
+    size, sparse = mksparse(tmpdir)
+    err, cfg, host, port = @err, Yahns::Config.new, @srv.addr[3], @srv.addr[1]
+    pid = mkserver(cfg) do
+      cfg.instance_eval do
+        app(:rack, Rack::File.new(tmpdir)) { listen "#{host}:#{port}" }
+        stderr_path err.path
+      end
+    end
+    c = get_tcp_client(host, port)
+    c.write "GET /sparse HTTP/1.1\r\nHost: example.com\r\n\r\n"
+    wait_for_full(c)
+    File.truncate(sparse, 5)
+    buf = Timeout.timeout(60) { c.read }
+    c.close
+    assert_operator buf.size, :<, size
+  ensure
+    quit_wait(pid)
+    FileUtils.rm_rf(tmpdir)
+  end
+
+  def test_expanded_sendfile
+    tmpdir = Dir.mktmpdir
+    size, sparse = mksparse(tmpdir)
+    err, cfg, host, port = @err, Yahns::Config.new, @srv.addr[3], @srv.addr[1]
+    pid = mkserver(cfg) do
+      cfg.instance_eval do
+        app(:rack, Rack::File.new(tmpdir)) { listen "#{host}:#{port}" }
+        stderr_path err.path
+      end
+    end
+    c = get_tcp_client(host, port)
+    c.write "GET /sparse\r\n\r\n"
+    wait_for_full(c)
+
+    File.open(sparse, "w") do |fp|
+      fp.sysseek(size * 2)
+      fp.syswrite '.'
+    end
+    Timeout.timeout(60) do
+      bytes = IO.copy_stream(c, "/dev/null")
+      assert_equal bytes, size
+      assert_raises(EOFError) { c.readpartial 1 }
+    end
+    c.close
+  ensure
+    quit_wait(pid)
+    FileUtils.rm_rf(tmpdir)
+  end
 end
diff --git a/test/test_wbuf.rb b/test/test_wbuf.rb
index a9dc717..25cdeba 100644
--- a/test/test_wbuf.rb
+++ b/test/test_wbuf.rb
@@ -82,12 +82,7 @@ class TestWbuf < Testcase
     b.read(nr - 2) if nr > 2
     assert_equal b, IO.select([b], nil, nil, 5)[0][0]
     assert_equal "HI", b.read(2)
-    begin
-      wbuf.wbuf_flush(a)
-      assert false
-    rescue => e
-    end
-    assert_match(%r{BUG: EOF on tmpio}, e.message)
+    assert_equal false, wbuf.wbuf_flush(a)
   ensure
     a.close
     b.close