Skip to content

HTTP Methods Reference — C source

A searchable reference for every HTTP request method - GET, POST, PUT, PATCH, DELETE, and more. See at a glance which are safe, idempotent, and cacheable, then compare any two methods side by side.

This is the C implementation — the same logic the interactive tool runs, in a shareable, citable form.

/*
 * Source: c.c
 * Language: C (C11, standard library only)
 *
 * HTTP request-method reference — pure, deterministic data + lookups.
 *
 * CosmoDev polyglot showcase port of the `http-methods` tool, ported from
 * src/lib/http-methods.ts. Functionally equivalent to the TypeScript original:
 * identical inputs yield identical outputs (case-insensitive lookup, flag +
 * free-text filtering, and a human-readable semantic comparison report).
 *
 * Property flags (safe / idempotent / cacheable / hasBody) follow RFC 9110
 * and the MDN reference table.
 *
 * License: display source — part of CosmoDev's polyglot tool pages.
 */

#include <ctype.h>
#include <stddef.h>
#include <stdio.h>
#include <string.h>

/* One HTTP request method and its semantic properties. */
typedef struct {
    const char *method;   /* Uppercase method name, e.g. "GET". */
    int safe;             /* Read-only semantics — no server state change. */
    int idempotent;       /* Repeating the call has the same effect as a single call. */
    int cacheable;        /* Responses may be stored by a cache (RFC 9110 / MDN). */
    int has_body;         /* The method conventionally carries a request body. */
    const char *description;
    const char *typical_use;
} MethodEntry;

/* The nine HTTP request methods (RFC 9110 / 9111), in canonical order.
 * A const table is the single source of truth; the helpers hand out
 * pointers into it. */
const MethodEntry METHODS[] = {
    { "GET",
      1, 1, 1, 0,
      "Retrieves a representation of the target resource; a read-only request.",
      "Fetching a web page, reading an API resource, loading an image." },
    { "POST",
      0, 0, 1, 1,
      "Submits data to be processed, typically creating a new resource or triggering an action.",
      "Submitting a form, creating a record, publishing a message." },
    { "PUT",
      0, 1, 0, 1,
      "Replaces the target resource entirely with the request body.",
      "Updating a full record at a known URL, uploading a file by its path." },
    { "PATCH",
      0, 0, 0, 1,
      "Applies a partial modification to the target resource.",
      "Updating one field of a record, toggling a flag." },
    { "DELETE",
      0, 1, 0, 0,
      "Removes the target resource.",
      "Deleting a record or file by its URL." },
    { "HEAD",
      1, 1, 1, 0,
      "Identical to GET but returns only the response headers, no body.",
      "Checking existence, size, or freshness before downloading." },
    { "OPTIONS",
      1, 1, 0, 0,
      "Describes the communication options for the target resource.",
      "CORS preflight requests, discovering allowed methods." },
    { "CONNECT",
      0, 0, 0, 0,
      "Establishes a tunnel to the server (used with TLS/HTTPS proxies).",
      "Proxying encrypted connections through an intermediary." },
    { "TRACE",
      1, 1, 0, 0,
      "Performs a message loop-back test along the path to the target (debugging only).",
      "Diagnosing request transformations by intermediaries." },
};

#define METHOD_COUNT (sizeof METHODS / sizeof METHODS[0])

/* Tri-state for filter flags: callers must distinguish "no constraint" from
 * "constrain to false" — a bare int cannot express all three. */
typedef enum {
    TRI_ANY = 0, /* No constraint. */
    TRI_NO = 1,  /* Constrain to false. */
    TRI_YES = 2  /* Constrain to true. */
} TriState;

/* Narrowing options for filter_methods(). */
typedef struct {
    TriState safe;
    TriState idempotent;
    TriState cacheable;
    /* Free text matched case-insensitively against method, description, and
     * typical_use. NULL, or empty after trimming, means no constraint. */
    const char *query;
} MethodFilter;

/* Semantic diff between two methods. */
#define MAX_DIFFERENCES 4 /* safe, idempotent, cacheable, has_body */

typedef struct {
    /* Both methods share the `safe` flag. */
    int same_safety;
    /* Both methods share the `idempotent` flag. */
    int same_idempotence;
    /* One sentence per mismatched property, in the order safe, idempotent,
     * cacheable, has_body; the first `difference_count` entries are set. */
    char differences[MAX_DIFFERENCES][192];
    size_t difference_count;
} MethodComparison;

/* Skips leading ASCII whitespace and writes the trimmed length (trailing
 * whitespace excluded from the count) through `len`. */
static const char *trim(const char *s, size_t *len)
{
    while (*s != '\0' && isspace((unsigned char)*s)) {
        s++;
    }
    size_t end = strlen(s);
    while (end > 0 && isspace((unsigned char)s[end - 1])) {
        end--;
    }
    *len = end;
    return s;
}

/* Case-insensitive ASCII substring search with an explicit needle length:
 * does `hay` contain `needle[0..needle_len)`? Hand-rolled because
 * strcasestr/strcasecmp are POSIX, not C11. */
static int contains_ci_n(const char *hay, const char *needle, size_t needle_len)
{
    if (needle_len == 0) {
        return 1;
    }
    for (; *hay != '\0'; hay++) {
        size_t i = 0;
        while (i < needle_len && hay[i] != '\0' &&
               tolower((unsigned char)hay[i]) == tolower((unsigned char)needle[i])) {
            i++;
        }
        if (i == needle_len) {
            return 1;
        }
    }
    return 0;
}

/* Does the tri-state flag `want` accept the actual value? */
static int tri_matches(TriState want, int actual)
{
    switch (want) {
    case TRI_ANY:
        return 1;
    case TRI_NO:
        return !actual;
    case TRI_YES:
        return actual;
    }
    return 1;
}

/* Case-insensitive single-method lookup.
 * Returns a pointer into the canonical table, or NULL when unknown — the C
 * analogue of TS's `MethodEntry | null`. */
const MethodEntry *get_method(const char *name)
{
    if (name == NULL) {
        return NULL;
    }

    size_t len = 0;
    const char *t = trim(name, &len);
    if (len == 0 || len > 63) {
        return NULL; /* No method token is empty or longer than 63 chars. */
    }

    /* ASCII method names: toupper matches TS toUpperCase for the alphabetic
     * tokens without paying for full Unicode case-folding. */
    char n[64];
    for (size_t i = 0; i < len; i++) {
        n[i] = (char)toupper((unsigned char)t[i]);
    }
    n[len] = '\0';

    for (size_t i = 0; i < METHOD_COUNT; i++) {
        if (strcmp(METHODS[i].method, n) == 0) {
            return &METHODS[i];
        }
    }
    return NULL;
}

/* Writes up to `cap` pointers into `out` and returns the total number of
 * matches (which may exceed `cap` when the buffer is too small).
 * A method must satisfy every present flag AND, when the query is non-empty
 * after trimming, match it in at least one of
 * {method, description, typical_use}. */
size_t filter_methods(const MethodFilter *opts, const MethodEntry **out, size_t cap)
{
    static const MethodFilter no_constraints = { TRI_ANY, TRI_ANY, TRI_ANY, NULL };
    if (opts == NULL) {
        opts = &no_constraints;
    }

    size_t query_len = 0;
    const char *query = "";
    if (opts->query != NULL) {
        query = trim(opts->query, &query_len);
    }

    size_t total = 0;
    for (size_t i = 0; i < METHOD_COUNT; i++) {
        const MethodEntry *m = &METHODS[i];

        /* Each present flag is an AND constraint; TRI_ANY means skip. */
        if (!tri_matches(opts->safe, m->safe) ||
            !tri_matches(opts->idempotent, m->idempotent) ||
            !tri_matches(opts->cacheable, m->cacheable)) {
            continue;
        }

        if (query_len > 0 &&
            !contains_ci_n(m->method, query, query_len) &&
            !contains_ci_n(m->description, query, query_len) &&
            !contains_ci_n(m->typical_use, query, query_len)) {
            continue;
        }

        if (total < cap) {
            out[total] = m;
        }
        total++;
    }
    return total;
}

/* Phrasing helpers keep the difference-sentence wording in one place, so the
 * output stays in lock-step across every polyglot port. (C needs them
 * defined before their first use in compare_methods.) */
static const char *safe_word(int v)
{
    return v ? "safe" : "not safe";
}

static const char *idempotent_word(int v)
{
    return v ? "idempotent" : "not idempotent";
}

static const char *cacheable_word(int v)
{
    return v ? "cacheable" : "not cacheable";
}

static const char *body_word(int v)
{
    return v ? "takes a body" : "does not take a body";
}

/* Surfaces where two methods' semantics agree and differ.
 * `differences` lists every mismatched property as a human-readable
 * sentence, reproducing the canonical phrasing verbatim so output stays
 * identical to the TypeScript original and the other polyglot ports. */
MethodComparison compare_methods(const MethodEntry *a, const MethodEntry *b)
{
    MethodComparison out;
    out.same_safety = a->safe == b->safe;
    out.same_idempotence = a->idempotent == b->idempotent;
    out.difference_count = 0;

    if (a->safe != b->safe) {
        snprintf(out.differences[out.difference_count++],
                 sizeof out.differences[0],
                 "%s is %s, %s is %s.",
                 a->method, safe_word(a->safe), b->method, safe_word(b->safe));
    }
    if (a->idempotent != b->idempotent) {
        snprintf(out.differences[out.difference_count++],
                 sizeof out.differences[0],
                 "%s is %s, %s is %s.",
                 a->method, idempotent_word(a->idempotent),
                 b->method, idempotent_word(b->idempotent));
    }
    if (a->cacheable != b->cacheable) {
        snprintf(out.differences[out.difference_count++],
                 sizeof out.differences[0],
                 "%s is %s, %s is %s.",
                 a->method, cacheable_word(a->cacheable),
                 b->method, cacheable_word(b->cacheable));
    }
    if (a->has_body != b->has_body) {
        snprintf(out.differences[out.difference_count++],
                 sizeof out.differences[0],
                 "%s %s, %s %s.",
                 a->method, body_word(a->has_body),
                 b->method, body_word(b->has_body));
    }

    return out;
}

Also available in 13 other languages

Every CosmoDev tool ships its pure logic in TypeScript (web) and Go (CLI), with authored implementations in a dozen-plus languages — the same contract, ported. Compare all languages side by side →