Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8d4c5f34bf | |||
| 35c189759c | |||
| 5c94b8680d | |||
| cebf3ded62 | |||
| b83ecf52f9 | |||
| dbf2c659d9 | |||
| 2b8062c55f |
@@ -1475,10 +1475,13 @@ static void http_send_response(int fd, const char* body) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const char* eff_body = is_envelope ? env_body : body;
|
const char* eff_body = is_envelope ? env_body : body;
|
||||||
/* Use the real byte count from fs_read if available (handles binary files
|
/* Use max(strlen, fs_read_len). fs_read_len is the real byte count for binary
|
||||||
* with embedded null bytes — PNG, WOFF2, etc.). Fall back to strlen for
|
* files (strlen stops at embedded NULs — PNG, WOFF2). strlen is correct AND larger
|
||||||
* normal text/JSON responses where _tl_fs_read_len is 0. */
|
* when a handler WRAPS fs_read output in a longer text/JSON response (e.g.
|
||||||
size_t blen = (_tl_fs_read_len > 0) ? _tl_fs_read_len : strlen(eff_body);
|
* /api/safety-contact returns {"configured":...,"contact": <file>}); using
|
||||||
|
* fs_read_len alone truncated those responses to the file's length. */
|
||||||
|
size_t _blen_s = strlen(eff_body);
|
||||||
|
size_t blen = (_tl_fs_read_len > _blen_s) ? _tl_fs_read_len : _blen_s;
|
||||||
_tl_fs_read_len = 0; /* consume — one-shot per response */
|
_tl_fs_read_len = 0; /* consume — one-shot per response */
|
||||||
int head_only = _tl_http_head_only;
|
int head_only = _tl_http_head_only;
|
||||||
|
|
||||||
@@ -1552,7 +1555,8 @@ static void* http_worker(void* arg) {
|
|||||||
/* Copy response out BEFORE arena teardown.
|
/* Copy response out BEFORE arena teardown.
|
||||||
* For binary files, _tl_fs_read_len holds the real byte count —
|
* For binary files, _tl_fs_read_len holds the real byte count —
|
||||||
* use memcpy instead of strdup so null bytes are preserved. */
|
* use memcpy instead of strdup so null bytes are preserved. */
|
||||||
size_t rlen = _tl_fs_read_len > 0 ? _tl_fs_read_len : (rs ? strlen(rs) : 0);
|
size_t _rlen_s = rs ? strlen(rs) : 0;
|
||||||
|
size_t rlen = (_tl_fs_read_len > _rlen_s) ? _tl_fs_read_len : _rlen_s;
|
||||||
response = malloc(rlen + 1);
|
response = malloc(rlen + 1);
|
||||||
if (response && rs) { memcpy(response, rs, rlen); response[rlen] = '\0'; }
|
if (response && rs) { memcpy(response, rs, rlen); response[rlen] = '\0'; }
|
||||||
else if (response) { response[0] = '\0'; }
|
else if (response) { response[0] = '\0'; }
|
||||||
@@ -1799,7 +1803,8 @@ static void* http_worker_v2(void* arg) {
|
|||||||
el_val_t hmap = http_build_headers_map(hdr_block ? hdr_block : "");
|
el_val_t hmap = http_build_headers_map(hdr_block ? hdr_block : "");
|
||||||
el_val_t r = h(EL_STR(dispatch_method), EL_STR(path), hmap, EL_STR(body));
|
el_val_t r = h(EL_STR(dispatch_method), EL_STR(path), hmap, EL_STR(body));
|
||||||
const char* rs = EL_CSTR(r);
|
const char* rs = EL_CSTR(r);
|
||||||
size_t rlen = _tl_fs_read_len > 0 ? _tl_fs_read_len : (rs ? strlen(rs) : 0);
|
size_t _rlen_s = rs ? strlen(rs) : 0;
|
||||||
|
size_t rlen = (_tl_fs_read_len > _rlen_s) ? _tl_fs_read_len : _rlen_s;
|
||||||
response = malloc(rlen + 1);
|
response = malloc(rlen + 1);
|
||||||
if (response && rs) { memcpy(response, rs, rlen); response[rlen] = '\0'; }
|
if (response && rs) { memcpy(response, rs, rlen); response[rlen] = '\0'; }
|
||||||
else if (response) { response[0] = '\0'; }
|
else if (response) { response[0] = '\0'; }
|
||||||
@@ -3173,23 +3178,49 @@ static void jb_puts(JsonBuf* b, const char* s) {
|
|||||||
|
|
||||||
static void jb_emit_escaped(JsonBuf* b, const char* s) {
|
static void jb_emit_escaped(JsonBuf* b, const char* s) {
|
||||||
jb_putc(b, '"');
|
jb_putc(b, '"');
|
||||||
for (; *s; s++) {
|
const unsigned char* p = (const unsigned char*)s;
|
||||||
unsigned char c = (unsigned char)*s;
|
while (*p) {
|
||||||
|
unsigned char c = *p;
|
||||||
switch (c) {
|
switch (c) {
|
||||||
case '"': jb_puts(b, "\\\""); break;
|
case '"': jb_puts(b, "\\\""); p++; break;
|
||||||
case '\\': jb_puts(b, "\\\\"); break;
|
case '\\': jb_puts(b, "\\\\"); p++; break;
|
||||||
case '\b': jb_puts(b, "\\b"); break;
|
case '\b': jb_puts(b, "\\b"); p++; break;
|
||||||
case '\f': jb_puts(b, "\\f"); break;
|
case '\f': jb_puts(b, "\\f"); p++; break;
|
||||||
case '\n': jb_puts(b, "\\n"); break;
|
case '\n': jb_puts(b, "\\n"); p++; break;
|
||||||
case '\r': jb_puts(b, "\\r"); break;
|
case '\r': jb_puts(b, "\\r"); p++; break;
|
||||||
case '\t': jb_puts(b, "\\t"); break;
|
case '\t': jb_puts(b, "\\t"); p++; break;
|
||||||
default:
|
default:
|
||||||
if (c < 0x20) {
|
if (c < 0x20) {
|
||||||
char tmp[8];
|
char tmp[8];
|
||||||
snprintf(tmp, sizeof(tmp), "\\u%04x", c);
|
snprintf(tmp, sizeof(tmp), "\\u%04x", c);
|
||||||
jb_puts(b, tmp);
|
jb_puts(b, tmp);
|
||||||
} else {
|
p++;
|
||||||
|
} else if (c < 0x80) {
|
||||||
jb_putc(b, (char)c);
|
jb_putc(b, (char)c);
|
||||||
|
p++;
|
||||||
|
} else {
|
||||||
|
/* Multi-byte UTF-8: validate sequence, pass through if valid,
|
||||||
|
* escape as \u00xx if the start byte is invalid/orphaned. */
|
||||||
|
int seq_len = 0;
|
||||||
|
if ((c & 0xE0) == 0xC0) seq_len = 2;
|
||||||
|
else if ((c & 0xF0) == 0xE0) seq_len = 3;
|
||||||
|
else if ((c & 0xF8) == 0xF0) seq_len = 4;
|
||||||
|
if (seq_len >= 2) {
|
||||||
|
int valid = 1;
|
||||||
|
for (int i = 1; i < seq_len; i++) {
|
||||||
|
if ((p[i] & 0xC0) != 0x80) { valid = 0; break; }
|
||||||
|
}
|
||||||
|
if (valid) {
|
||||||
|
for (int i = 0; i < seq_len; i++) jb_putc(b, (char)p[i]);
|
||||||
|
p += seq_len;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* Invalid start byte or truncated sequence — escape it */
|
||||||
|
char tmp[8];
|
||||||
|
snprintf(tmp, sizeof(tmp), "\\u%04x", c);
|
||||||
|
jb_puts(b, tmp);
|
||||||
|
p++;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -6219,7 +6250,9 @@ static void engram_grow_edges(void) {
|
|||||||
static char* engram_new_id(void) {
|
static char* engram_new_id(void) {
|
||||||
el_val_t v = uuid_new();
|
el_val_t v = uuid_new();
|
||||||
const char* s = EL_CSTR(v);
|
const char* s = EL_CSTR(v);
|
||||||
return el_strdup(s ? s : "");
|
/* Persistent: node ids live in the global store; an arena (el_strdup) id is
|
||||||
|
* freed at el_request_end(), corrupting the node after the creating request. */
|
||||||
|
return el_strdup_persist(s ? s : "");
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Convert a node into an ElMap of its fields. */
|
/* Convert a node into an ElMap of its fields. */
|
||||||
@@ -6314,12 +6347,17 @@ el_val_t engram_node_full(el_val_t content, el_val_t node_type, el_val_t label,
|
|||||||
const char* lb = EL_CSTR(label);
|
const char* lb = EL_CSTR(label);
|
||||||
const char* ti = EL_CSTR(tier);
|
const char* ti = EL_CSTR(tier);
|
||||||
const char* tg = EL_CSTR(tags);
|
const char* tg = EL_CSTR(tags);
|
||||||
n->content = el_strdup(c ? c : "");
|
/* Persistent (el_strdup_persist, NOT el_strdup): these strings are owned by the
|
||||||
n->node_type = el_strdup(nt && *nt ? nt : "Memory");
|
* persistent global node store. el_strdup tracks into the per-request arena, which
|
||||||
n->label = el_strdup(lb && *lb ? lb : (c ? engram_first_n_chars(c, 60) : ""));
|
* el_request_end() frees when the creating HTTP request completes — leaving the
|
||||||
n->tier = el_strdup(ti && *ti ? ti : "Working");
|
* stored node with dangling pointers (corrupted ids, "saved but never listed").
|
||||||
n->tags = el_strdup(tg ? tg : "");
|
* This is the root cause of the hallucinated/lost-saves class of bugs. */
|
||||||
n->metadata = el_strdup("{}");
|
n->content = el_strdup_persist(c ? c : "");
|
||||||
|
n->node_type = el_strdup_persist(nt && *nt ? nt : "Memory");
|
||||||
|
n->label = el_strdup_persist(lb && *lb ? lb : (c ? engram_first_n_chars(c, 60) : ""));
|
||||||
|
n->tier = el_strdup_persist(ti && *ti ? ti : "Working");
|
||||||
|
n->tags = el_strdup_persist(tg ? tg : "");
|
||||||
|
n->metadata = el_strdup_persist("{}");
|
||||||
n->salience = engram_decode_score(salience);
|
n->salience = engram_decode_score(salience);
|
||||||
n->importance = engram_decode_score(importance);
|
n->importance = engram_decode_score(importance);
|
||||||
n->confidence = engram_decode_score(confidence);
|
n->confidence = engram_decode_score(confidence);
|
||||||
|
|||||||
Reference in New Issue
Block a user