class Async::HTTP::Reference

A relative reference, excluding any authority.

def self.[] reference

def self.[] reference
	if reference.is_a? self
		return reference
	else
		return self.parse(reference)
	end
end

def self.parse(path = '/', parameters = nil)

Generate a reference from a path and user parameters. The path may contain a `#fragment` or `?query=parameters`.
def self.parse(path = '/', parameters = nil)
	base, fragment = path.split('#', 2)
	path, query_string = base.split('?', 2)
	
	self.new(path, query_string, fragment, parameters)
end

def + other

def + other
	other = self.class[other]
	
	self.class.new(
		expand_path(self.path, other.path),
		other.query_string,
		other.fragment,
		other.parameters,
	)
end

def [] parameters

def [] parameters
	self.dup(nil, parameters)
end

def append(buffer)

def append(buffer)
	if @query_string
		buffer << escape_path(@path) << '?' << query_string
		buffer << '&' << encode(@parameters) if @parameters
	else
		buffer << escape_path(@path)
		buffer << '?' << encode(@parameters) if @parameters
	end
	
	if @fragment
		buffer << '#' << escape(@fragment)
	end
	
	return buffer
end

def dup(path = nil, parameters = nil)

def dup(path = nil, parameters = nil)
	if parameters and @parameters
		parameters = @parameters.merge(parameters)
	else
		parameters = @parameters
	end
	
	if path
		path = @path + '/' + path
	else
		path = @path
	end
	
	self.class.new(path, @query_string, @fragment, parameters)
end

def encode(value, prefix = nil)

Encodes a hash or array into a query string
def encode(value, prefix = nil)
	case value
	when Array
		value.map { |v|
			encode(v, "#{prefix}[]")
		}.join("&")
	when Hash
		value.map { |k, v|
			encode(v, prefix ? "#{prefix}[#{escape(k.to_s)}]" : escape(k.to_s))
		}.reject(&:empty?).join('&')
	when nil
		prefix
	else
		raise ArgumentError, "value must be a Hash" if prefix.nil?
		"#{prefix}=#{escape(value.to_s)}"
	end
end

def escape(string)

Escapes a generic string, using percent encoding.
def escape(string)
	encoding = string.encoding
	string.b.gsub(/([^a-zA-Z0-9_.\-]+)/) do |m|
		'%' + m.unpack('H2' * m.bytesize).join('%').upcase
	end.force_encoding(encoding)
end

def escape_path(path)

Escapes a path
def escape_path(path)
	encoding = path.encoding
	path.b.gsub(NON_PCHAR) do |m|
		'%' + m.unpack('H2' * m.bytesize).join('%').upcase
	end.force_encoding(encoding)
end

def expand_path(base, relative)

def expand_path(base, relative)
	if relative.start_with? '/'
		return relative
	else
		path = base.split('/')
		parts = relative.split('/')
		
		parts.each do |part|
			if part == '..'
				path.pop
			else
				path << part
			end
		end
		
		return path.join('/')
	end
end

def initialize(path, query_string, fragment, parameters)

def initialize(path, query_string, fragment, parameters)
	@path = path
	@query_string = query_string
	@fragment = fragment
	@parameters = parameters
end

def to_str

def to_str
	append(String.new)
end