Mercurial > sci
view libsci/apic.c @ 37:62aefd02ac59
make: remove config.mk
author | David Demelier <markand@malikania.fr> |
---|---|
date | Sun, 07 Aug 2022 08:41:06 +0200 |
parents | 1d0ddf9e6efd |
children | b474f0985e39 |
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 curlpack { CURL *curl; CURLcode code; struct curl_slist *headers; }; 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 char * create_url(const char *fmt, va_list args) { static _Thread_local char ret[1024]; char page[128]; va_list ap; ret[0] = 0; va_copy(ap, args); vsnprintf(page, sizeof (page), fmt, ap); va_end(ap); snprintf(ret, sizeof (ret), "%s/%s", apiconf.baseurl, page); return ret; } static inline FILE * create_file(char **buf, size_t *bufsz) { FILE *fp; *buf = NULL; *bufsz = 0; if (!(fp = open_memstream(buf, bufsz))) util_die("open_memstream: %s\n", strerror(errno)); return fp; } static struct curlpack create_curl(FILE *fp, const char *body, const char *url) { struct curlpack pack = {0}; pack.headers = curl_slist_append(pack.headers, "Content-Type: application/json"); pack.curl = curl_easy_init(); curl_easy_setopt(pack.curl, CURLOPT_HTTPHEADER, pack.headers); curl_easy_setopt(pack.curl, CURLOPT_URL, url); curl_easy_setopt(pack.curl, CURLOPT_FOLLOWLOCATION, 1L); curl_easy_setopt(pack.curl, CURLOPT_TIMEOUT, 3L); curl_easy_setopt(pack.curl, CURLOPT_WRITEFUNCTION, writer); curl_easy_setopt(pack.curl, CURLOPT_WRITEDATA, fp); curl_easy_setopt(pack.curl, CURLOPT_NOSIGNAL, 1L); /* Assume POST request if there is a body. */ if (body) { curl_easy_setopt(pack.curl, CURLOPT_POSTFIELDS, body); curl_easy_setopt(pack.curl, CURLOPT_POSTFIELDSIZE, strlen(body)); } pack.code = curl_easy_perform(pack.curl); return pack; } static json_t * perform(struct apic *req, const char *body, const char *fmt, va_list ap) { FILE *fp; char *response, *url; size_t responsesz; json_t *doc = NULL; json_error_t error; struct curlpack curl; memset(req, 0, sizeof (*req)); url = create_url(fmt, ap); fp = create_file(&response, &responsesz); curl = create_curl(fp, body, url); /* Perform that request now. */ fclose(fp); if (curl.code != CURLE_OK) snprintf(req->error, sizeof (req->error), "%s", curl_easy_strerror(curl.code)); else { curl_easy_getinfo(curl.curl, CURLINFO_RESPONSE_CODE, &req->status); if (req->status != 200) snprintf(req->error, sizeof (req->error), "HTTP returned %ld", req->status); if (response[0] && !(doc = json_loads(response, 0, &error))) snprintf(req->error, sizeof (req->error), "JSON parse error: %s", error.text); } curl_easy_cleanup(curl.curl); curl_slist_free_all(curl.headers); free(response); return 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); if (!ret || (!json_is_object(ret) && !json_is_array(ret))) snprintf(req->error, sizeof (req->error), "invalid JSON document received"); 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 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); }