OverviewFilesCommitsIssuesDocsReleasesPeers

ap-bridge / lib / admin-serve.js

38 lines · javascript ref:
// The transport half of the admin surface — kept apart from admin.js so that file stays pure.
//
// ⛔ SMALL ON PURPOSE. An operator page posts two fields; pulling in a form parser (or reusing the
// multipart one favela-surface needs for pictures) would be more moving parts than the thing it serves.
// Bodies are bounded because a listener that buffers whatever it is handed is a memory bug with a form
// in front of it, even on loopback.

const MAX_BODY = 64 * 1024

export function parseForm (req) {
  return new Promise((resolve) => {
    if (req.method !== 'POST') return resolve({})
    let size = 0
    const chunks = []
    req.on('data', (c) => {
      size += c.length
      // ⚠ Stop COLLECTING but keep draining: destroying the stream mid-body makes the browser show a
      // transport error instead of the page, for a form that was merely too big.
      if (size <= MAX_BODY) chunks.push(c)
    })
    req.on('end', () => {
      const body = Buffer.concat(chunks).toString('utf8')
      const out = {}
      for (const pair of body.split('&')) {
        if (!pair) continue
        const i = pair.indexOf('=')
        const k = i === -1 ? pair : pair.slice(0, i)
        const v = i === -1 ? '' : pair.slice(i + 1)
        try { out[decodeURIComponent(k.replace(/\+/g, ' '))] = decodeURIComponent(v.replace(/\+/g, ' ')) } catch { /* skip a malformed pair */ }
      }
      resolve(out)
    })
    req.on('error', () => resolve({}))
  })
}

export default { parseForm }