aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKazuki Yamaguchi <k@rhe.jp>2016-05-08 21:03:25 +0900
committerKazuki Yamaguchi <k@rhe.jp>2016-05-08 22:02:50 +0900
commit7be98b6090baf6d67be53977bcd0fc87d83737cb (patch)
treeac4be21689902b31643636161ae1a22ffd0843e4
parent26ec6b88a8e078499835851a0310eba486475b7b (diff)
downloadplum-7be98b6090baf6d67be53977bcd0fc87d83737cb.tar.gz
frame: create subclasses for each frame type
This improves code readability.
-rw-r--r--lib/plum.rb11
-rw-r--r--lib/plum/connection.rb10
-rw-r--r--lib/plum/flow_control.rb3
-rw-r--r--lib/plum/frame.rb211
-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
-rw-r--r--lib/plum/server/http_connection.rb6
-rw-r--r--lib/plum/stream.rb10
-rw-r--r--test/plum/client/test_upgrade_client_session.rb8
-rw-r--r--test/plum/connection/test_handle_frame.rb16
-rw-r--r--test/plum/server/test_connection.rb15
-rw-r--r--test/plum/server/test_http_connection.rb6
-rw-r--r--test/plum/stream/test_handle_frame.rb44
-rw-r--r--test/plum/test_connection.rb34
-rw-r--r--test/plum/test_connection_utils.rb6
-rw-r--r--test/plum/test_flow_control.rb70
-rw-r--r--test/plum/test_frame.rb22
-rw-r--r--test/plum/test_frame_factory.rb20
-rw-r--r--test/plum/test_frame_utils.rb29
-rw-r--r--test/plum/test_stream.rb10
-rw-r--r--test/utils/client.rb4
-rw-r--r--test/utils/server.rb2
31 files changed, 462 insertions, 330 deletions
diff --git a/lib/plum.rb b/lib/plum.rb
index 3220bbc..fa70317 100644
--- a/lib/plum.rb
+++ b/lib/plum.rb
@@ -13,6 +13,17 @@ require "plum/hpack/context"
require "plum/hpack/decoder"
require "plum/hpack/encoder"
require "plum/frame"
+require "plum/frame/data"
+require "plum/frame/headers"
+require "plum/frame/priority"
+require "plum/frame/rst_stream"
+require "plum/frame/settings"
+require "plum/frame/push_promise"
+require "plum/frame/ping"
+require "plum/frame/goaway"
+require "plum/frame/window_update"
+require "plum/frame/continuation"
+require "plum/frame/unknown"
require "plum/flow_control"
require "plum/connection"
require "plum/stream"
diff --git a/lib/plum/connection.rb b/lib/plum/connection.rb
index 1eac16e..36faedd 100644
--- a/lib/plum/connection.rb
+++ b/lib/plum/connection.rb
@@ -85,7 +85,7 @@ module Plum
# Sends local settings to the peer.
# @param new_settings [Hash<Symbol, Integer>]
def settings(**new_settings)
- send_immediately Frame.settings(**new_settings)
+ send_immediately Frame::Settings.new(**new_settings)
old_settings = @local_settings.dup
@local_settings.merge!(new_settings)
@@ -98,14 +98,14 @@ module Plum
# @param data [String] Must be 8 octets.
# @raise [ArgumentError] If the data is not 8 octets.
def ping(data = "plum\x00\x00\x00\x00")
- send_immediately Frame.ping(data)
+ send_immediately Frame::Ping.new(data)
end
# Sends GOAWAY frame to the peer and closes the connection.
# @param error_type [Symbol] The error type to be contained in the GOAWAY frame.
def goaway(error_type = :no_error, message = "")
last_id = @max_stream_ids.max
- send_immediately Frame.goaway(last_id, error_type, message)
+ send_immediately Frame::Goaway.new(last_id, error_type, message)
end
# Returns whether peer enables server push or not
@@ -202,7 +202,7 @@ module Plum
callback(:remote_settings, @remote_settings, old_remote_settings)
- send_immediately Frame.settings(:ack) if send_ack
+ send_immediately Frame::Settings.new(:ack) if send_ack
if @state == :waiting_settings
@state = :open
@@ -223,7 +223,7 @@ module Plum
else
opaque_data = frame.payload
callback(:ping, opaque_data)
- send_immediately Frame.ping(:ack, opaque_data)
+ send_immediately Frame::Ping.new(:ack, opaque_data)
end
end
diff --git a/lib/plum/flow_control.rb b/lib/plum/flow_control.rb
index 9a2aa1d..f28fa3a 100644
--- a/lib/plum/flow_control.rb
+++ b/lib/plum/flow_control.rb
@@ -29,9 +29,8 @@ module Plum
# @param wsi [Integer] The amount to increase receiving window size. The legal range is 1 to 2^32-1.
def window_update(wsi)
@recv_remaining_window += wsi
- payload = String.new.push_uint32(wsi)
sid = (Stream === self) ? self.id : 0
- send_immediately Frame.new(type: :window_update, stream_id: sid, payload: payload)
+ send_immediately Frame::WindowUpdate.new(sid, wsi)
end
protected
diff --git a/lib/plum/frame.rb b/lib/plum/frame.rb
index 57b3061..1a4b402 100644
--- a/lib/plum/frame.rb
+++ b/lib/plum/frame.rb
@@ -1,7 +1,6 @@
# frozen-string-literal: true
using Plum::BinaryString
-
module Plum
class Frame
FRAME_TYPES = {
@@ -53,14 +52,10 @@ module Plum
# @!visibility private
FRAME_FLAGS_MAP = FRAME_FLAGS.values.inject(:merge).freeze
- 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
+ # type_value = 0x00 - 0x09 are known, but these classes aren't defined yet...
+ # Frame.register_subclass adds them.
+ SUB_CLASSES = []
+ private_constant :SUB_CLASSES
# RFC7540: 4.1 Frame format
# +-----------------------------------------------+
@@ -82,12 +77,29 @@ module Plum
# [String] The payload. Value is frozen.
attr_reader :payload
- def initialize(type: nil, type_value: nil, flags: nil, flags_value: nil, stream_id: nil, payload: nil)
+ # @private
+ protected def initialize_base(type: nil, type_value: nil, flags: nil, flags_value: nil, stream_id: nil, payload: nil)
@payload = payload || ""
@length = @payload.bytesize
- @type_value = type_value or self.type = type
+ @type_value = type_value || FRAME_TYPES[type] or raise ArgumentError.new("unknown frame type: #{type}")
@flags_value = flags_value or self.flags = flags
@stream_id = stream_id or raise ArgumentError.new("stream_id is necessary")
+ self
+ end
+
+ # @private
+ def initialize(*, **)
+ raise ArgumentError, "can't instantiate abstract class"
+ end
+
+ # Creates a new instance of Frame or an subclass of Frame.
+ # @private
+ def self.craft(type: nil, type_value: nil, **args)
+ type_value ||= type && FRAME_TYPES[type] or (raise ArgumentError, "unknown frame type")
+ klass = SUB_CLASSES[type_value] || Frame::Unknown
+ instance = klass.allocate
+ instance.send(:initialize_base, type_value: type_value, **args)
+ instance
end
# Returns the length of payload.
@@ -102,12 +114,6 @@ module Plum
FRAME_TYPES_INVERSE[@type_value] || ("unknown_%02x" % @type_value).to_sym
end
- # Sets the frame type.
- # @param value [Symbol] The type.
- def type=(value)
- @type_value = FRAME_TYPES[value] or raise ArgumentError.new("unknown frame type: #{value}")
- end
-
# Returns the set flags on the frame.
# @return [Array<Symbol>] The flags.
def flags
@@ -147,153 +153,36 @@ module Plum
# @private
def inspect
- "#<Plum::Frame:0x%04x} length=%d, type=%p, flags=%p, stream_id=0x%04x, payload=%p>" % [__id__, length, type, flags, stream_id, 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)
- case type
- when :data
- yield Frame.new(type_value: 0, stream_id: @stream_id, payload: first, flags_value: @flags_value & ~1)
- mid.each { |slice|
- yield Frame.new(type_value: 0, stream_id: @stream_id, payload: slice, flags_value: 0)
- }
- yield Frame.new(type_value: 0, stream_id: @stream_id, payload: last, flags_value: @flags_value & 1)
- when :headers, :push_promise
- yield Frame.new(type_value: @type_value, stream_id: @stream_id, payload: first, flags_value: @flags_value & ~4)
- mid.each { |slice|
- yield Frame.new(type: :continuation, stream_id: @stream_id, payload: slice, flags_value: 0)
- }
- yield Frame.new(type: :continuation, stream_id: @stream_id, payload: last, flags_value: @flags_value & 4)
- else
- raise NotImplementedError.new("frame split of frame with type #{type} is not supported")
+ "#<%s:0x%04x length=%d, flags=%p, stream_id=0x%04x, payload=%p>" % [self.class, __id__, length, flags, stream_id, payload]
+ end
+
+ class << self
+ # Parses a frame from given buffer. It changes given buffer.
+ # @param buffer [String] The buffer stored the data received from peer. Encoding must be Encoding::BINARY.
+ # @return [Frame, nil] The parsed frame or nil if the buffer is imcomplete.
+ def parse!(buffer)
+ return nil if buffer.bytesize < 9 # header: 9 bytes
+ length = buffer.uint24
+ return nil if buffer.bytesize < 9 + length
+
+ cur = buffer.byteshift(9 + length)
+ type_value, flags_value, r_sid = cur.byteslice(3, 6).unpack("CCN")
+ # r = r_sid >> 31 # currently not used
+ stream_id = r_sid # & ~(1 << 31)
+
+ frame = (SUB_CLASSES[type_value] || Frame::Unknown).allocate
+ frame.send(:initialize_base,
+ type_value: type_value,
+ flags_value: flags_value,
+ stream_id: stream_id,
+ payload: cur.byteslice(9, length))
+ frame.freeze
end
- 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 = Frame::SETTINGS_TYPE.key(id)
- # ignore unknown settings type
- settings[name] = param.uint32(2) if name
- end
- settings
- end
-
- # Parses a frame from given buffer. It changes given buffer.
- # @param buffer [String] The buffer stored the data received from peer. Encoding must be Encoding::BINARY.
- # @return [Frame, nil] The parsed frame or nil if the buffer is imcomplete.
- def self.parse!(buffer)
- return nil if buffer.bytesize < 9 # header: 9 bytes
- length = buffer.uint24
- return nil if buffer.bytesize < 9 + length
- cur = buffer.byteshift(9 + length)
- type_value, flags_value, r_sid = cur.byteslice(3, 6).unpack("CCN")
- # r = r_sid >> 31 # currently not used
- stream_id = r_sid # & ~(1 << 31)
-
- self.new(type_value: type_value,
- flags_value: flags_value,
- stream_id: stream_id,
- payload: cur.byteslice(9, length)).freeze
- end
-
- # 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 self.rst_stream(stream_id, error_type)
- payload = String.new.push_uint32(HTTPError::ERROR_CODES[error_type])
- Frame.new(type: :rst_stream, stream_id: stream_id, payload: payload)
- end
-
- # 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 self.goaway(last_id, error_type, message = "")
- payload = String.new.push_uint32(last_id)
- .push_uint32(HTTPError::ERROR_CODES[error_type])
- .push(message)
- Frame.new(type: :goaway, stream_id: 0, payload: payload)
- end
-
- # 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 self.settings(ack = nil, **args)
- payload = String.new
- args.each { |key, value|
- id = Frame::SETTINGS_TYPE[key] or raise ArgumentError.new("invalid settings type")
- payload.push_uint16(id)
- payload.push_uint32(value)
- }
- Frame.new(type: :settings, stream_id: 0, flags: [ack], payload: payload)
- end
-
- # 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 self.ping(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
- Frame.new(type: :ping, stream_id: 0, payload: arg1)
- else
- Frame.new(type: :ping, stream_id: 0, flags: [:ack], payload: arg2)
+ # Sub classes must call this.
+ protected def register_subclass(type_value)
+ SUB_CLASSES[type_value] = self
end
end
-
- # Creates a DATA frame.
- # @param stream_id [Integer] The stream ID.
- # @param payload [String] Payload.
- # @param end_stream [Boolean] add END_STREAM flag
- def self.data(stream_id, payload = "", end_stream: false)
- payload = payload.b if payload&.encoding != Encoding::BINARY
- fval = end_stream ? 1 : 0
- Frame.new(type_value: 0, stream_id: stream_id, flags_value: fval, payload: payload)
- end
-
- # 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 self.headers(stream_id, encoded, end_stream: false, end_headers: false)
- fval = end_stream ? 1 : 0
- fval += 4 if end_headers
- Frame.new(type_value: 1, stream_id: stream_id, flags_value: fval, payload: encoded)
- end
-
- # 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 self.push_promise(stream_id, new_id, encoded, end_headers: false)
- payload = String.new.push_uint32(new_id)
- .push(encoded)
- fval = end_headers ? 4 : 0
- Frame.new(type: :push_promise, stream_id: stream_id, flags_value: fval, payload: payload)
- end
-
- # Creates a CONTINUATION frame.
- # @param stream_id [Integer] The stream ID.
- # @param payload [String] Payload.
- # @param end_headers [Boolean] add END_HEADERS flag
- def self.continuation(stream_id, payload, end_headers: false)
- Frame.new(type: :continuation, stream_id: stream_id, flags_value: (end_headers ? 4 : 0), payload: payload)
- end
end
end
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
diff --git a/lib/plum/server/http_connection.rb b/lib/plum/server/http_connection.rb
index b257137..1cf80dc 100644
--- a/lib/plum/server/http_connection.rb
+++ b/lib/plum/server/http_connection.rb
@@ -49,7 +49,7 @@ module Plum
def switch_protocol(settings, parser, headers, data)
self.on(:negotiated) {
- _frame = Frame.new(type: :settings, stream_id: 0, payload: Base64.urlsafe_decode64(settings))
+ _frame = Frame.craft(type: :settings, stream_id: 0, payload: Base64.urlsafe_decode64(settings))
receive_settings(_frame, send_ack: false) # HTTP2-Settings
process_first_request(parser, headers, data)
}
@@ -71,8 +71,8 @@ module Plum
":authority" => headers["host"] })
.reject { |n, v| ["connection", "http2-settings", "upgrade", "host"].include?(n) }
- stream.receive_frame Frame.headers(1, encoder.encode(nheaders), end_headers: true)
- stream.receive_frame Frame.data(1, body, end_stream: true)
+ stream.receive_frame Frame::Headers.new(1, encoder.encode(nheaders), end_headers: true)
+ stream.receive_frame Frame::Data.new(1, body, end_stream: true)
end
end
end
diff --git a/lib/plum/stream.rb b/lib/plum/stream.rb
index 733aeaf..420ee23 100644
--- a/lib/plum/stream.rb
+++ b/lib/plum/stream.rb
@@ -53,7 +53,7 @@ module Plum
end
rescue RemoteStreamError => e
callback(:stream_error, e)
- send_immediately Frame.rst_stream(id, e.http2_error_type)
+ send_immediately Frame::RstStream.new(id, e.http2_error_type)
close
end
@@ -97,7 +97,7 @@ module Plum
def promise(headers)
stream = @connection.reserve_stream(weight: self.weight + 1, parent: self)
encoded = @connection.hpack_encoder.encode(headers)
- frame = Frame.push_promise(id, stream.id, encoded, end_headers: true)
+ frame = Frame::PushPromise.new(id, stream.id, encoded, end_headers: true)
send frame
stream
end
@@ -107,7 +107,7 @@ module Plum
# @param end_stream [Boolean] Set END_STREAM flag or not.
def send_headers(headers, end_stream:)
encoded = @connection.hpack_encoder.encode(headers)
- frame = Frame.headers(id, encoded, end_headers: true, end_stream: end_stream)
+ frame = Frame::Headers.new(id, encoded, end_headers: true, end_stream: end_stream)
send frame
@state = :half_closed_local if end_stream
end
@@ -120,10 +120,10 @@ module Plum
if data.is_a?(IO)
until data.eof?
fragment = data.readpartial(max)
- send Frame.data(id, fragment, end_stream: end_stream && data.eof?)
+ send Frame::Data.new(id, fragment, end_stream: end_stream && data.eof?)
end
else
- send Frame.data(id, data, end_stream: end_stream)
+ send Frame::Data.new(id, data, end_stream: end_stream)
end
@state = :half_closed_local if end_stream
end
diff --git a/test/plum/client/test_upgrade_client_session.rb b/test/plum/client/test_upgrade_client_session.rb
index 3cc97b4..446462c 100644
--- a/test/plum/client/test_upgrade_client_session.rb
+++ b/test/plum/client/test_upgrade_client_session.rb
@@ -21,11 +21,11 @@ class UpgradeClientSessionTest < Minitest::Test
def test_request
sock = StringSocket.new("HTTP/1.1 101\r\n\r\n")
session = UpgradeClientSession.new(sock, Client::DEFAULT_CONFIG)
- sock.rio.string << Frame.settings().assemble
- sock.rio.string << Frame.settings(:ack).assemble
+ sock.rio.string << Frame::Settings.new.assemble
+ sock.rio.string << Frame::Settings.new(:ack).assemble
res = session.request({ ":method" => "GET", ":path" => "/aa" }, "aa", {})
- sock.rio.string << Frame.headers(3, HPACK::Encoder.new(3).encode(":status" => "200", "content-length" => "3"), end_headers: true).assemble
- sock.rio.string << Frame.data(3, "aaa", end_stream: true).assemble
+ sock.rio.string << Frame::Headers.new(3, HPACK::Encoder.new(3).encode(":status" => "200", "content-length" => "3"), end_headers: true).assemble
+ sock.rio.string << Frame::Data.new(3, "aaa", end_stream: true).assemble
session.succ until res.finished?
assert(res.finished?)
assert_equal("aaa", res.body)
diff --git a/test/plum/connection/test_handle_frame.rb b/test/plum/connection/test_handle_frame.rb
index 704c128..ca4ef18 100644
--- a/test/plum/connection/test_handle_frame.rb
+++ b/test/plum/connection/test_handle_frame.rb
@@ -7,7 +7,7 @@ class ServerConnectionHandleFrameTest < Minitest::Test
def test_server_handle_settings
open_server_connection { |con|
assert_equal(4096, con.remote_settings[:header_table_size])
- con << Frame.new(type: :settings, stream_id: 0, payload: "\x00\x01\x00\x00\x10\x10").assemble
+ con << Frame.craft(type: :settings, stream_id: 0, payload: "\x00\x01\x00\x00\x10\x10").assemble
assert_equal(0x1010, con.remote_settings[:header_table_size])
}
end
@@ -15,10 +15,10 @@ class ServerConnectionHandleFrameTest < Minitest::Test
def test_server_handle_settings
open_server_connection { |con|
assert_no_error {
- con << Frame.new(type: :settings, stream_id: 0, flags: [:ack], payload: "").assemble
+ con << Frame.craft(type: :settings, stream_id: 0, flags: [:ack], payload: "").assemble
}
assert_connection_error(:frame_size_error) {
- con << Frame.new(type: :settings, stream_id: 0, flags: [:ack], payload: "\x00").assemble
+ con << Frame.craft(type: :settings, stream_id: 0, flags: [:ack], payload: "\x00").assemble
}
}
end
@@ -26,7 +26,7 @@ class ServerConnectionHandleFrameTest < Minitest::Test
def test_server_handle_settings_invalid
open_server_connection { |con|
assert_no_error {
- con << Frame.new(type: :settings, stream_id: 0, payload: "\xff\x01\x00\x00\x10\x10").assemble
+ con << Frame.craft(type: :settings, stream_id: 0, payload: "\xff\x01\x00\x00\x10\x10").assemble
}
}
end
@@ -34,7 +34,7 @@ class ServerConnectionHandleFrameTest < Minitest::Test
## PING
def test_server_handle_ping
open_server_connection { |con|
- con << Frame.new(type: :ping, flags: [], stream_id: 0, payload: "AAAAAAAA").assemble
+ con << Frame.craft(type: :ping, flags: [], stream_id: 0, payload: "AAAAAAAA").assemble
last = sent_frames.last
assert_equal(:ping, last.type)
assert_equal([:ack], last.flags)
@@ -45,14 +45,14 @@ class ServerConnectionHandleFrameTest < Minitest::Test
def test_server_handle_ping_error
open_server_connection { |con|
assert_connection_error(:frame_size_error) {
- con << Frame.new(type: :ping, stream_id: 0, payload: "A" * 7).assemble
+ con << Frame.craft(type: :ping, stream_id: 0, payload: "A" * 7).assemble
}
}
end
def test_server_handle_ping_ack
open_server_connection { |con|
- con << Frame.new(type: :ping, flags: [:ack], stream_id: 0, payload: "A" * 8).assemble
+ con << Frame.craft(type: :ping, flags: [:ack], stream_id: 0, payload: "A" * 8).assemble
last = sent_frames.last
refute_equal(:ping, last.type) if last
}
@@ -63,7 +63,7 @@ class ServerConnectionHandleFrameTest < Minitest::Test
open_server_connection { |con|
assert_no_error {
begin
- con << Frame.goaway(1, :stream_closed).assemble
+ con << Frame::Goaway.new(1, :stream_closed).assemble
rescue LocalHTTPError
end
}
diff --git a/test/plum/server/test_connection.rb b/test/plum/server/test_connection.rb
index 87e1893..ccc9dae 100644
--- a/test/plum/server/test_connection.rb
+++ b/test/plum/server/test_connection.rb
@@ -21,7 +21,7 @@ class HTTPSConnectionNegotiationTest < Minitest::Test
con = ServerConnection.new(StringIO.new.method(:write))
con << Connection::CLIENT_CONNECTION_PREFACE
assert_connection_error(:protocol_error) {
- con << Frame.new(type: :window_update, stream_id: 0, payload: "".push_uint32(1)).assemble
+ con << Frame::WindowUpdate.new(0, 1).assemble
}
end
@@ -31,7 +31,7 @@ class HTTPSConnectionNegotiationTest < Minitest::Test
assert_no_error {
con << magic[0...5]
con << magic[5..-1]
- con << Frame.new(type: :settings, stream_id: 0).assemble
+ con << Frame::Settings.new.assemble
}
end
@@ -57,10 +57,9 @@ class HTTPSConnectionNegotiationTest < Minitest::Test
end
}
}
+ rescue Errno::EPIPE
rescue Timeout::Error
flunk "server timeout"
- rescue => e
- flunk e
ensure
tcp_server.close
end
@@ -75,16 +74,14 @@ class HTTPSConnectionNegotiationTest < Minitest::Test
ssl = OpenSSL::SSL::SSLSocket.new(sock, ctx)
ssl.connect
ssl.write Connection::CLIENT_CONNECTION_PREFACE
- ssl.write Frame.settings.assemble
- sleep
- rescue => e
- flunk e
+ ssl.write Frame::Settings.new.assemble
+ rescue Errno::EPIPE
ensure
sock.close
end
}
server_thread.join
- client_thread.kill
+ client_thread.join
flunk "test not run" unless run
end
diff --git a/test/plum/server/test_http_connection.rb b/test/plum/server/test_http_connection.rb
index 1326deb..e03523b 100644
--- a/test/plum/server/test_http_connection.rb
+++ b/test/plum/server/test_http_connection.rb
@@ -9,7 +9,7 @@ class HTTPConnectionNegotiationTest < Minitest::Test
con = HTTPServerConnection.new(io.method(:write))
con << Connection::CLIENT_CONNECTION_PREFACE
assert_connection_error(:protocol_error) {
- con << Frame.new(type: :window_update, stream_id: 0, payload: "".push_uint32(1)).assemble
+ con << Frame::WindowUpdate.new(0, 1).assemble
}
end
@@ -20,7 +20,7 @@ class HTTPConnectionNegotiationTest < Minitest::Test
assert_no_error {
con << magic[0...5]
con << magic[5..-1]
- con << Frame.new(type: :settings, stream_id: 0).assemble
+ con << Frame::Settings.new.assemble
}
end
@@ -41,7 +41,7 @@ class HTTPConnectionNegotiationTest < Minitest::Test
assert(io.string.include?("HTTP/1.1 101 "), "Response is not HTTP/1.1 101: #{io.string}")
assert_no_error {
con << Connection::CLIENT_CONNECTION_PREFACE
- con << Frame.new(type: :settings, stream_id: 0).assemble
+ con << Frame::Settings.new.assemble
}
assert_equal(:half_closed_remote, con.streams[1].state)
assert_equal({ ":method" => "GET", ":path" => "/", ":authority" => "rhe.jp", "user-agent" => "nya"}, heads)
diff --git a/test/plum/stream/test_handle_frame.rb b/test/plum/stream/test_handle_frame.rb
index 2c14c26..6defb82 100644
--- a/test/plum/stream/test_handle_frame.rb
+++ b/test/plum/stream/test_handle_frame.rb
@@ -9,7 +9,7 @@ class StreamHandleFrameTest < Minitest::Test
open_new_stream(state: :open) { |stream|
data = nil
stream.connection.on(:data) { |_, _data| data = _data }
- stream.receive_frame(Frame.new(type: :data, stream_id: stream.id,
+ stream.receive_frame(Frame.craft(type: :data, stream_id: stream.id,
flags: [], payload: payload))
assert_equal(payload, data)
}
@@ -20,7 +20,7 @@ class StreamHandleFrameTest < Minitest::Test
open_new_stream(state: :open) { |stream|
data = nil
stream.connection.on(:data) { |_, _data| data = _data }
- stream.receive_frame(Frame.new(type: :data, stream_id: stream.id,
+ stream.receive_frame(Frame.craft(type: :data, stream_id: stream.id,
flags: [:padded], payload: "".push_uint8(6).push(payload).push("\x00"*6)))
assert_equal(payload, data)
}
@@ -30,7 +30,7 @@ class StreamHandleFrameTest < Minitest::Test
payload = "ABC" * 5
open_new_stream(state: :open) { |stream|
assert_connection_error(:protocol_error) {
- stream.receive_frame(Frame.new(type: :data, stream_id: stream.id,
+ stream.receive_frame(Frame.craft(type: :data, stream_id: stream.id,
flags: [:padded], payload: "".push_uint8(100).push(payload).push("\x00"*6)))
}
}
@@ -39,7 +39,7 @@ class StreamHandleFrameTest < Minitest::Test
def test_stream_handle_data_end_stream
payload = "ABC" * 5
open_new_stream(state: :open) { |stream|
- stream.receive_frame(Frame.new(type: :data, stream_id: stream.id,
+ stream.receive_frame(Frame.craft(type: :data, stream_id: stream.id,
flags: [:end_stream], payload: payload))
assert_equal(:half_closed_remote, stream.state)
}
@@ -49,7 +49,7 @@ class StreamHandleFrameTest < Minitest::Test
payload = "ABC" * 5
open_new_stream(state: :half_closed_remote) { |stream|
assert_stream_error(:stream_closed) {
- stream.receive_frame(Frame.new(type: :data, stream_id: stream.id,
+ stream.receive_frame(Frame.craft(type: :data, stream_id: stream.id,
flags: [:end_stream], payload: payload))
}
}
@@ -62,7 +62,7 @@ class StreamHandleFrameTest < Minitest::Test
stream.connection.on(:headers) { |_, _headers|
headers = _headers
}
- stream.receive_frame(Frame.new(type: :headers,
+ stream.receive_frame(Frame.craft(type: :headers,
stream_id: stream.id,
flags: [:end_headers],
payload: HPACK::Encoder.new(0).encode([[":path", "/"]])))
@@ -78,12 +78,12 @@ class StreamHandleFrameTest < Minitest::Test
stream.connection.on(:headers) { |_, _headers|
headers = _headers
}
- stream.receive_frame(Frame.new(type: :headers,
+ stream.receive_frame(Frame.craft(type: :headers,
stream_id: stream.id,
flags: [:end_stream],
payload: payload[0..4]))
assert_equal(nil, headers) # wait CONTINUATION
- stream.receive_frame(Frame.new(type: :continuation,
+ stream.receive_frame(Frame.craft(type: :continuation,
stream_id: stream.id,
flags: [:end_headers],
payload: payload[5..-1]))
@@ -99,7 +99,7 @@ class StreamHandleFrameTest < Minitest::Test
stream.connection.on(:headers) { |_, _headers|
headers = _headers
}
- stream.receive_frame(Frame.new(type: :headers,
+ stream.receive_frame(Frame.craft(type: :headers,
stream_id: stream.id,
flags: [:end_headers, :padded],
payload: "".push_uint8(payload.bytesize).push(payload).push("\x00"*payload.bytesize)))
@@ -111,7 +111,7 @@ class StreamHandleFrameTest < Minitest::Test
open_new_stream { |stream|
payload = HPACK::Encoder.new(0).encode([[":path", "/"]])
assert_connection_error(:protocol_error) {
- stream.receive_frame(Frame.new(type: :headers,
+ stream.receive_frame(Frame.craft(type: :headers,
stream_id: stream.id,
flags: [:end_headers, :padded],
payload: "".push_uint8(payload.bytesize+1).push(payload).push("\x00"*(payload.bytesize+1))))
@@ -123,7 +123,7 @@ class StreamHandleFrameTest < Minitest::Test
open_new_stream { |stream|
payload = "\x00\x01\x02"
assert_connection_error(:compression_error) {
- stream.receive_frame(Frame.new(type: :headers,
+ stream.receive_frame(Frame.craft(type: :headers,
stream_id: stream.id,
flags: [:end_headers],
payload: payload))
@@ -135,17 +135,17 @@ class StreamHandleFrameTest < Minitest::Test
_payload = HPACK::Encoder.new(0).encode([[":path", "/"]])
open_new_stream(state: :reserved_local) { |stream|
assert_connection_error(:protocol_error) {
- stream.receive_frame(Frame.new(type: :headers, stream_id: stream.id, flags: [:end_headers, :end_stream], payload: _payload))
+ stream.receive_frame(Frame.craft(type: :headers, stream_id: stream.id, flags: [:end_headers, :end_stream], payload: _payload))
}
}
open_new_stream(state: :closed) { |stream|
assert_connection_error(:stream_closed) {
- stream.receive_frame(Frame.new(type: :headers, stream_id: stream.id, flags: [:end_headers, :end_stream], payload: _payload))
+ stream.receive_frame(Frame.craft(type: :headers, stream_id: stream.id, flags: [:end_headers, :end_stream], payload: _payload))
}
}
open_new_stream(state: :half_closed_remote) { |stream|
assert_stream_error(:stream_closed) {
- stream.receive_frame(Frame.new(type: :headers, stream_id: stream.id, flags: [:end_headers, :end_stream], payload: _payload))
+ stream.receive_frame(Frame.craft(type: :headers, stream_id: stream.id, flags: [:end_headers, :end_stream], payload: _payload))
}
}
end
@@ -161,7 +161,7 @@ class StreamHandleFrameTest < Minitest::Test
payload = "".push_uint32((1 << 31) | parent.id)
.push_uint8(50)
.push(header_block)
- stream.receive_frame(Frame.new(type: :headers,
+ stream.receive_frame(Frame.craft(type: :headers,
stream_id: stream.id,
flags: [:end_headers, :priority],
payload: payload))
@@ -180,7 +180,7 @@ class StreamHandleFrameTest < Minitest::Test
payload = "".push_uint32((1 << 31) | parent.id)
.push_uint8(50)
- stream.receive_frame(Frame.new(type: :priority,
+ stream.receive_frame(Frame.craft(type: :priority,
stream_id: stream.id,
payload: payload))
assert_equal(true, stream.exclusive)
@@ -193,7 +193,7 @@ class StreamHandleFrameTest < Minitest::Test
open_server_connection { |con|
stream = open_new_stream(con)
payload = "".push_uint32((0 << 31) | stream.id).push_uint8(6)
- stream.receive_frame(Frame.new(type: :priority,
+ stream.receive_frame(Frame.craft(type: :priority,
stream_id: stream.id,
payload: payload))
last = sent_frames.last
@@ -210,7 +210,7 @@ class StreamHandleFrameTest < Minitest::Test
stream2 = open_new_stream(con, parent: parent)
payload = "".push_uint32((1 << 31) | parent.id).push_uint8(6)
- stream0.receive_frame(Frame.new(type: :priority,
+ stream0.receive_frame(Frame.craft(type: :priority,
stream_id: stream0.id,
payload: payload))
assert_equal(parent, stream0.parent)
@@ -222,7 +222,7 @@ class StreamHandleFrameTest < Minitest::Test
def test_stream_handle_frame_size_error
open_new_stream { |stream|
assert_stream_error(:frame_size_error) {
- stream.receive_frame(Frame.new(type: :priority,
+ stream.receive_frame(Frame.craft(type: :priority,
stream_id: stream.id,
payload: "\x00"))
}
@@ -232,7 +232,7 @@ class StreamHandleFrameTest < Minitest::Test
## RST_STREAM
def test_stream_handle_rst_stream
open_new_stream(state: :reserved_local) { |stream|
- stream.receive_frame(Frame.new(type: :rst_stream,
+ stream.receive_frame(Frame.craft(type: :rst_stream,
stream_id: stream.id,
payload: "\x00\x00\x00\x00"))
assert_equal(:closed, stream.state)
@@ -242,7 +242,7 @@ class StreamHandleFrameTest < Minitest::Test
def test_stream_handle_rst_stream_idle
open_new_stream(state: :idle) { |stream|
assert_connection_error(:protocol_error) {
- stream.receive_frame(Frame.new(type: :rst_stream,
+ stream.receive_frame(Frame.craft(type: :rst_stream,
stream_id: stream.id,
payload: "\x00\x00\x00\x00"))
}
@@ -252,7 +252,7 @@ class StreamHandleFrameTest < Minitest::Test
def test_stream_handle_rst_stream_frame_size
open_new_stream(state: :reserved_local) { |stream|
assert_connection_error(:frame_size_error) {
- stream.receive_frame(Frame.new(type: :rst_stream,
+ stream.receive_frame(Frame.craft(type: :rst_stream,
stream_id: stream.id,
payload: "\x00\x00\x00"))
}
diff --git a/test/plum/test_connection.rb b/test/plum/test_connection.rb
index fad70b4..a28672b 100644
--- a/test/plum/test_connection.rb
+++ b/test/plum/test_connection.rb
@@ -4,7 +4,7 @@ using Plum::BinaryString
class ConnectionTest < Minitest::Test
def test_server_must_raise_frame_size_error_when_exeeeded_max_size
- _settings = "".push_uint16(Frame::SETTINGS_TYPE[:max_frame_size]).push_uint32(2**14)
+ _settings = "".push_uint16(Frame::Settings::SETTINGS_TYPE[:max_frame_size]).push_uint32(2**14)
limit = 2 ** 14
new_con = -> (&blk) {
@@ -15,23 +15,23 @@ class ConnectionTest < Minitest::Test
new_con.call { |con|
assert_no_error {
- con << Frame.new(type: :settings, stream_id: 0, payload: _settings * (limit / 6)).assemble
+ con << Frame.craft(type: :settings, stream_id: 0, payload: _settings * (limit / 6)).assemble
}
}
new_con.call { |con|
assert_connection_error(:frame_size_error) {
- con << Frame.new(type: :settings, stream_id: 0, payload: _settings * (limit / 6 + 1)).assemble
+ con << Frame.craft(type: :settings, stream_id: 0, payload: _settings * (limit / 6 + 1)).assemble
}
}
new_con.call { |con|
assert_connection_error(:frame_size_error) {
- con << Frame.new(type: :headers, stream_id: 3, payload: "\x00" * (limit + 1)).assemble
+ con << Frame.craft(type: :headers, stream_id: 3, payload: "\x00" * (limit + 1)).assemble
}
}
new_con.call { |con|
assert_stream_error(:frame_size_error) {
- con << Frame.new(type: :headers, stream_id: 3, flags: [:end_headers], payload: "").assemble
- con << Frame.new(type: :data, stream_id: 3, payload: "\x00" * (limit + 1)).assemble
+ con << Frame.craft(type: :headers, stream_id: 3, flags: [:end_headers], payload: "").assemble
+ con << Frame.craft(type: :data, stream_id: 3, payload: "\x00" * (limit + 1)).assemble
}
}
end
@@ -40,7 +40,7 @@ class ConnectionTest < Minitest::Test
[:data, :headers, :priority, :rst_stream, :push_promise, :continuation].each do |type|
con = open_server_connection
assert_connection_error(:protocol_error) {
- con << Frame.new(type: type, stream_id: 0).assemble
+ con << Frame.craft(type: type, stream_id: 0).assemble
}
end
end
@@ -56,39 +56,39 @@ class ConnectionTest < Minitest::Test
def test_server_raise_cprotocol_error_client_start_even_stream_id
con = open_server_connection
assert_connection_error(:protocol_error) {
- con << Frame.new(type: :headers, flags: [:end_headers], stream_id: 2).assemble
+ con << Frame.craft(type: :headers, flags: [:end_headers], stream_id: 2).assemble
}
end
def test_server_raise_cprotocol_error_client_start_small_stream_id
con = open_server_connection
- con << Frame.new(type: :headers, flags: [:end_headers], stream_id: 51).assemble
+ con << Frame.craft(type: :headers, flags: [:end_headers], stream_id: 51).assemble
assert_connection_error(:protocol_error) {
- con << Frame.new(type: :headers, flags: [:end_headers], stream_id: 31).assemble
+ con << Frame.craft(type: :headers, flags: [:end_headers], stream_id: 31).assemble
}
end
def test_server_raise_cprotocol_error_invalid_continuation_state
prepare = -> &blk {
con = open_server_connection
- con << Frame.new(type: :headers, flags: [:end_headers], stream_id: 1).assemble
- con << Frame.new(type: :headers, flags: [:end_stream], stream_id: 3).assemble
+ con << Frame.craft(type: :headers, flags: [:end_headers], stream_id: 1).assemble
+ con << Frame.craft(type: :headers, flags: [:end_stream], stream_id: 3).assemble
blk.call(con)
}
prepare.call { |con|
assert_connection_error(:protocol_error) {
- con << Frame.new(type: :data, stream_id: 1, payload: "hello").assemble
+ con << Frame.craft(type: :data, stream_id: 1, payload: "hello").assemble
}
}
prepare.call { |con|
assert_connection_error(:protocol_error) {
- con << Frame.new(type: :data, stream_id: 3, payload: "hello").assemble
+ con << Frame.craft(type: :data, stream_id: 3, payload: "hello").assemble
}
}
prepare.call { |con|
assert_equal(:waiting_continuation, con.state)
- con << Frame.new(type: :continuation, flags: [:end_headers], stream_id: 3, payload: "").assemble
+ con << Frame.craft(type: :continuation, flags: [:end_headers], stream_id: 3, payload: "").assemble
assert_equal(:open, con.state)
}
end
@@ -96,7 +96,7 @@ class ConnectionTest < Minitest::Test
def test_connection_local_error
open_server_connection { |con|
assert_raises(LocalConnectionError) {
- con << Frame.goaway(0, :frame_size_error).assemble
+ con << Frame::Goaway.new(0, :frame_size_error).assemble
}
}
end
@@ -105,7 +105,7 @@ class ConnectionTest < Minitest::Test
io = StringIO.new
con = Connection.new(io.method(:write))
fs = parse_frames(io) {
- con.__send__(:send_immediately, Frame.new(type: :data, stream_id: 1, payload: "a"*16385))
+ con.__send__(:send_immediately, Frame.craft(type: :data, stream_id: 1, payload: "a"*16385))
}
assert_equal(2, fs.size)
assert_equal(16384, fs.first.length)
diff --git a/test/plum/test_connection_utils.rb b/test/plum/test_connection_utils.rb
index 1147dd3..785c74c 100644
--- a/test/plum/test_connection_utils.rb
+++ b/test/plum/test_connection_utils.rb
@@ -16,7 +16,7 @@ class ServerConnectionUtilsTest < Minitest::Test
def test_server_goaway
open_server_connection { |con|
- con << Frame.headers(3, "", end_stream: true, end_headers: true).assemble
+ con << Frame::Headers.new(3, "", end_stream: true, end_headers: true).assemble
con.goaway(:stream_closed)
last = sent_frames.last
@@ -29,9 +29,9 @@ class ServerConnectionUtilsTest < Minitest::Test
def test_push_enabled
open_server_connection { |con|
- con << Frame.settings(enable_push: 0).assemble
+ con << Frame::Settings.new(enable_push: 0).assemble
assert_equal(false, con.push_enabled?)
- con << Frame.settings(enable_push: 1).assemble
+ con << Frame::Settings.new(enable_push: 1).assemble
assert_equal(true, con.push_enabled?)
}
end
diff --git a/test/plum/test_flow_control.rb b/test/plum/test_flow_control.rb
index 316361c..38958b7 100644
--- a/test/plum/test_flow_control.rb
+++ b/test/plum/test_flow_control.rb
@@ -32,9 +32,7 @@ class FlowControlTest < Minitest::Test
def test_flow_control_window_update_zero
open_new_stream { |stream|
assert_stream_error(:protocol_error) {
- stream.receive_frame Frame.new(type: :window_update,
- stream_id: stream.id,
- payload: "".push_uint32(0))
+ stream.receive_frame Frame::WindowUpdate.new(stream.id, 0)
}
}
end
@@ -42,9 +40,9 @@ class FlowControlTest < Minitest::Test
def test_flow_control_window_update_frame_size
open_new_stream { |stream|
assert_connection_error(:frame_size_error) {
- stream.receive_frame Frame.new(type: :window_update,
- stream_id: stream.id,
- payload: "".push_uint16(0))
+ stream.receive_frame Frame.craft(type: :window_update,
+ stream_id: stream.id,
+ payload: "".push_uint16(0))
}
}
end
@@ -52,18 +50,11 @@ class FlowControlTest < Minitest::Test
def test_flow_control_dont_send_data_exceeding_send_window
open_new_stream { |stream|
con = stream.connection
- con << Frame.new(type: :settings,
- stream_id: 0,
- payload: "".push_uint16(Frame::SETTINGS_TYPE[:initial_window_size])
- .push_uint32(4*2+1)).assemble
+ con << Frame::Settings.new(initial_window_size: 4 * 2 + 1).assemble
# only extend stream window size
- con << Frame.new(type: :window_update,
- stream_id: stream.id,
- payload: "".push_uint32(100)).assemble
+ con << Frame::WindowUpdate.new(stream.id, 100).assemble
10.times { |i|
- stream.send Frame.new(type: :data,
- stream_id: stream.id,
- payload: "".push_uint32(i))
+ stream.send Frame::Data.new(stream.id, "".push_uint32(i))
}
last = sent_frames.last
@@ -74,23 +65,14 @@ class FlowControlTest < Minitest::Test
def test_flow_control_dont_send_data_upto_updated_send_window
open_new_stream { |stream|
con = stream.connection
- con << Frame.new(type: :settings,
- stream_id: 0,
- payload: "".push_uint16(Frame::SETTINGS_TYPE[:initial_window_size])
- .push_uint32(4*2+1)).assemble
+ con << Frame::Settings.new(initial_window_size: 4 * 2 + 1).assemble
10.times { |i|
- stream.send Frame.new(type: :data,
- stream_id: stream.id,
- payload: "".push_uint32(i))
+ stream.send Frame::Data.new(stream.id, "".push_uint32(i))
}
# only extend stream window size
- con << Frame.new(type: :window_update,
- stream_id: stream.id,
- payload: "".push_uint32(100)).assemble
+ con << Frame::WindowUpdate.new(stream.id, 100).assemble
# and extend connection window size
- con << Frame.new(type: :window_update,
- stream_id: 0,
- payload: "".push_uint32(4*2+1)).assemble
+ con << Frame::WindowUpdate.new(0, 4 * 2 + 1).assemble
last = sent_frames.last
assert_equal(3, last.payload.uint32)
@@ -100,24 +82,14 @@ class FlowControlTest < Minitest::Test
def test_flow_control_update_send_initial_window_size
open_new_stream { |stream|
con = stream.connection
- con << Frame.new(type: :settings,
- stream_id: 0,
- payload: "".push_uint16(Frame::SETTINGS_TYPE[:initial_window_size])
- .push_uint32(4*2+1)).assemble
+ con << Frame::Settings.new(initial_window_size: 4 * 2 + 1).assemble
10.times { |i|
- stream.send Frame.new(type: :data,
- stream_id: stream.id,
- payload: "".push_uint32(i))
+ stream.send Frame::Data.new(stream.id, "".push_uint32(i))
}
# only extend stream window size
- con << Frame.new(type: :window_update,
- stream_id: stream.id,
- payload: "".push_uint32(100)).assemble
+ con << Frame::WindowUpdate.new(stream.id, 100).assemble
# and update initial window size
- con << Frame.new(type: :settings,
- stream_id: 0,
- payload: "".push_uint16(Frame::SETTINGS_TYPE[:initial_window_size])
- .push_uint32(4*4+1)).assemble
+ con << Frame::Settings.new(initial_window_size: 4 * 4 + 1).assemble
last = sent_frames.reverse.find { |f| f.type == :data }
assert_equal(3, last.payload.uint32)
@@ -135,17 +107,17 @@ class FlowControlTest < Minitest::Test
prepare.call { |con, stream|
con.window_update(500) # extend only connection
- con << Frame.headers(stream.id, "", end_headers: true).assemble
+ con << Frame::Headers.new(stream.id, "", end_headers: true).assemble
assert_stream_error(:flow_control_error) {
- con << Frame.data(stream.id, "\x00" * 30, end_stream: true).assemble
+ con << Frame::Data.new(stream.id, "\x00" * 30, end_stream: true).assemble
}
}
prepare.call { |con, stream|
stream.window_update(500) # extend only stream
- con << Frame.headers(stream.id, "", end_headers: true).assemble
+ con << Frame::Headers.new(stream.id, "", end_headers: true).assemble
assert_connection_error(:flow_control_error) {
- con << Frame.data(stream.id, "\x00" * 30, end_stream: true).assemble
+ con << Frame::Data.new(stream.id, "\x00" * 30, end_stream: true).assemble
}
}
end
@@ -155,8 +127,8 @@ class FlowControlTest < Minitest::Test
con = stream.connection
con.settings(initial_window_size: 24)
stream.window_update(1)
- con << Frame.headers(stream.id, "", end_headers: true).assemble
- con << Frame.data(stream.id, "\x00" * 20, end_stream: true).assemble
+ con << Frame::Headers.new(stream.id, "", end_headers: true).assemble
+ con << Frame::Data.new(stream.id, "\x00" * 20, end_stream: true).assemble
assert_equal(4, con.recv_remaining_window)
assert_equal(5, stream.recv_remaining_window)
con.settings(initial_window_size: 60)
diff --git a/test/plum/test_frame.rb b/test/plum/test_frame.rb
index 4795627..40cde01 100644
--- a/test/plum/test_frame.rb
+++ b/test/plum/test_frame.rb
@@ -34,17 +34,17 @@ class FrameTest < Minitest::Test
# Frame#assemble
def test_assemble
- frame = Plum::Frame.new(type: :push_promise, flags: [:end_headers, :padded], stream_id: 0x678, payload: "payl")
+ frame = Plum::Frame.craft(type: :push_promise, flags: [:end_headers, :padded], stream_id: 0x678, payload: "payl")
bin = "\x00\x00\x04" << "\x05" << "\x0c" << "\x00\x00\x06\x78" << "payl"
assert_equal(bin, frame.assemble)
end
- # Frame#generate
- def test_new
- frame = Plum::Frame.new(type: :data,
- stream_id: 12345,
- flags: [:end_stream, :padded],
- payload: "ぺいろーど".encode(Encoding::UTF_8))
+ # Frame.craft
+ def test_new_raw
+ frame = Plum::Frame.craft(type: :data,
+ stream_id: 12345,
+ flags: [:end_stream, :padded],
+ payload: "ぺいろーど".encode(Encoding::UTF_8))
assert_equal("ぺいろーど", frame.payload)
assert_equal("ぺいろーど".bytesize, frame.length)
assert_equal(:data, frame.type) # DATA
@@ -53,10 +53,10 @@ class FrameTest < Minitest::Test
end
def test_inspect
- frame = Plum::Frame.new(type: :data,
- stream_id: 12345,
- flags: [:end_stream, :padded],
- payload: "ぺいろーど")
+ frame = Plum::Frame.craft(type: :data,
+ stream_id: 12345,
+ flags: [:end_stream, :padded],
+ payload: "ぺいろーど")
frame.inspect
end
end
diff --git a/test/plum/test_frame_factory.rb b/test/plum/test_frame_factory.rb
index 109fb95..a4253ba 100644
--- a/test/plum/test_frame_factory.rb
+++ b/test/plum/test_frame_factory.rb
@@ -3,7 +3,7 @@ require "test_helper"
using Plum::BinaryString
class FrameFactoryTest < Minitest::Test
def test_rst_stream
- frame = Frame.rst_stream(123, :stream_closed)
+ frame = Frame::RstStream.new(123, :stream_closed)
assert_frame(frame,
type: :rst_stream,
stream_id: 123)
@@ -11,7 +11,7 @@ class FrameFactoryTest < Minitest::Test
end
def test_goaway
- frame = Frame.goaway(0x55, :stream_closed, "debug")
+ frame = Frame::Goaway.new(0x55, :stream_closed, "debug")
assert_frame(frame,
type: :goaway,
stream_id: 0,
@@ -19,7 +19,7 @@ class FrameFactoryTest < Minitest::Test
end
def test_settings
- frame = Frame.settings(header_table_size: 0x1010)
+ frame = Frame::Settings.new(header_table_size: 0x1010)
assert_frame(frame,
type: :settings,
stream_id: 0,
@@ -28,7 +28,7 @@ class FrameFactoryTest < Minitest::Test
end
def test_settings_ack
- frame = Frame.settings(:ack)
+ frame = Frame::Settings.new(:ack)
assert_frame(frame,
type: :settings,
stream_id: 0,
@@ -37,7 +37,7 @@ class FrameFactoryTest < Minitest::Test
end
def test_ping
- frame = Frame.ping("12345678")
+ frame = Frame::Ping.new("12345678")
assert_frame(frame,
type: :ping,
stream_id: 0,
@@ -46,7 +46,7 @@ class FrameFactoryTest < Minitest::Test
end
def test_ping_ack
- frame = Frame.ping(:ack, "12345678")
+ frame = Frame::Ping.new(:ack, "12345678")
assert_frame(frame,
type: :ping,
stream_id: 0,
@@ -55,7 +55,7 @@ class FrameFactoryTest < Minitest::Test
end
def test_continuation
- frame = Frame.continuation(123, "abc", end_headers: true)
+ frame = Frame::Continuation.new(123, "abc", end_headers: true)
assert_frame(frame,
type: :continuation,
stream_id: 123,
@@ -64,7 +64,7 @@ class FrameFactoryTest < Minitest::Test
end
def test_data
- frame = Frame.data(123, "abc".force_encoding("UTF-8"))
+ frame = Frame::Data.new(123, "abc".force_encoding("UTF-8"))
assert_frame(frame,
type: :data,
stream_id: 123,
@@ -74,7 +74,7 @@ class FrameFactoryTest < Minitest::Test
end
def test_headers
- frame = Frame.headers(123, "abc", end_stream: true)
+ frame = Frame::Headers.new(123, "abc", end_stream: true)
assert_frame(frame,
type: :headers,
stream_id: 123,
@@ -83,7 +83,7 @@ class FrameFactoryTest < Minitest::Test
end
def test_push_promise
- frame = Frame.push_promise(345, 2, "abc", end_headers: true)
+ frame = Frame::PushPromise.new(345, 2, "abc", end_headers: true)
assert_frame(frame,
type: :push_promise,
stream_id: 345,
diff --git a/test/plum/test_frame_utils.rb b/test/plum/test_frame_utils.rb
index 7ad869f..3851f6a 100644
--- a/test/plum/test_frame_utils.rb
+++ b/test/plum/test_frame_utils.rb
@@ -2,19 +2,14 @@ require "test_helper"
class FrameUtilsTest < Minitest::Test
def test_frame_enough_short
- frame = Frame.new(type: :data, stream_id: 1, payload: "123")
+ frame = Frame::Data.new(1, "123")
ret = frame.to_enum(:split, 3).to_a
assert_equal(1, ret.size)
assert_equal("123", ret.first.payload)
end
- def test_frame_unknown
- frame = Frame.new(type: :settings, stream_id: 1, payload: "123")
- assert_raises(NotImplementedError) { frame.split(2) }
- end
-
def test_frame_data
- frame = Frame.new(type: :data, flags: [:end_stream], stream_id: 1, payload: "12345")
+ frame = Frame::Data.new(1, "12345", end_stream: true)
ret = frame.to_enum(:split, 2).to_a
assert_equal(3, ret.size)
assert_equal("12", ret.first.payload)
@@ -23,8 +18,8 @@ class FrameUtilsTest < Minitest::Test
assert_equal([:end_stream], ret.last.flags)
end
- def test_frame_headers
- frame = Frame.new(type: :headers, flags: [:priority, :end_stream, :end_headers], stream_id: 1, payload: "1234567")
+ def test_headers_split
+ frame = Frame.craft(type: :headers, flags: [:priority, :end_stream, :end_headers], stream_id: 1, payload: "1234567")
ret = frame.to_enum(:split, 3).to_a
assert_equal(3, ret.size)
assert_equal("123", ret[0].payload)
@@ -35,9 +30,23 @@ class FrameUtilsTest < Minitest::Test
assert_equal([:end_headers], ret[2].flags)
end
+ def test_push_promise_split
+ frame = Frame::PushPromise.new(1, 2, "1234567", end_headers: true)
+ ret = frame.to_enum(:split, 3).to_a
+ assert_equal(4, ret.size)
+ assert_equal("\x00\x00\x00", ret[0].payload)
+ assert_equal([], ret[0].flags)
+ assert_equal("\x0212", ret[1].payload)
+ assert_equal([], ret[1].flags)
+ assert_equal("345", ret[2].payload)
+ assert_equal([], ret[2].flags)
+ assert_equal("67", ret[3].payload)
+ assert_equal([:end_headers], ret[3].flags)
+ end
+
def test_frame_parse_settings
# :header_table_size => 0x1010, :enable_push => 0x00, :header_table_size => 0x1011 (overwrite)
- frame = Frame.new(type: :settings, flags: [], stream_id: 0, payload: "\x00\x01\x00\x00\x10\x10\x00\x02\x00\x00\x00\x00\x00\x01\x00\x00\x10\x11")
+ frame = Frame.parse!("\x00\x00\x12\x04\x00\x00\x00\x00\x00" "\x00\x01\x00\x00\x10\x10\x00\x02\x00\x00\x00\x00\x00\x01\x00\x00\x10\x11")
ret = frame.parse_settings
assert_equal(0x1011, ret[:header_table_size])
assert_equal(0x0000, ret[:enable_push])
diff --git a/test/plum/test_stream.rb b/test/plum/test_stream.rb
index fc1e94b..db681e7 100644
--- a/test/plum/test_stream.rb
+++ b/test/plum/test_stream.rb
@@ -6,7 +6,7 @@ class StreamTest < Minitest::Test
def test_stream_illegal_frame_type
open_new_stream { |stream|
assert_connection_error(:protocol_error) {
- stream.receive_frame(Frame.new(type: :goaway, stream_id: stream.id, payload: "\x00\x00\x00\x00"))
+ stream.receive_frame(Frame::Goaway.new(0, :no_error))
}
}
end
@@ -14,7 +14,7 @@ class StreamTest < Minitest::Test
def test_stream_unknown_frame_type
open_new_stream { |stream|
assert_no_error {
- stream.receive_frame(Frame.new(type_value: 0x0f, stream_id: stream.id, payload: "\x00\x00\x00\x00"))
+ stream.receive_frame(Frame::Unknown.new(0x0f, flags_value: 0, stream_id: stream.id, payload: "\x00\x00\x00\x00"))
}
}
end
@@ -28,7 +28,7 @@ class StreamTest < Minitest::Test
}
assert_stream_error(:frame_size_error) {
- con << Frame.headers(1, "", end_headers: true).assemble
+ con << Frame::Headers.new(1, "", end_headers: true).assemble
}
last = sent_frames.last
@@ -43,8 +43,8 @@ class StreamTest < Minitest::Test
stream = type = nil
con.on(:rst_stream) { |s, t| stream = s; type = t }
- con << Frame.headers(1, "", end_headers: true).assemble
- con << Frame.rst_stream(1, :frame_size_error).assemble
+ con << Frame::Headers.new(1, "", end_headers: true).assemble
+ con << Frame::RstStream.new(1, :frame_size_error).assemble
assert_equal(1, stream.id)
assert_equal(:frame_size_error, type)
diff --git a/test/utils/client.rb b/test/utils/client.rb
index be8d78d..f8937e2 100644
--- a/test/utils/client.rb
+++ b/test/utils/client.rb
@@ -4,8 +4,8 @@ module ServerUtils
def open_client_connection(scheme = :https)
io = StringIO.new
@_ccon = ClientConnection.new(io.method(:write))
- @_ccon << Frame.new(type: :settings, stream_id: 0, flags: [:ack]).assemble
- @_ccon << Frame.new(type: :settings, stream_id: 0).assemble
+ @_ccon << Frame::Settings.new(:ack).assemble
+ @_ccon << Frame::Settings.new.assemble
if block_given?
yield @_ccon
else
diff --git a/test/utils/server.rb b/test/utils/server.rb
index 5f1baa7..001a3fe 100644
--- a/test/utils/server.rb
+++ b/test/utils/server.rb
@@ -5,7 +5,7 @@ module ServerUtils
@_io = StringIO.new
@_con = (scheme == :https ? ServerConnection : HTTPServerConnection).new(@_io.method(:write))
@_con << Connection::CLIENT_CONNECTION_PREFACE
- @_con << Frame.new(type: :settings, stream_id: 0).assemble
+ @_con << Frame::Settings.new.assemble
if block_given?
yield @_con
else