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: cpp.cpp
// Language: C++ (C++17, 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 <algorithm>
#include <array>
#include <cctype>
#include <optional>
#include <string>
#include <string_view>
#include <vector>

namespace {

/// One HTTP request method and its semantic properties.
///
/// Fields use `std::string_view` so the canonical table can live as a
/// `constexpr` array with no allocation.
struct MethodEntry {
    std::string_view method;   ///< Uppercase method name, e.g. "GET".
    bool safe;                 ///< Read-only semantics — no server state change.
    bool idempotent;           ///< Repeating the call has the same effect as a single call.
    bool cacheable;            ///< Responses may be stored by a cache (RFC 9110 / MDN).
    bool has_body;             ///< The method conventionally carries a request body.
    std::string_view description;
    std::string_view typical_use;
};

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

/// Filter options. Each boolean is `std::optional<bool>` so callers can
/// distinguish "constrain to `false`" from "no constraint" — a tri-state a
/// bare `bool` cannot express.
struct MethodFilter {
    std::optional<bool> safe;
    std::optional<bool> idempotent;
    std::optional<bool> cacheable;
    /// Free text matched case-insensitively against method, description, and
    /// typical_use. Ignored when empty after trimming.
    std::optional<std::string> query;
};

/// Semantic diff between two methods.
struct MethodComparison {
    bool same_safety = false;       ///< Both methods share the `safe` flag.
    bool same_idempotence = false;  ///< Both methods share the `idempotent` flag.
    /// One sentence per mismatched property (safe, idempotent, cacheable, has_body).
    std::vector<std::string> differences;
};

std::string to_ascii_lower(std::string_view s)
{
    std::string out{s};
    std::transform(out.begin(), out.end(), out.begin(), [](unsigned char c) {
        return static_cast<char>(std::tolower(c));
    });
    return out;
}

std::string to_ascii_upper(std::string_view s)
{
    std::string out{s};
    std::transform(out.begin(), out.end(), out.begin(), [](unsigned char c) {
        return static_cast<char>(std::toupper(c));
    });
    return out;
}

std::string_view trim(std::string_view s)
{
    while (!s.empty() && std::isspace(static_cast<unsigned char>(s.front()))) {
        s.remove_prefix(1);
    }
    while (!s.empty() && std::isspace(static_cast<unsigned char>(s.back()))) {
        s.remove_suffix(1);
    }
    return s;
}

/// Does `hay` contain the already-lowercased `needle`? Case-insensitive for
/// ASCII — method names, descriptions, and queries are ASCII text.
bool contains_ci(std::string_view hay, const std::string& lowered_needle)
{
    return to_ascii_lower(hay).find(lowered_needle) != std::string::npos;
}

// Phrasing helpers keep the difference-sentence wording in one place, so the
// output stays in lock-step across every polyglot port.
const char* safe_word(bool v)
{
    return v ? "safe" : "not safe";
}

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

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

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

} // namespace

/// Case-insensitive single-method lookup.
///
/// Returns a pointer into the canonical table, or null when unknown — the
/// C++ analogue of Rust's `Option<&MethodEntry>` / TS's `MethodEntry | null`.
const MethodEntry* get_method(std::string_view name)
{
    // ASCII method names: to_ascii_upper matches TS toUpperCase for the
    // alphabetic tokens without paying for full Unicode case-folding.
    const std::string n = to_ascii_upper(trim(name));
    const auto it = std::find_if(METHODS.begin(), METHODS.end(),
                                 [&](const MethodEntry& m) { return m.method == n; });
    return it == METHODS.end() ? nullptr : &*it;
}

/// Returns the methods satisfying every present flag and, when `query` is
/// non-empty after trimming, matching it in at least one of
/// {method, description, typical_use}.
///
/// Returns pointers into the canonical table — no allocation of the records
/// themselves, only of the returned vector.
std::vector<const MethodEntry*> filter_methods(const MethodFilter& opts = {})
{
    const std::string q = to_ascii_lower(trim(opts.query.value_or("")));

    std::vector<const MethodEntry*> out;
    for (const MethodEntry& m : METHODS) {
        // Each present flag is an AND constraint; nullopt means skip.
        if (opts.safe.has_value() && m.safe != *opts.safe) {
            continue;
        }
        if (opts.idempotent.has_value() && m.idempotent != *opts.idempotent) {
            continue;
        }
        if (opts.cacheable.has_value() && m.cacheable != *opts.cacheable) {
            continue;
        }

        if (!q.empty() && !contains_ci(m.method, q) && !contains_ci(m.description, q) &&
            !contains_ci(m.typical_use, q)) {
            continue;
        }

        out.push_back(&m);
    }
    return out;
}

/// Surfaces where two methods' semantics agree and differ.
///
/// `differences` lists every mismatched property (safe, idempotent,
/// cacheable, has_body) 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;

    if (a.safe != b.safe) {
        out.differences.push_back(std::string(a.method) + " is " + safe_word(a.safe) + ", " +
                                  std::string(b.method) + " is " + safe_word(b.safe) + ".");
    }
    if (a.idempotent != b.idempotent) {
        out.differences.push_back(std::string(a.method) + " is " + idempotent_word(a.idempotent) +
                                  ", " + std::string(b.method) + " is " +
                                  idempotent_word(b.idempotent) + ".");
    }
    if (a.cacheable != b.cacheable) {
        out.differences.push_back(std::string(a.method) + " is " + cacheable_word(a.cacheable) +
                                  ", " + std::string(b.method) + " is " +
                                  cacheable_word(b.cacheable) + ".");
    }
    if (a.has_body != b.has_body) {
        out.differences.push_back(std::string(a.method) + " " + body_word(a.has_body) + ", " +
                                  std::string(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 →