HTTP - Hypertext Transfer Protocol¶
pcapkit.protocols.application.http contains
HTTP,
which is a base class for Hypertext Transfer
Protocol (HTTP) [*] family, eg.
HTTP/1.*
and HTTP/2,
and test_start_line(),
which tells whether a payload opens with an HTTP/1.* start line.
- class pcapkit.protocols.application.http.HTTP(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]
- read(length=None, *, version=None, preface=False, **kwargs)[source]¶
Read (parse) packet data.
- Parameters:
- Return type:
TypeVar(_PT, bound= Data)- Returns:
Parsed packet data. When the payload opens with the HTTP/2 connection preface, its 24 octets are recorded as
preface.
- make(version=1, *, preface=False, **kwargs)[source]¶
Make (construct) packet data.
- Parameters:
- Return type:
TypeVar(_ST, bound= Schema)- Returns:
The header schema the versioned
makeconstructs.- Raises:
ProtocolError – If
versionis unknown, or ifprefaceis requested for a version other than HTTP/2.
- _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. Trying each version in turn and taking whichever parser does not object answers “did a parser accept this?” where the question is “what is this?”: the HTTP/2 preface reads as a frame whose declared length is 5,265,993 (
b'PRI'as a 24-bit integer), and text that is not HTTP at all can be accepted the same way.- Parameters:
length (
int) – Length of packet data.- Keyword Arguments:
**kwargs – 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
Two things this deliberately does not decide, because one payload with no flow context cannot:
An
Upgrade: h2cexchange (RFC 7540 Section 3.2, deprecated but not removed by RFC 9113 Section 3.1) stays HTTP/1.1 here, and is correctly HTTP/1.1: on the wire the upgrade request and its101 Switching Protocolsresponse are HTTP/1.1 messages and parse as such. The switch takes effect only after the101, so deciding that later segments of the same connection are HTTP/2 needs per-connection state keyed on the 4-tuple, and this method is handed a single payload with no such context. Recognising theUpgrade: h2cfield is possible; acting on it is not, so it is left alone rather than half-implemented.A mid-stream segment – a bare HTTP/2 frame header with no preface ahead of it, or an opaque HTTP/1 message body – is genuinely undecidable from one payload, and the honest answer is the
Rawthat an escapingProtocolErrorbecomes underbeholder(). The nine-octet frame header is therefore never used to identify HTTP/2 ahead of HTTP/1: as identification it misfires on binary HTTP/1 bodies, which is precisely how garbage text was classified HTTP/2 to begin with. A bare frame is parsed as HTTP/2 only by the fall-through below, and only if its header is one a conforming sender could write (see_test_http2_frame()): a frame type RFC 9113 defines, no undefined flag, and a stream that type may be sent on. Nine zero octets – aDATAframe on stream 0, a protocol error under RFC 9113 Section 6.1 – are not guessed as HTTP/2.
- _preface_length = 0¶
Octets consumed ahead of the identified version’s own header – in practice the 24-octet HTTP/2 connection preface, when
_guess_version()identified one and parsed the frame that follows it. They belong to this packet’s header rather than to its payload, soread()adds them tolength; otherwiseProtocolBase.__init__’sself._info.__update__(packet=self.packet.payload)would slice the payload from octet 9 of a buffer whose frame starts at octet 24 and report the tail of the preface as packet payload.
- pcapkit.protocols.application.http.test_start_line(data)[source]¶
Whether
dataopens with an HTTP/1.* start line.This is a classification predicate and parses nothing: it answers “is this HTTP/1?” for
HTTP._guess_version. Classifying by trial-parsing every version in the family would decide a payload that is not HTTP at all by which parser happened to fail 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
The acceptance rule is deliberately the same one
HTTP._read_http_headerapplies –_RE_METHODwith_RE_VERSIONfor a request,_RE_VERSIONwith_RE_STATUSfor a response – which is why those three patterns are defined beside this predicate andhttpv1imports them from here. The two must accept the same start lines: a predicate looser than the parser classifies payloads the parser then refuses, and one tighter than the parser hands real HTTP/1 to a later arm.test_start_line_predicate_agrees_with_the_httpv1_parserpins that agreement.The checks the parser performs before those patterns are mirrored too, and this is not pedantry – they are the whole reason the HTTP/2 connection preface is not claimed here.
PRI * HTTP/2.0\r\n\r\nSM\r\n\r\nis deliberately a well-formed HTTP/1.1 request line (RFC 9113 Section 3.4), so a predicate that tested only the first line would answerTruefor it. Split at the header/body separator first, asHTTP.readdoes, and the preface’s header is exactlyPRI * HTTP/2.0(_HTTP2_PREFACE_HEADER), which the parser refuses by name and so does this. Any other header with no CRLF in it 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 carries only two tokens and so is not recognised here either, matching the parser, which raises on fewer than three.
- pcapkit.protocols.application.http._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). A client opens every HTTP/2 connection – with prior knowledge, over TLS, or after an upgrade – by sending exactly these 24 octets, and the sequence is designed to be identifiable without parsing: it is a well-formed HTTP/1.1 request line whose method
PRIis reserved and permanently unregistered, so no valid HTTP/1 message can begin with it and a prefix compare cannot false-positive on one. That makes it a positive identification rather than a heuristic, which is whyHTTP._guess_version()tests it before attempting any parse.
Footnotes