Exercises — HTTP - 2 — multiplexing, header compression (HPACK), server push
Before we start, one shared vocabulary anchor so no symbol appears un-earned.

Level 1 — Recognition
L1.1
Q. A frame header is 9 bytes. List the five fields packed into it and give each field's bit-width.
Recall Solution
From the framing spec, the 9 bytes = 72 bits split as:
- Length — 24 bits
- Type — 8 bits
- Flags — 8 bits
- R (reserved) — 1 bit
- Stream ID — 31 bits
Check: bits bytes. ✅
L1.2
Q. Client-initiated streams use which parity of Stream ID, and server-pushed streams use which? Why does this rule exist at all?
Recall Solution
Client streams use odd IDs (1, 3, 5, …); server-pushed streams use even IDs (2, 4, 6, …). Why: both sides open streams on the same connection. If both could pick from the same pool, two independent choosers could pick the same number → collision. Splitting into odd/even is a zero-coordination way to guarantee the two never clash.
L1.3
Q. Match each frame type to its job: HEADERS, DATA, PUSH_PROMISE, RST_STREAM.
Recall Solution
HEADERS— carries the (HPACK-encoded) header block that begins a message.DATA— carries the body bytes.PUSH_PROMISE— announces a stream the server intends to push, reserving an even Stream ID.RST_STREAM— abruptly cancels one stream without touching the connection.
Level 2 — Application
L2.1
Q. The default max frame payload is bytes. Exactly how many bytes is that, and why cap it so low when the Length field can express up to ?
Recall Solution
bytes ( KiB). The Length field could hold up to bytes, but the default cap is deliberately smaller. Why: if one frame could be huge, sending it would hog the single TCP connection for a long stretch — reintroducing the very head-of-line stall multiplexing exists to kill. Capping at 16 KiB means frames from other streams can slip in between.
L2.2
Q. Encode the integer into a 5-bit prefix using HPACK prefix-integer encoding. How many bytes does it take?
Recall Solution
The rule: if store directly in the bits. Here , so . Since , we just write into the 5 bits. The 3 high bits of that byte are HPACK flag bits (they mark the representation type). One byte total. No continuation needed.
L2.3
Q. Encode the integer into a 5-bit prefix. Give the byte sequence.
Recall Solution
and , so the prefix overflows.
- Emit the prefix value (all 5 bits set).
- Remainder .
- Split into 7-bit groups, low group first, high bit = "more follow":
- , so first continuation byte .
- Next , so final byte (high bit clear = "last").
Byte sequence: 31, 154, 10 — three bytes.
L2.4
Q. The static table entry :method GET is index 2. Write the fully-indexed HPACK representation
of this header and state its size.
Recall Solution
Fully indexed = leading 1 bit, then the index in a 7-bit prefix. Index , so
it fits directly. The byte is 1 followed by 0000010:
One byte encodes the entire :method: GET line.
Level 3 — Analysis
L3.1
Q. A page needs 4 origins. TCP handshake ≈ 1 RTT and TLS ≈ 2 RTTs. Under HTTP/1.1 the browser opens 6 connections to the single busiest origin but each origin is contacted independently; take the per-origin cost as since connections to one origin share nothing faster. Compare setup RTTs: HTTP/1.1 (6 cold connections to one origin) vs HTTP/2 (1 connection). Given 1 RTT = 40 ms, give both setup times for that one origin.
Recall Solution
Per connection setup RTTs.
- HTTP/1.1, 6 cold connections opened for one origin: the handshakes can overlap in time, but each connection still pays RTTs and each starts with a tiny cold congestion window. Setup RTTs per connection RTT ms.
- HTTP/2, 1 connection: RTT ms — same handshake latency, but only one warmed congestion window instead of six cold ones.
The real win isn't the handshake number (both are RTTs of setup); it's that HTTP/2 pays it once and then reuses one properly-grown congestion window, while HTTP/1.1's six windows each start cold and compete. Total handshake work: HTTP/1.1 RTT-connections of setup cost; HTTP/2 . Ratio less setup cost.
L3.2
Q. Explain, using a lost-packet scenario, why HTTP/2 multiplexing does not eliminate head-of-line blocking, and which layer it lives at.
Recall Solution
Setup: three streams (1, 3, 5) interleave DATA frames over one TCP connection. Suppose one TCP
segment carrying part of stream 1 is lost on the network.
- TCP guarantees the application receives bytes in order. So TCP buffers everything that arrived after the lost segment — including the perfectly-intact frames for streams 3 and 5 — and refuses to hand them up until the lost segment is retransmitted (≈ 1 RTT later).
- At the HTTP/2 layer, streams are independent — nothing there blocks. But the block happens one layer below, at TCP (transport).
Conclusion: HTTP/2 removes application-layer HOL blocking but not transport-layer HOL blocking. HTTP/3 over QUIC gives each stream its own ordering, so a lost packet stalls only its own stream — see TCP for why in-order delivery forces the wait.
L3.3
Q. A 200-byte cookie is sent on 100 requests. Estimate total header bytes for that cookie under (a) HTTP/1.1 (verbatim each time) and (b) HTTP/2 HPACK (literal-with-indexing once, indexed after). Take the indexed reference as 1 byte.
Recall Solution
(a) HTTP/1.1: bytes. (b) HPACK: request 1 sends the full literal ( bytes) and stores it in the dynamic table; requests 2–100 each send a 1-byte index → bytes. Savings: bytes, a shrink.
Level 4 — Synthesis
L4.1
Q. Design the frame sequence for: client GET /index.html; server wants to push /style.css.
List every frame with its Stream ID, in order, and justify each ID.
Recall Solution
HEADERSon Stream 1 — client's request for/index.html. Stream 1 is odd → client-initiated. ✅PUSH_PROMISEon Stream 1 — announced on the requesting stream, reserving Stream 2 (even → server-initiated) for the pushed resource.HEADERSon Stream 2 — response headers for/style.css.DATAon Stream 2 — the CSS bytes.HEADERSon Stream 1 — response headers for/index.html.DATAon Stream 1 — the HTML bytes.
Key point: the promise rides the client's stream (1), but the pushed content lives on a fresh
even stream (2), so it never collides with any odd client stream. When the parser discovers the
<link> to style.css, it's already arriving/cached → round trip saved.
L4.2
Q. You must compress the header list below into total HPACK bytes. Assume all three are in the
static/dynamic table with the indices shown, all fit a 7-bit prefix, and each fully-indexed header
costs 1 byte. Give the total.
:method GET (idx 2), :path / (idx 4), :scheme https (idx 7)
Recall Solution
Each index , so each is a single fully-indexed byte: 1 || 7-bit index.
- idx 2 → 1 byte
- idx 4 → 1 byte
- idx 7 → 1 byte
Total bytes for three whole header lines. (Compare: writing them as text would be dozens of bytes.)
Level 5 — Mastery
L5.1
Q. A malicious page tries a CRIME-style attack: it injects a header whose value it controls and hopes the compressed size reveals bytes of a secret cookie. Explain precisely which HPACK design choices defeat this, and name the transport layer that would still need protection.
Recall Solution
Two HPACK properties break the attack:
- No adaptive entropy coding across attacker+secret. HPACK's string compression uses a static Huffman table fixed in the spec, not an adaptive coder that shrinks when attacker text matches secret text. So the compressed length no longer signals "your guess matched a secret byte."
- Indexing is per-whole-header, table-based. A secret cookie either is a table index (opaque, fixed 1 byte) or a literal — it isn't mixed byte-by-byte with attacker input the way gzip's sliding window would mix them.
Together these remove the length-vs-guess correlation CRIME exploited. The layer still needing its own protection is TLS (encryption/integrity of the whole connection); HPACK protects the compression side, TLS protects the confidentiality side. See CRIME-and-BREACH-attacks.
L5.2
Q. Prove the prefix-integer round-trips: encode in a 5-bit prefix, then decode the byte sequence back and confirm you recover .
Recall Solution
Encode (from L2.3): prefix ; remainder → bytes 31, 154, 10.
Decode: prefix is all-ones () → start accumulator at , then read continuation bytes:
- byte : high bit set (subtract ), add . Accumulator . More follow.
- byte : high bit clear → add , then stop. Accumulator . ✅
Recovered — the encoding is lossless. This is the same varint idea: 7 data bits per byte, top bit = continuation flag.
L5.3
Q. Given the 24-bit Length field, and knowing frames default to bytes, how many default-max frames are needed to carry a 1 MiB (-byte) response body, and would a single frame of that size even be representable by the Length field?
Recall Solution
Default frame payload bytes. Body bytes.
Number of frames frames exactly.
Representable in one frame? The Length field holds up to bytes, and
, so yes, a single 1 MiB frame is representable — but only if the
peer raised SETTINGS_MAX_FRAME_SIZE. By default you'd send frames of KiB so no single
frame monopolises the connection.
Recall Self-test one-liners
Odd Stream IDs belong to whom? ::: The client (client-initiated streams). Even Stream IDs belong to whom? ::: The server (pushed streams). Default max frame payload in bytes? ::: 16384 (). HPACK's three mechanisms? ::: Static table, dynamic table, static Huffman coding. Which HOL blocking does HTTP/2 still suffer? ::: Transport-layer (TCP in-order delivery). The modern replacement for server push? ::: 103 Early Hints + link rel=preload.