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()giveHTTPv1orHTTPv2, never an instance of this class (#2524).versionnames 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 adict. 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, soread()adds them tolength; otherwiseProtocol.__init__would report the tail of the preface as the packet payload.
- classmethod id()[source]¶
Index ID of the protocol.
The family base lists its members’ names alone, as
IP.id()does:HTTP/1for RFC 9112’s HTTP/1.1 and the versions before it, and RFC 9113’sHTTP/2. A dispatched layer is the member_dispatch_version()picks, so each name selects its own version, and theHTTPkey selects either through this tuple.- Return type:
- 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:
- 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:
- Return type:
_ST- Returns:
The header schema the versioned
makeconstructs.- Raises:
ProtocolError – If
versionis missing or names neither member, or ifprefaceis requested for a version other than HTTP/2.
- classmethod from_data(data, **kwargs)[source]¶
Create protocol instance from data.
- Parameters:
**kwargs (
Any) – AsProtocol.from_data.
- Return type:
- Returns:
Protocol instance; on this class, an instance of the member whose data model
datais (#2524).- Raises:
ProtocolError – If
datais neither member’s data model, on this class. Adictis refused: it carries no version, and no key picks a member (#2574).
- classmethod __index__()[source]¶
Numeral registry index of the protocol.
- Return type:
- Returns:
Numeral registry index of the protocol in IANA service names and port numbers: TCP port 80, i.e.
TCP.httpas anint(The Registry Index). The versioned classes inherit it.
- __init__(file=None, length=None, *, version=None, **kwargs)[source]¶
Initialisation.
- Parameters:
version (
Literal[1,2] |None) – Version of HTTP,1or2: the member__new__()picks. Building from fields, it must be this class’s own. Parsing, it is handed on unread.**kwargs (
Any) – AsProtocol.__init__.
- Raises:
ProtocolError – If, building from fields,
versionnames 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 ifprefaceis asked of HTTP/1.
- static __new__(cls, file=None, length=None, **kwargs)[source]¶
Create the member a construction of this class describes.
The
versionkeyword 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:
- Return type:
- Returns:
An uninitialised instance of the member.
- Raises:
ProtocolError – If
versionnames 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
datafor protocol construction.- Parameters:
data (
_PT) – protocol data- Return type:
- Returns:
Key-value pairs for protocol construction, including the
versionthatmake()dispatches on.- Raises:
ProtocolError – If
datais 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 subclassStructError, whoseeofNoPayloadhandling 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 itRawunderbeholderinstead. The final HTTP/2 trial absorbs it only as a backstop:HTTPv2.unpackandFieldBase.lengthalready raiseProtocolErrorfor a short buffer.- Parameters:
length (
int) – Length of packet data.- Keyword Arguments:
**kwargs (
Any) – Arbitrary keyword arguments.- Return type:
- 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
ProtocolErrorfrom a trial parse falls through to the next version, and the final HTTP/2 trial also absorbsstruct.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: h2cexchange (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 the101 Switching Protocolsresponse, 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
Rawan escapingProtocolErrorturns into underbeholder(). 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 –DATAon stream 0, a protocol error under RFC 9113 Section 6.1 – are not guessed as HTTP/2.
- 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 isHTTPv1orHTTPv2, never this base (#2524).It is the counterpart of
IGMP._dispatch, named apart because_dispatchalready namesAppType._dispatch, the port registries’ lookup hook.
- pcapkit.protocols.application.http.protocol.test_start_line(data)[source]¶
Whether
dataopens 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:
- Returns:
Whether the payload’s first line is a
request-lineor astatus-line(RFC 9112 Section 2.1).
Note
It accepts exactly what
HTTPv1._read_http_headeraccepts –_RE_METHODwith_RE_VERSIONfor a request,_RE_VERSIONwith_RE_STATUSfor a response – which is whyprotocolimports 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_parserpins 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.readdoes, the preface’s header is exactlyPRI * 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
PRIis registered as one no actual client uses (RFC 7540 Section 11.6), so a prefix match identifies HTTP/2, andHTTP._guess_version()tests it before any parse.
Footnotes