Skip to content
Lesson 3 of 16

Step 1 of 5 · Reading · ~3 min

Read

Connection & Parsing

Reading the Request Body

The headers end at an empty line, and then comes the body. How many bytes is it? TCP will not tell you: it delivers a stream with no "end of message" marker, and the next request may already be queued behind this one. HTTP solves the framing problem with two mechanisms. Your program will pick the right one, extract the body, and report how many bytes it left unread.

Mechanism 1: Content-Length

The sender counts the body and says so:

POST /notes HTTP/1.1\r\n
Host: a.test\r\n
Content-Length: 11\r\n
\r\n
hello world            <- exactly 11 bytes, no terminator
GET /next HTTP/1.1\r\n <- the NEXT request, already on the wire

Read exactly 11 bytes. Read 10 and the last byte is parsed as the start of the next request line. Read 12 and you swallow the G of the next request. Both bugs only appear once a client reuses its connection, which is why they survive single-shot tests. Validate the number first: digits only. -5, +5, 0x10, 5 5 and the empty string are all malformed, and two Content-Length headers that disagree are an attack signature, so the answer for any of those is 400 bad-length. Two identical ones are harmless.

Mechanism 2: chunked

When the sender does not know the size up front (streaming, compression on the fly, a proxy) it sends Transfer-Encoding: chunked, and the body becomes a sequence of self-describing pieces. This is the MDN example, byte for byte:

7\r\n
Mozilla\r\n
11\r\n
Developer Network\r\n
0\r\n
\r\n

Each chunk is: the size in hexadecimal, optional ;extension, CRLF, exactly that many data bytes, CRLF. A chunk of size 0 ends the body, and it may be followed by trailer header lines, then a final empty line. Decoded, the body above is MozillaDeveloper Network. Note 11 is seventeen, not eleven.

Two properties make this a parser and not a split:

  • Data is counted, never scanned. A chunk may contain CRLF. If you switch to line reading inside the data you cut the payload in half. Only the size line is a line.
  • The CRLF after the data is a separator, not payload. If the two bytes there are not CRLF, the framing has been lost: 400 bad-chunk.

When both headers are present

A request with Content-Length and Transfer-Encoding is the classic smuggling primitive: a front end that obeys one and a back end that obeys the other see different request boundaries. This course refuses it: 400 both. Any Transfer-Encoding other than exactly chunked (case-insensitive, one header) is 400 bad-encoding. A request with neither header has no body at all, and everything after the blank line is the next request.

The output contract

framing: content-length | chunked | none
body: "<body with \\ \r \n and non-printing bytes escaped>"
rest: <number of input bytes after the end of this message>

or one line 400 <reason> with the reasons both, bad-encoding, bad-length, bad-chunk and truncated (the input ended before the message was complete, including a header block with no empty line). Check in that order: headers first, then both, bad-encoding, bad-length, then walk the body.

In the body line a backslash prints as \\, CR as \r, LF as \n, tab as \t, and any other byte below 0x20 or above 0x7e as \x plus two lowercase hex digits. A size field with more than 8 hex digits is bad-chunk.

Common mistakes

  • Reading chunk sizes as decimal. It works until the first chunk of ten bytes or more, and the tests include 1A and 11.
  • Using a line reader for chunk data, so a body containing \r\n is split.
  • Forgetting the trailer lines after the zero chunk, so rest is wrong and the next request starts mid-trailer.
  • Counting characters instead of bytes: héllo is six bytes.
  • Letting a huge declared length overflow an integer instead of reporting truncated.

Your exercise

The starter reads the headers for you and hands you the two framing headers. You decode the body, and you account for every byte, including the ones that are not yours.

Up nextBuilding a ResponseRouting & Responses

Discussion

Ask a question, share an insight, or help someone who’s stuck.

Sign in to post a comment or reply.

Loading…