class Headers
Headers are an array of key-value pairs. Some header keys represent multiple values.
Nested
Definitions
def self.[] headers
Construct an instance from a headers Array or Hash. No-op if already an instance of Headers
. If the underlying array is frozen, it will be duped.
Implementation
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 [] key
Get the value of the specified header key.
Signature
-
parameter
key
String
The header key.
-
returns
String | Array | Object
The header value.
Implementation
def [] key
to_h[key]
end
def initialize(fields = [], indexed = nil)
Initialize the headers with the specified fields.
Signature
-
parameter
fields
Array
An array of
[key, value]
pairs.-
parameter
indexed
Hash
A hash table of normalized headers, if available.
Implementation
def initialize(fields = [], indexed = nil)
@fields = fields
@indexed = indexed
# Marks where trailer start in the @fields array.
@tail = nil
end
def initialize_dup(other)
Initialize a copy of the headers.
Signature
-
parameter
other
Headers
The headers to copy.
Implementation
def initialize_dup(other)
super
@fields = @fields.dup
@indexed = @indexed.dup
end
def clear
Clear all headers.
Implementation
def clear
@fields.clear
@indexed = nil
@tail = nil
end
def flatten!
Flatten trailer into the headers, in-place.
Implementation
def flatten!
if @tail
self.delete(TRAILER)
@tail = nil
end
return self
end
def flatten
Flatten trailer into the headers, returning a new instance of class Protocol::HTTP::Headers
.
Implementation
def flatten
self.dup.flatten!
end
attr :fields
Signature
-
attribute
Array
An array of
[key, value]
pairs.
def trailer?
Signature
-
returns
Boolean
Whether there are any trailers.
Implementation
def trailer?
@tail != nil
end
def trailer!(&block)
Record the current headers, and prepare to add trailers.
This method is typically used after headers are sent to capture any additional headers which should then be sent as trailers.
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.
Signature
-
parameter
names
Array
The trailer header names which will be added later.
-
yields
{|name, value| ...}
the trailing headers if a block is given.
Implementation
def trailer!(&block)
@tail ||= @fields.size
return trailer(&block)
end
def trailer(&block)
Enumerate all headers in the trailer, if there are any.
Implementation
def trailer(&block)
return to_enum(:trailer) unless block_given?
if @tail
@fields.drop(@tail).each(&block)
end
end
def freeze
Freeze the headers, and ensure the indexed hash is generated.
Implementation
def freeze
return if frozen?
# Ensure @indexed is generated:
self.to_h
@fields.freeze
@indexed.freeze
super
end
def empty?
Signature
-
returns
Boolean
Whether the headers are empty.
Implementation
def empty?
@fields.empty?
end
def each(&block)
Enumerate all header keys and values.
Signature
-
yields
{|key, value| ...}
-
parameter
key
String
The header key.
-
parameter
value
String
The header value.
-
parameter
Implementation
def each(&block)
@fields.each(&block)
end
def include? key
Signature
-
returns
Boolean
Whether the headers include the specified key.
Implementation
def include? key
self[key] != nil
end
def keys
Signature
-
returns
Array
All the keys of the headers.
Implementation
def keys
self.to_h.keys
end
def extract(keys)
Extract the specified keys from the headers.
Signature
-
parameter
keys
Array
The keys to extract.
Implementation
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 add(key, value)
Add the specified header key value pair.
Signature
-
parameter
key
String
the header key.
-
parameter
value
String
the header value to assign.
Implementation
def add(key, value)
self[key] = value
end
def set(key, value)
Set the specified header key to the specified value, replacing any existing header keys with the same name.
Signature
-
parameter
key
String
the header key to replace.
-
parameter
value
String
the header value to assign.
Implementation
def set(key, value)
# TODO This could be a bit more efficient:
self.delete(key)
self.add(key, value)
end
def merge!(headers)
Merge the headers into this instance.
Implementation
def merge!(headers)
headers.each do |key, value|
self[key] = value
end
return self
end
def merge(headers)
Merge the headers into a new instance of class Protocol::HTTP::Headers
.
Implementation
def merge(headers)
self.dup.merge!(headers)
end
def []= key, value
Append the value to the given key. Some values can be appended multiple times, others can only be set once.
Signature
-
parameter
key
String
The header key.
-
parameter
value
String
The header value.
Implementation
def []= key, value
if @indexed
merge_into(@indexed, key.downcase, value)
end
@fields << [key, value]
end
POLICY
The policy for various headers, including how they are merged and normalized.
Implementation
POLICY = {
# Headers which may only be specified once:
"content-type" => false,
"content-disposition" => false,
"content-length" => false,
"user-agent" => false,
"referer" => false,
"host" => false,
"from" => false,
"location" => false,
"max-forwards" => false,
"retry-after" => false,
# Custom headers:
"connection" => Header::Connection,
"cache-control" => Header::CacheControl,
"vary" => Header::Vary,
"priority" => Header::Priority,
# Headers specifically for proxies:
"via" => Split,
"x-forwarded-for" => Split,
# Authorization headers:
"authorization" => Header::Authorization,
"proxy-authorization" => Header::Authorization,
# Cache validations:
"etag" => Header::ETag,
"if-match" => Header::ETags,
"if-none-match" => Header::ETags,
# Headers which may be specified multiple times, but which can't be concatenated:
"www-authenticate" => Multiple,
"proxy-authenticate" => Multiple,
# Custom headers:
"set-cookie" => Header::SetCookie,
"cookie" => Header::Cookie,
# Date headers:
# These headers include a comma as part of the formatting so they can't be concatenated.
"date" => Header::Date,
"expires" => Header::Date,
"last-modified" => Header::Date,
"if-modified-since" => Header::Date,
"if-unmodified-since" => Header::Date,
}.tap{|hash| hash.default = Split}
def delete(key)
Delete all header values for the given key, and return the merged value.
Signature
-
parameter
key
String
The header key.
-
returns
String | Array | Object
The merged header value.
Implementation
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 to_h
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.
Signature
-
returns
Hash
A hash table of
{key, value}
pairs.
Implementation
def to_h
@indexed ||= @fields.inject({}) do |hash, (key, value)|
merge_into(hash, key.downcase, value)
hash
end
end
def inspect
Inspect the headers.
Signature
-
returns
String
A string representation of the headers.
Implementation
def inspect
"#<#{self.class} #{@fields.inspect}>"
end
def == other
Compare this object to another object. May depend on the order of the fields.
Signature
-
returns
Boolean
Whether the other object is equal to this one.
Implementation
def == other
case other
when Hash
to_h == other
when Headers
@fields == other.fields
else
@fields == other
end
end