http-2 0.12.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -5,16 +5,28 @@ module HTTP2
5
5
  # be split and / or may be buffered based on current flow control window.
6
6
  #
7
7
  module FlowBuffer
8
+ include Error
9
+
10
+ MAX_WINDOW_SIZE = (2 << 30) - 1
11
+
8
12
  # Amount of buffered data. Only DATA payloads are subject to flow stream
9
13
  # and connection flow control.
10
14
  #
11
15
  # @return [Integer]
12
16
  def buffered_amount
13
- @send_buffer.map { |f| f[:length] }.reduce(:+) || 0
17
+ send_buffer.bytesize
18
+ end
19
+
20
+ def flush
21
+ send_data
14
22
  end
15
23
 
16
24
  private
17
25
 
26
+ def send_buffer
27
+ @send_buffer ||= FrameBuffer.new
28
+ end
29
+
18
30
  def update_local_window(frame)
19
31
  frame_size = frame[:payload].bytesize
20
32
  frame_size += frame[:padding] || 0
@@ -26,7 +38,7 @@ module HTTP2
26
38
  # current received window size + delta length is strictly larger than
27
39
  # local window size, it throws a flow control error.
28
40
  #
29
- error(:flow_control_error) if @local_window.negative?
41
+ error(:flow_control_error) if @local_window < 0
30
42
 
31
43
  # Send WINDOW_UPDATE if the received window size goes over
32
44
  # the local window size / 2.
@@ -55,51 +67,92 @@ module HTTP2
55
67
  # Buffered DATA frames are emitted in FIFO order.
56
68
  #
57
69
  # @param frame [Hash]
58
- # @param encode [Boolean] set to true by co
70
+ # @param encode [Boolean] set to true by connection
59
71
  def send_data(frame = nil, encode = false)
60
- @send_buffer.push frame unless frame.nil?
61
-
62
- # FIXME: Frames with zero length with the END_STREAM flag set (that
63
- # is, an empty DATA frame) MAY be sent if there is no available space
64
- # in either flow control window.
65
- while @remote_window.positive? && !@send_buffer.empty?
66
- frame = @send_buffer.shift
72
+ send_buffer << frame unless frame.nil?
67
73
 
68
- sent = 0
69
- frame_size = frame[:payload].bytesize
74
+ while (frame = send_buffer.retrieve(@remote_window))
70
75
 
71
- if frame_size > @remote_window
72
- payload = frame.delete(:payload)
73
- chunk = frame.dup
74
-
75
- # Split frame so that it fits in the window
76
- # TODO: consider padding!
77
- frame[:payload] = payload.slice!(0, @remote_window)
78
- chunk[:length] = payload.bytesize
79
- chunk[:payload] = payload
80
-
81
- # if no longer last frame in sequence...
82
- frame[:flags] -= [:end_stream] if frame[:flags].include? :end_stream
83
-
84
- @send_buffer.unshift chunk
85
- sent = @remote_window
86
- else
87
- sent = frame_size
88
- end
76
+ # puts "#{self.class} -> #{@remote_window}"
77
+ sent = frame[:payload].bytesize
89
78
 
90
79
  manage_state(frame) do
91
- frames = encode ? encode(frame) : [frame]
92
- frames.each { |f| emit(:frame, f) }
80
+ if encode
81
+ encode(frame).each { |f| emit(:frame, f) }
82
+ else
83
+ emit(:frame, frame)
84
+ end
93
85
  @remote_window -= sent
94
86
  end
95
87
  end
96
88
  end
97
89
 
98
- def process_window_update(frame)
90
+ def process_window_update(frame:, encode: false)
99
91
  return if frame[:ignore]
100
92
 
101
- @remote_window += frame[:increment]
102
- send_data
93
+ if frame[:increment]
94
+ raise ProtocolError, "increment MUST be higher than zero" if frame[:increment].zero?
95
+
96
+ @remote_window += frame[:increment]
97
+ error(:flow_control_error, msg: "window size too large") if @remote_window > MAX_WINDOW_SIZE
98
+ end
99
+ send_data(nil, encode)
100
+ end
101
+ end
102
+
103
+ class FrameBuffer
104
+ attr_reader :bytesize
105
+
106
+ def initialize
107
+ @buffer = []
108
+ @bytesize = 0
109
+ end
110
+
111
+ def <<(frame)
112
+ @buffer << frame
113
+ @bytesize += frame[:payload].bytesize
114
+ end
115
+
116
+ def empty?
117
+ @bytesize.zero?
118
+ end
119
+
120
+ def retrieve(window_size)
121
+ frame = @buffer.first or return
122
+
123
+ frame_size = frame[:payload].bytesize
124
+ end_stream = frame[:flags].include? :end_stream
125
+
126
+ # Frames with zero length with the END_STREAM flag set (that
127
+ # is, an empty DATA frame) MAY be sent if there is no available space
128
+ # in either flow control window.
129
+ return if window_size <= 0 && !(frame_size == 0 && end_stream)
130
+
131
+ @buffer.shift
132
+
133
+ if frame_size > window_size
134
+ payload = frame[:payload]
135
+ chunk = frame.dup
136
+
137
+ # Split frame so that it fits in the window
138
+ # TODO: consider padding!
139
+ frame_bytes = payload.byteslice(0, window_size)
140
+ payload = payload.byteslice(window_size..-1)
141
+
142
+ frame[:payload] = frame_bytes
143
+ frame[:length] = frame_bytes.bytesize
144
+ chunk[:payload] = payload
145
+ chunk[:length] = payload.bytesize
146
+
147
+ # if no longer last frame in sequence...
148
+ frame[:flags] -= [:end_stream] if end_stream
149
+
150
+ @buffer.unshift(chunk)
151
+ @bytesize -= window_size
152
+ else
153
+ @bytesize -= frame_size
154
+ end
155
+ frame
103
156
  end
104
157
  end
105
158
  end
data/lib/http/2/framer.rb CHANGED
@@ -4,13 +4,16 @@ module HTTP2
4
4
  # Performs encoding, decoding, and validation of binary HTTP/2 frames.
5
5
  #
6
6
  class Framer
7
+ using StringExtensions
8
+
7
9
  include Error
10
+ include PackingExtensions
8
11
 
9
12
  # Default value of max frame size (16384 bytes)
10
- DEFAULT_MAX_FRAME_SIZE = 2**14
13
+ DEFAULT_MAX_FRAME_SIZE = 2 << 13
11
14
 
12
- # Current maximum frame size
13
- attr_accessor :max_frame_size
15
+ # maximum frame size
16
+ attr_accessor :local_max_frame_size, :remote_max_frame_size
14
17
 
15
18
  # Maximum stream ID (2^31)
16
19
  MAX_STREAM_ID = 0x7fffffff
@@ -30,7 +33,8 @@ module HTTP2
30
33
  goaway: 0x7,
31
34
  window_update: 0x8,
32
35
  continuation: 0x9,
33
- altsvc: 0xa
36
+ altsvc: 0xa,
37
+ origin: 0xc
34
38
  }.freeze
35
39
 
36
40
  FRAME_TYPES_WITH_PADDING = %i[data headers push_promise].freeze
@@ -59,7 +63,13 @@ module HTTP2
59
63
  goaway: {},
60
64
  window_update: {},
61
65
  continuation: { end_headers: 2 },
62
- altsvc: {}
66
+ altsvc: {},
67
+ origin: {
68
+ reserved: 1,
69
+ reserved2: 2,
70
+ reserved3: 4,
71
+ reserved4: 8
72
+ }
63
73
  }.each_value(&:freeze).freeze
64
74
 
65
75
  # Default settings as defined by the spec
@@ -93,9 +103,9 @@ module HTTP2
93
103
  RBIT = 0x7fffffff
94
104
  RBYTE = 0x0fffffff
95
105
  EBIT = 0x80000000
96
- UINT32 = 'N'
97
- UINT16 = 'n'
98
- UINT8 = 'C'
106
+ UINT32 = "N"
107
+ UINT16 = "n"
108
+ UINT8 = "C"
99
109
  HEADERPACK = (UINT8 + UINT16 + UINT8 + UINT8 + UINT32).freeze
100
110
  FRAME_LENGTH_HISHIFT = 16
101
111
  FRAME_LENGTH_LOMASK = 0xFFFF
@@ -104,23 +114,24 @@ module HTTP2
104
114
 
105
115
  # Initializes new framer object.
106
116
  #
107
- def initialize
108
- @max_frame_size = DEFAULT_MAX_FRAME_SIZE
117
+ def initialize(local_max_frame_size = DEFAULT_MAX_FRAME_SIZE,
118
+ remote_max_frame_size = DEFAULT_MAX_FRAME_SIZE)
119
+ @local_max_frame_size = local_max_frame_size
120
+ @remote_max_frame_size = remote_max_frame_size
109
121
  end
110
122
 
111
123
  # Generates common 9-byte frame header.
112
124
  # - http://tools.ietf.org/html/draft-ietf-httpbis-http2-16#section-4.1
113
125
  #
114
126
  # @param frame [Hash]
127
+ # @param buffer [String] buffer to pack bytes into
115
128
  # @return [String]
116
- def common_header(frame)
117
- header = []
118
-
129
+ def common_header(frame, buffer:)
119
130
  raise CompressionError, "Invalid frame type (#{frame[:type]})" unless FRAME_TYPES[frame[:type]]
120
131
 
121
- raise CompressionError, "Frame size is too large: #{frame[:length]}" if frame[:length] > @max_frame_size
132
+ raise CompressionError, "Frame size is too large: #{frame[:length]}" if frame[:length] > @remote_max_frame_size
122
133
 
123
- raise CompressionError, "Frame size is invalid: #{frame[:length]}" if (frame[:length]).negative?
134
+ raise CompressionError, "Frame size is invalid: #{frame[:length]}" if frame[:length] < 0
124
135
 
125
136
  raise CompressionError, "Stream ID (#{frame[:stream]}) is too large" if frame[:stream] > MAX_STREAM_ID
126
137
 
@@ -128,32 +139,33 @@ module HTTP2
128
139
  raise CompressionError, "Window increment (#{frame[:increment]}) is too large"
129
140
  end
130
141
 
131
- header << (frame[:length] >> FRAME_LENGTH_HISHIFT)
132
- header << (frame[:length] & FRAME_LENGTH_LOMASK)
133
- header << FRAME_TYPES[frame[:type]]
134
- header << frame[:flags].reduce(0) do |acc, f|
135
- position = FRAME_FLAGS[frame[:type]][f]
136
- raise CompressionError, "Invalid frame flag (#{f}) for #{frame[:type]}" unless position
137
-
138
- acc | (1 << position)
139
- end
140
-
141
- header << frame[:stream]
142
- header.pack(HEADERPACK) # 8+16,8,8,32
142
+ pack([
143
+ (frame[:length] >> FRAME_LENGTH_HISHIFT),
144
+ (frame[:length] & FRAME_LENGTH_LOMASK),
145
+ FRAME_TYPES[frame[:type]],
146
+ frame[:flags].reduce(0) do |acc, f|
147
+ position = FRAME_FLAGS[frame[:type]][f]
148
+ raise CompressionError, "Invalid frame flag (#{f}) for #{frame[:type]}" unless position
149
+
150
+ acc | (1 << position)
151
+ end,
152
+ frame[:stream]
153
+ ], HEADERPACK, buffer: buffer, offset: 0) # 8+16,8,8,32
143
154
  end
144
155
 
145
156
  # Decodes common 9-byte header.
146
157
  #
147
158
  # @param buf [Buffer]
159
+ # @return [Hash] the corresponding frame
148
160
  def read_common_header(buf)
149
161
  frame = {}
150
- len_hi, len_lo, type, flags, stream = buf.slice(0, 9).unpack(HEADERPACK)
162
+ len_hi, len_lo, type, flags, stream = buf.byteslice(0, 9).unpack(HEADERPACK)
151
163
 
152
164
  frame[:length] = (len_hi << FRAME_LENGTH_HISHIFT) | len_lo
153
165
  frame[:type], = FRAME_TYPES.find { |_t, pos| type == pos }
154
166
  if frame[:type]
155
167
  frame[:flags] = FRAME_FLAGS[frame[:type]].each_with_object([]) do |(name, pos), acc|
156
- acc << name if (flags & (1 << pos)).positive?
168
+ acc << name if (flags & (1 << pos)) > 0
157
169
  end
158
170
  end
159
171
 
@@ -166,7 +178,7 @@ module HTTP2
166
178
  #
167
179
  # @param frame [Hash]
168
180
  def generate(frame)
169
- bytes = Buffer.new
181
+ bytes = "".b
170
182
  length = 0
171
183
 
172
184
  frame[:flags] ||= []
@@ -175,11 +187,12 @@ module HTTP2
175
187
  case frame[:type]
176
188
  when :data
177
189
  bytes << frame[:payload]
190
+ bytes.force_encoding(Encoding::BINARY)
178
191
  length += frame[:payload].bytesize
179
192
 
180
193
  when :headers
181
- if frame[:weight] || frame[:stream_dependency] || !frame[:exclusive].nil?
182
- unless frame[:weight] && frame[:stream_dependency] && !frame[:exclusive].nil?
194
+ if frame[:weight] || frame[:dependency] || !frame[:exclusive].nil?
195
+ unless frame[:weight] && frame[:dependency] && !frame[:exclusive].nil?
183
196
  raise CompressionError, "Must specify all of priority parameters for #{frame[:type]}"
184
197
  end
185
198
 
@@ -187,8 +200,8 @@ module HTTP2
187
200
  end
188
201
 
189
202
  if frame[:flags].include? :priority
190
- bytes << [(frame[:exclusive] ? EBIT : 0) | (frame[:stream_dependency] & RBIT)].pack(UINT32)
191
- bytes << [frame[:weight] - 1].pack(UINT8)
203
+ pack([(frame[:exclusive] ? EBIT : 0) | (frame[:dependency] & RBIT)], UINT32, buffer: bytes)
204
+ pack([frame[:weight] - 1], UINT8, buffer: bytes)
192
205
  length += 5
193
206
  end
194
207
 
@@ -196,23 +209,23 @@ module HTTP2
196
209
  length += frame[:payload].bytesize
197
210
 
198
211
  when :priority
199
- unless frame[:weight] && frame[:stream_dependency] && !frame[:exclusive].nil?
212
+ unless frame[:weight] && frame[:dependency] && !frame[:exclusive].nil?
200
213
  raise CompressionError, "Must specify all of priority parameters for #{frame[:type]}"
201
214
  end
202
215
 
203
- bytes << [(frame[:exclusive] ? EBIT : 0) | (frame[:stream_dependency] & RBIT)].pack(UINT32)
204
- bytes << [frame[:weight] - 1].pack(UINT8)
216
+ pack([(frame[:exclusive] ? EBIT : 0) | (frame[:dependency] & RBIT)], UINT32, buffer: bytes)
217
+ pack([frame[:weight] - 1], UINT8, buffer: bytes)
205
218
  length += 5
206
219
 
207
220
  when :rst_stream
208
- bytes << pack_error(frame[:error])
221
+ pack_error(frame[:error], buffer: bytes)
209
222
  length += 4
210
223
 
211
224
  when :settings
212
225
  raise CompressionError, "Invalid stream ID (#{frame[:stream]})" if (frame[:stream]).nonzero?
213
226
 
214
227
  frame[:payload].each do |(k, v)|
215
- if k.is_a? Integer
228
+ if k.is_a? Integer # rubocop:disable Style/GuardClause
216
229
  DEFINED_SETTINGS.value?(k) || next
217
230
  else
218
231
  k = DEFINED_SETTINGS[k]
@@ -220,27 +233,25 @@ module HTTP2
220
233
  raise CompressionError, "Unknown settings ID for #{k}" if k.nil?
221
234
  end
222
235
 
223
- bytes << [k].pack(UINT16)
224
- bytes << [v].pack(UINT32)
236
+ pack([k], UINT16, buffer: bytes)
237
+ pack([v], UINT32, buffer: bytes)
225
238
  length += 6
226
239
  end
227
240
 
228
241
  when :push_promise
229
- bytes << [frame[:promise_stream] & RBIT].pack(UINT32)
242
+ pack([frame[:promise_stream] & RBIT], UINT32, buffer: bytes)
230
243
  bytes << frame[:payload]
231
244
  length += 4 + frame[:payload].bytesize
232
245
 
233
246
  when :ping
234
- if frame[:payload].bytesize != 8
235
- raise CompressionError, "Invalid payload size (#{frame[:payload].size} != 8 bytes)"
236
- end
247
+ raise CompressionError, "Invalid payload size (#{frame[:payload].size} != 8 bytes)" if frame[:payload].bytesize != 8
237
248
 
238
249
  bytes << frame[:payload]
239
250
  length += 8
240
251
 
241
252
  when :goaway
242
- bytes << [frame[:last_stream] & RBIT].pack(UINT32)
243
- bytes << pack_error(frame[:error])
253
+ pack([frame[:last_stream] & RBIT], UINT32, buffer: bytes)
254
+ pack_error(frame[:error], buffer: bytes)
244
255
  length += 8
245
256
 
246
257
  if frame[:payload]
@@ -249,7 +260,7 @@ module HTTP2
249
260
  end
250
261
 
251
262
  when :window_update
252
- bytes << [frame[:increment] & RBIT].pack(UINT32)
263
+ pack([frame[:increment] & RBIT], UINT32, buffer: bytes)
253
264
  length += 4
254
265
 
255
266
  when :continuation
@@ -257,32 +268,40 @@ module HTTP2
257
268
  length += frame[:payload].bytesize
258
269
 
259
270
  when :altsvc
260
- bytes << [frame[:max_age], frame[:port]].pack(UINT32 + UINT16)
271
+ pack([frame[:max_age], frame[:port]], UINT32 + UINT16, buffer: bytes)
261
272
  length += 6
262
273
  if frame[:proto]
263
- raise CompressionError, 'Proto too long' if frame[:proto].bytesize > 255
274
+ raise CompressionError, "Proto too long" if frame[:proto].bytesize > 255
264
275
 
265
- bytes << [frame[:proto].bytesize].pack(UINT8)
266
- bytes << frame[:proto].force_encoding(Encoding::BINARY)
276
+ pack([frame[:proto].bytesize], UINT8, buffer: bytes)
277
+ bytes << frame[:proto]
267
278
  length += 1 + frame[:proto].bytesize
268
279
  else
269
- bytes << [0].pack(UINT8)
280
+ pack([0], UINT8, buffer: bytes)
270
281
  length += 1
271
282
  end
272
283
  if frame[:host]
273
- raise CompressionError, 'Host too long' if frame[:host].bytesize > 255
284
+ raise CompressionError, "Host too long" if frame[:host].bytesize > 255
274
285
 
275
- bytes << [frame[:host].bytesize].pack(UINT8)
276
- bytes << frame[:host].force_encoding(Encoding::BINARY)
286
+ pack([frame[:host].bytesize], UINT8, buffer: bytes)
287
+ bytes << frame[:host]
277
288
  length += 1 + frame[:host].bytesize
278
289
  else
279
- bytes << [0].pack(UINT8)
290
+ pack([0], UINT8, buffer: bytes)
280
291
  length += 1
281
292
  end
282
293
  if frame[:origin]
283
294
  bytes << frame[:origin]
284
295
  length += frame[:origin].bytesize
285
296
  end
297
+
298
+ when :origin
299
+ frame[:payload].each do |origin|
300
+ pack([origin.bytesize], UINT16, buffer: bytes)
301
+ bytes << origin
302
+ length += 2 + origin.bytesize
303
+ end
304
+
286
305
  end
287
306
 
288
307
  # Process padding.
@@ -295,12 +314,12 @@ module HTTP2
295
314
 
296
315
  padlen = frame[:padding]
297
316
 
298
- if padlen <= 0 || padlen > 256 || padlen + length > @max_frame_size
317
+ if padlen <= 0 || padlen > 256 || padlen + length > @remote_max_frame_size
299
318
  raise CompressionError, "Invalid padding #{padlen}"
300
319
  end
301
320
 
302
321
  length += padlen
303
- bytes.prepend([padlen -= 1].pack(UINT8))
322
+ pack([padlen -= 1], UINT8, buffer: bytes, offset: 0)
304
323
  frame[:flags] << :padded
305
324
 
306
325
  # Padding: Padding octets that contain no application semantic value.
@@ -310,7 +329,7 @@ module HTTP2
310
329
  end
311
330
 
312
331
  frame[:length] = length
313
- bytes.prepend(common_header(frame))
332
+ common_header(frame, buffer: bytes)
314
333
  end
315
334
 
316
335
  # Decodes complete HTTP/2 frame from provided buffer. If the buffer
@@ -318,12 +337,12 @@ module HTTP2
318
337
  #
319
338
  # @param buf [Buffer]
320
339
  def parse(buf)
321
- return nil if buf.size < 9
340
+ return if buf.size < 9
322
341
 
323
342
  frame = read_common_header(buf)
324
- return nil if buf.size < 9 + frame[:length]
343
+ return if buf.size < 9 + frame[:length]
325
344
 
326
- raise ProtocolError, 'payload too large' if frame[:length] > DEFAULT_MAX_FRAME_SIZE
345
+ raise ProtocolError, "payload too large" if frame[:length] > @local_max_frame_size
327
346
 
328
347
  buf.read(9)
329
348
  payload = buf.read(frame[:length])
@@ -331,7 +350,7 @@ module HTTP2
331
350
  # Implementations MUST discard frames
332
351
  # that have unknown or unsupported types.
333
352
  # - http://tools.ietf.org/html/draft-ietf-httpbis-http2-16#section-5.5
334
- return nil if frame[:type].nil?
353
+ return frame if frame[:type].nil?
335
354
 
336
355
  # Process padding
337
356
  padlen = 0
@@ -340,38 +359,46 @@ module HTTP2
340
359
  if padded
341
360
  padlen = payload.read(1).unpack1(UINT8)
342
361
  frame[:padding] = padlen + 1
343
- raise ProtocolError, 'padding too long' if padlen > payload.bytesize
362
+ raise ProtocolError, "padding too long" if padlen > payload.bytesize
344
363
 
345
- payload.slice!(-padlen, padlen) if padlen.positive?
364
+ payload = payload.byteslice(0, payload.bytesize - padlen) if padlen > 0
346
365
  frame[:length] -= frame[:padding]
347
366
  frame[:flags].delete(:padded)
348
367
  end
349
368
  end
350
369
 
351
370
  case frame[:type]
352
- when :data
371
+ when :data, :ping, :continuation
353
372
  frame[:payload] = payload.read(frame[:length])
354
373
  when :headers
355
374
  if frame[:flags].include? :priority
356
375
  e_sd = payload.read_uint32
357
- frame[:stream_dependency] = e_sd & RBIT
376
+ frame[:dependency] = e_sd & RBIT
358
377
  frame[:exclusive] = (e_sd & EBIT) != 0
359
- frame[:weight] = payload.getbyte + 1
378
+ weight = payload.byteslice(0, 1).ord + 1
379
+ frame[:weight] = weight
380
+ payload = payload.byteslice(1..-1)
360
381
  end
361
382
  frame[:payload] = payload.read(frame[:length])
362
383
  when :priority
384
+ raise FrameSizeError, "Invalid length for PRIORITY_STREAM (#{frame[:length]} != 5)" if frame[:length] != 5
385
+
363
386
  e_sd = payload.read_uint32
364
- frame[:stream_dependency] = e_sd & RBIT
387
+ frame[:dependency] = e_sd & RBIT
365
388
  frame[:exclusive] = (e_sd & EBIT) != 0
366
- frame[:weight] = payload.getbyte + 1
389
+ weight = payload.byteslice(0, 1).ord + 1
390
+ frame[:weight] = weight
391
+ payload = payload.byteslice(1..-1)
367
392
  when :rst_stream
393
+ raise FrameSizeError, "Invalid length for RST_STREAM (#{frame[:length]} != 4)" if frame[:length] != 4
394
+
368
395
  frame[:error] = unpack_error payload.read_uint32
369
396
 
370
397
  when :settings
371
398
  # NOTE: frame[:length] might not match the number of frame[:payload]
372
399
  # because unknown extensions are ignored.
373
400
  frame[:payload] = []
374
- raise ProtocolError, 'Invalid settings payload length' unless (frame[:length] % 6).zero?
401
+ raise ProtocolError, "Invalid settings payload length" unless (frame[:length] % 6).zero?
375
402
 
376
403
  raise ProtocolError, "Invalid stream ID (#{frame[:stream]})" if (frame[:stream]).nonzero?
377
404
 
@@ -387,28 +414,40 @@ module HTTP2
387
414
  when :push_promise
388
415
  frame[:promise_stream] = payload.read_uint32 & RBIT
389
416
  frame[:payload] = payload.read(frame[:length])
390
- when :ping
391
- frame[:payload] = payload.read(frame[:length])
392
417
  when :goaway
393
418
  frame[:last_stream] = payload.read_uint32 & RBIT
394
419
  frame[:error] = unpack_error payload.read_uint32
395
420
 
396
421
  size = frame[:length] - 8 # for last_stream and error
397
- frame[:payload] = payload.read(size) if size.positive?
422
+ frame[:payload] = payload.read(size) if size > 0
398
423
  when :window_update
424
+ if frame[:length] % 4 != 0
425
+ raise FrameSizeError, "Invalid length for WINDOW_UPDATE (#{frame[:length]} not multiple of 4)"
426
+ end
427
+
399
428
  frame[:increment] = payload.read_uint32 & RBIT
400
- when :continuation
401
- frame[:payload] = payload.read(frame[:length])
402
429
  when :altsvc
403
430
  frame[:max_age], frame[:port] = payload.read(6).unpack(UINT32 + UINT16)
404
431
 
405
- len = payload.getbyte
406
- frame[:proto] = payload.read(len) if len.positive?
432
+ len = payload.byteslice(0, 1).ord
433
+ payload = payload.byteslice(1..-1)
434
+ frame[:proto] = payload.read(len) if len > 0
407
435
 
408
- len = payload.getbyte
409
- frame[:host] = payload.read(len) if len.positive?
436
+ len = payload.byteslice(0, 1).ord
437
+ payload = payload.byteslice(1..-1)
438
+ frame[:host] = payload.read(len) if len > 0
410
439
 
411
440
  frame[:origin] = payload.read(payload.size) unless payload.empty?
441
+
442
+ when :origin
443
+ origins = []
444
+
445
+ until payload.empty?
446
+ len = payload.read(2).unpack1(UINT16)
447
+ origins << payload.read(len)
448
+ end
449
+
450
+ frame[:payload] = origins
412
451
  # else # Unknown frame type is explicitly allowed
413
452
  end
414
453
 
@@ -417,19 +456,18 @@ module HTTP2
417
456
 
418
457
  private
419
458
 
420
- def pack_error(e)
421
- unless e.is_a? Integer
422
- raise CompressionError, "Unknown error ID for #{e}" if DEFINED_ERRORS[e].nil?
459
+ def pack_error(error, buffer:)
460
+ unless error.is_a? Integer
461
+ error = DEFINED_ERRORS[error]
423
462
 
424
- e = DEFINED_ERRORS[e]
463
+ raise CompressionError, "Unknown error ID for #{error}" unless error
425
464
  end
426
465
 
427
- [e].pack(UINT32)
466
+ pack([error], UINT32, buffer: buffer)
428
467
  end
429
468
 
430
- def unpack_error(e)
431
- name, = DEFINED_ERRORS.find { |_name, v| v == e }
432
- name || error
469
+ def unpack_error(error)
470
+ DEFINED_ERRORS.key(error) || error
433
471
  end
434
472
  end
435
473
  end