cybersecurity · intermediate · ~25 min

Unfold an HTTP/1.1 continuation header

Careful protocol parsing with strict line discipline.

Challenge

Unfold an HTTP/1.1 continuation ("folded") header into one line. Folding is deprecated by RFC 7230, but mis-handling it is a known request-smuggling vector, so parsing it carefully is worth understanding.

Task

Implement int unfold_header(const char *input, char *out, size_t cap) that joins a folded header group into a single line.

The input is a header group where:

  • Lines are separated by \r\n.
  • The first line is the primary header (e.g. X-Foo: bar).
  • A line that begins with a space or tab is a continuation: append it to the running value, replacing the \r\n + leading whitespace with a single space.

Input

  • input: the CRLF-separated header bytes. The grader passes a fixed buffer.
  • out, cap: destination buffer and its size; write the result NUL-terminated, using at most cap - 1 bytes.

Output

Returns the number of input lines consumed (>= 1) on success, with the unfolded value in out. Returns 0 on failure (empty input, or the result would not fit in cap).

Example

"X-Foo: bar\r\n other\r\n"                          ->   2, out = "X-Foo: bar other"
"Content-Type: text/html\r\nHost: x\r\n"            ->   1, out = "Content-Type: text/html"
"X-Long: one\r\n\ttwo\r\n  three\r\nNext: 1\r\n"   ->   3, out = "X-Long: one two three"
"" (empty)                                          ->   0

Edge cases

  • Empty input returns 0.
  • A following header that is NOT a continuation (no leading space/tab) stops the unfold and is not joined.
  • A too-small out returns 0.

Rules

  • Cap-check before every write; no allocations.
  • Treat only \r\n as a line break (strict CRLF).

Why this matters

HTTP/1.1 'header folding' lets a header value span multiple lines using a continuation line that begins with whitespace. RFC 7230 has since deprecated this — but parsers still encounter it, and mishandling it is a known request-smuggling vector. Writing a careful unfold helps you understand why proxies are so paranoid.

Input format

A CRLF-separated header group input, a destination buffer out, and its size cap.

Output format

The count of lines consumed (>= 1) with out set; 0 on empty input or insufficient cap.

Constraints

Cap-aware; no allocations; strict CRLF line breaks.

Starter code

#include <stddef.h>
int unfold_header(const char *input, char *out, size_t cap) { /* TODO */ return 0; }

Common mistakes

Joining lines with \r\n left in. Treating LF without preceding CR as end of line — strict HTTP wants CRLF only. Forgetting the leading-WS rule for continuations.

Edge cases to handle

Header with no continuation; empty input; continuation lines with only whitespace; cap too small.

Complexity

O(strlen(input)).

Background lessons

Up next

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