diff options
-rw-r--r-- | lib/yahns/proxy_http_response.rb | 37 | ||||
-rw-r--r-- | lib/yahns/proxy_pass.rb | 5 | ||||
-rw-r--r-- | lib/yahns/wbuf_common.rb | 1 | ||||
-rw-r--r-- | test/test_proxy_pass.rb | 15 |
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 |