From 8d7c7d93a5a530ba4a0e52a4b813dfdc88421238 Mon Sep 17 00:00:00 2001 From: Carl Niklas Rydberg Date: Sat, 24 Jan 2026 06:50:53 +0100 Subject: [PATCH] http out --- CMakeLists.txt | 4 +- src/amduatd.c | 689 +--------------------------------- src/amduatd_caps.c | 72 +--- src/amduatd_concepts.c | 88 +---- src/amduatd_http.c | 819 +++++++++++++++++++++++++++++++++++++++++ src/amduatd_http.h | 116 ++++++ src/amduatd_ui.c | 38 +- src/amduatd_ui.h | 20 +- 8 files changed, 951 insertions(+), 895 deletions(-) create mode 100644 src/amduatd_http.c create mode 100644 src/amduatd_http.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 8316992..90ef5d9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -24,8 +24,8 @@ target_link_libraries(amduat_federation PRIVATE amduat_asl amduat_enc amduat_util amduat_fed ) -set(amduatd_sources src/amduatd.c src/amduatd_caps.c src/amduatd_space.c - src/amduatd_concepts.c) +set(amduatd_sources src/amduatd.c src/amduatd_http.c src/amduatd_caps.c + src/amduatd_space.c src/amduatd_concepts.c) if(AMDUATD_ENABLE_UI) list(APPEND amduatd_sources src/amduatd_ui.c) endif() diff --git a/src/amduatd.c b/src/amduatd.c index 0df8103..2e9a1cb 100644 --- a/src/amduatd.c +++ b/src/amduatd.c @@ -34,6 +34,7 @@ #include "amduat/util/log.h" #include "amduat/hash/asl1.h" #include "amduatd_concepts.h" +#include "amduatd_http.h" #include "amduatd_ui.h" #include "amduatd_caps.h" #include "amduatd_space.h" @@ -64,17 +65,6 @@ typedef struct amduatd_strbuf { size_t cap; } amduatd_strbuf_t; -bool amduatd_http_send_json(int fd, - int code, - const char *reason, - const char *json, - bool head_only); - -bool amduatd_send_json_error(int fd, - int code, - const char *reason, - const char *msg); - amduatd_ref_status_t amduatd_decode_ref_or_name_latest( amduat_asl_store_t *store, const amduat_asl_store_fs_config_t *cfg, @@ -282,97 +272,6 @@ static void amduatd_on_signal(int signo) { amduatd_should_exit = 1; } -bool amduatd_write_all(int fd, const uint8_t *buf, size_t len) { - size_t off = 0; - while (off < len) { - ssize_t n = write(fd, buf + off, len - off); - if (n < 0) { - if (errno == EINTR) { - continue; - } - return false; - } - if (n == 0) { - return false; - } - off += (size_t)n; - } - return true; -} - -bool amduatd_read_exact(int fd, uint8_t *buf, size_t len); - -bool amduatd_read_urandom(uint8_t *out, size_t len) { - int fd; - if (out == NULL || len == 0) { - return false; - } - fd = open("/dev/urandom", O_RDONLY); - if (fd < 0) { - return false; - } - if (!amduatd_read_exact(fd, out, len)) { - close(fd); - return false; - } - close(fd); - return true; -} - -bool amduatd_read_exact(int fd, uint8_t *buf, size_t len) { - size_t off = 0; - while (off < len) { - ssize_t n = read(fd, buf + off, len - off); - if (n < 0) { - if (errno == EINTR) { - continue; - } - return false; - } - if (n == 0) { - return false; - } - off += (size_t)n; - } - return true; -} - -static bool amduatd_read_line(int fd, char *buf, size_t cap, size_t *out_len) { - size_t len = 0; - while (len + 1 < cap) { - char c = 0; - ssize_t n = read(fd, &c, 1); - if (n < 0) { - if (errno == EINTR) { - continue; - } - return false; - } - if (n == 0) { - return false; - } - if (c == '\n') { - break; - } - buf[len++] = c; - } - if (len == 0) { - return false; - } - if (len > 0 && buf[len - 1] == '\r') { - len--; - } - buf[len] = '\0'; - if (out_len != NULL) { - *out_len = len; - } - return true; -} - -static void amduatd_http_req_init(amduatd_http_req_t *req) { - memset(req, 0, sizeof(*req)); -} - typedef struct { uid_t *uids; size_t len; @@ -493,258 +392,6 @@ static bool amduatd_get_peer_actor(int client_fd, return true; } -static bool amduatd_http_parse_request(int fd, amduatd_http_req_t *out_req) { - char line[2048]; - size_t line_len = 0; - char *sp1 = NULL; - char *sp2 = NULL; - - if (out_req == NULL) { - return false; - } - amduatd_http_req_init(out_req); - - if (!amduatd_read_line(fd, line, sizeof(line), &line_len)) { - return false; - } - - sp1 = strchr(line, ' '); - if (sp1 == NULL) { - return false; - } - *sp1++ = '\0'; - sp2 = strchr(sp1, ' '); - if (sp2 == NULL) { - return false; - } - *sp2++ = '\0'; - - if (strlen(line) >= sizeof(out_req->method)) { - return false; - } - strncpy(out_req->method, line, sizeof(out_req->method) - 1); - - if (strlen(sp1) >= sizeof(out_req->path)) { - return false; - } - strncpy(out_req->path, sp1, sizeof(out_req->path) - 1); - - for (;;) { - if (!amduatd_read_line(fd, line, sizeof(line), &line_len)) { - return false; - } - if (line_len == 0) { - break; - } - - if (strncasecmp(line, "Content-Length:", 15) == 0) { - const char *v = line + 15; - while (*v == ' ' || *v == '\t') { - v++; - } - out_req->content_length = (size_t)strtoull(v, NULL, 10); - } else if (strncasecmp(line, "Content-Type:", 13) == 0) { - const char *v = line + 13; - while (*v == ' ' || *v == '\t') { - v++; - } - strncpy(out_req->content_type, v, sizeof(out_req->content_type) - 1); - } else if (strncasecmp(line, "Accept:", 7) == 0) { - const char *v = line + 7; - while (*v == ' ' || *v == '\t') { - v++; - } - strncpy(out_req->accept, v, sizeof(out_req->accept) - 1); - } else if (strncasecmp(line, "X-Amduat-Type-Tag:", 18) == 0) { - const char *v = line + 18; - while (*v == ' ' || *v == '\t') { - v++; - } - strncpy(out_req->x_type_tag, v, sizeof(out_req->x_type_tag) - 1); - } else if (strncasecmp(line, "X-Amduat-Capability:", 20) == 0) { - const char *v = line + 20; - while (*v == ' ' || *v == '\t') { - v++; - } - strncpy(out_req->x_capability, v, sizeof(out_req->x_capability) - 1); - } - } - - return true; -} - -bool amduatd_http_send_status(int fd, - int code, - const char *reason, - const char *content_type, - const uint8_t *body, - size_t body_len, - bool head_only) { - char hdr[512]; - int n = snprintf(hdr, - sizeof(hdr), - "HTTP/1.1 %d %s\r\n" - "Content-Length: %zu\r\n" - "Content-Type: %s\r\n" - "Connection: close\r\n" - "\r\n", - code, - reason != NULL ? reason : "", - body_len, - content_type != NULL ? content_type : "text/plain"); - if (n <= 0 || (size_t)n >= sizeof(hdr)) { - return false; - } - if (!amduatd_write_all(fd, (const uint8_t *)hdr, (size_t)n)) { - return false; - } - if (!head_only && body != NULL && body_len != 0) { - return amduatd_write_all(fd, body, body_len); - } - return true; -} - -bool amduatd_http_send_text(int fd, - int code, - const char *reason, - const char *text, - bool head_only) { - const uint8_t *body = (const uint8_t *)(text != NULL ? text : ""); - size_t len = text != NULL ? strlen(text) : 0; - return amduatd_http_send_status( - fd, code, reason, "text/plain; charset=utf-8", body, len, head_only); -} - -bool amduatd_http_send_json(int fd, - int code, - const char *reason, - const char *json, - bool head_only) { - const uint8_t *body = (const uint8_t *)(json != NULL ? json : "{}"); - size_t len = json != NULL ? strlen(json) : 2; - return amduatd_http_send_status( - fd, code, reason, "application/json", body, len, head_only); -} - -static bool amduatd_http_req_wants_html(const amduatd_http_req_t *req) { - if (req == NULL) { - return false; - } - if (req->accept[0] == '\0') { - return false; - } - return strstr(req->accept, "text/html") != NULL; -} - -static bool amduatd_http_send_not_found(int fd, const amduatd_http_req_t *req) { - if (amduatd_http_req_wants_html(req)) { - const char *path = (req != NULL && req->path[0] != '\0') ? req->path : "/"; - const char *method = - (req != NULL && req->method[0] != '\0') ? req->method : "GET"; - char html[4096]; - int n = snprintf( - html, - sizeof(html), - "\n" - "\n" - "\n" - " \n" - " \n" - " amduatd — Not Found\n" - " \n" - "\n" - "\n" - "
\n" - "
\n" - "

404 — Not Found

\n" - "

amduatd didn’t recognize %s %s.

\n" - "

Try one of these:

\n" - " \n" - "

Artifact bytes: /v1/artifacts/<ref>

\n" - "
\n" - "
\n" - "\n" - "\n", - method, - path); - if (n <= 0 || (size_t)n >= sizeof(html)) { - return amduatd_http_send_text(fd, 404, "Not Found", "not found\n", false); - } - return amduatd_http_send_status(fd, - 404, - "Not Found", - "text/html; charset=utf-8", - (const uint8_t *)html, - (size_t)n, - false); - } - return amduatd_http_send_text(fd, 404, "Not Found", "not found\n", false); -} - -const char *amduatd_query_param(const char *path, - const char *key, - char *out_value, - size_t out_cap) { - const char *q = strchr(path, '?'); - size_t key_len = 0; - if (out_value != NULL && out_cap != 0) { - out_value[0] = '\0'; - } - if (q == NULL || key == NULL || out_value == NULL || out_cap == 0) { - return NULL; - } - q++; - key_len = strlen(key); - - while (*q != '\0') { - const char *k = q; - const char *eq = strchr(k, '='); - const char *amp = strchr(k, '&'); - size_t klen = 0; - const char *v = NULL; - size_t vlen = 0; - - if (amp == NULL) { - amp = q + strlen(q); - } - if (eq == NULL || eq > amp) { - q = (*amp == '&') ? (amp + 1) : amp; - continue; - } - klen = (size_t)(eq - k); - if (klen == key_len && strncmp(k, key, key_len) == 0) { - v = eq + 1; - vlen = (size_t)(amp - v); - if (vlen >= out_cap) { - vlen = out_cap - 1; - } - memcpy(out_value, v, vlen); - out_value[vlen] = '\0'; - return out_value; - } - q = (*amp == '&') ? (amp + 1) : amp; - } - - return NULL; -} - static void amduatd_strbuf_free(amduatd_strbuf_t *b) { if (b == NULL) { return; @@ -816,73 +463,6 @@ static bool amduatd_strbuf_append_char(amduatd_strbuf_t *b, char c) { return amduatd_strbuf_append(b, &c, 1u); } -const char *amduatd_json_skip_ws(const char *p, const char *end) { - while (p < end) { - char c = *p; - if (c != ' ' && c != '\t' && c != '\n' && c != '\r') { - break; - } - p++; - } - return p; -} - -bool amduatd_json_expect(const char **p, - const char *end, - char expected) { - const char *cur; - if (p == NULL || *p == NULL) { - return false; - } - cur = amduatd_json_skip_ws(*p, end); - if (cur >= end || *cur != expected) { - return false; - } - *p = cur + 1; - return true; -} - -bool amduatd_json_parse_string_noesc(const char **p, - const char *end, - const char **out_start, - size_t *out_len) { - const char *cur; - const char *s; - if (out_start != NULL) { - *out_start = NULL; - } - if (out_len != NULL) { - *out_len = 0; - } - if (p == NULL || *p == NULL) { - return false; - } - cur = amduatd_json_skip_ws(*p, end); - if (cur >= end || *cur != '"') { - return false; - } - cur++; - s = cur; - while (cur < end) { - unsigned char c = (unsigned char)*cur; - if (c == '"') { - if (out_start != NULL) { - *out_start = s; - } - if (out_len != NULL) { - *out_len = (size_t)(cur - s); - } - *p = cur + 1; - return true; - } - if (c == '\\' || c < 0x20u) { - return false; - } - cur++; - } - return false; -} - static bool amduatd_json_parse_u32(const char **p, const char *end, uint32_t *out) { @@ -930,53 +510,6 @@ static bool amduatd_json_parse_u32(const char **p, return true; } -bool amduatd_json_parse_u64(const char **p, - const char *end, - uint64_t *out) { - const char *cur; - const char *start; - unsigned long long v; - char *tmp = NULL; - char *endp = NULL; - size_t n; - - if (out != NULL) { - *out = 0; - } - if (p == NULL || *p == NULL || out == NULL) { - return false; - } - - cur = amduatd_json_skip_ws(*p, end); - start = cur; - if (cur >= end) { - return false; - } - if (*cur < '0' || *cur > '9') { - return false; - } - cur++; - while (cur < end && *cur >= '0' && *cur <= '9') { - cur++; - } - n = (size_t)(cur - start); - tmp = (char *)malloc(n + 1u); - if (tmp == NULL) { - return false; - } - memcpy(tmp, start, n); - tmp[n] = '\0'; - errno = 0; - v = strtoull(tmp, &endp, 10); - free(tmp); - if (errno != 0 || endp == NULL || *endp != '\0') { - return false; - } - *out = (uint64_t)v; - *p = cur; - return true; -} - static bool amduatd_json_parse_u32_loose(const char **p, const char *end, uint32_t *out) { @@ -1070,183 +603,6 @@ static void amduatd_json_peek_token(const char *p, out[n] = '\0'; } -static bool amduatd_json_skip_string(const char **p, const char *end) { - const char *cur; - if (p == NULL || *p == NULL) { - return false; - } - cur = amduatd_json_skip_ws(*p, end); - if (cur >= end || *cur != '"') { - return false; - } - cur++; - while (cur < end) { - unsigned char c = (unsigned char)*cur++; - if (c == '"') { - *p = cur; - return true; - } - if (c == '\\') { - if (cur >= end) { - return false; - } - cur++; - } else if (c < 0x20u) { - return false; - } - } - return false; -} - -bool amduatd_json_skip_value(const char **p, - const char *end, - int depth); - -static bool amduatd_json_skip_array(const char **p, - const char *end, - int depth) { - const char *cur; - if (!amduatd_json_expect(p, end, '[')) { - return false; - } - cur = amduatd_json_skip_ws(*p, end); - if (cur < end && *cur == ']') { - *p = cur + 1; - return true; - } - for (;;) { - if (!amduatd_json_skip_value(p, end, depth + 1)) { - return false; - } - cur = amduatd_json_skip_ws(*p, end); - if (cur >= end) { - return false; - } - if (*cur == ',') { - *p = cur + 1; - continue; - } - if (*cur == ']') { - *p = cur + 1; - return true; - } - return false; - } -} - -static bool amduatd_json_skip_object(const char **p, - const char *end, - int depth) { - const char *cur; - if (!amduatd_json_expect(p, end, '{')) { - return false; - } - cur = amduatd_json_skip_ws(*p, end); - if (cur < end && *cur == '}') { - *p = cur + 1; - return true; - } - for (;;) { - if (!amduatd_json_skip_string(p, end)) { - return false; - } - if (!amduatd_json_expect(p, end, ':')) { - return false; - } - if (!amduatd_json_skip_value(p, end, depth + 1)) { - return false; - } - cur = amduatd_json_skip_ws(*p, end); - if (cur >= end) { - return false; - } - if (*cur == ',') { - *p = cur + 1; - continue; - } - if (*cur == '}') { - *p = cur + 1; - return true; - } - return false; - } -} - -bool amduatd_json_skip_value(const char **p, - const char *end, - int depth) { - const char *cur; - if (p == NULL || *p == NULL) { - return false; - } - if (depth > 32) { - return false; - } - cur = amduatd_json_skip_ws(*p, end); - if (cur >= end) { - return false; - } - if (*cur == '"') { - return amduatd_json_skip_string(p, end); - } - if (*cur == '{') { - return amduatd_json_skip_object(p, end, depth); - } - if (*cur == '[') { - return amduatd_json_skip_array(p, end, depth); - } - if (*cur == 't' && (end - cur) >= 4 && memcmp(cur, "true", 4) == 0) { - *p = cur + 4; - return true; - } - if (*cur == 'f' && (end - cur) >= 5 && memcmp(cur, "false", 5) == 0) { - *p = cur + 5; - return true; - } - if (*cur == 'n' && (end - cur) >= 4 && memcmp(cur, "null", 4) == 0) { - *p = cur + 4; - return true; - } - if (*cur == '-' || (*cur >= '0' && *cur <= '9')) { - cur++; - while (cur < end) { - char c = *cur; - if ((c >= '0' && c <= '9') || c == '.' || c == 'e' || c == 'E' || - c == '+' || c == '-') { - cur++; - continue; - } - break; - } - *p = cur; - return true; - } - return false; -} - -bool amduatd_copy_json_str(const char *s, - size_t len, - char **out) { - char *buf; - if (out == NULL) { - return false; - } - *out = NULL; - if (len > (SIZE_MAX - 1u)) { - return false; - } - buf = (char *)malloc(len + 1u); - if (buf == NULL) { - return false; - } - if (len != 0) { - memcpy(buf, s, len); - } - buf[len] = '\0'; - *out = buf; - return true; -} - static bool amduatd_decode_ref_hex_str(const char *s, size_t len, amduat_reference_t *out_ref) { @@ -1264,47 +620,6 @@ static bool amduatd_decode_ref_hex_str(const char *s, return ok; } -bool amduatd_send_json_error(int fd, - int code, - const char *reason, - const char *msg) { - amduatd_strbuf_t b; - memset(&b, 0, sizeof(b)); - if (!amduatd_strbuf_append_cstr(&b, "{\"error\":\"") || - !amduatd_strbuf_append_cstr(&b, msg != NULL ? msg : "error") || - !amduatd_strbuf_append_cstr(&b, "\"}\n")) { - amduatd_strbuf_free(&b); - return amduatd_http_send_text(fd, 500, "Internal Server Error", "error\n", - false); - } - { - bool ok = amduatd_http_send_json(fd, code, reason, b.data, false); - amduatd_strbuf_free(&b); - return ok; - } -} - -static void amduatd_path_without_query(const char *path, - char *out, - size_t cap) { - const char *q = NULL; - size_t n = 0; - if (out == NULL || cap == 0) { - return; - } - out[0] = '\0'; - if (path == NULL) { - return; - } - q = strchr(path, '?'); - n = q != NULL ? (size_t)(q - path) : strlen(path); - if (n >= cap) { - n = cap - 1; - } - memcpy(out, path, n); - out[n] = '\0'; -} - static bool amduatd_parse_type_tag_hex(const char *text, bool *out_has_type_tag, amduat_type_tag_t *out_type_tag) { @@ -4695,7 +4010,7 @@ static bool amduatd_handle_conn(int fd, ok = amduatd_http_send_not_found(fd, &req); conn_cleanup: - free((void *)req.actor.data); + amduatd_http_req_free(&req); return ok; } diff --git a/src/amduatd_caps.c b/src/amduatd_caps.c index bf5da80..4f8b818 100644 --- a/src/amduatd_caps.c +++ b/src/amduatd_caps.c @@ -1,4 +1,5 @@ #include "amduatd_caps.h" +#include "amduatd_http.h" #include "amduat/asl/asl_pointer_fs.h" #include "amduat/asl/ref_text.h" @@ -23,45 +24,6 @@ #include #include -bool amduatd_write_all(int fd, const uint8_t *buf, size_t len); -bool amduatd_read_exact(int fd, uint8_t *buf, size_t len); -bool amduatd_read_urandom(uint8_t *out, size_t len); - -bool amduatd_http_send_json(int fd, - int code, - const char *reason, - const char *json, - bool head_only); - -bool amduatd_send_json_error(int fd, - int code, - const char *reason, - const char *msg); - -const char *amduatd_query_param(const char *path, - const char *key, - char *out_value, - size_t out_cap); - -const char *amduatd_json_skip_ws(const char *p, const char *end); - -bool amduatd_json_expect(const char **p, - const char *end, - char expected); - -bool amduatd_json_parse_string_noesc(const char **p, - const char *end, - const char **out, - size_t *out_len); - -bool amduatd_json_parse_u64(const char **p, const char *end, uint64_t *out); - -bool amduatd_json_skip_value(const char **p, - const char *end, - int depth); - -bool amduatd_copy_json_str(const char *s, size_t len, char **out); - amduatd_ref_status_t amduatd_decode_ref_or_name_latest( amduat_asl_store_t *store, const amduat_asl_store_fs_config_t *cfg, @@ -1152,28 +1114,6 @@ static bool amduatd_cap_verify_token(const amduatd_caps_t *cap, return true; } -static bool amduatd_caps_path_without_query(const char *path, - char *out, - size_t cap) { - const char *q = NULL; - size_t n = 0; - if (out == NULL || cap == 0) { - return false; - } - out[0] = '\0'; - if (path == NULL) { - return false; - } - q = strchr(path, '?'); - n = q != NULL ? (size_t)(q - path) : strlen(path); - if (n >= cap) { - n = cap - 1; - } - memcpy(out, path, n); - out[n] = '\0'; - return true; -} - static bool amduatd_handle_post_capabilities( int fd, amduat_asl_store_t *store, @@ -1862,7 +1802,7 @@ bool amduatd_caps_can_handle(const amduatd_http_req_t *req) { if (req == NULL) { return false; } - if (!amduatd_caps_path_without_query(req->path, no_query, sizeof(no_query))) { + if (!amduatd_path_without_query(req->path, no_query, sizeof(no_query))) { return false; } if (strcmp(req->method, "POST") == 0 && @@ -1887,11 +1827,11 @@ bool amduatd_caps_handle(amduatd_ctx_t *ctx, if (!amduatd_caps_can_handle(req)) { return false; } - if (!amduatd_caps_path_without_query(req->path, no_query, sizeof(no_query))) { + if (!amduatd_path_without_query(req->path, no_query, sizeof(no_query))) { resp->ok = amduatd_send_json_error(resp->fd, - 400, - "Bad Request", - "invalid path"); + 400, + "Bad Request", + "invalid path"); return true; } diff --git a/src/amduatd_concepts.c b/src/amduatd_concepts.c index 50823ca..227a0c7 100644 --- a/src/amduatd_concepts.c +++ b/src/amduatd_concepts.c @@ -4,6 +4,7 @@ #define _POSIX_C_SOURCE 200809L #include "amduatd_concepts.h" +#include "amduatd_http.h" #include "amduatd_caps.h" @@ -34,37 +35,6 @@ #include #include -bool amduatd_read_exact(int fd, uint8_t *buf, size_t len); -bool amduatd_read_urandom(uint8_t *out, size_t len); - -bool amduatd_http_send_json(int fd, - int code, - const char *reason, - const char *json, - bool head_only); - -bool amduatd_send_json_error(int fd, - int code, - const char *reason, - const char *msg); - -const char *amduatd_json_skip_ws(const char *p, const char *end); - -bool amduatd_json_expect(const char **p, - const char *end, - char expected); - -bool amduatd_json_parse_string_noesc(const char **p, - const char *end, - const char **out, - size_t *out_len); - -bool amduatd_json_skip_value(const char **p, - const char *end, - int depth); - -bool amduatd_copy_json_str(const char *s, size_t len, char **out); - typedef struct amduatd_strbuf { char *data; size_t len; @@ -2995,37 +2965,6 @@ concepts_cleanup: return ok; } -static bool amduatd_path_extract_name(const char *path, - const char *prefix, - char *out, - size_t cap) { - size_t plen; - const char *p; - size_t len; - - if (out != NULL && cap != 0) { - out[0] = '\0'; - } - if (path == NULL || prefix == NULL || out == NULL || cap == 0) { - return false; - } - plen = strlen(prefix); - if (strncmp(path, prefix, plen) != 0) { - return false; - } - p = path + plen; - if (*p == '\0') { - return false; - } - len = strlen(p); - if (len >= cap) { - len = cap - 1; - } - memcpy(out, p, len); - out[len] = '\0'; - return true; -} - static bool amduatd_handle_post_concept_publish(int fd, amduat_asl_store_t *store, amduatd_concepts_t *concepts, @@ -3219,34 +3158,13 @@ static bool amduatd_handle_get_resolve(int fd, return amduatd_http_send_json(fd, 200, "OK", json, false); } -static void amduatd_concepts_path_without_query(const char *path, - char *out, - size_t cap) { - const char *q = NULL; - size_t n = 0; - if (out == NULL || cap == 0) { - return; - } - out[0] = '\0'; - if (path == NULL) { - return; - } - q = strchr(path, '?'); - n = q != NULL ? (size_t)(q - path) : strlen(path); - if (n >= cap) { - n = cap - 1; - } - memcpy(out, path, n); - out[n] = '\0'; -} - bool amduatd_concepts_can_handle(const amduatd_http_req_t *req) { char no_query[1024]; if (req == NULL) { return false; } - amduatd_concepts_path_without_query(req->path, no_query, sizeof(no_query)); + amduatd_path_without_query(req->path, no_query, sizeof(no_query)); if (strcmp(req->method, "POST") == 0 && strcmp(no_query, "/v1/concepts") == 0) { @@ -3286,7 +3204,7 @@ bool amduatd_concepts_handle(amduatd_ctx_t *ctx, return false; } - amduatd_concepts_path_without_query(req->path, no_query, sizeof(no_query)); + amduatd_path_without_query(req->path, no_query, sizeof(no_query)); if (strcmp(req->method, "POST") == 0 && strcmp(no_query, "/v1/concepts") == 0) { resp->ok = amduatd_handle_post_concepts(resp->fd, diff --git a/src/amduatd_http.c b/src/amduatd_http.c new file mode 100644 index 0000000..1fd40bb --- /dev/null +++ b/src/amduatd_http.c @@ -0,0 +1,819 @@ +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif +#define _POSIX_C_SOURCE 200809L + +#include "amduatd_http.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef struct amduatd_http_strbuf { + char *data; + size_t len; + size_t cap; +} amduatd_http_strbuf_t; + +static void amduatd_http_strbuf_free(amduatd_http_strbuf_t *b) { + if (b == NULL) { + return; + } + free(b->data); + b->data = NULL; + b->len = 0; + b->cap = 0; +} + +static bool amduatd_http_strbuf_reserve(amduatd_http_strbuf_t *b, size_t extra) { + size_t need; + size_t next_cap; + char *next; + + if (b == NULL) { + return false; + } + if (extra > (SIZE_MAX - b->len)) { + return false; + } + need = b->len + extra; + if (need <= b->cap) { + return true; + } + next_cap = b->cap != 0 ? b->cap : 256u; + while (next_cap < need) { + if (next_cap > (SIZE_MAX / 2u)) { + next_cap = need; + break; + } + next_cap *= 2u; + } + next = (char *)realloc(b->data, next_cap); + if (next == NULL) { + return false; + } + b->data = next; + b->cap = next_cap; + return true; +} + +static bool amduatd_http_strbuf_append(amduatd_http_strbuf_t *b, + const char *s, + size_t n) { + if (b == NULL) { + return false; + } + if (n == 0) { + return true; + } + if (s == NULL) { + return false; + } + if (!amduatd_http_strbuf_reserve(b, n + 1u)) { + return false; + } + memcpy(b->data + b->len, s, n); + b->len += n; + b->data[b->len] = '\0'; + return true; +} + +static bool amduatd_http_strbuf_append_cstr(amduatd_http_strbuf_t *b, + const char *s) { + return amduatd_http_strbuf_append( + b, s != NULL ? s : "", s != NULL ? strlen(s) : 0u); +} + +bool amduatd_write_all(int fd, const uint8_t *buf, size_t len) { + size_t off = 0; + while (off < len) { + ssize_t n = write(fd, buf + off, len - off); + if (n < 0) { + if (errno == EINTR) { + continue; + } + return false; + } + if (n == 0) { + return false; + } + off += (size_t)n; + } + return true; +} + +bool amduatd_read_exact(int fd, uint8_t *buf, size_t len) { + size_t off = 0; + while (off < len) { + ssize_t n = read(fd, buf + off, len - off); + if (n < 0) { + if (errno == EINTR) { + continue; + } + return false; + } + if (n == 0) { + return false; + } + off += (size_t)n; + } + return true; +} + +bool amduatd_read_urandom(uint8_t *out, size_t len) { + int fd; + if (out == NULL || len == 0) { + return false; + } + fd = open("/dev/urandom", O_RDONLY); + if (fd < 0) { + return false; + } + if (!amduatd_read_exact(fd, out, len)) { + close(fd); + return false; + } + close(fd); + return true; +} + +static bool amduatd_read_line(int fd, char *buf, size_t cap, size_t *out_len) { + size_t len = 0; + while (len + 1 < cap) { + char c = 0; + ssize_t n = read(fd, &c, 1); + if (n < 0) { + if (errno == EINTR) { + continue; + } + return false; + } + if (n == 0) { + return false; + } + if (c == '\n') { + break; + } + buf[len++] = c; + } + if (len == 0) { + return false; + } + if (len > 0 && buf[len - 1] == '\r') { + len--; + } + buf[len] = '\0'; + if (out_len != NULL) { + *out_len = len; + } + return true; +} + +static void amduatd_http_req_init(amduatd_http_req_t *req) { + memset(req, 0, sizeof(*req)); +} + +bool amduatd_http_parse_request(int fd, amduatd_http_req_t *out_req) { + char line[2048]; + size_t line_len = 0; + char *sp1 = NULL; + char *sp2 = NULL; + + if (out_req == NULL) { + return false; + } + amduatd_http_req_init(out_req); + + if (!amduatd_read_line(fd, line, sizeof(line), &line_len)) { + return false; + } + + sp1 = strchr(line, ' '); + if (sp1 == NULL) { + return false; + } + *sp1++ = '\0'; + sp2 = strchr(sp1, ' '); + if (sp2 == NULL) { + return false; + } + *sp2++ = '\0'; + + if (strlen(line) >= sizeof(out_req->method)) { + return false; + } + strncpy(out_req->method, line, sizeof(out_req->method) - 1); + + if (strlen(sp1) >= sizeof(out_req->path)) { + return false; + } + strncpy(out_req->path, sp1, sizeof(out_req->path) - 1); + + for (;;) { + if (!amduatd_read_line(fd, line, sizeof(line), &line_len)) { + return false; + } + if (line_len == 0) { + break; + } + + if (strncasecmp(line, "Content-Length:", 15) == 0) { + const char *v = line + 15; + while (*v == ' ' || *v == '\t') { + v++; + } + out_req->content_length = (size_t)strtoull(v, NULL, 10); + } else if (strncasecmp(line, "Content-Type:", 13) == 0) { + const char *v = line + 13; + while (*v == ' ' || *v == '\t') { + v++; + } + strncpy(out_req->content_type, v, sizeof(out_req->content_type) - 1); + } else if (strncasecmp(line, "Accept:", 7) == 0) { + const char *v = line + 7; + while (*v == ' ' || *v == '\t') { + v++; + } + strncpy(out_req->accept, v, sizeof(out_req->accept) - 1); + } else if (strncasecmp(line, "X-Amduat-Type-Tag:", 18) == 0) { + const char *v = line + 18; + while (*v == ' ' || *v == '\t') { + v++; + } + strncpy(out_req->x_type_tag, v, sizeof(out_req->x_type_tag) - 1); + } else if (strncasecmp(line, "X-Amduat-Capability:", 20) == 0) { + const char *v = line + 20; + while (*v == ' ' || *v == '\t') { + v++; + } + strncpy(out_req->x_capability, v, sizeof(out_req->x_capability) - 1); + } + } + + return true; +} + +void amduatd_http_req_free(amduatd_http_req_t *req) { + if (req == NULL) { + return; + } + free((void *)req->actor.data); + req->actor = amduat_octets(NULL, 0u); + req->has_actor = false; + req->has_uid = false; + req->uid = 0; +} + +bool amduatd_http_send_response(int fd, const amduatd_http_resp_t *resp) { + if (resp == NULL) { + return false; + } + return amduatd_http_send_status(fd, + resp->status, + resp->reason, + resp->content_type, + resp->body, + resp->body_len, + resp->head_only); +} + +bool amduatd_http_send_status(int fd, + int code, + const char *reason, + const char *content_type, + const uint8_t *body, + size_t body_len, + bool head_only) { + char hdr[512]; + int n = snprintf(hdr, + sizeof(hdr), + "HTTP/1.1 %d %s\r\n" + "Content-Length: %zu\r\n" + "Content-Type: %s\r\n" + "Connection: close\r\n" + "\r\n", + code, + reason != NULL ? reason : "", + body_len, + content_type != NULL ? content_type : "text/plain"); + if (n <= 0 || (size_t)n >= sizeof(hdr)) { + return false; + } + if (!amduatd_write_all(fd, (const uint8_t *)hdr, (size_t)n)) { + return false; + } + if (!head_only && body != NULL && body_len != 0) { + return amduatd_write_all(fd, body, body_len); + } + return true; +} + +bool amduatd_http_send_text(int fd, + int code, + const char *reason, + const char *text, + bool head_only) { + const uint8_t *body = (const uint8_t *)(text != NULL ? text : ""); + size_t len = text != NULL ? strlen(text) : 0; + return amduatd_http_send_status( + fd, code, reason, "text/plain; charset=utf-8", body, len, head_only); +} + +bool amduatd_http_send_json(int fd, + int code, + const char *reason, + const char *json, + bool head_only) { + const uint8_t *body = (const uint8_t *)(json != NULL ? json : "{}"); + size_t len = json != NULL ? strlen(json) : 2; + return amduatd_http_send_status( + fd, code, reason, "application/json", body, len, head_only); +} + +static bool amduatd_http_req_wants_html(const amduatd_http_req_t *req) { + if (req == NULL) { + return false; + } + if (req->accept[0] == '\0') { + return false; + } + return strstr(req->accept, "text/html") != NULL; +} + +bool amduatd_http_send_not_found(int fd, const amduatd_http_req_t *req) { + if (amduatd_http_req_wants_html(req)) { + const char *path = (req != NULL && req->path[0] != '\0') ? req->path : "/"; + const char *method = + (req != NULL && req->method[0] != '\0') ? req->method : "GET"; + char html[4096]; + int n = snprintf( + html, + sizeof(html), + "\n" + "\n" + "\n" + " \n" + " \n" + " amduatd — Not Found\n" + " \n" + "\n" + "\n" + "
\n" + "
\n" + "

404 — Not Found

\n" + "

amduatd didn’t recognize %s %s.

\n" + "

Try one of these:

\n" + " \n" + "

Artifact bytes: /v1/artifacts/<ref>

\n" + "
\n" + "
\n" + "\n" + "\n", + method, + path); + if (n <= 0 || (size_t)n >= sizeof(html)) { + return amduatd_http_send_text(fd, 404, "Not Found", "not found\n", false); + } + return amduatd_http_send_status(fd, + 404, + "Not Found", + "text/html; charset=utf-8", + (const uint8_t *)html, + (size_t)n, + false); + } + return amduatd_http_send_text(fd, 404, "Not Found", "not found\n", false); +} + +bool amduatd_send_json_error(int fd, + int code, + const char *reason, + const char *msg) { + amduatd_http_strbuf_t b; + memset(&b, 0, sizeof(b)); + if (!amduatd_http_strbuf_append_cstr(&b, "{\"error\":\"") || + !amduatd_http_strbuf_append_cstr(&b, msg != NULL ? msg : "error") || + !amduatd_http_strbuf_append_cstr(&b, "\"}\n")) { + amduatd_http_strbuf_free(&b); + return amduatd_http_send_text(fd, 500, "Internal Server Error", "error\n", + false); + } + { + bool ok = amduatd_http_send_json(fd, code, reason, b.data, false); + amduatd_http_strbuf_free(&b); + return ok; + } +} + +const char *amduatd_query_param(const char *path, + const char *key, + char *out_value, + size_t out_cap) { + const char *q = strchr(path, '?'); + size_t key_len = 0; + if (out_value != NULL && out_cap != 0) { + out_value[0] = '\0'; + } + if (q == NULL || key == NULL || out_value == NULL || out_cap == 0) { + return NULL; + } + q++; + key_len = strlen(key); + + while (*q != '\0') { + const char *k = q; + const char *eq = strchr(k, '='); + const char *amp = strchr(k, '&'); + size_t klen = 0; + const char *v = NULL; + size_t vlen = 0; + + if (amp == NULL) { + amp = q + strlen(q); + } + if (eq == NULL || eq > amp) { + q = (*amp == '&') ? (amp + 1) : amp; + continue; + } + klen = (size_t)(eq - k); + if (klen == key_len && strncmp(k, key, key_len) == 0) { + v = eq + 1; + vlen = (size_t)(amp - v); + if (vlen >= out_cap) { + vlen = out_cap - 1; + } + memcpy(out_value, v, vlen); + out_value[vlen] = '\0'; + return out_value; + } + q = (*amp == '&') ? (amp + 1) : amp; + } + + return NULL; +} + +bool amduatd_path_without_query(const char *path, char *out, size_t cap) { + const char *q = NULL; + size_t n = 0; + if (out == NULL || cap == 0) { + return false; + } + out[0] = '\0'; + if (path == NULL) { + return false; + } + q = strchr(path, '?'); + n = q != NULL ? (size_t)(q - path) : strlen(path); + if (n >= cap) { + n = cap - 1; + } + memcpy(out, path, n); + out[n] = '\0'; + return true; +} + +bool amduatd_path_extract_name(const char *path, + const char *prefix, + char *out, + size_t cap) { + size_t plen; + const char *p; + size_t len; + + if (out != NULL && cap != 0) { + out[0] = '\0'; + } + if (path == NULL || prefix == NULL || out == NULL || cap == 0) { + return false; + } + plen = strlen(prefix); + if (strncmp(path, prefix, plen) != 0) { + return false; + } + p = path + plen; + if (*p == '\0') { + return false; + } + len = strlen(p); + if (len >= cap) { + len = cap - 1; + } + memcpy(out, p, len); + out[len] = '\0'; + return true; +} + +const char *amduatd_json_skip_ws(const char *p, const char *end) { + while (p < end) { + char c = *p; + if (c != ' ' && c != '\t' && c != '\n' && c != '\r') { + break; + } + p++; + } + return p; +} + +bool amduatd_json_expect(const char **p, + const char *end, + char expected) { + const char *cur; + if (p == NULL || *p == NULL) { + return false; + } + cur = amduatd_json_skip_ws(*p, end); + if (cur >= end || *cur != expected) { + return false; + } + *p = cur + 1; + return true; +} + +bool amduatd_json_parse_string_noesc(const char **p, + const char *end, + const char **out_start, + size_t *out_len) { + const char *cur; + const char *s; + if (out_start != NULL) { + *out_start = NULL; + } + if (out_len != NULL) { + *out_len = 0; + } + if (p == NULL || *p == NULL) { + return false; + } + cur = amduatd_json_skip_ws(*p, end); + if (cur >= end || *cur != '"') { + return false; + } + cur++; + s = cur; + while (cur < end) { + unsigned char c = (unsigned char)*cur; + if (c == '"') { + if (out_start != NULL) { + *out_start = s; + } + if (out_len != NULL) { + *out_len = (size_t)(cur - s); + } + *p = cur + 1; + return true; + } + if (c == '\\' || c < 0x20u) { + return false; + } + cur++; + } + return false; +} + +bool amduatd_json_parse_u64(const char **p, + const char *end, + uint64_t *out) { + const char *cur; + const char *start; + unsigned long long v; + char *tmp = NULL; + char *endp = NULL; + size_t n; + + if (out != NULL) { + *out = 0; + } + if (p == NULL || *p == NULL || out == NULL) { + return false; + } + + cur = amduatd_json_skip_ws(*p, end); + start = cur; + if (cur >= end) { + return false; + } + if (*cur < '0' || *cur > '9') { + return false; + } + cur++; + while (cur < end && *cur >= '0' && *cur <= '9') { + cur++; + } + n = (size_t)(cur - start); + tmp = (char *)malloc(n + 1u); + if (tmp == NULL) { + return false; + } + memcpy(tmp, start, n); + tmp[n] = '\0'; + errno = 0; + v = strtoull(tmp, &endp, 10); + free(tmp); + if (errno != 0 || endp == NULL || *endp != '\0') { + return false; + } + *out = (uint64_t)v; + *p = cur; + return true; +} + +static bool amduatd_json_skip_string(const char **p, const char *end) { + const char *cur; + if (p == NULL || *p == NULL) { + return false; + } + cur = amduatd_json_skip_ws(*p, end); + if (cur >= end || *cur != '"') { + return false; + } + cur++; + while (cur < end) { + unsigned char c = (unsigned char)*cur++; + if (c == '"') { + *p = cur; + return true; + } + if (c == '\\') { + if (cur >= end) { + return false; + } + cur++; + } else if (c < 0x20u) { + return false; + } + } + return false; +} + +bool amduatd_json_skip_value(const char **p, + const char *end, + int depth); + +static bool amduatd_json_skip_array(const char **p, + const char *end, + int depth) { + const char *cur; + if (!amduatd_json_expect(p, end, '[')) { + return false; + } + cur = amduatd_json_skip_ws(*p, end); + if (cur < end && *cur == ']') { + *p = cur + 1; + return true; + } + for (;;) { + if (!amduatd_json_skip_value(p, end, depth + 1)) { + return false; + } + cur = amduatd_json_skip_ws(*p, end); + if (cur >= end) { + return false; + } + if (*cur == ',') { + *p = cur + 1; + continue; + } + if (*cur == ']') { + *p = cur + 1; + return true; + } + return false; + } +} + +static bool amduatd_json_skip_object(const char **p, + const char *end, + int depth) { + const char *cur; + if (!amduatd_json_expect(p, end, '{')) { + return false; + } + cur = amduatd_json_skip_ws(*p, end); + if (cur < end && *cur == '}') { + *p = cur + 1; + return true; + } + for (;;) { + if (!amduatd_json_skip_string(p, end)) { + return false; + } + if (!amduatd_json_expect(p, end, ':')) { + return false; + } + if (!amduatd_json_skip_value(p, end, depth + 1)) { + return false; + } + cur = amduatd_json_skip_ws(*p, end); + if (cur >= end) { + return false; + } + if (*cur == ',') { + *p = cur + 1; + continue; + } + if (*cur == '}') { + *p = cur + 1; + return true; + } + return false; + } +} + +bool amduatd_json_skip_value(const char **p, + const char *end, + int depth) { + const char *cur; + if (p == NULL || *p == NULL) { + return false; + } + if (depth > 32) { + return false; + } + cur = amduatd_json_skip_ws(*p, end); + if (cur >= end) { + return false; + } + if (*cur == '"') { + return amduatd_json_skip_string(p, end); + } + if (*cur == '{') { + return amduatd_json_skip_object(p, end, depth); + } + if (*cur == '[') { + return amduatd_json_skip_array(p, end, depth); + } + if (*cur == 't' && (end - cur) >= 4 && memcmp(cur, "true", 4) == 0) { + *p = cur + 4; + return true; + } + if (*cur == 'f' && (end - cur) >= 5 && memcmp(cur, "false", 5) == 0) { + *p = cur + 5; + return true; + } + if (*cur == 'n' && (end - cur) >= 4 && memcmp(cur, "null", 4) == 0) { + *p = cur + 4; + return true; + } + if (*cur == '-' || (*cur >= '0' && *cur <= '9')) { + cur++; + while (cur < end) { + char c = *cur; + if ((c >= '0' && c <= '9') || c == '.' || c == 'e' || c == 'E' || + c == '+' || c == '-') { + cur++; + continue; + } + break; + } + *p = cur; + return true; + } + return false; +} + +bool amduatd_copy_json_str(const char *s, + size_t len, + char **out) { + char *buf; + if (out == NULL) { + return false; + } + *out = NULL; + if (len > (SIZE_MAX - 1u)) { + return false; + } + buf = (char *)malloc(len + 1u); + if (buf == NULL) { + return false; + } + if (len != 0) { + memcpy(buf, s, len); + } + buf[len] = '\0'; + *out = buf; + return true; +} diff --git a/src/amduatd_http.h b/src/amduatd_http.h new file mode 100644 index 0000000..f6c2a60 --- /dev/null +++ b/src/amduatd_http.h @@ -0,0 +1,116 @@ +#ifndef AMDUATD_HTTP_H +#define AMDUATD_HTTP_H + +#include "amduat/asl/core.h" + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + char method[8]; + char path[1024]; + char content_type[128]; + char accept[128]; + char x_type_tag[64]; + char x_capability[2048]; + size_t content_length; + bool has_actor; + amduat_octets_t actor; + bool has_uid; + uid_t uid; +} amduatd_http_req_t; + +typedef struct { + int fd; + int status; + const char *reason; + const char *content_type; + const uint8_t *body; + size_t body_len; + bool head_only; + bool ok; +} amduatd_http_resp_t; + +bool amduatd_write_all(int fd, const uint8_t *buf, size_t len); + +bool amduatd_read_exact(int fd, uint8_t *buf, size_t len); + +bool amduatd_read_urandom(uint8_t *out, size_t len); + +bool amduatd_http_parse_request(int fd, amduatd_http_req_t *out_req); + +void amduatd_http_req_free(amduatd_http_req_t *req); + +bool amduatd_http_send_response(int fd, const amduatd_http_resp_t *resp); + +bool amduatd_http_send_status(int fd, + int code, + const char *reason, + const char *content_type, + const uint8_t *body, + size_t body_len, + bool head_only); + +bool amduatd_http_send_text(int fd, + int code, + const char *reason, + const char *text, + bool head_only); + +bool amduatd_http_send_json(int fd, + int code, + const char *reason, + const char *json, + bool head_only); + +bool amduatd_http_send_not_found(int fd, const amduatd_http_req_t *req); + +bool amduatd_send_json_error(int fd, + int code, + const char *reason, + const char *msg); + +const char *amduatd_query_param(const char *path, + const char *key, + char *out_value, + size_t out_cap); + +bool amduatd_path_without_query(const char *path, + char *out, + size_t cap); + +bool amduatd_path_extract_name(const char *path, + const char *prefix, + char *out, + size_t cap); + +const char *amduatd_json_skip_ws(const char *p, const char *end); + +bool amduatd_json_expect(const char **p, + const char *end, + char expected); + +bool amduatd_json_parse_string_noesc(const char **p, + const char *end, + const char **out_start, + size_t *out_len); + +bool amduatd_json_parse_u64(const char **p, const char *end, uint64_t *out); + +bool amduatd_json_skip_value(const char **p, + const char *end, + int depth); + +bool amduatd_copy_json_str(const char *s, size_t len, char **out); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* AMDUATD_HTTP_H */ diff --git a/src/amduatd_ui.c b/src/amduatd_ui.c index c4a17a5..b83d994 100644 --- a/src/amduatd_ui.c +++ b/src/amduatd_ui.c @@ -1,4 +1,5 @@ #include "amduatd_ui.h" +#include "amduatd_http.h" #include "amduat/asl/artifact_io.h" #include "amduat/asl/asl_store_fs.h" @@ -10,20 +11,6 @@ #include #include -bool amduatd_http_send_text(int fd, - int code, - const char *reason, - const char *text, - bool head_only); - -bool amduatd_http_send_status(int fd, - int code, - const char *reason, - const char *content_type, - const uint8_t *body, - size_t body_len, - bool head_only); - static const char k_amduatd_ui_html[] = "\n" "\n" @@ -337,27 +324,6 @@ static const char k_amduatd_ui_html[] = "\n" "\n"; -static void amduatd_ui_path_without_query(const char *path, - char *out, - size_t cap) { - const char *q = NULL; - size_t n = 0; - if (out == NULL || cap == 0) { - return; - } - out[0] = '\0'; - if (path == NULL) { - return; - } - q = strchr(path, '?'); - n = q != NULL ? (size_t)(q - path) : strlen(path); - if (n >= cap) { - n = cap - 1; - } - memcpy(out, path, n); - out[n] = '\0'; -} - bool amduatd_ui_can_handle(const amduatd_http_req_t *req) { char no_query[1024]; @@ -367,7 +333,7 @@ bool amduatd_ui_can_handle(const amduatd_http_req_t *req) { if (strcmp(req->method, "GET") != 0) { return false; } - amduatd_ui_path_without_query(req->path, no_query, sizeof(no_query)); + amduatd_path_without_query(req->path, no_query, sizeof(no_query)); return strcmp(no_query, "/v1/ui") == 0; } diff --git a/src/amduatd_ui.h b/src/amduatd_ui.h index 2dd9120..7acd8ae 100644 --- a/src/amduatd_ui.h +++ b/src/amduatd_ui.h @@ -3,6 +3,7 @@ #include "amduat/asl/asl_store_fs_meta.h" #include "amduat/asl/store.h" +#include "amduatd_http.h" #include #include @@ -16,25 +17,6 @@ typedef struct amduatd_caps_t amduatd_caps_t; typedef struct amduatd_cfg_t amduatd_cfg_t; typedef struct amduatd_concepts_t amduatd_concepts_t; -typedef struct { - char method[8]; - char path[1024]; - char content_type[128]; - char accept[128]; - char x_type_tag[64]; - char x_capability[2048]; - size_t content_length; - bool has_actor; - amduat_octets_t actor; - bool has_uid; - uid_t uid; -} amduatd_http_req_t; - -typedef struct { - int fd; - bool ok; -} amduatd_http_resp_t; - typedef struct { amduat_asl_store_t *store; amduat_reference_t ui_ref;