From 9d97d3cad93f1f16493afc3e598256eddf2eafef Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Fri, 6 May 2016 00:20:47 +0000 Subject: proxy_pass: split out body and trailer reading in response Hopefully this increases readability as well and allows us to make easier adjustments to support new features in the future. --- lib/yahns/proxy_http_response.rb | 105 +++++++++++++++++++++------------------ 1 file changed, 58 insertions(+), 47 deletions(-) (limited to 'lib') diff --git a/lib/yahns/proxy_http_response.rb b/lib/yahns/proxy_http_response.rb index 00915df..52a8aff 100644 --- a/lib/yahns/proxy_http_response.rb +++ b/lib/yahns/proxy_http_response.rb @@ -124,63 +124,74 @@ module Yahns::HttpResponse # :nodoc: [ alive, wbuf, have_body ] end + def proxy_read_body(tip, kcar, req_res, alive, wbuf) + chunk = ''.dup if kcar.chunked? + len = kcar.body_bytes_left + rbuf = Thread.current[:yahns_rbuf] + + case tmp = tip.shift || req_res.kgio_tryread(0x2000, rbuf) + when String + if len + kcar.body_bytes_left -= tmp.size # progress for body_eof? => true + elsif chunk + kcar.filter_body(chunk, rbuf = tmp) # progress for body_eof? => true + next if chunk.empty? # call req_res.kgio_tryread for more + tmp = chunk_out(chunk) + elsif alive # HTTP/1.0 upstream, HTTP/1.1 client + tmp = chunk_out(tmp) + # else # HTTP/1.0 upstream, HTTP/1.0 client, do nothing + end + wbuf = proxy_write(wbuf, tmp, alive) + chunk.clear if chunk + when nil # EOF + # HTTP/1.1 upstream, unexpected premature EOF: + return proxy_err_response(nil, req_res, nil, wbuf) if len || chunk + + # HTTP/1.0 upstream: + wbuf = proxy_write(wbuf, "0\r\n\r\n".freeze, true) if alive + req_res.shutdown + break + when :wait_readable + return wait_on_upstream(req_res, alive, wbuf) + end until kcar.body_eof? + + if chunk + # tip is an empty array and becomes trailer storage + req_res.proxy_trailers = [ rbuf.dup, tip ] + return proxy_read_trailers(kcar, req_res, alive, wbuf) + end + wbuf ? proxy_busy_mod_blocked(wbuf, wbuf.busy) : proxy_busy_mod_done(alive) + end + + def proxy_read_trailers(kcar, req_res, alive, wbuf) + chunk, tlr = req_res.proxy_trailers + rbuf = Thread.current[:yahns_rbuf] + + until kcar.trailers(tlr, chunk) + case rv = req_res.kgio_tryread(0x2000, rbuf) + when String + chunk << rv + when :wait_readable + return wait_on_upstream(req_res, alive, wbuf) + when nil # premature EOF + return proxy_err_response(nil, req_res, nil, wbuf) + end # no loop here + end + wbuf = proxy_write(wbuf, trailer_out(tlr), alive) + wbuf ? proxy_busy_mod_blocked(wbuf, wbuf.busy) : proxy_busy_mod_done(alive) + 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 def proxy_response_start(res, tip, kcar, req_res) alive, wbuf, have_body = proxy_res_headers(res) - rbuf = Thread.current[:yahns_rbuf] tip = tip.empty? ? [] : [ tip ] if have_body req_res.proxy_trailers = nil # define to avoid uninitialized warnings - chunk = ''.dup if kcar.chunked? - tlr = nil - len = kcar.body_bytes_left - - case tmp = tip.shift || req_res.kgio_tryread(0x2000, rbuf) - when String - if len - kcar.body_bytes_left -= tmp.size # progress for body_eof? => true - elsif chunk - kcar.filter_body(chunk, rbuf = tmp) # progress for body_eof? => true - next if chunk.empty? # read req_res.kgio_tryread for more - tmp = chunk_out(chunk) - elsif alive # HTTP/1.0 upstream, HTTP/1.1 client - tmp = chunk_out(tmp) - # else # HTTP/1.0 upstream, HTTP/1.0 client, do nothing - end - wbuf = proxy_write(wbuf, tmp, alive) - chunk.clear if chunk - when nil # EOF - # HTTP/1.1 upstream, unexpected premature EOF: - return proxy_err_response(nil, req_res, nil, wbuf) if len || chunk - - # HTTP/1.0 upstream: - wbuf = proxy_write(wbuf, "0\r\n\r\n".freeze, true) if alive - req_res.shutdown - break - when :wait_readable - return wait_on_upstream(req_res, alive, wbuf) - end until kcar.body_eof? - - if chunk - chunk = rbuf - req_res.proxy_trailers = [ chunk, tlr = [] ] - rbuf = Thread.current[:yahns_rbuf] = ''.dup - until kcar.trailers(tlr, chunk) - case rv = req_res.kgio_tryread(0x2000, rbuf) - when String - chunk << rv - when :wait_readable - return wait_on_upstream(req_res, alive, wbuf) - when nil # premature EOF - return proxy_err_response(nil, req_res, nil, wbuf) - end # no loop here - end - wbuf = proxy_write(wbuf, trailer_out(tlr), alive) - end + return proxy_read_body(tip, kcar, req_res, alive, wbuf) end # all done reading response from upstream, req_res will be discarded -- cgit v1.2.3-24-ge0c7