class Async::HTTP::Client

def self.open(*args, &block)

def self.open(*args, &block)
	client = self.new(*args)
	
	return client unless block_given?
	
	begin
		yield client
	ensure
		client.close
	end
end

def call(request)

def call(request)
	request.scheme ||= self.scheme
	request.authority ||= self.authority
	
	attempt = 0
	
	# We may retry the request if it is possible to do so. https://tools.ietf.org/html/draft-nottingham-httpbis-retry-01 is a good guide for how retrying requests should work.
	begin
		attempt += 1
		
		# As we cache pool, it's possible these pool go bad (e.g. closed by remote host). In this case, we need to try again. It's up to the caller to impose a timeout on this. If this is the last attempt, we force a new connection.
		connection = @pool.acquire
		
		response = request.call(connection)
		
		# The connection won't be released until the body is completely read/released.
		::Protocol::HTTP::Body::Streamable.wrap(response) do
			@pool.release(connection)
		end
		
		return response
	rescue Protocol::RequestFailed
		# This is a specific case where the entire request wasn't sent before a failure occurred. So, we can even resend non-idempotent requests.
		@pool.release(connection) if connection
		
		if attempt < @retries
			retry
		else
			raise
		end
	rescue
		@pool.release(connection) if connection
		
		if request.idempotent? and attempt < @retries
			retry
		else
			raise
		end
	end
end

def close

def close
	@pool.close
end

def initialize(endpoint, protocol = endpoint.protocol, scheme = endpoint.scheme, authority = endpoint.authority, retries: 3, connection_limit: nil)

The client object will never become unusable. It internally manages persistent connections (or non-persistent connections if that's required).
* If a request fails, it will retry it up to N times if it was idempotent.
* If there are already connections, it will reuse it.
* If there are no connections, it will create one.
Provides a robust interface to a server.
def initialize(endpoint, protocol = endpoint.protocol, scheme = endpoint.scheme, authority = endpoint.authority, retries: 3, connection_limit: nil)
	@endpoint = endpoint
	@protocol = protocol
	
	@retries = retries
	@pool = make_pool(connection_limit)
	
	@scheme = scheme
	@authority = authority
end

def make_pool(connection_limit = nil)

def make_pool(connection_limit = nil)
	Pool.new(connection_limit) do
		Async.logger.debug(self) {"Making connection to #{@endpoint.inspect}"}
		
		@protocol.client(IO::Stream.new(@endpoint.connect))
	end
end

def secure?

def secure?
	@endpoint.secure?
end