lib/async/http/headers.rb in async-http-0.23.3 vs lib/async/http/headers.rb in async-http-0.24.0
- old
+ new
@@ -19,10 +19,42 @@
# THE SOFTWARE.
module Async
module HTTP
class Headers
+ class Split < Array
+ COMMA = /\s*,\s*/
+
+ def initialize(value)
+ super(value.split(COMMA))
+ end
+
+ def << value
+ super value.split(COMMA)
+ end
+
+ def to_s
+ join(", ")
+ end
+ end
+
+ class Multiple < Array
+ def initialize(value)
+ super()
+
+ self << value
+ end
+
+ def to_s
+ join("\n")
+ end
+ end
+
+ def self.[] hash
+ self.new(hash.to_a)
+ end
+
def initialize(fields = [])
@fields = fields
@indexed = to_h
end
@@ -34,10 +66,14 @@
@indexed = to_h
super
end
+ def empty?
+ @fields.empty?
+ end
+
def each(&block)
@fields.each(&block)
end
def include? key
@@ -57,21 +93,73 @@
if field = values.last
return field.last
end
end
+ def slice!(keys)
+ values, @fields = @fields.partition do |field|
+ keys.include?(field.first.downcase)
+ end
+
+ if @indexed
+ keys.each do |key|
+ @indexed.delete(key)
+ end
+ end
+ end
+
+ def add(key, value)
+ self[key] = value
+ end
+
def []= key, value
@fields << [key, value]
if @indexed
- key = key.downcase
-
- if current_value = @indexed[key]
- @indexed[key] = Array(current_value) << value
+ # It would be good to do some kind of validation here.
+ merge(@indexed, key.downcase, value)
+ end
+ end
+
+ MERGE_POLICY = {
+ # Headers which may only be specified once.
+ 'content-type' => false,
+ 'content-disposition' => false,
+ 'content-length' => false,
+ 'user-agent' => false,
+ 'referer' => false,
+ 'host' => false,
+ 'authorization' => false,
+ 'proxy-authorization' => false,
+ 'if-modified-since' => false,
+ 'if-unmodified-since' => false,
+ 'from' => false,
+ 'location' => false,
+ 'max-forwards' => false,
+
+ 'connection' => Split,
+
+ # Headers specifically for proxies:
+ 'via' => Split,
+ 'x-forwarded-for' => Split,
+
+ # Headers which may be specified multiple times, but which can't be concatenated.
+ 'set-cookie' => Multiple,
+ 'www-authenticate' => Multiple,
+ 'proxy-authenticate' => Multiple
+ }.tap{|hash| hash.default = Split}
+
+ def merge(hash, key, value)
+ if policy = MERGE_POLICY[key]
+ if current_value = hash[key]
+ current_value << value
else
- @indexed[key] = value
+ hash[key] = policy.new(value)
end
+ else
+ # We can't merge these, we only expose the last one set.
+ hash[key] = value
end
end
def [] key
@indexed ||= to_h
@@ -79,26 +167,34 @@
@indexed[key]
end
def to_h
@fields.inject({}) do |hash, (key, value)|
- key = key.downcase
+ merge(hash, key.downcase, value)
- if current_value = hash[key]
- hash[key] = Array(current_value) << value
- else
- hash[key] = value
- end
-
hash
end
end
def == other
if other.is_a? Hash
to_h == other
else
@fields == other.fields
+ end
+ end
+
+ class Merged
+ def initialize(*all)
+ @all = all
+ end
+
+ def each(&block)
+ @all.each do |headers|
+ headers.each do |key, value|
+ yield key, value.to_s
+ end
+ end
end
end
end
end
end