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
--
unsubscribe: [email protected]
archive: http://yhbt.net/yahns-public/