about summary refs log tree commit homepage
diff options
context:
space:
mode:
-rw-r--r--lib/yahns/proxy_http_response.rb37
-rw-r--r--lib/yahns/proxy_pass.rb5
-rw-r--r--lib/yahns/wbuf_common.rb1
-rw-r--r--test/test_proxy_pass.rb15
4 files changed, 40 insertions, 18 deletions
diff --git a/lib/yahns/proxy_http_response.rb b/lib/yahns/proxy_http_response.rb
index c8a2a42..c5e7be5 100644
--- a/lib/yahns/proxy_http_response.rb
+++ b/lib/yahns/proxy_http_response.rb
@@ -43,7 +43,12 @@ module Yahns::HttpResponse # :nodoc:
                      Rack::Utils::HTTP_STATUS_CODES[code]}\r\n\r\n") rescue nil
 
     shutdown rescue nil
-    req_res.shutdown rescue nil
+    @input = @input.close if @input
+
+    # this is safe ONLY because we are in an :ignore state after
+    # Fdmap#forget when we got hijacked:
+    close
+
     nil # signal close of req_res from yahns_step in yahns/proxy_pass.rb
   ensure
     wbuf.wbuf_abort if wbuf
@@ -56,6 +61,7 @@ module Yahns::HttpResponse # :nodoc:
     :wait_readable # self remains in :ignore, wait on upstream
   end
 
+  # start streaming the response once upstream is done sending headers to us.
   # returns :wait_readable if we need to read more from req_res
   # returns :ignore if we yield control to the client(self)
   # returns nil if completely done
@@ -101,7 +107,6 @@ module Yahns::HttpResponse # :nodoc:
     # but the backend does not terminate properly
     if alive && ! term && (env['HTTP_VERSION'] == 'HTTP/1.1'.freeze)
       res << "Transfer-Encoding: chunked\r\n".freeze
-      alive = true
     end
     res << (alive ? "Connection: keep-alive\r\n\r\n".freeze
                   : "Connection: close\r\n\r\n".freeze)
@@ -178,9 +183,9 @@ module Yahns::HttpResponse # :nodoc:
       end
     end
 
-    return proxy_busy_mod_done(alive) unless wbuf
-    req_res.resbuf = wbuf
-    proxy_busy_mod_blocked(wbuf, wbuf.busy)
+    # all done reading response from upstream, req_res will be discarded
+    # when we return nil:
+    wbuf ? proxy_busy_mod_blocked(wbuf, wbuf.busy) : proxy_busy_mod_done(alive)
   rescue => e
     proxy_err_response(502, req_res, e, wbuf)
   end
@@ -248,7 +253,7 @@ module Yahns::HttpResponse # :nodoc:
     end
 
     busy = wbuf.busy and return proxy_busy_mod_blocked(wbuf, busy)
-    proxy_busy_mod_done(wbuf.wbuf_persist) # returns nil
+    proxy_busy_mod_done(wbuf.wbuf_persist) # returns nil to close req_res
   end
 
   def proxy_wait_next(qflags)
@@ -289,23 +294,23 @@ module Yahns::HttpResponse # :nodoc:
     when :close then close
     end
 
-    nil # close the req_res, too
+    nil # signal close for ReqRes#yahns_step
   end
 
   def proxy_busy_mod_blocked(wbuf, busy)
-    q = Thread.current[:yahns_queue]
     # we are completely done reading and buffering the upstream response,
     # but have not completely written the response to the client,
     # yield control to the client socket:
     @state = wbuf
-    case busy
-    when :wait_readable then q.queue_mod(self, Yahns::Queue::QEV_RD)
-    when :wait_writable then q.queue_mod(self, Yahns::Queue::QEV_WR)
-    else
-      abort "BUG: invalid wbuf.busy: #{busy.inspect}"
-    end
-    # no touching self after queue_mod
-    :ignore
+    proxy_wait_next(case busy
+      when :wait_readable then Yahns::Queue::QEV_RD
+      when :wait_writable then Yahns::Queue::QEV_WR
+      else
+        abort "BUG: invalid wbuf.busy: #{busy.inspect}"
+      end)
+    # no touching self after proxy_wait_next, we may be running
+    # HttpClient#yahns_step in a different thread at this point
+    nil # signal close for ReqRes#yahns_step
   end
 
   # n.b.: we can use String#size for optimized dispatch under YARV instead
diff --git a/lib/yahns/proxy_pass.rb b/lib/yahns/proxy_pass.rb
index 511db02..148957b 100644
--- a/lib/yahns/proxy_pass.rb
+++ b/lib/yahns/proxy_pass.rb
@@ -63,7 +63,8 @@ class Yahns::ProxyPass # :nodoc:
 
         when Yahns::WbufCommon # streaming/buffering the response body
 
-          return c.proxy_response_finish(req, resbuf, self)
+          # we assign wbuf for rescue below:
+          return c.proxy_response_finish(req, wbuf = resbuf, self)
 
         end while true # case @resbuf
 
@@ -79,7 +80,7 @@ class Yahns::ProxyPass # :nodoc:
       when Errno::ECONNREFUSED, Errno::ECONNRESET, Errno::EPIPE
         e.set_backtrace([])
       end
-      c.proxy_err_response(502, self, e, nil)
+      c.proxy_err_response(502, self, e, wbuf)
     end
 
     # returns :wait_readable if complete, :wait_writable if not
diff --git a/lib/yahns/wbuf_common.rb b/lib/yahns/wbuf_common.rb
index c51050b..ee18218 100644
--- a/lib/yahns/wbuf_common.rb
+++ b/lib/yahns/wbuf_common.rb
@@ -38,6 +38,7 @@ module Yahns::WbufCommon # :nodoc:
     end while @sf_count > 0
     wbuf_close(client)
   rescue
+    @wbuf_persist = false # ensure a hijack response is not called
     wbuf_close(client)
     raise
   end
diff --git a/test/test_proxy_pass.rb b/test/test_proxy_pass.rb
index 448e480..c938976 100644
--- a/test/test_proxy_pass.rb
+++ b/test/test_proxy_pass.rb
@@ -586,6 +586,21 @@ class TestProxyPass < Testcase
       assert_match %r{\AHTTP/1\.1 200 OK\r\n}, res
       assert_match %r{\r\n\r\neof-body-slow\z}, res
       s.close
+
+      # we auto-chunk on 1.1 requests and 1.0 backends
+      %w(eof-body-slow eof-body-fast).each do |x|
+        s = TCPSocket.new(host, port)
+        s.write("GET /#{x} HTTP/1.1\r\nHost: example.com\r\n\r\n")
+        res = ''.dup
+        res << s.readpartial(512) until res =~ /0\r\n\r\n\z/
+        s.close
+        head, body = res.split("\r\n\r\n", 2)
+        head = head.split("\r\n")
+        assert_equal 'HTTP/1.1 200 OK', head[0]
+        assert head.include?('Connection: keep-alive')
+        assert head.include?('Transfer-Encoding: chunked')
+        assert_match %r{\Ad\r\n#{x}\r\n0\r\n\r\n\z}, body
+      end
     end
   end