From f38678f2d7a7b3a78a566c5efc9f88c02373dc09 Mon Sep 17 00:00:00 2001 From: Evan Phoenix Date: Wed, 22 Aug 2012 22:34:10 -0700 Subject: [PATCH] Handle SSL eof and nonblocking --- ext/puma_http11/mini_ssl.c | 11 +++++++---- lib/puma/client.rb | 12 ++++++++++-- lib/puma/minissl.rb | 18 ++++++++++++++++++ 3 files changed, 35 insertions(+), 6 deletions(-) diff --git a/ext/puma_http11/mini_ssl.c b/ext/puma_http11/mini_ssl.c index b321cc80..899e5b6a 100644 --- a/ext/puma_http11/mini_ssl.c +++ b/ext/puma_http11/mini_ssl.c @@ -94,11 +94,10 @@ static VALUE eError; void raise_error(SSL* ssl, int result) { int error = SSL_get_error(ssl, result); - char buffer[256]; + char* msg = ERR_error_string(error, NULL); - ERR_error_string_n(error, buffer, sizeof(buffer)); - - rb_raise(eError, "OpenSSL error: %s", buffer); + ERR_clear_error(); + rb_raise(eError, "OpenSSL error: %s - %d", msg, error); } VALUE engine_read(VALUE self) { @@ -116,6 +115,10 @@ VALUE engine_read(VALUE self) { if(SSL_want_read(conn->ssl)) return Qnil; + if(SSL_get_error(conn->ssl, bytes) == SSL_ERROR_ZERO_RETURN) { + rb_eof_error(); + } + raise_error(conn->ssl, bytes); } diff --git a/lib/puma/client.rb b/lib/puma/client.rb index 01a6d2af..000465a7 100644 --- a/lib/puma/client.rb +++ b/lib/puma/client.rb @@ -99,7 +99,11 @@ module Puma def try_to_finish return read_body unless @read_header - data = @io.readpartial(CHUNK_SIZE) + begin + data = @io.read_nonblock(CHUNK_SIZE) + rescue Errno::EAGAIN + return false + end if @buffer @buffer << data @@ -136,7 +140,11 @@ module Puma want = remain end - chunk = @io.readpartial(want) + begin + chunk = @io.read_nonblock(want) + rescue Errno::EAGAIN + return false + end # No chunk means a closed socket unless chunk diff --git a/lib/puma/minissl.rb b/lib/puma/minissl.rb index e62c988e..ccf0a1f9 100644 --- a/lib/puma/minissl.rb +++ b/lib/puma/minissl.rb @@ -26,6 +26,24 @@ module Puma::MiniSSL end end + def read_nonblock(size) + while true + output = @engine.read + return output if output + + data = @socket.read_nonblock(size) + + @engine.inject(data) + output = @engine.read + + return output if output + + while neg_data = @engine.extract + @socket.write neg_data + end + end + end + def write(data) need = data.size