networking · intermediate · ~25 min

Decode an HTTP/1.1 chunked body

Strict chunked-encoding parser; refuse permissive variants.

Challenge

HTTP/1.1 can send a body in "chunked" transfer encoding: a series of size-prefixed pieces. Decode such a body into one flat buffer, parsing strictly so that smuggling-style variants are rejected.

Task

Implement int decode_chunked(const char *in, int in_len, char *out, int cap, int *out_len) that decodes the chunked body in in into out.

The wire format is a repetition of HEX_SIZE CRLF DATA CRLF, terminated by a zero-size chunk 0 CRLF CRLF. HEX_SIZE is the chunk's data length in hexadecimal.

Input

  • in, in_len: the chunked body bytes and their length.
  • out, cap: destination buffer and its capacity in bytes.
  • out_len: set to the total number of decoded bytes on success.

Output

Returns 1 on success (with *out_len set to the decoded length), or 0 on any parse error.

Example

"4\r\nWiki\r\n5\r\npedia\r\n0\r\n\r\n"   ->   1, out="Wikipedia", *out_len=9
"0\r\n\r\n"                                ->   1, *out_len=0

Edge cases

  • Empty body (just 0\r\n\r\n) decodes to length 0.
  • Reject trailing garbage on the size line (e.g. 4abc).
  • Reject a missing CRLF after a chunk's data.
  • Reject if the decoded data would exceed cap.

Rules

  • Parse strictly: reject non-hex characters in the size, surrounding whitespace, and any deviation from the grammar.

Why this matters

The chunked-encoding parser is the smuggling-vector surface. Writing one correctly — strict, bounded — is the defensive baseline.

Input format

Chunked body bytes (in) and length (in_len), an output buffer (out) with capacity (cap), and out_len for the decoded length.

Output format

Returns 1 on success with *out_len set, or 0 on any parse error.

Constraints

Parse strictly: refuse non-hex size chars, surrounding whitespace, missing CRLFs, and any decode that would overflow cap.

Starter code

#include <stddef.h>
int decode_chunked(const char *in, int in_len, char *out, int cap, int *out_len) { /* TODO */ (void)in; (void)in_len; (void)out; (void)cap; (void)out_len; return 0; }

Common mistakes

Accepting whitespace before/after the size. Accepting non-hex chars.

Edge cases to handle

Empty body (just 0\r\n\r\n). Single chunk. Multiple chunks. Chunk size with leading 0x.

Complexity

O(in_len).

Background lessons

Up next

Solve this exercise in the browser editor — compile and run against the test harness, no setup required.