示例#1
0
    def test_priority_reprioritizing_stream_with_absent_parent(
            self, exclusive):
        """
        Attemping to reprioritize a stream to depend on a stream that is not in
        the tree automatically inserts the parent with default priority.
        """
        p = priority.PriorityTree()
        p.insert_stream(stream_id=3)

        p.reprioritize(stream_id=3,
                       depends_on=1,
                       exclusive=exclusive,
                       weight=32)

        # Iterate 10 times to prove that the parent stream starts blocked.
        first_ten_ids = [next(p) for _ in range(0, 10)]
        assert first_ten_ids == [3] * 10

        # Unblock the parent.
        p.unblock(1)

        # Iterate 10 times, expecting only the parent.
        next_ten_ids = [next(p) for _ in range(0, 10)]
        assert next_ten_ids == [1] * 10

        # Insert a new stream into the tree with default priority.
        p.insert_stream(stream_id=5)

        # Iterate 10 more times. Expect the parent, and the new stream, in
        # equal amounts.
        next_ten_ids = [next(p) for _ in range(0, 10)]
        assert next_ten_ids == [5, 1] * 5
示例#2
0
    def test_period_of_repetition(self, streams_and_weights):
        """
        The period of repetition of a priority sequence is given by the sum of
        the weights of the streams. Once that many values have been pulled out
        the sequence repeats identically.
        """
        p = priority.PriorityTree()
        weights = []

        for stream, weight in streams_and_weights:
            p.insert_stream(stream_id=stream, weight=weight)
            weights.append(weight)

        period = sum(weights)

        # Pop off the first n elements, which will always be evenly
        # distributed.
        for _ in weights:
            next(p)

        pattern = [next(p) for _ in range(period)]
        pattern = itertools.cycle(pattern)

        for i in range(period * 20):
            assert next(p) == next(pattern), i
示例#3
0
    def __init__(self, reactor=None):
        config = h2.config.H2Configuration(client_side=False,
                                           header_encoding=None)
        self.conn = h2.connection.H2Connection(config=config)
        self.streams = {}

        self.priority = priority.PriorityTree()
        self._consumerBlocked = None
        self._sendingDeferred = None
        self._outboundStreamQueues = {}
        self._streamCleanupCallbacks = {}
        self._stillProducing = True

        # Limit the number of buffered control frame (e.g. PING and
        # SETTINGS) bytes.
        self._maxBufferedControlFrameBytes = 1024 * 17
        self._bufferedControlFrames = deque()
        self._bufferedControlFrameBytes = 0

        if reactor is None:
            from twisted.internet import reactor
        self._reactor = reactor

        # Start the data sending function.
        self._reactor.callLater(0, self._sendPrioritisedData)
示例#4
0
 def test_maximum_streams_with_non_int_is_error(self, maximum_streams):
     """
     Creating a PriorityTree with a non-int argument for maximum_streams
     is an error.
     """
     with pytest.raises(TypeError) as err:
         priority.PriorityTree(maximum_streams=maximum_streams)
     assert err.value.args[0] == "maximum_streams must be an int."
示例#5
0
 def test_stream_depending_on_self_is_error(self, stream_id, exclusive):
     """
     Inserting a stream that is dependent on itself is rejected.
     """
     p = priority.PriorityTree()
     with pytest.raises(priority.PriorityLoop):
         p.insert_stream(stream_id=stream_id,
                         depends_on=stream_id,
                         exclusive=exclusive)
示例#6
0
 def test_maximum_streams_with_bad_int_is_error(self, maximum_streams):
     """
     Creating a PriorityTree with a non-positive integer for maximum_streams
     is an error.
     """
     with pytest.raises(ValueError) as err:
         priority.PriorityTree(maximum_streams=maximum_streams)
     assert (
         err.value.args[0] == "maximum_streams must be a positive integer.")
示例#7
0
    def test_priority_tree_raises_error_inserting_duplicate(self):
        """
        Attempting to insert a stream that is already in the tree raises a
        DuplicateStreamError
        """
        p = priority.PriorityTree()
        p.insert_stream(1)

        with pytest.raises(priority.DuplicateStreamError):
            p.insert_stream(1)
示例#8
0
 def test_reprioritize_depend_on_self_is_error(self, stream_id, exclusive):
     """
     Reprioritizing a stream to make it dependent on itself is an error.
     """
     p = priority.PriorityTree()
     p.insert_stream(stream_id=stream_id)
     with pytest.raises(priority.PriorityLoop):
         p.reprioritize(stream_id=stream_id,
                        depends_on=stream_id,
                        exclusive=exclusive)
示例#9
0
def readme_tree():
    """
    Provide a tree configured as the one in the readme.
    """
    p = priority.PriorityTree()
    p.insert_stream(stream_id=1)
    p.insert_stream(stream_id=3)
    p.insert_stream(stream_id=5, depends_on=1)
    p.insert_stream(stream_id=7, weight=32)
    p.insert_stream(stream_id=9, depends_on=7, weight=8)
    p.insert_stream(stream_id=11, depends_on=7, exclusive=True)
    return p
示例#10
0
    def test_stream_with_non_integer_weight_is_error(self, weight):
        """
        Giving a stream a non-integer weight is rejected.
        """
        p = priority.PriorityTree()
        with pytest.raises(priority.BadWeightError) as err:
            p.insert_stream(stream_id=1, weight=weight)
        assert err.value.args[0] == 'Stream weight should be an integer'

        p.insert_stream(stream_id=2)
        with pytest.raises(priority.BadWeightError) as err:
            p.reprioritize(stream_id=2, weight=weight)
        assert err.value.args[0] == 'Stream weight should be an integer'
示例#11
0
    def test_priority_refuses_to_allow_too_many_streams_in_tree(self, count):
        """
        Attempting to insert more streams than maximum_streams into the tree
        fails.
        """
        p = priority.PriorityTree(maximum_streams=count)

        # This isn't an off-by-one error: stream 0 is in the tree by default.
        for x in range(1, count):
            p.insert_stream(x)

        with pytest.raises(priority.TooManyStreamsError):
            p.insert_stream(x + 1)
示例#12
0
    def test_can_insert_stream_with_exclusive_dependency_on_0(
            self, depends_on):
        """
        It is acceptable to insert a stream with an exclusive dependency on
        stream 0, both explicitly and implicitly.
        """
        p = priority.PriorityTree()
        p.insert_stream(stream_id=1)
        p.insert_stream(stream_id=3)

        p.insert_stream(stream_id=5, depends_on=depends_on, exclusive=True)

        next_ten_ids = [next(p) for _ in range(0, 10)]
        assert next_ten_ids == [5] * 10
示例#13
0
    def __init__(self,
                 client_side: bool,
                 *,
                 loop=None,
                 concurrency=8,
                 functional_timeout=2):
        if loop is None:
            loop = asyncio.get_event_loop()
        self._loop = loop
        config = H2Configuration(client_side=client_side,
                                 header_encoding='utf-8')
        self._conn = H2Connection(config=config)
        self._transport = None
        self._streams = {}
        self._inbound_requests = asyncio.Queue(concurrency, loop=loop)
        self._priority = priority.PriorityTree()
        self._priority_events = {}
        self._handler = None

        # Locks

        self._is_resumed = False
        self._resumed = CallableEvent(lambda: self._is_resumed, loop=loop)
        self._stream_creatable = CallableEvent(self._is_stream_creatable,
                                               loop=loop)
        self._last_active = 0
        self._ping_index = -1
        self._ping_time = 0
        self._rtt = None
        self._functional_timeout = functional_timeout
        self._functional = CallableEvent(self._is_functional, loop=loop)

        # Dispatch table

        self._event_handlers = {
            events.RequestReceived: self._request_received,
            events.ResponseReceived: self._response_received,
            events.TrailersReceived: self._trailers_received,
            events.DataReceived: self._data_received,
            events.WindowUpdated: self._window_updated,
            events.RemoteSettingsChanged: self._remote_settings_changed,
            events.PingAcknowledged: self._ping_acknowledged,
            events.StreamEnded: self._stream_ended,
            events.StreamReset: self._stream_reset,
            events.PushedStreamReceived: self._pushed_stream_received,
            events.SettingsAcknowledged: self._settings_acknowledged,
            events.PriorityUpdated: self._priority_updated,
            events.ConnectionTerminated: self._connection_terminated,
        }
示例#14
0
    def test_stream_with_out_of_bounds_weight_is_error(self, weight):
        """
        Giving a stream an out-of-bounds integer weight is rejected.
        """
        p = priority.PriorityTree()
        with pytest.raises(priority.BadWeightError) as err:
            p.insert_stream(stream_id=1, weight=weight)
        assert (err.value.args[0] ==
                'Stream weight must be between 1 and 256 (inclusive)')

        p.insert_stream(stream_id=2)
        with pytest.raises(priority.BadWeightError) as err:
            p.reprioritize(stream_id=2, weight=weight)
        assert (err.value.args[0] ==
                'Stream weight must be between 1 and 256 (inclusive)')
示例#15
0
    def test_priority_raises_good_errors_for_zero_stream(self):
        """
        Attempting operations on stream 0 raises a PseudoStreamError.
        """
        p = priority.PriorityTree()
        p.insert_stream(1)

        with pytest.raises(priority.PseudoStreamError):
            p.reprioritize(0)

        with pytest.raises(priority.PseudoStreamError):
            p.block(0)

        with pytest.raises(priority.PseudoStreamError):
            p.unblock(0)

        with pytest.raises(priority.PseudoStreamError):
            p.remove_stream(0)
示例#16
0
    def test_priority_raises_good_errors_for_missing_streams(self):
        """
        Attempting operations on absent streams raises a MissingStreamError.
        """
        p = priority.PriorityTree()
        p.insert_stream(1)

        with pytest.raises(priority.MissingStreamError):
            p.reprioritize(3)

        with pytest.raises(priority.MissingStreamError):
            p.block(3)

        with pytest.raises(priority.MissingStreamError):
            p.unblock(3)

        with pytest.raises(priority.MissingStreamError):
            p.remove_stream(3)
示例#17
0
    def __init__(self, reactor=None):
        self.conn = h2.connection.H2Connection(client_side=False,
                                               header_encoding=None)
        self.streams = {}

        self.priority = priority.PriorityTree()
        self._consumerBlocked = None
        self._sendingDeferred = None
        self._outboundStreamQueues = {}
        self._streamCleanupCallbacks = {}
        self._stillProducing = True

        if reactor is None:
            from twisted.internet import reactor
        self._reactor = reactor

        # Start the data sending function.
        self._reactor.callLater(0, self._sendPrioritisedData)
示例#18
0
    def __init__(
        self,
        config: Config,
        ssl: bool,
        client: Optional[Tuple[str, int]],
        server: Optional[Tuple[str, int]],
        send: Callable[[Event], Awaitable[None]],
        spawn_app: Callable[[dict, Callable], Awaitable[Callable]],
        event_class: Type[IOEvent],
    ) -> None:
        self.client = client
        self.closed = False
        self.config = config

        self.connection = h2.connection.H2Connection(
            config=h2.config.H2Configuration(client_side=False,
                                             header_encoding=None))
        self.connection.DEFAULT_MAX_INBOUND_FRAME_SIZE = config.h2_max_inbound_frame_size
        self.connection.local_settings = h2.settings.Settings(
            client=False,
            initial_values={
                h2.settings.SettingCodes.MAX_CONCURRENT_STREAMS:
                config.h2_max_concurrent_streams,
                h2.settings.SettingCodes.MAX_HEADER_LIST_SIZE:
                config.h2_max_header_list_size,
                h2.settings.SettingCodes.ENABLE_CONNECT_PROTOCOL: 1,
            },
        )

        self.event_class = event_class
        self.send = send
        self.server = server
        self.spawn_app = spawn_app
        self.ssl = ssl
        self.streams: Dict[int, Union[HTTPStream, WSStream]] = {}
        # The below are used by the sending task
        self.has_data = event_class()
        self.priority = priority.PriorityTree()
        self.stream_buffers: Dict[int, StreamBuffer] = {}
示例#19
0
    def test_priority_tree_distribution(self, streams_and_weights):
        """
        Once a full period of repetition has been observed, each stream has
        been emitted a number of times equal to its weight.
        """
        p = priority.PriorityTree()
        weights = []

        for stream, weight in streams_and_weights:
            p.insert_stream(stream_id=stream, weight=weight)
            weights.append(weight)

        period = sum(weights)

        # Pop off the first n elements, which will always be evenly
        # distributed.
        for _ in weights:
            next(p)

        count = collections.Counter(next(p) for _ in range(period))

        assert len(count) == len(streams_and_weights)
        for stream, weight in streams_and_weights:
            count[stream] == weight
示例#20
0
 def __init__(self):
     super(PriorityStateMachine, self).__init__()
     self.tree = priority.PriorityTree()
     self.stream_ids = set([0])
     self.blocked_stream_ids = set()