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