diff --git a/src/runtime/server/NodeHTTPResponse.rs b/src/runtime/server/NodeHTTPResponse.rs index 15977391c7e4..fad9d54d6028 100644 --- a/src/runtime/server/NodeHTTPResponse.rs +++ b/src/runtime/server/NodeHTTPResponse.rs @@ -1920,21 +1920,30 @@ impl NodeHTTPResponse { let mut string_or_buffer = crate::node::StringOrBuffer::EMPTY; if !input_value.is_undefined_or_null() { let mut encoding = crate::node::Encoding::Utf8; - if !encoding_value.is_undefined_or_null() { - if !encoding_value.is_string() { - return Err(global_object.throw_invalid_argument_type_value( - b"encoding", - b"string", - encoding_value, - )); - } - - encoding = match crate::node::Encoding::from_js(encoding_value, global_object)? { + // Like Writable.prototype.write: a falsy encoding means the default. + if !encoding_value.is_falsey() { + let known = if encoding_value.is_string() { + crate::node::Encoding::from_js(encoding_value, global_object)? + } else { + None + }; + encoding = match known { Some(e) => e, None => { - return Err( - global_object.throw_invalid_arguments(format_args!("Invalid encoding")) - ); + let name = if encoding_value.is_string() { + encoding_value.to_bun_string(global_object)? + } else { + JSGlobalObject::inspect_for_error_message( + global_object, + encoding_value, + )? + }; + return Err(global_object + .err( + ErrorCode::UNKNOWN_ENCODING, + format_args!("Unknown encoding: {}", name), + ) + .throw()); } }; } diff --git a/test/js/node/http/node-http.test.ts b/test/js/node/http/node-http.test.ts index 71ad7e6435d6..df14fc22b658 100644 --- a/test/js/node/http/node-http.test.ts +++ b/test/js/node/http/node-http.test.ts @@ -329,6 +329,54 @@ describe("node:http", () => { "test": "test", }); }); + + // Node's Writable.prototype.write uses the default encoding for a falsy + // encoding argument, so res.write(chunk, "") and res.end(chunk, "") write utf8. + test.each(["write", "end"])("res.%s accepts an empty-string encoding (#43370)", async method => { + const body = "héllo wörld ✓"; + await using server = http.createServer((req, res) => { + try { + if (method === "write") { + res.write(body, ""); + res.end(); + } else { + res.end(body, ""); + } + } catch (e: any) { + res.statusCode = 500; + res.end(`${e.code}: ${e.message}`); + } + }); + await once(server.listen(0, "127.0.0.1"), "listening"); + const { port } = server.address() as AddressInfo; + + const response = await fetch(`http://127.0.0.1:${port}/`); + expect(await response.text()).toBe(body); + expect(response.status).toBe(200); + }); + + test.each(["write", "end"])("res.%s throws ERR_UNKNOWN_ENCODING for an unknown encoding", async method => { + const errors: string[] = []; + await using server = http.createServer((req, res) => { + for (const encoding of ["bogus", 123, {}]) { + try { + res[method]("x", encoding); + } catch (e: any) { + errors.push(`${e.code}: ${e.message}`); + } + } + res.end(); + }); + await once(server.listen(0, "127.0.0.1"), "listening"); + const { port } = server.address() as AddressInfo; + + await fetch(`http://127.0.0.1:${port}/`); + expect(errors).toEqual([ + "ERR_UNKNOWN_ENCODING: Unknown encoding: bogus", + "ERR_UNKNOWN_ENCODING: Unknown encoding: 123", + "ERR_UNKNOWN_ENCODING: Unknown encoding: {}", + ]); + }); }); describe("request", () => {