txiki.js: Can't read from from process and write to client in Native Messaging Host
I have working C++
// C++ Native Messaging host
// https://browserext.github.io/native-messaging/
// https://developer.chrome.com/docs/apps/nativeMessaging/
// https://discourse.mozilla.org/t/webextension-with-native-messaging-c-app-side/30821
#include <iostream>
using namespace std;
void sendMessage(string message) {
uint32_t size = uint32_t(message.size());
char *length = reinterpret_cast<char *>(&size);
fwrite(length, 4, sizeof(char), stdout);
fwrite(message.c_str(), message.length(), sizeof(char), stdout);
fflush(stdout);
}
string getMessage() {
char length[4];
fread(length, 4, sizeof(char), stdin);
uint32_t len = *reinterpret_cast<uint32_t *>(length);
if (!len) {
exit(EXIT_SUCCESS);
}
char message[len];
fread(message, len, sizeof(char), stdin);
string content(message, message + sizeof(message) / sizeof(message[0]));
return content;
}
int main() {
string message = getMessage();
// Exclude double quotation marks from beginning and end of string
string input = message.substr(1, message.length() - 2);
FILE *pipe = popen(input.c_str(), "r");
while (true) {
unsigned char buffer[1764]; // 441 * 4
int count = fread(buffer, 1, sizeof(buffer), pipe);
string output;
output += "[";
for (int i = 0; i < count; i++) {
output += to_string((int)buffer[i]);
if (i < count - 1) {
output += ",";
}
}
output += "]";
sendMessage(output);
}
}
and Python code (https://github.com/guest271314/captureSystemAudio/blob/master/native_messaging/capture_system_audio/capture_system_audio.py) to open a new subprocess and stream stdout from the process to client (browser).
QuickJS and txiki.js do not appear capable of handling reading from the subprocess and writing to the client.
I have tried different approaches, none achieve the expected result.
#!tjs
// txiki.js Native Messaging host
// guest271314, 5-6-2022
(async () => {
let encoder = new TextEncoder();
let decoder = new TextDecoder();
async function getMessage() {
const header = new Uint8Array(4);
const input = await tjs.stdin.read(header);
// https://stackoverflow.com/a/26140545
const length = new Uint32Array(header.buffer).reduce(
(a, b, index) => a | (b << (index * 8)),
0
);
const output = new Uint8Array(length);
await tjs.stdin.read(output);
return output;
}
async function sendMessage(message) {
// https://stackoverflow.com/a/24777120
const header = Uint32Array.from(
{
length: 4,
},
(_, index) => (message.length >> (index * 8)) & 0xff
);
const output = new Uint8Array(header.length + message.length);
output.set(header, 0);
output.set(message, 4);
await tjs.stdout.write(output);
return true;
}
let message, proc, data;
while (true) {
// message = await getMessage();
// await sendMessage(message);
proc = tjs.spawn(['parec', '-d', '@DEFAULT_MONITOR@'], { stdout: 'pipe' });
data = new Uint8Array(1024);
while (true) {
await proc.stdout.read(data);
// await sendMessage(data);
console.log(data);
}
}
})();
Using QuickJS
#!/usr/bin/env -S /path/to/qjs -m --std
// QuickJS Native Messaging host
// guest271314, 5-6-2022
import * as std from 'std';
import * as os from 'os';
async function getMessage() {
const header = new Uint8Array(4);
std.in.read(header.buffer, 0, header.length);
// https://stackoverflow.com/a/26140545
const length = new Uint32Array(header.buffer).reduce(
(a, b, index) => a | (b << (index * 8)),
0
);
const output = new Uint8Array(length);
std.in.read(output.buffer, 0, length);
return output;
}
async function encodeMessage(message) {
// https://stackoverflow.com/a/24777120
const header = Uint32Array.from(
{
length: 4,
},
(_, index) => (message.length >> (index * 8)) & 0xff
);
const output = new Uint8Array(header.length + message.length);
output.set(header, 0);
output.set(message, 4);
return output;
}
async function sendMessage(message) {
std.out.write(message.buffer, 0, message.length);
std.out.flush();
return true;
}
async function main() {
let message = await getMessage();
let fds = os.pipe();
let pid = os.exec(["sh", "-c", "parec -d @DEFAULT_MONITOR@"], {
stdout: fds[1],
block: false
});
os.close(fds[1]);
let f = std.fdopen(fds[0], "r");
while (true) {
let data = new Uint8Array([...f.readAsString(1024)].map(s => s.codePointAt(0) & 0xff));
await sendMessage(await encodeMessage(data));
// console.log(data);
}
}
main();
The expected result is that the message is passed to Native Messaging Host, parsed to string, or array if necessary, passed to spawn() and stdout from that process is sent to the Native Messaging client.
When the file is run standalone, removing getMessage() and sendMessage() calls, substituting console.log(data) for sendMessage(data), the process is started, and an object representaion of Uint8Array are printed at terminal.
(I am not sure if Uint8Array is completely implemented?)
Why does read() block in this case?
What do I need to adjust to achieve the same or similar result I do using C++ and Python?
About this issue
- Original URL
- State: closed
- Created 2 years ago
- Comments: 28 (28 by maintainers)
Got the
Uint8Array()and write()` version working as expectedGot the code working without importing the .so file substituting sending JSON string with
puts()forUint8Array()withwrite().