requests_ruby 1.0.2 → 1.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,241 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Requests
4
+ module HPACK
5
+ STATIC_TABLE = [
6
+ [':authority', ''],[':method', 'GET'],[':method', 'POST'],[':path', '/'],
7
+ [':path', '/index.html'],[':scheme', 'http'],[':scheme', 'https'],[':status', '200'],
8
+ [':status', '204'],[':status', '206'],[':status', '304'],[':status', '400'],
9
+ [':status', '404'],[':status', '500'],['accept-charset', ''],['accept-encoding', 'gzip, deflate'],
10
+ ['accept-language', ''],['accept-ranges', ''],['accept', ''],['access-control-allow-origin', ''],
11
+ ['age', ''],['allow', ''],['authorization', ''],['cache-control', ''],
12
+ ['content-disposition', ''],['content-encoding', ''],['content-language', ''],['content-length', ''],
13
+ ['content-location', ''],['content-range', ''],['content-type', ''],['cookie', ''],
14
+ ['date', ''],['etag', ''],['expect', ''],['expires', ''],
15
+ ['from', ''],['host', ''],['if-match', ''],['if-modified-since', ''],
16
+ ['if-none-match', ''],['if-range', ''],['if-unmodified-since', ''],['last-modified', ''],
17
+ ['link', ''],['location', ''],['max-forwards', ''],['proxy-authenticate', ''],
18
+ ['proxy-authorization', ''],['range', ''],['referer', ''],['refresh', ''],
19
+ ['retry-after', ''],['server', ''],['set-cookie', ''],['strict-transport-security', ''],
20
+ ['transfer-encoding', ''],['user-agent', ''],['vary', ''],['via', ''],
21
+ ['www-authenticate', '']
22
+ ].freeze
23
+
24
+ module Huffman
25
+ CODES = [
26
+ [0x1ff8, 13],[0x7fffd8, 23],[0xfffffe2, 28],[0xfffffe3, 28],[0xfffffe4, 28],[0xfffffe5, 28],
27
+ [0xfffffe6, 28],[0xfffffe7, 28],[0xfffffe8, 28],[0xffffea, 24],[0x3ffffffc, 30],[0xfffffe9, 28],
28
+ [0xfffffea, 28],[0x3ffffffd, 30],[0xfffffeb, 28],[0xfffffec, 28],[0xfffffed, 28],[0xfffffee, 28],
29
+ [0xfffffef, 28],[0xffffff0, 28],[0xffffff1, 28],[0xffffff2, 28],[0x3ffffffe, 30],[0xffffff3, 28],
30
+ [0xffffff4, 28],[0xffffff5, 28],[0xffffff6, 28],[0xffffff7, 28],[0xffffff8, 28],[0xffffff9, 28],
31
+ [0xffffffa, 28],[0xffffffb, 28],[0x14, 6],[0x3f8, 10],[0x3f9, 10],[0xffa, 12],
32
+ [0x1ff9, 13],[0x15, 6],[0xf8, 8],[0x7fa, 11],[0x3fa, 10],[0x3fb, 10],
33
+ [0xf9, 8],[0x7fb, 11],[0xfa, 8],[0x16, 6],[0x17, 6],[0x18, 6],
34
+ [0x0, 5],[0x1, 5],[0x2, 5],[0x19, 6],[0x1a, 6],[0x1b, 6],
35
+ [0x1c, 6],[0x1d, 6],[0x1e, 6],[0x1f, 6],[0x5c, 7],[0xfb, 8],
36
+ [0x7ffc, 15],[0x20, 6],[0xffb, 12],[0x3fc, 10],[0x1ffa, 13],[0x21, 6],
37
+ [0x5d, 7],[0x5e, 7],[0x5f, 7],[0x60, 7],[0x61, 7],[0x62, 7],
38
+ [0x63, 7],[0x64, 7],[0x65, 7],[0x66, 7],[0x67, 7],[0x68, 7],
39
+ [0x69, 7],[0x6a, 7],[0x6b, 7],[0x6c, 7],[0x6d, 7],[0x6e, 7],
40
+ [0x6f, 7],[0x70, 7],[0x71, 7],[0x72, 7],[0xfc, 8],[0x73, 7],
41
+ [0xfd, 8],[0x1ffb, 13],[0x7fff0, 19],[0x1ffc, 13],[0x3ffc, 14],[0x22, 6],
42
+ [0x7ffd, 15],[0x3, 5],[0x23, 6],[0x4, 5],[0x24, 6],[0x5, 5],
43
+ [0x25, 6],[0x26, 6],[0x27, 6],[0x6, 5],[0x74, 7],[0x75, 7],
44
+ [0x28, 6],[0x29, 6],[0x2a, 6],[0x7, 5],[0x2b, 6],[0x76, 7],
45
+ [0x2c, 6],[0x8, 5],[0x9, 5],[0x2d, 6],[0x77, 7],[0x78, 7],
46
+ [0x79, 7],[0x7a, 7],[0x7b, 7],[0x7ffe, 15],[0x7fc, 11],[0x3ffd, 14],
47
+ [0x1ffd, 13],[0xffffffc, 28],[0xfffe6, 20],[0x3fffd2, 22],[0xfffe7, 20],[0xfffe8, 20],
48
+ [0x3fffd3, 22],[0x3fffd4, 22],[0x3fffd5, 22],[0x7fffd9, 23],[0x3fffd6, 22],[0x7fffda, 23],
49
+ [0x7fffdb, 23],[0x7fffdc, 23],[0x7fffdd, 23],[0x7fffde, 23],[0xffffeb, 24],[0x7fffdf, 23],
50
+ [0xffffec, 24],[0xffffed, 24],[0x3fffd7, 22],[0x7fffe0, 23],[0xffffee, 24],[0x7fffe1, 23],
51
+ [0x7fffe2, 23],[0x7fffe3, 23],[0x7fffe4, 23],[0x1fffdc, 21],[0x3fffd8, 22],[0x7fffe5, 23],
52
+ [0x3fffd9, 22],[0x7fffe6, 23],[0x7fffe7, 23],[0xffffef, 24],[0x3fffda, 22],[0x1fffdd, 21],
53
+ [0xfffe9, 20],[0x3fffdb, 22],[0x3fffdc, 22],[0x7fffe8, 23],[0x7fffe9, 23],[0x1fffde, 21],
54
+ [0x7fffea, 23],[0x3fffdd, 22],[0x3fffde, 22],[0xfffff0, 24],[0x1fffdf, 21],[0x3fffdf, 22],
55
+ [0x7fffeb, 23],[0x7fffec, 23],[0x1fffe0, 21],[0x1fffe1, 21],[0x3fffe0, 22],[0x1fffe2, 21],
56
+ [0x7fffed, 23],[0x3fffe1, 22],[0x7fffee, 23],[0x7fffef, 23],[0xfffea, 20],[0x3fffe2, 22],
57
+ [0x3fffe3, 22],[0x3fffe4, 22],[0x7ffff0, 23],[0x3fffe5, 22],[0x3fffe6, 22],[0x7ffff1, 23],
58
+ [0x3ffffe0, 26],[0x3ffffe1, 26],[0xfffeb, 20],[0x7fff1, 19],[0x3fffe7, 22],[0x7ffff2, 23],
59
+ [0x3fffe8, 22],[0x1ffffec, 25],[0x3ffffe2, 26],[0x3ffffe3, 26],[0x3ffffe4, 26],[0x7ffffde, 27],
60
+ [0x7ffffdf, 27],[0x3ffffe5, 26],[0xfffff1, 24],[0x1ffffed, 25],[0x7fff2, 19],[0x1fffe3, 21],
61
+ [0x3ffffe6, 26],[0x7ffffe0, 27],[0x7ffffe1, 27],[0x3ffffe7, 26],[0x7ffffe2, 27],[0xfffff2, 24],
62
+ [0x1fffe4, 21],[0x1fffe5, 21],[0x3ffffe8, 26],[0x3ffffe9, 26],[0xffffffd, 28],[0x7ffffe3, 27],
63
+ [0x7ffffe4, 27],[0x7ffffe5, 27],[0xfffec, 20],[0xfffff3, 24],[0xfffed, 20],[0x1fffe6, 21],
64
+ [0x3fffe9, 22],[0x1fffe7, 21],[0x1fffe8, 21],[0x7ffff3, 23],[0x3fffea, 22],[0x3fffeb, 22],
65
+ [0x1ffffee, 25],[0x1ffffef, 25],[0xfffff4, 24],[0xfffff5, 24],[0x3ffffea, 26],[0x7ffff4, 23],
66
+ [0x3ffffeb, 26],[0x7ffffe6, 27],[0x3ffffec, 26],[0x3ffffed, 26],[0x7ffffe7, 27],[0x7ffffe8, 27],
67
+ [0x7ffffe9, 27],[0x7ffffea, 27],[0x7ffffeb, 27],[0xffffffe, 28],[0x7ffffec, 27],[0x7ffffed, 27],
68
+ [0x7ffffee, 27],[0x7ffffef, 27],[0x7fffff0, 27],[0x3ffffee, 26],[0x3fffffff, 30]
69
+ ].freeze
70
+ EOS = 256
71
+ ENCODE_TABLE = CODES.map { |c, l| [c].pack('N').unpack1('B*')[-l..-1] }.freeze
72
+ def self.build_tree
73
+ root = {}
74
+ CODES.each_with_index do |(code, len), sym|
75
+ node = root
76
+ (len - 1).downto(0) do |i|
77
+ bit = (code >> i) & 1
78
+ node = (node[bit] ||= {})
79
+ end
80
+ node[:sym] = sym
81
+ end
82
+ root
83
+ end
84
+ DECODE_TREE = build_tree
85
+ def self.encode(str)
86
+ bits = String.new
87
+ str.each_byte { |b| bits << ENCODE_TABLE[b] }
88
+ pad = (8 - bits.size % 8) % 8
89
+ bits << ('1' * pad) if pad > 0
90
+ [bits].pack('B*')
91
+ end
92
+ def self.decode(bytes)
93
+ out = String.new(encoding: Encoding::BINARY)
94
+ node = DECODE_TREE
95
+ bitcount = bytes.bytesize * 8
96
+ pos = 0
97
+ bytes.each_byte do |byte|
98
+ 7.downto(0) do |i|
99
+ pos += 1
100
+ bit = (byte >> i) & 1
101
+ nxt = node[bit]
102
+ unless nxt
103
+ raise Requests::ContentDecodingError, 'invalid huffman padding' if pos > bitcount - 8 || pos < bitcount
104
+ return out
105
+ end
106
+ node = nxt
107
+ if node.key?(:sym)
108
+ sym = node[:sym]
109
+ raise Requests::ContentDecodingError, 'unexpected huffman EOS' if sym == EOS
110
+ out << sym.chr(Encoding::BINARY)
111
+ node = DECODE_TREE
112
+ end
113
+ end
114
+ end
115
+ out
116
+ end
117
+ end
118
+
119
+ module Integer
120
+ module_function
121
+ def encode(value, prefix_bits, first_byte = 0)
122
+ max = (1 << prefix_bits) - 1
123
+ if value < max
124
+ [first_byte | value].pack('C')
125
+ else
126
+ out = [first_byte | max].pack('C')
127
+ value -= max
128
+ while value >= 128
129
+ out << [(value % 128) | 128].pack('C')
130
+ value /= 128
131
+ end
132
+ out << [value].pack('C')
133
+ end
134
+ end
135
+ def decode(bytes, pos, prefix_bits)
136
+ max = (1 << prefix_bits) - 1
137
+ value = bytes.getbyte(pos) & max
138
+ pos += 1
139
+ if value == max
140
+ m = 0
141
+ loop do
142
+ b = bytes.getbyte(pos)
143
+ raise Requests::ContentDecodingError, 'truncated hpack integer' if b.nil?
144
+ pos += 1
145
+ value += (b & 127) * (2**m)
146
+ m += 7
147
+ break if b & 128 == 0
148
+ end
149
+ end
150
+ [value, pos]
151
+ end
152
+ end
153
+
154
+ class Encoder
155
+ def encode(headers)
156
+ out = String.new(encoding: Encoding::BINARY)
157
+ headers.each { |name, value| out << encode_field(name.to_s.downcase, value.to_s) }
158
+ out
159
+ end
160
+ private
161
+ def encode_field(name, value)
162
+ exact = STATIC_TABLE.index { |n, v| n == name && v == value }
163
+ return Integer.encode(exact + 1, 7, 0x80) if exact
164
+ name_idx = STATIC_TABLE.index { |n, _| n == name }
165
+ head = name_idx ? Integer.encode(name_idx + 1, 4, 0x00) : Integer.encode(0, 4, 0x00) + literal(name)
166
+ head + literal(value)
167
+ end
168
+ def literal(str)
169
+ h = Huffman.encode(str)
170
+ if h.bytesize < str.bytesize
171
+ Integer.encode(h.bytesize, 7, 0x80) + h
172
+ else
173
+ Integer.encode(str.bytesize, 7, 0x00) + str.dup.force_encoding(Encoding::BINARY)
174
+ end
175
+ end
176
+ end
177
+
178
+ class Decoder
179
+ def initialize(max_size: 4096)
180
+ @dynamic = []
181
+ @max_size = max_size
182
+ @size = 0
183
+ end
184
+ def decode(bytes)
185
+ pos = 0
186
+ out = []
187
+ while pos < bytes.bytesize
188
+ byte = bytes.getbyte(pos)
189
+ if byte & 0x80 != 0
190
+ idx, pos = Integer.decode(bytes, pos, 7)
191
+ out << lookup(idx)
192
+ elsif byte & 0x40 != 0
193
+ name, value, pos = read_literal(bytes, pos, 6)
194
+ add_dynamic(name, value)
195
+ out << [name, value]
196
+ elsif byte & 0x20 != 0
197
+ size, pos = Integer.decode(bytes, pos, 5)
198
+ @max_size = size
199
+ evict
200
+ else
201
+ name, value, pos = read_literal(bytes, pos, 4)
202
+ out << [name, value]
203
+ end
204
+ end
205
+ out
206
+ end
207
+ private
208
+ def lookup(idx)
209
+ return STATIC_TABLE[idx - 1] if idx <= STATIC_TABLE.size
210
+ d = @dynamic[idx - STATIC_TABLE.size - 1]
211
+ raise Requests::ContentDecodingError, "invalid hpack index #{idx}" unless d
212
+ d
213
+ end
214
+ def read_literal(bytes, pos, prefix_bits)
215
+ idx, pos = Integer.decode(bytes, pos, prefix_bits)
216
+ name, pos = idx.zero? ? read_string(bytes, pos) : [lookup(idx)[0], pos]
217
+ value, pos = read_string(bytes, pos)
218
+ [name, value, pos]
219
+ end
220
+ def read_string(bytes, pos)
221
+ huff = bytes.getbyte(pos) & 0x80 != 0
222
+ len, pos = Integer.decode(bytes, pos, 7)
223
+ raw = bytes.byteslice(pos, len)
224
+ pos += len
225
+ [huff ? Huffman.decode(raw) : raw, pos]
226
+ end
227
+ def add_dynamic(name, value)
228
+ entry_size = name.bytesize + value.bytesize + 32
229
+ @dynamic.unshift([name, value])
230
+ @size += entry_size
231
+ evict
232
+ end
233
+ def evict
234
+ while @size > @max_size && !@dynamic.empty?
235
+ name, value = @dynamic.pop
236
+ @size -= name.bytesize + value.bytesize + 32
237
+ end
238
+ end
239
+ end
240
+ end
241
+ end