From e9791e1d8bb52f49bdfa87ee9b3e11f6e0ab9413 Mon Sep 17 00:00:00 2001 From: Ciro Spaciari Date: Fri, 11 Oct 2024 23:06:18 -0700 Subject: [PATCH] fix goaway to match nodejs --- src/bun.js/api/bun/h2_frame_parser.zig | 7 +- ...-early-hints-invalid-argument-type.test.js | 72 +++++++++++++++++++ .../parallel/http2-goaway-opaquedata.test.js | 58 +++++++++++++++ 3 files changed, 131 insertions(+), 6 deletions(-) create mode 100644 test/js/node/test/parallel/http2-compat-write-early-hints-invalid-argument-type.test.js create mode 100644 test/js/node/test/parallel/http2-goaway-opaquedata.test.js diff --git a/src/bun.js/api/bun/h2_frame_parser.zig b/src/bun.js/api/bun/h2_frame_parser.zig index 0ed29ab266d76..09262623ce1b4 100644 --- a/src/bun.js/api/bun/h2_frame_parser.zig +++ b/src/bun.js/api/bun/h2_frame_parser.zig @@ -1925,15 +1925,10 @@ pub const H2FrameParser = struct { if (handleIncommingPayload(this, data, frame.streamIdentifier)) |content| { const payload = content.data; - const last_stream_id: u32 = @intCast(UInt31WithReserved.fromBytes(payload[0..4]).uint31); const error_code = UInt31WithReserved.fromBytes(payload[4..8]).toUInt32(); const chunk = this.handlers.binary_type.toJS(payload[8..], this.handlers.globalObject); this.readBuffer.reset(); - if (error_code != @intFromEnum(ErrorCode.NO_ERROR)) { - this.dispatchWith2Extra(.onGoAway, JSC.JSValue.jsNumber(error_code), JSC.JSValue.jsNumber(last_stream_id), chunk); - } else { - this.dispatchWithExtra(.onGoAway, JSC.JSValue.jsNumber(last_stream_id), chunk); - } + this.dispatchWith2Extra(.onGoAway, JSC.JSValue.jsNumber(error_code), JSC.JSValue.jsNumber(this.lastStreamID), chunk); return content.end; } return data.len; diff --git a/test/js/node/test/parallel/http2-compat-write-early-hints-invalid-argument-type.test.js b/test/js/node/test/parallel/http2-compat-write-early-hints-invalid-argument-type.test.js new file mode 100644 index 0000000000000..0ab3a588a330f --- /dev/null +++ b/test/js/node/test/parallel/http2-compat-write-early-hints-invalid-argument-type.test.js @@ -0,0 +1,72 @@ +//#FILE: test-http2-compat-write-early-hints-invalid-argument-type.js +//#SHA1: 8ae2eba59668a38b039a100d3ad26f88e54be806 +//----------------- +"use strict"; + +const http2 = require("node:http2"); +const util = require("node:util"); +const debug = util.debuglog("test"); + +const testResBody = "response content"; + +// Check if crypto is available +let hasCrypto = false; +try { + require("crypto"); + hasCrypto = true; +} catch (err) { + // crypto not available +} + +(hasCrypto ? describe : describe.skip)("HTTP2 compat writeEarlyHints invalid argument type", () => { + let server; + let client; + + beforeAll(done => { + server = http2.createServer(); + server.listen(0, () => { + done(); + }); + }); + + afterAll(() => { + if (client) { + client.close(); + } + server.close(); + }); + + test("should throw ERR_INVALID_ARG_TYPE for invalid object value", done => { + server.on("request", (req, res) => { + debug("Server sending early hints..."); + expect(() => { + res.writeEarlyHints("this should not be here"); + }).toThrow( + expect.objectContaining({ + code: "ERR_INVALID_ARG_TYPE", + name: "TypeError", + }), + ); + + debug("Server sending full response..."); + res.end(testResBody); + }); + + client = http2.connect(`http://localhost:${server.address().port}`); + const req = client.request(); + + debug("Client sending request..."); + + req.on("headers", () => { + done(new Error("Should not receive headers")); + }); + + req.on("response", () => { + done(); + }); + + req.end(); + }); +}); + +//<#END_FILE: test-http2-compat-write-early-hints-invalid-argument-type.js diff --git a/test/js/node/test/parallel/http2-goaway-opaquedata.test.js b/test/js/node/test/parallel/http2-goaway-opaquedata.test.js new file mode 100644 index 0000000000000..7de326326636b --- /dev/null +++ b/test/js/node/test/parallel/http2-goaway-opaquedata.test.js @@ -0,0 +1,58 @@ +//#FILE: test-http2-goaway-opaquedata.js +//#SHA1: 5ad5b6a64cb0e7419753dcd88d59692eb97973ed +//----------------- +'use strict'; + +const http2 = require('http2'); + +let server; +let serverPort; + +beforeAll((done) => { + server = http2.createServer(); + server.listen(0, () => { + serverPort = server.address().port; + done(); + }); +}); + +afterAll((done) => { + server.close(done); +}); + +test('HTTP/2 GOAWAY with opaque data', (done) => { + const data = Buffer.from([0x1, 0x2, 0x3, 0x4, 0x5]); + let session; + + server.once('stream', (stream) => { + session = stream.session; + session.on('close', () => { + expect(true).toBe(true); // Session closed + }); + session.goaway(0, 0, data); + stream.respond(); + stream.end(); + }); + + const client = http2.connect(`http://localhost:${serverPort}`); + client.once('goaway', (code, lastStreamID, buf) => { + expect(code).toBe(0); + expect(lastStreamID).toBe(1); + expect(buf).toEqual(data); + session.close(); + client.close(); + done(); + }); + + const req = client.request(); + req.resume(); + req.on('end', () => { + expect(true).toBe(true); // Request ended + }); + req.on('close', () => { + expect(true).toBe(true); // Request closed + }); + req.end(); +}); + +//<#END_FILE: test-http2-goaway-opaquedata.js