Class: Protocol::HTTP::Headers

Inherits:
Object
  • Object
show all
Defined in:
lib/protocol/http/headers.rb

Overview

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

Defined Under Namespace

Classes: Merged

Constant Summary collapse

Split =
Header::Split
Multiple =
Header::Multiple
TRAILER =
'trailer'
POLICY =
{
  # Headers which may only be specified once.
  'content-type' => false,
  'content-disposition' => false,
  'content-length' => false,
  'user-agent' => false,
  'referer' => false,
  'host' => false,
  'if-modified-since' => false,
  'if-unmodified-since' => false,
  'from' => false,
  'location' => false,
  'max-forwards' => false,
  
  # Custom headers:
  'connection' => Header::Connection,
  'cache-control' => Header::CacheControl,
  'vary' => Header::Vary,
  
  # 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,
}.tap{|hash| hash.default = Split}

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(fields = [], indexed = nil) ⇒ Headers

Returns a new instance of Headers.



66
67
68
69
70
71
72
# File 'lib/protocol/http/headers.rb', line 66

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

Instance Attribute Details

#fieldsObject (readonly)

An array of ‘[key, value]` pairs.



102
103
104
# File 'lib/protocol/http/headers.rb', line 102

def fields
  @fields
end

Class Method Details

.[](headers) ⇒ 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.

Returns:

  • (Headers)

    an instance of headers.



44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
# File 'lib/protocol/http/headers.rb', line 44

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

Instance Method Details

#==(other) ⇒ Object



316
317
318
319
320
321
322
323
324
325
# File 'lib/protocol/http/headers.rb', line 316

def == other
  case other
  when Hash
    to_h == other
  when Headers
    @fields == other.fields
  else
    @fields == other
  end
end

#[](key) ⇒ Object



299
300
301
# File 'lib/protocol/http/headers.rb', line 299

def [] key
  to_h[key]
end

#[]=(key, value) ⇒ Object

Append the value to the given key. Some values can be appended multiple times, others can only be set once.

Parameters:

  • key (String)

    The header key.

  • value

    The header value.



212
213
214
215
216
217
218
# File 'lib/protocol/http/headers.rb', line 212

def []= key, value
  if @indexed
    merge_into(@indexed, key.downcase, value)
  end
  
  @fields << [key, value]
end

#add(key, value) ⇒ Object

Add the specified header key value pair.

Parameters:

  • key (String)

    the header key.

  • value (String)

    the header value to assign.



184
185
186
# File 'lib/protocol/http/headers.rb', line 184

def add(key, value)
  self[key] = value
end

#clearObject



81
82
83
84
85
# File 'lib/protocol/http/headers.rb', line 81

def clear
  @fields.clear
  @indexed = nil
  @tail = nil
end

#delete(key) ⇒ Object

Delete all headers with the given key, and return the merged value.



262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
# File 'lib/protocol/http/headers.rb', line 262

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

#each(&block) ⇒ Object



152
153
154
# File 'lib/protocol/http/headers.rb', line 152

def each(&block)
  @fields.each(&block)
end

#empty?Boolean

Returns:

  • (Boolean)


148
149
150
# File 'lib/protocol/http/headers.rb', line 148

def empty?
  @fields.empty?
end

#extract(keys) ⇒ Object



166
167
168
169
170
171
172
173
174
175
176
177
178
# File 'lib/protocol/http/headers.rb', line 166

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

#flattenObject



97
98
99
# File 'lib/protocol/http/headers.rb', line 97

def flatten
  self.dup.flatten!
end

#flatten!Object

Flatten trailer into the headers.



88
89
90
91
92
93
94
95
# File 'lib/protocol/http/headers.rb', line 88

def flatten!
  if @tail
    self.delete(TRAILER)
    @tail = nil
  end
  
  return self
end

#freezeObject



136
137
138
139
140
141
142
143
144
145
146
# File 'lib/protocol/http/headers.rb', line 136

def freeze
  return if frozen?
  
  # Ensure @indexed is generated:
  self.to_h
  
  @fields.freeze
  @indexed.freeze
  
  super
end

#include?(key) ⇒ Boolean Also known as: key?

Returns:

  • (Boolean)


156
157
158
# File 'lib/protocol/http/headers.rb', line 156

def include? key
  self[key] != nil
end

#initialize_dup(other) ⇒ Object



74
75
76
77
78
79
# File 'lib/protocol/http/headers.rb', line 74

def initialize_dup(other)
  super
  
  @fields = @fields.dup
  @indexed = @indexed.dup
end

#inspectObject



312
313
314
# File 'lib/protocol/http/headers.rb', line 312

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

#keysObject



162
163
164
# File 'lib/protocol/http/headers.rb', line 162

def keys
  self.to_h.keys
end

#merge(headers) ⇒ Object



205
206
207
# File 'lib/protocol/http/headers.rb', line 205

def merge(headers)
  self.dup.merge!(headers)
end

#merge!(headers) ⇒ Object



197
198
199
200
201
202
203
# File 'lib/protocol/http/headers.rb', line 197

def merge!(headers)
  headers.each do |key, value|
    self[key] = value
  end
  
  return self
end

#set(key, value) ⇒ Object

Set the specified header key to the specified value, replacing any existing header keys with the same name.

Parameters:

  • key (String)

    the header key to replace.

  • value (String)

    the header value to assign.



191
192
193
194
195
# File 'lib/protocol/http/headers.rb', line 191

def set(key, value)
  # TODO This could be a bit more efficient:
  self.delete(key)
  self.add(key, value)
end

#to_hObject

A hash table of ‘policy.map(values)`



304
305
306
307
308
309
310
# File 'lib/protocol/http/headers.rb', line 304

def to_h
  @indexed ||= @fields.inject({}) do |hash, (key, value)|
    merge_into(hash, key.downcase, value)
    
    hash
  end
end

#trailer(&block) ⇒ Object

Enumerate all headers in the trailer, if there are any.



128
129
130
131
132
133
134
# File 'lib/protocol/http/headers.rb', line 128

def trailer(&block)
  return to_enum(:trailer) unless block_given?
  
  if @tail
    @fields.drop(@tail).each(&block)
  end
end

#trailer!(&block) ⇒ Object

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.



121
122
123
124
125
# File 'lib/protocol/http/headers.rb', line 121

def trailer!(&block)
  @tail ||= @fields.size
  
  return trailer(&block)
end

#trailer?Boolean

Returns:

  • (Boolean)


105
106
107
# File 'lib/protocol/http/headers.rb', line 105

def trailer?
  @tail != nil
end