diff options
Diffstat (limited to 'lib/plum/frame')
-rw-r--r-- | lib/plum/frame/continuation.rb | 16 | ||||
-rw-r--r-- | lib/plum/frame/data.rb | 31 | ||||
-rw-r--r-- | lib/plum/frame/goaway.rb | 20 | ||||
-rw-r--r-- | lib/plum/frame/headers.rb | 32 | ||||
-rw-r--r-- | lib/plum/frame/ping.rb | 24 | ||||
-rw-r--r-- | lib/plum/frame/priority.rb | 12 | ||||
-rw-r--r-- | lib/plum/frame/push_promise.rb | 33 | ||||
-rw-r--r-- | lib/plum/frame/rst_stream.rb | 16 | ||||
-rw-r--r-- | lib/plum/frame/settings.rb | 43 | ||||
-rw-r--r-- | lib/plum/frame/unknown.rb | 12 | ||||
-rw-r--r-- | lib/plum/frame/window_update.rb | 16 |
11 files changed, 255 insertions, 0 deletions
diff --git a/lib/plum/frame/continuation.rb b/lib/plum/frame/continuation.rb new file mode 100644 index 0000000..1c5232c --- /dev/null +++ b/lib/plum/frame/continuation.rb @@ -0,0 +1,16 @@ +# frozen-string-literal: true + +using Plum::BinaryString +module Plum + class Frame::Continuation < Frame + register_subclass 0x09 + + # Creates a CONTINUATION frame. + # @param stream_id [Integer] The stream ID. + # @param payload [String] Payload. + # @param end_headers [Boolean] add END_HEADERS flag + def initialize(stream_id, payload, end_headers: false) + initialize_base(type: :continuation, stream_id: stream_id, flags_value: (end_headers ? 4 : 0), payload: payload) + end + end +end diff --git a/lib/plum/frame/data.rb b/lib/plum/frame/data.rb new file mode 100644 index 0000000..7c02029 --- /dev/null +++ b/lib/plum/frame/data.rb @@ -0,0 +1,31 @@ +# frozen-string-literal: true + +using Plum::BinaryString +module Plum + class Frame::Data < Frame + register_subclass 0x00 + + # Creates a DATA frame. + # @param stream_id [Integer] The stream ID. + # @param payload [String] Payload. + # @param end_stream [Boolean] add END_STREAM flag + def initialize(stream_id, payload = "", end_stream: false) + payload = payload.b if payload&.encoding != Encoding::BINARY + fval = end_stream ? 1 : 0 + initialize_base(type_value: 0, stream_id: stream_id, flags_value: fval, payload: payload) + end + + # Splits this frame into multiple frames not to exceed MAX_FRAME_SIZE. + # @param max [Integer] The maximum size of a frame payload. + # @yield [Frame] The splitted frames. + def split(max) + return yield self if @length <= max + first, *mid, last = @payload.chunk(max) + yield Frame.craft(type_value: 0, stream_id: @stream_id, payload: first, flags_value: @flags_value & ~1) + mid.each { |slice| + yield Frame.craft(type_value: 0, stream_id: @stream_id, payload: slice, flags_value: 0) + } + yield Frame.craft(type_value: 0, stream_id: @stream_id, payload: last, flags_value: @flags_value & 1) + end + end +end diff --git a/lib/plum/frame/goaway.rb b/lib/plum/frame/goaway.rb new file mode 100644 index 0000000..e1e0097 --- /dev/null +++ b/lib/plum/frame/goaway.rb @@ -0,0 +1,20 @@ +# frozen-string-literal: true + +using Plum::BinaryString +module Plum + class Frame::Goaway < Frame + register_subclass 0x07 + + # Creates a GOAWAY frame. + # @param last_id [Integer] The biggest processed stream ID. + # @param error_type [Symbol] The error type defined in RFC 7540 Section 7. + # @param message [String] Additional debug data. + # @see RFC 7540 Section 6.8 + def initialize(last_id, error_type, message = "") + payload = String.new.push_uint32(last_id) + .push_uint32(HTTPError::ERROR_CODES[error_type]) + .push(message) + initialize_base(type: :goaway, stream_id: 0, payload: payload) + end + end +end diff --git a/lib/plum/frame/headers.rb b/lib/plum/frame/headers.rb new file mode 100644 index 0000000..12d6a61 --- /dev/null +++ b/lib/plum/frame/headers.rb @@ -0,0 +1,32 @@ +# frozen-string-literal: true + +using Plum::BinaryString +module Plum + class Frame::Headers < Frame + register_subclass 0x01 + + # Creates a HEADERS frame. + # @param stream_id [Integer] The stream ID. + # @param encoded [String] Headers. + # @param end_stream [Boolean] add END_STREAM flag + # @param end_headers [Boolean] add END_HEADERS flag + def initialize(stream_id, encoded, end_stream: false, end_headers: false) + fval = end_stream ? 1 : 0 + fval += 4 if end_headers + initialize_base(type_value: 1, stream_id: stream_id, flags_value: fval, payload: encoded) + end + + # Splits this frame into multiple frames not to exceed MAX_FRAME_SIZE. + # @param max [Integer] The maximum size of a frame payload. + # @yield [Frame] The splitted frames. + def split(max) + return yield self if @length <= max + first, *mid, last = @payload.chunk(max) + yield Frame.craft(type_value: @type_value, stream_id: @stream_id, payload: first, flags_value: @flags_value & ~4) + mid.each { |slice| + yield Frame.craft(type: :continuation, stream_id: @stream_id, payload: slice, flags_value: 0) + } + yield Frame.craft(type: :continuation, stream_id: @stream_id, payload: last, flags_value: @flags_value & 4) + end + end +end diff --git a/lib/plum/frame/ping.rb b/lib/plum/frame/ping.rb new file mode 100644 index 0000000..b0b3804 --- /dev/null +++ b/lib/plum/frame/ping.rb @@ -0,0 +1,24 @@ +# frozen-string-literal: true + +using Plum::BinaryString +module Plum + class Frame::Ping < Frame + register_subclass 0x06 + + # Creates a PING frame. + # @overload ping(ack, payload) + # @param ack [Symbol] Pass :ack to create an ACK frame. + # @param payload [String] 8 bytes length data to send. + # @overload ping(payload = "plum\x00\x00\x00\x00") + # @param payload [String] 8 bytes length data to send. + def initialize(arg1 = "plum\x00\x00\x00\x00".b, arg2 = nil) + if !arg2 + raise ArgumentError.new("data must be 8 octets") if arg1.bytesize != 8 + arg1 = arg1.b if arg1.encoding != Encoding::BINARY + initialize_base(type: :ping, stream_id: 0, payload: arg1) + else + initialize_base(type: :ping, stream_id: 0, flags: [:ack], payload: arg2) + end + end + end +end diff --git a/lib/plum/frame/priority.rb b/lib/plum/frame/priority.rb new file mode 100644 index 0000000..2d081ce --- /dev/null +++ b/lib/plum/frame/priority.rb @@ -0,0 +1,12 @@ +# frozen-string-literal: true + +using Plum::BinaryString +module Plum + class Frame::Priority < Frame + register_subclass 0x02 + + # Creates a PRIORITY frame. + def initialize + end + end +end diff --git a/lib/plum/frame/push_promise.rb b/lib/plum/frame/push_promise.rb new file mode 100644 index 0000000..e07b8e6 --- /dev/null +++ b/lib/plum/frame/push_promise.rb @@ -0,0 +1,33 @@ +# frozen-string-literal: true + +using Plum::BinaryString +module Plum + class Frame::PushPromise < Frame + register_subclass 0x05 + + # Creates a PUSH_PROMISE frame. + # @param stream_id [Integer] The stream ID. + # @param new_id [Integer] The stream ID to create. + # @param encoded [String] Request headers. + # @param end_headers [Boolean] add END_HEADERS flag + def initialize(stream_id, new_id, encoded, end_headers: false) + payload = String.new.push_uint32(new_id) + .push(encoded) + fval = end_headers ? 4 : 0 + initialize_base(type: :push_promise, stream_id: stream_id, flags_value: fval, payload: payload) + end + + # Splits this frame into multiple frames not to exceed MAX_FRAME_SIZE. + # @param max [Integer] The maximum size of a frame payload. + # @yield [Frame] The splitted frames. + def split(max) + return yield self if @length <= max + first, *mid, last = @payload.chunk(max) + yield Frame.craft(type_value: @type_value, stream_id: @stream_id, payload: first, flags_value: @flags_value & ~4) + mid.each { |slice| + yield Frame.craft(type: :continuation, stream_id: @stream_id, payload: slice, flags_value: 0) + } + yield Frame.craft(type: :continuation, stream_id: @stream_id, payload: last, flags_value: @flags_value & 4) + end + end +end diff --git a/lib/plum/frame/rst_stream.rb b/lib/plum/frame/rst_stream.rb new file mode 100644 index 0000000..a8004d5 --- /dev/null +++ b/lib/plum/frame/rst_stream.rb @@ -0,0 +1,16 @@ +# frozen-string-literal: true + +using Plum::BinaryString +module Plum + class Frame::RstStream < Frame + register_subclass 0x03 + + # Creates a RST_STREAM frame. + # @param stream_id [Integer] The stream ID. + # @param error_type [Symbol] The error type defined in RFC 7540 Section 7. + def initialize(stream_id, error_type) + payload = String.new.push_uint32(HTTPError::ERROR_CODES[error_type]) + initialize_base(type: :rst_stream, stream_id: stream_id, payload: payload) + end + end +end diff --git a/lib/plum/frame/settings.rb b/lib/plum/frame/settings.rb new file mode 100644 index 0000000..3bd13d4 --- /dev/null +++ b/lib/plum/frame/settings.rb @@ -0,0 +1,43 @@ +# frozen-string-literal: true + +using Plum::BinaryString +module Plum + class Frame::Settings < Frame + register_subclass 0x04 + + SETTINGS_TYPE = { + header_table_size: 0x01, + enable_push: 0x02, + max_concurrent_streams: 0x03, + initial_window_size: 0x04, + max_frame_size: 0x05, + max_header_list_size: 0x06 + }.freeze + + # Creates a SETTINGS frame. + # @param ack [Symbol] Pass :ack to create an ACK frame. + # @param args [Hash<Symbol, Integer>] The settings values to send. + def initialize(ack = nil, **args) + payload = String.new + args.each { |key, value| + id = SETTINGS_TYPE[key] or raise ArgumentError.new("invalid settings type: #{key}") + payload.push_uint16(id) + payload.push_uint32(value) + } + initialize_base(type: :settings, stream_id: 0, flags: [ack], payload: payload) + end + + # Parses SETTINGS frame payload. Ignores unknown settings type (see RFC7540 6.5.2). + # @return [Hash<Symbol, Integer>] The parsed strings. + def parse_settings + settings = {} + payload.each_byteslice(6) do |param| + id = param.uint16 + name = SETTINGS_TYPE.key(id) + # ignore unknown settings type + settings[name] = param.uint32(2) if name + end + settings + end + end +end diff --git a/lib/plum/frame/unknown.rb b/lib/plum/frame/unknown.rb new file mode 100644 index 0000000..6e9decb --- /dev/null +++ b/lib/plum/frame/unknown.rb @@ -0,0 +1,12 @@ +# frozen-string-literal: true + +using Plum::BinaryString +module Plum + class Frame::Unknown < Frame + # Creates a frame with unknown type value. + def initialize(type_value, **args) + initialize_base(type_value: type_value, **args) + end + end +end + diff --git a/lib/plum/frame/window_update.rb b/lib/plum/frame/window_update.rb new file mode 100644 index 0000000..6e36b2c --- /dev/null +++ b/lib/plum/frame/window_update.rb @@ -0,0 +1,16 @@ +# frozen-string-literal: true + +using Plum::BinaryString +module Plum + class Frame::WindowUpdate < Frame + register_subclass 0x08 + + # Creates a WINDOW_UPDATE frame. + # @param stream_id [Integer] the stream ID or 0. + # @param wsi [Integer] the amount to increase + def initialize(stream_id, wsi) + payload = String.new.push_uint32(wsi) + initialize_base(type: :window_update, stream_id: stream_id, payload: payload) + end + end +end |