yahns Ruby server user/dev discussion
 help / color / mirror / code / Atom feed
From: Eric Wong <e@80x24.org>
To: yahns-public@yhbt.net
Subject: [WIP OPTION #2] fix output buffering with SSL_write
Date: Tue, 16 Feb 2016 11:14:11 +0000	[thread overview]
Message-ID: <20160216111411.GA24123@dcvr.yhbt.net> (raw)
In-Reply-To: <20160216111126.GA22532@dcvr.yhbt.net>

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


  reply	other threads:[~2016-02-16 11:14 UTC|newest]

Thread overview: 4+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2016-02-16 11:11 [WIP OPTION #1] " Eric Wong
2016-02-16 11:14 ` Eric Wong [this message]
2016-02-20  2:54   ` [PATCH] " Eric Wong
2016-02-20 21:33     ` Eric Wong

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://yhbt.net/yahns/README

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20160216111411.GA24123@dcvr.yhbt.net \
    --to=e@80x24.org \
    --cc=yahns-public@yhbt.net \
    --subject='Re: [WIP OPTION #2] fix output buffering with SSL_write' \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link

Code repositories for project(s) associated with this inbox:

	../../yahns.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).