Request retry mechanism is working inconstently based on wheither the request body is nil or empty array.
Consider following spec:
require_relative 'server_context'
require 'async/http/client'
require 'async/http/endpoint'
RSpec.describe 'inconsistent retry behaviour' do
include_context Async::HTTP::Server
let(:delay) { 0.1 }
let(:retries) { 2 }
let(:protocol) { Async::HTTP::Protocol::HTTP1 }
let(:server) do
Async::HTTP::Server.for(endpoint, protocol) do |request|
Async::Task.current.sleep(delay)
Protocol::HTTP::Response[200, {}, []]
end
end
def make_request(body)
Async::Task.current.with_timeout(delay / 2) do
client.get('/', {}, body)
end
end
specify 'nil body' do
make_request(nil)
end
specify 'empty array body' do
make_request([])
end
end
The nil body spec will pass, as the request will be retried after timeout, while the empty array body spec will fail as there will be no retries (actual requests being made will be completely identical and empty in both cases).
Looks like this is happening because the request is considered to be non-idempotent in the empty array body case, here is the chain of events:
Protocol::HTTP1::Connection#write_empty_body sends the request and closes the body:
Request retry mechanism is working inconstently based on wheither the request body is nil or empty array.
Consider following spec:
The
nil body
spec will pass, as the request will be retried after timeout, while theempty array body
spec will fail as there will be no retries (actual requests being made will be completely identical and empty in both cases).Looks like this is happening because the request is considered to be non-idempotent in the
empty array body
case, here is the chain of events:Protocol::HTTP1::Connection#write_empty_body
sends the request and closes the body:Protocol::HTTP::Body::Buffered#close
appends nil to body request chunks:Once the exception is raised, rescue handler tries to determine if it's safe to retry it, and checks idempotency of the request:
@body.empty?
returns false, as there is a single chunk after step 2, the request is considered to be non-idempotent and is not retried