diff --git a/src/deps/picohttp.zig b/src/deps/picohttp.zig index b674897fb9..a0507c6ffa 100644 --- a/src/deps/picohttp.zig +++ b/src/deps/picohttp.zig @@ -97,6 +97,15 @@ pub const Request = struct { ignore_insecure: bool = false, body: []const u8 = "", + fn isPrintableBody(content_type: []const u8) bool { + if (content_type.len == 0) return false; + + return bun.strings.hasPrefixComptime(content_type, "text/") or + bun.strings.hasPrefixComptime(content_type, "application/json") or + bun.strings.containsComptime(content_type, "json") or + bun.strings.hasPrefixComptime(content_type, "application/x-www-form-urlencoded"); + } + pub fn format(self: @This(), comptime _: []const u8, _: fmt.FormatOptions, writer: anytype) !void { const request = self.request; if (Output.enable_ansi_colors_stderr) { @@ -132,7 +141,7 @@ pub const Request = struct { } } - if (self.body.len > 0 and (content_type.len > 0 and bun.strings.hasPrefixComptime(content_type, "application/json") or bun.strings.hasPrefixComptime(content_type, "text/") or bun.strings.containsComptime(content_type, "json"))) { + if (self.body.len > 0 and isPrintableBody(content_type)) { _ = try writer.writeAll(" --data-raw "); try bun.js_printer.writeJSONString(self.body, @TypeOf(writer), writer, .utf8); } diff --git a/test/regression/issue/12042.test.ts b/test/regression/issue/12042.test.ts new file mode 100644 index 0000000000..f882aa78fe --- /dev/null +++ b/test/regression/issue/12042.test.ts @@ -0,0 +1,47 @@ +import { expect, test } from "bun:test"; +import { bunEnv, bunExe, normalizeBunSnapshot, tempDir } from "harness"; + +test("#12042 curl verbose fetch logs form-urlencoded body", async () => { + using dir = tempDir("issue-12042", { + "form.ts": ` +const server = Bun.serve({ + port: 0, + fetch() { + return new Response(JSON.stringify({ ok: true }), { + headers: { "Content-Type": "application/json" }, + }); + }, +}); + +const params = new URLSearchParams(); +params.set("grant_type", "client_credentials"); +params.set("client_id", "abc"); +params.set("client_secret", "xyz"); + +await fetch(String(server.url), { + method: "POST", + headers: { "Content-Type": "application/x-www-form-urlencoded" }, + body: params, +}); + +await server.stop(); + `, + }); + + const dirPath = String(dir); + + await using proc = Bun.spawn({ + cmd: [bunExe(), "form.ts"], + env: { ...bunEnv, BUN_CONFIG_VERBOSE_FETCH: "curl" }, + cwd: dirPath, + stdout: "pipe", + stderr: "pipe", + }); + + const [stdout, stderr] = await Promise.all([proc.stdout.text(), proc.stderr.text()]); + + const output = stdout + stderr; + const normalized = normalizeBunSnapshot(output, dirPath); + + expect(normalized).toContain('--data-raw "grant_type=client_credentials&client_id=abc&client_secret=xyz'); +});