---
id: "0110"
title: "Gap registry: helper HTTP cliente C++ (curl/popen) reutilizable"
status: pendiente
type: feature
domain:
  - cpp-stack
  - registry-quality
scope: registry
priority: media
depends: []
blocks:
  - "0111"
related:
  - "0106"
created: 2026-05-18
updated: 2026-05-18
tags: [http, cpp, registry-gap, curl, helper, ausente-ready]
---

# 0110 — Helper HTTP cliente C++ en el registry

## Problema

Hoy no existe funcion HTTP cliente reutilizable en `cpp/functions/`. Cada app C++ que necesita
golpear un endpoint reinventa la capa:

| App | Fichero | Tecnica | LOC aprox |
|---|---|---|---|
| `apps/services_monitor/` | `http_client.cpp` | cURL popen/WinHTTP segun plataforma | ~150 |
| `apps/dag_engine_ui/` | inline en `main.cpp` | curl CLI via popen + parse | ~80 |
| `apps/data_factory/` | inline | popen curl | ~60 |
| `cpp/functions/core/llm_anthropic.cpp` | propio | cURL popen — solo Anthropic | — |
| `apps/process_explorer/` (issue 0111) | `http_client.cpp` local | pendiente — clonara services_monitor | ~150 esperados |

El patron ya supera el umbral `>2x` que dispara la regla de promocion (CLAUDE.md
"Si patron se repite >2x → propose nueva funcion via fn-constructor"). Cada app duplica:

- Detection de plataforma (Linux: `popen("curl -s ...")`, Win: `WinHTTPOpen`/popen)
- Manejo de basicAuth / Bearer tokens
- Timeouts
- Captura de body + status code
- Manejo de errores transitorios (DNS, conexion rechazada)

## Decision

Anadir al registry dos funciones C++ en dominio `core` (o `infra`):

### `http_request_cpp_core` (impure)

```cpp
namespace fn_http {
    struct Request {
        std::string method;        // "GET", "POST", "PUT", "DELETE"
        std::string url;
        std::vector<std::pair<std::string,std::string>> headers;
        std::string body;           // raw bytes (JSON, etc.)
        int timeout_ms = 5000;
        std::string bearer_token;   // shortcut: anade Authorization: Bearer <token>
        std::string basic_user;     // shortcut: anade Authorization: Basic base64(user:pass)
        std::string basic_pass;
    };
    struct Response {
        int    status = 0;          // 0 = error de transporte
        std::string body;
        std::vector<std::pair<std::string,std::string>> headers;
        std::string error;          // vacio si OK
        int64_t duration_ms = 0;
    };
    Response request(const Request& req);
}
```

Implementacion: cURL via popen (portable WSL+Win+Linux, igual que `llm_anthropic`).
Si en el futuro queremos rendimiento real, swap a libcurl linkado estaticamente
o WinHTTP via `#ifdef _WIN32` — interfaz Request/Response no cambia.

### `http_get_json_cpp_core` (impure, pure wrapper)

Helper que envuelve `http_request` + parse JSON (via `nlohmann::json` o similar
ya disponible en el repo) para los casos comunes:

```cpp
namespace fn_http {
    // Devuelve parsed JSON o lanza si status != 2xx
    nlohmann::json get_json(const std::string& url,
                            const std::string& bearer_token = "",
                            int timeout_ms = 5000);
}
```

## Plan de migracion

Tras crear las funciones, abrir issue separado por cada consumer para migrar:

1. `apps/services_monitor/http_client.cpp` -> usar `fn_http::request`
2. `apps/dag_engine_ui/main.cpp` (inline)
3. `apps/data_factory/` (inline)
4. `cpp/functions/core/llm_anthropic.cpp` — refactor para usar `fn_http::request` por
   debajo (mantiene API publica)
5. `apps/process_explorer/` (issue 0111) — nace ya usando el helper

## Criterios de aceptacion

- [ ] `cpp/functions/core/http_request.{cpp,h,md}` registrado en `registry.db`
- [ ] `cpp/functions/core/http_get_json.{cpp,h,md}` idem
- [ ] Tests visuales o de integracion contra `httpbin.org` (200/404/timeout/auth)
- [ ] Frontmatter completo (`params`/`output`/`tags`/`example`)
- [ ] `.md` cumple contrato self-doc (`## Ejemplo`, `## Cuando usarla`, `## Gotchas`)
- [ ] Al menos 1 consumer migrado para validar API (recomendado `services_monitor`)
- [ ] `fn doctor uses-functions` limpio

## Gotchas conocidos

- cURL popen en Windows necesita `curl.exe` en PATH — todos los WSL/Win lo tienen,
  pero documentar en `## Gotchas`.
- Bodies binarios: popen complica el escape; primera version solo string bodies.
- TLS verify: por defecto on; permitir `req.insecure = true` solo para testing.
- Timeouts: cURL `--max-time` cubre handshake+transfer; documentar diferencia con
  read-timeout puro.

## Por que no usar libcurl linkado

- `popen("curl ...")` no requiere anadir libcurl al toolchain MinGW cross-compile
  (que ya costo configurar). `llm_anthropic` lleva meses funcionando asi.
- Cuando aparezca un caso real de latencia (>10 req/s sostenido), abrimos issue
  separado para swap a libcurl.

## Out of scope (no en este issue)

- WebSocket / SSE — cliente WS C++ es otro gap; abrir issue propio cuando aplique.
- Cliente gRPC.
- Streaming responses (SSE chunk-by-chunk) — usar caso de `dag_engine_ui` para
  decidir cuando.
