Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/four-lamps-walk.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@sveltejs/kit': patch
---

[breaking] Disallow error status codes outside 400-599
12 changes: 11 additions & 1 deletion packages/kit/src/exports/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,22 @@ import { HttpError, Redirect, ValidationError } from '../runtime/control.js';
* @param {any} message
*/
export function error(status, message) {
if (
(!__SVELTEKIT_BROWSER__ || __SVELTEKIT_DEV__) &&
(isNaN(status) || status < 400 || status > 599)
) {
throw new Error(`HTTP error status codes must be between 400 and 599 — ${status} is invalid`);
}

return new HttpError(status, message);
}

/** @type {import('@sveltejs/kit').redirect} */
export function redirect(status, location) {
if (isNaN(status) || status < 300 || status > 308) {
if (
(!__SVELTEKIT_BROWSER__ || __SVELTEKIT_DEV__) &&
(isNaN(status) || status < 300 || status > 308)
) {
throw new Error('Invalid status code');
}

Expand Down
53 changes: 0 additions & 53 deletions packages/kit/src/runtime/control.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,56 +43,3 @@ export class ValidationError {
this.data = data;
}
}

/**
* Creates an `HttpError` object with an HTTP status code and an optional message.
* This object, if thrown during request handling, will cause SvelteKit to
* return an error response without invoking `handleError`
* @param {number} status
* @param {string | undefined} [message]
*/
export function error(status, message) {
return new HttpError(status, message);
}

/**
* Creates a `Redirect` object. If thrown during request handling, SvelteKit will
* return a redirect response.
* @param {300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308} status
* @param {string} location
*/
export function redirect(status, location) {
if (isNaN(status) || status < 300 || status > 308) {
throw new Error('Invalid status code');
}

return new Redirect(status, location);
}

/**
* Generates a JSON `Response` object from the supplied data.
* @param {any} data
* @param {ResponseInit} [init]
*/
export function json(data, init) {
// TODO deprecate this in favour of `Response.json` when it's
// more widely supported
const headers = new Headers(init?.headers);
if (!headers.has('content-type')) {
headers.set('content-type', 'application/json');
}

return new Response(JSON.stringify(data), {
...init,
headers
});
}

/**
* Generates a `ValidationError` object.
* @param {number} status
* @param {Record<string, any> | undefined} [data]
*/
export function invalid(status, data) {
return new ValidationError(status, data);
}
12 changes: 8 additions & 4 deletions packages/kit/test/apps/basics/test/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -1295,28 +1295,32 @@ test.describe('Redirects', () => {

await clicknav('[href="/redirect/missing-status/a"]');

const message = process.env.DEV || !javaScriptEnabled ? 'Invalid status code' : 'Redirect loop';

expect(page.url()).toBe(`${baseURL}/redirect/missing-status/a`);
expect(await page.textContent('h1')).toBe('500');
expect(await page.textContent('#message')).toBe(
'This is your custom error page saying: "Invalid status code"'
`This is your custom error page saying: "${message}"`
);

if (!javaScriptEnabled) {
// handleError is not invoked for client-side navigation
const lines = read_errors('/redirect/missing-status/a').stack.split('\n');
expect(lines[0]).toBe('Error: Invalid status code');
expect(lines[0]).toBe(`Error: ${message}`);
}
});

test('errors on invalid status', async ({ baseURL, page, clicknav }) => {
test('errors on invalid status', async ({ baseURL, page, clicknav, javaScriptEnabled }) => {
await page.goto('/redirect');

await clicknav('[href="/redirect/missing-status/b"]');

const message = process.env.DEV || !javaScriptEnabled ? 'Invalid status code' : 'Redirect loop';

expect(page.url()).toBe(`${baseURL}/redirect/missing-status/b`);
expect(await page.textContent('h1')).toBe('500');
expect(await page.textContent('#message')).toBe(
'This is your custom error page saying: "Invalid status code"'
`This is your custom error page saying: "${message}"`
);
});

Expand Down