在 GJS 中编写本地消息传递主机

Writing a native messaging host in GJS

我正在尝试编写一个 native messaging host for a chrome/firefox extension in GJS (since it will rely on code already written in GJS) but encountering some hurdles. I'm using chrome-gnome-shell 作为粗略模板,因为它也使用 GLib/Gio 自省和 GApplication,但它具有 python struct 的优点我没有。

很快,本地消息传递主机通过 stdin/stdout 交换消息,消息是一个 Int32(4 字节)长度后跟一个 utf-8 编码的字符串 JSON.

chrome-gnome-shell 使用 GLib.IOChannelset_encoding('utf-8')struct 来处理 int32 字节。我在 GJS 中使用 class 时遇到了问题并且没有 struct 所以一直在尝试 Gio.UnixInputStream 包装在 Gio.DataInputStream (和输出对应物)中, put_int32()/read_int32()put_string()/read_string().

显然我对自己在做什么感到非常困惑。如果我调用 Gio.DataInputStream.read_int32() 它 returns 一个数字 369098752,那么我猜 int32 没有被转换为常规数字。如果我调用 Gio.DataInputStream.read_bytes(4, null).unref_to_array() 来获取 ByteArray; ByteArray.toString() returns '\u0016' 而 ByteArray[0] returns '22' 似乎是实际长度。

关于 reading/writing int32 的一些指向数据流的指针,将不胜感激。

chrome-gnome-shell 参考资料:

我不知道这是否是解决此问题的最佳方法,但这是我想出的方法。

使用 ByteArray 导入的两个函数(从 SO 的某处修改):

const ByteArray = imports.byteArray;

function fromInt32 (byteArray) {
    var value = 0;

    for (var i = byteArray.length - 1; i >= 0; i--) {
        value = (value * 256) + byteArray[i];
    }

    return value;
};

function toInt32 (num) {
    var byteArray = [0, 0, 0, 0];

    for (var index_ = 0; index_ < byteArray.length; index_++) {
        var byte = num & 0xff;
        byteArray [index_] = byte;
        num = (num - byte) / 256 ;
    }

    return ByteArray.fromArray(byteArray);
};

对于receiving/sending:

const Gio = imports.gi.Gio;

// Receiving
let stdin = new Gio.DataInputStream({
    base_stream: new Gio.UnixInputStream({ fd: 0 })
});

let int32 = stdin.read_bytes(4, null).toArray();
let length = fromInt32(int32);
let data = stdin.read_bytes(length, null).toArray().toString();
let message = JSON.parse(data);

// Sending
let stdout = new Gio.DataOutputStream({
    base_stream: new Gio.UnixOutputStream({ fd: 1 })
});

let data = JSON.stringify(message);
let int32 = toInt32(data.length);
stdout.write(int32, null);
stdout.put_string(data, null);

当然,您应该根据需要将它们包装在 try-catch 中,并且您可能希望将源连接到输入(您可以使用 Gio.UnixInputStream):

let source = stdin.base_stream.create_source(null);
source.set_callback(onReceiveFunc);
source.attach(null);

您可以像使用 read_bytes()put_string() 一样使用 Gio.DataOutputStream.put_int32() and Gio.DataInputStream.read_int32()