about summary refs log tree commit diff
path: root/.venv/lib/python3.12/site-packages/h2/windows.py
diff options
context:
space:
mode:
Diffstat (limited to '.venv/lib/python3.12/site-packages/h2/windows.py')
-rw-r--r--.venv/lib/python3.12/site-packages/h2/windows.py133
1 files changed, 133 insertions, 0 deletions
diff --git a/.venv/lib/python3.12/site-packages/h2/windows.py b/.venv/lib/python3.12/site-packages/h2/windows.py
new file mode 100644
index 00000000..0efdd9fe
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/h2/windows.py
@@ -0,0 +1,133 @@
+"""
+h2/windows
+~~~~~~~~~~
+
+Defines tools for managing HTTP/2 flow control windows.
+
+The objects defined in this module are used to automatically manage HTTP/2
+flow control windows. Specifically, they keep track of what the size of the
+window is, how much data has been consumed from that window, and how much data
+the user has already used. It then implements a basic algorithm that attempts
+to manage the flow control window without user input, trying to ensure that it
+does not emit too many WINDOW_UPDATE frames.
+"""
+from __future__ import annotations
+
+from .exceptions import FlowControlError
+
+# The largest acceptable value for a HTTP/2 flow control window.
+LARGEST_FLOW_CONTROL_WINDOW = 2**31 - 1
+
+
+class WindowManager:
+    """
+    A basic HTTP/2 window manager.
+
+    :param max_window_size: The maximum size of the flow control window.
+    :type max_window_size: ``int``
+    """
+
+    def __init__(self, max_window_size: int) -> None:
+        assert max_window_size <= LARGEST_FLOW_CONTROL_WINDOW
+        self.max_window_size = max_window_size
+        self.current_window_size = max_window_size
+        self._bytes_processed = 0
+
+    def window_consumed(self, size: int) -> None:
+        """
+        We have received a certain number of bytes from the remote peer. This
+        necessarily shrinks the flow control window!
+
+        :param size: The number of flow controlled bytes we received from the
+            remote peer.
+        :type size: ``int``
+        :returns: Nothing.
+        :rtype: ``None``
+        """
+        self.current_window_size -= size
+        if self.current_window_size < 0:
+            msg = "Flow control window shrunk below 0"
+            raise FlowControlError(msg)
+
+    def window_opened(self, size: int) -> None:
+        """
+        The flow control window has been incremented, either because of manual
+        flow control management or because of the user changing the flow
+        control settings. This can have the effect of increasing what we
+        consider to be the "maximum" flow control window size.
+
+        This does not increase our view of how many bytes have been processed,
+        only of how much space is in the window.
+
+        :param size: The increment to the flow control window we received.
+        :type size: ``int``
+        :returns: Nothing
+        :rtype: ``None``
+        """
+        self.current_window_size += size
+
+        if self.current_window_size > LARGEST_FLOW_CONTROL_WINDOW:
+            msg = f"Flow control window mustn't exceed {LARGEST_FLOW_CONTROL_WINDOW}"
+            raise FlowControlError(msg)
+
+        self.max_window_size = max(self.current_window_size, self.max_window_size)
+
+    def process_bytes(self, size: int) -> int | None:
+        """
+        The application has informed us that it has processed a certain number
+        of bytes. This may cause us to want to emit a window update frame. If
+        we do want to emit a window update frame, this method will return the
+        number of bytes that we should increment the window by.
+
+        :param size: The number of flow controlled bytes that the application
+            has processed.
+        :type size: ``int``
+        :returns: The number of bytes to increment the flow control window by,
+            or ``None``.
+        :rtype: ``int`` or ``None``
+        """
+        self._bytes_processed += size
+        return self._maybe_update_window()
+
+    def _maybe_update_window(self) -> int | None:
+        """
+        Run the algorithm.
+
+        Our current algorithm can be described like this.
+
+        1. If no bytes have been processed, we immediately return 0. There is
+           no meaningful way for us to hand space in the window back to the
+           remote peer, so let's not even try.
+        2. If there is no space in the flow control window, and we have
+           processed at least 1024 bytes (or 1/4 of the window, if the window
+           is smaller), we will emit a window update frame. This is to avoid
+           the risk of blocking a stream altogether.
+        3. If there is space in the flow control window, and we have processed
+           at least 1/2 of the window worth of bytes, we will emit a window
+           update frame. This is to minimise the number of window update frames
+           we have to emit.
+
+        In a healthy system with large flow control windows, this will
+        irregularly emit WINDOW_UPDATE frames. This prevents us starving the
+        connection by emitting eleventy bajillion WINDOW_UPDATE frames,
+        especially in situations where the remote peer is sending a lot of very
+        small DATA frames.
+        """
+        # TODO: Can the window be smaller than 1024 bytes? If not, we can
+        # streamline this algorithm.
+        if not self._bytes_processed:
+            return None
+
+        max_increment = (self.max_window_size - self.current_window_size)
+        increment = 0
+
+        # Note that, even though we may increment less than _bytes_processed,
+        # we still want to set it to zero whenever we emit an increment. This
+        # is because we'll always increment up to the maximum we can.
+        if ((self.current_window_size == 0) and (
+                self._bytes_processed > min(1024, self.max_window_size // 4))) or self._bytes_processed >= (self.max_window_size // 2):
+            increment = min(self._bytes_processed, max_increment)
+            self._bytes_processed = 0
+
+        self.current_window_size += increment
+        return increment