aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKazuki Yamaguchi <k@rhe.jp>2016-05-08 22:41:53 +0900
committerKazuki Yamaguchi <k@rhe.jp>2016-05-08 22:41:53 +0900
commitef9daa152ecf2e3f9fe0f2c5eb083379a5077c3b (patch)
treeb9e2afa99c7912ffca4dc0a24c1ccd886348f91b
parent85f218f35d2e9f1332701d908d9570c698708620 (diff)
parent89c73779d0eab2d9aae1e524245beebd589bf15d (diff)
downloadplum-ef9daa152ecf2e3f9fe0f2c5eb083379a5077c3b.tar.gz
Merge branch 'topic/refactor-for-0.3'
* topic/refactor-for-0.3: examples: static_server: use test certificate in test/ style: prefer "".b over String.new frame: settings: add Frame::Settings.ack avoid Frame#type if possible client: decoders: unfreeze Client::Decoders::DECODERS frame: create subclasses for each frame type merge *_{utils,factory}
-rw-r--r--examples/client/twitter.rb2
-rw-r--r--examples/non_tls_server.rb2
-rw-r--r--examples/static_server.rb6
-rw-r--r--lib/plum.rb15
-rw-r--r--lib/plum/client/decoders.rb2
-rw-r--r--lib/plum/client/legacy_client_session.rb2
-rw-r--r--lib/plum/connection.rb62
-rw-r--r--lib/plum/connection_utils.rb42
-rw-r--r--lib/plum/flow_control.rb7
-rw-r--r--lib/plum/frame.rb89
-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.rb49
-rw-r--r--lib/plum/frame/unknown.rb12
-rw-r--r--lib/plum/frame/window_update.rb16
-rw-r--r--lib/plum/frame_factory.rb97
-rw-r--r--lib/plum/frame_utils.rb44
-rw-r--r--lib/plum/hpack/huffman.rb4
-rw-r--r--lib/plum/rack/session.rb2
-rw-r--r--lib/plum/server/http_connection.rb10
-rw-r--r--lib/plum/stream.rb68
-rw-r--r--lib/plum/stream_utils.rb44
-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
42 files changed, 566 insertions, 479 deletions
diff --git a/examples/client/twitter.rb b/examples/client/twitter.rb
index a67fad3..e029f59 100644
--- a/examples/client/twitter.rb
+++ b/examples/client/twitter.rb
@@ -24,7 +24,7 @@ Plum::Client.start("userstream.twitter.com", 443) { |streaming|
exit
end
- buf = String.new
+ buf = "".b
res.on_chunk { |chunk| # when received DATA frame
next if chunk.empty?
buf << chunk
diff --git a/examples/non_tls_server.rb b/examples/non_tls_server.rb
index e481b05..7ea7ad2 100644
--- a/examples/non_tls_server.rb
+++ b/examples/non_tls_server.rb
@@ -55,7 +55,7 @@ loop do
stream.on(:open) do
headers = nil
- data = String.new
+ data = "".b
end
stream.on(:headers) do |headers_|
diff --git a/examples/static_server.rb b/examples/static_server.rb
index 6f7e00a..452f718 100644
--- a/examples/static_server.rb
+++ b/examples/static_server.rb
@@ -31,8 +31,8 @@ if ctx.respond_to?(:tmp_ecdh_callback) && !ctx.respond_to?(:set_ecdh_curves)
OpenSSL::PKey::EC.new("prime256v1")
}
end
-ctx.cert = OpenSSL::X509::Certificate.new File.read(".crt.local")
-ctx.key = OpenSSL::PKey::RSA.new File.read(".key.local")
+ctx.cert = OpenSSL::X509::Certificate.new File.read(File.expand_path("../../test/server.crt", __FILE__))
+ctx.key = OpenSSL::PKey::RSA.new File.read(File.expand_path("../../test/server.key", __FILE__))
tcp_server = TCPServer.new("0.0.0.0", 40443)
ssl_server = OpenSSL::SSL::SSLServer.new(tcp_server, ctx)
@@ -75,7 +75,7 @@ loop do
stream.on(:open) do
headers = nil
- data = String.new
+ data = "".b
end
stream.on(:headers) do |headers_|
diff --git a/lib/plum.rb b/lib/plum.rb
index 9452d8f..fa70317 100644
--- a/lib/plum.rb
+++ b/lib/plum.rb
@@ -12,13 +12,20 @@ require "plum/hpack/huffman"
require "plum/hpack/context"
require "plum/hpack/decoder"
require "plum/hpack/encoder"
-require "plum/frame_utils"
-require "plum/frame_factory"
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_utils"
require "plum/connection"
-require "plum/stream_utils"
require "plum/stream"
require "plum/server/connection"
require "plum/server/ssl_socket_connection"
diff --git a/lib/plum/client/decoders.rb b/lib/plum/client/decoders.rb
index f1d9ed9..297b13b 100644
--- a/lib/plum/client/decoders.rb
+++ b/lib/plum/client/decoders.rb
@@ -46,6 +46,6 @@ module Plum
end
end
- DECODERS = { "gzip" => GZip, "deflate" => Deflate }.freeze
+ DECODERS = { "gzip" => GZip, "deflate" => Deflate }
end
end
diff --git a/lib/plum/client/legacy_client_session.rb b/lib/plum/client/legacy_client_session.rb
index 9dea6cd..bdbce9a 100644
--- a/lib/plum/client/legacy_client_session.rb
+++ b/lib/plum/client/legacy_client_session.rb
@@ -68,7 +68,7 @@ module Plum
end
def construct_request(headers)
- out = String.new
+ out = "".b
out << "%s %s HTTP/1.1\r\n" % [headers[":method"], headers[":path"]]
headers.each { |key, value|
next if key.start_with?(":") # HTTP/2 psuedo headers
diff --git a/lib/plum/connection.rb b/lib/plum/connection.rb
index 0ba5989..739bbf3 100644
--- a/lib/plum/connection.rb
+++ b/lib/plum/connection.rb
@@ -6,7 +6,6 @@ module Plum
class Connection
include EventEmitter
include FlowControl
- include ConnectionUtils
CLIENT_CONNECTION_PREFACE = "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n"
@@ -28,7 +27,7 @@ module Plum
@writer = writer
@local_settings = Hash.new { |hash, key| DEFAULT_SETTINGS[key] }.merge!(local_settings)
@remote_settings = Hash.new { |hash, key| DEFAULT_SETTINGS[key] }
- @buffer = String.new
+ @buffer = "".b
@streams = {}
@hpack_decoder = HPACK::Decoder.new(@local_settings[:header_table_size])
@hpack_encoder = HPACK::Encoder.new(@remote_settings[:header_table_size])
@@ -83,6 +82,37 @@ module Plum
stream
end
+ # Sends local settings to the peer.
+ # @param new_settings [Hash<Symbol, Integer>]
+ def settings(**new_settings)
+ send_immediately Frame::Settings.new(**new_settings)
+
+ old_settings = @local_settings.dup
+ @local_settings.merge!(new_settings)
+
+ @hpack_decoder.limit = @local_settings[:header_table_size]
+ update_recv_initial_window_size(@local_settings[:initial_window_size] - old_settings[:initial_window_size])
+ end
+
+ # Sends a PING frame to the peer.
+ # @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.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.new(last_id, error_type, message)
+ end
+
+ # Returns whether peer enables server push or not
+ def push_enabled?
+ @remote_settings[:enable_push] == 1
+ end
+
private
def consume_buffer
while frame = Frame.parse!(@buffer)
@@ -104,12 +134,12 @@ module Plum
end
def validate_received_frame(frame)
- if @state == :waiting_settings && frame.type != :settings
+ if @state == :waiting_settings && !(Frame::Settings === frame)
raise RemoteConnectionError.new(:protocol_error)
end
if @state == :waiting_continuation
- if frame.type != :continuation || frame.stream_id != @continuation_id
+ if !(Frame::Continuation === frame) || frame.stream_id != @continuation_id
raise RemoteConnectionError.new(:protocol_error)
end
if frame.end_headers?
@@ -117,7 +147,7 @@ module Plum
end
end
- if frame.type == :headers || frame.type == :push_promise
+ if Frame::Headers === frame || Frame::PushPromise === frame
if !frame.end_headers?
@state = :waiting_continuation
@continuation_id = frame.stream_id
@@ -132,7 +162,7 @@ module Plum
if frame.stream_id == 0
receive_control_frame(frame)
else
- stream(frame.stream_id, frame.type == :headers).receive_frame(frame)
+ stream(frame.stream_id, Frame::Headers === frame).receive_frame(frame)
end
end
@@ -141,16 +171,12 @@ module Plum
raise RemoteConnectionError.new(:frame_size_error)
end
- case frame.type
- when :settings
- receive_settings(frame)
- when :window_update
- receive_window_update(frame)
- when :ping
- receive_ping(frame)
- when :goaway
- receive_goaway(frame)
- when :data, :headers, :priority, :rst_stream, :push_promise, :continuation
+ case frame
+ when Frame::Settings then receive_settings(frame)
+ when Frame::WindowUpdate then receive_window_update(frame)
+ when Frame::Ping then receive_ping(frame)
+ when Frame::Goaway then receive_goaway(frame)
+ when Frame::Data, Frame::Headers, Frame::Priority, Frame::RstStream, Frame::PushPromise, Frame::Continuation
raise Plum::RemoteConnectionError.new(:protocol_error)
else
# MUST ignore unknown frame type.
@@ -172,7 +198,7 @@ module Plum
callback(:remote_settings, @remote_settings, old_remote_settings)
- send_immediately Frame.settings(:ack) if send_ack
+ send_immediately Frame::Settings.ack if send_ack
if @state == :waiting_settings
@state = :open
@@ -193,7 +219,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/connection_utils.rb b/lib/plum/connection_utils.rb
deleted file mode 100644
index b8329b8..0000000
--- a/lib/plum/connection_utils.rb
+++ /dev/null
@@ -1,42 +0,0 @@
-# frozen-string-literal: true
-
-using Plum::BinaryString
-
-module Plum
- module ConnectionUtils
- # Sends local settings to the peer.
- # @param kwargs [Hash<Symbol, Integer>]
- def settings(**kwargs)
- send_immediately Frame.settings(**kwargs)
- update_local_settings(kwargs)
- end
-
- # Sends a PING frame to the peer.
- # @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)
- 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)
- end
-
- # Returns whether peer enables server push or not
- def push_enabled?
- @remote_settings[:enable_push] == 1
- end
-
- private
- def update_local_settings(new_settings)
- old_settings = @local_settings.dup
- @local_settings.merge!(new_settings)
-
- @hpack_decoder.limit = @local_settings[:header_table_size]
- update_recv_initial_window_size(@local_settings[:initial_window_size] - old_settings[:initial_window_size])
- end
- end
-end
diff --git a/lib/plum/flow_control.rb b/lib/plum/flow_control.rb
index 9a2aa1d..4b7ef79 100644
--- a/lib/plum/flow_control.rb
+++ b/lib/plum/flow_control.rb
@@ -9,7 +9,7 @@ module Plum
# Sends frame respecting inner-stream flow control.
# @param frame [Frame] The frame to be sent.
def send(frame)
- if frame.type == :data
+ if Frame::Data === frame
@send_buffer << frame
if @send_remaining_window < frame.length
if Stream === self
@@ -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
@@ -63,7 +62,7 @@ module Plum
end
def consume_recv_window(frame)
- if frame.type == :data
+ if Frame::Data === frame
@recv_remaining_window -= frame.length
if @recv_remaining_window < 0
local_error = (Connection === self) ? RemoteConnectionError : RemoteStreamError
diff --git a/lib/plum/frame.rb b/lib/plum/frame.rb
index bf61629..1a4b402 100644
--- a/lib/plum/frame.rb
+++ b/lib/plum/frame.rb
@@ -1,12 +1,8 @@
# frozen-string-literal: true
using Plum::BinaryString
-
module Plum
class Frame
- extend FrameFactory
- include FrameUtils
-
FRAME_TYPES = {
data: 0x00,
headers: 0x01,
@@ -56,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
# +-----------------------------------------------+
@@ -85,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.
@@ -105,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
@@ -150,26 +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]
+ "#<%s:0x%04x length=%d, flags=%p, stream_id=0x%04x, payload=%p>" % [self.class, __id__, length, flags, stream_id, payload]
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
+ 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
+
+ # Sub classes must call this.
+ protected def register_subclass(type_value)
+ SUB_CLASSES[type_value] = self
+ end
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..5eeb28a
--- /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 = "".b.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..200fa37
--- /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 = "".b.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..c7794a9
--- /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 = "".b.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..71a92eb
--- /dev/null
+++ b/lib/plum/frame/settings.rb
@@ -0,0 +1,49 @@
+# 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 args [Hash<Symbol, Integer>] The settings values to send.
+ def initialize(**args)
+ payload = "".b
+ 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, payload: payload)
+ end
+
+ # Creates a SETTINGS frame with ACK flag.
+ def self.ack
+ frame = allocate
+ frame.send(:initialize_base, type: :settings, stream_id: 0, flags_value: 0x01)
+ frame
+ 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..3fe5ecd
--- /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 = "".b.push_uint32(wsi)
+ initialize_base(type: :window_update, stream_id: stream_id, payload: payload)
+ end
+ end
+end
diff --git a/lib/plum/frame_factory.rb b/lib/plum/frame_factory.rb
deleted file mode 100644
index 514d99d..0000000
--- a/lib/plum/frame_factory.rb
+++ /dev/null
@@ -1,97 +0,0 @@
-# frozen-string-literal: true
-
-using Plum::BinaryString
-
-module Plum
- module FrameFactory
- # 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 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 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 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 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)
- 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 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 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 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 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_utils.rb b/lib/plum/frame_utils.rb
deleted file mode 100644
index cbb16fa..0000000
--- a/lib/plum/frame_utils.rb
+++ /dev/null
@@ -1,44 +0,0 @@
-# frozen-string-literal: true
-
-using Plum::BinaryString
-
-module Plum
- module FrameUtils
- # 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")
- 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
- end
-end
diff --git a/lib/plum/hpack/huffman.rb b/lib/plum/hpack/huffman.rb
index 8fcc120..9604f1f 100644
--- a/lib/plum/hpack/huffman.rb
+++ b/lib/plum/hpack/huffman.rb
@@ -9,7 +9,7 @@ module Plum
# Static-Huffman-encodes the specified String.
def encode(bytestr)
- out = String.new
+ out = "".b
bytestr.each_byte do |b|
out << HUFFMAN_TABLE[b]
end
@@ -21,7 +21,7 @@ module Plum
def decode(encoded)
bits = encoded.unpack("B*")[0]
out = []
- buf = String.new
+ buf = "".b
bits.each_char do |cb|
buf << cb
if c = HUFFMAN_TABLE_INVERSED[buf]
diff --git a/lib/plum/rack/session.rb b/lib/plum/rack/session.rb
index d7e2f97..1c9a4df 100644
--- a/lib/plum/rack/session.rb
+++ b/lib/plum/rack/session.rb
@@ -55,7 +55,7 @@ module Plum
reqs = {}
@plum.on(:headers) { |stream, h|
- reqs[stream] = { headers: h, data: String.new.force_encoding(Encoding::BINARY) }
+ reqs[stream] = { headers: h, data: "".b }
}
@plum.on(:data) { |stream, d|
diff --git a/lib/plum/server/http_connection.rb b/lib/plum/server/http_connection.rb
index b257137..473cef6 100644
--- a/lib/plum/server/http_connection.rb
+++ b/lib/plum/server/http_connection.rb
@@ -6,7 +6,7 @@ module Plum
class HTTPServerConnection < ServerConnection
def initialize(writer, local_settings = {})
require "http/parser"
- @negobuf = String.new
+ @negobuf = "".b
@_http_parser = setup_parser
super(writer, local_settings)
end
@@ -22,7 +22,7 @@ module Plum
def setup_parser
headers = nil
- body = String.new
+ body = "".b
parser = HTTP::Parser.new
parser.on_headers_complete = proc { |_headers|
@@ -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 823a41a..6e6724e 100644
--- a/lib/plum/stream.rb
+++ b/lib/plum/stream.rb
@@ -6,7 +6,6 @@ module Plum
class Stream
include EventEmitter
include FlowControl
- include StreamUtils
attr_reader :id, :state, :connection
attr_reader :weight, :exclusive
@@ -32,29 +31,22 @@ module Plum
validate_received_frame(frame)
consume_recv_window(frame)
- case frame.type
- when :data
- receive_data(frame)
- when :headers
- receive_headers(frame)
- when :priority
- receive_priority(frame)
- when :rst_stream
- receive_rst_stream(frame)
- when :window_update
- receive_window_update(frame)
- when :continuation
- receive_continuation(frame)
- when :push_promise
- receive_push_promise(frame)
- when :ping, :goaway, :settings
+ case frame
+ when Frame::Data then receive_data(frame)
+ when Frame::Headers then receive_headers(frame)
+ when Frame::Priority then receive_priority(frame)
+ when Frame::RstStream then receive_rst_stream(frame)
+ when Frame::WindowUpdate then receive_window_update(frame)
+ when Frame::Continuation then receive_continuation(frame)
+ when Frame::PushPromise then receive_push_promise(frame)
+ when Frame::Ping, Frame::Goaway, Frame::Settings
raise RemoteConnectionError.new(:protocol_error) # stream_id MUST be 0x00
else
# MUST ignore unknown frame
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
@@ -92,6 +84,43 @@ module Plum
end
end
+ # Reserves a stream to server push. Sends PUSH_PROMISE and create new stream.
+ # @param headers [Enumerable<String, String>] The *request* headers. It must contain all of them: ':authority', ':method', ':scheme' and ':path'.
+ # @return [Stream] The stream to send push response.
+ def promise(headers)
+ stream = @connection.reserve_stream(weight: self.weight + 1, parent: self)
+ encoded = @connection.hpack_encoder.encode(headers)
+ frame = Frame::PushPromise.new(id, stream.id, encoded, end_headers: true)
+ send frame
+ stream
+ end
+
+ # Sends response headers. If the encoded frame is larger than MAX_FRAME_SIZE, the headers will be splitted into HEADERS frame and CONTINUATION frame(s).
+ # @param headers [Enumerable<String, String>] The response headers.
+ # @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.new(id, encoded, end_headers: true, end_stream: end_stream)
+ send frame
+ @state = :half_closed_local if end_stream
+ end
+
+ # Sends DATA frame. If the data is larger than MAX_FRAME_SIZE, DATA frame will be splitted.
+ # @param data [String, IO] The data to send.
+ # @param end_stream [Boolean] Set END_STREAM flag or not.
+ def send_data(data = "", end_stream: true)
+ max = @connection.remote_settings[:max_frame_size]
+ if data.is_a?(IO)
+ until data.eof?
+ fragment = data.readpartial(max)
+ send Frame::Data.new(id, fragment, end_stream: end_stream && data.eof?)
+ end
+ else
+ send Frame::Data.new(id, data, end_stream: end_stream)
+ end
+ @state = :half_closed_local if end_stream
+ end
+
private
def send_immediately(frame)
@connection.send(frame)
@@ -99,7 +128,8 @@ module Plum
def validate_received_frame(frame)
if frame.length > @connection.local_settings[:max_frame_size]
- if [:headers, :push_promise, :continuation].include?(frame.type)
+ case frame
+ when Frame::Headers, Frame::PushPromise, Frame::Continuation
raise RemoteConnectionError.new(:frame_size_error)
else
raise RemoteStreamError.new(:frame_size_error)
diff --git a/lib/plum/stream_utils.rb b/lib/plum/stream_utils.rb
deleted file mode 100644
index dca86e7..0000000
--- a/lib/plum/stream_utils.rb
+++ /dev/null
@@ -1,44 +0,0 @@
-# frozen-string-literal: true
-
-using Plum::BinaryString
-
-module Plum
- module StreamUtils
- # Reserves a stream to server push. Sends PUSH_PROMISE and create new stream.
- # @param headers [Enumerable<String, String>] The *request* headers. It must contain all of them: ':authority', ':method', ':scheme' and ':path'.
- # @return [Stream] The stream to send push response.
- 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)
- send frame
- stream
- end
-
- # Sends response headers. If the encoded frame is larger than MAX_FRAME_SIZE, the headers will be splitted into HEADERS frame and CONTINUATION frame(s).
- # @param headers [Enumerable<String, String>] The response headers.
- # @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)
- send frame
- @state = :half_closed_local if end_stream
- end
-
- # Sends DATA frame. If the data is larger than MAX_FRAME_SIZE, DATA frame will be splitted.
- # @param data [String, IO] The data to send.
- # @param end_stream [Boolean] Set END_STREAM flag or not.
- def send_data(data = "", end_stream: true)
- max = @connection.remote_settings[:max_frame_size]
- if data.is_a?(IO)
- until data.eof?
- fragment = data.readpartial(max)
- send Frame.data(id, fragment, end_stream: end_stream && data.eof?)
- end
- else
- send Frame.data(id, data, end_stream: end_stream)
- end
- @state = :half_closed_local if end_stream
- end
- end
-end
diff --git a/test/plum/client/test_upgrade_client_session.rb b/test/plum/client/test_upgrade_client_session.rb
index 3cc97b4..4c20e3b 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.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..df3093e 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.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..ae8ca16 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.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