fix: deviate from fetch() spec and allow status codes up to 999

This commit is contained in:
Ashcon Partovi
2025-03-20 12:48:58 -07:00
parent 62de1caa02
commit 9a1a080d49
5 changed files with 101 additions and 9 deletions

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -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(
{

View File

@@ -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();
});
});

View File

@@ -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);
});