Skip to content

Commit ddaa8a4

Browse files
committed
Reduce memory usage when reading response body
Currently for each chunk read from the socket a new string is being allocated. This is not necessary, because we're feeding that string object into the HTTP parser and never using it again. So we initialize a buffer string and pass it in when reading chunks. This will make each chunk be read into the same buffer object, no new string will be allocated in that operation. In my benchmark this halves the allocated memory when downloading large response bodies.
1 parent 160967e commit ddaa8a4

4 files changed

Lines changed: 14 additions & 13 deletions

File tree

lib/http/connection.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ def initialize(req, options)
3535
@pending_request = false
3636
@pending_response = false
3737
@failed_proxy_connect = false
38+
@buffer = "".b
3839

3940
@parser = Response::Parser.new
4041

@@ -210,7 +211,7 @@ def set_keep_alive
210211
def read_more(size)
211212
return if @parser.finished?
212213

213-
value = @socket.readpartial(size)
214+
value = @socket.readpartial(size, @buffer)
214215
if value == :eof
215216
@parser << ""
216217
:eof

lib/http/timeout/global.rb

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,8 @@ def connect_ssl
4646
end
4747

4848
# Read from the socket
49-
def readpartial(size)
50-
perform_io { read_nonblock(size) }
49+
def readpartial(size, buffer = nil)
50+
perform_io { read_nonblock(size, buffer) }
5151
end
5252

5353
# Write to the socket
@@ -60,16 +60,16 @@ def write(data)
6060
private
6161

6262
if RUBY_VERSION < "2.1.0"
63-
def read_nonblock(size)
64-
@socket.read_nonblock(size)
63+
def read_nonblock(size, buffer = nil)
64+
@socket.read_nonblock(size, buffer)
6565
end
6666

6767
def write_nonblock(data)
6868
@socket.write_nonblock(data)
6969
end
7070
else
71-
def read_nonblock(size)
72-
@socket.read_nonblock(size, :exception => false)
71+
def read_nonblock(size, buffer = nil)
72+
@socket.read_nonblock(size, buffer, :exception => false)
7373
end
7474

7575
def write_nonblock(data)

lib/http/timeout/null.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,8 @@ def start_tls(host, ssl_socket_class, ssl_context)
4141
end
4242

4343
# Read from the socket
44-
def readpartial(size)
45-
@socket.readpartial(size)
44+
def readpartial(size, buffer = nil)
45+
@socket.readpartial(size, buffer)
4646
rescue EOFError
4747
:eof
4848
end

lib/http/timeout/per_operation.rb

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,9 @@ def connect_ssl
3737
# NIO with exceptions
3838
if RUBY_VERSION < "2.1.0"
3939
# Read data from the socket
40-
def readpartial(size)
40+
def readpartial(size, buffer = nil)
4141
rescue_readable do
42-
@socket.read_nonblock(size)
42+
@socket.read_nonblock(size, buffer)
4343
end
4444
rescue EOFError
4545
:eof
@@ -57,10 +57,10 @@ def write(data)
5757
# NIO without exceptions
5858
else
5959
# Read data from the socket
60-
def readpartial(size)
60+
def readpartial(size, buffer = nil)
6161
timeout = false
6262
loop do
63-
result = @socket.read_nonblock(size, :exception => false)
63+
result = @socket.read_nonblock(size, buffer, :exception => false)
6464

6565
return :eof if result.nil?
6666
return result if result != :wait_readable

0 commit comments

Comments
 (0)