lib/http/headers/normalizer.rb
# frozen_string_literal: true module HTTP class Headers class Normalizer # Matches HTTP header names when in "Canonical-Http-Format" CANONICAL_NAME_RE = /\A[A-Z][a-z]*(?:-[A-Z][a-z]*)*\z/ # Matches valid header field name according to RFC. # @see http://tools.ietf.org/html/rfc7230#section-3.2 COMPLIANT_NAME_RE = /\A[A-Za-z0-9!#$%&'*+\-.^_`|~]+\z/ NAME_PARTS_SEPARATOR_RE = /[\-_]/ # @private # Normalized header names cache class Cache MAX_SIZE = 200 def initialize @store = {} end def get(key) @store[key] end alias [] get def set(key, value) # Maintain cache size @store.delete(@store.each_key.first) while MAX_SIZE <= @store.size @store[key] = value end alias []= set end def initialize @cache = Cache.new end # Transforms `name` to canonical HTTP header capitalization def call(name) name = -name.to_s value = (@cache[name] ||= -normalize_header(name)) value.dup end private # Transforms `name` to canonical HTTP header capitalization # # @param [String] name # @raise [HeaderError] if normalized name does not # match {COMPLIANT_NAME_RE} # @return [String] canonical HTTP header name def normalize_header(name) return name if CANONICAL_NAME_RE.match?(name) normalized = name.split(NAME_PARTS_SEPARATOR_RE).each(&:capitalize!).join("-") return normalized if COMPLIANT_NAME_RE.match?(normalized) raise HeaderError, "Invalid HTTP header field name: #{name.inspect}" end end end end