UNPKG

@worker-tools/middleware

Version:

A suite of standalone HTTP server middlewares for Worker Runtimes.

139 lines 5.39 kB
import { combine } from "./context.js"; import { accepts } from './content-negotiation.js'; import { payloadTooLarge } from '@worker-tools/response-creators'; export const JSON = 'application/json'; export const FORM = 'application/x-www-form-urlencoded'; export const FORM_DATA = 'multipart/form-data'; export const TEXT_HTML = 'text/html'; export const TEXT_PLAIN = 'text/plain'; /** Standard MIME type for binary data */ export const BINARY = 'application/octet-stream'; /** Non-standard MIME type for binary data. Sometimes used, so included anyway. */ export const X_BINARY = 'application/x-binary'; const defaultBody = [ FORM, FORM_DATA, JSON, BINARY, X_BINARY, TEXT_HTML, TEXT_PLAIN, ]; // Not possible to provide a fallback rn: https://github.com/microsoft/TypeScript/issues/48073 const _isString = (x) => !(x[1] instanceof File); const _isFile = (x) => x[1] instanceof File; const isBodyTextContext = (nx) => { var _a; return (_a = nx.accepted) === null || _a === void 0 ? void 0 : _a.startsWith('text/'); }; const isBodyVendorJSONContext = (nx) => { var _a; return ((_a = nx.accepted) === null || _a === void 0 ? void 0 : _a.startsWith('application/')) && nx.accepted.endsWith('+json'); }; // const isBodyVendorBinaryContext = <J = any>(nx: BodyContext<J>): nx is BodyVendorBinaryContext => // nx.accepted?.startsWith('application/vnd.') const MB = 1024 ** 2; async function checkSize(req, maxSize) { let size = 0; await req.clone().body.pipeTo(new WritableStream({ write(chunk, ctrl) { size += chunk.byteLength; if (size > maxSize) { ctrl.error(new Error('Payload too large')); } } })); return size <= maxSize; } export const bodyParser = (opts = {}) => async (ax) => { var _a; const x = await ax; const nx = x; const ok = await checkSize(x.request, (_a = opts.maxSize) !== null && _a !== void 0 ? _a : 1 * MB); if (!ok) throw payloadTooLarge(); switch (nx.accepted) { case JSON: { nx.body = nx.json = await x.request.json(); return nx; } case FORM: { nx.body = nx.form = new URLSearchParams(await x.request.text()); // FIXME: Multiple values per key?? // nx.form = Object.fromEntries(form); return nx; } case FORM_DATA: { nx.body = nx.formData = await x.request.formData(); // FIXME: Multiple values per key?? // const tuples = [...formData]; // nx.form = Object.fromEntries(tuples.filter(isString)); // nx.files = Object.fromEntries(tuples.filter(isFile)); return nx; } case BINARY: case X_BINARY: { nx.body = nx.arrayBuffer = await x.request.arrayBuffer(); nx.blob = new Blob([nx.arrayBuffer]); // TODO: does this copy?? return nx; } default: { if (isBodyTextContext(nx)) { nx.body = nx.text = await x.request.text(); } else if (isBodyVendorJSONContext(nx)) { nx.body = nx.json = await x.request.json(); return nx; // } else if (isBodyVendorBinaryContext(nx)) { // nx.body = nx.buffer = await x.request.arrayBuffer(); // nx.blob = new Blob([nx.buffer]) // TODO: does this copy?? // return nx; } else { // Anything else gets the binary treatment (outside of scope of type system) nx.body = nx.buffer = await x.request.arrayBuffer(); nx.blob = new Blob([nx.buffer]); return nx; } return nx; } } }; export const defaultBodyParser = (options) => combine(accepts(defaultBody), bodyParser(options)); // type ErrorOf<T> = T extends { error?: infer E } ? E : never // (async () => { // const ctx: Context = { request: new Request('/'), effects: [], waitUntil: (_f: any) => {}, handled: Promise.resolve(null as any) } // const z = provides([])(accepts([])(ctx)) // const x = await parseBody()(accepts(['text/x-foo', 'application/vnd.github.v3+json', FORM, FORM_DATA])(ctx)) // if (x.accepted === 'application/vnd.github.v3+json') { // x.body // } else if (x.accepted === 'text/x-foo') { // x.body // } else if (x.accepted === 'application/x-www-form-urlencoded') { // x.body // } // const y = await bodyParser()(ctx) // if (y.accepted === 'application/x-www-form-urlencoded') { // y.bodyParams // y.body // } // if (y.accepted === 'multipart/form-data') { // y.formData // y.body // } // if (y.accepted === 'application/foobar+json') { // y.json // y.body // } // // if (x.accepted === 'application/x-www-form-urlencoded') { // // x.body // // x.bodyParams // // x.form // // } // // else if (x.accepted === 'multipart/form-data') { // // x.formData // // x.form // // x.files // // } else if (x.accepted === 'application/octet-stream' || x.accepted === 'application/x-binary') { // // x.buffer // // x.blob // // } else if (x.accepted === 'application/vnd.github.v3+json') { // // } else if (x.accepted === 'text/foo') { // // x.text // // } // }) //# sourceMappingURL=body-parser.js.map