class Protocol::HTTP::Headers

Headers are an array of key-value pairs. Some header keys represent multiple values.

def self.[] headers

Returns:
  • (Headers) - an instance of headers.
def self.[] headers
	if headers.nil?
		return self.new
	end
	
	if headers.is_a?(self)
		if headers.frozen?
			return headers.dup
		else
			return headers
		end
	end
	
	fields = headers.to_a
	
	if fields.frozen?
		fields = fields.dup
	end
	
	return self.new(fields)
end

def == other

@returns [Boolean] Whether the other object is equal to this one.

Compare this object to another object. May depend on the order of the fields.
def == other
	case other
	when Hash
		to_h == other
	when Headers
		@fields == other.fields
	else
		@fields == other
	end
end

def [] key

@returns [String | Array | Object] The header value.
@parameter key [String] The header key.

Get the value of the specified header key.
def [] key
	to_h[key]
end

def []= key, value

@parameter value [String] The header value.
@parameter key [String] The header key.

Append the value to the given key. Some values can be appended multiple times, others can only be set once.
def []= key, value
	if @indexed
		merge_into(@indexed, key.downcase, value)
	end
	
	@fields << [key, value]
end

def add(key, value)

@parameter value [String] the header value to assign.
@parameter key [String] the header key.

Add the specified header key value pair.
def add(key, value)
	self[key] = value
end

def clear

Clear all headers.
def clear
	@fields.clear
	@indexed = nil
	@tail = nil
end

def delete(key)

@returns [String | Array | Object] The merged header value.
@parameter key [String] The header key.

Delete all header values for the given key, and return the merged value.
def delete(key)
	deleted, @fields = @fields.partition do |field|
		field.first.downcase == key
	end
	
	if deleted.empty?
		return nil
	end
	
	if @indexed
		return @indexed.delete(key)
	elsif policy = POLICY[key]
		(key, value), *tail = deleted
		merged = policy.new(value)
		
		tail.each{|k,v| merged << v}
		
		return merged
	else
		key, value = deleted.last
		return value
	end
end

def each(&block)

@parameter value [String] The header value.
@parameter key [String] The header key.
@yields {|key, value| ...}

Enumerate all header keys and values.
def each(&block)
	@fields.each(&block)
end

def empty?

@returns [Boolean] Whether the headers are empty.
def empty?
	@fields.empty?
end

def extract(keys)

@parameter keys [Array] The keys to extract.

Extract the specified keys from the headers.
def extract(keys)
	deleted, @fields = @fields.partition do |field|
		keys.include?(field.first.downcase)
	end
	
	if @indexed
		keys.each do |key|
			@indexed.delete(key)
		end
	end
	
	return deleted
end

def flatten

Flatten trailer into the headers, returning a new instance of {Headers}.
def flatten
	self.dup.flatten!
end

def flatten!

Flatten trailer into the headers, in-place.
def flatten!
	if @tail
		self.delete(TRAILER)
		@tail = nil
	end
	
	return self
end

def freeze

Freeze the headers, and ensure the indexed hash is generated.
def freeze
	return if frozen?
	
	# Ensure @indexed is generated:
	self.to_h
	
	@fields.freeze
	@indexed.freeze
	
	super
end

def include? key

@returns [Boolean] Whether the headers include the specified key.
def include? key
	self[key] != nil
end

def initialize(fields = [], indexed = nil)

@parameter indexed [Hash] A hash table of normalized headers, if available.
@parameter fields [Array] An array of `[key, value]` pairs.

Initialize the headers with the specified fields.
def initialize(fields = [], indexed = nil)
	@fields = fields
	@indexed = indexed
	
	# Marks where trailer start in the @fields array.
	@tail = nil
end

def initialize_dup(other)

@parameter other [Headers] The headers to copy.

Initialize a copy of the headers.
def initialize_dup(other)
	super
	
	@fields = @fields.dup
	@indexed = @indexed.dup
end

def inspect

@returns [String] A string representation of the headers.

Inspect the headers.
def inspect
	"#<#{self.class} #{@fields.inspect}>"
end

def keys

@returns [Array] All the keys of the headers.
def keys
	self.to_h.keys
end

def merge(headers)

Merge the headers into a new instance of {Headers}.
def merge(headers)
	self.dup.merge!(headers)
end

def merge!(headers)

Merge the headers into this instance.
def merge!(headers)
	headers.each do |key, value|
		self[key] = value
	end
	
	return self
end

def merge_into(hash, key, value)

@parameter value [String] The raw header value.
@parameter key [String] The header key.
@parameter hash [Hash] The hash to merge into.

Merge the value into the hash according to the policy for the given key.
def merge_into(hash, key, value)
 = POLICY[key]
nt_value = hash[key]
_value << value
y] = policy.new(value)
't merge these, we only expose the last one set.
] = value

def set(key, value)

@parameter value [String] the header value to assign.
@parameter key [String] the header key to replace.

Set the specified header key to the specified value, replacing any existing header keys with the same name.
def set(key, value)
	# TODO This could be a bit more efficient:
	self.delete(key)
	self.add(key, value)
end

def to_h

@returns [Hash] A hash table of `{key, value}` pairs.

Compute a hash table of headers, where the keys are normalized to lower case and the values are normalized according to the policy for that header.
def to_h
	@indexed ||= @fields.inject({}) do |hash, (key, value)|
		merge_into(hash, key.downcase, value)
		
		hash
	end
end

def trailer(&block)

Enumerate all headers in the trailer, if there are any.
def trailer(&block)
	return to_enum(:trailer) unless block_given?
	
	if @tail
		@fields.drop(@tail).each(&block)
	end
end

def trailer!(&block)

@returns An enumerator which is suitable for iterating over trailers.
@yields {|name, value| ...} the trailing headers if a block is given.
@parameter names [Array] The trailer header names which will be added later.

A sender that intends to generate one or more trailer fields in a message should generate a trailer header field in the header section of that message to indicate which fields might be present in the trailers.

This method is typically used after headers are sent to capture any additional headers which should then be sent as trailers.

Record the current headers, and prepare to add trailers.
def trailer!(&block)
	@tail ||= @fields.size
	
	return trailer(&block)
end

def trailer?

@returns [Boolean] Whether there are any trailers.
def trailer?
	@tail != nil
end