Low-Level Details ================= .. warning:: This section of the documentation covers low-level implementation details of h2. This is most likely to be of use to h2 developers and to other HTTP/2 implementers, though it could well be of general interest. Feel free to peruse it, but if you're looking for information about how to *use* h2 you should consider looking elsewhere. State Machines -------------- h2 is fundamentally built on top of a pair of interacting Finite State Machines. One of these FSMs manages per-connection state, and another manages per-stream state. Almost without exception (see :ref:`priority` for more details) every single frame is unconditionally translated into events for both state machines and those state machines are turned. The advantages of a system such as this is that the finite state machines can very densely encode the kinds of things that are allowed at any particular moment in a HTTP/2 connection. However, most importantly, almost all protocols are defined *in terms* of finite state machines: that is, protocol descriptions can be reduced to a number of states and inputs. That makes FSMs a very natural tool for implementing protocol stacks. Indeed, most protocol implementations that do not explicitly encode a finite state machine almost always *implicitly* encode a finite state machine, by using classes with a bunch of variables that amount to state-tracking variables, or by using the call-stack as an implicit state tracking mechanism. While these methods are not immediately problematic, they tend to lack *explicitness*, and can lead to subtle bugs of the form "protocol action X is incorrectly allowed in state Y". For these reasons, we have implemented two *explicit* finite state machines. These machines aim to encode most of the protocol-specific state, in particular regarding what frame is allowed at what time. This target goal is sometimes not achieved: in particular, as of this writing the *stream* FSM contains a number of other state variables that really ought to be rolled into the state machine itself in the form of new states, or in the form of a transformation of the FSM to use state *vectors* instead of state *scalars*. The following sections contain some implementers notes on these FSMs. Connection State Machine ~~~~~~~~~~~~~~~~~~~~~~~~ The "outer" state machine, the first one that is encountered when sending or receiving data, is the connection state machine. This state machine tracks whole-connection state. This state machine is primarily intended to forbid certain actions on the basis of whether the implementation is acting as a client or a server. For example, clients are not permitted to send ``PUSH_PROMISE`` frames: this state machine forbids that by refusing to define a valid transition from the ``CLIENT_OPEN`` state for the ``SEND_PUSH_PROMISE`` event. Otherwise, this particular state machine triggers no side-effects. It has a very coarse, high-level, functionality. A visual representation of this FSM is shown below: .. image:: _static/h2.connection.H2ConnectionStateMachine.dot.png :alt: A visual representation of the connection FSM. :target: _static/h2.connection.H2ConnectionStateMachine.dot.png .. _stream-state-machine: Stream State Machine ~~~~~~~~~~~~~~~~~~~~ Once the connection state machine has been spun, any frame that belongs to a stream is passed to the stream state machine for its given stream. Each stream has its own instance of the state machine, but all of them share the transition table: this is because the table itself is sufficiently large that having it be per-instance would be a ridiculous memory overhead. Unlike the connection state machine, the stream state machine is quite complex. This is because it frequently needs to encode some side-effects. The most common side-effect is emitting a ``RST_STREAM`` frame when an error is encountered: the need to do this means that far more transitions need to be encoded than for the connection state machine. Many of the side-effect functions in this state machine also raise :class:`ProtocolError