From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on dcvr.yhbt.net X-Spam-Flag: YES X-Spam-Level: ********* X-Spam-ASN: AS200651 185.100.84.0/23 X-Spam-Status: Yes, score=9.9 required=3.0 tests=AWL,BAYES_00, RCVD_IN_BL_SPAMCOP_NET,RCVD_IN_BRBL_LASTEXT,RCVD_IN_MSPIKE_BL, RCVD_IN_MSPIKE_L5,RCVD_IN_PSBL,RCVD_IN_RP_RNBL,RCVD_IN_SBL_CSS,RCVD_IN_XBL, RDNS_NONE,SPF_FAIL,SPF_HELO_FAIL shortcircuit=no autolearn=no autolearn_force=no version=3.4.0 X-Spam-Report: * 3.3 RCVD_IN_SBL_CSS RBL: Received via a relay in Spamhaus SBL-CSS * [185.100.84.108 listed in zen.spamhaus.org] * 0.4 RCVD_IN_XBL RBL: Received via a relay in Spamhaus XBL * 1.4 RCVD_IN_BRBL_LASTEXT RBL: No description available. * [185.100.84.108 listed in bb.barracudacentral.org] * 0.0 RCVD_IN_MSPIKE_L5 RBL: Very bad reputation (-5) * [185.100.84.108 listed in bl.mailspike.net] * 2.7 RCVD_IN_PSBL RBL: Received via a relay in PSBL * [185.100.84.108 listed in psbl.surriel.com] * 1.3 RCVD_IN_BL_SPAMCOP_NET RBL: Received via a relay in bl.spamcop.net * [Blocked - see ] * 1.3 RCVD_IN_RP_RNBL RBL: Relay in RNBL, * https://senderscore.org/blacklistlookup/ * [185.100.84.108 listed in bl.score.senderscore.com] * 0.0 SPF_HELO_FAIL SPF: HELO does not match SPF record (fail) * [SPF failed: Please see http://www.openspf.org/Why?s=helo;id=80x24.org;ip=185.100.84.108;r=dcvr.yhbt.net] * 0.0 SPF_FAIL SPF: sender does not match SPF record (fail) * [SPF failed: Please see http://www.openspf.org/Why?s=mfrom;id=e%4080x24.org;ip=185.100.84.108;r=dcvr.yhbt.net] * -1.9 BAYES_00 BODY: Bayes spam probability is 0 to 1% * [score: 0.0000] * 0.0 RCVD_IN_MSPIKE_BL Mailspike blacklisted * 0.8 RDNS_NONE Delivered to internal network by a host with no rDNS * 0.5 AWL AWL: Adjusted score from AWL reputation of From: address Received: from 80x24.org (unknown [185.100.84.108]) by dcvr.yhbt.net (Postfix) with ESMTP id 9137D1FA7A for ; Mon, 16 May 2016 01:43:56 +0000 (UTC) From: Eric Wong To: yahns-public@yhbt.net Subject: [PATCH 4/7] proxy_pass: split out body and trailer reading in response Date: Mon, 16 May 2016 01:43:37 +0000 Message-Id: <20160516014340.8258-5-e@80x24.org> In-Reply-To: <20160516014340.8258-1-e@80x24.org> References: <20160516014340.8258-1-e@80x24.org> List-Id: 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(-) 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 @@ def proxy_res_headers(res) [ 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