Skip to content

Commit

Permalink
Improve TCPSocket read_nonblock & run TCP specs.
Browse files Browse the repository at this point in the history
The main fix: read_nonblock(x) is allowed to return < x bytes even before EOF
as documented in the ruby docs for IO#read_nonblock

Most TCPSocket specs were being ignored because of a Proc.new that wasn't
called.

Also takes the liberty of decoupling TCPSocket specs from the bigger
dependencies such as mysql2, amqp, etc.
  • Loading branch information
greensnark committed Feb 11, 2013
1 parent b28ad32 commit 3adb818
Show file tree
Hide file tree
Showing 4 changed files with 103 additions and 19 deletions.
15 changes: 12 additions & 3 deletions lib/em-synchrony/tcpsocket.rb
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,13 @@ def write(msg)
def read(num_bytes = nil, dest = nil)
handle_read(:read, num_bytes, dest)
end
alias_method :read_nonblock, :read

def read_nonblock(maxlen, dest = nil)
raise ArgumentError, "maxlen must be > 0" if !maxlen || maxlen <= 0
read_bytes = handle_read(:read_nonblock, maxlen, dest)
raise EOFError if read_bytes.nil?
read_bytes
end

def recv(num_bytes, flags = 0)
raise "Unknown flags in recv(): #{flags}" if flags.nonzero?
Expand Down Expand Up @@ -159,11 +165,14 @@ def handle_read(type, num_bytes, dest=nil)
end

def try_read_data
if @read_type == :read
if @read_type == :read || @read_type == :read_nonblock
nonblocking = @read_type == :read_nonblock
unless @remote_closed
if @read_bytes
# read(n) on an open socket, with >= than n buffered data, returns n data
if @in_buff.size >= @read_bytes then @in_buff.slice!(0, @read_bytes)
if (@in_buff.size >= @read_bytes ||
(nonblocking && @in_buff.size > 0)) then
@in_buff.slice!(0, @read_bytes)
# read(n) on an open socket, with < than n buffered data, blocks
else :block end
else
Expand Down
7 changes: 1 addition & 6 deletions spec/helper/all.rb
Original file line number Diff line number Diff line change
@@ -1,9 +1,4 @@
require 'rubygems'
require 'rspec'
require 'pp'

require 'lib/em-synchrony'
require 'lib/em-synchrony/em-http'
require 'spec/helper/core'
require 'lib/em-synchrony/mysql2'
require 'lib/em-synchrony/em-remcached'
require 'lib/em-synchrony/em-memcache'
Expand Down
6 changes: 6 additions & 0 deletions spec/helper/core.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
require 'rubygems'
require 'rspec'
require 'pp'

require 'lib/em-synchrony'
require 'lib/em-synchrony/em-http'
94 changes: 84 additions & 10 deletions spec/tcpsocket_spec.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
require "spec/helper/all"
require "spec/helper/core"

module SendAndClose
def post_init
Expand All @@ -21,15 +21,13 @@ def post_init
end

def tcp_test(server_type, ops={}, &block)
Proc.new do
EventMachine.synchrony do
ops = {:stop => true}.merge ops
EM::start_server('localhost', 12345, server_type)
@socket = EventMachine::Synchrony::TCPSocket.new 'localhost', 12345
@socket.close if ops[:close]
block.call
EM.stop if ops[:stop]
end
EventMachine.synchrony do
ops = {:stop => true}.merge ops
EM::start_server('localhost', 12345, server_type)
@socket = EventMachine::Synchrony::TCPSocket.new 'localhost', 12345
@socket.close if ops[:close]
block.call
EM.stop if ops[:stop]
end
end

Expand Down Expand Up @@ -231,6 +229,82 @@ def tcp_test(server_type, ops={}, &block)
end
end

context '#read_nonblock' do
context 'with a positive length argument' do
context 'when the connection is open' do
context 'with greater or equal than the requested data buffered' do
it 'returns the requested data and no more' do
tcp_test(SendAndKeepOpen) do
@socket.read_nonblock(2).size.should eq 2
@socket.read_nonblock(1).size.should eq 1
end
end
end
context 'with less than the requested data buffered' do
it 'returns the available data' do
tcp_test(SendAndKeepOpen) do
@socket.read_nonblock(10).size.should eq 4
end
end
end
end
context 'when the peer has closed the connection' do
context 'with no data buffered' do
it 'raises EOFError' do
tcp_test(SendAndClose) do
@socket.read_nonblock(4).size.should eq 4
lambda {
@socket.read_nonblock(1)
}.should raise_error(EOFError)
end
end
end
context 'with less than the requested data buffered' do
it 'returns the buffered data' do
tcp_test(SendAndClose) do
@socket.read_nonblock(50).size.should eq 4
end
end
end
context 'with greater or equal than the requested data buffered' do
it 'returns the requested data and no more' do
tcp_test(SendAndClose) do
@socket = EventMachine::Synchrony::TCPSocket.new 'localhost', 12345
@socket.read_nonblock(2).size.should eq 2
end
end
end
end
context 'when we closed the connection' do
it 'raises IOError' do
tcp_test(SendAndKeepOpen, :close => true) do
proc {
@socket.read_nonblock(4)
}.should raise_error(IOError)
end
end
end
end
context 'with a negative length argument' do
it 'raises ArgumentError' do
tcp_test(SendAndKeepOpen) do
proc {
@socket.read_nonblock(-10)
}.should raise_error(ArgumentError)
end
end
end
context 'with a zero length argument' do
it 'raises ArgumentError' do
tcp_test(SendAndKeepOpen) do
proc {
@socket.read_nonblock(0)
}.should raise_error(ArgumentError)
end
end
end
end

context '#recv' do
context 'with a length argument' do
context 'with a possitive length argument' do
Expand Down

0 comments on commit 3adb818

Please sign in to comment.