From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.3.2 (2011-06-06) on dcvr.yhbt.net X-Spam-Level: X-Spam-ASN: X-Spam-Status: No, score=-2.8 required=3.0 tests=ALL_TRUSTED,AWL,BAYES_00, URIBL_BLOCKED shortcircuit=no autolearn=unavailable version=3.3.2 X-Original-To: yahns-public@yhbt.net Received: from localhost (dcvr.yhbt.net [127.0.0.1]) by dcvr.yhbt.net (Postfix) with ESMTP id 019F720348; Tue, 16 Feb 2016 11:14:12 +0000 (UTC) Date: Tue, 16 Feb 2016 11:14:11 +0000 From: Eric Wong To: yahns-public@yhbt.net Subject: [WIP OPTION #2] fix output buffering with SSL_write Message-ID: <20160216111411.GA24123@dcvr.yhbt.net> References: <20160216111126.GA22532@dcvr.yhbt.net> MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline In-Reply-To: <20160216111126.GA22532@dcvr.yhbt.net> List-Id: The underlying SSL_write called by the OpenSSL socket when we use write_nonblock must get the same arguments after a call returns SSL_ERROR_WANT_READ or SSL_ERROR_WANT_WRITE. Theoretically this works better than option #1 w.r.t embedded strings. But it falls down badly when I run: git clone --mirror https://yhbt.net/yahns-public To clone the mailing list archives. Plain HTTP works fine. So I'm running Option #1 on https://yhbt.net/, for now... Sleepy and tired now :< --- lib/yahns/openssl_client.rb | 21 ++++++++++++++++++++- lib/yahns/proxy_http_response.rb | 1 + lib/yahns/sendfile_compat.rb | 22 ++++++++++++++++------ lib/yahns/wbuf.rb | 3 +++ 4 files changed, 40 insertions(+), 7 deletions(-) diff --git a/lib/yahns/openssl_client.rb b/lib/yahns/openssl_client.rb index bf64255..4587483 100644 --- a/lib/yahns/openssl_client.rb +++ b/lib/yahns/openssl_client.rb @@ -37,15 +37,34 @@ def sync def yahns_init_ssl(ssl_ctx) @need_accept = true @ssl = OpenSSL::SSL::SSLSocket.new(self, ssl_ctx) + @ssl_blocked = ''.dup end def kgio_trywrite(buf) + ssl_blocked = @ssl_blocked + if ! ssl_blocked.empty? && ssl_blocked.object_id != buf.object_id + warn "bk: #{ssl_blocked.inspect}\n!= #{buf.inspect}" + end + warn "buf: #{buf.inspect}" if buf.bytesize <= 23 + #warn "b: #{buf.object_id} #{buf.object_id == ssl_blocked.object_id}" + buf = ssl_blocked.replace(buf) # buf and ssl_blocked may be the same obj + buf.force_encoding Encoding::BINARY rv = @ssl.write_nonblock(buf, exception: false) - Integer === rv and + case rv + when :wait_readable, :wait_writable + #warn "#{fileno} blocking #{buf.bytesize} #{buf.object_id}" + return rv # do not clear ssl_blocked + when Integer rv = buf.bytesize == rv ? nil : buf.byteslice(rv, buf.bytesize) + end + ssl_blocked.clear rv end + def kgio_trywritev(buf) + abort 'BUG: we cannot use writev with OpenSSL' + end + def kgio_syssend(buf, flags) kgio_trywrite(buf) end diff --git a/lib/yahns/proxy_http_response.rb b/lib/yahns/proxy_http_response.rb index 0a7e722..287d039 100644 --- a/lib/yahns/proxy_http_response.rb +++ b/lib/yahns/proxy_http_response.rb @@ -11,6 +11,7 @@ module Yahns::HttpResponse # :nodoc: # it may return a newly-created wbuf or nil def proxy_write(wbuf, buf, alive) unless wbuf + buf = buf.join if Array === buf && respond_to?(:yahns_init_ssl) # no write buffer, try to write directly to the client socket case rv = String === buf ? kgio_trywrite(buf) : kgio_trywritev(buf) when nil then return # done writing buf, likely diff --git a/lib/yahns/sendfile_compat.rb b/lib/yahns/sendfile_compat.rb index 8bd4622..e382f18 100644 --- a/lib/yahns/sendfile_compat.rb +++ b/lib/yahns/sendfile_compat.rb @@ -6,19 +6,29 @@ module Yahns::SendfileCompat # :nodoc: def trysendfile(io, offset, count) return 0 if count == 0 - count = 0x4000 if count > 0x4000 - buf = Thread.current[:yahns_sfbuf] ||= ''.dup - io.pos = offset - str = io.read(count, buf) or return # nil for EOF + + if respond_to?(:yahns_init_ssl) && !@ssl_blocked.empty? + str = @ssl_blocked + #warn "#{fileno} unblocking #{str.bytesize} #{str.object_id}" + else + count = 0x4000 if count > 0x4000 + buf = Thread.current[:yahns_sfbuf] ||= ''.dup + io.pos = offset + str = io.read(count, buf) or return # nil for EOF + end + n = 0 case rv = kgio_trywrite(str) when String # partial write, keep trying - n += (str.size - rv.size) + n += (str.bytesize - rv.size) str = rv when :wait_writable, :wait_readable + if respond_to?(:ssl_blocked) + #warn "empty: #{@ssl_blocked.empty?}" + end return n > 0 ? n : rv when nil - return n + str.size # yay! + return n + str.bytesize # yay! end while true end end diff --git a/lib/yahns/wbuf.rb b/lib/yahns/wbuf.rb index 1b4ce6e..ff67e0c 100644 --- a/lib/yahns/wbuf.rb +++ b/lib/yahns/wbuf.rb @@ -48,6 +48,9 @@ def wbuf_writev(buf) end def wbuf_write(c, buf) + ssl = c.respond_to?(:yahns_init_ssl) + buf = buf.join if ssl && Array === buf + # try to bypass the VFS layer and write directly to the socket # if we're all caught up case rv = String === buf ? c.kgio_trywrite(buf) : c.kgio_trywritev(buf) -- EW