Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[fix] handle undefined body on endpoint output #1808

Merged
merged 12 commits into from
Jul 10, 2021
5 changes: 5 additions & 0 deletions .changeset/fluffy-cheetahs-remain.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@sveltejs/kit': patch
---

handle undefined body on endpoint output
15 changes: 7 additions & 8 deletions packages/kit/src/runtime/server/endpoint.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,11 @@ export default async function render_route(request, route) {
const params = route.params(match);

const response = await handler({ ...request, params });
const preface = `Invalid response from route ${request.path}`;

if (response) {
if (typeof response !== 'object') {
return error(
`Invalid response from route ${request.path}: expected an object, got ${typeof response}`
);
return error(`${preface}: expected an object, got ${typeof response}`);
}

let { status = 200, body, headers = {} } = response;
Expand All @@ -41,25 +40,25 @@ export default async function render_route(request, route) {
// validation
if (type === 'application/octet-stream' && !(body instanceof Uint8Array)) {
return error(
`Invalid response from route ${request.path}: body must be an instance of Uint8Array if content type is application/octet-stream`
`${preface}: body must be an instance of Uint8Array if content type is application/octet-stream`
);
}

if (body instanceof Uint8Array && type !== 'application/octet-stream') {
return error(
`Invalid response from route ${request.path}: Uint8Array body must be accompanied by content-type: application/octet-stream header`
`${preface}: Uint8Array body must be accompanied by content-type: application/octet-stream header`
);
}

/** @type {import('types/hooks').StrictBody} */
let normalized_body;

if (
typeof body === 'object' &&
(!type || type === 'application/json' || type === 'application/json; charset=utf-8')
(typeof body === 'object' || typeof body === 'undefined') &&
(!type || type.startsWith('application/json'))
) {
headers = { ...headers, 'content-type': 'application/json; charset=utf-8' };
normalized_body = JSON.stringify(body);
normalized_body = JSON.stringify(body || {});
} else {
normalized_body = /** @type {import('types/hooks').StrictBody} */ (body);
}
Expand Down
39 changes: 39 additions & 0 deletions packages/kit/test/apps/basics/src/routes/endpoint-output/_tests.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import * as assert from 'uvu/assert';

/** @type {import('test').TestMaker} */
export default function (test) {
test('not ok on void endpoint', null, async ({ fetch }) => {
const res = await fetch('/endpoint-output/empty', { method: 'DELETE' });
assert.equal(res.ok, false);
});
test('200 status on empty endpoint', null, async ({ fetch }) => {
const res = await fetch('/endpoint-output/empty');
assert.equal(res.status, 200);
assert.equal(await res.json(), {});
ignatiusmb marked this conversation as resolved.
Show resolved Hide resolved
});

test('set-cookie without body', null, async ({ fetch }) => {
const res = await fetch('/endpoint-output/headers');
assert.equal(res.status, 200);
assert.equal(res.headers.has('set-cookie'), true);
});

test('200 status by default', null, async ({ fetch }) => {
const res = await fetch('/endpoint-output/body');
assert.equal(res.status, 200);
assert.equal(await res.text(), '{}');
});

test('does not throw on blob method', null, async ({ fetch }) => {
const res = await fetch('/endpoint-output/empty');
assert.type(await res.blob(), 'object');
});
test('does not throw on arrayBuffer method', null, async ({ fetch }) => {
const res = await fetch('/endpoint-output/empty');
assert.type(await res.arrayBuffer(), 'object');
});
test('does not throw on buffer method', null, async ({ fetch }) => {
const res = await fetch('/endpoint-output/empty');
assert.type(await res.buffer(), 'object');
});
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/** @type {import('@sveltejs/kit').RequestHandler} */
export function get() {
return { body: {} };
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/** @type {import('@sveltejs/kit').RequestHandler} */
export function get() {
return {};
}

/** @type {import('@sveltejs/kit').RequestHandler} */
export function del() {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/** @type {import('@sveltejs/kit').RequestHandler} */
export function get() {
return {
headers: {
'Set-Cookie': 'foo=bar'
}
};
}