File: headers.rb

package info (click to toggle)
ruby-httpx 1.7.2-3
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 1,816 kB
  • sloc: ruby: 12,209; makefile: 4
file content (176 lines) | stat: -rw-r--r-- 4,082 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
# frozen_string_literal: true

module HTTPX
  class Headers
    class << self
      def new(headers = nil)
        return headers if headers.is_a?(self)

        super
      end
    end

    def initialize(headers = nil)
      if headers.nil? || headers.empty?
        @headers = headers.to_h
        return
      end

      @headers = {}

      headers.each do |field, value|
        field = downcased(field)

        value = array_value(value)

        current = @headers[field]

        if current.nil?
          @headers[field] = value
        else
          current.concat(value)
        end
      end
    end

    # cloned initialization
    def initialize_clone(orig, **kwargs)
      super
      @headers = orig.instance_variable_get(:@headers).clone(**kwargs)
    end

    # dupped initialization
    def initialize_dup(orig)
      super
      @headers = orig.instance_variable_get(:@headers).transform_values(&:dup)
    end

    # freezes the headers hash
    def freeze
      @headers.each_value(&:freeze).freeze
      super
    end

    # merges headers with another header-quack.
    # the merge rule is, if the header already exists,
    # ignore what the +other+ headers has. Otherwise, set
    #
    def merge(other)
      headers = dup
      other.each do |field, value|
        headers[downcased(field)] = value
      end
      headers
    end

    # returns the comma-separated values of the header field
    # identified by +field+, or nil otherwise.
    #
    def [](field)
      a = @headers[downcased(field)] || return
      a.join(", ")
    end

    # sets +value+ (if not nil) as single value for the +field+ header.
    #
    def []=(field, value)
      return unless value

      @headers[downcased(field)] = array_value(value)
    end

    # deletes all values associated with +field+ header.
    #
    def delete(field)
      canonical = downcased(field)
      @headers.delete(canonical) if @headers.key?(canonical)
    end

    # adds additional +value+ to the existing, for header +field+.
    #
    def add(field, value)
      (@headers[downcased(field)] ||= []) << String(value)
    end

    # helper to be used when adding an header field as a value to another field
    #
    #     h2_headers.add_header("vary", "accept-encoding")
    #     h2_headers["vary"] #=> "accept-encoding"
    #     h1_headers.add_header("vary", "accept-encoding")
    #     h1_headers["vary"] #=> "Accept-Encoding"
    #
    alias_method :add_header, :add

    # returns the enumerable headers store in pairs of header field + the values in
    # the comma-separated string format
    #
    def each(extra_headers = nil)
      return enum_for(__method__, extra_headers) { @headers.size } unless block_given?

      @headers.each do |field, value|
        yield(field, value.join(", ")) unless value.empty?
      end

      extra_headers.each do |field, value|
        yield(field, value) unless value.empty?
      end if extra_headers
    end

    def ==(other)
      other == to_hash
    end

    def empty?
      @headers.empty?
    end

    # the headers store in Hash format
    def to_hash
      Hash[to_a]
    end
    alias_method :to_h, :to_hash

    # the headers store in array of pairs format
    def to_a
      Array(each)
    end

    # headers as string
    def to_s
      @headers.to_s
    end

    # :nocov:
    def inspect
      "#<#{self.class}:#{object_id} " \
        "#{to_hash.inspect}>"
    end
    # :nocov:

    # this is internal API and doesn't abide to other public API
    # guarantees, like downcasing strings.
    # Please do not use this outside of core!
    #
    def key?(downcased_key)
      @headers.key?(downcased_key)
    end

    # returns the values for the +field+ header in array format.
    # This method is more internal, and for this reason doesn't try
    # to "correct" the user input, i.e. it doesn't downcase the key.
    #
    def get(field)
      @headers[field] || EMPTY
    end

    private

    def array_value(value)
      Array(value)
    end

    def downcased(field)
      String(field).downcase
    end
  end
end