fix blob and http
This commit is contained in:
@@ -56,6 +56,7 @@ int blob_write_fit(blob *b, int64_t fit, int length);
|
||||
int blob_write_kim(blob *b, int64_t value);
|
||||
int blob_write_pad(blob *b, int block_size);
|
||||
int blob_write_text(blob *b, const char *text);
|
||||
int blob_write_text_raw(blob *b, const char *text);
|
||||
|
||||
int blob_write_bytes(blob *b, void *data, size_t length);
|
||||
|
||||
@@ -66,6 +67,7 @@ int blob_read_dec64(const blob *b, size_t from, double *out_value);
|
||||
int blob_read_fit(const blob *b, size_t from, int length, int64_t *out_value);
|
||||
int blob_read_kim(const blob *b, size_t from, int64_t *out_value, size_t *bits_read);
|
||||
int blob_read_text(const blob *b, size_t from, char **out_text, size_t *bits_read);
|
||||
int blob_read_text_raw(const blob *b, size_t from, char **out_text, size_t *bits_read);
|
||||
int blob_pad_check(const blob *b, size_t from, int block_size);
|
||||
|
||||
// Utility functions
|
||||
@@ -388,6 +390,13 @@ int blob_write_text(blob *b, const char *text) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int blob_write_text_raw(blob *b, const char *text) {
|
||||
if (!b || !text || b->is_stone) return -1;
|
||||
|
||||
size_t len = strlen(text);
|
||||
return blob_write_bytes(b, (void *)text, len);
|
||||
}
|
||||
|
||||
int blob_write_bytes(blob *b, void *data, size_t length) {
|
||||
if (!b || !data || b->is_stone) return -1;
|
||||
|
||||
@@ -538,6 +547,27 @@ int blob_read_text(const blob *b, size_t from, char **out_text, size_t *bits_rea
|
||||
return 0;
|
||||
}
|
||||
|
||||
int blob_read_text_raw(const blob *b, size_t from, char **out_text, size_t *bits_read) {
|
||||
if (!b || !b->is_stone || !out_text || !bits_read) return -1;
|
||||
if (from >= b->bit_length) return -1;
|
||||
|
||||
// Check that from is byte-aligned
|
||||
if (from % 8 != 0) return -1;
|
||||
|
||||
size_t from_byte = from / 8;
|
||||
size_t length_bytes = (b->bit_length - from) / 8;
|
||||
|
||||
char *str = malloc(length_bytes + 1);
|
||||
if (!str) return -1;
|
||||
|
||||
memcpy(str, b->data + from_byte, length_bytes);
|
||||
str[length_bytes] = '\0';
|
||||
|
||||
*out_text = str;
|
||||
*bits_read = length_bytes * 8;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int blob_pad_check(const blob *b, size_t from, int block_size) {
|
||||
if (!b || !b->is_stone) return 0;
|
||||
if (block_size <= 0) return 0;
|
||||
|
||||
@@ -289,6 +289,30 @@ static JSValue js_blob_write_text(JSContext *ctx, JSValueConst this_val,
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
// blob.write_text_raw(text)
|
||||
static JSValue js_blob_write_text_raw(JSContext *ctx, JSValueConst this_val,
|
||||
int argc, JSValueConst *argv) {
|
||||
if (argc < 1) {
|
||||
return JS_ThrowTypeError(ctx, "write_text_raw(text) requires 1 argument");
|
||||
}
|
||||
blob *bd = js2blob(ctx, this_val);
|
||||
if (!bd) {
|
||||
return JS_ThrowTypeError(ctx, "write_text_raw: not called on a blob");
|
||||
}
|
||||
|
||||
const char *str = JS_ToCString(ctx, argv[0]);
|
||||
if (!str) return JS_EXCEPTION;
|
||||
|
||||
int result = blob_write_text_raw(bd, str);
|
||||
JS_FreeCString(ctx, str);
|
||||
|
||||
if (result < 0) {
|
||||
return JS_ThrowTypeError(ctx, "write_text_raw: cannot write to stone blob or OOM");
|
||||
}
|
||||
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
// blob.read_logical(from)
|
||||
static JSValue js_blob_read_logical(JSContext *ctx, JSValueConst this_val,
|
||||
int argc, JSValueConst *argv) {
|
||||
@@ -433,9 +457,6 @@ static JSValue js_blob_read_kim(JSContext *ctx, JSValueConst this_val,
|
||||
// blob.read_text(from)
|
||||
static JSValue js_blob_read_text(JSContext *ctx, JSValueConst this_val,
|
||||
int argc, JSValueConst *argv) {
|
||||
if (argc < 1) {
|
||||
return JS_ThrowTypeError(ctx, "read_text(from) requires 1 argument");
|
||||
}
|
||||
blob *bd = js2blob(ctx, this_val);
|
||||
if (!bd) {
|
||||
return JS_ThrowTypeError(ctx, "read_text: not called on a blob");
|
||||
@@ -464,6 +485,39 @@ static JSValue js_blob_read_text(JSContext *ctx, JSValueConst this_val,
|
||||
return obj;
|
||||
}
|
||||
|
||||
// blob.read_text_raw(from)
|
||||
static JSValue js_blob_read_text_raw(JSContext *ctx, JSValueConst this_val,
|
||||
int argc, JSValueConst *argv) {
|
||||
blob *bd = js2blob(ctx, this_val);
|
||||
if (!bd) {
|
||||
return JS_ThrowTypeError(ctx, "read_text_raw: not called on a blob");
|
||||
}
|
||||
|
||||
if (!bd->is_stone) {
|
||||
return JS_ThrowTypeError(ctx, "read_text_raw: blob must be stone");
|
||||
}
|
||||
|
||||
int64_t from = 0;
|
||||
if (argc >= 1) {
|
||||
if (JS_ToInt64(ctx, &from, argv[0]) < 0) return JS_EXCEPTION;
|
||||
}
|
||||
|
||||
char *text;
|
||||
size_t bits_read;
|
||||
if (blob_read_text_raw(bd, from, &text, &bits_read) < 0) {
|
||||
return JS_ThrowRangeError(ctx, "read_text_raw: out of range or not byte-aligned");
|
||||
}
|
||||
|
||||
JSValue result = JS_NewString(ctx, text);
|
||||
free(text);
|
||||
|
||||
// Return object with text and total bits read
|
||||
JSValue obj = JS_NewObject(ctx);
|
||||
JS_SetPropertyStr(ctx, obj, "text", result);
|
||||
JS_SetPropertyStr(ctx, obj, "bits_read", JS_NewInt64(ctx, bits_read));
|
||||
return obj;
|
||||
}
|
||||
|
||||
// blob.pad?(from, block_size)
|
||||
static JSValue js_blob_pad_q(JSContext *ctx, JSValueConst this_val,
|
||||
int argc, JSValueConst *argv) {
|
||||
@@ -557,7 +611,7 @@ static const JSCFunctionListEntry js_blob_funcs[] = {
|
||||
JS_CFUNC_DEF("write_fit", 2, js_blob_write_fit),
|
||||
JS_CFUNC_DEF("write_kim", 1, js_blob_write_kim),
|
||||
JS_CFUNC_DEF("write_pad", 1, js_blob_write_pad),
|
||||
JS_CFUNC_DEF("write_text", 1, js_blob_write_text),
|
||||
JS_CFUNC_DEF("write_text", 1, js_blob_write_text_raw),
|
||||
|
||||
// Read methods
|
||||
JS_CFUNC_DEF("read_logical", 1, js_blob_read_logical),
|
||||
@@ -565,7 +619,7 @@ static const JSCFunctionListEntry js_blob_funcs[] = {
|
||||
JS_CFUNC_DEF("read_dec64", 1, js_blob_read_dec64),
|
||||
JS_CFUNC_DEF("read_fit", 2, js_blob_read_fit),
|
||||
JS_CFUNC_DEF("read_kim", 1, js_blob_read_kim),
|
||||
JS_CFUNC_DEF("read_text", 1, js_blob_read_text),
|
||||
JS_CFUNC_DEF("read_text", 1, js_blob_read_text_raw),
|
||||
JS_CFUNC_DEF("pad?", 2, js_blob_pad_q),
|
||||
|
||||
// Other methods
|
||||
|
||||
@@ -254,16 +254,79 @@ exit:
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Extract body from HTTP response
|
||||
static char *extract_body(const char *response, size_t response_len, size_t *body_len) {
|
||||
const char *body_start = strstr(response, "\r\n\r\n");
|
||||
if (!body_start) {
|
||||
return NULL;
|
||||
}
|
||||
body_start += 4;
|
||||
|
||||
*body_len = response_len - (body_start - response);
|
||||
return (char *)body_start;
|
||||
/* ---------------------------------------------------------------------
|
||||
Very small, non-streaming de-chunker.
|
||||
Removes the hex size lines and keeps the actual data.
|
||||
Returns 0 on success, -1 on format errors.
|
||||
-------------------------------------------------------------------*/
|
||||
static int decode_chunked(buffer_t *out,
|
||||
const char *src, size_t src_len)
|
||||
{
|
||||
printf("[CHUNK] Starting decode_chunked, input len: %zu\n", src_len);
|
||||
int chunk_count = 0;
|
||||
|
||||
while (src_len) {
|
||||
/* read hex size line */
|
||||
size_t n = 0;
|
||||
while (n < src_len && src[n] != '\r') { n++; }
|
||||
if (n == src_len || n + 1 >= src_len || src[n+1] != '\n') {
|
||||
printf("[CHUNK] Error: incomplete chunk size line\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
char size_buf[32];
|
||||
if (n >= sizeof(size_buf)) {
|
||||
printf("[CHUNK] Error: chunk size line too long (%zu)\n", n);
|
||||
return -1;
|
||||
}
|
||||
memcpy(size_buf, src, n); size_buf[n] = 0;
|
||||
|
||||
char *endptr;
|
||||
long chunk_len = strtol(size_buf, &endptr, 16);
|
||||
if (endptr == size_buf || chunk_len < 0) {
|
||||
printf("[CHUNK] Error: invalid chunk size '%s'\n", size_buf);
|
||||
return -1;
|
||||
}
|
||||
|
||||
printf("[CHUNK] Chunk %d: size=%ld (0x%lx)\n", chunk_count++, chunk_len, chunk_len);
|
||||
|
||||
if (chunk_len == 0) {
|
||||
printf("[CHUNK] Found terminating chunk, done\n");
|
||||
return 0; /* done */
|
||||
}
|
||||
|
||||
/* skip "<hex>\r\n" */
|
||||
src += n + 2;
|
||||
src_len -= n + 2;
|
||||
|
||||
if ((size_t)chunk_len > src_len) {
|
||||
printf("[CHUNK] Error: chunk size %ld exceeds remaining data %zu\n", chunk_len, src_len);
|
||||
return -1;
|
||||
}
|
||||
if (buffer_append(out, src, chunk_len) < 0) return -1;
|
||||
|
||||
/* skip chunk data */
|
||||
src += chunk_len;
|
||||
src_len -= chunk_len;
|
||||
|
||||
/* skip trailing \r\n after chunk data */
|
||||
if (src_len < 2 || src[0] != '\r' || src[1] != '\n') {
|
||||
printf("[CHUNK] Error: missing CRLF after chunk data (remaining=%zu)\n", src_len);
|
||||
if (src_len > 0) {
|
||||
printf("[CHUNK] Next bytes: ");
|
||||
for (size_t i = 0; i < 10 && i < src_len; i++) {
|
||||
printf("\\x%02x", (unsigned char)src[i]);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
src += 2;
|
||||
src_len -= 2;
|
||||
}
|
||||
printf("[CHUNK] Finished without terminating chunk\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Parse HTTP headers
|
||||
@@ -790,22 +853,76 @@ static JSValue js_fetch(JSContext *ctx, JSValueConst this_val, int argc, JSValue
|
||||
}
|
||||
|
||||
if (ret == 0 && response_buf.data) {
|
||||
// Extract body
|
||||
size_t body_len;
|
||||
char *body = extract_body(response_buf.data, response_buf.size, &body_len);
|
||||
|
||||
if (body) {
|
||||
// Return body as ArrayBuffer
|
||||
result = js_new_blob_stoned_copy(ctx, (uint8_t *)body, body_len);
|
||||
} else {
|
||||
// Split headers / body
|
||||
char *body = strstr(response_buf.data, "\r\n\r\n");
|
||||
if (!body) {
|
||||
result = JS_ThrowInternalError(ctx, "Failed to parse HTTP response");
|
||||
goto cleanup;
|
||||
}
|
||||
size_t header_len = body + 4 - response_buf.data;
|
||||
size_t body_len = response_buf.size - header_len;
|
||||
body += 4;
|
||||
|
||||
// Look for "Transfer-Encoding: chunked"
|
||||
int is_chunked = (strstr(response_buf.data, "Transfer-Encoding: chunked") ||
|
||||
strstr(response_buf.data, "transfer-encoding: chunked"));
|
||||
|
||||
printf("[HTTP] Total response size: %zu bytes\n", response_buf.size);
|
||||
printf("[HTTP] Header length: %zu bytes\n", header_len);
|
||||
printf("[HTTP] Body length before decoding: %zu bytes\n", body_len);
|
||||
printf("[HTTP] Is chunked: %s\n", is_chunked ? "yes" : "no");
|
||||
|
||||
// Print first 100 chars of body before decoding
|
||||
printf("[HTTP] First 100 chars of raw body: ");
|
||||
for (size_t i = 0; i < 100 && i < body_len; i++) {
|
||||
if (body[i] >= 32 && body[i] <= 126) {
|
||||
printf("%c", body[i]);
|
||||
} else {
|
||||
printf("\\x%02x", (unsigned char)body[i]);
|
||||
}
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
buffer_t clean_body;
|
||||
buffer_init(&clean_body);
|
||||
|
||||
if (is_chunked) {
|
||||
if (decode_chunked(&clean_body, body, body_len) < 0) {
|
||||
buffer_free(&clean_body);
|
||||
result = JS_ThrowInternalError(ctx, "Failed to decode chunked response");
|
||||
goto cleanup;
|
||||
}
|
||||
body = clean_body.data;
|
||||
body_len = clean_body.size;
|
||||
} else {
|
||||
// not chunked → just copy
|
||||
buffer_append(&clean_body, body, body_len);
|
||||
body = clean_body.data;
|
||||
body_len = clean_body.size;
|
||||
}
|
||||
|
||||
printf("[HTTP] Body length after decoding: %zu bytes\n", body_len);
|
||||
printf("[HTTP] First 100 chars of decoded body: ");
|
||||
for (size_t i = 0; i < 100 && i < body_len; i++) {
|
||||
if (body[i] >= 32 && body[i] <= 126) {
|
||||
printf("%c", body[i]);
|
||||
} else {
|
||||
printf("\\x%02x", (unsigned char)body[i]);
|
||||
}
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
// build blob from *body / body_len* exactly as before
|
||||
result = js_new_blob_stoned_copy(ctx, (uint8_t *)body, body_len);
|
||||
|
||||
buffer_free(&clean_body);
|
||||
} else {
|
||||
char error_buf[256];
|
||||
mbedtls_strerror(ret, error_buf, sizeof(error_buf));
|
||||
result = JS_ThrowInternalError(ctx, "Request failed: %s", error_buf);
|
||||
}
|
||||
|
||||
cleanup:
|
||||
// Cleanup
|
||||
JS_FreeCString(ctx, url);
|
||||
free(host);
|
||||
|
||||
Reference in New Issue
Block a user