Skip to content

Commit 124dfe7

Browse files
committed
Improved compatibility of gets implementation.
1 parent 156c761 commit 124dfe7

File tree

2 files changed

+71
-2
lines changed

2 files changed

+71
-2
lines changed

lib/protocol/http/body/stream.rb

+56-2
Original file line numberDiff line numberDiff line change
@@ -191,9 +191,63 @@ def read_until(pattern, offset = 0, chomp: false)
191191
# Read a single line from the stream.
192192
#
193193
# @parameter separator [String] The line separator, defaults to `\n`.
194+
# @parameter limit [Integer] The maximum number of bytes to read.
194195
# @parameter *options [Hash] Additional options, passed to {read_until}.
195-
def gets(separator = NEWLINE, **options)
196-
read_until(separator, **options)
196+
def gets(separator = NEWLINE, limit = nil, chomp: false)
197+
# If the separator is an integer, it is actually the limit:
198+
if separator.is_a?(Integer)
199+
limit = separator
200+
separator = NEWLINE
201+
end
202+
203+
# If no separator is given, this is the same as a read operation:
204+
if separator.nil?
205+
return read(limit)
206+
end
207+
208+
# We don't want to split on the separator, so we subtract the size of the separator:
209+
split_offset = separator.bytesize - 1
210+
211+
@buffer ||= read_next
212+
return nil if @buffer.nil?
213+
214+
offset = 0
215+
until index = @buffer.index(separator, offset)
216+
offset = @buffer.bytesize - split_offset
217+
offset = 0 if offset < 0
218+
219+
# If we have gone past the limit, we are done:
220+
if limit and offset >= limit
221+
@buffer.freeze
222+
matched = @buffer.byteslice(0, limit)
223+
@buffer = @buffer.byteslice(limit, @buffer.bytesize)
224+
return matched
225+
end
226+
227+
# Read more data:
228+
if chunk = read_next
229+
@buffer << chunk
230+
else
231+
# No more data could be read, return the remaining data:
232+
buffer = @buffer
233+
@buffer = nil
234+
235+
return @buffer
236+
end
237+
end
238+
239+
# Freeze the buffer, as this enables us to use byteslice without generating a hidden copy:
240+
@buffer.freeze
241+
242+
if limit and index > limit
243+
line = @buffer.byteslice(0, limit)
244+
@buffer = @buffer.byteslice(limit, @buffer.bytesize)
245+
else
246+
line = @buffer.byteslice(0, index+(chomp ? 0 : separator.bytesize))
247+
@buffer = @buffer.byteslice(index+separator.bytesize, @buffer.bytesize)
248+
end
249+
250+
return line
197251
end
198252
end
199253

test/protocol/http/body/stream.rb

+15
Original file line numberDiff line numberDiff line change
@@ -175,11 +175,26 @@
175175
expect(stream.gets).to be == nil
176176
end
177177

178+
it "can read lines with limit" do
179+
expect(stream.gets(2)).to be == "He"
180+
expect(stream.gets(6)).to be == "llo\n"
181+
expect(stream.gets(2)).to be == "Wo"
182+
expect(stream.gets(6)).to be == "rld\n"
183+
expect(stream.gets(2)).to be == nil
184+
end
185+
178186
it "can read lines and chomp separators" do
179187
expect(stream.gets(chomp: true)).to be == "Hello"
180188
expect(stream.gets(chomp: true)).to be == "World"
181189
expect(stream.gets(chomp: true)).to be == nil
182190
end
191+
192+
it "can read without separator" do
193+
expect(stream.gets(nil, 4)).to be == "Hell"
194+
expect(stream.gets(nil, 4)).to be == "o\nWo"
195+
expect(stream.gets(nil, 4)).to be == "rld\n"
196+
expect(stream.gets(nil, 4)).to be == nil
197+
end
183198
end
184199

185200
with "#close_read" do

0 commit comments

Comments
 (0)