http-2 0.12.0 → 1.0.2

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