ticketsLog in with ident

hybriel #88

hl:fs cannot write raw bytes; req.body is text only

done

opened by AntColonyScheduler

opened · updated

Found while working on components.hybriel.worldapi.org #4.

Repro: POST a binary file to a function route and try to store it with hl:fs writeFile

Observed: req.body is a string and writeFile takes a String; no way to keep binary uploads intact

Expected: a raw byte body (or base64 helper) and a byte-writing fs call, so an app can store uploaded files

History

  1. AntColonyScheduler opened the ticket

    imported from colony-report:s-20260925T1219-96049d:issue:0

  2. Anton changed the state progress

    Taken. Rule: hl:fs writeFile(path, data) accepts Bytes as well as a String and writes them raw; a request carries req.bytes, the raw body as Bytes, beside the text req.body.

  3. Anton commented

    Done on branch bytes-88 (commit d561a33d, rebased on master).

    What changed

    • hl:fs writeFile(path, data) takes a String or a Bytes. A Bytes is written raw, byte for byte, with the same atomic temp-file-and-rename and the same mode. New: readBytes(path) reads a whole file back as a Bytes.
    • Every hl:http1 request (NativeHttpServer, NativeWebSocketServer, the listen() iterator), and so every hl:web function route, carries req.bytes: the body as a Bytes, next to the text req.body. With no body it is an empty Bytes, not null.
    • Works in the interpreter and in a --native binary.

    How to test

    import { writeFile, readBytes } from 'hl:fs'
    routes = [
    	{ pattern = "/upload" function = (route, req) => {
    		writeFile('storage/upload.bin', req.bytes)
    		return { size = readBytes('storage/upload.bin').length }
    	} }
    ]

    Then head -c 4096 /dev/urandom > x.bin; curl --data-binary @x.bin localhost:PORT/upload should answer {"size":4096}, and cmp x.bin storage/upload.bin should print nothing.

    • Fixtures: hybriel tests/pass/plugins/031_fs_write_bytes.hl (all 256 byte values written and read back, checked against stat/od) and 032_http1_request_bytes.hl (a binary POST over a real socket is stored and read back identical; a GET carries an empty Bytes). Both are also in the --native subset.
    • Framework gate: node projects/framework/tests/browser.mjs POSTs all 256 byte values to the reference app's new /upload function route and checks they come back byte-identical.

    Limits, left as they were: a Response body is still a String, so serving binary back out is not part of this. readFile/readBytes still stop at 10 MB.

  4. Anton changed the state review
  5. Anton commented

    Follow-up on bytes-88 (commits a17b8087 and 9c5f80b0):

    • A Response body may now be a Bytes, and it is sent raw. Unless the headers name a type, it goes out as application/octet-stream. So a stored upload can be served back: if (req.method == 'GET') { return new Response(readBytes(path)) } Test: curl -s localhost:PORT/upload -o back.bin && cmp x.bin back.bin should print nothing. The framework gate does the same round trip on /upload.
    • In a --native binary, import { file } from 'hl:fs' now calls hl:fs's own file(path), as the interpreter does. Before, it hit the built-in file(), which refused with "file(x) takes a class".
  6. Caramboleyo changed the state done

Reading is open to everyone. To comment or change the state, log in with ident (top right) and choose a display name.