Source code for pcapkit.protocols.application.http
# -*- coding: utf-8 -*-
"""HTTP - Hypertext Transfer Protocol
========================================
: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.data.application.http import HTTP as DataType_HTTP
from pcapkit.protocols.protocol import PT
from pcapkit.utilities.exceptions import ProtocolError
if TYPE_CHECKING:
from typing import Any, Optional, Type
from typing_extensions import Literal
from pcapkit.protocols.protocol import Protocol
__all__ = ['HTTP']
[docs]class HTTP(Application[DataType_HTTP], Generic[PT]):
"""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`]
"""
##########################################################################
# 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["HTTPv1"], Literal["HTTPv2"]]':
"""Index ID of the protocol."""
return ('HTTPv1', 'HTTPv2')
[docs] def read(self, length: 'Optional[int]' = None, *,
version: 'Optional[Literal[1, 2]]' = None, **kwargs: 'Any') -> 'DataType_HTTP':
"""Read (parse) packet data.
Args:
length: Length of packet data.
version: Version of HTTP.
**kwargs: Arbitrary keyword arguments.
Returns:
Parsed packet data.
"""
if TYPE_CHECKING:
protocol: 'Type[HTTP]'
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 # type: ignore[no-redef] # isort: skip # pylint: disable=line-too-long,import-outside-toplevel
elif version == 2:
from pcapkit.protocols.application.httpv2 import HTTP as protocol # type: ignore[no-redef] # 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 # type: ignore[attr-defined]
self._length = http.length
return http.info
[docs] def make(self, *, version: 'Literal[1, 2]', **kwargs: 'Any') -> 'bytes': # type: ignore[override] # pylint: disable=arguments-differ
"""Make (construct) packet data.
Args:
version: Version of HTTP.
**kwargs: Arbitrary keyword arguments.
Returns:
bytes: Constructed packet data.
"""
if TYPE_CHECKING:
protocol: 'Type[HTTP]'
if version == 1:
from pcapkit.protocols.application.httpv1 import HTTP as protocol # type: ignore[no-redef] # isort: skip # pylint: disable=line-too-long,import-outside-toplevel
elif version == 2:
from pcapkit.protocols.application.httpv2 import HTTP as protocol # type: ignore[no-redef] # isort: skip # pylint: disable=line-too-long,import-outside-toplevel
else:
raise ProtocolError(f"invalid HTTP version: {version}")
http = protocol(**kwargs)
self._version = http.version
self._length = http.length
return http.data
##########################################################################
# Utilities.
##########################################################################
[docs] def _guess_version(self, length: 'int', **kwargs: 'Any') -> 'Protocol':
"""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._file, 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._file, length, **kwargs)
raise ProtocolError("unknown HTTP version")