From a843f84a183af141d64bf004ca0fe2cc7b0e6843 Mon Sep 17 00:00:00 2001 From: Egutierrez Date: Mon, 18 May 2026 18:14:56 +0200 Subject: [PATCH] docs(cpp/core): registry .md for http_request + http_get_json (issue 0110) Frontmatter + self-doc sections (Ejemplo, Cuando usarla, Gotchas) following the contract in .claude/rules/function_growth_and_self_docs.md. Tags include 'http', 'client', 'curl', 'network', 'registry-gap', 'helper' so FTS surfaces them when an agent asks for HTTP / fetch / GET / Bearer. http_get_json declares uses_functions: [http_request_cpp_core] so the dependency is auditable via mcp__registry__fn_uses. --- cpp/functions/core/http_get_json.md | 74 +++++++++++++++++++++++++ cpp/functions/core/http_request.md | 84 +++++++++++++++++++++++++++++ 2 files changed, 158 insertions(+) create mode 100644 cpp/functions/core/http_get_json.md create mode 100644 cpp/functions/core/http_request.md diff --git a/cpp/functions/core/http_get_json.md b/cpp/functions/core/http_get_json.md new file mode 100644 index 00000000..ac2a6825 --- /dev/null +++ b/cpp/functions/core/http_get_json.md @@ -0,0 +1,74 @@ +--- +name: http_get_json +kind: function +lang: cpp +domain: core +version: "1.0.0" +purity: impure +signature: "nlohmann::json fn_http::get_json(const std::string& url, const std::string& bearer_token = \"\", int timeout_ms = 5000)" +description: "Convenience wrapper over fn_http::request for the common case: GET with optional Bearer auth, expect 2xx, parse body as JSON. Returns nlohmann::json. Throws std::runtime_error on transport failure, non-2xx status, or JSON parse error. For non-GET, custom headers, or status-aware control flow use http_request directly." +tags: [http, json, client, curl, network, registry-gap, core, helper] +uses_functions: + - http_request_cpp_core +uses_types: [] +returns: [] +returns_optional: false +error_type: "error_go_core" +imports: + - "core/http_get_json.h" +tested: true +tests: + - "get_json: parses 200 JSON body" + - "get_json: throws on 404" + - "get_json: bearer_token reaches server" + - "get_json: throws on invalid JSON" +test_file_path: "cpp/tests/test_http_get_json.cpp" +file_path: "cpp/functions/core/http_get_json.cpp" +params: + - name: url + desc: "Full URL incl. scheme (https://api.example.com/path?q=1)." + - name: bearer_token + desc: "Optional. If non-empty, sent as Authorization: Bearer . Pass empty string for unauthenticated." + - name: timeout_ms + desc: "Hard timeout. Default 5000ms." +output: "nlohmann::json — the parsed body. Whatever shape the endpoint returns (object, array, scalar). Use json[\"key\"] / .at() / .get() to traverse. Throws std::runtime_error on any failure." +--- + +## Ejemplo + +```cpp +#include "core/http_get_json.h" + +try { + auto data = fn_http::get_json("https://httpbin.org/get"); + std::cout << "url echoed: " << data["url"].get() << "\n"; +} catch (const std::exception& e) { + std::cerr << "fetch failed: " << e.what() << "\n"; +} +``` + +Con Bearer token + timeout corto (sqlite_api `/api/databases`): + +```cpp +auto dbs = fn_http::get_json( + "http://localhost:8484/api/databases", + std::getenv("SQLITE_API_TOKEN"), + 2000); +for (const auto& db : dbs) { + std::cout << db["name"].get() << "\n"; +} +``` + +## Cuando usarla + +- Cuando una app C++ necesita GET a un endpoint JSON y prefiere fail-fast (throw) en vez de inspeccionar `Response.status` a mano. +- Para pollers / heartbeats / health checks contra APIs internas (sqlite_api, services_api, registry_api). +- NO usar cuando necesites POST, headers custom, o distinguir entre 404 y 500 sin try/catch — usa `http_request` directo. + +## Gotchas + +- **Throws**, no devuelve un Result. Si llamas en un loop tight, envuelve en try/catch o tu app peta al primer 5xx. +- **Hereda gotchas de `http_request`**: requiere `curl` en PATH, sin streaming, sin cookies, TLS verify activo por defecto. +- **No reusa `get_json` cuando un endpoint devuelve algo que NO es JSON** (HTML, texto plano). La excepcion sera `invalid JSON` y perderas el status real. Usa `http_request` para esos casos. +- **`bearer_token` vacio es valido** (no se manda header). No pasar el `Authorization: Bearer ` literal — eso ya lo construye `http_request`. +- **No se ofrece `post_json`** intencionalmente — la combinatoria de POST + JSON + auth + retry es lo suficientemente rica como para que `http_request` directo sea mas claro que un wrapper con N parametros opcionales. diff --git a/cpp/functions/core/http_request.md b/cpp/functions/core/http_request.md new file mode 100644 index 00000000..735ed265 --- /dev/null +++ b/cpp/functions/core/http_request.md @@ -0,0 +1,84 @@ +--- +name: http_request +kind: function +lang: cpp +domain: core +version: "1.0.0" +purity: impure +signature: "fn_http::Response fn_http::request(const fn_http::Request& req)" +description: "Generic HTTP client via cURL CLI (popen). Supports GET/POST/PUT/DELETE/PATCH, custom headers, raw body, Bearer/Basic auth shortcuts, timeout, optional TLS-verify skip. Portable across Linux / WSL / MinGW — requires only `curl` in PATH at runtime, no link-time libcurl. Returns status, body, response headers, error, duration_ms. Replaces inline curl-popen patterns in apps/services_monitor, dag_engine_ui, data_factory." +tags: [http, client, curl, network, registry-gap, core, helper] +uses_functions: [] +uses_types: [] +returns: [] +returns_optional: false +error_type: "error_go_core" +imports: + - "core/http_request.h" +tested: true +tests: + - "request: GET 200 status + body" + - "request: GET 404 status (HTTP error not transport)" + - "request: POST with body + content-type header" + - "request: bearer_token shortcut adds Authorization header" + - "request: basic_user/basic_pass shortcut authenticates" + - "request: invalid URL surfaces transport error" + - "request: timeout_ms honored" +test_file_path: "cpp/tests/test_http_request.cpp" +file_path: "cpp/functions/core/http_request.cpp" +params: + - name: req + desc: "Request struct: method (GET/POST/PUT/DELETE/PATCH, empty defaults to GET), url (full URL incl. scheme), headers (vector of {key,value}), body (raw request body bytes), timeout_ms (hard timeout, default 5000), bearer_token (shortcut for Authorization: Bearer), basic_user + basic_pass (shortcut for HTTP Basic, curl --user), insecure (bool — skip TLS verify, TESTING ONLY)." +output: "Response struct: status (HTTP code; 0 = transport error), body (response bytes), headers (vector of {key,value} as returned by server), error (empty on success, populated on transport failure / non-zero curl exit), duration_ms (wall clock of the call)." +--- + +## Ejemplo + +```cpp +#include "core/http_request.h" + +fn_http::Request req; +req.method = "GET"; +req.url = "https://httpbin.org/get"; +req.headers = {{"Accept", "application/json"}}; +req.timeout_ms = 3000; + +fn_http::Response res = fn_http::request(req); +if (!res.error.empty()) { + fprintf(stderr, "transport error: %s\n", res.error.c_str()); +} else if (res.status / 100 != 2) { + fprintf(stderr, "http %d: %s\n", res.status, res.body.c_str()); +} else { + printf("OK %d (%lld ms): %.200s\n", res.status, + (long long)res.duration_ms, res.body.c_str()); +} +``` + +POST con JSON + Bearer auth: + +```cpp +fn_http::Request req; +req.method = "POST"; +req.url = "https://api.example.com/items"; +req.headers = {{"Content-Type", "application/json"}}; +req.body = R"({"name":"hello"})"; +req.bearer_token = std::getenv("API_TOKEN"); +auto res = fn_http::request(req); +``` + +## Cuando usarla + +- Cuando una app C++ necesita hablar HTTP con un endpoint (REST API local, servicio en VPS, webhook). +- Antes de copiar otra vez el patron `popen("curl -s ...")` desde `apps/services_monitor/http_client.cpp` o `apps/dag_engine_ui/main.cpp` — esto es exactamente la promocion que hace falta (issue 0110). +- Como base para wrappers JSON especificos (`http_get_json` ya existe; los siguientes deberian apilarse encima de `request`, no de curl directo). + +## Gotchas + +- **Requiere `curl` en `PATH`** en runtime. No hay fallback; si falta, `Response.error` lo dice y `status==0`. +- **Sin streaming**: la respuesta entera entra en memoria. No usar para downloads grandes (>50 MB). +- **`insecure=true` desactiva TLS verify** — solo para tests contra mocks self-signed. NUNCA en produccion. +- **Status `0` significa transport error** (DNS, conexion rechazada, timeout, curl ausente). Status `>=400` es respuesta HTTP normal — el caller decide si es fallo. +- **Headers son case-insensitive en HTTP**; aqui los devolvemos tal como los manda el servidor. Si necesitas lookup case-insensitive, normaliza en el caller. +- **Body multipart / form-encoded**: no implementado como shortcut. Construye el body a mano y setea `Content-Type` apropiado en `headers`. +- **Cookies**: no se mantienen sesion. Cada llamada es independiente. Si necesitas cookie jar, pasala via header `Cookie` explicito. +- **Shell injection**: los argumentos pasan via `popen` con quoting POSIX (single-quote escape). URLs y headers con comillas raras se manejan bien, pero NO pases input no confiable como `url` si el binario corre con privilegios.