http-2 0.12.0 → 1.0.0

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.
@@ -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