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 →