Documentation
¶
Overview ¶
Command headonly rewrites the head of a document and passes the body through without parsing it.
$ headonly page.html head 114 bytes, rewritten to 116; body 200034 bytes, copied stopped at <p>, the first element that cannot be in a head saved the body was never parsed
Not parsing the body is worth two orders of magnitude ¶
A rewriter runs to Close and cannot be switched off part way, so "only the head" looks like it has to mean handlers that check a flag and do nothing. It does not. A handler can stop the rewrite by returning an error, and what has reached the destination at that point is documented to be byte for byte what a fresh rewriter produces from that much input - not a truncation. The element's lolhtml.SourceLocation gives the offset in the input where it began, so the rest of the input is exactly what still has to be sent, and it can be copied.
Measured on a document with a 114-byte head, fastest of twenty runs:
body size stop and copy handlers gated off a plain no-handler pass 100 bytes 6µs 7µs 1µs 10 KB 8µs 99µs 20µs 200 KB 16µs 1.842ms 375µs
A hundred and fifteen times faster than gating at 200 KB, and twenty-three times faster than passing the body through a rewriter with no handlers at all - because it does not pass the body through anything. The outputs are identical, which the tests assert rather than assume.
Where the head ends when nothing says so ¶
Stopping at <body> is not enough, because a document need not spell one: `<html><head><title>T </title><p>x` has a body in the tree and no body tag in the source, and a rewriter reports the source. So the rule is the specification's - stop at the first element that cannot appear in a head:
base link meta noscript script style template title
Anything else ends the head, and <body> is only the most explicit case of it. A document that is nothing but head elements has no body to copy, which is reported rather than assumed.
The elements *inside* a template are the exception, and they have to be counted out. A template's contents are an inert fragment rather than content of the document, so a <div> in one is not a div in the head - but a handler on "*" is shown it like any other element, and taking it for the end of the head stopped the rewrite at the template's first child. What follows in the head is then never rewritten, and the report says the head ended at an element the head still contains.
What it costs to not parse something ¶
The bytes have to be available from the offset, so this reads the document into memory. A stream can do the same by keeping what it has read past the stop point, which is bounded by one write: the stop is reported from Write, so at most that write's worth of input is past the point where the copy has to begin.
And the body is not checked. Nothing in it is parsed, so nothing in it is validated - a body that would have failed a sanitiser passes through untouched. That is the trade: this is for rewrites whose subject is the head, and a rewrite that has anything to say about the body has to read the body.