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
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
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)
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."
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)
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.")
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)
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)
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
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'
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)
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
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, }
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)')
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)
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)
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)
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] = {}
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
def __init__(self): super(PriorityStateMachine, self).__init__() self.tree = priority.PriorityTree() self.stream_ids = set([0]) self.blocked_stream_ids = set()