mieps_http-2 0.8.0

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