HTTP - Hypertext Transfer Protocol

Removed in version 1.6.0: pcapkit.protocols.application.http._HTTP2_PREFACE and pcapkit.protocols.application.http.test_start_line, with no alias: they are in this module, and pcapkit.protocols.application.http exports the protocol class alone (#2534).

pcapkit.protocols.application.http.protocol contains HTTP, which is a base class for Hypertext Transfer Protocol (HTTP) [*] family, e.g. HTTP/1.* and HTTP/2, and test_start_line(), which tells whether a payload opens with an HTTP/1.* start line.

Changed in version 1.6.0: A payload a transport port binds to HTTP, and constructing or rebuilding that class, give HTTPv1 or HTTPv2 rather than an instance of it, so the data sits under httpv1 or httpv2 rather than http (#2524). Building it from fields takes version and is refused without it, where it built HTTP/1.*, and its from_data() refuses a dict, where it read the receipt or sid key (#2628).

class pcapkit.protocols.application.http.protocol.HTTP[_PT: Data, _ST: Schema](file=None, length=None, **kwargs)[source]

Bases: 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]

It dispatches on both paths: constructing it, from octets or from fields, and from_data() give HTTPv1 or HTTPv2, never an instance of this class (#2524). version names the member; without it, octets give the one _dispatch_version() identifies, and fields are refused, since no field picks a member (#2574). from_data() gives the member whose data model it is handed, and refuses a dict. See __new__().

_preface_length = 0

Octets consumed ahead of the identified version’s header – the HTTP/2 connection preface, when _guess_version() found one. They are header, so read() adds them to length; otherwise Protocol.__init__ would report the tail of the preface as the packet payload.

property name: Literal['Hypertext Transfer Protocol']

Name of current protocol.

property alias: Literal['HTTP/0.9', 'HTTP/1.0', 'HTTP/1.1', 'HTTP/2']

Acronym of current protocol.

property length: int

Header length of current protocol.

property version: Literal['0.9', '1.0', '1.1', '2']

Version of current protocol.

classmethod id()[source]

Index ID of the protocol.

The family base lists its members’ names alone, as IP.id() does: HTTP/1 for RFC 9112’s HTTP/1.1 and the versions before it, and RFC 9113’s HTTP/2. A dispatched layer is the member _dispatch_version() picks, so each name selects its own version, and the HTTP key selects either through this tuple.

Return type:

tuple[Literal['HTTP/1'], Literal['HTTP/2']]

Returns:

Index ID of the protocol; the versioned classes narrow it to their own name.

Changed in version 1.6.0: ('HTTP/1', 'HTTP/2'), where it was ('HTTP', 'HTTPv1', 'HTTPv2') (#2524).

read(length=None, *, version=None, preface=False, **kwargs)[source]

Read (parse) packet data.

Parameters:
  • length (int | None) – Length of packet data.

  • version (Literal[1, 2] | None) – Version of HTTP.

  • preface (bool) – Construction keyword of make(); whether a preface is parsed is decided by the octets alone.

  • **kwargs (Any) – Arbitrary keyword arguments.

Return type:

_PT

Returns:

Parsed packet data. When the payload opens with the HTTP/2 connection preface, its 24 octets are recorded as preface.

make(version=None, *, preface=False, **kwargs)[source]

Make (construct) packet data.

Parameters:
  • version (Literal[1, 2] | None) – Version of HTTP, 1 or 2. There is no default: no field picks a member (#2574).

  • preface (bool) – Whether the HTTP/2 connection preface goes ahead of the frame; pack() writes it.

  • **kwargs (Any) – Arbitrary keyword arguments.

Return type:

_ST

Returns:

The header schema the versioned make constructs.

Raises:

ProtocolError – If version is missing or names neither member, or if preface is requested for a version other than HTTP/2.

classmethod from_data(data, **kwargs)[source]

Create protocol instance from data.

Parameters:
Return type:

Self

Returns:

Protocol instance; on this class, an instance of the member whose data model data is (#2524).

Raises:

ProtocolError – If data is neither member’s data model, on this class. A dict is refused: it carries no version, and no key picks a member (#2574).

classmethod __index__()[source]

Numeral registry index of the protocol.

Return type:

int

Returns:

Numeral registry index of the protocol in IANA service names and port numbers: TCP port 80, i.e. TCP.http as an int (The Registry Index). The versioned classes inherit it.

Changed in version 1.6.0: Returns TCP port 80, where it raised IntError (#2627).

__init__(file=None, length=None, *, version=None, **kwargs)[source]

Initialisation.

Parameters:
  • file (IO[bytes] | bytes | None) – Source packet stream.

  • length (int | None) – Length of packet data.

  • version (Literal[1, 2] | None) – Version of HTTP, 1 or 2: the member __new__() picks. Building from fields, it must be this class’s own. Parsing, it is handed on unread.

  • **kwargs (Any) – As Protocol.__init__.

Raises:

ProtocolError – If, building from fields, version names neither member, or is not this class’s own version: that of the member it is or derives from, which a subclass outside the members does not have; or if preface is asked of HTTP/1.

static __new__(cls, file=None, length=None, **kwargs)[source]

Create the member a construction of this class describes.

The version keyword names the member, on either path. Without it, parsing octets gives the member _dispatch_version() identifies, since HTTP octets carry their own version, and building from fields is refused, since no field picks a member (#2574). That member’s __init__ then runs with the same arguments. A subclass of this class is created as itself.

Parameters:
  • file (IO[bytes] | bytes | None) – Source packet stream, read here without being consumed.

  • length (int | None) – Length of packet data.

  • **kwargs (Any) – Arbitrary keyword arguments.

Return type:

HTTP

Returns:

An uninitialised instance of the member.

Raises:

ProtocolError – If version names neither member; or, without it, if building from fields, or if the octets are neither version’s, including an HTTP/2 connection preface with no frame after it.

classmethod _make_data(data)[source]

Create key-value pairs from data for protocol construction.

Parameters:

data (_PT) – protocol data

Return type:

dict[str, Any]

Returns:

Key-value pairs for protocol construction, including the version that make() dispatches on.

Raises:

ProtocolError – If data is neither HTTP/1.* nor HTTP/2 data.

_guess_version(length, **kwargs)[source]

Identify the HTTP version of the payload, and parse it with that version.

The payload is identified first and trial-parsed only as a last resort. A parser accepting a payload does not make it that version: the HTTP/2 preface reads as a frame of declared length 5,263,945 (b'PRI' as a 24-bit integer), and non-HTTP text can be accepted the same way.

Neither HTTP/1 arm absorbs struct.error, so it and its subclass StructError, whose eof NoPayload handling reads, reach the caller. Absorbed on the trial arm, they would hand valid HTTP/1 to the HTTP/2 arm, mislabelling it; escaping, they make it Raw under beholder instead. The final HTTP/2 trial absorbs it only as a backstop: HTTPv2.unpack and FieldBase.length already raise ProtocolError for a short buffer.

Parameters:

length (int) – Length of packet data.

Keyword Arguments:

**kwargs (Any) – Arbitrary keyword arguments.

Return type:

HTTP

Returns:

Parsed packet data.

Raises:

ProtocolError – If no version in the family accepts the payload, or if an identified version’s own parser refuses it. Only a ProtocolError from a trial parse falls through to the next version, and the final HTTP/2 trial also absorbs struct.error; any other exception from either HTTP/1 arm, or from that HTTP/2 trial, propagates unconverted.

Note

One payload with no flow context cannot decide these, so this does not:

  • An Upgrade: h2c exchange (RFC 7540 Section 3.2, deprecated by RFC 9113 Section 3.1) parses as the HTTP/1.1 it is on the wire. HTTP/2 starts only after the 101 Switching Protocols response, and telling that needs per-connection state keyed on the 4-tuple.

  • A mid-stream segment – a bare HTTP/2 frame header with no preface, or an opaque HTTP/1 body – is undecidable, and becomes the Raw an escaping ProtocolError turns into under beholder(). A frame header never identifies HTTP/2 ahead of HTTP/1, as it misfires on binary HTTP/1 bodies and garbage text. A bare frame is parsed as HTTP/2 only by the fall-through, and only if _test_http2_frame() accepts its header, so nine zero octets – DATA on stream 0, a protocol error under RFC 9113 Section 6.1 – are not guessed as HTTP/2.

static _member_of_version(version)[source]

The member a version of 1 or 2 names.

Parameters:

version (Any) – Version of HTTP.

Return type:

Type[HTTP]

Returns:

HTTPv1 or HTTPv2.

Raises:

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

classmethod _dispatch_version(data, length)[source]

Pick the class that parses the HTTP message at the start of data.

The payload is identified before any class is built, as _guess_version() identifies it: the connection preface, or else a frame header a conforming sender could write, is HTTP/2, and an HTTP/1 start line is HTTP/1. A transport layer whose port binds this class parses its payload with the class this returns, so a dispatched layer is HTTPv1 or HTTPv2, never this base (#2524).

It is the counterpart of IGMP._dispatch, named apart because _dispatch already names AppType._dispatch, the port registries’ lookup hook.

Parameters:
  • data (bytes) – Payload of the transport layer, starting at the HTTP message.

  • length (int) – The HTTP message’s length, in octets.

Return type:

Type[ProtocolBase]

Returns:

HTTPv1 or HTTPv2 for the version the payload identifies as; Raw for one it does not, which then keeps its octets undissected.

Note

A connection preface with no frame after it, which a client may send in a segment of its own, is Raw here: the HTTP/2 data model holds one frame per packet, with its frame header fields required, and HTTPv2 refuses a frameless segment.

pcapkit.protocols.application.http.protocol.test_start_line(data)[source]

Whether data opens with an HTTP/1.* start line.

A classification predicate for HTTP._guess_version; it parses nothing. Trial-parsing every version instead would classify a non-HTTP payload by which parser failed less loudly.

Parameters:

data (bytes) – Payload to classify.

Return type:

bool

Returns:

Whether the payload’s first line is a request-line or a status-line (RFC 9112 Section 2.1).

Note

It accepts exactly what HTTPv1._read_http_header accepts – _RE_METHOD with _RE_VERSION for a request, _RE_VERSION with _RE_STATUS for a response – which is why protocol imports those patterns from here. A looser predicate classifies payloads the parser refuses; a tighter one hands real HTTP/1 to a later arm. test_start_line_predicate_agrees_with_the_httpv1_parser pins the agreement.

It mirrors the parser’s earlier checks too, which keep out the HTTP/2 connection preface, itself a well-formed HTTP/1.1 request line (RFC 9113 Section 3.4). Split at the header/body separator, as HTTPv1.read does, the preface’s header is exactly PRI * HTTP/2.0 (_HTTP2_PREFACE_HEADER), which both refuse by name. Any other header with no CRLF is a start line with zero field lines, which RFC 9112 Section 2.1 allows and both accept (#1041).

An HTTP/0.9 request line has only two tokens, so both refuse it.

pcapkit.protocols.application.http.protocol._HTTP2_PREFACE = b'PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n'

The HTTP/2 connection preface (RFC 9113 Section 3.4), which opens every HTTP/2 connection. Its method PRI is registered as one no actual client uses (RFC 7540 Section 11.6), so a prefix match identifies HTTP/2, and HTTP._guess_version() tests it before any parse.

Footnotes