aboutsummaryrefslogtreecommitdiffstats
path: root/lib/plum/frame
diff options
context:
space:
mode:
Diffstat (limited to 'lib/plum/frame')
-rw-r--r--lib/plum/frame/continuation.rb16
-rw-r--r--lib/plum/frame/data.rb31
-rw-r--r--lib/plum/frame/goaway.rb20
-rw-r--r--lib/plum/frame/headers.rb32
-rw-r--r--lib/plum/frame/ping.rb24
-rw-r--r--lib/plum/frame/priority.rb12
-rw-r--r--lib/plum/frame/push_promise.rb33
-rw-r--r--lib/plum/frame/rst_stream.rb16
-rw-r--r--lib/plum/frame/settings.rb43
-rw-r--r--lib/plum/frame/unknown.rb12
-rw-r--r--lib/plum/frame/window_update.rb16
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