Sus::Fixtures::Protocol::HTTPSourceSusFixturesProtocolHTTPClient

class Client

An in-process client for exercising protocol HTTP middleware.

Definitions

def initialize(delegate, scheme: "http", authority: "localhost")

Initialize the client.

Signature

parameter delegate ::Protocol::HTTP::Middleware

The middleware to exercise.

parameter scheme String

The default request scheme.

parameter authority String

The default request authority.

Implementation

def initialize(delegate, scheme: "http", authority: "localhost")
	super(delegate)
	
	@scheme = scheme
	@authority = authority
	
	@headers = {}
	@cookies = {}
	@closed = false
	@exchange_closed = true
end

attr :scheme

Signature

attribute String

The default request scheme.

attr :authority

Signature

attribute String

The default request authority.

attr :headers

Signature

attribute Hash(String, String)

Headers added to every request.

attr :cookies

Signature

attribute Hash(String, String)

Cookies retained between requests.

attr :last_request

Signature

attribute ::Protocol::HTTP::Request | Nil

The most recent request.

attr :last_response

Signature

attribute ::Protocol::HTTP::Response | Nil

The most recent response.

def request(method, path, headers = nil, body = nil, **options)

Construct and perform a request.

Signature

parameter method String

The request method.

parameter path String

The request path.

parameter headers Hash | ::Protocol::HTTP::Headers | Nil

The request headers.

parameter body String | Array(String) | ::Protocol::HTTP::Body::Readable | Nil

The request body.

parameter options Hash

Additional options for ::Protocol::HTTP::Request.[].

returns ::Protocol::HTTP::Response

The application response.

Implementation

def request(method, path, headers = nil, body = nil, **options)
	return self.call(
		::Protocol::HTTP::Request[method, path, headers, body, **options]
	)
end

def call(request)

Perform a prepared request against the application.

Signature

parameter request ::Protocol::HTTP::Request

The prepared request.

returns ::Protocol::HTTP::Response

The application response.

Implementation

def call(request)
	if @closed
		raise IOError, "Client is closed!"
	end
	
	self.close_exchange
	self.prepare_request(request)
	
	@last_request = request
	@last_response = nil
	@exchange_closed = false
	
	begin
		@last_response = super(request)
		self.store_cookies(@last_response.headers["set-cookie"])
		
		return @last_response
	rescue => error
		self.close_exchange(error)
		raise
	end
end

def follow_redirect!

Follow the location in the most recent redirect response.

Statuses 307 and 308 preserve the original method and body. Other redirects use GET, except that HEAD remains HEAD.

Signature

returns ::Protocol::HTTP::Response

The redirected response.

Implementation

def follow_redirect!
	response = @last_response
	request = @last_request
	
	unless response&.redirection?
		raise RuntimeError, "The last response is not a redirect!"
	end
	
	location = response.headers["location"]
	unless location
		raise RuntimeError, "The redirect response has no location!"
	end
	
	base = "#{request.scheme}://#{request.authority}#{request.path}"
	target = ::URI.join(base, location.to_s)
	
	if response.preserve_method?
		if @request_had_body && !@replay_body
			raise IOError, "The request body cannot be replayed!"
		end
		
		method = request.method
		body = @replay_body
	elsif request.head?
		method = ::Protocol::HTTP::Methods::HEAD
		body = nil
	else
		method = ::Protocol::HTTP::Methods::GET
		body = nil
	end
	
	return self.request(
		method,
		target.request_uri,
		nil,
		body,
		scheme: target.scheme,
		authority: target.authority,
	)
end

def close(error = nil)

Close the current exchange and application.

Signature

parameter error Exception | Nil

The error which caused the client to close.

Implementation

def close(error = nil)
	return if @closed
	
	@closed = true
	
	begin
		self.close_exchange(error)
	ensure
		super()
	end
end