http-2 0.7.0 → 0.8.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.
@@ -1,19 +1,17 @@
1
1
  module HTTP2
2
-
3
2
  # Basic event emitter implementation with support for persistent and
4
3
  # one-time event callbacks.
5
4
  #
6
5
  module Emitter
7
-
8
6
  # Subscribe to all future events for specified type.
9
7
  #
10
8
  # @param event [Symbol]
11
9
  # @param block [Proc] callback function
12
10
  def add_listener(event, &block)
13
- raise Exception.new("must provide callback") if !block_given?
11
+ fail ArgumentError, 'must provide callback' unless block_given?
14
12
  listeners(event.to_sym).push block
15
13
  end
16
- alias :on :add_listener
14
+ alias_method :on, :add_listener
17
15
 
18
16
  # Subscribe to next event (at most once) for specified type.
19
17
  #
@@ -1,17 +1,17 @@
1
1
  module HTTP2
2
-
3
2
  # Stream, connection, and compressor exceptions.
4
3
  module Error
4
+ class Error < StandardError; end
5
5
 
6
6
  # Raised if connection header is missing or invalid indicating that
7
7
  # this is an invalid HTTP 2.0 request - no frames are emitted and the
8
8
  # connection must be aborted.
9
- class HandshakeError < Exception; end
9
+ class HandshakeError < Error; end
10
10
 
11
11
  # Raised by stream or connection handlers, results in GOAWAY frame
12
12
  # which signals termination of the current connection. You *cannot*
13
13
  # recover from this exception, or any exceptions subclassed from it.
14
- class ProtocolError < Exception; end
14
+ class ProtocolError < Error; end
15
15
 
16
16
  # Raised on any header encoding / decoding exception.
17
17
  #
@@ -25,20 +25,20 @@ module HTTP2
25
25
 
26
26
  # Raised on invalid stream processing: invalid frame type received or
27
27
  # sent, or invalid command issued.
28
- class StreamError < ProtocolError; end
28
+ class InternalError < ProtocolError; end
29
29
 
30
30
  #
31
31
  # -- Recoverable errors -------------------------------------------------
32
32
  #
33
33
 
34
34
  # Raised if stream has been closed and new frames cannot be sent.
35
- class StreamClosed < Exception; end
35
+ class StreamClosed < Error; end
36
36
 
37
37
  # Raised if connection has been closed (or draining) and new stream
38
38
  # cannot be opened.
39
- class ConnectionClosed < Exception; end
39
+ class ConnectionClosed < Error; end
40
40
 
41
41
  # Raised if stream limit has been reached and new stream cannot be opened.
42
- class StreamLimitExceeded < Exception; end
42
+ class StreamLimitExceeded < Error; end
43
43
  end
44
44
  end
@@ -1,16 +1,14 @@
1
1
  module HTTP2
2
-
3
2
  # Implementation of stream and connection DATA flow control: frames may
4
3
  # be split and / or may be buffered based on current flow control window.
5
4
  #
6
5
  module FlowBuffer
7
-
8
6
  # Amount of buffered data. Only DATA payloads are subject to flow stream
9
7
  # and connection flow control.
10
8
  #
11
9
  # @return [Integer]
12
10
  def buffered_amount
13
- @send_buffer.map {|f| f[:length] }.reduce(:+) || 0
11
+ @send_buffer.map { |f| f[:length] }.reduce(:+) || 0
14
12
  end
15
13
 
16
14
  private
@@ -25,12 +23,12 @@ module HTTP2
25
23
  # @param frame [Hash]
26
24
  # @param encode [Boolean] set to true by co
27
25
  def send_data(frame = nil, encode = false)
28
- @send_buffer.push frame if !frame.nil?
26
+ @send_buffer.push frame unless frame.nil?
29
27
 
30
28
  # FIXME: Frames with zero length with the END_STREAM flag set (that
31
29
  # is, an empty DATA frame) MAY be sent if there is no available space
32
30
  # in either flow control window.
33
- while @remote_window > 0 && !@send_buffer.empty? do
31
+ while @remote_window > 0 && !@send_buffer.empty?
34
32
  frame = @send_buffer.shift
35
33
 
36
34
  sent, frame_size = 0, frame[:payload].bytesize
@@ -46,9 +44,7 @@ module HTTP2
46
44
  chunk[:payload] = payload
47
45
 
48
46
  # if no longer last frame in sequence...
49
- if frame[:flags].include? :end_stream
50
- frame[:flags] -= [:end_stream]
51
- end
47
+ frame[:flags] -= [:end_stream] if frame[:flags].include? :end_stream
52
48
 
53
49
  @send_buffer.unshift chunk
54
50
  sent = @remote_window
@@ -57,10 +53,15 @@ module HTTP2
57
53
  end
58
54
 
59
55
  frames = encode ? encode(frame) : [frame]
60
- frames.each {|f| emit(:frame, f) }
56
+ frames.each { |f| emit(:frame, f) }
61
57
  @remote_window -= sent
62
58
  end
63
59
  end
64
- end
65
60
 
61
+ def process_window_update(frame)
62
+ return if frame[:ignore]
63
+ @remote_window += frame[:increment]
64
+ send_data
65
+ end
66
+ end
66
67
  end
@@ -1,5 +1,4 @@
1
1
  module HTTP2
2
-
3
2
  # Performs encoding, decoding, and validation of binary HTTP/2 frames.
4
3
  #
5
4
  class Framer
@@ -30,19 +29,22 @@ module HTTP2
30
29
  window_update: 0x8,
31
30
  continuation: 0x9,
32
31
  altsvc: 0xa,
33
- }
32
+ }.freeze
34
33
 
35
- FRAME_TYPES_WITH_PADDING = [ :data, :headers, :push_promise ]
34
+ FRAME_TYPES_WITH_PADDING = [:data, :headers, :push_promise].freeze
36
35
 
37
36
  # Per frame flags as defined by the spec
38
37
  FRAME_FLAGS = {
39
38
  data: {
40
39
  end_stream: 0,
41
- padded: 3, compressed: 5
40
+ padded: 3,
41
+ compressed: 5,
42
42
  },
43
43
  headers: {
44
- end_stream: 0, end_headers: 2,
45
- padded: 3, priority: 5,
44
+ end_stream: 0,
45
+ end_headers: 2,
46
+ padded: 3,
47
+ priority: 5,
46
48
  },
47
49
  priority: {},
48
50
  rst_stream: {},
@@ -53,10 +55,10 @@ module HTTP2
53
55
  },
54
56
  ping: { ack: 0 },
55
57
  goaway: {},
56
- window_update:{},
58
+ window_update: {},
57
59
  continuation: { end_headers: 2 },
58
60
  altsvc: {},
59
- }
61
+ }.each_value(&:freeze).freeze
60
62
 
61
63
  # Default settings as defined by the spec
62
64
  DEFINED_SETTINGS = {
@@ -66,7 +68,7 @@ module HTTP2
66
68
  settings_initial_window_size: 4,
67
69
  settings_max_frame_size: 5,
68
70
  settings_max_header_list_size: 6,
69
- }
71
+ }.freeze
70
72
 
71
73
  # Default error types as defined by the spec
72
74
  DEFINED_ERRORS = {
@@ -83,20 +85,20 @@ module HTTP2
83
85
  connect_error: 10,
84
86
  enhance_your_calm: 11,
85
87
  inadequate_security: 12,
86
- }
88
+ http_1_1_required: 13,
89
+ }.freeze
87
90
 
88
91
  RBIT = 0x7fffffff
89
92
  RBYTE = 0x0fffffff
90
93
  EBIT = 0x80000000
91
- UINT32 = "N".freeze
92
- UINT16 = "n".freeze
93
- UINT8 = "C".freeze
94
+ UINT32 = 'N'.freeze
95
+ UINT16 = 'n'.freeze
96
+ UINT8 = 'C'.freeze
94
97
  HEADERPACK = (UINT8 + UINT16 + UINT8 + UINT8 + UINT32).freeze
95
98
  FRAME_LENGTH_HISHIFT = 16
96
99
  FRAME_LENGTH_LOMASK = 0xFFFF
97
- BINARY = 'binary'.freeze
98
100
 
99
- private_constant :RBIT, :RBYTE, :EBIT, :HEADERPACK, :UINT32, :UINT16, :UINT8, :BINARY
101
+ private_constant :RBIT, :RBYTE, :EBIT, :HEADERPACK, :UINT32, :UINT16, :UINT8
100
102
 
101
103
  # Initializes new framer object.
102
104
  #
@@ -105,31 +107,31 @@ module HTTP2
105
107
  end
106
108
 
107
109
  # Generates common 9-byte frame header.
108
- # - http://tools.ietf.org/html/draft-ietf-httpbis-http2-14#section-4.1
110
+ # - http://tools.ietf.org/html/draft-ietf-httpbis-http2-16#section-4.1
109
111
  #
110
112
  # @param frame [Hash]
111
113
  # @return [String]
112
- def commonHeader(frame)
114
+ def common_header(frame)
113
115
  header = []
114
116
 
115
- if !FRAME_TYPES[frame[:type]]
116
- raise CompressionError.new("Invalid frame type (#{frame[:type]})")
117
+ unless FRAME_TYPES[frame[:type]]
118
+ fail CompressionError, "Invalid frame type (#{frame[:type]})"
117
119
  end
118
120
 
119
121
  if frame[:length] > @max_frame_size
120
- raise CompressionError.new("Frame size is too large: #{frame[:length]}")
122
+ fail CompressionError, "Frame size is too large: #{frame[:length]}"
121
123
  end
122
124
 
123
125
  if frame[:length] < 0
124
- raise CompressionError.new("Frame size is invalid: #{frame[:length]}")
126
+ fail CompressionError, "Frame size is invalid: #{frame[:length]}"
125
127
  end
126
128
 
127
129
  if frame[:stream] > MAX_STREAM_ID
128
- raise CompressionError.new("Stream ID (#{frame[:stream]}) is too large")
130
+ fail CompressionError, "Stream ID (#{frame[:stream]}) is too large"
129
131
  end
130
132
 
131
133
  if frame[:type] == :window_update && frame[:increment] > MAX_WINDOWINC
132
- raise CompressionError.new("Window increment (#{frame[:increment]}) is too large")
134
+ fail CompressionError, "Window increment (#{frame[:increment]}) is too large"
133
135
  end
134
136
 
135
137
  header << (frame[:length] >> FRAME_LENGTH_HISHIFT)
@@ -137,12 +139,11 @@ module HTTP2
137
139
  header << FRAME_TYPES[frame[:type]]
138
140
  header << frame[:flags].reduce(0) do |acc, f|
139
141
  position = FRAME_FLAGS[frame[:type]][f]
140
- if !position
141
- raise CompressionError.new("Invalid frame flag (#{f}) for #{frame[:type]}")
142
+ unless position
143
+ fail CompressionError, "Invalid frame flag (#{f}) for #{frame[:type]}"
142
144
  end
143
145
 
144
- acc |= (1 << position)
145
- acc
146
+ acc | (1 << position)
146
147
  end
147
148
 
148
149
  header << frame[:stream]
@@ -152,16 +153,15 @@ module HTTP2
152
153
  # Decodes common 9-byte header.
153
154
  #
154
155
  # @param buf [Buffer]
155
- def readCommonHeader(buf)
156
+ def read_common_header(buf)
156
157
  frame = {}
157
- len_hi, len_lo, type, flags, stream = buf.slice(0,9).unpack(HEADERPACK)
158
+ len_hi, len_lo, type, flags, stream = buf.slice(0, 9).unpack(HEADERPACK)
158
159
 
159
160
  frame[:length] = (len_hi << FRAME_LENGTH_HISHIFT) | len_lo
160
- frame[:type], _ = FRAME_TYPES.select { |t,pos| type == pos }.first
161
+ frame[:type], _ = FRAME_TYPES.find { |_t, pos| type == pos }
161
162
  if frame[:type]
162
- frame[:flags] = FRAME_FLAGS[frame[:type]].reduce([]) do |acc, (name, pos)|
163
+ frame[:flags] = FRAME_FLAGS[frame[:type]].each_with_object([]) do |(name, pos), acc|
163
164
  acc << name if (flags & (1 << pos)) > 0
164
- acc
165
165
  end
166
166
  end
167
167
 
@@ -177,111 +177,106 @@ module HTTP2
177
177
  bytes = Buffer.new
178
178
  length = 0
179
179
 
180
- frame[:flags] ||= []
180
+ frame[:flags] ||= []
181
181
  frame[:stream] ||= 0
182
182
 
183
183
  case frame[:type]
184
184
  when :data
185
- bytes << frame[:payload]
185
+ bytes << frame[:payload]
186
186
  length += frame[:payload].bytesize
187
187
 
188
188
  when :headers
189
189
  if frame[:weight] || frame[:stream_dependency] || !frame[:exclusive].nil?
190
190
  unless frame[:weight] && frame[:stream_dependency] && !frame[:exclusive].nil?
191
- raise CompressionError.new("Must specify all of priority parameters for #{frame[:type]}")
191
+ fail CompressionError, "Must specify all of priority parameters for #{frame[:type]}"
192
192
  end
193
- frame[:flags] += [:priority] if !frame[:flags].include? :priority
193
+ frame[:flags] += [:priority] unless frame[:flags].include? :priority
194
194
  end
195
195
 
196
196
  if frame[:flags].include? :priority
197
- bytes << [(frame[:exclusive] ? EBIT : 0) |
198
- (frame[:stream_dependency] & RBIT)].pack(UINT32)
197
+ bytes << [(frame[:exclusive] ? EBIT : 0) | (frame[:stream_dependency] & RBIT)].pack(UINT32)
199
198
  bytes << [frame[:weight] - 1].pack(UINT8)
200
199
  length += 5
201
200
  end
202
201
 
203
- bytes << frame[:payload]
202
+ bytes << frame[:payload]
204
203
  length += frame[:payload].bytesize
205
204
 
206
205
  when :priority
207
206
  unless frame[:weight] && frame[:stream_dependency] && !frame[:exclusive].nil?
208
- raise CompressionError.new("Must specify all of priority parameters for #{frame[:type]}")
207
+ fail CompressionError, "Must specify all of priority parameters for #{frame[:type]}"
209
208
  end
210
- bytes << [(frame[:exclusive] ? EBIT : 0) |
211
- (frame[:stream_dependency] & RBIT)].pack(UINT32)
209
+ bytes << [(frame[:exclusive] ? EBIT : 0) | (frame[:stream_dependency] & RBIT)].pack(UINT32)
212
210
  bytes << [frame[:weight] - 1].pack(UINT8)
213
211
  length += 5
214
212
 
215
213
  when :rst_stream
216
- bytes << pack_error(frame[:error])
214
+ bytes << pack_error(frame[:error])
217
215
  length += 4
218
216
 
219
217
  when :settings
220
218
  if frame[:stream] != 0
221
- raise CompressionError.new("Invalid stream ID (#{frame[:stream]})")
219
+ fail CompressionError, "Invalid stream ID (#{frame[:stream]})"
222
220
  end
223
221
 
224
- frame[:payload].each do |(k,v)|
222
+ frame[:payload].each do |(k, v)|
225
223
  if k.is_a? Integer
226
- DEFINED_SETTINGS.has_value?(k) or next
224
+ DEFINED_SETTINGS.value?(k) || next
227
225
  else
228
226
  k = DEFINED_SETTINGS[k]
229
227
 
230
- if k.nil?
231
- raise CompressionError.new("Unknown settings ID for #{k}")
232
- end
228
+ fail CompressionError, "Unknown settings ID for #{k}" if k.nil?
233
229
  end
234
230
 
235
- bytes << [k].pack(UINT16)
236
- bytes << [v].pack(UINT32)
231
+ bytes << [k].pack(UINT16)
232
+ bytes << [v].pack(UINT32)
237
233
  length += 6
238
234
  end
239
235
 
240
236
  when :push_promise
241
- bytes << [frame[:promise_stream] & RBIT].pack(UINT32)
242
- bytes << frame[:payload]
237
+ bytes << [frame[:promise_stream] & RBIT].pack(UINT32)
238
+ bytes << frame[:payload]
243
239
  length += 4 + frame[:payload].bytesize
244
240
 
245
241
  when :ping
246
242
  if frame[:payload].bytesize != 8
247
- raise CompressionError.new("Invalid payload size \
248
- (#{frame[:payload].size} != 8 bytes)")
243
+ fail CompressionError, "Invalid payload size (#{frame[:payload].size} != 8 bytes)"
249
244
  end
250
- bytes << frame[:payload]
245
+ bytes << frame[:payload]
251
246
  length += 8
252
247
 
253
248
  when :goaway
254
- bytes << [frame[:last_stream] & RBIT].pack(UINT32)
255
- bytes << pack_error(frame[:error])
249
+ bytes << [frame[:last_stream] & RBIT].pack(UINT32)
250
+ bytes << pack_error(frame[:error])
256
251
  length += 8
257
252
 
258
253
  if frame[:payload]
259
- bytes << frame[:payload]
254
+ bytes << frame[:payload]
260
255
  length += frame[:payload].bytesize
261
256
  end
262
257
 
263
258
  when :window_update
264
- bytes << [frame[:increment] & RBIT].pack(UINT32)
259
+ bytes << [frame[:increment] & RBIT].pack(UINT32)
265
260
  length += 4
266
261
 
267
262
  when :continuation
268
- bytes << frame[:payload]
263
+ bytes << frame[:payload]
269
264
  length += frame[:payload].bytesize
270
265
 
271
266
  when :altsvc
272
267
  bytes << [frame[:max_age], frame[:port]].pack(UINT32 + UINT16)
273
268
  length += 6
274
269
  if frame[:proto]
275
- frame[:proto].bytesize > 255 and raise CompressionError.new("Proto too long")
276
- bytes << [frame[:proto].bytesize].pack(UINT8) << frame[:proto].force_encoding(BINARY)
270
+ fail CompressionError, 'Proto too long' if frame[:proto].bytesize > 255
271
+ bytes << [frame[:proto].bytesize].pack(UINT8) << frame[:proto].force_encoding(Encoding::BINARY)
277
272
  length += 1 + frame[:proto].bytesize
278
273
  else
279
274
  bytes << [0].pack(UINT8)
280
275
  length += 1
281
276
  end
282
277
  if frame[:host]
283
- frame[:host].bytesize > 255 and raise CompressionError.new("Host too long")
284
- bytes << [frame[:host].bytesize].pack(UINT8) << frame[:host].force_encoding(BINARY)
278
+ fail CompressionError, 'Host too long' if frame[:host].bytesize > 255
279
+ bytes << [frame[:host].bytesize].pack(UINT8) << frame[:host].force_encoding(Encoding::BINARY)
285
280
  length += 1 + frame[:host].bytesize
286
281
  else
287
282
  bytes << [0].pack(UINT8)
@@ -295,16 +290,16 @@ module HTTP2
295
290
 
296
291
  # Process padding.
297
292
  # frame[:padding] gives number of extra octets to be added.
298
- # - http://tools.ietf.org/html/draft-ietf-httpbis-http2-12#section-6.1
293
+ # - http://tools.ietf.org/html/draft-ietf-httpbis-http2-16#section-6.1
299
294
  if frame[:padding]
300
295
  unless FRAME_TYPES_WITH_PADDING.include?(frame[:type])
301
- raise CompressionError.new("Invalid padding flag for #{frame[:type]}")
296
+ fail CompressionError, "Invalid padding flag for #{frame[:type]}"
302
297
  end
303
298
 
304
299
  padlen = frame[:padding]
305
300
 
306
301
  if padlen <= 0 || padlen > 256 || padlen + length > @max_frame_size
307
- raise CompressionError.new("Invalid padding #{padlen}")
302
+ fail CompressionError, "Invalid padding #{padlen}"
308
303
  end
309
304
 
310
305
  length += padlen
@@ -318,7 +313,7 @@ module HTTP2
318
313
  end
319
314
 
320
315
  frame[:length] = length
321
- bytes.prepend(commonHeader(frame))
316
+ bytes.prepend(common_header(frame))
322
317
  end
323
318
 
324
319
  # Decodes complete HTTP/2 frame from provided buffer. If the buffer
@@ -327,7 +322,7 @@ module HTTP2
327
322
  # @param buf [Buffer]
328
323
  def parse(buf)
329
324
  return nil if buf.size < 9
330
- frame = readCommonHeader(buf)
325
+ frame = read_common_header(buf)
331
326
  return nil if buf.size < 9 + frame[:length]
332
327
 
333
328
  buf.read(9)
@@ -335,7 +330,7 @@ module HTTP2
335
330
 
336
331
  # Implementations MUST discard frames
337
332
  # that have unknown or unsupported types.
338
- # - http://tools.ietf.org/html/draft-ietf-httpbis-http2-14#section-5.5
333
+ # - http://tools.ietf.org/html/draft-ietf-httpbis-http2-16#section-5.5
339
334
  return nil if frame[:type].nil?
340
335
 
341
336
  # Process padding
@@ -345,8 +340,8 @@ module HTTP2
345
340
  if padded
346
341
  padlen = payload.read(1).unpack(UINT8).first
347
342
  frame[:padding] = padlen + 1
348
- padlen > payload.bytesize and raise ProtocolError.new("padding too long")
349
- padlen > 0 and payload.slice!(-padlen,padlen)
343
+ fail ProtocolError, 'padding too long' if padlen > payload.bytesize
344
+ payload.slice!(-padlen, padlen) if padlen > 0
350
345
  frame[:length] -= frame[:padding]
351
346
  frame[:flags].delete(:padded)
352
347
  end
@@ -376,11 +371,11 @@ module HTTP2
376
371
  # because unknown extensions are ignored.
377
372
  frame[:payload] = []
378
373
  unless frame[:length] % 6 == 0
379
- raise ProtocolError.new("Invalid settings payload length")
374
+ fail ProtocolError, 'Invalid settings payload length'
380
375
  end
381
376
 
382
377
  if frame[:stream] != 0
383
- raise ProtocolError.new("Invalid stream ID (#{frame[:stream]})")
378
+ fail ProtocolError, "Invalid stream ID (#{frame[:stream]})"
384
379
  end
385
380
 
386
381
  (frame[:length] / 6).times do
@@ -389,7 +384,7 @@ module HTTP2
389
384
 
390
385
  # Unsupported or unrecognized settings MUST be ignored.
391
386
  # Here we send it along.
392
- name, _ = DEFINED_SETTINGS.select { |name, v| v == id }.first
387
+ name, _ = DEFINED_SETTINGS.find { |_name, v| v == id }
393
388
  frame[:payload] << [name, val] if name
394
389
  end
395
390
  when :push_promise
@@ -411,16 +406,13 @@ module HTTP2
411
406
  frame[:max_age], frame[:port] = payload.read(6).unpack(UINT32 + UINT16)
412
407
 
413
408
  len = payload.getbyte
414
- len > 0 and frame[:proto] = payload.read(len)
409
+ frame[:proto] = payload.read(len) if len > 0
415
410
 
416
411
  len = payload.getbyte
417
- len > 0 and frame[:host] = payload.read(len)
412
+ frame[:host] = payload.read(len) if len > 0
418
413
 
419
- if payload.size > 0
420
- frame[:origin] = payload.read(payload.size)
421
- end
422
- else
423
- # Unknown frame type is explicitly allowed
414
+ frame[:origin] = payload.read(payload.size) if payload.size > 0
415
+ # else # Unknown frame type is explicitly allowed
424
416
  end
425
417
 
426
418
  frame
@@ -429,9 +421,9 @@ module HTTP2
429
421
  private
430
422
 
431
423
  def pack_error(e)
432
- if !e.is_a? Integer
424
+ unless e.is_a? Integer
433
425
  if DEFINED_ERRORS[e].nil?
434
- raise CompressionError.new("Unknown error ID for #{e}")
426
+ fail CompressionError, "Unknown error ID for #{e}"
435
427
  end
436
428
 
437
429
  e = DEFINED_ERRORS[e]
@@ -441,9 +433,8 @@ module HTTP2
441
433
  end
442
434
 
443
435
  def unpack_error(e)
444
- name, _ = DEFINED_ERRORS.select { |name, v| v == e }.first
436
+ name, _ = DEFINED_ERRORS.find { |_name, v| v == e }
445
437
  name || error
446
438
  end
447
-
448
439
  end
449
440
  end