mirror of
https://github.com/HoneyryderChuck/httpx.git
synced 2025-08-10 00:01:27 -04:00
experimental optimizations in the connection call loop.
skip entering the read/write hot loops when the connection interests specifically say so. this led to a overhaul of how interests are calculated.
This commit is contained in:
parent
ad88aa27cf
commit
4bab923f25
@ -170,7 +170,7 @@ module HTTPX
|
||||
end
|
||||
|
||||
# if the write buffer is full, we drain it
|
||||
return :w if @write_buffer.full?
|
||||
return :w unless @write_buffer.empty?
|
||||
|
||||
return @parser.interests if @parser
|
||||
|
||||
@ -254,8 +254,14 @@ module HTTPX
|
||||
loop do
|
||||
parser.consume
|
||||
|
||||
# we exit if there's no more data to process
|
||||
if @pending.size.zero? && @inflight.zero?
|
||||
# we exit if there's no more requests to process
|
||||
#
|
||||
# this condition takes into account:
|
||||
#
|
||||
# * the number of inflight requests
|
||||
# * the number of pending requests
|
||||
# * whether the write buffer has bytes (i.e. for close handshake)
|
||||
if @pending.size.zero? && @inflight.zero? && @write_buffer.empty?
|
||||
log(level: 3) { "NO MORE REQUESTS..." }
|
||||
return
|
||||
end
|
||||
@ -265,7 +271,14 @@ module HTTPX
|
||||
read_drained = false
|
||||
write_drained = nil
|
||||
|
||||
# dread
|
||||
#
|
||||
# tight read loop.
|
||||
#
|
||||
# read as much of the socket as possible.
|
||||
#
|
||||
# this tight loop reads all the data it can from the socket and pipes it to
|
||||
# its parser.
|
||||
#
|
||||
loop do
|
||||
siz = @io.read(@window_size, @read_buffer)
|
||||
log(level: 3, color: :cyan) { "IO READ: #{siz} bytes..." }
|
||||
@ -276,6 +289,7 @@ module HTTPX
|
||||
return
|
||||
end
|
||||
|
||||
# socket has been drained. mark and exit the read loop.
|
||||
if siz.zero?
|
||||
read_drained = @read_buffer.empty?
|
||||
break
|
||||
@ -283,16 +297,27 @@ module HTTPX
|
||||
|
||||
parser << @read_buffer.to_s
|
||||
|
||||
# continue reading if possible.
|
||||
break if interests == :w
|
||||
|
||||
# exit the read loop if connection is preparing to be closed
|
||||
break if @state == :closing || @state == :closed
|
||||
|
||||
# for HTTP/2, we just want to write goaway frame
|
||||
end unless @state == :closing
|
||||
# exit #consume altogether if all outstanding requests have been dealt with
|
||||
return if @pending.size.zero? && @inflight.zero?
|
||||
end unless (interests == :w || @state == :closing) && !epiped
|
||||
|
||||
# dwrite
|
||||
#
|
||||
# tight write loop.
|
||||
#
|
||||
# flush as many bytes as the sockets allow.
|
||||
#
|
||||
loop do
|
||||
# buffer has been drainned, mark and exit the write loop.
|
||||
if @write_buffer.empty?
|
||||
# we only mark as drained on the first loop
|
||||
write_drained = write_drained.nil? && @inflight.positive?
|
||||
|
||||
break
|
||||
end
|
||||
|
||||
@ -305,21 +330,28 @@ module HTTPX
|
||||
return
|
||||
end
|
||||
|
||||
# socket closed for writing. mark and exit the write loop.
|
||||
if siz.zero?
|
||||
write_drained = !@write_buffer.empty?
|
||||
break
|
||||
end
|
||||
|
||||
break if @state == :closing || @state == :closed
|
||||
# exit write loop if marked to consume from peer, or is closing.
|
||||
break if interests == :r || @state == :closing || @state == :closed
|
||||
|
||||
write_drained = false
|
||||
end
|
||||
end unless interests == :r
|
||||
|
||||
# return if socket is drained
|
||||
if read_drained && write_drained
|
||||
log(level: 3) { "WAITING FOR EVENTS..." }
|
||||
return
|
||||
end
|
||||
next unless (interests != :r || read_drained) &&
|
||||
(interests != :w || write_drained)
|
||||
|
||||
# gotta go back to the event loop. It happens when:
|
||||
#
|
||||
# * the socket is drained of bytes or it's not the interest of the conn to read;
|
||||
# * theres nothing more to write, or it's not in the interest of the conn to write;
|
||||
log(level: 3) { "(#{interests}): WAITING FOR EVENTS..." }
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -42,7 +42,7 @@ module HTTPX
|
||||
return @buffer.empty? ? :r : :rw
|
||||
end
|
||||
|
||||
return :w unless @pending.empty?
|
||||
return :w if !@pending.empty? && can_buffer_more_requests?
|
||||
|
||||
return :w if @streams.each_key.any? { |r| r.interests == :w }
|
||||
|
||||
@ -70,10 +70,14 @@ module HTTPX
|
||||
@connection << data
|
||||
end
|
||||
|
||||
def can_buffer_more_requests?
|
||||
@handshake_completed &&
|
||||
@streams.size < @max_concurrent_requests &&
|
||||
@streams.size < @max_requests
|
||||
end
|
||||
|
||||
def send(request)
|
||||
if !@handshake_completed ||
|
||||
@streams.size >= @max_concurrent_requests ||
|
||||
@streams.size >= @max_requests
|
||||
unless can_buffer_more_requests?
|
||||
@pending << request
|
||||
return
|
||||
end
|
||||
|
@ -16,6 +16,14 @@ module HTTPX
|
||||
Error = Socks4Error
|
||||
|
||||
module ConnectionMethods
|
||||
def interests
|
||||
if @state == :connecting
|
||||
return @write_buffer.empty? ? :r : :w
|
||||
end
|
||||
|
||||
super
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def transition(nextstate)
|
||||
|
@ -35,6 +35,14 @@ module HTTPX
|
||||
super || @state == :authenticating || @state == :negotiating
|
||||
end
|
||||
|
||||
def interests
|
||||
if @state == :connecting || @state == :authenticating || @state == :negotiating
|
||||
return @write_buffer.empty? ? :r : :w
|
||||
end
|
||||
|
||||
super
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def transition(nextstate)
|
||||
|
@ -23,6 +23,8 @@ module HTTPX
|
||||
|
||||
def <<: (String) -> void
|
||||
|
||||
def can_buffer_more_requests: () -> bool
|
||||
|
||||
def send: (Request) -> void
|
||||
|
||||
def consume: () -> void
|
||||
|
Loading…
x
Reference in New Issue
Block a user