diff options
-rw-r--r-- | lib/plum/connection.rb | 16 | ||||
-rw-r--r-- | lib/plum/errors.rb | 12 | ||||
-rw-r--r-- | lib/plum/flow_control.rb | 6 | ||||
-rw-r--r-- | lib/plum/server/connection.rb | 2 | ||||
-rw-r--r-- | lib/plum/server/http_connection.rb | 2 | ||||
-rw-r--r-- | lib/plum/server/https_connection.rb | 2 | ||||
-rw-r--r-- | lib/plum/stream.rb | 54 | ||||
-rw-r--r-- | test/plum/connection/test_handle_frame.rb | 5 | ||||
-rw-r--r-- | test/plum/test_connection.rb | 8 | ||||
-rw-r--r-- | test/plum/test_error.rb | 3 | ||||
-rw-r--r-- | test/plum/test_stream.rb | 28 | ||||
-rw-r--r-- | test/utils/assertions.rb | 18 |
12 files changed, 96 insertions, 60 deletions
diff --git a/lib/plum/connection.rb b/lib/plum/connection.rb index 1d47360..c31836a 100644 --- a/lib/plum/connection.rb +++ b/lib/plum/connection.rb @@ -51,7 +51,7 @@ module Plum return if new_data.empty? @buffer << new_data consume_buffer - rescue ConnectionError => e + rescue RemoteConnectionError => e callback(:connection_error, e) goaway(e.http2_error_type) close @@ -94,12 +94,12 @@ module Plum def validate_received_frame(frame) if @state == :waiting_settings && frame.type != :settings - raise ConnectionError.new(:protocol_error) + raise RemoteConnectionError.new(:protocol_error) end if @state == :waiting_continuation if frame.type != :continuation || frame.stream_id != @continuation_id - raise ConnectionError.new(:protocol_error) + raise RemoteConnectionError.new(:protocol_error) end if frame.end_headers? @state = :open @@ -127,7 +127,7 @@ module Plum def receive_control_frame(frame) if frame.length > @local_settings[:max_frame_size] - raise ConnectionError.new(:frame_size_error) + raise RemoteConnectionError.new(:frame_size_error) end case frame.type @@ -140,7 +140,7 @@ module Plum when :goaway receive_goaway(frame) when :data, :headers, :priority, :rst_stream, :push_promise, :continuation - raise Plum::ConnectionError.new(:protocol_error) + raise Plum::RemoteConnectionError.new(:protocol_error) else # MUST ignore unknown frame type. end @@ -148,11 +148,11 @@ module Plum def receive_settings(frame, send_ack: true) if frame.ack? - raise ConnectionError.new(:frame_size_error) if frame.length != 0 + raise RemoteConnectionError.new(:frame_size_error) if frame.length != 0 callback(:settings_ack) return else - raise ConnectionError.new(:frame_size_error) if frame.length % 6 != 0 + raise RemoteConnectionError.new(:frame_size_error) if frame.length % 6 != 0 end old_remote_settings = @remote_settings.dup @@ -175,7 +175,7 @@ module Plum end def receive_ping(frame) - raise Plum::ConnectionError.new(:frame_size_error) if frame.length != 8 + raise Plum::RemoteConnectionError.new(:frame_size_error) if frame.length != 8 if frame.ack? callback(:ping_ack) diff --git a/lib/plum/errors.rb b/lib/plum/errors.rb index d5cce48..3220c67 100644 --- a/lib/plum/errors.rb +++ b/lib/plum/errors.rb @@ -31,9 +31,6 @@ module Plum ERROR_CODES[@http2_error_type] end end - class ConnectionError < HTTPError; end - class StreamError < HTTPError; end - class LegacyHTTPError < Error attr_reader :headers, :data, :parser @@ -44,7 +41,10 @@ module Plum end end - # Client - class LocalConnectionError < HTTPError; end - class LocalStreamError < HTTPError; end + class RemoteHTTPError < HTTPError; end + class RemoteConnectionError < RemoteHTTPError; end + class RemoteStreamError < RemoteHTTPError; end + class LocalHTTPError < HTTPError; end + class LocalConnectionError < LocalHTTPError; end + class LocalStreamError < LocalHTTPError; end end diff --git a/lib/plum/flow_control.rb b/lib/plum/flow_control.rb index cfb181d..ccb57dd 100644 --- a/lib/plum/flow_control.rb +++ b/lib/plum/flow_control.rb @@ -65,7 +65,7 @@ module Plum if frame.type == :data @recv_remaining_window -= frame.length if @recv_remaining_window < 0 - local_error = (Connection === self) ? ConnectionError : StreamError + local_error = (Connection === self) ? RemoteConnectionError : RemoteStreamError raise local_error.new(:flow_control_error) end end @@ -82,7 +82,7 @@ module Plum def receive_window_update(frame) if frame.length != 4 - raise Plum::ConnectionError.new(:frame_size_error) + raise Plum::RemoteConnectionError.new(:frame_size_error) end r_wsi = frame.payload.uint32 @@ -90,7 +90,7 @@ module Plum wsi = r_wsi # & ~(1 << 31) if wsi == 0 - local_error = (Connection === self) ? ConnectionError : StreamError + local_error = (Connection === self) ? RemoteConnectionError : RemoteStreamError raise local_error.new(:protocol_error) end diff --git a/lib/plum/server/connection.rb b/lib/plum/server/connection.rb index 450dcf6..a82d4aa 100644 --- a/lib/plum/server/connection.rb +++ b/lib/plum/server/connection.rb @@ -29,7 +29,7 @@ module Plum def negotiate! unless CLIENT_CONNECTION_PREFACE.start_with?(@buffer.byteslice(0, 24)) - raise ConnectionError.new(:protocol_error) # (MAY) send GOAWAY. sending. + raise RemoteConnectionError.new(:protocol_error) # (MAY) send GOAWAY. sending. end if @buffer.bytesize >= 24 diff --git a/lib/plum/server/http_connection.rb b/lib/plum/server/http_connection.rb index 7111bb4..cb49a29 100644 --- a/lib/plum/server/http_connection.rb +++ b/lib/plum/server/http_connection.rb @@ -23,7 +23,7 @@ module Plum private def negotiate! super - rescue ConnectionError + rescue RemoteConnectionError # Upgrade from HTTP/1.1 offset = @_http_parser << @buffer @buffer.byteshift(offset) diff --git a/lib/plum/server/https_connection.rb b/lib/plum/server/https_connection.rb index 09e360f..bac1b4b 100644 --- a/lib/plum/server/https_connection.rb +++ b/lib/plum/server/https_connection.rb @@ -10,7 +10,7 @@ module Plum if @sock.respond_to?(:cipher) # OpenSSL::SSL::SSLSocket-like if CIPHER_BLACKLIST.include?(@sock.cipher.first) # [cipher-suite, ssl-version, keylen, alglen] on(:negotiated) { - raise ConnectionError.new(:inadequate_security) + raise RemoteConnectionError.new(:inadequate_security) } end end diff --git a/lib/plum/stream.rb b/lib/plum/stream.rb index 61c7cd5..87e228e 100644 --- a/lib/plum/stream.rb +++ b/lib/plum/stream.rb @@ -47,20 +47,21 @@ module Plum when :push_promise receive_push_promise(frame) when :ping, :goaway, :settings - raise ConnectionError.new(:protocol_error) # stream_id MUST be 0x00 + raise RemoteConnectionError.new(:protocol_error) # stream_id MUST be 0x00 else # MUST ignore unknown frame end - rescue StreamError => e + rescue RemoteStreamError => e callback(:stream_error, e) - close(e.http2_error_type) + send_immediately Frame.rst_stream(id, e.http2_error_type) + close end # Closes this stream. Sends RST_STREAM frame to the peer. # @param error_type [Symbol] The error type to be contained in the RST_STREAM frame. - def close(error_type = :no_error) + def close @state = :closed - send_immediately Frame.rst_stream(id, error_type) + callback(:close) end # @api private @@ -70,7 +71,7 @@ module Plum # @api private def update_dependency(weight: nil, parent: nil, exclusive: nil) - raise StreamError.new(:protocol_error, "A stream cannot depend on itself.") if parent == self + raise RemoteStreamError.new(:protocol_error, "A stream cannot depend on itself.") if parent == self if weight @weight = weight @@ -102,9 +103,9 @@ module Plum def validate_received_frame(frame) if frame.length > @connection.local_settings[:max_frame_size] if [:headers, :push_promise, :continuation].include?(frame.type) - raise ConnectionError.new(:frame_size_error) + raise RemoteConnectionError.new(:frame_size_error) else - raise StreamError.new(:frame_size_error) + raise RemoteStreamError.new(:frame_size_error) end end end @@ -116,13 +117,13 @@ module Plum def receive_data(frame) if @state != :open && @state != :half_closed_local - raise StreamError.new(:stream_closed) + raise RemoteStreamError.new(:stream_closed) end if frame.padded? padding_length = frame.payload.uint8 if padding_length >= frame.length - raise ConnectionError.new(:protocol_error, "padding is too long") + raise RemoteConnectionError.new(:protocol_error, "padding is too long") end callback(:data, frame.payload.byteslice(1, frame.length - padding_length - 1)) else @@ -149,7 +150,7 @@ module Plum end if padding_length > payload.bytesize - raise ConnectionError.new(:protocol_error, "padding is too long") + raise RemoteConnectionError.new(:protocol_error, "padding is too long") end frames.each do |frame| @@ -159,7 +160,7 @@ module Plum begin decoded_headers = @connection.hpack_decoder.decode(payload) rescue => e - raise ConnectionError.new(:compression_error, e) + raise RemoteConnectionError.new(:compression_error, e) end callback(:headers, decoded_headers) @@ -169,15 +170,15 @@ module Plum def receive_headers(frame) if @state == :reserved_local - raise ConnectionError.new(:protocol_error) + raise RemoteConnectionError.new(:protocol_error) elsif @state == :half_closed_remote - raise StreamError.new(:stream_closed) + raise RemoteStreamError.new(:stream_closed) elsif @state == :closed - raise ConnectionError.new(:stream_closed) + raise RemoteConnectionError.new(:stream_closed) elsif @state == :closed_implicitly - raise ConnectionError.new(:protocol_error) + raise RemoteConnectionError.new(:protocol_error) elsif @state == :idle && self.id.even? - raise ConnectionError.new(:protocol_error) + raise RemoteConnectionError.new(:protocol_error) end @state = :open @@ -195,10 +196,10 @@ module Plum if promised_stream.state == :closed_implicitly # 5.1.1 An endpoint that receives an unexpected stream identifier MUST respond with a connection error of type PROTOCOL_ERROR. - raise ConnectionError.new(:protocol_error) + raise RemoteConnectionError.new(:protocol_error) elsif promised_id.odd? # 5.1.1 Streams initiated by the server MUST use even-numbered stream identifiers. - raise ConnectionError.new(:protocol_error) + raise RemoteConnectionError.new(:protocol_error) end end @@ -214,7 +215,7 @@ module Plum def receive_priority(frame) if frame.length != 5 - raise StreamError.new(:frame_size_error) + raise RemoteStreamError.new(:frame_size_error) end receive_priority_payload(frame.payload) end @@ -230,13 +231,18 @@ module Plum def receive_rst_stream(frame) if frame.length != 4 - raise ConnectionError.new(:frame_size_error) + raise RemoteConnectionError.new(:frame_size_error) elsif @state == :idle - raise ConnectionError.new(:protocol_error) + raise RemoteConnectionError.new(:protocol_error) end - - callback(:rst_stream, frame) @state = :closed # MUST NOT send RST_STREAM + + error_code = frame.payload.uint32 + if error_code > 0 + raise LocalStreamError.new(HTTPError::ERROR_CODES.key(error_code)) + else + callback(:rst_stream, frame) + end end # override EventEmitter diff --git a/test/plum/connection/test_handle_frame.rb b/test/plum/connection/test_handle_frame.rb index 6aa7db7..d1cc54e 100644 --- a/test/plum/connection/test_handle_frame.rb +++ b/test/plum/connection/test_handle_frame.rb @@ -62,7 +62,10 @@ class ServerConnectionHandleFrameTest < Minitest::Test def test_server_handle_goaway_reply open_server_connection {|con| assert_no_error { - con << Frame.goaway(1234, :stream_closed).assemble + begin + con << Frame.goaway(1, :stream_closed).assemble + rescue LocalHTTPError + end } assert_equal(:goaway, sent_frames.last.type) } diff --git a/test/plum/test_connection.rb b/test/plum/test_connection.rb index f490a87..b1a4803 100644 --- a/test/plum/test_connection.rb +++ b/test/plum/test_connection.rb @@ -92,4 +92,12 @@ class ConnectionTest < Minitest::Test assert_equal(:open, con.state) } end + + def test_connection_local_error + open_server_connection { |con| + assert_raises(LocalConnectionError) { + con << Frame.goaway(0, :frame_size_error).assemble + } + } + end end diff --git a/test/plum/test_error.rb b/test/plum/test_error.rb index 167538a..8eaa489 100644 --- a/test/plum/test_error.rb +++ b/test/plum/test_error.rb @@ -7,7 +7,6 @@ class ErrorTest < Minitest::Test assert_equal(0x08, e.http2_error_code) } - test.call ConnectionError - test.call StreamError + test.call HTTPError end end diff --git a/test/plum/test_stream.rb b/test/plum/test_stream.rb index 9df9a55..a9de58e 100644 --- a/test/plum/test_stream.rb +++ b/test/plum/test_stream.rb @@ -19,14 +19,34 @@ class StreamTest < Minitest::Test } end - def test_stream_close - open_new_stream(state: :half_closed_local) {|stream| - stream.close(:frame_size_error) + def test_stream_remote_error + open_server_connection { |con| + stream = nil + con.on(:headers) { |s| + stream = s + raise RemoteStreamError.new(:frame_size_error) + } + + assert_stream_error(:frame_size_error) { + con << Frame.headers(1, "", :end_headers).assemble + } last = sent_frames.last assert_equal(:rst_stream, last.type) - assert_equal(StreamError.new(:frame_size_error).http2_error_code, last.payload.uint32) + assert_equal(HTTPError::ERROR_CODES[:frame_size_error], last.payload.uint32) assert_equal(:closed, stream.state) } end + + def test_stream_local_error + open_server_connection { |con| + stream = nil + con.on(:headers) { |s| stream = s } + + con << Frame.headers(1, "", :end_headers).assemble + assert_raises(LocalStreamError) { + con << Frame.rst_stream(1, :frame_size_error).assemble + } + } + end end diff --git a/test/utils/assertions.rb b/test/utils/assertions.rb index 71928ed..746d475 100644 --- a/test/utils/assertions.rb +++ b/test/utils/assertions.rb @@ -1,21 +1,21 @@ module CustomAssertions def assert_connection_error(type, &blk) - assert_http_error(Plum::ConnectionError, type, &blk) + assert_http_error(Plum::RemoteConnectionError, type, &blk) end def assert_stream_error(type, &blk) - assert_http_error(Plum::StreamError, type, &blk) + assert_http_error(Plum::RemoteStreamError, type, &blk) end def assert_no_error(stream: nil, connection: nil, &blk) - Plum::ConnectionError.reset - Plum::StreamError.reset + Plum::RemoteConnectionError.reset + Plum::RemoteStreamError.reset begin blk.call - rescue Plum::HTTPError + rescue Plum::RemoteHTTPError end - assert_nil(Plum::StreamError.last, "No stream error expected but raised: #{Plum::StreamError.last}") - assert_nil(Plum::ConnectionError.last, "No connection error expected but raised: #{Plum::ConnectionError.last}") + assert_nil(Plum::RemoteStreamError.last, "No stream error expected but raised: #{Plum::RemoteStreamError.last}") + assert_nil(Plum::RemoteConnectionError.last, "No connection error expected but raised: #{Plum::RemoteConnectionError.last}") end def assert_frame(frame, **args) @@ -56,5 +56,5 @@ module LastErrorExtension base.reset end end -Plum::ConnectionError.__send__(:prepend, LastErrorExtension) -Plum::StreamError.__send__(:prepend, LastErrorExtension) +Plum::RemoteConnectionError.__send__(:prepend, LastErrorExtension) +Plum::RemoteStreamError.__send__(:prepend, LastErrorExtension) |