From 18e6795c128788deb33c5587a50eced0f73bac42 Mon Sep 17 00:00:00 2001 From: Kazuki Yamaguchi Date: Thu, 16 Jul 2015 19:57:25 +0900 Subject: make examples work --- lib/plum/frame.rb | 114 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 114 insertions(+) create mode 100644 lib/plum/frame.rb (limited to 'lib/plum/frame.rb') diff --git a/lib/plum/frame.rb b/lib/plum/frame.rb new file mode 100644 index 0000000..cbddbe8 --- /dev/null +++ b/lib/plum/frame.rb @@ -0,0 +1,114 @@ +module Plum + class Frame + FRAME_TYPES = { + data: 0x00, + headers: 0x01, + priority: 0x02, + rst_stream: 0x03, + settings: 0x04, + push_promise: 0x05, + ping: 0x06, + goaway: 0x07, + window_update: 0x08, + continuation: 0x09 + } + + FRAME_FLAGS = { + data: { + end_stream: 0x01, + padded: 0x08 + }, + headers: { + end_stream: 0x01, + end_headers: 0x04, + padded: 0x08, + priority: 0x20 + }, + priority: {}, + rst_stream: {}, + settings: { + ack: 0x01 + }, + push_promise: { + end_headers: 0x04, + padded: 0x08 + }, + ping: { + ack: 0x01 + }, + goaway: {}, + window_update: {}, + continuation: { + end_headers: 0x04 + } + } + + # RFC7540: 4.1 Frame format + # +-----------------------------------------------+ + # | Length (24) | + # +---------------+---------------+---------------+ + # | Type (8) | Flags (8) | + # +-+-------------+---------------+-------------------------------+ + # |R| Stream Identifier (31) | + # +=+=============================================================+ + # | Frame Payload (0...) ... + # +---------------------------------------------------------------+ + + # [Integer] The length of payload. unsigned 24-bit integer + attr_reader :length + # [Integer] Frame type. 8-bit + attr_reader :type_value + # [Integer] Flags. 8-bit + attr_reader :flags_value + # [Integer] Stream Identifier. unsigned 31-bit integer + attr_reader :stream_id + # [String] The payload. + attr_reader :payload + + def initialize(length: nil, type: nil, type_value: nil, flags: nil, flags_value: nil, stream_id: nil, payload: nil) + @payload = payload.to_s + @length = length || @payload.bytesize + @type_value = type_value || FRAME_TYPES[type] or raise ArgumentError.new("type_value or type is necessary") + @flags_value = flags_value || (flags && flags.map {|flag| FRAME_FLAGS[type][flag] }.inject(:|)) || 0 + @stream_id = stream_id or raise ArgumentError.new("stream_id is necessary") + end + + def type + FRAME_TYPES.key(type_value) + end + + def flags + FRAME_FLAGS[type].select {|name, value| value & flags_value > 0 }.map {|name, value| name } + end + + def assemble + bytes = "" + bytes << [length].pack("N")[1, 3] # last 3*8 bits + bytes << [type_value].pack("C") + bytes << [flags_value].pack("C") + bytes << [stream_id & ~(1 << 31)].pack("N") # first bit is reserved (MUST be 0) + bytes << payload + bytes + end + + def inspect + "#" + end + + def self.parse!(buffer) + return nil if buffer.size < 9 # header: 9 bytes + bhead = buffer[0, 9] + length = ("\x00" + bhead[0, 3]).unpack("N")[0] + return nil if buffer.size < 9 + length + + payload = buffer.slice!(0...(9 + length))[9, length] + type = bhead[3, 1].unpack("C")[0] + flags = bhead[4, 1].unpack("C")[0] + r_sid = bhead[5, 4].unpack("N")[0] + r = r_sid >> 31 + stream_id = r_sid & ~(1 << 31) + + self.new(length: length, type_value: type, flags_value: flags, stream_id: stream_id, payload: payload) + end + end +end -- cgit v1.2.3