fn check_extensions_against_status_on_encode(
status: Option<u64>,
extension_headers_len: u64,
) -> Result<(), CodecError>Expand description
Refuse an object that would be written with extension headers beside a status other than Normal.
Draft-15 Section 10.2.1.2: “Any Object with status Normal can have extension headers. If an endpoint receives extension headers on Objects with status that is not Normal, it MUST close the session with a PROTOCOL_VIOLATION.”
Wider than the rule drafts 11 through 14 carry. There, at draft-14 Section 10.2.1.2, the sentence read “Any Object may have extension headers except those with Object Status ‘Object Does Not Exist’”, which left extensions beside End of Group and End of Track legal; draft-15 is where the single exception became the general case. Using the earlier wording here would let an End of Group object carry metadata draft-15 says to close the session over.
Called from DatagramHeader::encode_checked and nowhere else, and
deliberately so. A frame carrying extensions beside a non-Normal status is
well formed — every length is honest and every field parses — so a decoder
can hand it back intact, and a tool that reproduces a capture has to.
Refusing it on decode would make a captured violation unreadable, which
loses the one artifact anybody debugging it needs.
The same argument bars it from a carrier’s only writer.
SubgroupObjectReader::write_object and
FetchObjectReader::write_object_header are the sole way to write their
objects, so a refusal there would leave a captured violation impossible to
re-emit; the corpus ships exactly such a subgroup frame. A datagram is the
one carrier with two encoders, so encode_checked can refuse while
DatagramHeader::encode still reproduces bytes verbatim. That is the
whole of the rule: opt-in strictness where an unchecked path exists, and a
predicate — SubgroupObject::extensions_permitted,
SubgroupObjectMeta::extensions_permitted,
DatagramHeader::extensions_permitted,
FetchObjectHeader::extensions_permitted — everywhere else.
That is why drafts 11 through 14 look different and should stay different: they state only the narrow Object Does Not Exist form, they apply it on both sides, and no vector exercises it. Drafts 15 through 19 state the broad form and enforce it on encode alone. The split is intentional, not an inconsistency to harmonise away.
status is the code the object resolves to, not the field as it appears on
the wire. An object whose framing omits the status field has status Normal —
on a subgroup or fetch stream because its payload length is non-zero, on a
datagram because its type byte leaves the status bit clear — and such an
object may carry extensions. Passing None says exactly that.