rhenium/plum

View on GitHub
test/plum/stream/test_handle_frame.rb

Summary

Maintainability
A
45 mins
Test Coverage
require "test_helper"

using Plum::BinaryString

class StreamHandleFrameTest < Minitest::Test
  ## DATA
  def test_stream_handle_data
    payload = "ABC" * 5
    open_new_stream(state: :open) { |stream|
      data = nil
      stream.connection.on(:data) { |_, _data| data = _data }
      stream.receive_frame(Frame.craft(type: :data, stream_id: stream.id,
                                     flags: [], payload: payload))
      assert_equal(payload, data)
    }
  end

  def test_stream_handle_data_padded
    payload = "ABC" * 5
    open_new_stream(state: :open) { |stream|
      data = nil
      stream.connection.on(:data) { |_, _data| data = _data }
      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)
    }
  end

  def test_stream_handle_data_too_long_padding
    payload = "ABC" * 5
    open_new_stream(state: :open) { |stream|
      assert_connection_error(:protocol_error) {
        stream.receive_frame(Frame.craft(type: :data, stream_id: stream.id,
                                       flags: [:padded], payload: "".push_uint8(100).push(payload).push("\x00"*6)))
      }
    }
  end

  def test_stream_handle_data_end_stream
    payload = "ABC" * 5
    open_new_stream(state: :open) { |stream|
      stream.receive_frame(Frame.craft(type: :data, stream_id: stream.id,
                                     flags: [:end_stream], payload: payload))
      assert_equal(:half_closed_remote, stream.state)
    }
  end

  def test_stream_handle_data_invalid_state
    payload = "ABC" * 5
    open_new_stream(state: :half_closed_remote) { |stream|
      assert_stream_error(:stream_closed) {
        stream.receive_frame(Frame.craft(type: :data, stream_id: stream.id,
                                       flags: [:end_stream], payload: payload))
      }
    }
  end

  ## HEADERS
  def test_stream_handle_headers_single
    open_new_stream { |stream|
      headers = nil
      stream.connection.on(:headers) { |_, _headers|
        headers = _headers
      }
      stream.receive_frame(Frame.craft(type: :headers,
                                     stream_id: stream.id,
                                     flags: [:end_headers],
                                     payload: HPACK::Encoder.new(0).encode([[":path", "/"]])))
      assert_equal(:open, stream.state)
      assert_equal([[":path", "/"]], headers)
    }
  end

  def test_stream_handle_headers_continuation
    open_new_stream { |stream|
      payload = HPACK::Encoder.new(0).encode([[":path", "/"]])
      headers = nil
      stream.connection.on(:headers) { |_, _headers|
        headers = _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.craft(type: :continuation,
                                     stream_id: stream.id,
                                     flags: [:end_headers],
                                     payload: payload[5..-1]))
      assert_equal(:half_closed_remote, stream.state)
      assert_equal([[":path", "/"]], headers)
    }
  end

  def test_stream_handle_headers_padded
    open_new_stream { |stream|
      payload = HPACK::Encoder.new(0).encode([[":path", "/"]])
      headers = nil
      stream.connection.on(:headers) { |_, _headers|
        headers = _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)))
      assert_equal([[":path", "/"]], headers)
    }
  end

  def test_stream_handle_headers_too_long_padding
    open_new_stream { |stream|
      payload = HPACK::Encoder.new(0).encode([[":path", "/"]])
      assert_connection_error(:protocol_error) {
        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))))
      }
    }
  end

  def test_stream_handle_headers_broken
    open_new_stream { |stream|
      payload = "\x00\x01\x02"
      assert_connection_error(:compression_error) {
        stream.receive_frame(Frame.craft(type: :headers,
                                       stream_id: stream.id,
                                       flags: [:end_headers],
                                       payload: payload))
      }
    }
  end

  def test_stream_handle_headers_state
    _payload = HPACK::Encoder.new(0).encode([[":path", "/"]])
    open_new_stream(state: :reserved_local) { |stream|
      assert_connection_error(:protocol_error) {
        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.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.craft(type: :headers, stream_id: stream.id, flags: [:end_headers, :end_stream], payload: _payload))
      }
    }
  end

  def test_stream_handle_headers_priority
    open_server_connection { |con|
      parent = open_new_stream(con)
      stream = open_new_stream(con)

      headers = nil
      stream.connection.on(:headers) { |_, _headers| headers = _headers }
      header_block = HPACK::Encoder.new(0).encode([[":path", "/"]])
      payload = "".push_uint32((1 << 31) | parent.id)
                  .push_uint8(50)
        .push(header_block)
      stream.receive_frame(Frame.craft(type: :headers,
                                     stream_id: stream.id,
                                     flags: [:end_headers, :priority],
                                     payload: payload))
      assert_equal(true, stream.exclusive)
      assert_equal(parent, stream.parent)
      assert_equal(50, stream.weight)
      assert_equal([[":path", "/"]], headers)
    }
  end

  ## PRIORITY
  def test_stream_handle_priority
    open_server_connection { |con|
      parent = open_new_stream(con)
      stream = open_new_stream(con)

      payload = "".push_uint32((1 << 31) | parent.id)
                  .push_uint8(50)
      stream.receive_frame(Frame.craft(type: :priority,
                                     stream_id: stream.id,
                                     payload: payload))
      assert_equal(true, stream.exclusive)
      assert_equal(parent, stream.parent)
      assert_equal(50, stream.weight)
    }
  end

  def test_stream_handle_priority_self_depend
    open_server_connection { |con|
      stream = open_new_stream(con)
      payload = "".push_uint32((0 << 31) | stream.id).push_uint8(6)
      stream.receive_frame(Frame.craft(type: :priority,
                                     stream_id: stream.id,
                                     payload: payload))
      last = sent_frames.last
      assert_equal(:rst_stream, last.type)
      assert_equal(HTTPError::ERROR_CODES[:protocol_error], last.payload.uint32)
    }
  end

  def test_stream_handle_priority_exclusive
    open_server_connection { |con|
      parent = open_new_stream(con)
      stream0 = open_new_stream(con, parent: parent)
      stream1 = open_new_stream(con, parent: parent)
      stream2 = open_new_stream(con, parent: parent)

      payload = "".push_uint32((1 << 31) | parent.id).push_uint8(6)
      stream0.receive_frame(Frame.craft(type: :priority,
                                      stream_id: stream0.id,
                                      payload: payload))
      assert_equal(parent, stream0.parent)
      assert_equal(stream0, stream1.parent)
      assert_equal(stream0, stream2.parent)
    }
  end

  def test_stream_handle_frame_size_error
    open_new_stream { |stream|
      assert_stream_error(:frame_size_error) {
        stream.receive_frame(Frame.craft(type: :priority,
                                       stream_id: stream.id,
                                       payload: "\x00"))
      }
    }
  end

  ## RST_STREAM
  def test_stream_handle_rst_stream
    open_new_stream(state: :reserved_local) { |stream|
      stream.receive_frame(Frame.craft(type: :rst_stream,
                                     stream_id: stream.id,
                                     payload: "\x00\x00\x00\x00"))
      assert_equal(:closed, stream.state)
    }
  end

  def test_stream_handle_rst_stream_idle
    open_new_stream(state: :idle) { |stream|
      assert_connection_error(:protocol_error) {
        stream.receive_frame(Frame.craft(type: :rst_stream,
                                       stream_id: stream.id,
                                       payload: "\x00\x00\x00\x00"))
      }
    }
  end

  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.craft(type: :rst_stream,
                                       stream_id: stream.id,
                                       payload: "\x00\x00\x00"))
      }
    }
  end

end