http-2 0.11.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (78) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +10 -9
  3. data/lib/http/2/base64.rb +45 -0
  4. data/lib/http/2/client.rb +19 -6
  5. data/lib/http/2/connection.rb +235 -163
  6. data/lib/http/2/emitter.rb +7 -5
  7. data/lib/http/2/error.rb +24 -1
  8. data/lib/http/2/extensions.rb +53 -0
  9. data/lib/http/2/flow_buffer.rb +91 -33
  10. data/lib/http/2/framer.rb +184 -157
  11. data/lib/http/2/header/compressor.rb +157 -0
  12. data/lib/http/2/header/decompressor.rb +144 -0
  13. data/lib/http/2/header/encoding_context.rb +337 -0
  14. data/lib/http/2/{huffman.rb → header/huffman.rb} +25 -19
  15. data/lib/http/2/{huffman_statemachine.rb → header/huffman_statemachine.rb} +2 -0
  16. data/lib/http/2/header.rb +35 -0
  17. data/lib/http/2/server.rb +47 -20
  18. data/lib/http/2/stream.rb +130 -61
  19. data/lib/http/2/version.rb +3 -1
  20. data/lib/http/2.rb +14 -13
  21. data/sig/client.rbs +9 -0
  22. data/sig/connection.rbs +93 -0
  23. data/sig/emitter.rbs +13 -0
  24. data/sig/error.rbs +35 -0
  25. data/sig/extensions.rbs +5 -0
  26. data/sig/flow_buffer.rbs +21 -0
  27. data/sig/frame_buffer.rbs +13 -0
  28. data/sig/framer.rbs +54 -0
  29. data/sig/header/compressor.rbs +27 -0
  30. data/sig/header/decompressor.rbs +22 -0
  31. data/sig/header/encoding_context.rbs +34 -0
  32. data/sig/header/huffman.rbs +9 -0
  33. data/sig/header.rbs +27 -0
  34. data/sig/next.rbs +101 -0
  35. data/sig/server.rbs +12 -0
  36. data/sig/stream.rbs +91 -0
  37. metadata +38 -79
  38. data/.autotest +0 -20
  39. data/.coveralls.yml +0 -1
  40. data/.gitignore +0 -20
  41. data/.gitmodules +0 -3
  42. data/.rspec +0 -5
  43. data/.rubocop.yml +0 -93
  44. data/.rubocop_todo.yml +0 -131
  45. data/.travis.yml +0 -17
  46. data/Gemfile +0 -16
  47. data/Guardfile +0 -18
  48. data/Guardfile.h2spec +0 -12
  49. data/LICENSE +0 -21
  50. data/Rakefile +0 -49
  51. data/example/Gemfile +0 -3
  52. data/example/README.md +0 -44
  53. data/example/client.rb +0 -122
  54. data/example/helper.rb +0 -19
  55. data/example/keys/server.crt +0 -20
  56. data/example/keys/server.key +0 -27
  57. data/example/server.rb +0 -139
  58. data/example/upgrade_client.rb +0 -153
  59. data/example/upgrade_server.rb +0 -203
  60. data/http-2.gemspec +0 -22
  61. data/lib/http/2/buffer.rb +0 -76
  62. data/lib/http/2/compressor.rb +0 -572
  63. data/lib/tasks/generate_huffman_table.rb +0 -166
  64. data/spec/buffer_spec.rb +0 -28
  65. data/spec/client_spec.rb +0 -188
  66. data/spec/compressor_spec.rb +0 -666
  67. data/spec/connection_spec.rb +0 -681
  68. data/spec/emitter_spec.rb +0 -54
  69. data/spec/framer_spec.rb +0 -487
  70. data/spec/h2spec/h2spec.darwin +0 -0
  71. data/spec/h2spec/output/non_secure.txt +0 -317
  72. data/spec/helper.rb +0 -147
  73. data/spec/hpack_test_spec.rb +0 -84
  74. data/spec/huffman_spec.rb +0 -68
  75. data/spec/server_spec.rb +0 -52
  76. data/spec/stream_spec.rb +0 -878
  77. data/spec/support/deep_dup.rb +0 -55
  78. data/spec/support/duplicable.rb +0 -98
data/lib/http/2/framer.rb CHANGED
@@ -1,15 +1,19 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module HTTP2
2
4
  # Performs encoding, decoding, and validation of binary HTTP/2 frames.
3
5
  #
4
- # rubocop:disable ClassLength
5
6
  class Framer
7
+ using StringExtensions
8
+
6
9
  include Error
10
+ include PackingExtensions
7
11
 
8
12
  # Default value of max frame size (16384 bytes)
9
- DEFAULT_MAX_FRAME_SIZE = 2**14
13
+ DEFAULT_MAX_FRAME_SIZE = 2 << 13
10
14
 
11
- # Current maximum frame size
12
- attr_accessor :max_frame_size
15
+ # maximum frame size
16
+ attr_accessor :local_max_frame_size, :remote_max_frame_size
13
17
 
14
18
  # Maximum stream ID (2^31)
15
19
  MAX_STREAM_ID = 0x7fffffff
@@ -19,82 +23,89 @@ module HTTP2
19
23
 
20
24
  # HTTP/2 frame type mapping as defined by the spec
21
25
  FRAME_TYPES = {
22
- data: 0x0,
23
- headers: 0x1,
24
- priority: 0x2,
25
- rst_stream: 0x3,
26
- settings: 0x4,
27
- push_promise: 0x5,
28
- ping: 0x6,
29
- goaway: 0x7,
26
+ data: 0x0,
27
+ headers: 0x1,
28
+ priority: 0x2,
29
+ rst_stream: 0x3,
30
+ settings: 0x4,
31
+ push_promise: 0x5,
32
+ ping: 0x6,
33
+ goaway: 0x7,
30
34
  window_update: 0x8,
31
- continuation: 0x9,
32
- altsvc: 0xa,
35
+ continuation: 0x9,
36
+ altsvc: 0xa,
37
+ origin: 0xc
33
38
  }.freeze
34
39
 
35
- FRAME_TYPES_WITH_PADDING = [:data, :headers, :push_promise].freeze
40
+ FRAME_TYPES_WITH_PADDING = %i[data headers push_promise].freeze
36
41
 
37
42
  # Per frame flags as defined by the spec
38
43
  FRAME_FLAGS = {
39
44
  data: {
40
- end_stream: 0,
45
+ end_stream: 0,
41
46
  padded: 3,
42
- compressed: 5,
47
+ compressed: 5
43
48
  },
44
49
  headers: {
45
- end_stream: 0,
50
+ end_stream: 0,
46
51
  end_headers: 2,
47
52
  padded: 3,
48
- priority: 5,
53
+ priority: 5
49
54
  },
50
- priority: {},
51
- rst_stream: {},
52
- settings: { ack: 0 },
55
+ priority: {},
56
+ rst_stream: {},
57
+ settings: { ack: 0 },
53
58
  push_promise: {
54
59
  end_headers: 2,
55
- padded: 3,
60
+ padded: 3
56
61
  },
57
- ping: { ack: 0 },
58
- goaway: {},
62
+ ping: { ack: 0 },
63
+ goaway: {},
59
64
  window_update: {},
60
65
  continuation: { end_headers: 2 },
61
66
  altsvc: {},
67
+ origin: {
68
+ reserved: 1,
69
+ reserved2: 2,
70
+ reserved3: 4,
71
+ reserved4: 8
72
+ }
62
73
  }.each_value(&:freeze).freeze
63
74
 
64
75
  # Default settings as defined by the spec
65
76
  DEFINED_SETTINGS = {
66
- settings_header_table_size: 1,
67
- settings_enable_push: 2,
77
+ settings_header_table_size: 1,
78
+ settings_enable_push: 2,
68
79
  settings_max_concurrent_streams: 3,
69
- settings_initial_window_size: 4,
70
- settings_max_frame_size: 5,
71
- settings_max_header_list_size: 6,
80
+ settings_initial_window_size: 4,
81
+ settings_max_frame_size: 5,
82
+ settings_max_header_list_size: 6
72
83
  }.freeze
73
84
 
74
85
  # Default error types as defined by the spec
75
86
  DEFINED_ERRORS = {
76
- no_error: 0,
77
- protocol_error: 1,
78
- internal_error: 2,
87
+ no_error: 0,
88
+ protocol_error: 1,
89
+ internal_error: 2,
79
90
  flow_control_error: 3,
80
- settings_timeout: 4,
81
- stream_closed: 5,
82
- frame_size_error: 6,
83
- refused_stream: 7,
84
- cancel: 8,
85
- compression_error: 9,
86
- connect_error: 10,
87
- enhance_your_calm: 11,
91
+ settings_timeout: 4,
92
+ stream_closed: 5,
93
+ frame_size_error: 6,
94
+ refused_stream: 7,
95
+ cancel: 8,
96
+ compression_error: 9,
97
+ connect_error: 10,
98
+ enhance_your_calm: 11,
88
99
  inadequate_security: 12,
89
- http_1_1_required: 13,
100
+ http_1_1_required: 13
90
101
  }.freeze
91
102
 
92
103
  RBIT = 0x7fffffff
93
104
  RBYTE = 0x0fffffff
94
105
  EBIT = 0x80000000
95
- UINT32 = 'N'.freeze
96
- UINT16 = 'n'.freeze
97
- UINT8 = 'C'.freeze
106
+ UINT32 = "N"
107
+ UINT16 = "n"
108
+ UINT8 = "C"
98
109
  HEADERPACK = (UINT8 + UINT16 + UINT8 + UINT8 + UINT32).freeze
99
110
  FRAME_LENGTH_HISHIFT = 16
100
111
  FRAME_LENGTH_LOMASK = 0xFFFF
@@ -103,63 +114,55 @@ module HTTP2
103
114
 
104
115
  # Initializes new framer object.
105
116
  #
106
- def initialize
107
- @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
108
121
  end
109
122
 
110
123
  # Generates common 9-byte frame header.
111
124
  # - http://tools.ietf.org/html/draft-ietf-httpbis-http2-16#section-4.1
112
125
  #
113
126
  # @param frame [Hash]
127
+ # @param buffer [String] buffer to pack bytes into
114
128
  # @return [String]
115
- def common_header(frame)
116
- header = []
117
-
118
- unless FRAME_TYPES[frame[:type]]
119
- fail CompressionError, "Invalid frame type (#{frame[:type]})"
120
- end
129
+ def common_header(frame, buffer:)
130
+ raise CompressionError, "Invalid frame type (#{frame[:type]})" unless FRAME_TYPES[frame[:type]]
121
131
 
122
- if frame[:length] > @max_frame_size
123
- fail CompressionError, "Frame size is too large: #{frame[:length]}"
124
- end
132
+ raise CompressionError, "Frame size is too large: #{frame[:length]}" if frame[:length] > @remote_max_frame_size
125
133
 
126
- if frame[:length] < 0
127
- fail CompressionError, "Frame size is invalid: #{frame[:length]}"
128
- end
134
+ raise CompressionError, "Frame size is invalid: #{frame[:length]}" if frame[:length] < 0
129
135
 
130
- if frame[:stream] > MAX_STREAM_ID
131
- fail CompressionError, "Stream ID (#{frame[:stream]}) is too large"
132
- end
136
+ raise CompressionError, "Stream ID (#{frame[:stream]}) is too large" if frame[:stream] > MAX_STREAM_ID
133
137
 
134
138
  if frame[:type] == :window_update && frame[:increment] > MAX_WINDOWINC
135
- fail CompressionError, "Window increment (#{frame[:increment]}) is too large"
136
- end
137
-
138
- header << (frame[:length] >> FRAME_LENGTH_HISHIFT)
139
- header << (frame[:length] & FRAME_LENGTH_LOMASK)
140
- header << FRAME_TYPES[frame[:type]]
141
- header << frame[:flags].reduce(0) do |acc, f|
142
- position = FRAME_FLAGS[frame[:type]][f]
143
- unless position
144
- fail CompressionError, "Invalid frame flag (#{f}) for #{frame[:type]}"
145
- end
146
-
147
- acc | (1 << position)
139
+ raise CompressionError, "Window increment (#{frame[:increment]}) is too large"
148
140
  end
149
141
 
150
- header << frame[:stream]
151
- 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
152
154
  end
153
155
 
154
156
  # Decodes common 9-byte header.
155
157
  #
156
158
  # @param buf [Buffer]
159
+ # @return [Hash] the corresponding frame
157
160
  def read_common_header(buf)
158
161
  frame = {}
159
- 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)
160
163
 
161
164
  frame[:length] = (len_hi << FRAME_LENGTH_HISHIFT) | len_lo
162
- frame[:type], _ = FRAME_TYPES.find { |_t, pos| type == pos }
165
+ frame[:type], = FRAME_TYPES.find { |_t, pos| type == pos }
163
166
  if frame[:type]
164
167
  frame[:flags] = FRAME_FLAGS[frame[:type]].each_with_object([]) do |(name, pos), acc|
165
168
  acc << name if (flags & (1 << pos)) > 0
@@ -175,7 +178,7 @@ module HTTP2
175
178
  #
176
179
  # @param frame [Hash]
177
180
  def generate(frame)
178
- bytes = Buffer.new
181
+ bytes = "".b
179
182
  length = 0
180
183
 
181
184
  frame[:flags] ||= []
@@ -184,19 +187,21 @@ module HTTP2
184
187
  case frame[:type]
185
188
  when :data
186
189
  bytes << frame[:payload]
190
+ bytes.force_encoding(Encoding::BINARY)
187
191
  length += frame[:payload].bytesize
188
192
 
189
193
  when :headers
190
- if frame[:weight] || frame[:stream_dependency] || !frame[:exclusive].nil?
191
- unless frame[:weight] && frame[:stream_dependency] && !frame[:exclusive].nil?
192
- fail CompressionError, "Must specify all of priority parameters for #{frame[:type]}"
194
+ if frame[:weight] || frame[:dependency] || !frame[:exclusive].nil?
195
+ unless frame[:weight] && frame[:dependency] && !frame[:exclusive].nil?
196
+ raise CompressionError, "Must specify all of priority parameters for #{frame[:type]}"
193
197
  end
198
+
194
199
  frame[:flags] += [:priority] unless frame[:flags].include? :priority
195
200
  end
196
201
 
197
202
  if frame[:flags].include? :priority
198
- bytes << [(frame[:exclusive] ? EBIT : 0) | (frame[:stream_dependency] & RBIT)].pack(UINT32)
199
- 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)
200
205
  length += 5
201
206
  end
202
207
 
@@ -204,51 +209,49 @@ module HTTP2
204
209
  length += frame[:payload].bytesize
205
210
 
206
211
  when :priority
207
- unless frame[:weight] && frame[:stream_dependency] && !frame[:exclusive].nil?
208
- fail CompressionError, "Must specify all of priority parameters for #{frame[:type]}"
212
+ unless frame[:weight] && frame[:dependency] && !frame[:exclusive].nil?
213
+ raise CompressionError, "Must specify all of priority parameters for #{frame[:type]}"
209
214
  end
210
- bytes << [(frame[:exclusive] ? EBIT : 0) | (frame[:stream_dependency] & RBIT)].pack(UINT32)
211
- bytes << [frame[:weight] - 1].pack(UINT8)
215
+
216
+ pack([(frame[:exclusive] ? EBIT : 0) | (frame[:dependency] & RBIT)], UINT32, buffer: bytes)
217
+ pack([frame[:weight] - 1], UINT8, buffer: bytes)
212
218
  length += 5
213
219
 
214
220
  when :rst_stream
215
- bytes << pack_error(frame[:error])
221
+ pack_error(frame[:error], buffer: bytes)
216
222
  length += 4
217
223
 
218
224
  when :settings
219
- if (frame[:stream]).nonzero?
220
- fail CompressionError, "Invalid stream ID (#{frame[:stream]})"
221
- end
225
+ raise CompressionError, "Invalid stream ID (#{frame[:stream]})" if (frame[:stream]).nonzero?
222
226
 
223
227
  frame[:payload].each do |(k, v)|
224
- if k.is_a? Integer
228
+ if k.is_a? Integer # rubocop:disable Style/GuardClause
225
229
  DEFINED_SETTINGS.value?(k) || next
226
230
  else
227
231
  k = DEFINED_SETTINGS[k]
228
232
 
229
- fail CompressionError, "Unknown settings ID for #{k}" if k.nil?
233
+ raise CompressionError, "Unknown settings ID for #{k}" if k.nil?
230
234
  end
231
235
 
232
- bytes << [k].pack(UINT16)
233
- bytes << [v].pack(UINT32)
236
+ pack([k], UINT16, buffer: bytes)
237
+ pack([v], UINT32, buffer: bytes)
234
238
  length += 6
235
239
  end
236
240
 
237
241
  when :push_promise
238
- bytes << [frame[:promise_stream] & RBIT].pack(UINT32)
242
+ pack([frame[:promise_stream] & RBIT], UINT32, buffer: bytes)
239
243
  bytes << frame[:payload]
240
244
  length += 4 + frame[:payload].bytesize
241
245
 
242
246
  when :ping
243
- if frame[:payload].bytesize != 8
244
- fail CompressionError, "Invalid payload size (#{frame[:payload].size} != 8 bytes)"
245
- end
247
+ raise CompressionError, "Invalid payload size (#{frame[:payload].size} != 8 bytes)" if frame[:payload].bytesize != 8
248
+
246
249
  bytes << frame[:payload]
247
250
  length += 8
248
251
 
249
252
  when :goaway
250
- bytes << [frame[:last_stream] & RBIT].pack(UINT32)
251
- bytes << pack_error(frame[:error])
253
+ pack([frame[:last_stream] & RBIT], UINT32, buffer: bytes)
254
+ pack_error(frame[:error], buffer: bytes)
252
255
  length += 8
253
256
 
254
257
  if frame[:payload]
@@ -257,7 +260,7 @@ module HTTP2
257
260
  end
258
261
 
259
262
  when :window_update
260
- bytes << [frame[:increment] & RBIT].pack(UINT32)
263
+ pack([frame[:increment] & RBIT], UINT32, buffer: bytes)
261
264
  length += 4
262
265
 
263
266
  when :continuation
@@ -265,30 +268,40 @@ module HTTP2
265
268
  length += frame[:payload].bytesize
266
269
 
267
270
  when :altsvc
268
- bytes << [frame[:max_age], frame[:port]].pack(UINT32 + UINT16)
271
+ pack([frame[:max_age], frame[:port]], UINT32 + UINT16, buffer: bytes)
269
272
  length += 6
270
273
  if frame[:proto]
271
- fail CompressionError, 'Proto too long' if frame[:proto].bytesize > 255
272
- bytes << [frame[:proto].bytesize].pack(UINT8)
273
- bytes << frame[:proto].force_encoding(Encoding::BINARY)
274
+ raise CompressionError, "Proto too long" if frame[:proto].bytesize > 255
275
+
276
+ pack([frame[:proto].bytesize], UINT8, buffer: bytes)
277
+ bytes << frame[:proto]
274
278
  length += 1 + frame[:proto].bytesize
275
279
  else
276
- bytes << [0].pack(UINT8)
280
+ pack([0], UINT8, buffer: bytes)
277
281
  length += 1
278
282
  end
279
283
  if frame[:host]
280
- fail CompressionError, 'Host too long' if frame[:host].bytesize > 255
281
- bytes << [frame[:host].bytesize].pack(UINT8)
282
- bytes << frame[:host].force_encoding(Encoding::BINARY)
284
+ raise CompressionError, "Host too long" if frame[:host].bytesize > 255
285
+
286
+ pack([frame[:host].bytesize], UINT8, buffer: bytes)
287
+ bytes << frame[:host]
283
288
  length += 1 + frame[:host].bytesize
284
289
  else
285
- bytes << [0].pack(UINT8)
290
+ pack([0], UINT8, buffer: bytes)
286
291
  length += 1
287
292
  end
288
293
  if frame[:origin]
289
294
  bytes << frame[:origin]
290
295
  length += frame[:origin].bytesize
291
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
+
292
305
  end
293
306
 
294
307
  # Process padding.
@@ -296,27 +309,27 @@ module HTTP2
296
309
  # - http://tools.ietf.org/html/draft-ietf-httpbis-http2-16#section-6.1
297
310
  if frame[:padding]
298
311
  unless FRAME_TYPES_WITH_PADDING.include?(frame[:type])
299
- fail CompressionError, "Invalid padding flag for #{frame[:type]}"
312
+ raise CompressionError, "Invalid padding flag for #{frame[:type]}"
300
313
  end
301
314
 
302
315
  padlen = frame[:padding]
303
316
 
304
- if padlen <= 0 || padlen > 256 || padlen + length > @max_frame_size
305
- fail CompressionError, "Invalid padding #{padlen}"
317
+ if padlen <= 0 || padlen > 256 || padlen + length > @remote_max_frame_size
318
+ raise CompressionError, "Invalid padding #{padlen}"
306
319
  end
307
320
 
308
321
  length += padlen
309
- bytes.prepend([padlen -= 1].pack(UINT8))
322
+ pack([padlen -= 1], UINT8, buffer: bytes, offset: 0)
310
323
  frame[:flags] << :padded
311
324
 
312
325
  # Padding: Padding octets that contain no application semantic value.
313
326
  # Padding octets MUST be set to zero when sending and ignored when
314
327
  # receiving.
315
- bytes << "\0" * padlen
328
+ bytes << ("\0" * padlen)
316
329
  end
317
330
 
318
331
  frame[:length] = length
319
- bytes.prepend(common_header(frame))
332
+ common_header(frame, buffer: bytes)
320
333
  end
321
334
 
322
335
  # Decodes complete HTTP/2 frame from provided buffer. If the buffer
@@ -324,11 +337,12 @@ module HTTP2
324
337
  #
325
338
  # @param buf [Buffer]
326
339
  def parse(buf)
327
- return nil if buf.size < 9
340
+ return if buf.size < 9
341
+
328
342
  frame = read_common_header(buf)
329
- return nil if buf.size < 9 + frame[:length]
343
+ return if buf.size < 9 + frame[:length]
330
344
 
331
- fail ProtocolError, 'payload too large' if frame[:length] > DEFAULT_MAX_FRAME_SIZE
345
+ raise ProtocolError, "payload too large" if frame[:length] > @local_max_frame_size
332
346
 
333
347
  buf.read(9)
334
348
  payload = buf.read(frame[:length])
@@ -336,67 +350,70 @@ module HTTP2
336
350
  # Implementations MUST discard frames
337
351
  # that have unknown or unsupported types.
338
352
  # - http://tools.ietf.org/html/draft-ietf-httpbis-http2-16#section-5.5
339
- return nil if frame[:type].nil?
353
+ return frame if frame[:type].nil?
340
354
 
341
355
  # Process padding
342
356
  padlen = 0
343
357
  if FRAME_TYPES_WITH_PADDING.include?(frame[:type])
344
358
  padded = frame[:flags].include?(:padded)
345
359
  if padded
346
- padlen = payload.read(1).unpack(UINT8).first
360
+ padlen = payload.read(1).unpack1(UINT8)
347
361
  frame[:padding] = padlen + 1
348
- fail ProtocolError, 'padding too long' if padlen > payload.bytesize
349
- payload.slice!(-padlen, padlen) if padlen > 0
362
+ raise ProtocolError, "padding too long" if padlen > payload.bytesize
363
+
364
+ payload = payload.byteslice(0, payload.bytesize - padlen) if padlen > 0
350
365
  frame[:length] -= frame[:padding]
351
366
  frame[:flags].delete(:padded)
352
367
  end
353
368
  end
354
369
 
355
370
  case frame[:type]
356
- when :data
371
+ when :data, :ping, :continuation
357
372
  frame[:payload] = payload.read(frame[:length])
358
373
  when :headers
359
374
  if frame[:flags].include? :priority
360
375
  e_sd = payload.read_uint32
361
- frame[:stream_dependency] = e_sd & RBIT
376
+ frame[:dependency] = e_sd & RBIT
362
377
  frame[:exclusive] = (e_sd & EBIT) != 0
363
- frame[:weight] = payload.getbyte + 1
378
+ weight = payload.byteslice(0, 1).ord + 1
379
+ frame[:weight] = weight
380
+ payload = payload.byteslice(1..-1)
364
381
  end
365
382
  frame[:payload] = payload.read(frame[:length])
366
383
  when :priority
384
+ raise FrameSizeError, "Invalid length for PRIORITY_STREAM (#{frame[:length]} != 5)" if frame[:length] != 5
385
+
367
386
  e_sd = payload.read_uint32
368
- frame[:stream_dependency] = e_sd & RBIT
387
+ frame[:dependency] = e_sd & RBIT
369
388
  frame[:exclusive] = (e_sd & EBIT) != 0
370
- frame[:weight] = payload.getbyte + 1
389
+ weight = payload.byteslice(0, 1).ord + 1
390
+ frame[:weight] = weight
391
+ payload = payload.byteslice(1..-1)
371
392
  when :rst_stream
393
+ raise FrameSizeError, "Invalid length for RST_STREAM (#{frame[:length]} != 4)" if frame[:length] != 4
394
+
372
395
  frame[:error] = unpack_error payload.read_uint32
373
396
 
374
397
  when :settings
375
398
  # NOTE: frame[:length] might not match the number of frame[:payload]
376
399
  # because unknown extensions are ignored.
377
400
  frame[:payload] = []
378
- unless (frame[:length] % 6).zero?
379
- fail ProtocolError, 'Invalid settings payload length'
380
- end
401
+ raise ProtocolError, "Invalid settings payload length" unless (frame[:length] % 6).zero?
381
402
 
382
- if (frame[:stream]).nonzero?
383
- fail ProtocolError, "Invalid stream ID (#{frame[:stream]})"
384
- end
403
+ raise ProtocolError, "Invalid stream ID (#{frame[:stream]})" if (frame[:stream]).nonzero?
385
404
 
386
405
  (frame[:length] / 6).times do
387
- id = payload.read(2).unpack(UINT16).first
406
+ id = payload.read(2).unpack1(UINT16)
388
407
  val = payload.read_uint32
389
408
 
390
409
  # Unsupported or unrecognized settings MUST be ignored.
391
410
  # Here we send it along.
392
- name, _ = DEFINED_SETTINGS.find { |_name, v| v == id }
411
+ name, = DEFINED_SETTINGS.find { |_name, v| v == id }
393
412
  frame[:payload] << [name, val] if name
394
413
  end
395
414
  when :push_promise
396
415
  frame[:promise_stream] = payload.read_uint32 & RBIT
397
416
  frame[:payload] = payload.read(frame[:length])
398
- when :ping
399
- frame[:payload] = payload.read(frame[:length])
400
417
  when :goaway
401
418
  frame[:last_stream] = payload.read_uint32 & RBIT
402
419
  frame[:error] = unpack_error payload.read_uint32
@@ -404,19 +421,33 @@ module HTTP2
404
421
  size = frame[:length] - 8 # for last_stream and error
405
422
  frame[:payload] = payload.read(size) if size > 0
406
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
+
407
428
  frame[:increment] = payload.read_uint32 & RBIT
408
- when :continuation
409
- frame[:payload] = payload.read(frame[:length])
410
429
  when :altsvc
411
430
  frame[:max_age], frame[:port] = payload.read(6).unpack(UINT32 + UINT16)
412
431
 
413
- len = payload.getbyte
432
+ len = payload.byteslice(0, 1).ord
433
+ payload = payload.byteslice(1..-1)
414
434
  frame[:proto] = payload.read(len) if len > 0
415
435
 
416
- len = payload.getbyte
436
+ len = payload.byteslice(0, 1).ord
437
+ payload = payload.byteslice(1..-1)
417
438
  frame[:host] = payload.read(len) if len > 0
418
439
 
419
- frame[:origin] = payload.read(payload.size) if payload.size > 0
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
420
451
  # else # Unknown frame type is explicitly allowed
421
452
  end
422
453
 
@@ -425,22 +456,18 @@ module HTTP2
425
456
 
426
457
  private
427
458
 
428
- def pack_error(e)
429
- unless e.is_a? Integer
430
- if DEFINED_ERRORS[e].nil?
431
- fail CompressionError, "Unknown error ID for #{e}"
432
- end
459
+ def pack_error(error, buffer:)
460
+ unless error.is_a? Integer
461
+ error = DEFINED_ERRORS[error]
433
462
 
434
- e = DEFINED_ERRORS[e]
463
+ raise CompressionError, "Unknown error ID for #{error}" unless error
435
464
  end
436
465
 
437
- [e].pack(UINT32)
466
+ pack([error], UINT32, buffer: buffer)
438
467
  end
439
468
 
440
- def unpack_error(e)
441
- name, _ = DEFINED_ERRORS.find { |_name, v| v == e }
442
- name || error
469
+ def unpack_error(error)
470
+ DEFINED_ERRORS.key(error) || error
443
471
  end
444
472
  end
445
- # rubocop:enable ClassLength
446
473
  end