Skip to content

Commit

Permalink
fix(server): handle large POST bodies in node-http (#5724)
Browse files Browse the repository at this point in the history
* fix(server): handle large POST bodies in node-http

* Update packages/server/src/adapters/node-http/incomingMessageToRequest.ts
  • Loading branch information
jcarrus committed May 16, 2024
1 parent 3dc8f3b commit db2ec5c
Show file tree
Hide file tree
Showing 2 changed files with 56 additions and 35 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -71,19 +71,40 @@ test('basic POST', async () => {
test('POST with body', async () => {
const server = createServer({ maxBodySize: null });

await server.fetch(
{
method: 'POST',
body: JSON.stringify({ hello: 'world' }),
headers: {
'content-type': 'application/json',
{
// handles small text

await server.fetch(
{
method: 'POST',
body: JSON.stringify({ hello: 'world' }),
headers: {
'content-type': 'application/json',
},
},
},
async (request) => {
expect(request.method).toBe('POST');
expect(await request.json()).toEqual({ hello: 'world' });
},
);
async (request) => {
expect(request.method).toBe('POST');
expect(await request.json()).toEqual({ hello: 'world' });
},
);
}
{
// handles a body that is long enough to come in multiple chunks

const body = '0'.repeat(2 ** 17);
const bodyLength = body.length;

await server.fetch(
{
method: 'POST',
body,
},
async (request) => {
expect(request.method).toBe('POST');
expect((await request.text()).length).toBe(bodyLength);
},
);
}

await server.close();
});
Expand Down
46 changes: 23 additions & 23 deletions packages/server/src/adapters/node-http/incomingMessageToRequest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,32 +17,32 @@ function incomingMessageToBodyStream(
type Value = Buffer | Uint8Array | string | null;
let size = 0;
const maxBodySize = opts.maxBodySize;
let hasClosed = false;

let controller: ReadableStreamDefaultController<Value> =
null as unknown as ReadableStreamDefaultController<Value>;
const stream = new ReadableStream<Value>({
start(c) {
controller = c;
},
async pull(c) {
const chunk: Value = req.read();

if (chunk) {
start(controller) {
req.on('data', (chunk) => {
size += chunk.length;
}
if (maxBodySize !== null && size > maxBodySize) {
controller.error(
new TRPCError({
code: 'PAYLOAD_TOO_LARGE',
}),
);
return;
}
if (chunk === null) {
c.close();
return;
}
controller.enqueue(chunk);
if (maxBodySize != null && size > maxBodySize) {
controller.error(
new TRPCError({
code: 'PAYLOAD_TOO_LARGE',
}),
);
// an error is thrown if we try to close the controller after
// erroring, so track the closure
hasClosed = true;
return;
}
controller.enqueue(chunk);
});
req.once('end', () => {
if (hasClosed) {
return;
}
hasClosed = true;
controller.close();
});
},
cancel() {
req.destroy();
Expand Down

0 comments on commit db2ec5c

Please sign in to comment.