From 26ec6b88a8e078499835851a0310eba486475b7b Mon Sep 17 00:00:00 2001 From: Kazuki Yamaguchi Date: Sun, 8 May 2016 17:01:44 +0900 Subject: merge *_{utils,factory} This improves readability a bit. --- lib/plum.rb | 4 -- lib/plum/connection.rb | 32 ++++++++++- lib/plum/connection_utils.rb | 42 -------------- lib/plum/frame.rb | 130 ++++++++++++++++++++++++++++++++++++++++++- lib/plum/frame_factory.rb | 97 -------------------------------- lib/plum/frame_utils.rb | 44 --------------- lib/plum/stream.rb | 38 ++++++++++++- lib/plum/stream_utils.rb | 44 --------------- 8 files changed, 195 insertions(+), 236 deletions(-) delete mode 100644 lib/plum/connection_utils.rb delete mode 100644 lib/plum/frame_factory.rb delete mode 100644 lib/plum/frame_utils.rb delete mode 100644 lib/plum/stream_utils.rb diff --git a/lib/plum.rb b/lib/plum.rb index 9452d8f..3220bbc 100644 --- a/lib/plum.rb +++ b/lib/plum.rb @@ -12,13 +12,9 @@ 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/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/connection.rb b/lib/plum/connection.rb index 0ba5989..1eac16e 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" @@ -83,6 +82,37 @@ module Plum stream end + # Sends local settings to the peer. + # @param new_settings [Hash] + def settings(**new_settings) + send_immediately Frame.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 + + # 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 consume_buffer while frame = Frame.parse!(@buffer) 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] - 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/frame.rb b/lib/plum/frame.rb index bf61629..57b3061 100644 --- a/lib/plum/frame.rb +++ b/lib/plum/frame.rb @@ -4,9 +4,6 @@ using Plum::BinaryString module Plum class Frame - extend FrameFactory - include FrameUtils - FRAME_TYPES = { data: 0x00, headers: 0x01, @@ -153,6 +150,43 @@ module Plum "#" % [__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") + end + end + + # Parses SETTINGS frame payload. Ignores unknown settings type (see RFC7540 6.5.2). + # @return [Hash] 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. @@ -171,5 +205,95 @@ module Plum 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] 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) + 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_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] 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] 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/stream.rb b/lib/plum/stream.rb index 823a41a..733aeaf 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 @@ -92,6 +91,43 @@ module Plum end end + # Reserves a stream to server push. Sends PUSH_PROMISE and create new stream. + # @param headers [Enumerable] 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] 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 + private def send_immediately(frame) @connection.send(frame) 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] 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] 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 -- cgit v1.2.3 From 7be98b6090baf6d67be53977bcd0fc87d83737cb Mon Sep 17 00:00:00 2001 From: Kazuki Yamaguchi Date: Sun, 8 May 2016 21:03:25 +0900 Subject: frame: create subclasses for each frame type This improves code readability. --- lib/plum.rb | 11 ++ lib/plum/connection.rb | 10 +- lib/plum/flow_control.rb | 3 +- lib/plum/frame.rb | 211 ++++++------------------ lib/plum/frame/continuation.rb | 16 ++ lib/plum/frame/data.rb | 31 ++++ lib/plum/frame/goaway.rb | 20 +++ lib/plum/frame/headers.rb | 32 ++++ lib/plum/frame/ping.rb | 24 +++ lib/plum/frame/priority.rb | 12 ++ lib/plum/frame/push_promise.rb | 33 ++++ lib/plum/frame/rst_stream.rb | 16 ++ lib/plum/frame/settings.rb | 43 +++++ lib/plum/frame/unknown.rb | 12 ++ lib/plum/frame/window_update.rb | 16 ++ lib/plum/server/http_connection.rb | 6 +- lib/plum/stream.rb | 10 +- test/plum/client/test_upgrade_client_session.rb | 8 +- test/plum/connection/test_handle_frame.rb | 16 +- test/plum/server/test_connection.rb | 15 +- test/plum/server/test_http_connection.rb | 6 +- test/plum/stream/test_handle_frame.rb | 44 ++--- test/plum/test_connection.rb | 34 ++-- test/plum/test_connection_utils.rb | 6 +- test/plum/test_flow_control.rb | 70 +++----- test/plum/test_frame.rb | 22 +-- test/plum/test_frame_factory.rb | 20 +-- test/plum/test_frame_utils.rb | 29 ++-- test/plum/test_stream.rb | 10 +- test/utils/client.rb | 4 +- test/utils/server.rb | 2 +- 31 files changed, 462 insertions(+), 330 deletions(-) create mode 100644 lib/plum/frame/continuation.rb create mode 100644 lib/plum/frame/data.rb create mode 100644 lib/plum/frame/goaway.rb create mode 100644 lib/plum/frame/headers.rb create mode 100644 lib/plum/frame/ping.rb create mode 100644 lib/plum/frame/priority.rb create mode 100644 lib/plum/frame/push_promise.rb create mode 100644 lib/plum/frame/rst_stream.rb create mode 100644 lib/plum/frame/settings.rb create mode 100644 lib/plum/frame/unknown.rb create mode 100644 lib/plum/frame/window_update.rb 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] 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] The flags. def flags @@ -147,153 +153,36 @@ module Plum # @private def inspect - "#" % [__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] 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] 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] 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] 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 -- cgit v1.2.3 From 57a2e245f6021c5e41610353c3c47c108a43d908 Mon Sep 17 00:00:00 2001 From: Kazuki Yamaguchi Date: Sun, 8 May 2016 21:16:46 +0900 Subject: client: decoders: unfreeze Client::Decoders::DECODERS User may add custom decoders. --- lib/plum/client/decoders.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/plum/client/decoders.rb b/lib/plum/client/decoders.rb index d68f27b..a1578f9 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 -- cgit v1.2.3 From 0afb9eadcbb5e631757a777124281f663d8f6974 Mon Sep 17 00:00:00 2001 From: Kazuki Yamaguchi Date: Sun, 8 May 2016 21:31:17 +0900 Subject: avoid Frame#type if possible For typo-safety. --- lib/plum/connection.rb | 24 ++++++++++-------------- lib/plum/flow_control.rb | 4 ++-- lib/plum/stream.rb | 28 +++++++++++----------------- 3 files changed, 23 insertions(+), 33 deletions(-) diff --git a/lib/plum/connection.rb b/lib/plum/connection.rb index 36faedd..d5c507b 100644 --- a/lib/plum/connection.rb +++ b/lib/plum/connection.rb @@ -134,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? @@ -147,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 @@ -162,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 @@ -171,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. diff --git a/lib/plum/flow_control.rb b/lib/plum/flow_control.rb index f28fa3a..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 @@ -62,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/stream.rb b/lib/plum/stream.rb index 420ee23..6e6724e 100644 --- a/lib/plum/stream.rb +++ b/lib/plum/stream.rb @@ -31,22 +31,15 @@ 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 @@ -135,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) -- cgit v1.2.3 From fed5ea65bc7ac477379e9feda82f0e4d00a00e29 Mon Sep 17 00:00:00 2001 From: Kazuki Yamaguchi Date: Sun, 8 May 2016 21:58:19 +0900 Subject: frame: settings: add Frame::Settings.ack This is equivalent to Frame::Settings.new(:ack). --- lib/plum/connection.rb | 2 +- lib/plum/frame/settings.rb | 12 +++++++++--- test/plum/client/test_upgrade_client_session.rb | 2 +- test/plum/test_frame_factory.rb | 2 +- test/utils/client.rb | 2 +- 5 files changed, 13 insertions(+), 7 deletions(-) diff --git a/lib/plum/connection.rb b/lib/plum/connection.rb index d5c507b..2f177a6 100644 --- a/lib/plum/connection.rb +++ b/lib/plum/connection.rb @@ -198,7 +198,7 @@ module Plum callback(:remote_settings, @remote_settings, old_remote_settings) - send_immediately Frame::Settings.new(:ack) if send_ack + send_immediately Frame::Settings.ack if send_ack if @state == :waiting_settings @state = :open diff --git a/lib/plum/frame/settings.rb b/lib/plum/frame/settings.rb index 3bd13d4..7f7c2be 100644 --- a/lib/plum/frame/settings.rb +++ b/lib/plum/frame/settings.rb @@ -15,16 +15,22 @@ module Plum }.freeze # Creates a SETTINGS frame. - # @param ack [Symbol] Pass :ack to create an ACK frame. # @param args [Hash] The settings values to send. - def initialize(ack = nil, **args) + def initialize(**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) + 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). diff --git a/test/plum/client/test_upgrade_client_session.rb b/test/plum/client/test_upgrade_client_session.rb index 446462c..4c20e3b 100644 --- a/test/plum/client/test_upgrade_client_session.rb +++ b/test/plum/client/test_upgrade_client_session.rb @@ -22,7 +22,7 @@ class UpgradeClientSessionTest < Minitest::Test sock = StringSocket.new("HTTP/1.1 101\r\n\r\n") session = UpgradeClientSession.new(sock, Client::DEFAULT_CONFIG) sock.rio.string << Frame::Settings.new.assemble - sock.rio.string << Frame::Settings.new(:ack).assemble + sock.rio.string << Frame::Settings.ack.assemble res = session.request({ ":method" => "GET", ":path" => "/aa" }, "aa", {}) 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 diff --git a/test/plum/test_frame_factory.rb b/test/plum/test_frame_factory.rb index a4253ba..df3093e 100644 --- a/test/plum/test_frame_factory.rb +++ b/test/plum/test_frame_factory.rb @@ -28,7 +28,7 @@ class FrameFactoryTest < Minitest::Test end def test_settings_ack - frame = Frame::Settings.new(:ack) + frame = Frame::Settings.ack assert_frame(frame, type: :settings, stream_id: 0, diff --git a/test/utils/client.rb b/test/utils/client.rb index f8937e2..ae8ca16 100644 --- a/test/utils/client.rb +++ b/test/utils/client.rb @@ -4,7 +4,7 @@ module ServerUtils def open_client_connection(scheme = :https) io = StringIO.new @_ccon = ClientConnection.new(io.method(:write)) - @_ccon << Frame::Settings.new(:ack).assemble + @_ccon << Frame::Settings.ack.assemble @_ccon << Frame::Settings.new.assemble if block_given? yield @_ccon -- cgit v1.2.3 From 330d1d657fbc9c06c0e3304d8cb4ab6a2b4e948c Mon Sep 17 00:00:00 2001 From: Kazuki Yamaguchi Date: Sun, 8 May 2016 22:04:11 +0900 Subject: style: prefer "".b over String.new String.new requires a constant search and method search, while "".b requires only a method search. --- examples/client/twitter.rb | 2 +- examples/non_tls_server.rb | 2 +- examples/static_server.rb | 2 +- lib/plum/client/legacy_client_session.rb | 2 +- lib/plum/connection.rb | 2 +- lib/plum/frame/goaway.rb | 2 +- lib/plum/frame/push_promise.rb | 2 +- lib/plum/frame/rst_stream.rb | 2 +- lib/plum/frame/settings.rb | 2 +- lib/plum/frame/window_update.rb | 2 +- lib/plum/hpack/huffman.rb | 4 ++-- lib/plum/rack/session.rb | 2 +- lib/plum/server/http_connection.rb | 4 ++-- 13 files changed, 15 insertions(+), 15 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..0c61d96 100644 --- a/examples/static_server.rb +++ b/examples/static_server.rb @@ -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/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 2f177a6..739bbf3 100644 --- a/lib/plum/connection.rb +++ b/lib/plum/connection.rb @@ -27,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]) diff --git a/lib/plum/frame/goaway.rb b/lib/plum/frame/goaway.rb index e1e0097..5eeb28a 100644 --- a/lib/plum/frame/goaway.rb +++ b/lib/plum/frame/goaway.rb @@ -11,7 +11,7 @@ module Plum # @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) + payload = "".b.push_uint32(last_id) .push_uint32(HTTPError::ERROR_CODES[error_type]) .push(message) initialize_base(type: :goaway, stream_id: 0, payload: payload) diff --git a/lib/plum/frame/push_promise.rb b/lib/plum/frame/push_promise.rb index e07b8e6..200fa37 100644 --- a/lib/plum/frame/push_promise.rb +++ b/lib/plum/frame/push_promise.rb @@ -11,7 +11,7 @@ module Plum # @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) + 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) diff --git a/lib/plum/frame/rst_stream.rb b/lib/plum/frame/rst_stream.rb index a8004d5..c7794a9 100644 --- a/lib/plum/frame/rst_stream.rb +++ b/lib/plum/frame/rst_stream.rb @@ -9,7 +9,7 @@ module Plum # @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]) + payload = "".b.push_uint32(HTTPError::ERROR_CODES[error_type]) initialize_base(type: :rst_stream, stream_id: stream_id, payload: payload) end end diff --git a/lib/plum/frame/settings.rb b/lib/plum/frame/settings.rb index 7f7c2be..71a92eb 100644 --- a/lib/plum/frame/settings.rb +++ b/lib/plum/frame/settings.rb @@ -17,7 +17,7 @@ module Plum # Creates a SETTINGS frame. # @param args [Hash] The settings values to send. def initialize(**args) - payload = String.new + payload = "".b args.each { |key, value| id = SETTINGS_TYPE[key] or raise ArgumentError.new("invalid settings type: #{key}") payload.push_uint16(id) diff --git a/lib/plum/frame/window_update.rb b/lib/plum/frame/window_update.rb index 6e36b2c..3fe5ecd 100644 --- a/lib/plum/frame/window_update.rb +++ b/lib/plum/frame/window_update.rb @@ -9,7 +9,7 @@ module Plum # @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) + payload = "".b.push_uint32(wsi) initialize_base(type: :window_update, stream_id: stream_id, payload: payload) 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 1cf80dc..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| -- cgit v1.2.3 From 89c73779d0eab2d9aae1e524245beebd589bf15d Mon Sep 17 00:00:00 2001 From: Kazuki Yamaguchi Date: Sun, 8 May 2016 22:31:38 +0900 Subject: examples: static_server: use test certificate in test/ .crt.local doesn not exist in this repository. --- examples/static_server.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/static_server.rb b/examples/static_server.rb index 0c61d96..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) -- cgit v1.2.3