From 5a61719e0e90c70a9e7387c9025db27fe1b74790 Mon Sep 17 00:00:00 2001 From: pratham-asrani-bachatt Date: Thu, 29 Oct 2026 14:46:51 +0530 Subject: [PATCH] connection reset by peer fixed to payload too large request --- server/server.js | 24 +++++++++++++++++++++--- tests/api.test.mjs | 4 +++- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/server/server.js b/server/server.js index ebb5984..00e1d59 100644 --- a/server/server.js +++ b/server/server.js @@ -27,7 +27,7 @@ const CONFIG = { backupKeep: Math.max(0, parseInt(env.BACKUP_KEEP || '14', 10)), backupEveryHours: Math.max(1, parseInt(env.BACKUP_EVERY_HOURS || '24', 10)), historyKeep: Math.max(50, parseInt(env.HISTORY_KEEP || '5000', 10)), - maxImportBytes: parseInt(env.MAX_IMPORT_MB || '20', 10) * 1024 * 1024, + maxImportBytes: Math.max(1, parseInt(env.MAX_IMPORT_MB, 10) || 20) * 1024 * 1024, maxDocBytes: 512 * 1024, // AGPL-3.0 section 13: users of a modified version must be offered its source. Point this at your fork. sourceUrl: /^https?:\/\//.test(env.SOURCE_URL || '') ? env.SOURCE_URL : 'https://github.com/SolidGeeks/strata', @@ -296,8 +296,21 @@ function send(res, status, body, headers = {}) { const fail = (res, status, error, extra) => send(res, status, { error, ...(extra || {}) }); function readBody(req, limit) { return new Promise((resolve, reject) => { + const tooLarge = () => { req.resume(); reject(httpError(413, 'Payload too large'));}; + if(Number(req.headers['content-length'])>limit) { + return tooLarge(); + } let size = 0; const chunks = []; - req.on('data', c => { size += c.length; if (size > limit) { reject(httpError(413, 'Payload too large')); req.destroy(); return; } chunks.push(c); }); + const onData = c => { + size += c.length; + if(size <= limit) { + return chunks.push(c); + } + req.off('data', onData); + chunks.length = 0; + tooLarge(); + }; + req.on('data', onData); req.on('end', () => { try { resolve(chunks.length ? JSON.parse(Buffer.concat(chunks).toString('utf8')) : null); } catch { reject(httpError(400, 'Invalid JSON')); } }); req.on('error', reject); }); @@ -571,7 +584,12 @@ const server = http.createServer(async (req, res) => { else if (req.method === 'GET' || req.method === 'HEAD') await serveStatic(req, res, url.pathname); else fail(res, 405, 'Method not allowed'); } catch (err) { - if (!res.headersSent) fail(res, err.status || 500, err.status ? err.message : 'Internal error', err.extra); + if (!res.headersSent) { + if(err.status == 413) { + res.setHeader('Connection', 'close'); + } + fail(res, err.status || 500, err.message || 'Internal error', err.extra); + } if (!err.status) log('ERROR', req.method, req.url, err.stack || err.message); } }); diff --git a/tests/api.test.mjs b/tests/api.test.mjs index 8c06842..d7d8678 100644 --- a/tests/api.test.mjs +++ b/tests/api.test.mjs @@ -176,7 +176,9 @@ test('import: replaces the map, validates ids, and enforces MAX_IMPORT_MB', asyn const bob = as('bob'); assert.equal((await bob.post('/api/import', { si: { 'bad id!': {} }, assets: {} })).status, 400); const big = { si: { x: { name: 'x'.repeat(1.2 * 1024 * 1024) } }, assets: {} }; - const tooBig = await bob.post('/api/import', big).catch(e => ({ status: 'connection closed', error: e })); + const tooBig = await bob.post('/api/import', big); + assert.equal(tooBig.status, 413); + assert.deepEqual(tooBig.data, { error: 'Payload too large' }); assert.ok(tooBig.status === 413 || tooBig.status === 'connection closed', `got ${tooBig.status}`); const before = await rev(); const r = await bob.post('/api/import', { si: { lille: { name: 'Lille' } }, assets: {} });