http out
This commit is contained in:
parent
66291f6d43
commit
8d7c7d93a5
|
|
@ -24,8 +24,8 @@ target_link_libraries(amduat_federation
|
||||||
PRIVATE amduat_asl amduat_enc amduat_util amduat_fed
|
PRIVATE amduat_asl amduat_enc amduat_util amduat_fed
|
||||||
)
|
)
|
||||||
|
|
||||||
set(amduatd_sources src/amduatd.c src/amduatd_caps.c src/amduatd_space.c
|
set(amduatd_sources src/amduatd.c src/amduatd_http.c src/amduatd_caps.c
|
||||||
src/amduatd_concepts.c)
|
src/amduatd_space.c src/amduatd_concepts.c)
|
||||||
if(AMDUATD_ENABLE_UI)
|
if(AMDUATD_ENABLE_UI)
|
||||||
list(APPEND amduatd_sources src/amduatd_ui.c)
|
list(APPEND amduatd_sources src/amduatd_ui.c)
|
||||||
endif()
|
endif()
|
||||||
|
|
|
||||||
689
src/amduatd.c
689
src/amduatd.c
|
|
@ -34,6 +34,7 @@
|
||||||
#include "amduat/util/log.h"
|
#include "amduat/util/log.h"
|
||||||
#include "amduat/hash/asl1.h"
|
#include "amduat/hash/asl1.h"
|
||||||
#include "amduatd_concepts.h"
|
#include "amduatd_concepts.h"
|
||||||
|
#include "amduatd_http.h"
|
||||||
#include "amduatd_ui.h"
|
#include "amduatd_ui.h"
|
||||||
#include "amduatd_caps.h"
|
#include "amduatd_caps.h"
|
||||||
#include "amduatd_space.h"
|
#include "amduatd_space.h"
|
||||||
|
|
@ -64,17 +65,6 @@ typedef struct amduatd_strbuf {
|
||||||
size_t cap;
|
size_t cap;
|
||||||
} amduatd_strbuf_t;
|
} 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(
|
amduatd_ref_status_t amduatd_decode_ref_or_name_latest(
|
||||||
amduat_asl_store_t *store,
|
amduat_asl_store_t *store,
|
||||||
const amduat_asl_store_fs_config_t *cfg,
|
const amduat_asl_store_fs_config_t *cfg,
|
||||||
|
|
@ -282,97 +272,6 @@ static void amduatd_on_signal(int signo) {
|
||||||
amduatd_should_exit = 1;
|
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 {
|
typedef struct {
|
||||||
uid_t *uids;
|
uid_t *uids;
|
||||||
size_t len;
|
size_t len;
|
||||||
|
|
@ -493,258 +392,6 @@ static bool amduatd_get_peer_actor(int client_fd,
|
||||||
return true;
|
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),
|
|
||||||
"<!doctype html>\n"
|
|
||||||
"<html lang=\"en\">\n"
|
|
||||||
"<head>\n"
|
|
||||||
" <meta charset=\"utf-8\" />\n"
|
|
||||||
" <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n"
|
|
||||||
" <title>amduatd — Not Found</title>\n"
|
|
||||||
" <style>\n"
|
|
||||||
" :root{color-scheme:light dark;}\n"
|
|
||||||
" body{font-family:ui-sans-serif,system-ui,-apple-system,Segoe UI,Roboto,Ubuntu,Cantarell,Noto Sans,Arial,sans-serif;"
|
|
||||||
" margin:0;line-height:1.4;}\n"
|
|
||||||
" .wrap{max-width:860px;margin:0 auto;padding:40px 20px;}\n"
|
|
||||||
" .card{border:1px solid rgba(127,127,127,.35);border-radius:14px;padding:22px;"
|
|
||||||
" background:rgba(127,127,127,.06);}\n"
|
|
||||||
" h1{margin:0 0 6px;font-size:22px;}\n"
|
|
||||||
" p{margin:8px 0;opacity:.9;}\n"
|
|
||||||
" code{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,monospace;"
|
|
||||||
" font-size:13px;}\n"
|
|
||||||
" ul{margin:10px 0 0;padding-left:18px;}\n"
|
|
||||||
" a{color:inherit;}\n"
|
|
||||||
" .muted{opacity:.75;}\n"
|
|
||||||
" </style>\n"
|
|
||||||
"</head>\n"
|
|
||||||
"<body>\n"
|
|
||||||
" <div class=\"wrap\">\n"
|
|
||||||
" <div class=\"card\">\n"
|
|
||||||
" <h1>404 — Not Found</h1>\n"
|
|
||||||
" <p>amduatd didn’t recognize <code>%s %s</code>.</p>\n"
|
|
||||||
" <p class=\"muted\">Try one of these:</p>\n"
|
|
||||||
" <ul>\n"
|
|
||||||
" <li><a href=\"/v1/meta\">/v1/meta</a></li>\n"
|
|
||||||
" <li><a href=\"/v1/contract\">/v1/contract</a></li>\n"
|
|
||||||
" </ul>\n"
|
|
||||||
" <p class=\"muted\">Artifact bytes: <code>/v1/artifacts/<ref></code></p>\n"
|
|
||||||
" </div>\n"
|
|
||||||
" </div>\n"
|
|
||||||
"</body>\n"
|
|
||||||
"</html>\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) {
|
static void amduatd_strbuf_free(amduatd_strbuf_t *b) {
|
||||||
if (b == NULL) {
|
if (b == NULL) {
|
||||||
return;
|
return;
|
||||||
|
|
@ -816,73 +463,6 @@ static bool amduatd_strbuf_append_char(amduatd_strbuf_t *b, char c) {
|
||||||
return amduatd_strbuf_append(b, &c, 1u);
|
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,
|
static bool amduatd_json_parse_u32(const char **p,
|
||||||
const char *end,
|
const char *end,
|
||||||
uint32_t *out) {
|
uint32_t *out) {
|
||||||
|
|
@ -930,53 +510,6 @@ static bool amduatd_json_parse_u32(const char **p,
|
||||||
return true;
|
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,
|
static bool amduatd_json_parse_u32_loose(const char **p,
|
||||||
const char *end,
|
const char *end,
|
||||||
uint32_t *out) {
|
uint32_t *out) {
|
||||||
|
|
@ -1070,183 +603,6 @@ static void amduatd_json_peek_token(const char *p,
|
||||||
out[n] = '\0';
|
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,
|
static bool amduatd_decode_ref_hex_str(const char *s,
|
||||||
size_t len,
|
size_t len,
|
||||||
amduat_reference_t *out_ref) {
|
amduat_reference_t *out_ref) {
|
||||||
|
|
@ -1264,47 +620,6 @@ static bool amduatd_decode_ref_hex_str(const char *s,
|
||||||
return ok;
|
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,
|
static bool amduatd_parse_type_tag_hex(const char *text,
|
||||||
bool *out_has_type_tag,
|
bool *out_has_type_tag,
|
||||||
amduat_type_tag_t *out_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);
|
ok = amduatd_http_send_not_found(fd, &req);
|
||||||
|
|
||||||
conn_cleanup:
|
conn_cleanup:
|
||||||
free((void *)req.actor.data);
|
amduatd_http_req_free(&req);
|
||||||
return ok;
|
return ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
#include "amduatd_caps.h"
|
#include "amduatd_caps.h"
|
||||||
|
#include "amduatd_http.h"
|
||||||
|
|
||||||
#include "amduat/asl/asl_pointer_fs.h"
|
#include "amduat/asl/asl_pointer_fs.h"
|
||||||
#include "amduat/asl/ref_text.h"
|
#include "amduat/asl/ref_text.h"
|
||||||
|
|
@ -23,45 +24,6 @@
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
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(
|
amduatd_ref_status_t amduatd_decode_ref_or_name_latest(
|
||||||
amduat_asl_store_t *store,
|
amduat_asl_store_t *store,
|
||||||
const amduat_asl_store_fs_config_t *cfg,
|
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;
|
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(
|
static bool amduatd_handle_post_capabilities(
|
||||||
int fd,
|
int fd,
|
||||||
amduat_asl_store_t *store,
|
amduat_asl_store_t *store,
|
||||||
|
|
@ -1862,7 +1802,7 @@ bool amduatd_caps_can_handle(const amduatd_http_req_t *req) {
|
||||||
if (req == NULL) {
|
if (req == NULL) {
|
||||||
return false;
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
if (strcmp(req->method, "POST") == 0 &&
|
if (strcmp(req->method, "POST") == 0 &&
|
||||||
|
|
@ -1887,11 +1827,11 @@ bool amduatd_caps_handle(amduatd_ctx_t *ctx,
|
||||||
if (!amduatd_caps_can_handle(req)) {
|
if (!amduatd_caps_can_handle(req)) {
|
||||||
return false;
|
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,
|
resp->ok = amduatd_send_json_error(resp->fd,
|
||||||
400,
|
400,
|
||||||
"Bad Request",
|
"Bad Request",
|
||||||
"invalid path");
|
"invalid path");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@
|
||||||
#define _POSIX_C_SOURCE 200809L
|
#define _POSIX_C_SOURCE 200809L
|
||||||
|
|
||||||
#include "amduatd_concepts.h"
|
#include "amduatd_concepts.h"
|
||||||
|
#include "amduatd_http.h"
|
||||||
|
|
||||||
#include "amduatd_caps.h"
|
#include "amduatd_caps.h"
|
||||||
|
|
||||||
|
|
@ -34,37 +35,6 @@
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
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 {
|
typedef struct amduatd_strbuf {
|
||||||
char *data;
|
char *data;
|
||||||
size_t len;
|
size_t len;
|
||||||
|
|
@ -2995,37 +2965,6 @@ concepts_cleanup:
|
||||||
return ok;
|
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,
|
static bool amduatd_handle_post_concept_publish(int fd,
|
||||||
amduat_asl_store_t *store,
|
amduat_asl_store_t *store,
|
||||||
amduatd_concepts_t *concepts,
|
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);
|
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) {
|
bool amduatd_concepts_can_handle(const amduatd_http_req_t *req) {
|
||||||
char no_query[1024];
|
char no_query[1024];
|
||||||
|
|
||||||
if (req == NULL) {
|
if (req == NULL) {
|
||||||
return false;
|
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 &&
|
if (strcmp(req->method, "POST") == 0 &&
|
||||||
strcmp(no_query, "/v1/concepts") == 0) {
|
strcmp(no_query, "/v1/concepts") == 0) {
|
||||||
|
|
@ -3286,7 +3204,7 @@ bool amduatd_concepts_handle(amduatd_ctx_t *ctx,
|
||||||
return false;
|
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) {
|
if (strcmp(req->method, "POST") == 0 && strcmp(no_query, "/v1/concepts") == 0) {
|
||||||
resp->ok = amduatd_handle_post_concepts(resp->fd,
|
resp->ok = amduatd_handle_post_concepts(resp->fd,
|
||||||
|
|
|
||||||
819
src/amduatd_http.c
Normal file
819
src/amduatd_http.c
Normal file
|
|
@ -0,0 +1,819 @@
|
||||||
|
#ifndef _GNU_SOURCE
|
||||||
|
#define _GNU_SOURCE
|
||||||
|
#endif
|
||||||
|
#define _POSIX_C_SOURCE 200809L
|
||||||
|
|
||||||
|
#include "amduatd_http.h"
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <strings.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
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),
|
||||||
|
"<!doctype html>\n"
|
||||||
|
"<html lang=\"en\">\n"
|
||||||
|
"<head>\n"
|
||||||
|
" <meta charset=\"utf-8\" />\n"
|
||||||
|
" <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n"
|
||||||
|
" <title>amduatd — Not Found</title>\n"
|
||||||
|
" <style>\n"
|
||||||
|
" :root{color-scheme:light dark;}\n"
|
||||||
|
" body{font-family:ui-sans-serif,system-ui,-apple-system,Segoe UI,Roboto,Ubuntu,Cantarell,Noto Sans,Arial,sans-serif;"
|
||||||
|
" margin:0;line-height:1.4;}\n"
|
||||||
|
" .wrap{max-width:860px;margin:0 auto;padding:40px 20px;}\n"
|
||||||
|
" .card{border:1px solid rgba(127,127,127,.35);border-radius:14px;padding:22px;"
|
||||||
|
" background:rgba(127,127,127,.06);}\n"
|
||||||
|
" h1{margin:0 0 6px;font-size:22px;}\n"
|
||||||
|
" p{margin:8px 0;opacity:.9;}\n"
|
||||||
|
" code{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,monospace;"
|
||||||
|
" font-size:13px;}\n"
|
||||||
|
" ul{margin:10px 0 0;padding-left:18px;}\n"
|
||||||
|
" a{color:inherit;}\n"
|
||||||
|
" .muted{opacity:.75;}\n"
|
||||||
|
" </style>\n"
|
||||||
|
"</head>\n"
|
||||||
|
"<body>\n"
|
||||||
|
" <div class=\"wrap\">\n"
|
||||||
|
" <div class=\"card\">\n"
|
||||||
|
" <h1>404 — Not Found</h1>\n"
|
||||||
|
" <p>amduatd didn’t recognize <code>%s %s</code>.</p>\n"
|
||||||
|
" <p class=\"muted\">Try one of these:</p>\n"
|
||||||
|
" <ul>\n"
|
||||||
|
" <li><a href=\"/v1/meta\">/v1/meta</a></li>\n"
|
||||||
|
" <li><a href=\"/v1/contract\">/v1/contract</a></li>\n"
|
||||||
|
" </ul>\n"
|
||||||
|
" <p class=\"muted\">Artifact bytes: <code>/v1/artifacts/<ref></code></p>\n"
|
||||||
|
" </div>\n"
|
||||||
|
" </div>\n"
|
||||||
|
"</body>\n"
|
||||||
|
"</html>\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;
|
||||||
|
}
|
||||||
116
src/amduatd_http.h
Normal file
116
src/amduatd_http.h
Normal file
|
|
@ -0,0 +1,116 @@
|
||||||
|
#ifndef AMDUATD_HTTP_H
|
||||||
|
#define AMDUATD_HTTP_H
|
||||||
|
|
||||||
|
#include "amduat/asl/core.h"
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
#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 */
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
#include "amduatd_ui.h"
|
#include "amduatd_ui.h"
|
||||||
|
#include "amduatd_http.h"
|
||||||
|
|
||||||
#include "amduat/asl/artifact_io.h"
|
#include "amduat/asl/artifact_io.h"
|
||||||
#include "amduat/asl/asl_store_fs.h"
|
#include "amduat/asl/asl_store_fs.h"
|
||||||
|
|
@ -10,20 +11,6 @@
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
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[] =
|
static const char k_amduatd_ui_html[] =
|
||||||
"<!doctype html>\n"
|
"<!doctype html>\n"
|
||||||
"<html lang=\"en\">\n"
|
"<html lang=\"en\">\n"
|
||||||
|
|
@ -337,27 +324,6 @@ static const char k_amduatd_ui_html[] =
|
||||||
"</body>\n"
|
"</body>\n"
|
||||||
"</html>\n";
|
"</html>\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) {
|
bool amduatd_ui_can_handle(const amduatd_http_req_t *req) {
|
||||||
char no_query[1024];
|
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) {
|
if (strcmp(req->method, "GET") != 0) {
|
||||||
return false;
|
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;
|
return strcmp(no_query, "/v1/ui") == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
#include "amduat/asl/asl_store_fs_meta.h"
|
#include "amduat/asl/asl_store_fs_meta.h"
|
||||||
#include "amduat/asl/store.h"
|
#include "amduat/asl/store.h"
|
||||||
|
#include "amduatd_http.h"
|
||||||
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
|
@ -16,25 +17,6 @@ typedef struct amduatd_caps_t amduatd_caps_t;
|
||||||
typedef struct amduatd_cfg_t amduatd_cfg_t;
|
typedef struct amduatd_cfg_t amduatd_cfg_t;
|
||||||
typedef struct amduatd_concepts_t amduatd_concepts_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 {
|
typedef struct {
|
||||||
amduat_asl_store_t *store;
|
amduat_asl_store_t *store;
|
||||||
amduat_reference_t ui_ref;
|
amduat_reference_t ui_ref;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue