diff --git a/src/bun.js/api/server.zig b/src/bun.js/api/server.zig index 51a0ace195..0fdae7eb48 100644 --- a/src/bun.js/api/server.zig +++ b/src/bun.js/api/server.zig @@ -6559,7 +6559,7 @@ pub const NodeHTTPResponse = struct { if (status_code_value != .undefined) { break :brk globalObject.validateIntegerRange(status_code_value, i32, 200, .{ .min = 100, - .max = 599, + .max = 999, }) catch return error.JSError; } diff --git a/src/bun.js/webcore/response.zig b/src/bun.js/webcore/response.zig index 0b27ce768e..4a2dedb634 100644 --- a/src/bun.js/webcore/response.zig +++ b/src/bun.js/webcore/response.zig @@ -714,11 +714,13 @@ pub const Response = struct { if (response_init.fastGet(globalThis, .status)) |status_value| { const number = status_value.coerceToInt64(globalThis); - if ((200 <= number and number < 600) or number == 101) { + // Even though the fetch spec says the range is [200, 599], there are some websites + // that use status codes up to 999, like linkedin.com, so allow it. + if ((200 <= number and number <= 999) or number == 101) { result.status_code = @as(u16, @truncate(@as(u32, @intCast(number)))); } else { if (!globalThis.hasException()) { - const err = globalThis.createRangeErrorInstance("The status provided ({d}) must be 101 or in the range of [200, 599]", .{number}); + const err = globalThis.createRangeErrorInstance("The status provided ({d}) must be 101 or in the range of [200, 999]", .{number}); return globalThis.throwValue(err); } return error.JSError; diff --git a/test/js/bun/http/serve.test.ts b/test/js/bun/http/serve.test.ts index b279311995..48495a6291 100644 --- a/test/js/bun/http/serve.test.ts +++ b/test/js/bun/http/serve.test.ts @@ -303,7 +303,7 @@ describe.todoIf(isBroken && isIntelMacOS)( }, ); -[200, 200n, 303, 418, 599, 599n].forEach(statusCode => { +[200, 200n, 303, 418, 599, 599n, 999, 999n].forEach(statusCode => { it(`should response with HTTP status code (${statusCode})`, async () => { await runTest( { @@ -320,7 +320,7 @@ describe.todoIf(isBroken && isIntelMacOS)( }); }); -[-200, 42, 100, 102, 12345, Math.PI, 999, 600, 199, 199n, 600n, 100n, 102n].forEach(statusCode => { +[-200, 42, 100, 102, 12345, Math.PI, 199, 199n, 100n, 102n].forEach(statusCode => { it(`should error on invalid HTTP status code (${statusCode})`, async () => { await runTest( { diff --git a/test/js/node/test/parallel/test-http-response-statuscode.js b/test/js/node/test/parallel/test-http-response-statuscode.js new file mode 100644 index 0000000000..6f81f287fe --- /dev/null +++ b/test/js/node/test/parallel/test-http-response-statuscode.js @@ -0,0 +1,90 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const http = require('http'); +const Countdown = require('../common/countdown'); + +const MAX_REQUESTS = 13; +let reqNum = 0; + +function test(res, header, code) { + assert.throws(() => { + res.writeHead(header); + }, { + code: 'ERR_HTTP_INVALID_STATUS_CODE', + name: 'RangeError', + message: `Invalid status code: ${code}` + }); +} + +const server = http.Server(common.mustCall(function(req, res) { + switch (reqNum) { + case 0: + test(res, -1, '-1'); + break; + case 1: + test(res, Infinity, 'Infinity'); + break; + case 2: + test(res, NaN, 'NaN'); + break; + case 3: + test(res, {}, '{}'); + break; + case 4: + test(res, 99, '99'); + break; + case 5: + test(res, 1000, '1000'); + break; + case 6: + test(res, '1000', '"1000"'); + break; + case 7: + test(res, null, 'null'); + break; + case 8: + test(res, true, 'true'); + break; + case 9: + test(res, [], '[]'); + break; + case 10: + test(res, 'this is not valid', '"this is not valid"'); + break; + case 11: + test(res, '404 this is not valid either', '"404 this is not valid either"'); + break; + case 12: + assert.throws(() => { res.writeHead(); }, + { + code: 'ERR_HTTP_INVALID_STATUS_CODE', + name: 'RangeError', + message: 'Invalid status code: undefined' + }); + this.close(); + break; + default: + throw new Error('Unexpected request'); + } + res.statusCode = 200; + res.end(); +}, MAX_REQUESTS)); +server.listen(); + +const countdown = new Countdown(MAX_REQUESTS, () => server.close()); + +server.on('listening', function makeRequest() { + http.get({ + port: this.address().port + }, (res) => { + assert.strictEqual(res.statusCode, 200); + res.on('end', () => { + countdown.dec(); + reqNum = MAX_REQUESTS - countdown.remaining; + if (countdown.remaining > 0) + makeRequest.call(this); + }); + res.resume(); + }); +}); diff --git a/test/js/web/fetch/fetch.test.ts b/test/js/web/fetch/fetch.test.ts index 6e8fc443ef..0f84ad9676 100644 --- a/test/js/web/fetch/fetch.test.ts +++ b/test/js/web/fetch/fetch.test.ts @@ -1331,12 +1331,12 @@ describe("Response", () => { it("should work with bigint", () => { var r = new Response("hello status", { status: 200n }); expect(r.status).toBe(200); - r = new Response("hello status", { status: 599n }); - expect(r.status).toBe(599); + r = new Response("hello status", { status: 999n }); + expect(r.status).toBe(999); r = new Response("hello status", { status: BigInt(200) }); expect(r.status).toBe(200); - r = new Response("hello status", { status: BigInt(599) }); - expect(r.status).toBe(599); + r = new Response("hello status", { status: BigInt(999) }); + expect(r.status).toBe(999); }); testBlobInterface(data => new Response(data), true); });