Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Browser Stdin auto pipes to Stdout. #448

Open
IbrahimTanyalcin opened this issue Dec 8, 2024 · 1 comment
Open

Browser Stdin auto pipes to Stdout. #448

IbrahimTanyalcin opened this issue Dec 8, 2024 · 1 comment

Comments

@IbrahimTanyalcin
Copy link

Thank you for this nice package. I am currently trying to make it work with my wasm file in the browser. Here is some background:

I have a bash script that constantly listens to stdin and accumulates user input until a sequence of chars are encountered (____END____ in my case), the tty behavior is like below:

stdin  ===> {"a" 
stdout ===>{"a"
stdin  ===>  ____END____ 
stdout ===>{"a"____END____
stdout ===>{"type":"Error", "message": "Invalid JSON"}
stdin  ===> {"a":1,"b":2}____END____

stdout ===>success!!
stdout ===>{"type":"Error", "message": "Missing fields"}

Until I send SIGINT or similar. Now I converted this to a docker container (with openssl and all other deps with Alpine base image) and run:

docker run -it --rm --name app-test my-repo:my-tag

which replicates the behavior of the bash script 1 to 1. Next I converted the container to a wasm using container2wasm package using c2w binary. And run the resulting wasm using wasmtime:

wasmtime /home/user/path/to/out_alpine.wasm

and the above perfectly replicates the behavior of the bash script. So all 3 steps work. Next is using your package Wasmer to make it work in the browser. So on the main thread side I have this:

const worker = new Worker("static/js/worker.js", {type: "module"});

and on the worker side:

import { init, Wasmer } from "./wasmer-sdk.mjs"; //same as https://unpkg.com/@wasmer/[email protected]/dist/index.mjs, I also have wasmer_js_bg.wasm under the same path 'static/js/wasmer_js_bg.wasm'

let wasmInstance = null;
let wasmStdin = null;
let decoder = new TextDecoder();
let encoder = new TextEncoder();
let stdoutStream  = new WritableStream({
    write(chunk) {
        postMessage({ type: 'output', data: decoder.decode(chunk) + "--test" });
    }
});
let stderrStream   = new WritableStream({
    write(chunk) {
        postMessage({ type: 'error', data: decoder.decode(chunk) + "--err-test" });
    }
});

(async () => {
    await init({log: "trace"});
    try {
        // Fetch the WASM file as binary data
        const response = await fetch("/static/js/out_alpine.wasm");
        if (!response.ok) {
            throw new Error(`Failed to fetch WASM: ${response.statusText}`);
        }

        const binaryFile = new Uint8Array(await response.arrayBuffer());
        const wasmModule = await Wasmer.fromFile(binaryFile);
        const { entrypoint } = wasmModule;

        //wasmInstance = await entrypoint.run({args: ["/bin/bash", "./myscript.sh"], cwd:"/app"}); doesnt work
        //wasmInstance = await entrypoint.run({stdin: (new TextEncoder).encode('{"a":1,"b":2}____END____')}); doesnt work
        wasmInstance = await entrypoint.run();
        //await wasmInstance.wait(); throws null pointer passed to rust
        wasmStdin = wasmInstance.stdin?.getWriter();
        wasmInstance.stdout?.pipeTo(stdoutStream);
        wasmInstance.stderr?.pipeTo(stderrStream);
    } catch (err) {
        postMessage({ type: "error", data: err.message || "Unexpected Error!" });
    }
})().catch(err => postMessage({ type: "error", data: err.message || "Unexpected Error!" }));

onmessage = async (event) => {
    const { input } = event.data;
    try {
        if (wasmStdin) {
            await wasmStdin.write(encoder.encode(input));
            //await wasmStdin.close(); to force EOF and doing encoder.encode(input + "\n") does not work
        } else {
            throw new Error("WASM stdin is not initialized.");
        }
    } catch (err) {
        console.error("Error sending input:", err.message);
        postMessage({ type: 'error', data: err.message || "Unexpected Error!" });
    }
};

above does not throw an error, and in fact loads the wasm as I can see from the debug log, which I attached. My only problem is, when I postMessage to the worker, whatever I post is reflected back. I checked if wasmInstance.stdin or wasmInstance.stdout return the same pointer but no, they return different objects. So it all seems to be working accept the fact that when I postMessage
"abc____END_____", I get back "abc____END_____--test" into the DOM.

Why is stdin seems to be piped to stdout? What am I missing?
I attached the DEBUG log.
wasmer.log

Many thanks!

@gtg092x
Copy link

gtg092x commented Dec 31, 2024

This is additionally happening to me. Stdio is broken if it's using the streaming APIs.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants