class Async::HTTP::Protocol::HTTP11

Implements basic HTTP/1.1 request/response.

def initialize(stream)

def initialize(stream)
	super(stream, CRLF)
	
	@keep_alive = true
end

def keep_alive?(headers)

def keep_alive?(headers)
	headers[:connection] != CLOSE
end

def multiplex

Only one simultaneous connection at a time.
def multiplex
	1
end

def read_body(headers)

def read_body(headers)
	if headers[:transfer_encoding] == 'chunked'
		buffer = Async::IO::BinaryString.new
		
		while true
			size = read_line.to_i(16)
			
			if size == 0
				read_line
				break
			end
			
			buffer << @stream.read(size)
			
			read_line # Consume the trailing CRLF
		end
		
		return buffer
	elsif content_length = headers[:content_length]
		return @stream.read(Integer(content_length))
	end
end

def read_headers(headers = Headers.new)

def read_headers(headers = Headers.new)
	# Parsing headers:
	each_line do |line|
		if line =~ /^([a-zA-Z\-]+):\s*(.+?)\s*$/
			headers[$1] = $2
		else
			break
		end
	end
	
	return headers
end

def read_request

def read_request
	method, path, version = read_line.split(/\s+/, 3)
	headers = read_headers
	body = read_body(headers)
	
	return headers.delete(:host), method, path, version, headers, body
end

def read_response

def read_response
	version, status, reason = read_line.split(/\s+/, 3)
	headers = read_headers
	body = read_body(headers)
	
	@keep_alive = keep_alive?(headers)
	
	return version, Integer(status), reason, headers, body
end

def receive_requests(task: Task.current)

Server loop.
def receive_requests(task: Task.current)
	while true
		request = Request.new(*read_request)
		
		status, headers, body = yield request
		
		write_response(request.version, status, headers, body)
		
		unless keep_alive?(request.headers) and keep_alive?(headers)
			@keep_alive = false
			
			break
		end
		
		# This ensures we yield at least once every iteration of the loop and allow other fibers to execute.
		task.yield
	end
end

def reusable?

def reusable?
	@keep_alive
end

def send_request(authority, method, path, headers = {}, body = [])

Client request.
def send_request(authority, method, path, headers = {}, body = [])
	Async.logger.debug(self) {"#{method} #{path} #{headers.inspect}"}
	
	write_request(authority, method, path, version, headers, body)
	
	return Response.new(*read_response)
rescue EOFError
	return nil
end

def version

def version
	VERSION
end

def write_body(body, chunked = true)

def write_body(body, chunked = true)
	if chunked
		@stream.write("Transfer-Encoding: chunked\r\n\r\n")
		
		body.each do |chunk|
			next if chunk.size == 0
			
			@stream.write("#{chunk.bytesize.to_s(16).upcase}\r\n")
			@stream.write(chunk)
			@stream.write(CRLF)
		end
		
		@stream.write("0\r\n\r\n")
	else
		buffer = String.new
		body.each{|chunk| buffer << chunk}
		
		@stream.write("Content-Length: #{chunk.bytesize}\r\n\r\n")
		@stream.write(chunk)
	end
end

def write_headers(headers)

def write_headers(headers)
	headers.each do |name, value|
		@stream.write("#{name}: #{value}\r\n")
	end
end

def write_request(authority, method, path, version, headers, body)

def write_request(authority, method, path, version, headers, body)
	@stream.write("#{method} #{path} #{version}\r\n")
	@stream.write("Host: #{authority}\r\n")
	
	write_headers(headers)
	write_body(body)
	
	@stream.flush
	
	return true
end

def write_response(version, status, headers, body)

def write_response(version, status, headers, body)
	@stream.write("#{version} #{status}\r\n")
	write_headers(headers)
	write_body(body)
	
	@stream.flush
	
	return true
end