diff --git a/.rubocop.yml b/.rubocop.yml index fb20d92e9..96fb535ce 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -57,6 +57,9 @@ Style/ParallelAssignment: Style/NumericPredicate: Enabled: false +Style/IfUnlessModifier: + Enabled: false + Style/SignalException: Exclude: - 'lib/redis/connection/synchrony.rb' diff --git a/lib/redis/connection/ruby.rb b/lib/redis/connection/ruby.rb index a9aca333d..6b6b618d6 100644 --- a/lib/redis/connection/ruby.rb +++ b/lib/redis/connection/ruby.rb @@ -49,43 +49,47 @@ def gets end def _read_from_socket(nbytes) - begin - read_nonblock(nbytes) - rescue IO::WaitReadable - if IO.select([self], nil, nil, @timeout) - retry - else - raise Redis::TimeoutError - end - rescue IO::WaitWritable - if IO.select(nil, [self], nil, @timeout) - retry - else - raise Redis::TimeoutError + loop do + case chunk = read_nonblock(nbytes, exception: false) + when :wait_readable + unless IO.select([self], nil, nil, @timeout) + raise Redis::TimeoutError + end + when :wait_writable + unless IO.select(nil, [self], nil, @timeout) + raise Redis::TimeoutError + end + when nil + raise Errno::ECONNRESET + when String + return chunk end end - rescue EOFError - raise Errno::ECONNRESET end def _write_to_socket(data) - begin - write_nonblock(data) - rescue IO::WaitWritable - if IO.select(nil, [self], nil, @write_timeout) - retry - else - raise Redis::TimeoutError - end - rescue IO::WaitReadable - if IO.select([self], nil, nil, @write_timeout) - retry - else - raise Redis::TimeoutError + total_bytes_written = 0 + loop do + case bytes_written = write_nonblock(data, exception: false) + when :wait_readable + unless IO.select([self], nil, nil, @timeout) + raise Redis::TimeoutError + end + when :wait_writable + unless IO.select(nil, [self], nil, @timeout) + raise Redis::TimeoutError + end + when nil + raise Errno::ECONNRESET + when Integer + total_bytes_written += bytes_written + if bytes_written < data.bytesize + data.slice!(0, bytes_written) + else + return total_bytes_written + end end end - rescue EOFError - raise Errno::ECONNRESET end def write(data) @@ -245,25 +249,27 @@ def self.connect(host, port, timeout, ssl_params) ssl_sock = new(tcp_sock, ctx) ssl_sock.hostname = host - begin - # Initiate the socket connection in the background. If it doesn't fail - # immediately it will raise an IO::WaitWritable (Errno::EINPROGRESS) - # indicating the connection is in progress. - # Unlike waiting for a tcp socket to connect, you can't time out ssl socket - # connections during the connect phase properly, because IO.select only partially works. - # Instead, you have to retry. - ssl_sock.connect_nonblock - rescue Errno::EAGAIN, Errno::EWOULDBLOCK, IO::WaitReadable - if IO.select([ssl_sock], nil, nil, timeout) - retry - else - raise TimeoutError - end - rescue IO::WaitWritable - if IO.select(nil, [ssl_sock], nil, timeout) - retry - else - raise TimeoutError + loop do + begin + # Initiate the socket connection in the background. If it doesn't fail + # immediately it will raise an IO::WaitWritable (Errno::EINPROGRESS) + # indicating the connection is in progress. + # Unlike waiting for a tcp socket to connect, you can't time out ssl socket + # connections during the connect phase properly, because IO.select only partially works. + # Instead, you have to retry. + ssl_sock.connect_nonblock + rescue Errno::EAGAIN, Errno::EWOULDBLOCK, IO::WaitReadable + if IO.select([ssl_sock], nil, nil, timeout) + retry + else + raise TimeoutError + end + rescue IO::WaitWritable + if IO.select(nil, [ssl_sock], nil, timeout) + retry + else + raise TimeoutError + end end end diff --git a/test/internals_test.rb b/test/internals_test.rb index 27e99bb7b..e19b56690 100644 --- a/test/internals_test.rb +++ b/test/internals_test.rb @@ -244,7 +244,7 @@ def close_on_connection(seq) else raise "Expected SELECT" end - unless seq.include?(n) # rubocop:disable Style/IfUnlessModifier + unless seq.include?(n) session.write("+#{n}\r\n") while read_command.call(session) end end