http-2 0.7.0 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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