Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,12 @@ by key where there is no enum.

**The guard.** :file:`tests/project/test_protocol_member_order_2490_unit.py` holds
every protocol class to the groups, ``_make_data()`` first, each ``_read_xxx()``
before its ``_make_xxx()``, and subgroup b, with a table of known exceptions
(:issue:`2490`).
before its ``_make_xxx()``, subgroups b, c and d, and the order of the registry
maps, with a table of known exceptions (:issue:`2490`). It values a key that is an
enum member or an :class:`int`; a map with a key of another shape, such as the
computed keys of
:attr:`PCAPNG.__option__ <pcapkit.protocols.misc.pcapng.protocol.PCAPNG.__option__>`,
it leaves in its own order, which rule c then follows.

Example
~~~~~~~
Expand Down
52 changes: 26 additions & 26 deletions pcapkit/protocols/application/http/protocol.py
Original file line number Diff line number Diff line change
Expand Up @@ -805,6 +805,32 @@ def _guess_version(self, length: 'int', **kwargs: 'Any') -> 'HTTP':

raise ProtocolError('HTTP: unknown version')

@staticmethod
def _member_of_version(version: 'Any') -> 'Type[HTTP]':
"""The member a ``version`` of ``1`` or ``2`` names.

Args:
version: Version of HTTP.

Returns:
:class:`~pcapkit.protocols.application.httpv1.protocol.HTTPv1` or
:class:`~pcapkit.protocols.application.httpv2.protocol.HTTPv2`.

Raises:
ProtocolError: If ``version`` is neither, or is no :obj:`int`: a
:obj:`bool` or a :obj:`float` is refused even where it equals one.

"""
from pcapkit.protocols.application.httpv1.protocol import HTTPv1 # isort: skip # pylint: disable=import-outside-toplevel
from pcapkit.protocols.application.httpv2.protocol import HTTPv2 # isort: skip # pylint: disable=import-outside-toplevel

if isinstance(version, int) and not isinstance(version, bool):
if version == 1:
return HTTPv1
if version == 2:
return HTTPv2
raise ProtocolError(f'HTTP: invalid version: {version!r}')

@classmethod
def _dispatch_version(cls, data: 'bytes', length: 'int') -> 'Type[ProtocolBase]':
"""Pick the class that parses the HTTP message at the start of ``data``.
Expand Down Expand Up @@ -860,29 +886,3 @@ def _dispatch_version(cls, data: 'bytes', length: 'int') -> 'Type[ProtocolBase]'
if _test_http2_frame(data):
return HTTPv2
return Raw

@staticmethod
def _member_of_version(version: 'Any') -> 'Type[HTTP]':
"""The member a ``version`` of ``1`` or ``2`` names.

Args:
version: Version of HTTP.

Returns:
:class:`~pcapkit.protocols.application.httpv1.protocol.HTTPv1` or
:class:`~pcapkit.protocols.application.httpv2.protocol.HTTPv2`.

Raises:
ProtocolError: If ``version`` is neither, or is no :obj:`int`: a
:obj:`bool` or a :obj:`float` is refused even where it equals one.

"""
from pcapkit.protocols.application.httpv1.protocol import HTTPv1 # isort: skip # pylint: disable=import-outside-toplevel
from pcapkit.protocols.application.httpv2.protocol import HTTPv2 # isort: skip # pylint: disable=import-outside-toplevel

if isinstance(version, int) and not isinstance(version, bool):
if version == 1:
return HTTPv1
if version == 2:
return HTTPv2
raise ProtocolError(f'HTTP: invalid version: {version!r}')
54 changes: 27 additions & 27 deletions pcapkit/protocols/application/httpv1/protocol.py
Original file line number Diff line number Diff line change
Expand Up @@ -345,33 +345,6 @@ def _make_data(cls, data: 'Data_HTTP') -> 'dict[str, Any]': # type: ignore[over
'raw_header': getattr(data, 'raw_header', ()),
}

def _decode_text(self, text: 'bytes') -> 'tuple[str, Optional[str]]':
"""Decode start-line text, recording how to encode it back.

The text is decoded by :meth:`self.decode
<pcapkit.protocols.protocol.Protocol.decode>`. Where the result does
not encode back to ``text`` as UTF-8 -- which :meth:`make` uses by
default -- the detected charset is returned with it, or ``'latin-1'``
(decoding again) if that does not reproduce ``text`` either, so that the
original octets survive a rebuild.

Args:
text: Raw request URI or status message.

Returns:
The decoded text and the charset to encode it with, the latter
:obj:`None` for UTF-8.

"""
value = self.decode(text)
for charset in (None, detect(text)):
try:
if value.encode(charset or 'utf-8') == text:
return value, charset
except (LookupError, UnicodeError):
pass
return text.decode('latin-1'), 'latin-1'

def _read_http_header(self, header: 'bytes') -> 'tuple[Data_Header, OrderedMultiDict[str, str]]':
"""Read HTTP/1.* header.

Expand Down Expand Up @@ -640,3 +613,30 @@ def _read_field_line(self, line: 'bytes') -> 'tuple[str, str]':
if len(item) != 2:
raise ProtocolError('HTTP: invalid format')
return self.decode(item[0].strip()), self.decode(item[1].strip())

def _decode_text(self, text: 'bytes') -> 'tuple[str, Optional[str]]':
"""Decode start-line text, recording how to encode it back.

The text is decoded by :meth:`self.decode
<pcapkit.protocols.protocol.Protocol.decode>`. Where the result does
not encode back to ``text`` as UTF-8 -- which :meth:`make` uses by
default -- the detected charset is returned with it, or ``'latin-1'``
(decoding again) if that does not reproduce ``text`` either, so that the
original octets survive a rebuild.

Args:
text: Raw request URI or status message.

Returns:
The decoded text and the charset to encode it with, the latter
:obj:`None` for UTF-8.

"""
value = self.decode(text)
for charset in (None, detect(text)):
try:
if value.encode(charset or 'utf-8') == text:
return value, charset
except (LookupError, UnicodeError):
pass
return text.decode('latin-1'), 'latin-1'
34 changes: 17 additions & 17 deletions pcapkit/protocols/application/httpv2/protocol.py
Original file line number Diff line number Diff line change
Expand Up @@ -668,23 +668,6 @@ def _make_http_length(frame: 'bytes | Schema_FrameType', flags: 'Flags') -> 'int
return len(frame.fragment)
return len(frame.pack())

@staticmethod
def _read_http_flags(schema: 'Schema_FrameType') -> 'Data_Flags':
"""Read the flags of an HTTP/2 frame type that defines none.

Args:
schema: Parsed frame schema.

Returns:
Flags with the raw flags octet as ``__value__``.

"""
flags = Data_Flags()
flags.__update__({
'__value__': schema.__flags__,
})
return flags

def _read_http_none(self, schema: 'Schema_UnassignedFrame', *,
header: 'Schema_HTTP') -> 'Data_UnassignedFrame':
"""Read HTTP packet with unassigned type.
Expand Down Expand Up @@ -1692,6 +1675,23 @@ def _make_http_continuation(self, frame: 'Optional[Data_ContinuationFrame]' = No
fragment=fragment,
), flags

@staticmethod
def _read_http_flags(schema: 'Schema_FrameType') -> 'Data_Flags':
"""Read the flags of an HTTP/2 frame type that defines none.

Args:
schema: Parsed frame schema.

Returns:
Flags with the raw flags octet as ``__value__``.

"""
flags = Data_Flags()
flags.__update__({
'__value__': schema.__flags__,
})
return flags

def _import_next_layer(self, proto: 'int', length: 'Optional[int]' = None, *, # type: ignore[override]
packet: 'Optional[dict[str, Any]]' = None) -> 'ProtocolBase':
"""Import next layer extractor: a further whole frame, or the rest undissected.
Expand Down
94 changes: 47 additions & 47 deletions pcapkit/protocols/application/ospf/protocol.py
Original file line number Diff line number Diff line change
Expand Up @@ -421,53 +421,6 @@ def _make_data(cls, data: 'Data_OSPF') -> 'dict[str, Any]': # type: ignore[over
'trailer': data.get('trailer', b''),
}

def _read_id_numbers(self, id: 'bytes') -> 'IPv4Address':
"""Read router and area IDs.

Args:
id: ID bytes.

Returns:
Parsed IDs as an IPv4 address.

"""
#_byte = self._read_fileng(4)
#_addr = '.'.join(str(_) for _ in _byte)
return ipaddress.ip_address(id) # type: ignore[return-value]

def _make_id_numbers(self, id: 'IPv4Address | str | bytes | bytearray') -> 'bytes':
"""Make router and area IDs.

Args:
id: ID.

Returns:
ID bytes.

Raises:
FieldValueError: If ``id`` is a :obj:`bool` (see
:func:`~pcapkit.corekit.schema.ipaddress.parse_ip_address`).

Notes:
Latent rather than live: nothing in this module calls this method
(:meth:`make` builds ``router_id``/``area_id`` straight from its
own arguments), so the only caller is a unit test. It is routed
through :func:`~pcapkit.corekit.schema.ipaddress.parse_ip_address`
anyway, so that a future caller passing a :obj:`bool` gets
:exc:`~pcapkit.utilities.exceptions.FieldValueError` rather than a
silent ``0.0.0.1``.

The description below uses :attr:`self.__class__.__name__
<type.__name__>` rather than :attr:`self.alias
<pcapkit.protocols.protocol.Protocol.alias>`, because ``alias``
here reads ``self._version``, which :meth:`read` only assigns
from the wire -- unavailable to a construction-only instance that
never went through :meth:`read`.

"""
return cast('IPv4Address', parse_ip_address(
id, f'{self.__class__.__name__}: invalid ID', version=4)).packed

def _read_encrypt_auth(self, schema: 'Schema_CryptographicAuthentication') -> 'Data_CryptographicAuthentication':
"""Read Authentication field when Cryptographic Authentication is employed,
i.e. :attr:`~pcapkit.protocols.application.ospf.data.OSPF.autype` is ``2``.
Expand Down Expand Up @@ -546,3 +499,50 @@ def _make_encrypt_auth(self,
except KeyError as exc:
raise ProtocolError(f'OSPF: missing {exc} in auth_data: {auth_data!r}') from None
raise ProtocolError(f'OSPF: invalid type for auth_data: {auth_data!r}')

def _read_id_numbers(self, id: 'bytes') -> 'IPv4Address':
"""Read router and area IDs.

Args:
id: ID bytes.

Returns:
Parsed IDs as an IPv4 address.

"""
#_byte = self._read_fileng(4)
#_addr = '.'.join(str(_) for _ in _byte)
return ipaddress.ip_address(id) # type: ignore[return-value]

def _make_id_numbers(self, id: 'IPv4Address | str | bytes | bytearray') -> 'bytes':
"""Make router and area IDs.

Args:
id: ID.

Returns:
ID bytes.

Raises:
FieldValueError: If ``id`` is a :obj:`bool` (see
:func:`~pcapkit.corekit.schema.ipaddress.parse_ip_address`).

Notes:
Latent rather than live: nothing in this module calls this method
(:meth:`make` builds ``router_id``/``area_id`` straight from its
own arguments), so the only caller is a unit test. It is routed
through :func:`~pcapkit.corekit.schema.ipaddress.parse_ip_address`
anyway, so that a future caller passing a :obj:`bool` gets
:exc:`~pcapkit.utilities.exceptions.FieldValueError` rather than a
silent ``0.0.0.1``.

The description below uses :attr:`self.__class__.__name__
<type.__name__>` rather than :attr:`self.alias
<pcapkit.protocols.protocol.Protocol.alias>`, because ``alias``
here reads ``self._version``, which :meth:`read` only assigns
from the wire -- unavailable to a construction-only instance that
never went through :meth:`read`.

"""
return cast('IPv4Address', parse_ip_address(
id, f'{self.__class__.__name__}: invalid ID', version=4)).packed
Loading
Loading