Minimalist agentic harness in C
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159#include "httpClient.h"#include "lib.h"#include "log.h"#include "vec.h"#include <curl/curl.h>#include <curl/easy.h>
RESULT(httpClient) httpClientNew(str base_url, str bearer) { CURL* curl = curl_easy_init(); if (!curl) { return RESULT_ERR(httpClient, ERROR_CURL_INIT_FAILED); }
return RESULT_OK(httpClient, (httpClient) { .curl = curl, .headers_list = nullptr, .base_url = base_url, .bearer = bearer });}
error_thttpClientAddHeader(httpClient* client, str header) { assert(header.data != nullptr); assert(header.data[header.length] == 0);
struct curl_slist* headers_list = client->headers_list;
headers_list = curl_slist_append(headers_list, (const char*) header.data); if (!headers_list) { return ERROR_CURL_SLIST_APPEND_FAILED; }
client->headers_list = headers_list; return OK;}
error_thttpClientAddHeaderf(httpClient* client, const char* fmt, ...) { va_list ap; va_start(ap); error_t out; stromboni header_str = {0};
header_str = TRY_OR_ELSE(vstromprintf(fmt, ap), err, out = err; goto cleanup); out = httpClientAddHeader(client, borrow(header_str));cleanup: va_end(ap); stromboniFree(&header_str); return out;}
enum httpVerb { GET, POST,};
static bool vecStrWrite(void* vecptr, str chunk) { vec* vec = vecptr; error_t res = vecInsert(vec, vec->length, chunk.data, chunk.length); return res == OK;}
static size_t curlWriteCallback(char* ptr, size_t size, size_t nmemb, void* userdata) { httpSink* sink = (httpSink*)userdata; str chunk = (str){ .data = (const u8*) ptr, .length = size * nmemb }; bool res = sink->write(sink->ctx, chunk); return res ? size * nmemb : 0;}
static RESULT(httpMeta) httpClientCurlRequest(httpClient client, enum httpVerb verb, str path, str body, httpSink sink) { RESULT(httpMeta) out; curl_easy_reset(client.curl);
stromboni url = TRY_OR_ELSE(strConcat(client.base_url, path), err, return RESULT_ERR(httpMeta, err)); client_setopt(client, CURLOPT_URL, url.data);
switch(verb) { case GET: client_setopt(client, CURLOPT_HTTPGET, 1L); break; case POST: client_setopt(client, CURLOPT_POSTFIELDSIZE_LARGE, (curl_off_t)body.length); client_setopt(client, CURLOPT_POSTFIELDS, body.data); break; default: out = RESULT_ERR(httpMeta, ERROR_UNKNOWN); goto cleanup; }
client_setopt(client, CURLOPT_HTTPAUTH, CURLAUTH_BEARER); client_setopt(client, CURLOPT_XOAUTH2_BEARER, client.bearer.data); client_setopt(client, CURLOPT_HTTPHEADER, client.headers_list); client_setopt(client, CURLOPT_WRITEDATA, &sink); client_setopt(client, CURLOPT_WRITEFUNCTION, curlWriteCallback);
CURLcode res = curl_easy_perform(client.curl); if (res != CURLE_OK) { out = RESULT_ERR(httpMeta, ERROR_CURL_PERFORM_FAILED); logError("curl: %s", curl_easy_strerror(res)); goto cleanup; }
long http_code = 0; curl_easy_getinfo(client.curl, CURLINFO_RESPONSE_CODE, &http_code); httpMeta meta = { .status = http_code, };
out = RESULT_OK(httpMeta, meta);
cleanup: stromboniFree(&url); return out;}
static result_httpResponse httpClientRequest(httpClient client, enum httpVerb verb, str path, str body) { vec buffer = vecNew(1, nullptr); httpSink sink = { .ctx = &buffer, .write = vecStrWrite, };
result_httpResponse out; httpMeta meta = TRY_OR_GOTO(httpClientCurlRequest(client, verb, path, body, sink), err, out, error); stromboni data = TRY_OR_GOTO(stromboniFromVec(&buffer), err, out, error);
httpResponse response = { .meta = meta, .body = data, }; return RESULT_OK(httpResponse, response);
error: vecDestroy(&buffer); return out;}
result_httpResponse httpClientPost(httpClient client, str path, str body) { // Prevent curl from reading STDIN if (body.data == nullptr) { body = STR(""); }
return httpClientRequest(client, POST, path, body);}
result_httpResponse httpClientGet(httpClient client, str path) { return httpClientRequest(client, GET, path, STR_NULL);}
void httpClientFree(httpClient* client) { curl_slist_free_all(client->headers_list); client->headers_list = nullptr; curl_easy_cleanup(client->curl);}