http-2 0.11.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.
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