#############################################################################
#                                                                           #
#  http2.py --- HTTP/2 support for Scapy                                    #
#               see RFC7540 and RFC7541                                     #
#               for more information                                        #
#                                                                           #
#  Copyright (C) 2016  Florian Maury <florian.maury@ssi.gouv.fr>            #
#                                                                           #
#  This file is part of Scapy                                               #
#  Scapy is free software: you can redistribute it and/or modify it         #
#  under the terms of the GNU General Public License version 2 as           #
#  published by the Free Software Foundation.                               #
#                                                                           #
#  This program is distributed in the hope that it will be useful, but      #
#  WITHOUT ANY WARRANTY; without even the implied warranty of               #
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU        #
#  General Public License for more details.                                 #
#                                                                           #
#############################################################################
"""http2 Module
Implements packets and fields required to encode/decode HTTP/2 Frames
and HPack encoded headers
"""

# scapy.contrib.status=loads
# scapy.contrib.description=HTTP/2 (RFC 7540, RFC 7541)

# base_classes triggers an unwanted import warning
# flake8: noqa: F821

from __future__ import absolute_import
from __future__ import print_function
import abc
import re
import sys
from io import BytesIO
import struct
import scapy.modules.six as six
from scapy.compat import raw, plain_str, bytes_hex, orb, chb, bytes_encode

# Only required if using mypy-lang for static typing
# Most symbols are used in mypy-interpreted "comments".
# Sized must be one of the superclasses of a class implementing __len__
try:
    from typing import Optional, List, Union, Callable, Any, Tuple, Sized  # noqa: F401, E501
except ImportError:
    class Sized(object):
        pass

import scapy.fields as fields
import scapy.packet as packet
import scapy.config as config
import scapy.volatile as volatile
import scapy.error as error

###############################################################################
#                                                HPACK Integer Fields         #
###############################################################################


class HPackMagicBitField(fields.BitField):
    """ HPackMagicBitField is a BitField variant that cannot be assigned another
    value than the default one. This field must not be used where there is
    potential for fuzzing. OTOH, this field makes sense (for instance, if the
    magic bits are used by a dispatcher to select the payload class)
    """

    __slots__ = ['_magic']

    def __init__(self, name, default, size):
        # type: (str, int, int) -> None
        """
        @param str name: this field instance name.
        @param int default: this field only valid value.
        @param int size: this bitfield bitlength.
        @return None
        @raise AssertionError
        """
        assert(default >= 0)
        # size can be negative if encoding is little-endian (see rev property of bitfields)  # noqa: E501
        assert(size != 0)
        self._magic = default
        super(HPackMagicBitField, self).__init__(name, default, size)

    def addfield(self, pkt, s, val):
        # type: (Optional[packet.Packet], Union[str, Tuple[str, int, int]], int) -> Union[str, Tuple[str, int, int]]  # noqa: E501
        """
        @param packet.Packet|None pkt: the packet instance containing this field instance; probably unused.  # noqa: E501
        @param str|(str, int, long) s: either a str if 0 == size%8 or a tuple with the string to add this field to, the  # noqa: E501
          number of bits already generated and the generated value so far.
        @param int val: unused; must be equal to default value
        @return str|(str, int, long): the s string extended with this field machine representation  # noqa: E501
        @raise AssertionError
        """
        assert val == self._magic, 'val parameter must value {}; received: {}'.format(self._magic, val)  # noqa: E501
        return super(HPackMagicBitField, self).addfield(pkt, s, self._magic)

    def getfield(self, pkt, s):
        # type: (Optional[packet.Packet], Union[str, Tuple[str, int]]) -> Tuple[Union[Tuple[str, int], str], int]  # noqa: E501
        """
        @param packet.Packet|None pkt: the packet instance containing this field instance; probably unused.  # noqa: E501
        @param str|(str, int) s: either a str if size%8==0 or a tuple with the string to parse from and the number of  # noqa: E501
          bits already consumed by previous bitfield-compatible fields.
        @return (str|(str, int), int): Returns the remaining string and the parsed value. May return a tuple if there  # noqa: E501
          are remaining bits to parse in the first byte. Returned value is equal to default value  # noqa: E501
        @raise AssertionError
        """
        r = super(HPackMagicBitField, self).getfield(pkt, s)
        assert (
            isinstance(r, tuple) and
            len(r) == 2 and
            isinstance(r[1], six.integer_types)
        ), 'Second element of BitField.getfield return value expected to be an int or a long; API change detected'  # noqa: E501
        assert r[1] == self._magic, 'Invalid value parsed from s; error in class guessing detected!'  # noqa: E501
        return r

    def h2i(self, pkt, x):
        # type: (Optional[packet.Packet], int) -> int
        """
        @param packet.Packet|None pkt: the packet instance containing this field instance; probably unused  # noqa: E501
        @param int x: unused; must be equal to default value
        @return int; default value
        @raise AssertionError
        """
        assert x == self._magic, \
            'EINVAL: x: This field is magic. Do not attempt to modify it. Expected value: {}'.format(self._magic)  # noqa: E501
        return super(HPackMagicBitField, self).h2i(pkt, self._magic)

    def i2h(self, pkt, x):
        # type: (Optional[packet.Packet], int) -> int
        """
        @param packet.Packet|None pkt: the packet instance containing this field instance; probably unused  # noqa: E501
        @param int x: unused; must be equal to default value
        @return int; default value
        @raise AssertionError
        """
        assert x == self._magic, \
            'EINVAL: x: This field is magic. Do not attempt to modify it. Expected value: {}'.format(self._magic)  # noqa: E501
        return super(HPackMagicBitField, self).i2h(pkt, self._magic)

    def m2i(self, pkt, x):
        # type: (Optional[packet.Packet], int) -> int
        """
        @param packet.Packet|None pkt: the packet instance containing this field instance; probably unused  # noqa: E501
        @param int x: must be the machine representatino of the default value
        @return int; default value
        @raise AssertionError
        """
        r = super(HPackMagicBitField, self).m2i(pkt, x)
        assert r == self._magic, 'Invalid value parsed from m2i; error in class guessing detected!'  # noqa: E501
        return r

    def i2m(self, pkt, x):
        # type: (Optional[packet.Packet], int) -> int
        """
        @param packet.Packet|None pkt: the packet instance containing this field instance; probably unused  # noqa: E501
        @param int x: unused; must be equal to default value
        @return int; default value
        @raise AssertionError
        """
        assert x == self._magic, \
            'EINVAL: x: This field is magic. Do not attempt to modify it. Expected value: {}'.format(self._magic)  # noqa: E501
        return super(HPackMagicBitField, self).i2m(pkt, self._magic)

    def any2i(self, pkt, x):
        # type: (Optional[packet.Packet], int) -> int
        """
        @param packet.Packet|None pkt: the packet instance containing this field instance; probably unused  # noqa: E501
        @param int x: unused; must be equal to default value
        @return int; default value
        @raise AssertionError
        """
        assert x == self._magic, \
            'EINVAL: x: This field is magic. Do not attempt to modify it. Expected value: {}'.format(self._magic)  # noqa: E501
        return super(HPackMagicBitField, self).any2i(pkt, self._magic)


class AbstractUVarIntField(fields.Field):
    """AbstractUVarIntField represents an integer as defined in RFC7541
    """

    __slots__ = ['_max_value', 'size', 'rev']
    """
    :var int size: the bit length of the prefix of this AbstractUVarIntField. It  # noqa: E501
      represents the complement of the number of MSB that are used in the
      current byte for other purposes by some other BitFields
    :var int _max_value: the maximum value that can be stored in the
      sole prefix. If the integer equals or exceeds this value, the max prefix
      value is assigned to the size first bits and the multibyte representation
      is used
    :var bool rev: is a fake property, also emulated for the sake of
      compatibility with Bitfields
    """

    def __init__(self, name, default, size):
        # type: (str, Optional[int], int) -> None
        """
        @param str name: the name of this field instance
        @param int|None default: positive, null or None default value for this field instance.  # noqa: E501
        @param int size: the number of bits to consider in the first byte. Valid range is ]0;8]  # noqa: E501
        @return None
        @raise AssertionError
        """
        assert(default is None or (isinstance(default, six.integer_types) and default >= 0))  # noqa: E501
        assert(0 < size <= 8)
        super(AbstractUVarIntField, self).__init__(name, default)
        self.size = size
        self._max_value = (1 << self.size) - 1

        # Configuring the fake property that is useless for this class but that is  # noqa: E501
        # expected from BitFields
        self.rev = False

    def h2i(self, pkt, x):
        # type: (Optional[packet.Packet], Optional[int]) -> Optional[int]
        """
        @param packet.Packet|None pkt: unused.
        @param int|None x: the value to convert.
        @return int|None: the converted value.
        @raise AssertionError
        """
        assert(not isinstance(x, six.integer_types) or x >= 0)
        return x

    def i2h(self, pkt, x):
        # type: (Optional[packet.Packet], Optional[int]) -> Optional[int]
        """
        @param packet.Packet|None pkt: unused.
        @param int|None x: the value to convert.
        @return: int|None: the converted value.
        """
        return x

    def _detect_multi_byte(self, fb):
        # type: (str) -> bool
        """ _detect_multi_byte returns whether the AbstractUVarIntField is represented on  # noqa: E501
          multiple bytes or not.

          A multibyte representation is indicated by all of the first size bits being set  # noqa: E501

        @param str fb: first byte, as a character.
        @return bool: True if multibyte repr detected, else False.
        @raise AssertionError
        """
        assert(isinstance(fb, int) or len(fb) == 1)
        return (orb(fb) & self._max_value) == self._max_value

    def _parse_multi_byte(self, s):
        # type: (str) -> int
        """ _parse_multi_byte parses x as a multibyte representation to get the
          int value of this AbstractUVarIntField.

        @param str s: the multibyte string to parse.
        @return int: The parsed int value represented by this AbstractUVarIntField.  # noqa: E501
        @raise: AssertionError
        @raise: Scapy_Exception if the input value encodes an integer larger than 1<<64  # noqa: E501
        """

        assert(len(s) >= 2)

        tmp_len = len(s)

        value = 0
        i = 1
        byte = orb(s[i])
        # For CPU sake, stops at an arbitrary large number!
        max_value = 1 << 64
        # As long as the MSG is set, an another byte must be read
        while byte & 0x80:
            value += (byte ^ 0x80) << (7 * (i - 1))
            if value > max_value:
                raise error.Scapy_Exception(
                    'out-of-bound value: the string encodes a value that is too large (>2^{64}): {}'.format(value)  # noqa: E501
                )
            i += 1
            assert i < tmp_len, 'EINVAL: x: out-of-bound read: the string ends before the AbstractUVarIntField!'  # noqa: E501
            byte = orb(s[i])
        value += byte << (7 * (i - 1))
        value += self._max_value

        assert(value >= 0)
        return value

    def m2i(self, pkt, x):
        # type: (Optional[packet.Packet], Union[str, Tuple[str, int]]) -> int
        """
          A tuple is expected for the "x" param only if "size" is different than 8. If a tuple is received, some bits  # noqa: E501
          were consumed by another field. This field consumes the remaining bits, therefore the int of the tuple must  # noqa: E501
          equal "size".

        @param packet.Packet|None pkt: unused.
        @param str|(str, int) x: the string to convert. If bits were consumed by a previous bitfield-compatible field.  # noqa: E501
        @raise AssertionError
        """
        assert(isinstance(x, bytes) or (isinstance(x, tuple) and x[1] >= 0))

        if isinstance(x, tuple):
            assert (8 - x[1]) == self.size, 'EINVAL: x: not enough bits remaining in current byte to read the prefix'  # noqa: E501
            val = x[0]
        else:
            assert isinstance(x, bytes) and self.size == 8, 'EINVAL: x: tuple expected when prefix_len is not a full byte'  # noqa: E501
            val = x

        if self._detect_multi_byte(val[0]):
            ret = self._parse_multi_byte(val)
        else:
            ret = orb(val[0]) & self._max_value

        assert(ret >= 0)
        return ret

    def i2m(self, pkt, x):
        # type: (Optional[packet.Packet], int) -> str
        """
        @param packet.Packet|None pkt: unused.
        @param int x: the value to convert.
        @return str: the converted value.
        @raise AssertionError
        """
        assert(x >= 0)

        if x < self._max_value:
            return chb(x)
        else:
            # The sl list join is a performance trick, because string
            # concatenation is not efficient with Python immutable strings
            sl = [chb(self._max_value)]
            x -= self._max_value
            while x >= 0x80:
                sl.append(chb(0x80 | (x & 0x7F)))
                x >>= 7
            sl.append(chb(x))
            return b''.join(sl)

    def any2i(self, pkt, x):
        # type: (Optional[packet.Packet], Union[None, str, int]) -> Optional[int]  # noqa: E501
        """
          A "x" value as a string is parsed as a binary encoding of a UVarInt. An int is considered an internal value.  # noqa: E501
          None is returned as is.

        @param packet.Packet|None pkt: the packet containing this field; probably unused.  # noqa: E501
        @param str|int|None x: the value to convert.
        @return int|None: the converted value.
        @raise AssertionError
        """
        if isinstance(x, type(None)):
            return x
        if isinstance(x, six.integer_types):
            assert(x >= 0)
            ret = self.h2i(pkt, x)
            assert(isinstance(ret, six.integer_types) and ret >= 0)
            return ret
        elif isinstance(x, bytes):
            ret = self.m2i(pkt, x)
            assert (isinstance(ret, six.integer_types) and ret >= 0)
            return ret
        assert False, 'EINVAL: x: No idea what the parameter format is'

    def i2repr(self, pkt, x):
        # type: (Optional[packet.Packet], Optional[int]) -> str
        """
        @param packet.Packet|None pkt: probably unused.
        @param x: int|None: the positive, null or none value to convert.
        @return str: the representation of the value.
        """
        return repr(self.i2h(pkt, x))

    def addfield(self, pkt, s, val):
        # type: (Optional[packet.Packet], Union[str, Tuple[str, int, int]], int) -> str  # noqa: E501
        """ An AbstractUVarIntField prefix always consumes the remaining bits
          of a BitField;if no current BitField is in use (no tuple in
          entry) then the prefix length is 8 bits and the whole byte is to
          be consumed
        @param packet.Packet|None pkt: the packet containing this field. Probably unused.  # noqa: E501
        @param str|(str, int, long) s: the string to append this field to. A tuple indicates that some bits were already  # noqa: E501
          generated by another bitfield-compatible field. This MUST be the case if "size" is not 8. The int is the  # noqa: E501
          number of bits already generated in the first byte of the str. The long is the value that was generated by the  # noqa: E501
          previous bitfield-compatible fields.
        @param int val: the positive or null value to be added.
        @return str: s concatenated with the machine representation of this field.  # noqa: E501
        @raise AssertionError
        """
        assert(val >= 0)
        if isinstance(s, bytes):
            assert self.size == 8, 'EINVAL: s: tuple expected when prefix_len is not a full byte'  # noqa: E501
            return s + self.i2m(pkt, val)

        # s is a tuple
        # assert(s[1] >= 0)
        # assert(s[2] >= 0)
        # assert (8 - s[1]) == self.size, 'EINVAL: s: not enough bits remaining in current byte to read the prefix'  # noqa: E501

        if val >= self._max_value:
            return s[0] + chb((s[2] << self.size) + self._max_value) + self.i2m(pkt, val)[1:]  # noqa: E501
        # This AbstractUVarIntField is only one byte long; setting the prefix value  # noqa: E501
        # and appending the resulting byte to the string
        return s[0] + chb((s[2] << self.size) + orb(self.i2m(pkt, val)))

    @staticmethod
    def _detect_bytelen_from_str(s):
        # type: (str) -> int
        """ _detect_bytelen_from_str returns the length of the machine
          representation of an AbstractUVarIntField starting at the beginning
          of s and which is assumed to expand over multiple bytes
          (value > _max_prefix_value).

        @param str s: the string to parse. It is assumed that it is a multibyte int.  # noqa: E501
        @return The bytelength of the AbstractUVarIntField.
        @raise AssertionError
        """
        assert(len(s) >= 2)
        tmp_len = len(s)

        i = 1
        while orb(s[i]) & 0x80 > 0:
            i += 1
            assert i < tmp_len, 'EINVAL: s: out-of-bound read: unfinished AbstractUVarIntField detected'  # noqa: E501
        ret = i + 1

        assert(ret >= 0)
        return ret

    def i2len(self, pkt, x):
        # type: (Optional[packet.Packet], int) -> int
        """
        @param packet.Packet|None pkt: unused.
        @param int x: the positive or null value whose binary size if requested.  # noqa: E501
        @raise AssertionError
        """
        assert(x >= 0)
        if x < self._max_value:
            return 1

        # x is expressed over multiple bytes
        x -= self._max_value
        i = 1
        if x == 0:
            i += 1
        while x > 0:
            x >>= 7
            i += 1

        ret = i
        assert(ret >= 0)
        return ret

    def getfield(self, pkt, s):
        # type: (Optional[packet.Packet], Union[str, Tuple[str, int]]) -> Tuple[str, int]  # noqa: E501
        """
        @param packet.Packet|None pkt: the packet instance containing this field; probably unused.  # noqa: E501
        @param str|(str, int) s: the input value to get this field value from. If size is 8, s is a string, else  # noqa: E501
        it is a tuple containing the value and an int indicating the number of bits already consumed in the first byte  # noqa: E501
        of the str. The number of remaining bits to consume in the first byte must be equal to "size".  # noqa: E501
        @return (str, int): the remaining bytes of s and the parsed value.
        @raise AssertionError
        """
        if isinstance(s, tuple):
            assert(len(s) == 2)
            temp = s  # type: Tuple[str, int]
            ts, ti = temp
            assert(ti >= 0)
            assert 8 - ti == self.size, 'EINVAL: s: not enough bits remaining in current byte to read the prefix'  # noqa: E501
            val = ts
        else:
            assert isinstance(s, bytes) and self.size == 8, 'EINVAL: s: tuple expected when prefix_len is not a full byte'  # noqa: E501
            val = s

        if self._detect_multi_byte(val[0]):
            tmp_len = self._detect_bytelen_from_str(val)
        else:
            tmp_len = 1

        ret = val[tmp_len:], self.m2i(pkt, s)
        assert(ret[1] >= 0)
        return ret

    def randval(self):
        # type: () -> volatile.VolatileValue
        """
        @return volatile.VolatileValue: a volatile value for this field "long"-compatible internal value.  # noqa: E501
        """
        return volatile.RandLong()


class UVarIntField(AbstractUVarIntField):
    def __init__(self, name, default, size):
        # type: (str, int, int) -> None
        """
        @param str name: the name of this field instance.
        @param default: the default value for this field instance. default must be positive or null.  # noqa: E501
        @raise AssertionError
        """
        assert(default >= 0)
        assert(0 < size <= 8)

        super(UVarIntField, self).__init__(name, default, size)
        self.size = size
        self._max_value = (1 << self.size) - 1

        # Configuring the fake property that is useless for this class but that is  # noqa: E501
        # expected from BitFields
        self.rev = False

    def h2i(self, pkt, x):
        # type: (Optional[packet.Packet], int) -> int
        """ h2i is overloaded to restrict the acceptable x values (not None)

        @param packet.Packet|None pkt: the packet instance containing this field instance; probably unused.  # noqa: E501
        @param int x: the value to convert.
        @return int: the converted value.
        @raise AssertionError
        """
        ret = super(UVarIntField, self).h2i(pkt, x)
        assert(not isinstance(ret, type(None)) and ret >= 0)
        return ret

    def i2h(self, pkt, x):
        # type: (Optional[packet.Packet], int) -> int
        """ i2h is overloaded to restrict the acceptable x values (not None)

        @param packet.Packet|None pkt: the packet instance containing this field instance; probably unused.  # noqa: E501
        @param int x: the value to convert.
        @return int: the converted value.
        @raise AssertionError
        """
        ret = super(UVarIntField, self).i2h(pkt, x)
        assert(not isinstance(ret, type(None)) and ret >= 0)
        return ret

    def any2i(self, pkt, x):
        # type: (Optional[packet.Packet], Union[str, int]) -> int
        """ any2i is overloaded to restrict the acceptable x values (not None)

        @param packet.Packet|None pkt: the packet instance containing this field instance; probably unused.  # noqa: E501
        @param str|int x: the value to convert.
        @return int: the converted value.
        @raise AssertionError
        """
        ret = super(UVarIntField, self).any2i(pkt, x)
        assert(not isinstance(ret, type(None)) and ret >= 0)
        return ret

    def i2repr(self, pkt, x):
        # type: (Optional[packet.Packet], int) -> str
        """ i2repr is overloaded to restrict the acceptable x values (not None)

        @param packet.Packet|None pkt: the packet instance containing this field instance; probably unused.  # noqa: E501
        @param int x: the value to convert.
        @return str: the converted value.
        """
        return super(UVarIntField, self).i2repr(pkt, x)


class FieldUVarLenField(AbstractUVarIntField):
    __slots__ = ['_length_of', '_adjust']

    def __init__(self, name, default, size, length_of, adjust=lambda x: x):
        # type: (str, Optional[int], int, str, Callable[[int], int]) -> None
        """ Initializes a FieldUVarLenField

        @param str name: The name of this field instance.
        @param int|None default: the default value of this field instance.
        @param int size: the number of bits that are occupied by this field in the first byte of a binary string.  # noqa: E501
          size must be in the range ]0;8].
        @param str length_of: The name of the field this field value is measuring/representing.  # noqa: E501
        @param callable adjust: A function that modifies the value computed from the "length_of" field.  # noqa: E501

        adjust can be used for instance to add a constant to the length_of field  # noqa: E501
         length. For instance, let's say that i2len of the length_of field
         returns 2. If adjust is lambda x: x+1 In that case, this field will
         value 3 at build time.
        @return None
        @raise AssertionError
        """
        assert(default is None or default >= 0)
        assert(0 < size <= 8)

        super(FieldUVarLenField, self).__init__(name, default, size)
        self._length_of = length_of
        self._adjust = adjust

    def addfield(self, pkt, s, val):
        # type: (Optional[packet.Packet], Union[str, Tuple[str, int, int]], Optional[int]) -> str  # noqa: E501
        """
        @param packet.Packet|None pkt: the packet instance containing this field instance. This parameter must not be  # noqa: E501
          None if the val parameter is.
        @param str|(str, int, long) s: the string to append this field to. A tuple indicates that some bits were already  # noqa: E501
          generated by another bitfield-compatible field. This MUST be the case if "size" is not 8. The int is the  # noqa: E501
          number of bits already generated in the first byte of the str. The long is the value that was generated by the  # noqa: E501
          previous bitfield-compatible fields.
        @param int|None val: the positive or null value to be added. If None, the value is computed from pkt.  # noqa: E501
        @return str: s concatenated with the machine representation of this field.  # noqa: E501
        @raise AssertionError
        """
        if val is None:
            assert isinstance(pkt, packet.Packet), \
                'EINVAL: pkt: Packet expected when val is None; received {}'.format(type(pkt))  # noqa: E501
            val = self._compute_value(pkt)
        return super(FieldUVarLenField, self).addfield(pkt, s, val)

    def i2m(self, pkt, x):
        # type: (Optional[packet.Packet], Optional[int]) -> str
        """
        @param packet.Packet|None pkt: the packet instance containing this field instance. This parameter must not be  # noqa: E501
          None if the x parameter is.
        @param int|None x: the positive or null value to be added. If None, the value is computed from pkt.  # noqa: E501
        @return str
        @raise AssertionError
        """
        if x is None:
            assert isinstance(pkt, packet.Packet), \
                'EINVAL: pkt: Packet expected when x is None; received {}'.format(type(pkt))  # noqa: E501
            x = self._compute_value(pkt)
        return super(FieldUVarLenField, self).i2m(pkt, x)

    def _compute_value(self, pkt):
        # type: (packet.Packet) -> int
        """ Computes the value of this field based on the provided packet and
        the length_of field and the adjust callback

        @param packet.Packet pkt: the packet from which is computed this field value.  # noqa: E501
        @return int: the computed value for this field.
        @raise KeyError: the packet nor its payload do not contain an attribute
          with the length_of name.
        @raise AssertionError
        @raise KeyError if _length_of is not one of pkt fields
        """
        fld, fval = pkt.getfield_and_val(self._length_of)
        val = fld.i2len(pkt, fval)
        ret = self._adjust(val)
        assert(ret >= 0)
        return ret

###############################################################################
#                                                HPACK String Fields          #
###############################################################################

# Welcome the magic of Python inconsistencies !
# https://stackoverflow.com/a/41622155


if sys.version_info >= (3, 4):
    ABC = abc.ABC
else:
    ABC = abc.ABCMeta('ABC', (), {})


class HPackStringsInterface(ABC, Sized):
    @abc.abstractmethod
    def __str__(self):
        pass

    def __bytes__(self):
        r = self.__str__()
        return bytes_encode(r)

    @abc.abstractmethod
    def origin(self):
        pass

    @abc.abstractmethod
    def __len__(self):
        pass


class HPackLiteralString(HPackStringsInterface):
    """ HPackLiteralString is a string. This class is used as a marker and
    implements an interface in common with HPackZString
    """
    __slots__ = ['_s']

    def __init__(self, s):
        # type: (str) -> None
        self._s = s

    def __str__(self):
        # type: () -> str
        return self._s

    def origin(self):
        # type: () -> str
        return plain_str(self._s)

    def __len__(self):
        # type: () -> int
        return len(self._s)


class EOS(object):
    """ Simple "marker" to designate the End Of String symbol in the huffman table
    """


class HuffmanNode(object):
    """ HuffmanNode is an entry of the binary tree used for encoding/decoding
    HPack compressed HTTP/2 headers
    """

    __slots__ = ['left', 'right']
    """@var l: the left branch of this node
    @var r: the right branch of this Node

    These variables can value None (leaf node), another HuffmanNode, or a
     symbol. Symbols are either a character or the End Of String symbol (class
     EOS)
    """

    def __init__(self, left, right):
        # type: (Union[None, HuffmanNode, EOS, str], Union[None, HuffmanNode, EOS, str]) -> None  # noqa: E501
        self.left = left
        self.right = right

    def __getitem__(self, b):
        # type: (int) -> Union[None, HuffmanNode, EOS, str]
        return self.right if b else self.left

    def __setitem__(self, b, val):
        # type: (int, Union[None, HuffmanNode, EOS, str]) -> None
        if b:
            self.right = val
        else:
            self.left = val

    def __str__(self):
        # type: () -> str
        return self.__repr__()

    def __repr__(self):
        # type: () -> str
        return '({}, {})'.format(self.left, self.right)


class InvalidEncodingException(Exception):
    """ InvalidEncodingException is raised when a supposedly huffman-encoded
     string is decoded and a decoding error arises
    """


class HPackZString(HPackStringsInterface):
    __slots__ = ['_s', '_encoded']

    # From RFC 7541
    # Tuple is (code,code bitlength)
    # The bitlength is required to know how long the left padding
    # (implicit 0's) there are
    static_huffman_code = [
        (0x1ff8, 13),
        (0x7fffd8, 23),
        (0xfffffe2, 28),
        (0xfffffe3, 28),
        (0xfffffe4, 28),
        (0xfffffe5, 28),
        (0xfffffe6, 28),
        (0xfffffe7, 28),
        (0xfffffe8, 28),
        (0xffffea, 24),
        (0x3ffffffc, 30),
        (0xfffffe9, 28),
        (0xfffffea, 28),
        (0x3ffffffd, 30),
        (0xfffffeb, 28),
        (0xfffffec, 28),
        (0xfffffed, 28),
        (0xfffffee, 28),
        (0xfffffef, 28),
        (0xffffff0, 28),
        (0xffffff1, 28),
        (0xffffff2, 28),
        (0x3ffffffe, 30),
        (0xffffff3, 28),
        (0xffffff4, 28),
        (0xffffff5, 28),
        (0xffffff6, 28),
        (0xffffff7, 28),
        (0xffffff8, 28),
        (0xffffff9, 28),
        (0xffffffa, 28),
        (0xffffffb, 28),
        (0x14, 6),
        (0x3f8, 10),
        (0x3f9, 10),
        (0xffa, 12),
        (0x1ff9, 13),
        (0x15, 6),
        (0xf8, 8),
        (0x7fa, 11),
        (0x3fa, 10),
        (0x3fb, 10),
        (0xf9, 8),
        (0x7fb, 11),
        (0xfa, 8),
        (0x16, 6),
        (0x17, 6),
        (0x18, 6),
        (0x0, 5),
        (0x1, 5),
        (0x2, 5),
        (0x19, 6),
        (0x1a, 6),
        (0x1b, 6),
        (0x1c, 6),
        (0x1d, 6),
        (0x1e, 6),
        (0x1f, 6),
        (0x5c, 7),
        (0xfb, 8),
        (0x7ffc, 15),
        (0x20, 6),
        (0xffb, 12),
        (0x3fc, 10),
        (0x1ffa, 13),
        (0x21, 6),
        (0x5d, 7),
        (0x5e, 7),
        (0x5f, 7),
        (0x60, 7),
        (0x61, 7),
        (0x62, 7),
        (0x63, 7),
        (0x64, 7),
        (0x65, 7),
        (0x66, 7),
        (0x67, 7),
        (0x68, 7),
        (0x69, 7),
        (0x6a, 7),
        (0x6b, 7),
        (0x6c, 7),
        (0x6d, 7),
        (0x6e, 7),
        (0x6f, 7),
        (0x70, 7),
        (0x71, 7),
        (0x72, 7),
        (0xfc, 8),
        (0x73, 7),
        (0xfd, 8),
        (0x1ffb, 13),
        (0x7fff0, 19),
        (0x1ffc, 13),
        (0x3ffc, 14),
        (0x22, 6),
        (0x7ffd, 15),
        (0x3, 5),
        (0x23, 6),
        (0x4, 5),
        (0x24, 6),
        (0x5, 5),
        (0x25, 6),
        (0x26, 6),
        (0x27, 6),
        (0x6, 5),
        (0x74, 7),
        (0x75, 7),
        (0x28, 6),
        (0x29, 6),
        (0x2a, 6),
        (0x7, 5),
        (0x2b, 6),
        (0x76, 7),
        (0x2c, 6),
        (0x8, 5),
        (0x9, 5),
        (0x2d, 6),
        (0x77, 7),
        (0x78, 7),
        (0x79, 7),
        (0x7a, 7),
        (0x7b, 7),
        (0x7ffe, 15),
        (0x7fc, 11),
        (0x3ffd, 14),
        (0x1ffd, 13),
        (0xffffffc, 28),
        (0xfffe6, 20),
        (0x3fffd2, 22),
        (0xfffe7, 20),
        (0xfffe8, 20),
        (0x3fffd3, 22),
        (0x3fffd4, 22),
        (0x3fffd5, 22),
        (0x7fffd9, 23),
        (0x3fffd6, 22),
        (0x7fffda, 23),
        (0x7fffdb, 23),
        (0x7fffdc, 23),
        (0x7fffdd, 23),
        (0x7fffde, 23),
        (0xffffeb, 24),
        (0x7fffdf, 23),
        (0xffffec, 24),
        (0xffffed, 24),
        (0x3fffd7, 22),
        (0x7fffe0, 23),
        (0xffffee, 24),
        (0x7fffe1, 23),
        (0x7fffe2, 23),
        (0x7fffe3, 23),
        (0x7fffe4, 23),
        (0x1fffdc, 21),
        (0x3fffd8, 22),
        (0x7fffe5, 23),
        (0x3fffd9, 22),
        (0x7fffe6, 23),
        (0x7fffe7, 23),
        (0xffffef, 24),
        (0x3fffda, 22),
        (0x1fffdd, 21),
        (0xfffe9, 20),
        (0x3fffdb, 22),
        (0x3fffdc, 22),
        (0x7fffe8, 23),
        (0x7fffe9, 23),
        (0x1fffde, 21),
        (0x7fffea, 23),
        (0x3fffdd, 22),
        (0x3fffde, 22),
        (0xfffff0, 24),
        (0x1fffdf, 21),
        (0x3fffdf, 22),
        (0x7fffeb, 23),
        (0x7fffec, 23),
        (0x1fffe0, 21),
        (0x1fffe1, 21),
        (0x3fffe0, 22),
        (0x1fffe2, 21),
        (0x7fffed, 23),
        (0x3fffe1, 22),
        (0x7fffee, 23),
        (0x7fffef, 23),
        (0xfffea, 20),
        (0x3fffe2, 22),
        (0x3fffe3, 22),
        (0x3fffe4, 22),
        (0x7ffff0, 23),
        (0x3fffe5, 22),
        (0x3fffe6, 22),
        (0x7ffff1, 23),
        (0x3ffffe0, 26),
        (0x3ffffe1, 26),
        (0xfffeb, 20),
        (0x7fff1, 19),
        (0x3fffe7, 22),
        (0x7ffff2, 23),
        (0x3fffe8, 22),
        (0x1ffffec, 25),
        (0x3ffffe2, 26),
        (0x3ffffe3, 26),
        (0x3ffffe4, 26),
        (0x7ffffde, 27),
        (0x7ffffdf, 27),
        (0x3ffffe5, 26),
        (0xfffff1, 24),
        (0x1ffffed, 25),
        (0x7fff2, 19),
        (0x1fffe3, 21),
        (0x3ffffe6, 26),
        (0x7ffffe0, 27),
        (0x7ffffe1, 27),
        (0x3ffffe7, 26),
        (0x7ffffe2, 27),
        (0xfffff2, 24),
        (0x1fffe4, 21),
        (0x1fffe5, 21),
        (0x3ffffe8, 26),
        (0x3ffffe9, 26),
        (0xffffffd, 28),
        (0x7ffffe3, 27),
        (0x7ffffe4, 27),
        (0x7ffffe5, 27),
        (0xfffec, 20),
        (0xfffff3, 24),
        (0xfffed, 20),
        (0x1fffe6, 21),
        (0x3fffe9, 22),
        (0x1fffe7, 21),
        (0x1fffe8, 21),
        (0x7ffff3, 23),
        (0x3fffea, 22),
        (0x3fffeb, 22),
        (0x1ffffee, 25),
        (0x1ffffef, 25),
        (0xfffff4, 24),
        (0xfffff5, 24),
        (0x3ffffea, 26),
        (0x7ffff4, 23),
        (0x3ffffeb, 26),
        (0x7ffffe6, 27),
        (0x3ffffec, 26),
        (0x3ffffed, 26),
        (0x7ffffe7, 27),
        (0x7ffffe8, 27),
        (0x7ffffe9, 27),
        (0x7ffffea, 27),
        (0x7ffffeb, 27),
        (0xffffffe, 28),
        (0x7ffffec, 27),
        (0x7ffffed, 27),
        (0x7ffffee, 27),
        (0x7ffffef, 27),
        (0x7fffff0, 27),
        (0x3ffffee, 26),
        (0x3fffffff, 30)
    ]

    static_huffman_tree = None

    @classmethod
    def _huffman_encode_char(cls, c):
        # type: (Union[str, EOS]) -> Tuple[int, int]
        """ huffman_encode_char assumes that the static_huffman_tree was
        previously initialized

        @param str|EOS c: a symbol to encode
        @return (int, int): the bitstring of the symbol and its bitlength
        @raise AssertionError
        """
        if isinstance(c, EOS):
            return cls.static_huffman_code[-1]
        else:
            assert(isinstance(c, int) or len(c) == 1)
        return cls.static_huffman_code[orb(c)]

    @classmethod
    def huffman_encode(cls, s):
        # type: (str) -> Tuple[int, int]
        """ huffman_encode returns the bitstring and the bitlength of the
        bitstring representing the string provided as a parameter

        @param str s: the string to encode
        @return (int, int): the bitstring of s and its bitlength
        @raise AssertionError
        """
        i = 0
        ibl = 0
        for c in s:
            val, bl = cls._huffman_encode_char(c)
            i = (i << bl) + val
            ibl += bl

        padlen = 8 - (ibl % 8)
        if padlen != 8:
            val, bl = cls._huffman_encode_char(EOS())
            i = (i << padlen) + (val >> (bl - padlen))
            ibl += padlen

        ret = i, ibl
        assert(ret[0] >= 0)
        assert (ret[1] >= 0)
        return ret

    @classmethod
    def huffman_decode(cls, i, ibl):
        # type: (int, int) -> str
        """ huffman_decode decodes the bitstring provided as parameters.

        @param int i: the bitstring to decode
        @param int ibl: the bitlength of i
        @return str: the string decoded from the bitstring
        @raise AssertionError, InvalidEncodingException
        """
        assert(i >= 0)
        assert(ibl >= 0)

        if isinstance(cls.static_huffman_tree, type(None)):
            cls.huffman_compute_decode_tree()
        assert(not isinstance(cls.static_huffman_tree, type(None)))

        s = []
        j = 0
        interrupted = False
        cur = cls.static_huffman_tree
        cur_sym = 0
        cur_sym_bl = 0
        while j < ibl:
            b = (i >> (ibl - j - 1)) & 1
            cur_sym = (cur_sym << 1) + b
            cur_sym_bl += 1
            elmt = cur[b]

            if isinstance(elmt, HuffmanNode):
                interrupted = True
                cur = elmt
                if isinstance(cur, type(None)):
                    raise AssertionError()
            elif isinstance(elmt, EOS):
                raise InvalidEncodingException('Huffman decoder met the full EOS symbol')  # noqa: E501
            elif isinstance(elmt, bytes):
                interrupted = False
                s.append(elmt)
                cur = cls.static_huffman_tree
                cur_sym = 0
                cur_sym_bl = 0
            else:
                raise InvalidEncodingException('Should never happen, so incidentally it will')  # noqa: E501
            j += 1

        if interrupted:
            # Interrupted values true if the bitstring ends in the middle of a
            # symbol; this symbol must be, according to RFC7541 par5.2 the MSB
            # of the EOS symbol
            if cur_sym_bl > 7:
                raise InvalidEncodingException('Huffman decoder is detecting padding longer than 7 bits')  # noqa: E501
            eos_symbol = cls.static_huffman_code[-1]
            eos_msb = eos_symbol[0] >> (eos_symbol[1] - cur_sym_bl)
            if eos_msb != cur_sym:
                raise InvalidEncodingException('Huffman decoder is detecting unexpected padding format')  # noqa: E501
        return b''.join(s)

    @classmethod
    def huffman_conv2str(cls, bit_str, bit_len):
        # type: (int, int) -> str
        """ huffman_conv2str converts a bitstring of bit_len bitlength into a
        binary string. It DOES NOT compress/decompress the bitstring!

        @param int bit_str: the bitstring to convert.
        @param int bit_len: the bitlength of bit_str.
        @return str: the converted bitstring as a bytestring.
        @raise AssertionError
        """
        assert(bit_str >= 0)
        assert(bit_len >= 0)

        byte_len = bit_len // 8
        rem_bit = bit_len % 8
        if rem_bit != 0:
            bit_str <<= 8 - rem_bit
            byte_len += 1

        # As usual the list/join tricks is a performance trick to build
        # efficiently a Python string
        s = []  # type: List[str]
        i = 0
        while i < byte_len:
            s.insert(0, chb((bit_str >> (i * 8)) & 0xFF))
            i += 1
        return b''.join(s)

    @classmethod
    def huffman_conv2bitstring(cls, s):
        # type: (str) -> Tuple[int, int]
        """ huffman_conv2bitstring converts a string into its bitstring
        representation. It returns a tuple: the bitstring and its bitlength.
        This function DOES NOT compress/decompress the string!

        @param str s: the bytestring to convert.
        @return (int, int): the bitstring of s, and its bitlength.
        @raise AssertionError
        """
        i = 0
        ibl = len(s) * 8
        for c in s:
            i = (i << 8) + orb(c)

        ret = i, ibl
        assert(ret[0] >= 0)
        assert(ret[1] >= 0)
        return ret

    @classmethod
    def huffman_compute_decode_tree(cls):
        # type: () -> None
        """ huffman_compute_decode_tree initializes/builds the static_huffman_tree

        @return None
        @raise InvalidEncodingException if there is an encoding problem
        """
        cls.static_huffman_tree = HuffmanNode(None, None)
        i = 0
        for entry in cls.static_huffman_code:
            parent = cls.static_huffman_tree
            for idx in range(entry[1] - 1, -1, -1):
                b = (entry[0] >> idx) & 1
                if isinstance(parent[b], bytes):
                    raise InvalidEncodingException('Huffman unique prefix violation :/')  # noqa: E501
                if idx == 0:
                    parent[b] = chb(i) if i < 256 else EOS()
                elif parent[b] is None:
                    parent[b] = HuffmanNode(None, None)
                parent = parent[b]
            i += 1

    def __init__(self, s):
        # type: (str) -> None
        self._s = s
        i, ibl = type(self).huffman_encode(s)
        self._encoded = type(self).huffman_conv2str(i, ibl)

    def __str__(self):
        # type: () -> str
        return self._encoded

    def origin(self):
        # type: () -> str
        return plain_str(self._s)

    def __len__(self):
        # type: () -> int
        return len(self._encoded)


class HPackStrLenField(fields.Field):
    """ HPackStrLenField is a StrLenField variant specialized for HTTP/2 HPack

    This variant uses an internal representation that implements HPackStringsInterface.  # noqa: E501
    """
    __slots__ = ['_length_from', '_type_from']

    def __init__(self, name, default, length_from, type_from):
        # type: (str, HPackStringsInterface, Callable[[packet.Packet], int], str) -> None  # noqa: E501
        super(HPackStrLenField, self).__init__(name, default)
        self._length_from = length_from
        self._type_from = type_from

    def addfield(self, pkt, s, val):
        # type: (Optional[packet.Packet], str, HPackStringsInterface) -> str
        return s + self.i2m(pkt, val)

    @staticmethod
    def _parse(t, s):
        # type: (bool, str) -> HPackStringsInterface
        """
        @param bool t: whether this string is a huffman compressed string.
        @param str s: the string to parse.
        @return HPackStringsInterface: either a HPackLiteralString or HPackZString, depending on t.  # noqa: E501
        @raise InvalidEncodingException
        """
        if t:
            i, ibl = HPackZString.huffman_conv2bitstring(s)
            return HPackZString(HPackZString.huffman_decode(i, ibl))
        return HPackLiteralString(s)

    def getfield(self, pkt, s):
        # type: (packet.Packet, str) -> Tuple[str, HPackStringsInterface]
        """
        @param packet.Packet pkt: the packet instance containing this field instance.  # noqa: E501
        @param str s: the string to parse this field from.
        @return (str, HPackStringsInterface): the remaining string after this field was carved out & the extracted  # noqa: E501
          value.
        @raise KeyError if "type_from" is not a field of pkt or its payloads.
        @raise InvalidEncodingException
        """
        tmp_len = self._length_from(pkt)
        t = pkt.getfieldval(self._type_from) == 1
        return s[tmp_len:], self._parse(t, s[:tmp_len])

    def i2h(self, pkt, x):
        # type: (Optional[packet.Packet], HPackStringsInterface) -> str
        fmt = ''
        if isinstance(x, HPackLiteralString):
            fmt = "HPackLiteralString({})"
        elif isinstance(x, HPackZString):
            fmt = "HPackZString({})"
        return fmt.format(x.origin())

    def h2i(self, pkt, x):
        # type: (packet.Packet, str) -> HPackStringsInterface
        return HPackLiteralString(x)

    def m2i(self, pkt, x):
        # type: (packet.Packet, str) -> HPackStringsInterface
        """
        @param packet.Packet pkt: the packet instance containing this field instance.  # noqa: E501
        @param str x: the string to parse.
        @return HPackStringsInterface: the internal type of the value parsed from x.  # noqa: E501
        @raise AssertionError
        @raise InvalidEncodingException
        @raise KeyError if _type_from is not one of pkt fields.
        """
        t = pkt.getfieldval(self._type_from)
        tmp_len = self._length_from(pkt)

        assert t is not None and tmp_len is not None, 'Conversion from string impossible: no type or length specified'  # noqa: E501

        return self._parse(t == 1, x[:tmp_len])

    def any2i(self, pkt, x):
        # type: (Optional[packet.Packet], Union[str, HPackStringsInterface]) -> HPackStringsInterface  # noqa: E501
        """
        @param packet.Packet|None pkt: the packet instance containing this field instance.  # noqa: E501
        @param str|HPackStringsInterface x: the value to convert
        @return HPackStringsInterface: the Scapy internal value for this field
        @raise AssertionError, InvalidEncodingException
        """
        if isinstance(x, bytes):
            assert(isinstance(pkt, packet.Packet))
            return self.m2i(pkt, x)
        assert(isinstance(x, HPackStringsInterface))
        return x

    def i2m(self, pkt, x):
        # type: (Optional[packet.Packet], HPackStringsInterface) -> str
        return raw(x)

    def i2len(self, pkt, x):
        # type: (Optional[packet.Packet], HPackStringsInterface) -> int
        return len(x)

    def i2repr(self, pkt, x):
        # type: (Optional[packet.Packet], HPackStringsInterface) -> str
        return repr(self.i2h(pkt, x))

###############################################################################
#                                                HPACK Packets                #
###############################################################################


class HPackHdrString(packet.Packet):
    """ HPackHdrString is a packet that that is serialized into a RFC7541 par5.2
    string literal repr.
    """
    name = 'HPack Header String'
    fields_desc = [
        fields.BitEnumField('type', None, 1, {0: 'Literal', 1: 'Compressed'}),
        FieldUVarLenField('len', None, 7, length_of='data'),
        HPackStrLenField(
            'data', HPackLiteralString(''),
            length_from=lambda pkt: pkt.getfieldval('len'),
            type_from='type'
        )
    ]

    def guess_payload_class(self, payload):
        # type: (str) -> base_classes.Packet_metaclass
        # Trick to tell scapy that the remaining bytes of the currently
        # dissected string is not a payload of this packet but of some other
        # underlayer packet
        return config.conf.padding_layer

    def self_build(self, field_pos_list=None):
        # type: (Any) -> str
        """self_build is overridden because type and len are determined at
        build time, based on the "data" field internal type
        """
        if self.getfieldval('type') is None:
            self.type = 1 if isinstance(self.getfieldval('data'), HPackZString) else 0  # noqa: E501
        return super(HPackHdrString, self).self_build(field_pos_list)


class HPackHeaders(packet.Packet):
    """HPackHeaders uses the "dispatch_hook" trick of Packet_metaclass to select
    the correct HPack header packet type. For this, the first byte of the string  # noqa: E501
    to dissect is snooped on.
    """
    @classmethod
    def dispatch_hook(cls, s=None, *_args, **_kwds):
        # type: (Optional[str], *Any, **Any) -> base_classes.Packet_metaclass
        """dispatch_hook returns the subclass of HPackHeaders that must be used
        to dissect the string.
        """
        if s is None:
            return config.conf.raw_layer
        fb = orb(s[0])
        if fb & 0x80 != 0:
            return HPackIndexedHdr
        if fb & 0x40 != 0:
            return HPackLitHdrFldWithIncrIndexing
        if fb & 0x20 != 0:
            return HPackDynamicSizeUpdate
        return HPackLitHdrFldWithoutIndexing

    def guess_payload_class(self, payload):
        # type: (str) -> base_classes.Packet_metaclass
        return config.conf.padding_layer


class HPackIndexedHdr(HPackHeaders):
    """ HPackIndexedHdr implements RFC 7541 par6.1
    """
    name = 'HPack Indexed Header Field'
    fields_desc = [
        HPackMagicBitField('magic', 1, 1),
        UVarIntField('index', 2, 7)  # Default "2" is ":method GET"
    ]


class HPackLitHdrFldWithIncrIndexing(HPackHeaders):
    """ HPackLitHdrFldWithIncrIndexing implements RFC 7541 par6.2.1
    """
    name = 'HPack Literal Header With Incremental Indexing'
    fields_desc = [
        HPackMagicBitField('magic', 1, 2),
        UVarIntField('index', 0, 6),  # Default is New Name
        fields.ConditionalField(
            fields.PacketField('hdr_name', None, HPackHdrString),
            lambda pkt: pkt.getfieldval('index') == 0
        ),
        fields.PacketField('hdr_value', None, HPackHdrString)
    ]


class HPackLitHdrFldWithoutIndexing(HPackHeaders):
    """ HPackLitHdrFldWithIncrIndexing implements RFC 7541 par6.2.2
    and par6.2.3
    """
    name = 'HPack Literal Header Without Indexing (or Never Indexing)'
    fields_desc = [
        HPackMagicBitField('magic', 0, 3),
        fields.BitEnumField(
            'never_index', 0, 1,
            {0: "Don't Index", 1: 'Never Index'}
        ),
        UVarIntField('index', 0, 4),  # Default is New Name
        fields.ConditionalField(
            fields.PacketField('hdr_name', None, HPackHdrString),
            lambda pkt: pkt.getfieldval('index') == 0
        ),
        fields.PacketField('hdr_value', None, HPackHdrString)
    ]


class HPackDynamicSizeUpdate(HPackHeaders):
    """ HPackDynamicSizeUpdate implements RFC 7541 par6.3
    """
    name = 'HPack Dynamic Size Update'
    fields_desc = [
        HPackMagicBitField('magic', 1, 3),
        UVarIntField('max_size', 0, 5)
    ]

###############################################################################
#                                             HTTP/2 Frames                   #
###############################################################################


class H2FramePayload(packet.Packet):
    """ H2FramePayload is an empty class that is a super class of all Scapy
    HTTP/2 Frame Packets
    """

#                                             HTTP/2 Data Frame Packets                                                #  # noqa: E501


class H2DataFrame(H2FramePayload):
    """ H2DataFrame implements RFC7540 par6.1
    This packet is the Data Frame to use when there is no padding.
    """
    type_id = 0
    END_STREAM_FLAG = 0  # 0x1
    PADDED_FLAG = 3  # 0x8
    flags = {
        END_STREAM_FLAG: fields.MultiFlagsEntry('ES', 'End Stream'),
        PADDED_FLAG: fields.MultiFlagsEntry('P', 'Padded')
    }

    name = 'HTTP/2 Data Frame'
    fields_desc = [
        fields.StrField('data', '')
    ]


class H2PaddedDataFrame(H2DataFrame):
    """ H2DataFrame implements RFC7540 par6.1
    This packet is the Data Frame to use when there is padding.
    """
    __slots__ = ['s_len']

    name = 'HTTP/2 Padded Data Frame'
    fields_desc = [
        fields.FieldLenField('padlen', None, length_of='padding', fmt="B"),
        fields.StrLenField('data', '',
                           length_from=lambda pkt: pkt.get_data_len()
                           ),
        fields.StrLenField('padding', '',
                           length_from=lambda pkt: pkt.getfieldval('padlen')
                           )
    ]

    def get_data_len(self):
        # type: () -> int
        """ get_data_len computes the length of the data field

        To do this computation, the length of the padlen field and the actual
        padding is subtracted to the string that was provided to the pre_dissect  # noqa: E501
        fun of the pkt parameter
        @return int; length of the data part of the HTTP/2 frame packet provided as parameter  # noqa: E501
        @raise AssertionError
        """
        padding_len = self.getfieldval('padlen')
        fld, fval = self.getfield_and_val('padlen')
        padding_len_len = fld.i2len(self, fval)

        ret = self.s_len - padding_len_len - padding_len
        assert(ret >= 0)
        return ret

    def pre_dissect(self, s):
        # type: (str) -> str
        """pre_dissect is filling the s_len property of this instance. This
        property is later used during the getfield call of the "data" field when  # noqa: E501
        trying to evaluate the length of the StrLenField! This "trick" works
        because the underlayer packet (H2Frame) is assumed to override the
        "extract_padding" method and to only provide to this packet the data
        necessary for this packet. Tricky, tricky, will break some day probably!  # noqa: E501
        """
        self.s_len = len(s)
        return s


#                                             HTTP/2 Header Frame Packets                                              #  # noqa: E501

class H2AbstractHeadersFrame(H2FramePayload):
    """Superclass of all variants of HTTP/2 Header Frame Packets.
    May be used for type checking.
    """


class H2HeadersFrame(H2AbstractHeadersFrame):
    """ H2HeadersFrame implements RFC 7540 par6.2 Headers Frame
    when there is no padding and no priority information

    The choice of decomposing into four classes is probably preferable to having  # noqa: E501
    numerous conditional fields based on the underlayer :/
    """
    type_id = 1
    END_STREAM_FLAG = 0  # 0x1
    END_HEADERS_FLAG = 2  # 0x4
    PADDED_FLAG = 3  # 0x8
    PRIORITY_FLAG = 5  # 0x20
    flags = {
        END_STREAM_FLAG: fields.MultiFlagsEntry('ES', 'End Stream'),
        END_HEADERS_FLAG: fields.MultiFlagsEntry('EH', 'End Headers'),
        PADDED_FLAG: fields.MultiFlagsEntry('P', 'Padded'),
        PRIORITY_FLAG: fields.MultiFlagsEntry('+', 'Priority')
    }

    name = 'HTTP/2 Headers Frame'
    fields_desc = [
        fields.PacketListField('hdrs', [], HPackHeaders)
    ]


class H2PaddedHeadersFrame(H2AbstractHeadersFrame):
    """ H2PaddedHeadersFrame is the variant of H2HeadersFrame where padding flag
    is set and priority flag is cleared
    """
    __slots__ = ['s_len']

    name = 'HTTP/2 Headers Frame with Padding'
    fields_desc = [
        fields.FieldLenField('padlen', None, length_of='padding', fmt='B'),
        fields.PacketListField('hdrs', [], HPackHeaders,
                               length_from=lambda pkt: pkt.get_hdrs_len()
                               ),
        fields.StrLenField('padding', '',
                           length_from=lambda pkt: pkt.getfieldval('padlen')
                           )
    ]

    def get_hdrs_len(self):
        # type: () -> int
        """ get_hdrs_len computes the length of the hdrs field

        To do this computation, the length of the padlen field and the actual
        padding is subtracted to the string that was provided to the pre_dissect  # noqa: E501
        fun of the pkt parameter.
        @return int; length of the data part of the HTTP/2 frame packet provided as parameter  # noqa: E501
        @raise AssertionError
        """
        padding_len = self.getfieldval('padlen')
        fld, fval = self.getfield_and_val('padlen')
        padding_len_len = fld.i2len(self, fval)

        ret = self.s_len - padding_len_len - padding_len
        assert(ret >= 0)
        return ret

    def pre_dissect(self, s):
        # type: (str) -> str
        """pre_dissect is filling the s_len property of this instance. This
        property is later used during the parsing of the hdrs PacketListField
        when trying to evaluate the length of the PacketListField! This "trick"
        works because the underlayer packet (H2Frame) is assumed to override the  # noqa: E501
        "extract_padding" method and to only provide to this packet the data
        necessary for this packet. Tricky, tricky, will break some day probably!  # noqa: E501
        """
        self.s_len = len(s)
        return s


class H2PriorityHeadersFrame(H2AbstractHeadersFrame):
    """ H2PriorityHeadersFrame is the variant of H2HeadersFrame where priority flag
    is set and padding flag is cleared
    """
    __slots__ = ['s_len']

    name = 'HTTP/2 Headers Frame with Priority'
    fields_desc = [
        fields.BitField('exclusive', 0, 1),
        fields.BitField('stream_dependency', 0, 31),
        fields.ByteField('weight', 0),
        # This PacketListField will consume all remaining bytes; not a problem
        # because the underlayer (H2Frame) overrides "extract_padding" so that
        # this Packet only get to parser what it needs to
        fields.PacketListField('hdrs', [], HPackHeaders),
    ]


class H2PaddedPriorityHeadersFrame(H2AbstractHeadersFrame):
    """ H2PaddedPriorityHeadersFrame is the variant of H2HeadersFrame where
    both priority and padding flags are set
    """
    __slots__ = ['s_len']

    name = 'HTTP/2 Headers Frame with Padding and Priority'
    fields_desc = [
        fields.FieldLenField('padlen', None, length_of='padding', fmt='B'),
        fields.BitField('exclusive', 0, 1),
        fields.BitField('stream_dependency', 0, 31),
        fields.ByteField('weight', 0),
        fields.PacketListField('hdrs', [], HPackHeaders,
                               length_from=lambda pkt: pkt.get_hdrs_len()
                               ),
        fields.StrLenField('padding', '',
                           length_from=lambda pkt: pkt.getfieldval('padlen')
                           )
    ]

    def get_hdrs_len(self):
        # type: () -> int
        """ get_hdrs_len computes the length of the hdrs field

        To do this computation, the length of the padlen field, the priority
        information fields and the actual padding is subtracted to the string
        that was provided to the pre_dissect fun of the pkt parameter.
        @return int: the length of the hdrs field
        @raise AssertionError
        """

        padding_len = self.getfieldval('padlen')
        fld, fval = self.getfield_and_val('padlen')
        padding_len_len = fld.i2len(self, fval)
        bit_cnt = self.get_field('exclusive').size
        bit_cnt += self.get_field('stream_dependency').size
        fld, fval = self.getfield_and_val('weight')
        weight_len = fld.i2len(self, fval)
        ret = int(self.s_len -
                  padding_len_len -
                  padding_len -
                  (bit_cnt / 8) -
                  weight_len
                  )
        assert(ret >= 0)
        return ret

    def pre_dissect(self, s):
        # type: (str) -> str
        """pre_dissect is filling the s_len property of this instance. This
        property is later used during the parsing of the hdrs PacketListField
        when trying to evaluate the length of the PacketListField! This "trick"
        works because the underlayer packet (H2Frame) is assumed to override the  # noqa: E501
        "extract_padding" method and to only provide to this packet the data
        necessary for this packet. Tricky, tricky, will break some day probably!  # noqa: E501
        """
        self.s_len = len(s)
        return s

#                                           HTTP/2 Priority Frame Packets                                              #  # noqa: E501


class H2PriorityFrame(H2FramePayload):
    """ H2PriorityFrame implements RFC 7540 par6.3
    """
    type_id = 2
    name = 'HTTP/2 Priority Frame'
    fields_desc = [
        fields.BitField('exclusive', 0, 1),
        fields.BitField('stream_dependency', 0, 31),
        fields.ByteField('weight', 0)
    ]

#                                                 HTTP/2 Errors                                                        #  # noqa: E501


class H2ErrorCodes(object):
    """ H2ErrorCodes is an enumeration of the error codes defined in
    RFC7540 par7.
    This enumeration is not part of any frame because the error codes are in
    common with H2ResetFrame and H2GoAwayFrame.
    """

    NO_ERROR = 0x0
    PROTOCOL_ERROR = 0x1
    INTERNAL_ERROR = 0x2
    FLOW_CONTROL_ERROR = 0x3
    SETTINGS_TIMEOUT = 0x4
    STREAM_CLOSED = 0x5
    FRAME_SIZE_ERROR = 0x6
    REFUSED_STREAM = 0x7
    CANCEL = 0x8
    COMPRESSION_ERROR = 0x9
    CONNECT_ERROR = 0xa
    ENHANCE_YOUR_CALM = 0xb
    INADEQUATE_SECURITY = 0xc
    HTTP_1_1_REQUIRED = 0xd

    literal = {
        NO_ERROR: 'No error',
        PROTOCOL_ERROR: 'Protocol error',
        INTERNAL_ERROR: 'Internal error',
        FLOW_CONTROL_ERROR: 'Flow control error',
        SETTINGS_TIMEOUT: 'Settings timeout',
        STREAM_CLOSED: 'Stream closed',
        FRAME_SIZE_ERROR: 'Frame size error',
        REFUSED_STREAM: 'Refused stream',
        CANCEL: 'Cancel',
        COMPRESSION_ERROR: 'Compression error',
        CONNECT_ERROR: 'Control error',
        ENHANCE_YOUR_CALM: 'Enhance your calm',
        INADEQUATE_SECURITY: 'Inadequate security',
        HTTP_1_1_REQUIRED: 'HTTP/1.1 required'
    }


#                                           HTTP/2 Reset Frame Packets                                                 #  # noqa: E501

class H2ResetFrame(H2FramePayload):
    """ H2ResetFrame implements RFC 7540 par6.4
    """
    type_id = 3
    name = 'HTTP/2 Reset Frame'
    fields_desc = [
        fields.EnumField('error', 0, H2ErrorCodes.literal, fmt='!I')
    ]


#                                           HTTP/2 Settings Frame Packets                                              #  # noqa: E501

class H2Setting(packet.Packet):
    """ H2Setting implements a setting, as defined in RFC7540 par6.5.1
    """
    SETTINGS_HEADER_TABLE_SIZE = 0x1
    SETTINGS_ENABLE_PUSH = 0x2
    SETTINGS_MAX_CONCURRENT_STREAMS = 0x3
    SETTINGS_INITIAL_WINDOW_SIZE = 0x4
    SETTINGS_MAX_FRAME_SIZE = 0x5
    SETTINGS_MAX_HEADER_LIST_SIZE = 0x6

    name = 'HTTP/2 Setting'
    fields_desc = [
        fields.EnumField('id', 0, {
            SETTINGS_HEADER_TABLE_SIZE: 'Header table size',
            SETTINGS_ENABLE_PUSH: 'Enable push',
            SETTINGS_MAX_CONCURRENT_STREAMS: 'Max concurrent streams',
            SETTINGS_INITIAL_WINDOW_SIZE: 'Initial window size',
            SETTINGS_MAX_FRAME_SIZE: 'Max frame size',
            SETTINGS_MAX_HEADER_LIST_SIZE: 'Max header list size'
        }, fmt='!H'),
        fields.IntField('value', 0)
    ]

    def guess_payload_class(self, payload):
        # type: (str) -> base_classes.Packet_metaclass
        return config.conf.padding_layer


class H2SettingsFrame(H2FramePayload):
    """ H2SettingsFrame implements RFC7540 par6.5
    """
    type_id = 4
    ACK_FLAG = 0  # 0x1
    flags = {
        ACK_FLAG: fields.MultiFlagsEntry('A', 'ACK')
    }

    name = 'HTTP/2 Settings Frame'
    fields_desc = [
        fields.PacketListField('settings', [], H2Setting)
    ]

    def __init__(self, *args, **kwargs):
        """__init__ initializes this H2SettingsFrame

        If a _pkt arg is provided (by keyword), then this is an initialization
        from a string to dissect and therefore the length of the string to
        dissect have distinctive characteristics that we might want to check.
        This is possible because the underlayer packet (H2Frame) overrides
        extract_padding method to provided only the string that must be parsed
        by this packet!
        @raise AssertionError
        """

        # RFC7540 par6.5 p36
        assert(
            len(args) == 0 or (
                isinstance(args[0], bytes) and
                len(args[0]) % 6 == 0
            )
        ), 'Invalid settings frame; length is not a multiple of 6'
        super(H2SettingsFrame, self).__init__(*args, **kwargs)

#                                        HTTP/2 Push Promise Frame Packets                                             #  # noqa: E501


class H2PushPromiseFrame(H2FramePayload):
    """ H2PushPromiseFrame implements RFC7540 par6.6. This packet
    is the variant to use when the underlayer padding flag is cleared
    """
    type_id = 5
    END_HEADERS_FLAG = 2  # 0x4
    PADDED_FLAG = 3  # 0x8
    flags = {
        END_HEADERS_FLAG: fields.MultiFlagsEntry('EH', 'End Headers'),
        PADDED_FLAG: fields.MultiFlagsEntry('P', 'Padded')
    }

    name = 'HTTP/2 Push Promise Frame'
    fields_desc = [
        fields.BitField('reserved', 0, 1),
        fields.BitField('stream_id', 0, 31),
        fields.PacketListField('hdrs', [], HPackHeaders)
    ]


class H2PaddedPushPromiseFrame(H2PushPromiseFrame):
    """ H2PaddedPushPromiseFrame implements RFC7540 par6.6. This
    packet is the variant to use when the underlayer padding flag is set
    """
    __slots__ = ['s_len']

    name = 'HTTP/2 Padded Push Promise Frame'
    fields_desc = [
        fields.FieldLenField('padlen', None, length_of='padding', fmt='B'),
        fields.BitField('reserved', 0, 1),
        fields.BitField('stream_id', 0, 31),
        fields.PacketListField('hdrs', [], HPackHeaders,
                               length_from=lambda pkt: pkt.get_hdrs_len()
                               ),
        fields.StrLenField('padding', '',
                           length_from=lambda pkt: pkt.getfieldval('padlen')
                           )
    ]

    def get_hdrs_len(self):
        # type: () -> int
        """ get_hdrs_len computes the length of the hdrs field

        To do this computation, the length of the padlen field, reserved,
        stream_id and the actual padding is subtracted to the string that was
        provided to the pre_dissect fun of the pkt parameter.
        @return int: the length of the hdrs field
        @raise AssertionError
        """
        fld, padding_len = self.getfield_and_val('padlen')
        padding_len_len = fld.i2len(self, padding_len)
        bit_len = self.get_field('reserved').size
        bit_len += self.get_field('stream_id').size

        ret = int(self.s_len -
                  padding_len_len -
                  padding_len -
                  (bit_len / 8)
                  )
        assert(ret >= 0)
        return ret

    def pre_dissect(self, s):
        # type: (str) -> str
        """pre_dissect is filling the s_len property of this instance. This
        property is later used during the parsing of the hdrs PacketListField
        when trying to evaluate the length of the PacketListField! This "trick"
        works because the underlayer packet (H2Frame) is assumed to override the  # noqa: E501
        "extract_padding" method and to only provide to this packet the data
        necessary for this packet. Tricky, tricky, will break some day probably!  # noqa: E501
        """
        self.s_len = len(s)
        return s

#                                               HTTP/2 Ping Frame Packets                                              #  # noqa: E501


class H2PingFrame(H2FramePayload):
    """ H2PingFrame implements the RFC 7540 par6.7
    """
    type_id = 6
    ACK_FLAG = 0  # 0x1
    flags = {
        ACK_FLAG: fields.MultiFlagsEntry('A', 'ACK')
    }

    name = 'HTTP/2 Ping Frame'
    fields_desc = [
        fields.LongField('opaque', 0)
    ]

    def __init__(self, *args, **kwargs):
        """
        @raise AssertionError
        """
        # RFC7540 par6.7 p42
        assert(
            len(args) == 0 or (
                (isinstance(args[0], bytes) or
                 isinstance(args[0], str)) and
                len(args[0]) == 8
            )
        ), 'Invalid ping frame; length is not 8'
        super(H2PingFrame, self).__init__(*args, **kwargs)


#                                             HTTP/2 GoAway Frame Packets                                              #  # noqa: E501

class H2GoAwayFrame(H2FramePayload):
    """ H2GoAwayFrame implements the RFC 7540 par6.8
    """
    type_id = 7

    name = 'HTTP/2 Go Away Frame'
    fields_desc = [
        fields.BitField('reserved', 0, 1),
        fields.BitField('last_stream_id', 0, 31),
        fields.EnumField('error', 0, H2ErrorCodes.literal, fmt='!I'),
        fields.StrField('additional_data', '')
    ]

#                                      HTTP/2 Window Update Frame Packets                                              #  # noqa: E501


class H2WindowUpdateFrame(H2FramePayload):
    """ H2WindowUpdateFrame implements the RFC 7540 par6.9
    """
    type_id = 8

    name = 'HTTP/2 Window Update Frame'
    fields_desc = [
        fields.BitField('reserved', 0, 1),
        fields.BitField('win_size_incr', 0, 31)
    ]

    def __init__(self, *args, **kwargs):
        """
        @raise AssertionError
        """
        # RFC7540 par6.9 p46
        assert(
            len(args) == 0 or (
                (isinstance(args[0], bytes) or
                 isinstance(args[0], str)) and
                len(args[0]) == 4
            )
        ), 'Invalid window update frame; length is not 4'
        super(H2WindowUpdateFrame, self).__init__(*args, **kwargs)

#                                       HTTP/2 Continuation Frame Packets                                              #  # noqa: E501


class H2ContinuationFrame(H2FramePayload):
    """ H2ContinuationFrame implements the RFC 7540 par6.10
    """
    type_id = 9
    END_HEADERS_FLAG = 2  # Ox4
    flags = {
        END_HEADERS_FLAG: fields.MultiFlagsEntry('EH', 'End Headers')
    }

    name = 'HTTP/2 Continuation Frame'
    fields_desc = [
        fields.PacketListField('hdrs', [], HPackHeaders)
    ]

#                                          HTTP/2 Base Frame Packets                                                   #  # noqa: E501


class H2Frame(packet.Packet):
    """ H2Frame implements the frame structure as defined in RFC 7540 par4.1

    This packet may have a payload (one of the H2FramePayload) or none, in some
    rare cases such as settings acknowledgement)
    """
    name = 'HTTP/2 Frame'
    fields_desc = [
        fields.X3BytesField('len', None),
        fields.EnumField('type', None, {
            0: 'DataFrm',
            1: 'HdrsFrm',
            2: 'PrioFrm',
            3: 'RstFrm',
            4: 'SetFrm',
            5: 'PushFrm',
            6: 'PingFrm',
            7: 'GoawayFrm',
            8: 'WinFrm',
            9: 'ContFrm'
        }, "b"),
        fields.MultiFlagsField('flags', set(), 8, {
            H2DataFrame.type_id: H2DataFrame.flags,
            H2HeadersFrame.type_id: H2HeadersFrame.flags,
            H2PushPromiseFrame.type_id: H2PushPromiseFrame.flags,
            H2SettingsFrame.type_id: H2SettingsFrame.flags,
            H2PingFrame.type_id: H2PingFrame.flags,
            H2ContinuationFrame.type_id: H2ContinuationFrame.flags,
        },
            depends_on=lambda pkt: pkt.getfieldval('type')
        ),
        fields.BitField('reserved', 0, 1),
        fields.BitField('stream_id', 0, 31)
    ]

    def guess_payload_class(self, payload):
        # type: (str) -> base_classes.Packet_metaclass
        """ guess_payload_class returns the Class object to use for parsing a payload
        This function uses the H2Frame.type field value to decide which payload to parse. The implement cannot be  # noqa: E501
        performed using the simple bind_layers helper because sometimes the selection of which Class object to return  # noqa: E501
        also depends on the H2Frame.flags value.

        @param payload:
        @return:
        """
        if len(payload) == 0:
            return packet.NoPayload

        t = self.getfieldval('type')
        if t == H2DataFrame.type_id:
            if H2DataFrame.flags[H2DataFrame.PADDED_FLAG].short in self.getfieldval('flags'):  # noqa: E501
                return H2PaddedDataFrame
            return H2DataFrame

        if t == H2HeadersFrame.type_id:
            if H2HeadersFrame.flags[H2HeadersFrame.PADDED_FLAG].short in self.getfieldval('flags'):  # noqa: E501
                if H2HeadersFrame.flags[H2HeadersFrame.PRIORITY_FLAG].short in self.getfieldval('flags'):  # noqa: E501
                    return H2PaddedPriorityHeadersFrame
                else:
                    return H2PaddedHeadersFrame
            elif H2HeadersFrame.flags[H2HeadersFrame.PRIORITY_FLAG].short in self.getfieldval('flags'):  # noqa: E501
                return H2PriorityHeadersFrame
            return H2HeadersFrame

        if t == H2PriorityFrame.type_id:
            return H2PriorityFrame

        if t == H2ResetFrame.type_id:
            return H2ResetFrame

        if t == H2SettingsFrame.type_id:
            return H2SettingsFrame

        if t == H2PushPromiseFrame.type_id:
            if H2PushPromiseFrame.flags[H2PushPromiseFrame.PADDED_FLAG].short in self.getfieldval('flags'):  # noqa: E501
                return H2PaddedPushPromiseFrame
            return H2PushPromiseFrame

        if t == H2PingFrame.type_id:
            return H2PingFrame

        if t == H2GoAwayFrame.type_id:
            return H2GoAwayFrame

        if t == H2WindowUpdateFrame.type_id:
            return H2WindowUpdateFrame

        if t == H2ContinuationFrame.type_id:
            return H2ContinuationFrame

        return config.conf.padding_layer

    def extract_padding(self, s):
        # type: (str) -> Tuple[str, str]
        """
        @param str s: the string from which to tell the padding and the payload data apart  # noqa: E501
        @return (str, str): the padding and the payload data strings
        @raise AssertionError
        """
        assert isinstance(self.len, six.integer_types) and self.len >= 0, 'Invalid length: negative len?'  # noqa: E501
        assert len(s) >= self.len, 'Invalid length: string too short for this length'  # noqa: E501
        return s[:self.len], s[self.len:]

    def post_build(self, p, pay):
        # type: (str, str) -> str
        """
        @param str p: the stringified packet
        @param str pay: the stringified payload
        @return str: the stringified packet and payload, with the packet length field "patched"  # noqa: E501
        @raise AssertionError
        """
        # This logic, while awkward in the post_build and more reasonable in
        # a self_build is implemented here for performance tricks reason
        if self.getfieldval('len') is None:
            assert(len(pay) < (1 << 24)), 'Invalid length: payload is too long'
            p = struct.pack('!L', len(pay))[1:] + p[3:]
        return super(H2Frame, self).post_build(p, pay)


class H2Seq(packet.Packet):
    """ H2Seq is a helper packet that contains several H2Frames and their
    payload. This packet can be used, for instance, while reading manually from
    a TCP socket.
    """
    name = 'HTTP/2 Frame Sequence'
    fields_desc = [
        fields.PacketListField('frames', [], H2Frame)
    ]

    def guess_payload_class(self, payload):
        # type: (str) -> base_classes.Packet_metaclass
        return config.conf.padding_layer


packet.bind_layers(H2Frame, H2DataFrame, {'type': H2DataFrame.type_id})
packet.bind_layers(H2Frame, H2PaddedDataFrame, {'type': H2DataFrame.type_id})
packet.bind_layers(H2Frame, H2HeadersFrame, {'type': H2HeadersFrame.type_id})
packet.bind_layers(H2Frame, H2PaddedHeadersFrame, {'type': H2HeadersFrame.type_id})  # noqa: E501
packet.bind_layers(H2Frame, H2PriorityHeadersFrame, {'type': H2HeadersFrame.type_id})  # noqa: E501
packet.bind_layers(H2Frame, H2PaddedPriorityHeadersFrame, {'type': H2HeadersFrame.type_id})  # noqa: E501
packet.bind_layers(H2Frame, H2PriorityFrame, {'type': H2PriorityFrame.type_id})
packet.bind_layers(H2Frame, H2ResetFrame, {'type': H2ResetFrame.type_id})
packet.bind_layers(H2Frame, H2SettingsFrame, {'type': H2SettingsFrame.type_id})
packet.bind_layers(H2Frame, H2PingFrame, {'type': H2PingFrame.type_id})
packet.bind_layers(H2Frame, H2PushPromiseFrame, {'type': H2PushPromiseFrame.type_id})  # noqa: E501
packet.bind_layers(H2Frame, H2PaddedPushPromiseFrame, {'type': H2PaddedPushPromiseFrame.type_id})  # noqa: E501
packet.bind_layers(H2Frame, H2GoAwayFrame, {'type': H2GoAwayFrame.type_id})
packet.bind_layers(H2Frame, H2WindowUpdateFrame, {'type': H2WindowUpdateFrame.type_id})  # noqa: E501
packet.bind_layers(H2Frame, H2ContinuationFrame, {'type': H2ContinuationFrame.type_id})  # noqa: E501


#                                          HTTP/2 Connection Preface                                                   #  # noqa: E501
# From RFC 7540 par3.5
H2_CLIENT_CONNECTION_PREFACE = bytes_hex('505249202a20485454502f322e300d0a0d0a534d0d0a0d0a')  # noqa: E501


###############################################################################
#                                                   HTTP/2 Helpers            #
###############################################################################

class HPackHdrEntry(Sized):
    """ HPackHdrEntry is an entry of the HPackHdrTable helper

    Each HPackHdrEntry instance is a header line (name and value). Names are
    normalized (lowercase), according to RFC 7540 par8.1.2
    """
    __slots__ = ['_name', '_len', '_value']

    def __init__(self, name, value):
        # type: (str, str) -> None
        """
        @raise AssertionError
        """
        assert(len(name) > 0)

        self._name = name.lower()
        self._value = value

        # 32 bytes is an RFC-hardcoded value: see RFC 7541 par4.1
        self._len = (32 + len(self._name) + len(self._value))

    def name(self):
        # type: () -> str
        return self._name

    def value(self):
        # type: () -> str
        return self._value

    def size(self):
        # type: () -> int
        """ size returns the "length" of the header entry, as defined in
        RFC 7541 par4.1.
        """
        return self._len

    __len__ = size

    def __str__(self):
        # type: () -> str
        """ __str__ returns the header as it would be formatted in textual format
        """
        if self._name.startswith(':'):
            return "{} {}".format(self._name, self._value)
        else:
            return "{}: {}".format(self._name, self._value)

    def __bytes__(self):
        return bytes_encode(self.__str__())


class HPackHdrTable(Sized):
    """ HPackHdrTable is a helper class that implements some of the logic
    associated with indexing of headers (read and write operations in this
    "registry". THe HPackHdrTable also implements convenience functions to easily  # noqa: E501
    convert to and from textual representation and binary representation of
    a HTTP/2 requests
    """
    __slots__ = [
        '_dynamic_table',
        '_dynamic_table_max_size',
        '_dynamic_table_cap_size',
        '_regexp'
    ]
    """:var _dynamic_table: the list containing entries requested to be added by
    the peer and registered with a register() call
    :var _dynamic_table_max_size: the current maximum size of the dynamic table
    in bytes. This value is updated with the Dynamic Table Size Update messages
    defined in RFC 7541 par6.3
    :var _dynamic_table_cap_size: the maximum size of the dynamic table in
    bytes. This value is updated with the SETTINGS_HEADER_TABLE_SIZE HTTP/2
    setting.
    """

    # Manually imported from RFC 7541 Appendix A
    _static_entries = {
        1: HPackHdrEntry(':authority', ''),
        2: HPackHdrEntry(':method', 'GET'),
        3: HPackHdrEntry(':method', 'POST'),
        4: HPackHdrEntry(':path', '/'),
        5: HPackHdrEntry(':path', '/index.html'),
        6: HPackHdrEntry(':scheme', 'http'),
        7: HPackHdrEntry(':scheme', 'https'),
        8: HPackHdrEntry(':status', '200'),
        9: HPackHdrEntry(':status', '204'),
        10: HPackHdrEntry(':status', '206'),
        11: HPackHdrEntry(':status', '304'),
        12: HPackHdrEntry(':status', '400'),
        13: HPackHdrEntry(':status', '404'),
        14: HPackHdrEntry(':status', '500'),
        15: HPackHdrEntry('accept-charset', ''),
        16: HPackHdrEntry('accept-encoding', 'gzip, deflate'),
        17: HPackHdrEntry('accept-language', ''),
        18: HPackHdrEntry('accept-ranges', ''),
        19: HPackHdrEntry('accept', ''),
        20: HPackHdrEntry('access-control-allow-origin', ''),
        21: HPackHdrEntry('age', ''),
        22: HPackHdrEntry('allow', ''),
        23: HPackHdrEntry('authorization', ''),
        24: HPackHdrEntry('cache-control', ''),
        25: HPackHdrEntry('content-disposition', ''),
        26: HPackHdrEntry('content-encoding', ''),
        27: HPackHdrEntry('content-language', ''),
        28: HPackHdrEntry('content-length', ''),
        29: HPackHdrEntry('content-location', ''),
        30: HPackHdrEntry('content-range', ''),
        31: HPackHdrEntry('content-type', ''),
        32: HPackHdrEntry('cookie', ''),
        33: HPackHdrEntry('date', ''),
        34: HPackHdrEntry('etag', ''),
        35: HPackHdrEntry('expect', ''),
        36: HPackHdrEntry('expires', ''),
        37: HPackHdrEntry('from', ''),
        38: HPackHdrEntry('host', ''),
        39: HPackHdrEntry('if-match', ''),
        40: HPackHdrEntry('if-modified-since', ''),
        41: HPackHdrEntry('if-none-match', ''),
        42: HPackHdrEntry('if-range', ''),
        43: HPackHdrEntry('if-unmodified-since', ''),
        44: HPackHdrEntry('last-modified', ''),
        45: HPackHdrEntry('link', ''),
        46: HPackHdrEntry('location', ''),
        47: HPackHdrEntry('max-forwards', ''),
        48: HPackHdrEntry('proxy-authenticate', ''),
        49: HPackHdrEntry('proxy-authorization', ''),
        50: HPackHdrEntry('range', ''),
        51: HPackHdrEntry('referer', ''),
        52: HPackHdrEntry('refresh', ''),
        53: HPackHdrEntry('retry-after', ''),
        54: HPackHdrEntry('server', ''),
        55: HPackHdrEntry('set-cookie', ''),
        56: HPackHdrEntry('strict-transport-security', ''),
        57: HPackHdrEntry('transfer-encoding', ''),
        58: HPackHdrEntry('user-agent', ''),
        59: HPackHdrEntry('vary', ''),
        60: HPackHdrEntry('via', ''),
        61: HPackHdrEntry('www-authenticate', ''),
    }

    # The value of this variable cannot be determined at declaration time. It is  # noqa: E501
    # initialized by an init_static_table call
    _static_entries_last_idx = None

    @classmethod
    def init_static_table(cls):
        # type: () -> None
        cls._static_entries_last_idx = max(cls._static_entries)

    def __init__(self, dynamic_table_max_size=4096, dynamic_table_cap_size=4096):  # noqa: E501
        # type: (int, int) -> None
        """
        @param int dynamic_table_max_size: the current maximum size of the dynamic entry table in bytes  # noqa: E501
        @param int dynamic_table_cap_size: the maximum-maximum size of the dynamic entry table in bytes  # noqa: E501
        @raises AssertionError
        """
        self._regexp = None
        if isinstance(type(self)._static_entries_last_idx, type(None)):
            type(self).init_static_table()

        assert dynamic_table_max_size <= dynamic_table_cap_size, \
            'EINVAL: dynamic_table_max_size too large; expected value is less or equal to dynamic_table_cap_size'  # noqa: E501

        self._dynamic_table = []  # type: List[HPackHdrEntry]
        self._dynamic_table_max_size = dynamic_table_max_size
        self._dynamic_table_cap_size = dynamic_table_cap_size

    def __getitem__(self, idx):
        # type: (int) -> HPackHdrEntry
        """Gets an element from the header tables (static or dynamic indifferently)

        @param int idx: the index number of the entry to retrieve. If the index
        value is superior to the last index of the static entry table, then the
        dynamic entry type is requested, following the procedure described in
        RFC 7541 par2.3.3
        @return HPackHdrEntry: the entry defined at this requested index. If the entry does not exist, KeyError is  # noqa: E501
          raised
        @raise KeyError, AssertionError
        """
        assert(idx >= 0)
        if idx > type(self)._static_entries_last_idx:
            idx -= type(self)._static_entries_last_idx + 1
            if idx >= len(self._dynamic_table):
                raise KeyError(
                    'EINVAL: idx: out-of-bound read: {}; maximum index: {}'.format(idx, len(self._dynamic_table))  # noqa: E501
                )
            return self._dynamic_table[idx]
        return type(self)._static_entries[idx]

    def resize(self, ns):
        # type: (int) -> None
        """Resize the dynamic table. If the new size (ns) must be between 0 and
        the cap size. If the new size is lower than the current size of the
        dynamic table, entries are evicted.
        @param int ns: the new size of the dynamic table
        @raise AssertionError
        """
        assert 0 <= ns <= self._dynamic_table_cap_size, \
            'EINVAL: ns: out-of-range value; expected value is in the range [0;{}['.format(self._dynamic_table_cap_size)  # noqa: E501

        old_size = self._dynamic_table_max_size
        self._dynamic_table_max_size = ns
        if old_size > self._dynamic_table_max_size:
            self._reduce_dynamic_table()

    def recap(self, nc):
        # type: (int) -> None
        """recap changes the maximum size limit of the dynamic table. It also
        proceeds to a resize(), if the new size is lower than the previous one.
        @param int nc: the new cap of the dynamic table (that is the maximum-maximum size)  # noqa: E501
        @raise AssertionError
        """
        assert(nc >= 0)
        t = self._dynamic_table_cap_size > nc
        self._dynamic_table_cap_size = nc

        if t:
            # The RFC is not clear about whether this resize should happen;
            # we do it anyway
            self.resize(nc)

    def _reduce_dynamic_table(self, new_entry_size=0):
        # type: (int) -> None
        """_reduce_dynamic_table evicts entries from the dynamic table until it
        fits in less than the current size limit. The optional parameter,
        new_entry_size, allows the resize to happen so that a new entry of this
        size fits in.
        @param int new_entry_size: if called before adding a new entry, the size of the new entry in bytes (following  # noqa: E501
        the RFC7541 definition of the size of an entry)
        @raise AssertionError
        """
        assert(new_entry_size >= 0)
        cur_sz = len(self)
        dyn_tbl_sz = len(self._dynamic_table)
        while dyn_tbl_sz > 0 and cur_sz + new_entry_size > self._dynamic_table_max_size:  # noqa: E501
            last_elmt_sz = len(self._dynamic_table[-1])
            self._dynamic_table.pop()
            dyn_tbl_sz -= 1
            cur_sz -= last_elmt_sz

    def register(self, hdrs):
        # type: (Union[HPackLitHdrFldWithIncrIndexing, H2Frame, List[HPackHeaders]]) -> None  # noqa: E501
        """register adds to this table the instances of
        HPackLitHdrFldWithIncrIndexing provided as parameters.

        A H2Frame with a H2HeadersFrame payload can be provided, as much as a
        python list of HPackHeaders or a single HPackLitHdrFldWithIncrIndexing
        instance.
        @param HPackLitHdrFldWithIncrIndexing|H2Frame|list of HPackHeaders hdrs: the header(s) to register  # noqa: E501
        @raise AssertionError
        """
        if isinstance(hdrs, H2Frame):
            hdrs = [hdr for hdr in hdrs.payload.hdrs if isinstance(hdr, HPackLitHdrFldWithIncrIndexing)]  # noqa: E501
        elif isinstance(hdrs, HPackLitHdrFldWithIncrIndexing):
            hdrs = [hdrs]
        else:
            hdrs = [hdr for hdr in hdrs if isinstance(hdr, HPackLitHdrFldWithIncrIndexing)]  # noqa: E501

        for hdr in hdrs:
            if hdr.index == 0:
                hdr_name = hdr.hdr_name.getfieldval('data').origin()
            else:
                idx = int(hdr.index)
                hdr_name = self[idx].name()
            hdr_value = hdr.hdr_value.getfieldval('data').origin()

            # Note: we do not delete any existing hdrentry with the same names
            # and values, as dictated by RFC 7541 par2.3.2

            entry = HPackHdrEntry(hdr_name, hdr_value)
            # According to RFC7541 par4.4, "Before a new entry is added to
            # the dynamic table, entries are evicted
            # from the end of the dynamic table until the size of the dynamic
            # table is less than or equal to (maximum size - new entry size)
            # or until the table is empty"
            # Also, "It is not an error to attempt to add an entry that is
            # larger than the maximum size; an attempt to add an entry larger
            # than the maximum size causes the table to be emptied of all
            # existing entries and results in an empty table"
            # For this reason, we first call the _reduce_dynamic_table and
            # then throw an assertion error if the new entry does not fit in
            new_entry_len = len(entry)
            self._reduce_dynamic_table(new_entry_len)
            assert(new_entry_len <= self._dynamic_table_max_size)
            self._dynamic_table.insert(0, entry)

    def get_idx_by_name(self, name):
        # type: (str) -> Optional[int]
        """ get_idx_by_name returns the index of a matching registered header

        This implementation will prefer returning a static entry index whenever
        possible. If multiple matching header name are found in the static
        table, there is insurance that the first entry (lowest index number)
        will be returned.
        If no matching header is found, this method returns None.
        """
        name = name.lower()
        for key, val in six.iteritems(type(self)._static_entries):
            if val.name() == name:
                return key
        for idx, val in enumerate(self._dynamic_table):
            if val.name() == name:
                return type(self)._static_entries_last_idx + idx + 1
        return None

    def get_idx_by_name_and_value(self, name, value):
        # type: (str, str) -> Optional[int]
        """ get_idx_by_name_and_value returns the index of a matching registered
        header

        This implementation will prefer returning a static entry index whenever
        possible. If multiple matching headers are found in the dynamic table,
        the lowest index is returned
        If no matching header is found, this method returns None.
        """
        name = name.lower()
        for key, val in six.iteritems(type(self)._static_entries):
            if val.name() == name and val.value() == value:
                return key
        for idx, val in enumerate(self._dynamic_table):
            if val.name() == name and val.value() == value:
                return type(self)._static_entries_last_idx + idx + 1
        return None

    def __len__(self):
        # type: () -> int
        """ __len__ returns the summed length of all dynamic entries
        """
        return sum(len(x) for x in self._dynamic_table)

    def gen_txt_repr(self, hdrs, register=True):
        # type: (Union[H2Frame, List[HPackHeaders]], Optional[bool]) -> str
        """ gen_txt_repr returns a "textual" representation of the provided
        headers.

        The output of this function is compatible with the input of
        parse_txt_hdrs.
        @param H2Frame|list of HPackHeaders hdrs: the list of headers to convert to textual representation  # noqa: E501
        @param bool: whether incremental headers should be added to the dynamic table as we generate the text  # noqa: E501
            representation
        @return str: the textual representation of the provided headers
        @raise AssertionError
        """
        lst = []
        if isinstance(hdrs, H2Frame):
            hdrs = hdrs.payload.hdrs

        for hdr in hdrs:
            try:
                if isinstance(hdr, HPackIndexedHdr):
                    lst.append('{}'.format(self[hdr.index]))
                elif isinstance(hdr, (
                    HPackLitHdrFldWithIncrIndexing,
                    HPackLitHdrFldWithoutIndexing
                )):
                    if hdr.index != 0:
                        name = self[hdr.index].name()
                    else:
                        name = hdr.hdr_name.getfieldval('data').origin()
                    if name.startswith(':'):
                        lst.append(
                            '{} {}'.format(
                                name,
                                hdr.hdr_value.getfieldval('data').origin()
                            )
                        )
                    else:
                        lst.append(
                            '{}: {}'.format(
                                name,
                                hdr.hdr_value.getfieldval('data').origin()
                            )
                        )
                if register and isinstance(hdr, HPackLitHdrFldWithIncrIndexing):  # noqa: E501
                    self.register(hdr)
            except KeyError as e:  # raised when an index is out-of-bound
                print(e)
                continue
        return '\n'.join(lst)

    @staticmethod
    def _optimize_header_length_and_packetify(s):
        # type: (str) -> HPackHdrString
        # type: (str) -> HPackHdrString
        zs = HPackZString(s)
        if len(zs) >= len(s):
            return HPackHdrString(data=HPackLiteralString(s))
        return HPackHdrString(data=zs)

    def _convert_a_header_to_a_h2_header(self, hdr_name, hdr_value, is_sensitive, should_index):  # noqa: E501
        # type: (str, str, Callable[[str, str], bool], Callable[[str], bool]) -> Tuple[HPackHeaders, int]  # noqa: E501
        """ _convert_a_header_to_a_h2_header builds a HPackHeaders from a header
        name and a value. It returns a HPackIndexedHdr whenever possible. If not,  # noqa: E501
        it returns a HPackLitHdrFldWithoutIndexing or a
        HPackLitHdrFldWithIncrIndexing, based on the should_index callback.
        HPackLitHdrFldWithoutIndexing is forced if the is_sensitive callback
        returns True and its never_index bit is set.
        """

        # If both name and value are already indexed
        idx = self.get_idx_by_name_and_value(hdr_name, hdr_value)
        if idx is not None:
            return HPackIndexedHdr(index=idx), len(self[idx])

        # The value is not indexed for this headers

        hdr_value = self._optimize_header_length_and_packetify(hdr_value)

        # Searching if the header name is indexed
        idx = self.get_idx_by_name(hdr_name)
        if idx is not None:
            if is_sensitive(
                hdr_name,
                hdr_value.getfieldval('data').origin()
            ):
                return HPackLitHdrFldWithoutIndexing(
                    never_index=1,
                    index=idx,
                    hdr_value=hdr_value
                ), len(
                    HPackHdrEntry(
                        self[idx].name(),
                        hdr_value.getfieldval('data').origin()
                    )
                )
            if should_index(hdr_name):
                return HPackLitHdrFldWithIncrIndexing(
                    index=idx,
                    hdr_value=hdr_value
                ), len(
                    HPackHdrEntry(
                        self[idx].name(),
                        hdr_value.getfieldval('data').origin()
                    )
                )
            return HPackLitHdrFldWithoutIndexing(
                index=idx,
                hdr_value=hdr_value
            ), len(
                HPackHdrEntry(
                    self[idx].name(),
                    hdr_value.getfieldval('data').origin()
                )
            )

        hdr_name = self._optimize_header_length_and_packetify(hdr_name)

        if is_sensitive(
            hdr_name.getfieldval('data').origin(),
            hdr_value.getfieldval('data').origin()
        ):
            return HPackLitHdrFldWithoutIndexing(
                never_index=1,
                index=0,
                hdr_name=hdr_name,
                hdr_value=hdr_value
            ), len(
                HPackHdrEntry(
                    hdr_name.getfieldval('data').origin(),
                    hdr_value.getfieldval('data').origin()
                )
            )
        if should_index(hdr_name.getfieldval('data').origin()):
            return HPackLitHdrFldWithIncrIndexing(
                index=0,
                hdr_name=hdr_name,
                hdr_value=hdr_value
            ), len(
                HPackHdrEntry(
                    hdr_name.getfieldval('data').origin(),
                    hdr_value.getfieldval('data').origin()
                )
            )
        return HPackLitHdrFldWithoutIndexing(
            index=0,
            hdr_name=hdr_name,
            hdr_value=hdr_value
        ), len(
            HPackHdrEntry(
                hdr_name.getfieldval('data').origin(),
                hdr_value.getfieldval('data').origin()
            )
        )

    def _parse_header_line(self, l):
        # type: (str) -> Union[Tuple[None, None], Tuple[str, str]]

        if self._regexp is None:
            self._regexp = re.compile(br'^(?::([a-z\-0-9]+)|([a-z\-0-9]+):)\s+(.+)$')  # noqa: E501

        hdr_line = l.rstrip()
        grp = self._regexp.match(hdr_line)

        if grp is None or len(grp.groups()) != 3:
            return None, None

        if grp.group(1) is not None:
            hdr_name = b':' + grp.group(1)
        else:
            hdr_name = grp.group(2)
        return plain_str(hdr_name.lower()), plain_str(grp.group(3))

    def parse_txt_hdrs(self,
                       s,  # type: str
                       stream_id=1,  # type: int
                       body=None,  # type: Optional[str]
                       max_frm_sz=4096,  # type: int
                       max_hdr_lst_sz=0,  # type: int
                       is_sensitive=lambda n, v: False,  # type: Callable[[str, str], bool]  # noqa: E501
                       should_index=lambda x: False,  # type: Callable[[str], bool]  # noqa: E501
                       register=True,  # type: bool
                       ):
        # type: (...) -> H2Seq
        """ parse_txt_hdrs parses headers expressed in text and converts them
        into a series of H2Frames with the "correct" flags. A body can be provided  # noqa: E501
        in which case, the data frames are added, bearing the End Stream flag,
        instead of the H2HeadersFrame/H2ContinuationFrame. The generated frames
        may respect max_frm_sz (SETTINGS_MAX_FRAME_SIZE) and
        max_hdr_lst_sz (SETTINGS_MAX_HEADER_LIST_SIZE) if provided. The headers
        are split into multiple headers fragment (and H2Frames) to respect these  # noqa: E501
        limits. Also, a callback can be provided to tell if a header should be
        never indexed (sensitive headers, such as cookies), and another callback  # noqa: E501
        say if the header should be registered into the index table at all.
        For an header to be registered, the is_sensitive callback must return
        False AND the should_index callback should return True. This is the
        default behavior.

        @param str s: the string to parse for headers
        @param int stream_id: the stream id to use in the generated H2Frames
        @param str|None body: the eventual body of the request, that is added to the generated frames  # noqa: E501
        @param int max_frm_sz: the maximum frame size. This is used to split the headers and data frames according to  # noqa: E501
        the maximum frame size negotiated for this connection
        @param int max_hdr_lst_sz: the maximum size of a "header fragment" as defined in RFC7540  # noqa: E501
        @param callable is_sensitive: callback that returns True if the provided header is sensible and must be stored  # noqa: E501
        in a header packet requesting this header never to be indexed
        @param callable should_index: callback that returns True if the provided header should be stored in a header  # noqa: E501
        packet requesting indexation in the dynamic header table.
        @param bool register: whether to register new headers with incremental indexing as we parse them  # noqa: E501
        @raise Exception
        """

        sio = BytesIO(s)

        base_frm_len = len(raw(H2Frame()))

        ret = H2Seq()
        cur_frm = H2HeadersFrame()  # type: Union[H2HeadersFrame, H2ContinuationFrame]  # noqa: E501
        cur_hdr_sz = 0

        # For each line in the headers str to parse
        for hdr_line in sio:
            hdr_name, hdr_value = self._parse_header_line(hdr_line)
            if hdr_name is None:
                continue

            new_hdr, new_hdr_len = self._convert_a_header_to_a_h2_header(
                hdr_name, hdr_value, is_sensitive, should_index
            )
            new_hdr_bin_len = len(raw(new_hdr))

            if register and isinstance(new_hdr, HPackLitHdrFldWithIncrIndexing):  # noqa: E501
                self.register(new_hdr)

            # The new header binary length (+ base frame size) must not exceed
            # the maximum frame size or it will just never fit. Also, the
            # header entry length (as specified in RFC7540 par6.5.2) must not
            # exceed the maximum length of a header fragment or it will just
            # never fit
            if (new_hdr_bin_len + base_frm_len > max_frm_sz or
                    (max_hdr_lst_sz != 0 and new_hdr_len > max_hdr_lst_sz)):
                raise Exception('Header too long: {}'.format(hdr_name))

            if (max_frm_sz < len(raw(cur_frm)) + base_frm_len + new_hdr_len or
                (
                    max_hdr_lst_sz != 0 and
                    max_hdr_lst_sz < cur_hdr_sz + new_hdr_len
            )
            ):
                flags = set()
                if isinstance(cur_frm, H2HeadersFrame) and not body:
                    flags.add('ES')
                ret.frames.append(H2Frame(stream_id=stream_id, flags=flags) / cur_frm)  # noqa: E501
                cur_frm = H2ContinuationFrame()
                cur_hdr_sz = 0

            hdr_list = cur_frm.hdrs
            hdr_list += new_hdr
            cur_hdr_sz += new_hdr_len

        flags = {'EH'}
        if isinstance(cur_frm, H2HeadersFrame) and not body:
            flags.add('ES')
        ret.frames.append(H2Frame(stream_id=stream_id, flags=flags) / cur_frm)

        if body:
            base_data_frm_len = len(raw(H2DataFrame()))
            sio = BytesIO(body)
            frgmt = sio.read(max_frm_sz - base_data_frm_len - base_frm_len)
            while frgmt:
                nxt_frgmt = sio.read(max_frm_sz - base_data_frm_len - base_frm_len)  # noqa: E501
                flags = set()
                if len(nxt_frgmt) == 0:
                    flags.add('ES')
                ret.frames.append(
                    H2Frame(stream_id=stream_id, flags=flags) / H2DataFrame(data=frgmt)  # noqa: E501
                )
                frgmt = nxt_frgmt
        return ret
