Skip to content

Commit a0ccff5

Browse files
committed
♻️ Read responses in 4KiB chunks (hard-coded)
1 parent 909fe8a commit a0ccff5

File tree

1 file changed

+22
-5
lines changed

1 file changed

+22
-5
lines changed

lib/net/imap/response_reader.rb

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ def initialize(client, sock)
1212

1313
def read_response_buffer
1414
@buff = String.new
15+
@part_offset = 0
1516
catch :eof do
1617
while true
1718
read_line
@@ -29,21 +30,32 @@ def read_response_buffer
2930
attr_reader :buff, :literal_size
3031

3132
def bytes_read = buff.bytesize
33+
def part_size = bytes_read - @part_offset
3234
def empty? = buff.empty?
3335
def done? = line_done? && !get_literal_size
34-
def line_done? = buff.end_with?(CRLF)
36+
def line_done? = line_query? && @buff.end_with?("\r\n")
37+
def line_expecting_LF? = line_query? && @buff.end_with?("\r")
38+
def line_query? = literal_size ? part_size.zero? : part_size >= 2
3539
def get_literal_size = /\{(\d+)\}\r\n\z/n =~ buff && $1.to_i
40+
def literal_remaining = literal_size &.- part_size
41+
def literal_remaining? = literal_size &.> part_size
3642

3743
def read_line
38-
buff << gets
39-
max_response_remaining! unless line_done?
44+
@part_offset = bytes_read
45+
until line_done?
46+
buff << gets
47+
# peek one byte beyond CR, to look for CRLF
48+
buff << read(1) while line_expecting_LF?
49+
end
4050
end
4151

4252
def read_literal
53+
@part_offset = bytes_read
4354
# check before allocating memory for literal
4455
max_response_remaining!
4556
literal = String.new(capacity: literal_size)
4657
buff << read(literal_size, literal)
58+
buff << read(literal_remaining) while literal_remaining?
4759
ensure
4860
@literal_size = nil
4961
end
@@ -57,7 +69,7 @@ def read(limit = nil, into = nil)
5769
end
5870

5971
def read_limit(limit = nil)
60-
[limit, max_response_remaining!].compact.min
72+
[limit, socket_read_limit!, max_response_remaining!].compact.min
6173
end
6274

6375
def max_response_size = client.max_response_size
@@ -66,7 +78,7 @@ def response_too_large? = max_response_size &.< min_response_size
6678
def min_response_size = bytes_read + min_response_remaining
6779

6880
def min_response_remaining
69-
empty? ? 3 : done? ? 0 : (literal_size || 0) + 2
81+
empty? ? 3 : done? ? 0 : line_expecting_LF? ? 1 : (literal_remaining || 0) + 2
7082
end
7183

7284
def max_response_remaining!
@@ -76,6 +88,11 @@ def max_response_remaining!
7688
)
7789
end
7890

91+
# TODO: configurable socket_read_limit (currently hardcoded to 4KiB)
92+
def socket_read_limit!
93+
4096
94+
end
95+
7996
end
8097
end
8198
end

0 commit comments

Comments
 (0)