diff --git a/src/main/java/org/jruby/ext/openssl/SSLSocket.java b/src/main/java/org/jruby/ext/openssl/SSLSocket.java index fe53f0f6..c4fa32b3 100644 --- a/src/main/java/org/jruby/ext/openssl/SSLSocket.java +++ b/src/main/java/org/jruby/ext/openssl/SSLSocket.java @@ -733,19 +733,22 @@ private IRubyObject sysreadImpl(final ThreadContext context, if ( ex instanceof IRubyObject ) return (IRubyObject) ex; // :wait_readable } - ByteBuffer dst = ByteBuffer.allocate(length); - int rr = -1; + final ByteBuffer dst = ByteBuffer.allocate(length); + int read = -1; // ensure >0 bytes read; sysread is blocking read. - while ( rr <= 0 ) { + while ( read <= 0 ) { if ( engine == null ) { - rr = socketChannelImpl().read(dst); + read = socketChannelImpl().read(dst); } else { - rr = read(dst, blocking); + read = read(dst, blocking); } - if ( rr == -1 ) throw runtime.newEOFError(); + if ( read == -1 ) { + if ( exception ) throw runtime.newEOFError(); + return runtime.getNil(); + } - if ( rr == 0 && status == SSLEngineResult.Status.BUFFER_UNDERFLOW ) { + if ( read == 0 && status == SSLEngineResult.Status.BUFFER_UNDERFLOW ) { // If we didn't get any data back because we only read in a partial TLS record, // instead of spinning until the rest comes in, call waitSelect to either block // until the rest is available, or throw a "read would block" error if we are in @@ -754,10 +757,10 @@ private IRubyObject sysreadImpl(final ThreadContext context, if ( ex instanceof IRubyObject ) return (IRubyObject) ex; // :wait_readable } } - byte[] bss = new byte[rr]; - dst.position(dst.position() - rr); - dst.get(bss); - buffStr.setValue(new ByteList(bss, false)); + byte[] bytesRead = new byte[read]; + dst.position(dst.position() - read); + dst.get(bytesRead); + buffStr.setValue(new ByteList(bytesRead, false)); return buffStr; } catch (IOException ioe) { diff --git a/src/test/ruby/ssl/test_socket.rb b/src/test/ruby/ssl/test_socket.rb index d3993540..975767f9 100644 --- a/src/test/ruby/ssl/test_socket.rb +++ b/src/test/ruby/ssl/test_socket.rb @@ -85,4 +85,68 @@ def test_ssl_sysread_blocking_error end end if RUBY_VERSION > '2.2' + def test_read_nonblock_no_exception + ssl_pair do |s1, s2| + assert_equal :wait_readable, eval('s2.read_nonblock 10, exception: false') + s1.write "abc\ndef\n" + IO.select [ s2 ] + ret = eval('s2.read_nonblock 2, exception: false') + assert_equal "ab", ret + assert_equal "c\n", s2.gets + ret = eval('s2.read_nonblock 10, exception: false') + assert_equal("def\n", ret) + s1.close + sleep 0.1 + opts = { :exception => false } + assert_equal nil, s2.read_nonblock(10, opts) + end + end if RUBY_VERSION > '2.2' + + private + + def server + require 'socket' + host = "127.0.0.1"; port = 0 + ctx = OpenSSL::SSL::SSLContext.new() + ctx.ciphers = "ADH" + server = TCPServer.new(host, port) + OpenSSL::SSL::SSLServer.new(server, ctx) + end + + def client(port) + require 'socket' + host = "127.0.0.1" + ctx = OpenSSL::SSL::SSLContext.new() + ctx.ciphers = "ADH" + client = TCPSocket.new(host, port) + ssl = OpenSSL::SSL::SSLSocket.new(client, ctx) + ssl.connect + ssl.sync_close = true + ssl + end + + def ssl_pair + ssl_server = server + thread = Thread.new do + ssl_server.accept.tap { ssl_server.close } + end + port = ssl_server.to_io.local_address.ip_port + ssl_client = client(port) + ssl_socket = thread.value + if block_given? + begin + yield ssl_client, ssl_socket + ensure + ssl_client.close unless ssl_client.closed? + ssl_socket.close unless ssl_socket.closed? + end + else + return ssl_client, ssl_socket + end + ensure + if thread && thread.alive? + thread.kill; thread.join + end + end + end \ No newline at end of file