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 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 C585620348 for ; Tue, 16 Feb 2016 11:11:26 +0000 (UTC) Date: Tue, 16 Feb 2016 11:11:26 +0000 From: Eric Wong To: yahns-public@yhbt.net Subject: [WIP OPTION #1] fix output buffering with SSL_write Message-ID: <20160216111126.GA22532@dcvr.yhbt.net> MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline 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. FIXME: this probably breaks badly for embedded strings (but it's tricky to test...) --- lib/yahns/openssl_client.rb | 5 +++++ lib/yahns/proxy_http_response.rb | 1 + lib/yahns/sendfile_compat.rb | 23 +++++++++++++++++------ lib/yahns/wbuf.rb | 14 +++++++++++++- 4 files changed, 36 insertions(+), 7 deletions(-) diff --git a/lib/yahns/openssl_client.rb b/lib/yahns/openssl_client.rb index bf64255..c5b5c8a 100644 --- a/lib/yahns/openssl_client.rb +++ b/lib/yahns/openssl_client.rb @@ -37,6 +37,7 @@ def sync def yahns_init_ssl(ssl_ctx) @need_accept = true @ssl = OpenSSL::SSL::SSLSocket.new(self, ssl_ctx) + @ssl_blocked = false end def kgio_trywrite(buf) @@ -46,6 +47,10 @@ def kgio_trywrite(buf) 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..4842cda 100644 --- a/lib/yahns/sendfile_compat.rb +++ b/lib/yahns/sendfile_compat.rb @@ -4,21 +4,32 @@ # frozen_string_literal: true module Yahns::SendfileCompat # :nodoc: + attr_accessor :ssl_blocked + 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 + str = @ssl_blocked + @ssl_blocked = false + 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 + @ssl_blocked = str + Thread.current[:yahns_sfbuf] = ''.dup 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..3dddb95 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) @@ -60,7 +63,16 @@ def wbuf_write(c, buf) end until @busy @tmpio ||= Yahns::TmpIO.new(@tmpdir) - @sf_count += String === buf ? @tmpio.write(buf) : wbuf_writev(buf) + if @sf_count == 0 && c.respond_to?(:yahns_init_ssl) + # We need to maintain a consistent RSTRING_PTR and MRI strings are CoW + # XXX: this breaks if we have embedded strings! + c.ssl_blocked = buf.dup + # we need to maintain placeholder in tmpio so + # sf_offset remains consistent for wbuf_flush + @sf_count = @tmpio.write(buf) + else + @sf_count += String === buf ? @tmpio.write(buf) : wbuf_writev(buf) + end # we spent some time copying to the FS, try to write to # the socket again in case some space opened up... -- EW