|
import { createReadStream } from 'node:fs'; |
|
import { Readable } from 'node:stream'; |
|
import * as set_cookie_parser from 'set-cookie-parser'; |
|
import { SvelteKitError } from '../../runtime/control.js'; |
|
|
|
|
|
|
|
|
|
|
|
function get_raw_body(req, body_size_limit) { |
|
const h = req.headers; |
|
|
|
if (!h['content-type']) { |
|
return null; |
|
} |
|
|
|
const content_length = Number(h['content-length']); |
|
|
|
|
|
if ( |
|
(req.httpVersionMajor === 1 && isNaN(content_length) && h['transfer-encoding'] == null) || |
|
content_length === 0 |
|
) { |
|
return null; |
|
} |
|
|
|
if (req.destroyed) { |
|
const readable = new ReadableStream(); |
|
readable.cancel(); |
|
return readable; |
|
} |
|
|
|
let size = 0; |
|
let cancelled = false; |
|
|
|
return new ReadableStream({ |
|
start(controller) { |
|
if (body_size_limit !== undefined && content_length > body_size_limit) { |
|
let message = `Content-length of ${content_length} exceeds limit of ${body_size_limit} bytes.`; |
|
|
|
if (body_size_limit === 0) { |
|
|
|
|
|
message += ' To disable body size limits, specify Infinity rather than 0.'; |
|
} |
|
|
|
const error = new SvelteKitError(413, 'Payload Too Large', message); |
|
|
|
controller.error(error); |
|
return; |
|
} |
|
|
|
req.on('error', (error) => { |
|
cancelled = true; |
|
controller.error(error); |
|
}); |
|
|
|
req.on('end', () => { |
|
if (cancelled) return; |
|
controller.close(); |
|
}); |
|
|
|
req.on('data', (chunk) => { |
|
if (cancelled) return; |
|
|
|
size += chunk.length; |
|
if (size > content_length) { |
|
cancelled = true; |
|
|
|
const constraint = content_length ? 'content-length' : 'BODY_SIZE_LIMIT'; |
|
const message = `request body size exceeded ${constraint} of ${content_length}`; |
|
|
|
const error = new SvelteKitError(413, 'Payload Too Large', message); |
|
controller.error(error); |
|
|
|
return; |
|
} |
|
|
|
controller.enqueue(chunk); |
|
|
|
if (controller.desiredSize === null || controller.desiredSize <= 0) { |
|
req.pause(); |
|
} |
|
}); |
|
}, |
|
|
|
pull() { |
|
req.resume(); |
|
}, |
|
|
|
cancel(reason) { |
|
cancelled = true; |
|
req.destroy(reason); |
|
} |
|
}); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export async function getRequest({ request, base, bodySizeLimit }) { |
|
return new Request(base + request.url, { |
|
|
|
duplex: 'half', |
|
method: request.method, |
|
headers: (request.headers), |
|
body: |
|
request.method === 'GET' || request.method === 'HEAD' |
|
? undefined |
|
: get_raw_body(request, bodySizeLimit) |
|
}); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export async function setResponse(res, response) { |
|
for (const [key, value] of response.headers) { |
|
try { |
|
res.setHeader( |
|
key, |
|
key === 'set-cookie' |
|
? set_cookie_parser.splitCookiesString( |
|
|
|
(response.headers.get(key)) |
|
) |
|
: value |
|
); |
|
} catch (error) { |
|
res.getHeaderNames().forEach((name) => res.removeHeader(name)); |
|
res.writeHead(500).end(String(error)); |
|
return; |
|
} |
|
} |
|
|
|
res.writeHead(response.status); |
|
|
|
if (!response.body) { |
|
res.end(); |
|
return; |
|
} |
|
|
|
if (response.body.locked) { |
|
res.end( |
|
'Fatal error: Response body is locked. ' + |
|
"This can happen when the response was already read (for example through 'response.json()' or 'response.text()')." |
|
); |
|
return; |
|
} |
|
|
|
const reader = response.body.getReader(); |
|
|
|
if (res.destroyed) { |
|
reader.cancel(); |
|
return; |
|
} |
|
|
|
const cancel = ( error) => { |
|
res.off('close', cancel); |
|
res.off('error', cancel); |
|
|
|
|
|
|
|
reader.cancel(error).catch(() => {}); |
|
if (error) res.destroy(error); |
|
}; |
|
|
|
res.on('close', cancel); |
|
res.on('error', cancel); |
|
|
|
next(); |
|
async function next() { |
|
try { |
|
for (;;) { |
|
const { done, value } = await reader.read(); |
|
|
|
if (done) break; |
|
|
|
if (!res.write(value)) { |
|
res.once('drain', next); |
|
return; |
|
} |
|
} |
|
res.end(); |
|
} catch (error) { |
|
cancel(error instanceof Error ? error : new Error(String(error))); |
|
} |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export function createReadableStream(file) { |
|
return (Readable.toWeb(createReadStream(file))); |
|
} |
|
|