diff --git a/src/bun.js/ipc.zig b/src/bun.js/ipc.zig index 3717a9a90a..cfa4150f4f 100644 --- a/src/bun.js/ipc.zig +++ b/src/bun.js/ipc.zig @@ -194,21 +194,19 @@ const json = struct { // 1 is regular // 2 is internal - pub fn decodeIPCMessage( - data: []const u8, - globalThis: *JSC.JSGlobalObject, - ) IPCDecodeError!DecodeIPCMessageResult { + pub fn decodeIPCMessage(data: []const u8, globalThis: *JSC.JSGlobalObject) IPCDecodeError!DecodeIPCMessageResult { if (bun.strings.indexOfChar(data, '\n')) |idx| { var kind = data[0]; - var json_data = data[1..idx]; + var json_data = data[0..idx]; switch (kind) { - 1, 2 => {}, + 2 => { + json_data = data[1..idx]; + }, else => { - // if the message being recieved is from a node process then it wont have the leading marker byte - // assume full message will be json + // assume it's valid json with no header + // any error will be thrown by toJSByParseJSON below kind = 1; - json_data = data[0..idx]; }, } @@ -230,6 +228,7 @@ const json = struct { } const deserialized = str.toJSByParseJSON(globalThis); + if (deserialized == .zero) return error.InvalidFormat; return switch (kind) { 1 => .{ @@ -259,13 +258,12 @@ const json = struct { 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("\n"); - return 1 + slice.len + 1; + return slice.len + 1; } 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 bun.copy(u8, ipc.incoming.ptr[0..slice.len], slice); ipc.incoming.len = @truncate(slice.len); - log("hit NotEnoughBytes2", .{}); + log("hit NotEnoughBytes3", .{}); return; }, error.InvalidFormat => { diff --git a/test/js/bun/spawn/fixtures/ipc-child-bun.js b/test/js/bun/spawn/fixtures/ipc-child-bun.js new file mode 100644 index 0000000000..b5e3efed52 --- /dev/null +++ b/test/js/bun/spawn/fixtures/ipc-child-bun.js @@ -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"); diff --git a/test/js/bun/spawn/fixtures/ipc-child-node.js b/test/js/bun/spawn/fixtures/ipc-child-node.js new file mode 100644 index 0000000000..b5e3efed52 --- /dev/null +++ b/test/js/bun/spawn/fixtures/ipc-child-node.js @@ -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"); diff --git a/test/js/bun/spawn/fixtures/ipc-parent-bun.js b/test/js/bun/spawn/fixtures/ipc-parent-bun.js new file mode 100644 index 0000000000..dfebf93f69 --- /dev/null +++ b/test/js/bun/spawn/fixtures/ipc-parent-bun.js @@ -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"); diff --git a/test/js/bun/spawn/fixtures/ipc-parent-node.js b/test/js/bun/spawn/fixtures/ipc-parent-node.js new file mode 100644 index 0000000000..cb9c40b473 --- /dev/null +++ b/test/js/bun/spawn/fixtures/ipc-parent-node.js @@ -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"); diff --git a/test/js/bun/spawn/spawn.ipc.bun-node.test.ts b/test/js/bun/spawn/spawn.ipc.bun-node.test.ts new file mode 100644 index 0000000000..f03212e339 --- /dev/null +++ b/test/js/bun/spawn/spawn.ipc.bun-node.test.ts @@ -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(""); +}); diff --git a/test/js/bun/spawn/spawn.ipc.node-bun.test.ts b/test/js/bun/spawn/spawn.ipc.node-bun.test.ts new file mode 100644 index 0000000000..a41cdf8568 --- /dev/null +++ b/test/js/bun/spawn/spawn.ipc.node-bun.test.ts @@ -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 +`, + ); +});