make sure ipc with json serialization still works when bun is parent and not the child (#14756)

Co-authored-by: Jarred Sumner <jarred@jarredsumner.com>
This commit is contained in:
Meghan Denny
2025-01-11 19:18:28 -08:00
committed by GitHub
parent f9de8be417
commit 11feeff892
7 changed files with 97 additions and 13 deletions

View File

@@ -194,21 +194,19 @@ const json = struct {
// 1 is regular // 1 is regular
// 2 is internal // 2 is internal
pub fn decodeIPCMessage( pub fn decodeIPCMessage(data: []const u8, globalThis: *JSC.JSGlobalObject) IPCDecodeError!DecodeIPCMessageResult {
data: []const u8,
globalThis: *JSC.JSGlobalObject,
) IPCDecodeError!DecodeIPCMessageResult {
if (bun.strings.indexOfChar(data, '\n')) |idx| { if (bun.strings.indexOfChar(data, '\n')) |idx| {
var kind = data[0]; var kind = data[0];
var json_data = data[1..idx]; var json_data = data[0..idx];
switch (kind) { switch (kind) {
1, 2 => {}, 2 => {
json_data = data[1..idx];
},
else => { else => {
// if the message being recieved is from a node process then it wont have the leading marker byte // assume it's valid json with no header
// assume full message will be json // any error will be thrown by toJSByParseJSON below
kind = 1; kind = 1;
json_data = data[0..idx];
}, },
} }
@@ -230,6 +228,7 @@ const json = struct {
} }
const deserialized = str.toJSByParseJSON(globalThis); const deserialized = str.toJSByParseJSON(globalThis);
if (deserialized == .zero) return error.InvalidFormat;
return switch (kind) { return switch (kind) {
1 => .{ 1 => .{
@@ -259,13 +258,12 @@ const json = struct {
const slice = str.slice(); const slice = str.slice();
try writer.ensureUnusedCapacity(1 + slice.len + 1); try writer.ensureUnusedCapacity(slice.len + 1);
writer.writeAssumeCapacity(&.{1});
writer.writeAssumeCapacity(slice); writer.writeAssumeCapacity(slice);
writer.writeAssumeCapacity("\n"); writer.writeAssumeCapacity("\n");
return 1 + slice.len + 1; return slice.len + 1;
} }
pub fn serializeInternal(_: *IPCData, writer: anytype, global: *JSC.JSGlobalObject, value: JSValue) !usize { pub fn serializeInternal(_: *IPCData, writer: anytype, global: *JSC.JSGlobalObject, value: JSValue) !usize {
@@ -860,7 +858,7 @@ fn NewNamedPipeIPCHandler(comptime Context: type) type {
// copy the remaining bytes to the start of the buffer // copy the remaining bytes to the start of the buffer
bun.copy(u8, ipc.incoming.ptr[0..slice.len], slice); bun.copy(u8, ipc.incoming.ptr[0..slice.len], slice);
ipc.incoming.len = @truncate(slice.len); ipc.incoming.len = @truncate(slice.len);
log("hit NotEnoughBytes2", .{}); log("hit NotEnoughBytes3", .{});
return; return;
}, },
error.InvalidFormat => { error.InvalidFormat => {

View File

@@ -0,0 +1,7 @@
console.log("c start");
process.on("message", message => {
console.log("c", message);
process.send(message);
process.exit(0);
});
console.log("c end");

View File

@@ -0,0 +1,7 @@
console.log("c start");
process.on("message", message => {
console.log("c", message);
process.send(message);
process.exit(0);
});
console.log("c end");

View File

@@ -0,0 +1,14 @@
const path = require("node:path");
console.log("p start");
const child = Bun.spawn(["node", path.resolve(import.meta.dir, "ipc-child-node.js")], {
ipc(message) {
console.log("p", message);
process.exit(0);
},
stdio: ["ignore", "inherit", "inherit"],
serialization: "json",
});
child.send("I am your father");
console.log("p end");

View File

@@ -0,0 +1,15 @@
const path = require("node:path");
const child_process = require("node:child_process");
console.log("p start");
const child = child_process.spawn(process.argv[2], [path.resolve(__dirname, "ipc-child-bun.js")], {
stdio: ["ignore", "inherit", "inherit", "ipc"],
});
child.on("message", message => {
console.log("p", message);
process.exit(0);
});
child.send("I am your father");
console.log("p end");

View File

@@ -0,0 +1,21 @@
import { spawn } from "bun";
import { test, expect, it } from "bun:test";
import { bunExe } from "harness";
import path from "path";
test("ipc with json serialization still works when bun is parent and not the child", async () => {
const child = Bun.spawn([bunExe(), path.resolve(import.meta.dir, "fixtures", "ipc-parent-bun.js")], {
stdio: ["ignore", "pipe", "pipe"],
});
await child.exited;
expect(await new Response(child.stdout).text()).toEqual(
`p start
p end
c start
c end
c I am your father
p I am your father
`,
);
expect(await new Response(child.stderr).text()).toEqual("");
});

View File

@@ -0,0 +1,22 @@
import { spawn } from "bun";
import { test, expect, it } from "bun:test";
import { bunExe, nodeExe } from "harness";
import path from "path";
test("ipc with json serialization still works when bun is not the parent and the child", async () => {
// prettier-ignore
const child = Bun.spawn(["node", "--no-warnings", path.resolve(import.meta.dir, "fixtures", "ipc-parent-node.js"), bunExe()], {
stdio: ["ignore", "pipe", "pipe"],
});
await child.exited;
expect(await new Response(child.stderr).text()).toEqual("");
expect(await new Response(child.stdout).text()).toEqual(
`p start
p end
c start
c end
c I am your father
p I am your father
`,
);
});