Reading — step 1 of 5
Read
Parsing Headers
After the request line come the headers — the request's metadata, one Name: value per line, ended by one blank line. The format looks trivial, and the ways it isn't trivial are precisely the parts exercises (and attackers) test. This lesson builds a header parser with the three normalization rules every real server applies.
The shape on the wire
GET /api/users HTTP/1.1\r\n
Host: example.com\r\n
Content-Type: application/json\r\n
Content-Length: 27\r\n
\r\n ← blank line: headers end here
Per line: a field name, a colon, optional surrounding whitespace, the value, CRLF. The blank line (\r\n alone) is load-bearing — it's the only signal that headers are over and the body (if any) begins. A parser loops: read line; blank → done; otherwise split at the first colon.
Rule 1: names are case-insensitive; values are not
Content-Length, content-length, and CONTENT-LENGTH are the same header — the spec says so, and clients exercise the freedom. The standard implementation move is to normalize names to lowercase at parse time and store them that way, so every later lookup is one exact-match. Values keep their case untouched — Content-Type: TEXT/HTML normalized to lowercase would corrupt values where case matters (ETags, base64 credentials).
Rule 2: trim the value, split at the FIRST colon
Host: example.com → name host, value example.com — optional whitespace around the value is the sender's whim, trimmed by you. And first colon only:
Referer: http://example.com/page
Split at every colon and this shatters. Name is everything before the first colon; value is everything after it (trimmed). One more validation with security teeth: no whitespace allowed before the colon — Content-Length : 5 is malformed (400), because parsers disagreeing about that space is an actual request-smuggling vector. Strictness at boundaries, again.
Rule 3: duplicates happen — have a policy
The same header can legally appear twice. The general rule: equivalent to one header with comma-joined values (Accept: a + Accept: b ≡ Accept: a, b). But Set-Cookie breaks the rule (cookies can contain commas — it must stay a list), and a duplicated Content-Length with different values is an attack signature (smuggling again) that real servers reject outright. Your exercise specs its policy — comma-join, keep-list, or reject — and the meta-lesson is that you need a policy: the parser that silently keeps "the last one" has made a security decision by accident.
The headers that run the show
Parsing is generic; a few names steer the whole server, worth knowing as you store them: Host (mandatory in HTTP/1.1 — its absence is a 400; virtual hosting hangs off it), Content-Length (how many body bytes follow — next lesson's whole subject), Connection (keep-alive vs close — chapter 3), Content-Type, and Transfer-Encoding (the Content-Length alternative with its own lesson). Your parsed, normalized header map is the input to every remaining chapter — build it boringly correct.
Your exercise: Parse and Normalize Headers
Raw header blocks in; normalized name→value map out (or 400 for malformed lines), per the spec. The ladder: basic pairs → case normalization → whitespace trimming → colon-in-value → duplicate handling per spec → space-before-colon rejection → missing blank line. Every rule above is one test and a few lines of code — and jointly they're the difference between "parses my test request" and "parses the internet."
Discussion
Ask a question, share an insight, or help someone who’s stuck.
Sign in to post a comment or reply.
Loading…