http-2 0.6.1 → 0.6.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.autotest +2 -1
- data/Gemfile +8 -1
- data/README.md +27 -21
- data/example/README.md +53 -0
- data/example/client.rb +88 -30
- data/example/helper.rb +5 -0
- data/example/keys/mycert.pem +24 -0
- data/example/keys/mykey.pem +27 -0
- data/example/server.rb +47 -8
- data/http-2.gemspec +0 -2
- data/lib/http/2.rb +2 -0
- data/lib/http/2/buffer.rb +21 -4
- data/lib/http/2/client.rb +50 -0
- data/lib/http/2/compressor.rb +197 -181
- data/lib/http/2/connection.rb +57 -83
- data/lib/http/2/emitter.rb +2 -2
- data/lib/http/2/error.rb +5 -0
- data/lib/http/2/framer.rb +32 -31
- data/lib/http/2/server.rb +55 -0
- data/lib/http/2/stream.rb +1 -1
- data/lib/http/2/version.rb +1 -1
- data/spec/buffer_spec.rb +23 -0
- data/spec/client_spec.rb +93 -0
- data/spec/compressor_spec.rb +89 -80
- data/spec/connection_spec.rb +24 -75
- data/spec/emitter_spec.rb +8 -0
- data/spec/framer_spec.rb +36 -40
- data/spec/helper.rb +6 -2
- data/spec/server_spec.rb +50 -0
- data/spec/stream_spec.rb +23 -30
- metadata +13 -30
data/http-2.gemspec
CHANGED
data/lib/http/2.rb
CHANGED
data/lib/http/2/buffer.rb
CHANGED
@@ -4,17 +4,34 @@ module HTTP2
|
|
4
4
|
#
|
5
5
|
class Buffer < String
|
6
6
|
|
7
|
+
UINT32 = "N"
|
8
|
+
BINARY = "binary"
|
9
|
+
private_constant :UINT32, :BINARY
|
10
|
+
|
7
11
|
# Forces binary encoding on the string
|
8
|
-
def initialize(
|
9
|
-
force_encoding(
|
10
|
-
super(*args)
|
12
|
+
def initialize(data = '')
|
13
|
+
super(data.force_encoding(BINARY))
|
11
14
|
end
|
12
15
|
|
13
16
|
# Emulate StringIO#read: slice first n bytes from the buffer.
|
14
17
|
#
|
15
18
|
# @param n [Integer] number of bytes to slice from the buffer
|
16
19
|
def read(n)
|
17
|
-
slice!(0,n)
|
20
|
+
Buffer.new(slice!(0,n))
|
21
|
+
end
|
22
|
+
|
23
|
+
# Alias getbyte to readbyte
|
24
|
+
alias :readbyte :getbyte
|
25
|
+
|
26
|
+
# Emulate StringIO#getbyte: slice first byte from buffer.
|
27
|
+
def getbyte
|
28
|
+
read(1).ord
|
29
|
+
end
|
30
|
+
|
31
|
+
# Slice unsigned 32-bit integer from buffer.
|
32
|
+
# @return [Integer]
|
33
|
+
def read_uint32
|
34
|
+
read(4).unpack(UINT32).first
|
18
35
|
end
|
19
36
|
end
|
20
37
|
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module HTTP2
|
2
|
+
|
3
|
+
# HTTP 2.0 client connection class that implements appropriate header
|
4
|
+
# compression / decompression algorithms and stream management logic.
|
5
|
+
#
|
6
|
+
# Your code is responsible for driving the client object, which in turn
|
7
|
+
# performs all of the necessary HTTP 2.0 encoding / decoding, state
|
8
|
+
# management, and the rest. A simple example:
|
9
|
+
#
|
10
|
+
# @example
|
11
|
+
# socket = YourTransport.new
|
12
|
+
#
|
13
|
+
# conn = HTTP2::Client.new
|
14
|
+
# conn.on(:frame) {|bytes| socket << bytes }
|
15
|
+
#
|
16
|
+
# while bytes = socket.read
|
17
|
+
# conn << bytes
|
18
|
+
# end
|
19
|
+
#
|
20
|
+
class Client < Connection
|
21
|
+
|
22
|
+
# Initialize new HTTP 2.0 client object.
|
23
|
+
def initialize(*args)
|
24
|
+
@stream_id = 1
|
25
|
+
@state = :connection_header
|
26
|
+
@compressor = Header::Compressor.new(:request)
|
27
|
+
@decompressor = Header::Decompressor.new(:response)
|
28
|
+
|
29
|
+
super
|
30
|
+
end
|
31
|
+
|
32
|
+
# Send an outgoing frame. Connection and stream flow control is managed
|
33
|
+
# by Connection class.
|
34
|
+
#
|
35
|
+
# @see Connection
|
36
|
+
# @note Client will emit the connection header as the first 24 bytes
|
37
|
+
# @param frame [Hash]
|
38
|
+
def send(frame)
|
39
|
+
if @state == :connection_header
|
40
|
+
emit(:frame, CONNECTION_HEADER)
|
41
|
+
@state = :connected
|
42
|
+
|
43
|
+
settings(stream_limit: @stream_limit, window_limit: @window_limit)
|
44
|
+
end
|
45
|
+
|
46
|
+
super(frame)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
data/lib/http/2/compressor.rb
CHANGED
@@ -1,5 +1,3 @@
|
|
1
|
-
require "stringio"
|
2
|
-
|
3
1
|
module HTTP2
|
4
2
|
|
5
3
|
# Implementation of header compression for HTTP 2.0 (HPACK) format adapted
|
@@ -12,97 +10,84 @@ module HTTP2
|
|
12
10
|
# encoding context: an encoding context contains a header table and a
|
13
11
|
# reference set - there is one encoding context for each direction.
|
14
12
|
#
|
15
|
-
class
|
13
|
+
class EncodingContext
|
16
14
|
include Error
|
17
15
|
|
18
16
|
# TODO: replace StringIO with Buffer...
|
19
17
|
|
20
18
|
# Default request working set as defined by the spec.
|
21
19
|
REQ_DEFAULTS = [
|
22
|
-
[':scheme' ,'http' ],
|
23
|
-
[':scheme' ,'https'],
|
24
|
-
[':host' ,'' ],
|
25
|
-
[':path' ,'/' ],
|
26
|
-
[':method' ,'get' ],
|
27
|
-
['accept' ,'' ],
|
28
|
-
['accept-charset' ,'' ],
|
29
|
-
['accept-encoding' ,'' ],
|
30
|
-
['accept-language' ,'' ],
|
31
|
-
['cookie' ,'' ],
|
32
|
-
['if-modified-since' ,'' ],
|
33
|
-
['
|
34
|
-
['
|
35
|
-
['
|
36
|
-
['
|
37
|
-
['
|
38
|
-
['
|
39
|
-
['
|
40
|
-
['
|
41
|
-
['
|
42
|
-
['
|
43
|
-
['
|
44
|
-
['
|
45
|
-
['
|
46
|
-
['
|
47
|
-
['
|
48
|
-
['
|
49
|
-
['
|
50
|
-
['
|
51
|
-
['
|
52
|
-
|
53
|
-
['pragma' ,'' ],
|
54
|
-
['proxy-authorization','' ],
|
55
|
-
['range' ,'' ],
|
56
|
-
['te' ,'' ],
|
57
|
-
['upgrade' ,'' ],
|
58
|
-
['via' ,'' ],
|
59
|
-
['warning' ,'' ]
|
60
|
-
];
|
20
|
+
[':scheme' , 'http' ],
|
21
|
+
[':scheme' , 'https'],
|
22
|
+
[':host' , '' ],
|
23
|
+
[':path' , '/' ],
|
24
|
+
[':method' , 'get' ],
|
25
|
+
['accept' , '' ],
|
26
|
+
['accept-charset' , '' ],
|
27
|
+
['accept-encoding' , '' ],
|
28
|
+
['accept-language' , '' ],
|
29
|
+
['cookie' , '' ],
|
30
|
+
['if-modified-since' , '' ],
|
31
|
+
['user-agent' , '' ],
|
32
|
+
['referer' , '' ],
|
33
|
+
['authorization' , '' ],
|
34
|
+
['allow' , '' ],
|
35
|
+
['cache-control' , '' ],
|
36
|
+
['connection' , '' ],
|
37
|
+
['content-length' , '' ],
|
38
|
+
['content-type' , '' ],
|
39
|
+
['date' , '' ],
|
40
|
+
['expect' , '' ],
|
41
|
+
['from' , '' ],
|
42
|
+
['if-match' , '' ],
|
43
|
+
['if-none-match' , '' ],
|
44
|
+
['if-range' , '' ],
|
45
|
+
['if-unmodified-since', '' ],
|
46
|
+
['max-forwards' , '' ],
|
47
|
+
['proxy-authorization', '' ],
|
48
|
+
['range' , '' ],
|
49
|
+
['via' , '' ]
|
50
|
+
]
|
61
51
|
|
62
52
|
# Default response working set as defined by the spec.
|
63
53
|
RESP_DEFAULTS = [
|
64
|
-
[':status'
|
65
|
-
['age'
|
66
|
-
['cache-control'
|
67
|
-
['content-length'
|
68
|
-
['content-type'
|
69
|
-
['date'
|
70
|
-
['etag'
|
71
|
-
['expires'
|
72
|
-
['last-modified'
|
73
|
-
['server'
|
74
|
-
['set-cookie'
|
75
|
-
['vary'
|
76
|
-
['via'
|
77
|
-
['access-control-allow-origin','' ],
|
78
|
-
['accept-ranges'
|
79
|
-
['allow'
|
80
|
-
['connection'
|
81
|
-
['content-disposition'
|
82
|
-
['content-encoding'
|
83
|
-
['content-language'
|
84
|
-
['content-location'
|
85
|
-
['content-
|
86
|
-
['
|
87
|
-
['
|
88
|
-
['
|
89
|
-
['
|
90
|
-
['
|
91
|
-
['
|
92
|
-
['
|
93
|
-
['
|
94
|
-
|
95
|
-
['trailer' ,'' ],
|
96
|
-
['transfer-encoding' ,'' ],
|
97
|
-
['warning' ,'' ],
|
98
|
-
['www-authenticate' ,'' ]
|
99
|
-
];
|
54
|
+
[':status' , '200'],
|
55
|
+
['age' , '' ],
|
56
|
+
['cache-control' , '' ],
|
57
|
+
['content-length' , '' ],
|
58
|
+
['content-type' , '' ],
|
59
|
+
['date' , '' ],
|
60
|
+
['etag' , '' ],
|
61
|
+
['expires' , '' ],
|
62
|
+
['last-modified' , '' ],
|
63
|
+
['server' , '' ],
|
64
|
+
['set-cookie' , '' ],
|
65
|
+
['vary' , '' ],
|
66
|
+
['via' , '' ],
|
67
|
+
['access-control-allow-origin' , '' ],
|
68
|
+
['accept-ranges' , '' ],
|
69
|
+
['allow' , '' ],
|
70
|
+
['connection' , '' ],
|
71
|
+
['content-disposition' , '' ],
|
72
|
+
['content-encoding' , '' ],
|
73
|
+
['content-language' , '' ],
|
74
|
+
['content-location' , '' ],
|
75
|
+
['content-range' , '' ],
|
76
|
+
['link' , '' ],
|
77
|
+
['location' , '' ],
|
78
|
+
['proxy-authenticate' , '' ],
|
79
|
+
['refresh' , '' ],
|
80
|
+
['retry-after' , '' ],
|
81
|
+
['strict-transport-security' , '' ],
|
82
|
+
['transfer-encoding' , '' ],
|
83
|
+
['www-authenticate' , '' ]
|
84
|
+
]
|
100
85
|
|
101
86
|
# Current table of header key-value pairs.
|
102
87
|
attr_reader :table
|
103
88
|
|
104
|
-
# Current
|
105
|
-
attr_reader :
|
89
|
+
# Current reference set of header key-value pairs.
|
90
|
+
attr_reader :refset
|
106
91
|
|
107
92
|
# Initializes compression context with appropriate client/server
|
108
93
|
# defaults and maximum size of the header table.
|
@@ -113,37 +98,50 @@ module HTTP2
|
|
113
98
|
@type = type
|
114
99
|
@table = (type == :request) ? REQ_DEFAULTS.dup : RESP_DEFAULTS.dup
|
115
100
|
@limit = limit
|
116
|
-
@
|
101
|
+
@refset = []
|
117
102
|
end
|
118
103
|
|
119
104
|
# Performs differential coding based on provided command type.
|
120
|
-
# - http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-
|
105
|
+
# - http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-03#section-3.2
|
121
106
|
#
|
122
107
|
# @param cmd [Hash]
|
108
|
+
# @return [Hash] emitted header
|
123
109
|
def process(cmd)
|
110
|
+
emit = nil
|
111
|
+
|
124
112
|
# indexed representation
|
125
113
|
if cmd[:type] == :indexed
|
126
|
-
#
|
127
|
-
#
|
128
|
-
#
|
129
|
-
#
|
130
|
-
#
|
131
|
-
#
|
132
|
-
#
|
133
|
-
|
114
|
+
# An indexed representation corresponding to an entry not present
|
115
|
+
# in the reference set entails the following actions:
|
116
|
+
# - The header corresponding to the entry is emitted.
|
117
|
+
# - The entry is added to the reference set.
|
118
|
+
#
|
119
|
+
# An indexed representation corresponding to an entry present in
|
120
|
+
# the reference set entails the following actions:
|
121
|
+
# - The entry is removed from the reference set.
|
122
|
+
#
|
123
|
+
idx = cmd[:name]
|
124
|
+
cur = @refset.find_index {|(i,v)| i == idx}
|
134
125
|
|
135
126
|
if cur
|
136
|
-
@
|
127
|
+
@refset.delete_at(cur)
|
137
128
|
else
|
138
|
-
|
129
|
+
emit = @table[idx]
|
130
|
+
@refset.push [idx, @table[idx]]
|
139
131
|
end
|
140
132
|
|
141
133
|
else
|
142
|
-
#
|
143
|
-
#
|
144
|
-
#
|
145
|
-
#
|
146
|
-
#
|
134
|
+
# A literal representation that is not added to the header table
|
135
|
+
# entails the following action:
|
136
|
+
# - The header is emitted.
|
137
|
+
#
|
138
|
+
# A literal representation that is added to the header table entails
|
139
|
+
# the following actions:
|
140
|
+
# - The header is emitted.
|
141
|
+
# - The header is added to the header table, at the location
|
142
|
+
# defined by the representation.
|
143
|
+
# - The new entry is added to the reference set.
|
144
|
+
#
|
147
145
|
if cmd[:name].is_a? Integer
|
148
146
|
k,v = @table[cmd[:name]]
|
149
147
|
|
@@ -152,42 +150,29 @@ module HTTP2
|
|
152
150
|
cmd[:name] = k
|
153
151
|
end
|
154
152
|
|
155
|
-
|
153
|
+
emit = [cmd[:name], cmd[:value]]
|
156
154
|
|
157
155
|
if cmd[:type] != :noindex
|
158
|
-
size_check
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
156
|
+
if size_check(cmd)
|
157
|
+
|
158
|
+
case cmd[:type]
|
159
|
+
when :incremental
|
160
|
+
cmd[:index] = @table.size
|
161
|
+
when :substitution
|
162
|
+
if @table[cmd[:index]].nil?
|
163
|
+
raise HeaderException.new("invalid index")
|
164
|
+
end
|
165
|
+
when :prepend
|
166
|
+
@table = [emit] + @table
|
166
167
|
end
|
167
|
-
when :prepend
|
168
|
-
@table = [newval] + @table
|
169
|
-
end
|
170
168
|
|
171
|
-
|
169
|
+
@table[cmd[:index]] = emit
|
170
|
+
@refset.push [cmd[:index], emit]
|
171
|
+
end
|
172
172
|
end
|
173
|
-
|
174
|
-
@workset.push [cmd[:index], newval]
|
175
173
|
end
|
176
|
-
end
|
177
174
|
|
178
|
-
|
179
|
-
# reference set of headers is interpreted into the working set of
|
180
|
-
# headers: for each header in the reference set, an entry is added to
|
181
|
-
# the working set, containing the header name, its value, and its
|
182
|
-
# current index in the header table.
|
183
|
-
#
|
184
|
-
# @return [Array] current working set
|
185
|
-
def update_sets
|
186
|
-
# new refset is the the workset sans headers not in header table
|
187
|
-
refset = @workset.reject {|(i,h)| !@table.include? h}
|
188
|
-
|
189
|
-
# new workset is the refset with index of each header in header table
|
190
|
-
@workset = refset.collect {|(i,h)| [@table.find_index(h), h]}
|
175
|
+
emit
|
191
176
|
end
|
192
177
|
|
193
178
|
# Emits best available command to encode provided header.
|
@@ -230,40 +215,55 @@ module HTTP2
|
|
230
215
|
|
231
216
|
private
|
232
217
|
|
233
|
-
# Before
|
234
|
-
#
|
235
|
-
#
|
236
|
-
#
|
237
|
-
#
|
238
|
-
#
|
239
|
-
#
|
240
|
-
#
|
218
|
+
# Before doing such a modification, it has to be ensured that the header
|
219
|
+
# table size will stay lower than or equal to the
|
220
|
+
# SETTINGS_HEADER_TABLE_SIZE limit. To achieve this, repeatedly, the
|
221
|
+
# first entry of the header table is removed, until enough space is
|
222
|
+
# available for the modification.
|
223
|
+
#
|
224
|
+
# A consequence of removing one or more entries at the beginning of the
|
225
|
+
# header table is that the remaining entries are renumbered. The first
|
226
|
+
# entry of the header table is always associated to the index 0.
|
241
227
|
#
|
242
228
|
# @param cmd [Hash]
|
229
|
+
# @return [Boolean]
|
243
230
|
def size_check(cmd)
|
244
231
|
cursize = @table.join.bytesize + @table.size * 32
|
245
232
|
cmdsize = cmd[:name].bytesize + cmd[:value].bytesize + 32
|
246
233
|
|
234
|
+
# The addition of a new entry with a size greater than the
|
235
|
+
# SETTINGS_HEADER_TABLE_SIZE limit causes all the entries from the
|
236
|
+
# header table to be dropped and the new entry not to be added to the
|
237
|
+
# header table. The replacement of an existing entry with a new entry
|
238
|
+
# with a size greater than the SETTINGS_HEADER_TABLE_SIZE has the same
|
239
|
+
# consequences.
|
240
|
+
if cmdsize > @limit
|
241
|
+
@table.clear
|
242
|
+
return false
|
243
|
+
end
|
244
|
+
|
247
245
|
cur = 0
|
248
246
|
while (cursize + cmdsize) > @limit do
|
249
247
|
e = @table.shift
|
250
248
|
|
251
|
-
# When
|
252
|
-
#
|
253
|
-
#
|
254
|
-
#
|
255
|
-
#
|
256
|
-
#
|
249
|
+
# When the modification of the header table is the replacement of an
|
250
|
+
# existing entry, the replaced entry is the one indicated in the
|
251
|
+
# literal representation before any entry is removed from the header
|
252
|
+
# table. If the entry to be replaced is removed from the header table
|
253
|
+
# when performing the size adjustment, the replacement entry is
|
254
|
+
# inserted at the beginning of the header table.
|
257
255
|
if cmd[:type] == :substitution && cur == cmd[:index]
|
258
256
|
cmd[:type] = :prepend
|
259
257
|
end
|
260
258
|
|
261
259
|
cursize -= (e.join.bytesize + 32)
|
262
260
|
end
|
261
|
+
|
262
|
+
return true
|
263
263
|
end
|
264
264
|
|
265
265
|
def active?(idx)
|
266
|
-
!@
|
266
|
+
!@refset.find {|i,_| i == idx }.nil?
|
267
267
|
end
|
268
268
|
|
269
269
|
def default?(idx)
|
@@ -289,20 +289,20 @@ module HTTP2
|
|
289
289
|
# server_role = Compressor.new(:response)
|
290
290
|
class Compressor
|
291
291
|
def initialize(type)
|
292
|
-
@cc =
|
292
|
+
@cc = EncodingContext.new(type)
|
293
293
|
end
|
294
294
|
|
295
295
|
# Encodes provided value via integer representation.
|
296
|
-
# - http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-
|
296
|
+
# - http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-03#section-4.1.1
|
297
297
|
#
|
298
|
-
#
|
299
|
-
#
|
300
|
-
#
|
301
|
-
#
|
302
|
-
#
|
303
|
-
#
|
304
|
-
#
|
305
|
-
# I
|
298
|
+
# If I < 2^N - 1, encode I on N bits
|
299
|
+
# Else
|
300
|
+
# encode 2^N - 1 on N bits
|
301
|
+
# I = I - (2^N - 1)
|
302
|
+
# While I >= 128
|
303
|
+
# Encode (I % 128 + 128) on 8 bits
|
304
|
+
# I = I / 128
|
305
|
+
# encode (I) on 8 bits
|
306
306
|
#
|
307
307
|
# @param i [Integer] value to encode
|
308
308
|
# @param n [Integer] number of available bits
|
@@ -315,21 +315,17 @@ module HTTP2
|
|
315
315
|
bytes.push limit if !n.zero?
|
316
316
|
|
317
317
|
i -= limit
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
q, r = i.divmod(128)
|
322
|
-
r += 128 if (q > 0)
|
323
|
-
i = q
|
324
|
-
|
325
|
-
bytes.push(r)
|
318
|
+
while (i >= 128) do
|
319
|
+
bytes.push((i % 128) + 128)
|
320
|
+
i = i / 128
|
326
321
|
end
|
327
322
|
|
323
|
+
bytes.push i
|
328
324
|
bytes.pack('C*')
|
329
325
|
end
|
330
326
|
|
331
327
|
# Encodes provided value via string literal representation.
|
332
|
-
# - http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-
|
328
|
+
# - http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-03#section-4.1.3
|
333
329
|
#
|
334
330
|
# * The string length, defined as the number of bytes needed to store
|
335
331
|
# its UTF-8 representation, is represented as an integer with a zero
|
@@ -340,15 +336,15 @@ module HTTP2
|
|
340
336
|
# @param str [String]
|
341
337
|
# @return [String] binary string
|
342
338
|
def string(str)
|
343
|
-
integer(str.bytesize, 0)
|
339
|
+
integer(str.bytesize, 0) << str.dup.force_encoding('binary')
|
344
340
|
end
|
345
341
|
|
346
342
|
# Encodes header command with appropriate header representation.
|
347
|
-
# - http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-
|
343
|
+
# - http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-03#section-4
|
348
344
|
#
|
349
345
|
# @param h [Hash] header command
|
350
346
|
# @param buffer [String]
|
351
|
-
def header(h, buffer =
|
347
|
+
def header(h, buffer = Buffer.new)
|
352
348
|
rep = HEADREP[h[:type]]
|
353
349
|
|
354
350
|
if h[:type] == :indexed
|
@@ -383,29 +379,35 @@ module HTTP2
|
|
383
379
|
# Encodes provided list of HTTP headers.
|
384
380
|
#
|
385
381
|
# @param headers [Hash]
|
386
|
-
# @return [
|
382
|
+
# @return [Buffer]
|
387
383
|
def encode(headers)
|
384
|
+
buffer = Buffer.new
|
388
385
|
commands = []
|
389
|
-
@cc.update_sets
|
390
386
|
|
391
|
-
#
|
392
|
-
|
387
|
+
# Literal header names MUST be translated to lowercase before
|
388
|
+
# encoding and transmission.
|
389
|
+
headers.map! {|(hk,hv)| [hk.downcase, hv] }
|
390
|
+
|
391
|
+
# Generate remove commands for missing headers
|
392
|
+
@cc.refset.each do |idx, (wk,wv)|
|
393
393
|
if headers.find {|(hk,hv)| hk == wk && hv == wv }.nil?
|
394
394
|
commands.push @cc.removecmd idx
|
395
395
|
end
|
396
396
|
end
|
397
397
|
|
398
|
-
#
|
398
|
+
# Generate add commands for new headers
|
399
399
|
headers.each do |(hk,hv)|
|
400
|
-
if @cc.
|
400
|
+
if @cc.refset.find {|i,(wk,wv)| hk == wk && hv == wv}.nil?
|
401
401
|
commands.push @cc.addcmd [hk, hv]
|
402
402
|
end
|
403
403
|
end
|
404
404
|
|
405
|
-
commands.
|
405
|
+
commands.each do |cmd|
|
406
406
|
@cc.process cmd.dup
|
407
|
-
header
|
408
|
-
end
|
407
|
+
buffer << header(cmd)
|
408
|
+
end
|
409
|
+
|
410
|
+
buffer
|
409
411
|
end
|
410
412
|
end
|
411
413
|
|
@@ -418,7 +420,7 @@ module HTTP2
|
|
418
420
|
# client_role = Decompressor.new(:response)
|
419
421
|
class Decompressor
|
420
422
|
def initialize(type)
|
421
|
-
@cc =
|
423
|
+
@cc = EncodingContext.new(type)
|
422
424
|
end
|
423
425
|
|
424
426
|
# Decodes integer value from provided buffer.
|
@@ -430,7 +432,7 @@ module HTTP2
|
|
430
432
|
i = !n.zero? ? (buf.getbyte & limit) : 0
|
431
433
|
|
432
434
|
m = 0
|
433
|
-
buf.
|
435
|
+
while byte = buf.getbyte do
|
434
436
|
i += ((byte & 127) << m)
|
435
437
|
m += 7
|
436
438
|
|
@@ -450,10 +452,9 @@ module HTTP2
|
|
450
452
|
|
451
453
|
# Decodes header command from provided buffer.
|
452
454
|
#
|
453
|
-
# @param buf [
|
455
|
+
# @param buf [Buffer]
|
454
456
|
def header(buf)
|
455
|
-
peek = buf.
|
456
|
-
buf.seek(-1, IO::SEEK_CUR)
|
457
|
+
peek = buf.readbyte(0)
|
457
458
|
|
458
459
|
header = {}
|
459
460
|
header[:type], type = HEADREP.select do |t, desc|
|
@@ -481,11 +482,26 @@ module HTTP2
|
|
481
482
|
|
482
483
|
# Decodes and processes header commands within provided buffer.
|
483
484
|
#
|
484
|
-
#
|
485
|
+
# Once all the representations contained in a header block have been
|
486
|
+
# processed, the headers that are in common with the previous header
|
487
|
+
# set are emitted, during the reference set emission.
|
488
|
+
#
|
489
|
+
# For the reference set emission, each header contained in the
|
490
|
+
# reference set that has not been emitted during the processing of the
|
491
|
+
# header block is emitted.
|
492
|
+
#
|
493
|
+
# - http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-03#section-3.2.2
|
494
|
+
#
|
495
|
+
# @param buf [Buffer]
|
496
|
+
# @return [Array] set of HTTP headers
|
485
497
|
def decode(buf)
|
486
|
-
|
487
|
-
@cc.process(header(buf)) while !buf.
|
488
|
-
@cc.
|
498
|
+
set = []
|
499
|
+
set << @cc.process(header(buf)) while !buf.empty?
|
500
|
+
@cc.refset.each do |i,header|
|
501
|
+
set << header if !set.include? header
|
502
|
+
end
|
503
|
+
|
504
|
+
set.compact
|
489
505
|
end
|
490
506
|
end
|
491
507
|
|