Skip to content

Commit 7fe76bf

Browse files
lukeedbenmccann
authored andcommitted
fix: improve getRawBody parsing & handle error(s)
1 parent 94c1074 commit 7fe76bf

8 files changed

Lines changed: 75 additions & 32 deletions

File tree

.changeset/odd-ligers-swim.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
'@sveltejs/adapter-node': patch
3+
'@sveltejs/adapter-vercel': patch
4+
'@sveltejs/kit': patch
5+
---
6+
7+
ensure `content-length` limit respected; handle `getRawBody` error(s)

packages/adapter-node/src/server.js

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,12 +40,22 @@ export function createServer({ render }) {
4040
prerendered_handler,
4141
async (req, res) => {
4242
const parsed = new URL(req.url || '', 'http://localhost');
43+
44+
let body;
45+
46+
try {
47+
body = await getRawBody(req);
48+
} catch (err) {
49+
res.statusCode = err.status || 400;
50+
return res.end(err.reason || 'Invalid request body');
51+
}
52+
4353
const rendered = await render({
4454
method: req.method,
4555
headers: req.headers, // TODO: what about repeated headers, i.e. string[]
4656
path: parsed.pathname,
47-
rawBody: await getRawBody(req),
48-
query: parsed.searchParams
57+
query: parsed.searchParams,
58+
rawBody: body
4959
});
5060

5161
if (rendered) {

packages/adapter-vercel/files/entry.js

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,21 @@ import { render } from '../output/server/app.js'; // eslint-disable-line import/
77
export default async (req, res) => {
88
const { pathname, searchParams } = new URL(req.url || '', 'http://localhost');
99

10+
let body;
11+
12+
try {
13+
body = await getRawBody(req);
14+
} catch (err) {
15+
res.statusCode = err.status || 400;
16+
return res.end(err.reason || 'Invalid request body');
17+
}
18+
1019
const rendered = await render({
1120
method: req.method,
1221
headers: req.headers,
1322
path: pathname,
1423
query: searchParams,
15-
rawBody: await getRawBody(req)
24+
rawBody: body
1625
});
1726

1827
if (rendered) {

packages/kit/src/core/dev/index.js

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,14 @@ class Watcher extends EventEmitter {
153153
const root = (await this.vite.ssrLoadModule(`/${this.dir}/generated/root.svelte`))
154154
.default;
155155

156-
const rawBody = await getRawBody(req);
156+
let body;
157+
158+
try {
159+
body = await getRawBody(req);
160+
} catch (err) {
161+
res.statusCode = err.status || 400;
162+
return res.end(err.reason || 'Invalid request body');
163+
}
157164

158165
const host = /** @type {string} */ (this.config.kit.host ||
159166
req.headers[this.config.kit.hostHeader || 'host']);
@@ -165,7 +172,7 @@ class Watcher extends EventEmitter {
165172
host,
166173
path: parsed.pathname,
167174
query: new URLSearchParams(parsed.query),
168-
rawBody
175+
rawBody: body
169176
},
170177
{
171178
amp: this.config.kit.amp,
Lines changed: 24 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,45 @@
11
/**
22
* @param {import('http').IncomingMessage} req
3-
* @returns {Promise<string | Uint8Array>}
3+
* @returns {Promise<Uint8Array | string | null>}
44
*/
55
export function getRawBody(req) {
66
return new Promise((fulfil, reject) => {
77
const h = req.headers;
88

99
if (!h['content-type']) {
10-
fulfil(null);
11-
return;
10+
return fulfil(null);
1211
}
1312

1413
req.on('error', reject);
1514

1615
const length = Number(h['content-length']);
1716

18-
/** @type {Uint8Array} */
19-
let data;
20-
21-
if (!isNaN(length)) {
22-
data = new Uint8Array(length);
17+
// https://github.com/jshttp/type-is/blob/c1f4388c71c8a01f79934e68f630ca4a15fffcd6/index.js#L81-L95
18+
if (isNaN(length) && h['transfer-encoding'] == null) {
19+
return fulfil(null);
20+
}
2321

24-
let i = 0;
22+
let data = new Uint8Array(length || 0);
2523

24+
if (length > 0) {
25+
let offset = 0;
2626
req.on('data', (chunk) => {
27-
data.set(chunk, i);
28-
i += chunk.length;
29-
});
30-
} else {
31-
// https://github.com/jshttp/type-is/blob/c1f4388c71c8a01f79934e68f630ca4a15fffcd6/index.js#L81-L95
32-
if (h['transfer-encoding'] === undefined) {
33-
fulfil(null);
34-
return;
35-
}
27+
const new_len = offset + Buffer.byteLength(chunk);
3628

37-
data = new Uint8Array(0);
29+
if (new_len > length) {
30+
return reject({
31+
status: 413,
32+
reason: 'Exceeded "Content-Length" limit'
33+
});
34+
}
3835

36+
data.set(chunk, offset);
37+
offset = new_len;
38+
});
39+
} else {
3940
req.on('data', (chunk) => {
4041
const new_data = new Uint8Array(data.length + chunk.length);
41-
new_data.set(data);
42+
new_data.set(data, 0);
4243
new_data.set(chunk, data.length);
4344
data = new_data;
4445
});
@@ -48,11 +49,11 @@ export function getRawBody(req) {
4849
const [type] = h['content-type'].split(/;\s*/);
4950

5051
if (type === 'application/octet-stream') {
51-
fulfil(data);
52+
return fulfil(data);
5253
}
5354

54-
const decoder = new TextDecoder(h['content-encoding'] || 'utf-8');
55-
fulfil(decoder.decode(data));
55+
const encoding = h['content-encoding'] || 'utf-8';
56+
fulfil(new TextDecoder(encoding).decode(data));
5657
});
5758
});
5859
}

packages/kit/src/core/start/index.js

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,14 +53,23 @@ export async function start({ port, host, config, https: use_https = false, cwd
5353

5454
assets_handler(req, res, () => {
5555
static_handler(req, res, async () => {
56+
let body;
57+
58+
try {
59+
body = await getRawBody(req);
60+
} catch (err) {
61+
res.statusCode = err.status || 400;
62+
return res.end(err.reason || 'Invalid request body');
63+
}
64+
5665
const rendered = await app.render({
5766
host: /** @type {string} */ (config.kit.host ||
5867
req.headers[config.kit.hostHeader || 'host']),
5968
method: req.method,
6069
headers: /** @type {import('types/helper').Headers} */ (req.headers),
6170
path: decodeURIComponent(parsed.pathname),
62-
rawBody: await getRawBody(req),
63-
query: new URLSearchParams(parsed.query || '')
71+
query: new URLSearchParams(parsed.query || ''),
72+
rawBody: body
6473
});
6574

6675
if (rendered) {

packages/kit/types/endpoint.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ export type ServerRequest<Locals = Record<string, any>, Body = unknown> = {
77
path: string;
88
params: Record<string, string>;
99
query: URLSearchParams;
10-
rawBody: string | Uint8Array;
10+
rawBody: string | Uint8Array | null;
1111
body: ParameterizedBody<Body>;
1212
locals: Locals;
1313
};

packages/kit/types/hooks.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ export type Incoming = {
77
headers: Headers;
88
path: string;
99
query: URLSearchParams;
10-
rawBody: string | Uint8Array;
10+
rawBody: string | Uint8Array | null;
1111
body?: BaseBody;
1212
};
1313

0 commit comments

Comments
 (0)