Source code for pcapkit.protocols.application.http

# -*- coding: utf-8 -*-
"""HTTP - Hypertext Transfer Protocol
========================================

.. module:: pcapkit.protocols.application.http

:mod:`pcapkit.protocols.application.http` contains
:class:`~pcapkit.protocols.application.http.HTTP`
only, which is a base class for Hypertext Transfer
Protocol (HTTP) [*]_ family, eg.
:class:`HTTP/1.* <pcapkit.protocols.application.application.httpv1>`
and :class:`HTTP/2 <pcapkit.protocols.application.application.httpv2>`.

.. [*] https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol

"""
import contextlib
from typing import TYPE_CHECKING, Generic

from pcapkit.protocols.application.application import Application
from pcapkit.protocols.protocol import _PT, _ST
from pcapkit.utilities.exceptions import ProtocolError

if TYPE_CHECKING:
    from typing import Any, Optional

    from typing_extensions import Literal

__all__ = ['HTTP']


[docs] class HTTP(Application[_PT, _ST], Generic[_PT, _ST]): """This class implements all protocols in HTTP family. - Hypertext Transfer Protocol (HTTP/1.1) [:rfc:`7230`] - Hypertext Transfer Protocol version 2 (HTTP/2) [:rfc:`7540`] """ if TYPE_CHECKING: #: Saved subclass protocol data (only for HTTP base class). _http: 'HTTP[_PT, _ST]' ########################################################################## # Properties. ########################################################################## @property def name(self) -> 'Literal["Hypertext Transfer Protocol"]': """Name of current protocol.""" return 'Hypertext Transfer Protocol' @property def alias(self) -> 'Literal["HTTP/0.9", "HTTP/1.0", "HTTP/1.1", "HTTP/2"]': """Acronym of current protocol.""" return f'HTTP/{self.version}' # type: ignore[return-value] @property def length(self) -> 'int': """Header length of current protocol.""" return self._length @property def version(self) -> 'Literal["0.9", "1.0", "1.1", "2"]': """Version of current protocol.""" return self._version ########################################################################## # Methods. ##########################################################################
[docs] @classmethod def id(cls) -> 'tuple[Literal["HTTP"], Literal["HTTPv1"], Literal["HTTPv2"]]': """Index ID of the protocol.""" return ('HTTP', 'HTTPv1', 'HTTPv2')
[docs] def read(self, length: 'Optional[int]' = None, *, version: 'Optional[Literal[1, 2]]' = None, **kwargs: 'Any') -> '_PT': """Read (parse) packet data. Args: length: Length of packet data. version: Version of HTTP. **kwargs: Arbitrary keyword arguments. Returns: Parsed packet data. """ if length is None: length = len(self) if version is None: http = self._guess_version(length, **kwargs) else: if version == 1: from pcapkit.protocols.application.httpv1 import HTTP as protocol # isort: skip # pylint: disable=line-too-long,import-outside-toplevel elif version == 2: from pcapkit.protocols.application.httpv2 import HTTP as protocol # type: ignore[assignment] # isort: skip # pylint: disable=line-too-long,import-outside-toplevel else: raise ProtocolError(f"invalid HTTP version: {version}") http = protocol(self._file, length, **kwargs) self._version = http.version self._length = http.length self._http = http return http.info
[docs] def make(self, version: 'Literal[1, 2]' = 1, **kwargs: 'Any') -> '_ST': """Make (construct) packet data. Args: version: Version of HTTP. **kwargs: Arbitrary keyword arguments. Returns: bytes: Constructed packet data. """ if version == 1: from pcapkit.protocols.application.httpv1 import HTTP as protocol # isort: skip # pylint: disable=line-too-long,import-outside-toplevel elif version == 2: from pcapkit.protocols.application.httpv2 import HTTP as protocol # type: ignore[assignment] # isort: skip # pylint: disable=line-too-long,import-outside-toplevel else: raise ProtocolError(f"invalid HTTP version: {version}") return protocol.make(**kwargs) # type: ignore[return-value]
########################################################################## # Utilities. ##########################################################################
[docs] @classmethod def _make_data(cls, data: '_PT') -> 'dict[str, Any]': # type: ignore[override] """Create key-value pairs from ``data`` for protocol construction. Args: data: protocol data Returns: Key-value pairs for protocol construction. """ version = data.get('version', 0) if version == 1: from pcapkit.protocols.application.httpv1 import HTTP as protocol elif version == 2: from pcapkit.protocols.application.httpv2 import HTTP as protocol # type: ignore[assignment] # isort: skip else: raise ProtocolError(f"invalid HTTP version: {version}") return protocol._make_data(data) # type: ignore[arg-type]
[docs] def _guess_version(self, length: 'int', **kwargs: 'Any') -> 'HTTP': """Guess HTTP version. Args: length: Length of packet data. Keyword Args: **kwargs: Arbitrary keyword arguments. Returns: Parsed packet data. """ from pcapkit.protocols.application.httpv1 import HTTP as HTTPv1 # isort: skip # pylint: disable=line-too-long,import-outside-toplevel with contextlib.suppress(ProtocolError): return HTTPv1(self._data, length, **kwargs) from pcapkit.protocols.application.httpv2 import HTTP as HTTPv2 # isort: skip # pylint: disable=line-too-long,import-outside-toplevel with contextlib.suppress(ProtocolError): return HTTPv2(self._data, length, **kwargs) raise ProtocolError("unknown HTTP version")