cybersecurity · intermediate · ~15 min · safe pentest lab

Parse an HTTP request line into method/path/version

Bounded parsing of a text protocol with allow-list character validation.

Challenge

Parse an HTTP request line into method, path, and version with strict bounds and allow-list checks — the request line is where most HTTP attacks begin.

Task

Implement int parse_request_line(const char *buf, http_req_t *out) where:

typedef struct {
    char method[8];    /* e.g. "GET", "POST" */
    char path[256];
    char version[16];  /* e.g. "HTTP/1.1" */
} http_req_t;

Return 0 on success (filling out), -1 on any failure.

Input

  • buf: a NUL-terminated buffer holding the first line of an HTTP request, ending in \r\n. The grader passes fixed strings.
  • out: the struct to fill. Written only on success.

Output

Returns int: 0 on success, -1 on any malformed input.

Example

"GET /index.html HTTP/1.1\r\n"  -> 0, method="GET", path="/index.html", version="HTTP/1.1"
"GET /a\n"                       -> -1   (no CRLF)
"GET /a;b HTTP/1.1\r\n"         -> -1   (disallowed char in path)
"<260-char path>"                -> -1   (path overflow)

Edge cases

  • NULL buf or NULL out returns -1.
  • A line not ending in \r\n returns -1.
  • Anything other than exactly three space-separated tokens (e.g. a double space) returns -1.

Rules

  • Each token must fit its field: method <= 7, path <= 255, version <= 15 bytes (excluding the NUL).
  • The path may contain only A-Za-z0-9/?&=._-.
  • memcpy does not NUL-terminate — terminate each field after copying.

Why this matters

The request line is where every HTTP attack starts. A bounded, allow-list parser stops the easy ones at the door.

Input format

A NUL-terminated buffer containing the first line of an HTTP request.

Output format

0 on success, -1 on any malformed input. Output struct fields are written only on success.

Constraints

No strcpy without length. CRLF required. Allow-list characters only.

Starter code

#include <stddef.h>
typedef struct {
    char method[8];
    char path[256];
    char version[16];
} http_req_t;

int parse_request_line(const char *buf, http_req_t *out) {
    /* TODO */
    (void)buf; (void)out;
    return -1;
}

Common mistakes

Using sscanf without length specifiers. Forgetting NUL terminator. Allowing double-space (potential request smuggling vector).

Edge cases to handle

Method that's exactly 7 chars (fits, since NUL needs slot 8). Path with ? and &. Trailing space before CRLF.

Complexity

O(n) in the line length.

Background lessons

Up next

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