Mercurial > sci
view libsci/apic.c @ 64:562372396019
misc: improve manual pages and documentation
author | David Demelier <markand@malikania.fr> |
---|---|
date | Thu, 18 Aug 2022 20:17:18 +0200 |
parents | 308aa1086702 |
children | 26bbca3765dd |
line wrap: on
line source
/* * apic.c -- synchronous HTTP request * * Copyright (c) 2021-2022 David Demelier <markand@malikania.fr> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include <assert.h> #include <errno.h> #include <stdarg.h> #include <stdio.h> #include <string.h> #include <curl/curl.h> #include "apic.h" #include "util.h" struct context { /* Output */ FILE *fp; char *out; size_t outsz; /* HTTP requester. */ char url[1024]; char keyhdr[128]; CURL *curl; CURLcode code; struct curl_slist *headers; /* JSON stuff. */ json_t *doc; json_error_t error; }; struct apiconf apiconf = { .baseurl = "http://127.0.0.1" }; static size_t writer(char *in, size_t n, size_t w, FILE *fp) { if (fwrite(in, n, w, fp) != w) return 0; return w; } static inline void init_url(struct context *ctx, const char *fmt, va_list ap) { char page[128]; vsnprintf(page, sizeof (page), fmt, ap); snprintf(ctx->url, sizeof (ctx->url), "%s/%s", apiconf.baseurl, page); } static inline void init_output(struct context *ctx) { ctx->fp = util_open_memstream(&ctx->out, &ctx->outsz); } static void init_curl(struct context *ctx, const char *body) { /* Create API key string. */ snprintf(ctx->keyhdr, sizeof (ctx->keyhdr), "X-Api-Key: %s", apiconf.key); ctx->headers = curl_slist_append(ctx->headers, "Content-Type: application/json"); ctx->headers = curl_slist_append(ctx->headers, ctx->keyhdr); ctx->curl = curl_easy_init(); curl_easy_setopt(ctx->curl, CURLOPT_HTTPHEADER, ctx->headers); curl_easy_setopt(ctx->curl, CURLOPT_URL, ctx->url); curl_easy_setopt(ctx->curl, CURLOPT_FOLLOWLOCATION, 1L); curl_easy_setopt(ctx->curl, CURLOPT_TIMEOUT, 3L); curl_easy_setopt(ctx->curl, CURLOPT_WRITEFUNCTION, writer); curl_easy_setopt(ctx->curl, CURLOPT_WRITEDATA, ctx->fp); curl_easy_setopt(ctx->curl, CURLOPT_NOSIGNAL, 1L); /* Assume POST request if there is a body. */ if (body) { curl_easy_setopt(ctx->curl, CURLOPT_POSTFIELDS, body); curl_easy_setopt(ctx->curl, CURLOPT_POSTFIELDSIZE, strlen(body)); } } static void invoke(struct apic *req, struct context *ctx) { ctx->code = curl_easy_perform(ctx->curl); /* Close file descriptor to get its output. */ fclose(ctx->fp); ctx->fp = NULL; if (ctx->code != CURLE_OK) snprintf(req->error, sizeof (req->error), "%s", curl_easy_strerror(ctx->code)); else { curl_easy_getinfo(ctx->curl, CURLINFO_RESPONSE_CODE, &req->status); if (req->status != 200) snprintf(req->error, sizeof (req->error), "HTTP returned %ld", req->status); else if (ctx->out[0] && !(ctx->doc = json_loads(ctx->out, 0, &ctx->error))) snprintf(req->error, sizeof (req->error), "JSON parse error: %s", ctx->error.text); } } static void finish(struct context *ctx) { if (ctx->fp) fclose(ctx->fp); free(ctx->out); if (ctx->curl) curl_easy_cleanup(ctx->curl); if (ctx->headers) curl_slist_free_all(ctx->headers); } static json_t * perform(struct apic *req, const char *body, const char *fmt, va_list ap) { struct context ctx = {0}; init_url(&ctx, fmt, ap); init_output(&ctx); init_curl(&ctx, body); /* Perform that request now to obtain data. */ invoke(req, &ctx); finish(&ctx); return ctx.doc; } static json_t * get(struct apic *req, const char *fmt, ...) { va_list ap; json_t *ret; va_start(ap, fmt); ret = perform(req, NULL, fmt, ap); va_end(ap); return ret; } static int create(struct apic *req, json_t *doc, const char *fmt, ...) { va_list ap; json_t *ret; char *body; memset(req, 0, sizeof (*req)); body = util_json_dump(doc); va_start(ap, fmt); ret = perform(req, body, fmt, ap); va_end(ap); /* TODO: update id. */ (void)ret; free(body); json_decref(ret); return req->error[0] ? -1 : 0; } json_t * apic_get(struct apic *req, const char *fmt, ...) { assert(req); assert(fmt); va_list ap; json_t *ret; va_start(ap, fmt); ret = perform(req, NULL, fmt, ap); va_end(ap); return ret; } json_t * apic_post(struct apic *req, const json_t *doc, const char *fmt, ...) { assert(req); assert(fmt); va_list ap; json_t *ret; char *body; if (!(body = json_dumps(doc, JSON_COMPACT))) util_die("%s", strerror(ENOMEM)); va_start(ap, fmt); ret = perform(req, body, fmt, ap); va_end(ap); free(body); return ret; } int apic_job_add(struct apic *req, json_t *job) { assert(req); assert(job); return create(req, job, "api/v1/jobs"); } json_t * apic_job_todo(struct apic *req, const char *worker_name) { assert(req); assert(worker_name); return get(req, "api/v1/todo/%s", worker_name); } int apic_jobresult_add(struct apic *req, json_t *res) { assert(req); assert(res); return create(req, res, "api/v1/jobresults"); } int apic_project_save(struct apic *req, json_t *p) { assert(req); assert(p); return create(req, p, "api/v1/projects"); } json_t * apic_project_list(struct apic *req) { assert(req); return get(req, "api/v1/projects"); } json_t * apic_project_find(struct apic *req, const char *name) { assert(req); assert(name); return get(req, "api/v1/projects/%s", name); } int apic_worker_save(struct apic *req, json_t *wk) { assert(req); assert(wk); return create(req, wk, "api/v1/workers"); } json_t * apic_worker_list(struct apic *req) { assert(req); return get(req, "api/v1/workers"); } json_t * apic_worker_find(struct apic *req, const char *name) { assert(req); assert(name); return get(req, "api/v1/workers/%s", name); }