diff --git a/lib/net/protocol.rb b/lib/net/protocol.rb index 197ea09..3933bfe 100644 --- a/lib/net/protocol.rb +++ b/lib/net/protocol.rb @@ -195,7 +195,10 @@ def readuntil(terminator, ignore_eof = false) offset = @rbuf_offset begin until idx = @rbuf.index(terminator, offset) - offset = @rbuf.bytesize + new_offset = @rbuf.bytesize - terminator.bytesize + 1 + # Only assign the offset if it will advance. + # Otherwise an empty @rbuf could result in a negative offset. + offset = new_offset if new_offset > offset rbuf_fill end return rbuf_consume(idx + terminator.bytesize - @rbuf_offset) diff --git a/test/net/protocol/test_protocol.rb b/test/net/protocol/test_protocol.rb index 2f42fa3..bc88bf7 100644 --- a/test/net/protocol/test_protocol.rb +++ b/test/net/protocol/test_protocol.rb @@ -27,6 +27,26 @@ def test_each_crlf_line end end + def test_each_message_chunk_boundary + assert_output("", "") do + # Create this first chunk where the chunk will end with the + # line terminator \r\n only being partially read up to and including \r. + chunk_1 = "#{"1" * (Net::BufferedIO::BUFSIZE - 1)}\r\n".b + chunk_2 = "Second line\r\n".b + chunk_3 = "Third line\r\n".b + test_string = "#{chunk_1}#{chunk_2}#{chunk_3}".b + sio = StringIO.new("#{test_string}.\r\n".b) + io = Net::InternetMessageIO.new(sio) + expected_chunks = [] + io.each_message_chunk do |chunk| + expected_chunks << chunk + end + assert_equal chunk_1, expected_chunks[0] + assert_equal chunk_2, expected_chunks[1] + assert_equal chunk_3, expected_chunks[2] + end + end + def create_mockio(capacity: 100, max: nil) mockio = Object.new mockio.instance_variable_set(:@str, +'')