@reckona/mreact-router 0.0.205 → 0.0.206

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"multipart.d.ts","sourceRoot":"","sources":["../src/multipart.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,WAAW,2BAA2B;IAC1C,MAAM,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,2BAA2B,CAAC,CAAC,CAAC;IAC/D,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,2BAA2B;IAC1C,IAAI,CAAC,EAAE,QAAQ,GAAG,MAAM,CAAC;IACzB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,EAAE,cAAc,CAAC,UAAU,CAAC,eAAe,CAAC,CAAC,CAAC;IAClD,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;IACxB,WAAW,IAAI,OAAO,CAAC,WAAW,CAAC,CAAC;IACpC,iBAAiB,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,0BAA0B,CAAC;CACzE;AAED;;GAEG;AACH,MAAM,WAAW,0BAA0B;IACzC,QAAQ,EAAE,cAAc,CAAC,UAAU,CAAC,eAAe,CAAC,CAAC,CAAC;IACtD,IAAI,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;CACrB;AAYD,eAAO,MAAM,wBAAwB,QAAmB,CAAC;AACzD,eAAO,MAAM,wBAAwB,OAAQ,CAAC;AAE9C;;GAEG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,OAAO,EAChB,OAAO,GAAE,2BAAgC,GACxC,aAAa,CAAC,mBAAmB,CAAC,CAQpC;AAsSD,wBAAgB,yBAAyB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CASlE"}
1
+ {"version":3,"file":"multipart.d.ts","sourceRoot":"","sources":["../src/multipart.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,WAAW,2BAA2B;IAC1C,MAAM,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,2BAA2B,CAAC,CAAC,CAAC;IAC/D,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,2BAA2B;IAC1C,IAAI,CAAC,EAAE,QAAQ,GAAG,MAAM,CAAC;IACzB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,EAAE,cAAc,CAAC,UAAU,CAAC,eAAe,CAAC,CAAC,CAAC;IAClD,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;IACxB,WAAW,IAAI,OAAO,CAAC,WAAW,CAAC,CAAC;IACpC,iBAAiB,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,0BAA0B,CAAC;CACzE;AAED;;GAEG;AACH,MAAM,WAAW,0BAA0B;IACzC,QAAQ,EAAE,cAAc,CAAC,UAAU,CAAC,eAAe,CAAC,CAAC,CAAC;IACtD,IAAI,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;CACrB;AAaD,eAAO,MAAM,wBAAwB,QAAmB,CAAC;AACzD,eAAO,MAAM,wBAAwB,OAAQ,CAAC;AAE9C;;GAEG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,OAAO,EAChB,OAAO,GAAE,2BAAgC,GACxC,aAAa,CAAC,mBAAmB,CAAC,CAQpC;AA+TD,wBAAgB,yBAAyB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CASlE"}
package/dist/multipart.js CHANGED
@@ -1,6 +1,7 @@
1
1
  const crlf = "\r\n";
2
2
  const headerSeparator = "\r\n\r\n";
3
3
  const maxHeaderBytes = 64 * 1024;
4
+ const maxMultipartBoundaryCharacters = 70;
4
5
  export const defaultMultipartMaxBytes = 10 * 1024 * 1024;
5
6
  export const defaultMultipartMaxParts = 1_000;
6
7
  /**
@@ -35,6 +36,8 @@ async function parseMultipartRequest(request, options, queue) {
35
36
  let totalBytes = 0;
36
37
  let totalParts = 0;
37
38
  let started = false;
39
+ let invalidOpeningBoundarySeen = false;
40
+ let firstBoundarySearchOffset = 0;
38
41
  let currentPart;
39
42
  queue.setCancel(() => {
40
43
  void currentPart?.writer.abort(new Error("Multipart stream iteration was cancelled."));
@@ -50,26 +53,39 @@ async function parseMultipartRequest(request, options, queue) {
50
53
  }
51
54
  while (true) {
52
55
  if (!started) {
53
- const firstBoundaryIndex = indexOfBytes(buffer, firstBoundary);
56
+ const firstBoundaryIndex = indexOfBytes(buffer, firstBoundary, firstBoundarySearchOffset);
54
57
  if (firstBoundaryIndex < 0) {
55
58
  if (next.done) {
59
+ if (invalidOpeningBoundarySeen) {
60
+ throw new Error("Malformed multipart opening boundary.");
61
+ }
56
62
  throw new Error("Multipart request body does not contain the opening boundary.");
57
63
  }
58
64
  trimBufferToTail(0);
65
+ firstBoundarySearchOffset = 0;
59
66
  break;
60
67
  }
61
- buffer = buffer.slice(firstBoundaryIndex + firstBoundary.length);
62
- if (startsWithBytes(buffer, closeSuffix)) {
63
- queue.close();
64
- return;
65
- }
66
- if (!startsWithBytes(buffer, lineSuffix)) {
68
+ const firstBoundaryEnd = firstBoundaryIndex + firstBoundary.length;
69
+ if (buffer.length < firstBoundaryEnd + closeSuffix.length) {
67
70
  if (next.done) {
68
71
  throw new Error("Malformed multipart opening boundary.");
69
72
  }
73
+ if (firstBoundaryIndex > 0) {
74
+ buffer = buffer.slice(firstBoundaryIndex);
75
+ }
76
+ firstBoundarySearchOffset = 0;
70
77
  break;
71
78
  }
72
- buffer = buffer.slice(lineSuffix.length);
79
+ if (startsWithBytes(buffer, closeSuffix, firstBoundaryEnd)) {
80
+ queue.close();
81
+ return;
82
+ }
83
+ if (!startsWithBytes(buffer, lineSuffix, firstBoundaryEnd)) {
84
+ invalidOpeningBoundarySeen = true;
85
+ firstBoundarySearchOffset = firstBoundaryIndex + 1;
86
+ continue;
87
+ }
88
+ buffer = buffer.slice(firstBoundaryEnd + lineSuffix.length);
73
89
  started = true;
74
90
  }
75
91
  if (currentPart === undefined) {
@@ -102,21 +118,25 @@ async function parseMultipartRequest(request, options, queue) {
102
118
  continue;
103
119
  }
104
120
  await writePartChunk(currentPart, buffer.slice(0, delimiterIndex));
121
+ buffer = buffer.slice(delimiterIndex);
122
+ if (buffer.length < delimiter.length + closeSuffix.length) {
123
+ if (next.done) {
124
+ throw new Error("Malformed multipart boundary delimiter.");
125
+ }
126
+ break;
127
+ }
128
+ const closesMultipart = startsWithBytes(buffer, closeSuffix, delimiter.length);
129
+ if (!closesMultipart && !startsWithBytes(buffer, lineSuffix, delimiter.length)) {
130
+ throw new Error("Malformed multipart boundary delimiter.");
131
+ }
105
132
  await currentPart.writer.close();
106
- buffer = buffer.slice(delimiterIndex + delimiter.length);
107
133
  currentPart = undefined;
108
- if (startsWithBytes(buffer, closeSuffix)) {
134
+ buffer = buffer.slice(delimiter.length + closeSuffix.length);
135
+ if (closesMultipart) {
109
136
  queue.close();
110
137
  return;
111
138
  }
112
- if (startsWithBytes(buffer, lineSuffix)) {
113
- buffer = buffer.slice(lineSuffix.length);
114
- continue;
115
- }
116
- if (next.done) {
117
- throw new Error("Malformed multipart boundary delimiter.");
118
- }
119
- break;
139
+ continue;
120
140
  }
121
141
  if (next.done) {
122
142
  break;
@@ -255,7 +275,9 @@ function parseMultipartBoundary(contentType) {
255
275
  const unquoted = value.startsWith("\"") && value.endsWith("\"")
256
276
  ? value.slice(1, -1)
257
277
  : value;
258
- return unquoted === "" ? undefined : unquoted;
278
+ return unquoted === "" || unquoted.length > maxMultipartBoundaryCharacters
279
+ ? undefined
280
+ : unquoted;
259
281
  }
260
282
  return undefined;
261
283
  }
@@ -318,12 +340,12 @@ function concatBytes(left, right) {
318
340
  function indexOfAscii(bytes, needle) {
319
341
  return indexOfBytes(bytes, encodeAscii(needle));
320
342
  }
321
- function indexOfBytes(bytes, needle) {
322
- if (needle.length === 0 || bytes.length < needle.length) {
343
+ function indexOfBytes(bytes, needle, startOffset = 0) {
344
+ if (needle.length === 0 || bytes.length - startOffset < needle.length) {
323
345
  return -1;
324
346
  }
325
347
  const first = needle[0];
326
- for (let index = 0; index <= bytes.length - needle.length; index += 1) {
348
+ for (let index = startOffset; index <= bytes.length - needle.length; index += 1) {
327
349
  if (bytes[index] !== first) {
328
350
  continue;
329
351
  }
@@ -340,12 +362,12 @@ function indexOfBytes(bytes, needle) {
340
362
  }
341
363
  return -1;
342
364
  }
343
- function startsWithBytes(bytes, prefix) {
344
- if (bytes.length < prefix.length) {
365
+ function startsWithBytes(bytes, prefix, offset = 0) {
366
+ if (bytes.length - offset < prefix.length) {
345
367
  return false;
346
368
  }
347
369
  for (let index = 0; index < prefix.length; index += 1) {
348
- if (bytes[index] !== prefix[index]) {
370
+ if (bytes[offset + index] !== prefix[index]) {
349
371
  return false;
350
372
  }
351
373
  }
@@ -1 +1 @@
1
- {"version":3,"file":"multipart.js","sourceRoot":"","sources":["../src/multipart.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Configures multipart stream parsing limits and field handling.\n */\nexport interface MultipartStreamParseOptions {\n fields?: Readonly<Record<string, MultipartStreamFieldOptions>>;\n maxParts?: number;\n maxBytes?: number;\n}\n\n/**\n * Configures parsing behavior for a named multipart field.\n */\nexport interface MultipartStreamFieldOptions {\n type?: \"stream\" | \"text\";\n maxBytes?: number;\n}\n\n/**\n * Represents one parsed multipart field or file part.\n */\nexport interface MultipartStreamPart {\n name: string;\n filename?: string;\n safeFilename?: string;\n contentType?: string;\n headers: Headers;\n body: ReadableStream<Uint8Array<ArrayBufferLike>>;\n text(): Promise<string>;\n arrayBuffer(): Promise<ArrayBuffer>;\n fixedLengthStream(length?: number | bigint): MultipartFixedLengthStream;\n}\n\n/**\n * Couples a readable multipart part stream with a completion signal.\n */\nexport interface MultipartFixedLengthStream {\n readable: ReadableStream<Uint8Array<ArrayBufferLike>>;\n done: Promise<void>;\n}\n\ninterface PartWriter {\n name: string;\n maxBytes: number | undefined;\n bytes: number;\n writer: WritableStreamDefaultWriter<Uint8Array<ArrayBufferLike>>;\n}\n\nconst crlf = \"\\r\\n\";\nconst headerSeparator = \"\\r\\n\\r\\n\";\nconst maxHeaderBytes = 64 * 1024;\nexport const defaultMultipartMaxBytes = 10 * 1024 * 1024;\nexport const defaultMultipartMaxParts = 1_000;\n\n/**\n * Parses a multipart request body as an async iterable of streamable parts.\n */\nexport function parseMultipartStream(\n request: Request,\n options: MultipartStreamParseOptions = {},\n): AsyncIterable<MultipartStreamPart> {\n return {\n [Symbol.asyncIterator]() {\n const queue = new AsyncQueue<MultipartStreamPart>();\n void parseMultipartRequest(request, options, queue);\n return queue;\n },\n };\n}\n\nasync function parseMultipartRequest(\n request: Request,\n options: MultipartStreamParseOptions,\n queue: AsyncQueue<MultipartStreamPart>,\n): Promise<void> {\n const boundary = parseMultipartBoundary(request.headers.get(\"content-type\"));\n\n if (boundary === undefined) {\n queue.fail(new Error(\"Multipart request content-type must include a boundary.\"));\n return;\n }\n\n if (request.body === null) {\n queue.fail(new Error(\"Multipart request body is not readable.\"));\n return;\n }\n\n const reader = request.body.getReader();\n const delimiter = encodeAscii(`${crlf}--${boundary}`);\n const firstBoundary = encodeAscii(`--${boundary}`);\n const closeSuffix = encodeAscii(\"--\");\n const lineSuffix = encodeAscii(crlf);\n const keepTailBytes = delimiter.length + 4;\n let buffer: Uint8Array<ArrayBufferLike> = new Uint8Array();\n let totalBytes = 0;\n let totalParts = 0;\n let started = false;\n let currentPart: PartWriter | undefined;\n\n queue.setCancel(() => {\n void currentPart?.writer.abort(new Error(\"Multipart stream iteration was cancelled.\"));\n void reader.cancel();\n });\n\n try {\n while (true) {\n const next = await reader.read();\n\n if (!next.done) {\n totalBytes += next.value.byteLength;\n enforceByteLimit(\"multipart request\", totalBytes, options.maxBytes ?? defaultMultipartMaxBytes);\n buffer = concatBytes(buffer, next.value);\n }\n\n while (true) {\n if (!started) {\n const firstBoundaryIndex = indexOfBytes(buffer, firstBoundary);\n\n if (firstBoundaryIndex < 0) {\n if (next.done) {\n throw new Error(\"Multipart request body does not contain the opening boundary.\");\n }\n\n trimBufferToTail(0);\n break;\n }\n\n buffer = buffer.slice(firstBoundaryIndex + firstBoundary.length);\n\n if (startsWithBytes(buffer, closeSuffix)) {\n queue.close();\n return;\n }\n\n if (!startsWithBytes(buffer, lineSuffix)) {\n if (next.done) {\n throw new Error(\"Malformed multipart opening boundary.\");\n }\n break;\n }\n\n buffer = buffer.slice(lineSuffix.length);\n started = true;\n }\n\n if (currentPart === undefined) {\n const headerEnd = indexOfAscii(buffer, headerSeparator);\n\n if (headerEnd < 0) {\n if (next.done) {\n throw new Error(\"Multipart part ended before headers completed.\");\n }\n\n if (buffer.length > maxHeaderBytes) {\n throw new Error(`Multipart part headers exceeded ${maxHeaderBytes} bytes.`);\n }\n break;\n }\n\n totalParts += 1;\n enforcePartLimit(totalParts, options.maxParts ?? defaultMultipartMaxParts);\n currentPart = createPartWriter(\n decodeAscii(buffer.slice(0, headerEnd)),\n options,\n queue,\n );\n buffer = buffer.slice(headerEnd + headerSeparator.length);\n }\n\n const delimiterIndex = indexOfBytes(buffer, delimiter);\n\n if (delimiterIndex < 0) {\n if (next.done) {\n throw new Error(`Multipart part \"${currentPart.name}\" ended before the closing boundary.`);\n }\n\n const writableLength = Math.max(0, buffer.length - keepTailBytes);\n\n if (writableLength === 0) {\n break;\n }\n\n await writePartChunk(currentPart, buffer.slice(0, writableLength));\n buffer = buffer.slice(writableLength);\n continue;\n }\n\n await writePartChunk(currentPart, buffer.slice(0, delimiterIndex));\n await currentPart.writer.close();\n buffer = buffer.slice(delimiterIndex + delimiter.length);\n currentPart = undefined;\n\n if (startsWithBytes(buffer, closeSuffix)) {\n queue.close();\n return;\n }\n\n if (startsWithBytes(buffer, lineSuffix)) {\n buffer = buffer.slice(lineSuffix.length);\n continue;\n }\n\n if (next.done) {\n throw new Error(\"Malformed multipart boundary delimiter.\");\n }\n\n break;\n }\n\n if (next.done) {\n break;\n }\n }\n\n queue.close();\n } catch (error) {\n await currentPart?.writer.abort(error);\n queue.fail(error);\n } finally {\n queue.setCancel(undefined);\n reader.releaseLock();\n }\n\n function trimBufferToTail(prefixLength: number): void {\n if (buffer.length <= keepTailBytes + prefixLength) {\n return;\n }\n\n buffer = buffer.slice(buffer.length - keepTailBytes - prefixLength);\n }\n}\n\nfunction createPartWriter(\n rawHeaders: string,\n options: MultipartStreamParseOptions,\n queue: AsyncQueue<MultipartStreamPart>,\n): PartWriter {\n const headers = parsePartHeaders(rawHeaders);\n const disposition = parseContentDisposition(headers.get(\"content-disposition\"));\n\n if (disposition.name === undefined) {\n throw new Error(\"Multipart part is missing a form-data name.\");\n }\n\n const stream = new TransformStream<Uint8Array, Uint8Array>();\n const part = createPart({\n name: disposition.name,\n filename: disposition.filename,\n headers,\n body: stream.readable,\n });\n\n queue.push(part);\n\n return {\n name: disposition.name,\n maxBytes: options.fields?.[disposition.name]?.maxBytes,\n bytes: 0,\n writer: stream.writable.getWriter(),\n };\n}\n\nfunction createPart(options: {\n name: string;\n filename: string | undefined;\n headers: Headers;\n body: ReadableStream<Uint8Array>;\n}): MultipartStreamPart {\n const part: MultipartStreamPart = {\n name: options.name,\n headers: options.headers,\n body: options.body,\n text() {\n return new Response(options.body).text();\n },\n arrayBuffer() {\n return new Response(options.body).arrayBuffer();\n },\n fixedLengthStream(length) {\n const expectedLength = length ?? contentLengthFromHeaders(options.headers);\n const stream = createFixedLengthStream(expectedLength);\n\n return {\n readable: stream.readable,\n done: options.body.pipeTo(stream.writable),\n };\n },\n };\n\n if (options.filename !== undefined) {\n part.filename = options.filename;\n part.safeFilename = sanitizeMultipartFilename(options.filename);\n }\n\n const contentType = options.headers.get(\"content-type\");\n\n if (contentType !== null) {\n part.contentType = contentType;\n }\n\n return part;\n}\n\nfunction contentLengthFromHeaders(headers: Headers): number | bigint {\n const raw = headers.get(\"content-length\");\n\n if (raw === null || raw.trim() === \"\") {\n throw new Error(\n \"Multipart part fixedLengthStream() requires a byte length. Pass an explicit length or include a Content-Length part header.\",\n );\n }\n\n const parsed = Number(raw);\n\n if (!Number.isSafeInteger(parsed) || parsed < 0) {\n throw new Error(`Multipart part Content-Length must be a non-negative safe integer. Received ${JSON.stringify(raw)}.`);\n }\n\n return parsed;\n}\n\nfunction createFixedLengthStream(length: number | bigint): TransformStream<Uint8Array<ArrayBufferLike>, Uint8Array<ArrayBufferLike>> {\n const FixedLengthStream = (globalThis as {\n FixedLengthStream?: new (\n length: number | bigint,\n ) => TransformStream<Uint8Array<ArrayBufferLike>, Uint8Array<ArrayBufferLike>>;\n }).FixedLengthStream;\n\n if (FixedLengthStream === undefined) {\n throw new Error(\n \"FixedLengthStream is not available in this runtime. Use arrayBuffer() or pass the part body to a destination that accepts unknown-length streams.\",\n );\n }\n\n return new FixedLengthStream(length);\n}\n\nasync function writePartChunk(\n part: PartWriter,\n chunk: Uint8Array<ArrayBufferLike>,\n): Promise<void> {\n if (chunk.byteLength === 0) {\n return;\n }\n\n part.bytes += chunk.byteLength;\n enforceByteLimit(`multipart field \"${part.name}\"`, part.bytes, part.maxBytes);\n await part.writer.write(chunk);\n}\n\nfunction enforceByteLimit(label: string, bytes: number, maxBytes: number | undefined): void {\n if (maxBytes !== undefined && bytes > maxBytes) {\n throw new Error(`${label} exceeded ${maxBytes} bytes.`);\n }\n}\n\nfunction enforcePartLimit(parts: number, maxParts: number | undefined): void {\n if (maxParts !== undefined && parts > maxParts) {\n throw new Error(`multipart request exceeded ${maxParts} parts.`);\n }\n}\n\nexport function sanitizeMultipartFilename(filename: string): string {\n const leaf = filename.split(/[\\\\/]/u).at(-1) ?? \"\";\n let sanitized = \"\";\n\n for (const char of leaf) {\n sanitized += isControlCharacter(char) ? \"_\" : char;\n }\n\n return sanitized === \"\" || sanitized === \".\" || sanitized === \"..\" ? \"upload.bin\" : sanitized;\n}\n\nfunction isControlCharacter(char: string): boolean {\n const code = char.charCodeAt(0);\n\n return code < 0x20 || code === 0x7f;\n}\n\nfunction parseMultipartBoundary(contentType: string | null): string | undefined {\n if (contentType === null) {\n return undefined;\n }\n\n const [mediaType, ...parameters] = contentType.split(\";\").map((part) => part.trim());\n\n if (mediaType?.toLowerCase() !== \"multipart/form-data\") {\n return undefined;\n }\n\n for (const parameter of parameters) {\n const [rawName, ...rawValue] = parameter.split(\"=\");\n\n if (rawName?.trim().toLowerCase() !== \"boundary\") {\n continue;\n }\n\n const value = rawValue.join(\"=\").trim();\n const unquoted = value.startsWith(\"\\\"\") && value.endsWith(\"\\\"\")\n ? value.slice(1, -1)\n : value;\n\n return unquoted === \"\" ? undefined : unquoted;\n }\n\n return undefined;\n}\n\nfunction parsePartHeaders(rawHeaders: string): Headers {\n const headers = new Headers();\n\n for (const line of rawHeaders.split(crlf)) {\n if (line.trim() === \"\") {\n continue;\n }\n\n const separator = line.indexOf(\":\");\n\n if (separator < 0) {\n throw new Error(`Malformed multipart header: ${line}`);\n }\n\n headers.append(\n line.slice(0, separator).trim().toLowerCase(),\n line.slice(separator + 1).trim(),\n );\n }\n\n return headers;\n}\n\nfunction parseContentDisposition(value: string | null): {\n name: string | undefined;\n filename: string | undefined;\n} {\n if (value === null) {\n return { name: undefined, filename: undefined };\n }\n\n const parameters = value.split(\";\").map((part) => part.trim());\n const disposition = parameters.shift()?.toLowerCase();\n\n if (disposition !== \"form-data\") {\n return { name: undefined, filename: undefined };\n }\n\n let name: string | undefined;\n let filename: string | undefined;\n\n for (const parameter of parameters) {\n const separator = parameter.indexOf(\"=\");\n\n if (separator < 0) {\n continue;\n }\n\n const key = parameter.slice(0, separator).trim().toLowerCase();\n const value = unquoteHeaderParameter(parameter.slice(separator + 1).trim());\n\n if (key === \"name\") {\n name = value;\n } else if (key === \"filename\") {\n filename = value;\n }\n }\n\n return { name, filename };\n}\n\nfunction unquoteHeaderParameter(value: string): string {\n if (!value.startsWith(\"\\\"\") || !value.endsWith(\"\\\"\")) {\n return value;\n }\n\n return value.slice(1, -1).replaceAll(/\\\\\"/g, \"\\\"\").replaceAll(/\\\\\\\\/g, \"\\\\\");\n}\n\nfunction concatBytes(\n left: Uint8Array<ArrayBufferLike>,\n right: Uint8Array<ArrayBufferLike>,\n): Uint8Array<ArrayBufferLike> {\n if (left.length === 0) {\n return right;\n }\n\n const next = new Uint8Array(left.length + right.length);\n next.set(left);\n next.set(right, left.length);\n return next;\n}\n\nfunction indexOfAscii(bytes: Uint8Array<ArrayBufferLike>, needle: string): number {\n return indexOfBytes(bytes, encodeAscii(needle));\n}\n\nfunction indexOfBytes(\n bytes: Uint8Array<ArrayBufferLike>,\n needle: Uint8Array<ArrayBufferLike>,\n): number {\n if (needle.length === 0 || bytes.length < needle.length) {\n return -1;\n }\n\n const first = needle[0];\n\n for (let index = 0; index <= bytes.length - needle.length; index += 1) {\n if (bytes[index] !== first) {\n continue;\n }\n\n let matched = true;\n\n for (let offset = 1; offset < needle.length; offset += 1) {\n if (bytes[index + offset] !== needle[offset]) {\n matched = false;\n break;\n }\n }\n\n if (matched) {\n return index;\n }\n }\n\n return -1;\n}\n\nfunction startsWithBytes(\n bytes: Uint8Array<ArrayBufferLike>,\n prefix: Uint8Array<ArrayBufferLike>,\n): boolean {\n if (bytes.length < prefix.length) {\n return false;\n }\n\n for (let index = 0; index < prefix.length; index += 1) {\n if (bytes[index] !== prefix[index]) {\n return false;\n }\n }\n\n return true;\n}\n\nfunction encodeAscii(value: string): Uint8Array<ArrayBufferLike> {\n return new TextEncoder().encode(value);\n}\n\nfunction decodeAscii(value: Uint8Array<ArrayBufferLike>): string {\n return new TextDecoder(\"utf-8\", { fatal: false }).decode(value);\n}\n\nclass AsyncQueue<T> implements AsyncIterator<T> {\n private readonly values: T[] = [];\n private readonly waits: Array<{\n resolve(result: IteratorResult<T>): void;\n reject(error: unknown): void;\n }> = [];\n private done = false;\n private error: unknown;\n private onCancel: (() => void) | undefined;\n\n setCancel(onCancel: (() => void) | undefined): void {\n this.onCancel = onCancel;\n }\n\n push(value: T): void {\n const wait = this.waits.shift();\n\n if (wait !== undefined) {\n wait.resolve({ done: false, value });\n return;\n }\n\n this.values.push(value);\n }\n\n close(): void {\n this.done = true;\n\n for (const wait of this.waits.splice(0)) {\n wait.resolve({ done: true, value: undefined });\n }\n }\n\n fail(error: unknown): void {\n this.error = error;\n this.done = true;\n\n for (const wait of this.waits.splice(0)) {\n wait.reject(error);\n }\n }\n\n next(): Promise<IteratorResult<T>> {\n if (this.values.length > 0) {\n return Promise.resolve({ done: false, value: this.values.shift() as T });\n }\n\n if (this.error !== undefined) {\n return Promise.reject(this.error);\n }\n\n if (this.done) {\n return Promise.resolve({ done: true, value: undefined });\n }\n\n return new Promise((resolve, reject) => {\n this.waits.push({ resolve, reject });\n });\n }\n\n return(): Promise<IteratorResult<T>> {\n this.done = true;\n this.values.splice(0);\n this.onCancel?.();\n\n for (const wait of this.waits.splice(0)) {\n wait.resolve({ done: true, value: undefined });\n }\n\n return Promise.resolve({ done: true, value: undefined });\n }\n}\n"]}
1
+ {"version":3,"file":"multipart.js","sourceRoot":"","sources":["../src/multipart.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Configures multipart stream parsing limits and field handling.\n */\nexport interface MultipartStreamParseOptions {\n fields?: Readonly<Record<string, MultipartStreamFieldOptions>>;\n maxParts?: number;\n maxBytes?: number;\n}\n\n/**\n * Configures parsing behavior for a named multipart field.\n */\nexport interface MultipartStreamFieldOptions {\n type?: \"stream\" | \"text\";\n maxBytes?: number;\n}\n\n/**\n * Represents one parsed multipart field or file part.\n */\nexport interface MultipartStreamPart {\n name: string;\n filename?: string;\n safeFilename?: string;\n contentType?: string;\n headers: Headers;\n body: ReadableStream<Uint8Array<ArrayBufferLike>>;\n text(): Promise<string>;\n arrayBuffer(): Promise<ArrayBuffer>;\n fixedLengthStream(length?: number | bigint): MultipartFixedLengthStream;\n}\n\n/**\n * Couples a readable multipart part stream with a completion signal.\n */\nexport interface MultipartFixedLengthStream {\n readable: ReadableStream<Uint8Array<ArrayBufferLike>>;\n done: Promise<void>;\n}\n\ninterface PartWriter {\n name: string;\n maxBytes: number | undefined;\n bytes: number;\n writer: WritableStreamDefaultWriter<Uint8Array<ArrayBufferLike>>;\n}\n\nconst crlf = \"\\r\\n\";\nconst headerSeparator = \"\\r\\n\\r\\n\";\nconst maxHeaderBytes = 64 * 1024;\nconst maxMultipartBoundaryCharacters = 70;\nexport const defaultMultipartMaxBytes = 10 * 1024 * 1024;\nexport const defaultMultipartMaxParts = 1_000;\n\n/**\n * Parses a multipart request body as an async iterable of streamable parts.\n */\nexport function parseMultipartStream(\n request: Request,\n options: MultipartStreamParseOptions = {},\n): AsyncIterable<MultipartStreamPart> {\n return {\n [Symbol.asyncIterator]() {\n const queue = new AsyncQueue<MultipartStreamPart>();\n void parseMultipartRequest(request, options, queue);\n return queue;\n },\n };\n}\n\nasync function parseMultipartRequest(\n request: Request,\n options: MultipartStreamParseOptions,\n queue: AsyncQueue<MultipartStreamPart>,\n): Promise<void> {\n const boundary = parseMultipartBoundary(request.headers.get(\"content-type\"));\n\n if (boundary === undefined) {\n queue.fail(new Error(\"Multipart request content-type must include a boundary.\"));\n return;\n }\n\n if (request.body === null) {\n queue.fail(new Error(\"Multipart request body is not readable.\"));\n return;\n }\n\n const reader = request.body.getReader();\n const delimiter = encodeAscii(`${crlf}--${boundary}`);\n const firstBoundary = encodeAscii(`--${boundary}`);\n const closeSuffix = encodeAscii(\"--\");\n const lineSuffix = encodeAscii(crlf);\n const keepTailBytes = delimiter.length + 4;\n let buffer: Uint8Array<ArrayBufferLike> = new Uint8Array();\n let totalBytes = 0;\n let totalParts = 0;\n let started = false;\n let invalidOpeningBoundarySeen = false;\n let firstBoundarySearchOffset = 0;\n let currentPart: PartWriter | undefined;\n\n queue.setCancel(() => {\n void currentPart?.writer.abort(new Error(\"Multipart stream iteration was cancelled.\"));\n void reader.cancel();\n });\n\n try {\n while (true) {\n const next = await reader.read();\n\n if (!next.done) {\n totalBytes += next.value.byteLength;\n enforceByteLimit(\"multipart request\", totalBytes, options.maxBytes ?? defaultMultipartMaxBytes);\n buffer = concatBytes(buffer, next.value);\n }\n\n while (true) {\n if (!started) {\n const firstBoundaryIndex = indexOfBytes(buffer, firstBoundary, firstBoundarySearchOffset);\n\n if (firstBoundaryIndex < 0) {\n if (next.done) {\n if (invalidOpeningBoundarySeen) {\n throw new Error(\"Malformed multipart opening boundary.\");\n }\n\n throw new Error(\"Multipart request body does not contain the opening boundary.\");\n }\n\n trimBufferToTail(0);\n firstBoundarySearchOffset = 0;\n break;\n }\n\n const firstBoundaryEnd = firstBoundaryIndex + firstBoundary.length;\n\n if (buffer.length < firstBoundaryEnd + closeSuffix.length) {\n if (next.done) {\n throw new Error(\"Malformed multipart opening boundary.\");\n }\n\n if (firstBoundaryIndex > 0) {\n buffer = buffer.slice(firstBoundaryIndex);\n }\n firstBoundarySearchOffset = 0;\n break;\n }\n\n if (startsWithBytes(buffer, closeSuffix, firstBoundaryEnd)) {\n queue.close();\n return;\n }\n\n if (!startsWithBytes(buffer, lineSuffix, firstBoundaryEnd)) {\n invalidOpeningBoundarySeen = true;\n firstBoundarySearchOffset = firstBoundaryIndex + 1;\n continue;\n }\n\n buffer = buffer.slice(firstBoundaryEnd + lineSuffix.length);\n started = true;\n }\n\n if (currentPart === undefined) {\n const headerEnd = indexOfAscii(buffer, headerSeparator);\n\n if (headerEnd < 0) {\n if (next.done) {\n throw new Error(\"Multipart part ended before headers completed.\");\n }\n\n if (buffer.length > maxHeaderBytes) {\n throw new Error(`Multipart part headers exceeded ${maxHeaderBytes} bytes.`);\n }\n break;\n }\n\n totalParts += 1;\n enforcePartLimit(totalParts, options.maxParts ?? defaultMultipartMaxParts);\n currentPart = createPartWriter(\n decodeAscii(buffer.slice(0, headerEnd)),\n options,\n queue,\n );\n buffer = buffer.slice(headerEnd + headerSeparator.length);\n }\n\n const delimiterIndex = indexOfBytes(buffer, delimiter);\n\n if (delimiterIndex < 0) {\n if (next.done) {\n throw new Error(`Multipart part \"${currentPart.name}\" ended before the closing boundary.`);\n }\n\n const writableLength = Math.max(0, buffer.length - keepTailBytes);\n\n if (writableLength === 0) {\n break;\n }\n\n await writePartChunk(currentPart, buffer.slice(0, writableLength));\n buffer = buffer.slice(writableLength);\n continue;\n }\n\n await writePartChunk(currentPart, buffer.slice(0, delimiterIndex));\n buffer = buffer.slice(delimiterIndex);\n\n if (buffer.length < delimiter.length + closeSuffix.length) {\n if (next.done) {\n throw new Error(\"Malformed multipart boundary delimiter.\");\n }\n\n break;\n }\n\n const closesMultipart = startsWithBytes(buffer, closeSuffix, delimiter.length);\n\n if (!closesMultipart && !startsWithBytes(buffer, lineSuffix, delimiter.length)) {\n throw new Error(\"Malformed multipart boundary delimiter.\");\n }\n\n await currentPart.writer.close();\n currentPart = undefined;\n buffer = buffer.slice(delimiter.length + closeSuffix.length);\n\n if (closesMultipart) {\n queue.close();\n return;\n }\n\n continue;\n }\n\n if (next.done) {\n break;\n }\n }\n\n queue.close();\n } catch (error) {\n await currentPart?.writer.abort(error);\n queue.fail(error);\n } finally {\n queue.setCancel(undefined);\n reader.releaseLock();\n }\n\n function trimBufferToTail(prefixLength: number): void {\n if (buffer.length <= keepTailBytes + prefixLength) {\n return;\n }\n\n buffer = buffer.slice(buffer.length - keepTailBytes - prefixLength);\n }\n}\n\nfunction createPartWriter(\n rawHeaders: string,\n options: MultipartStreamParseOptions,\n queue: AsyncQueue<MultipartStreamPart>,\n): PartWriter {\n const headers = parsePartHeaders(rawHeaders);\n const disposition = parseContentDisposition(headers.get(\"content-disposition\"));\n\n if (disposition.name === undefined) {\n throw new Error(\"Multipart part is missing a form-data name.\");\n }\n\n const stream = new TransformStream<Uint8Array, Uint8Array>();\n const part = createPart({\n name: disposition.name,\n filename: disposition.filename,\n headers,\n body: stream.readable,\n });\n\n queue.push(part);\n\n return {\n name: disposition.name,\n maxBytes: options.fields?.[disposition.name]?.maxBytes,\n bytes: 0,\n writer: stream.writable.getWriter(),\n };\n}\n\nfunction createPart(options: {\n name: string;\n filename: string | undefined;\n headers: Headers;\n body: ReadableStream<Uint8Array>;\n}): MultipartStreamPart {\n const part: MultipartStreamPart = {\n name: options.name,\n headers: options.headers,\n body: options.body,\n text() {\n return new Response(options.body).text();\n },\n arrayBuffer() {\n return new Response(options.body).arrayBuffer();\n },\n fixedLengthStream(length) {\n const expectedLength = length ?? contentLengthFromHeaders(options.headers);\n const stream = createFixedLengthStream(expectedLength);\n\n return {\n readable: stream.readable,\n done: options.body.pipeTo(stream.writable),\n };\n },\n };\n\n if (options.filename !== undefined) {\n part.filename = options.filename;\n part.safeFilename = sanitizeMultipartFilename(options.filename);\n }\n\n const contentType = options.headers.get(\"content-type\");\n\n if (contentType !== null) {\n part.contentType = contentType;\n }\n\n return part;\n}\n\nfunction contentLengthFromHeaders(headers: Headers): number | bigint {\n const raw = headers.get(\"content-length\");\n\n if (raw === null || raw.trim() === \"\") {\n throw new Error(\n \"Multipart part fixedLengthStream() requires a byte length. Pass an explicit length or include a Content-Length part header.\",\n );\n }\n\n const parsed = Number(raw);\n\n if (!Number.isSafeInteger(parsed) || parsed < 0) {\n throw new Error(`Multipart part Content-Length must be a non-negative safe integer. Received ${JSON.stringify(raw)}.`);\n }\n\n return parsed;\n}\n\nfunction createFixedLengthStream(length: number | bigint): TransformStream<Uint8Array<ArrayBufferLike>, Uint8Array<ArrayBufferLike>> {\n const FixedLengthStream = (globalThis as {\n FixedLengthStream?: new (\n length: number | bigint,\n ) => TransformStream<Uint8Array<ArrayBufferLike>, Uint8Array<ArrayBufferLike>>;\n }).FixedLengthStream;\n\n if (FixedLengthStream === undefined) {\n throw new Error(\n \"FixedLengthStream is not available in this runtime. Use arrayBuffer() or pass the part body to a destination that accepts unknown-length streams.\",\n );\n }\n\n return new FixedLengthStream(length);\n}\n\nasync function writePartChunk(\n part: PartWriter,\n chunk: Uint8Array<ArrayBufferLike>,\n): Promise<void> {\n if (chunk.byteLength === 0) {\n return;\n }\n\n part.bytes += chunk.byteLength;\n enforceByteLimit(`multipart field \"${part.name}\"`, part.bytes, part.maxBytes);\n await part.writer.write(chunk);\n}\n\nfunction enforceByteLimit(label: string, bytes: number, maxBytes: number | undefined): void {\n if (maxBytes !== undefined && bytes > maxBytes) {\n throw new Error(`${label} exceeded ${maxBytes} bytes.`);\n }\n}\n\nfunction enforcePartLimit(parts: number, maxParts: number | undefined): void {\n if (maxParts !== undefined && parts > maxParts) {\n throw new Error(`multipart request exceeded ${maxParts} parts.`);\n }\n}\n\nexport function sanitizeMultipartFilename(filename: string): string {\n const leaf = filename.split(/[\\\\/]/u).at(-1) ?? \"\";\n let sanitized = \"\";\n\n for (const char of leaf) {\n sanitized += isControlCharacter(char) ? \"_\" : char;\n }\n\n return sanitized === \"\" || sanitized === \".\" || sanitized === \"..\" ? \"upload.bin\" : sanitized;\n}\n\nfunction isControlCharacter(char: string): boolean {\n const code = char.charCodeAt(0);\n\n return code < 0x20 || code === 0x7f;\n}\n\nfunction parseMultipartBoundary(contentType: string | null): string | undefined {\n if (contentType === null) {\n return undefined;\n }\n\n const [mediaType, ...parameters] = contentType.split(\";\").map((part) => part.trim());\n\n if (mediaType?.toLowerCase() !== \"multipart/form-data\") {\n return undefined;\n }\n\n for (const parameter of parameters) {\n const [rawName, ...rawValue] = parameter.split(\"=\");\n\n if (rawName?.trim().toLowerCase() !== \"boundary\") {\n continue;\n }\n\n const value = rawValue.join(\"=\").trim();\n const unquoted = value.startsWith(\"\\\"\") && value.endsWith(\"\\\"\")\n ? value.slice(1, -1)\n : value;\n\n return unquoted === \"\" || unquoted.length > maxMultipartBoundaryCharacters\n ? undefined\n : unquoted;\n }\n\n return undefined;\n}\n\nfunction parsePartHeaders(rawHeaders: string): Headers {\n const headers = new Headers();\n\n for (const line of rawHeaders.split(crlf)) {\n if (line.trim() === \"\") {\n continue;\n }\n\n const separator = line.indexOf(\":\");\n\n if (separator < 0) {\n throw new Error(`Malformed multipart header: ${line}`);\n }\n\n headers.append(\n line.slice(0, separator).trim().toLowerCase(),\n line.slice(separator + 1).trim(),\n );\n }\n\n return headers;\n}\n\nfunction parseContentDisposition(value: string | null): {\n name: string | undefined;\n filename: string | undefined;\n} {\n if (value === null) {\n return { name: undefined, filename: undefined };\n }\n\n const parameters = value.split(\";\").map((part) => part.trim());\n const disposition = parameters.shift()?.toLowerCase();\n\n if (disposition !== \"form-data\") {\n return { name: undefined, filename: undefined };\n }\n\n let name: string | undefined;\n let filename: string | undefined;\n\n for (const parameter of parameters) {\n const separator = parameter.indexOf(\"=\");\n\n if (separator < 0) {\n continue;\n }\n\n const key = parameter.slice(0, separator).trim().toLowerCase();\n const value = unquoteHeaderParameter(parameter.slice(separator + 1).trim());\n\n if (key === \"name\") {\n name = value;\n } else if (key === \"filename\") {\n filename = value;\n }\n }\n\n return { name, filename };\n}\n\nfunction unquoteHeaderParameter(value: string): string {\n if (!value.startsWith(\"\\\"\") || !value.endsWith(\"\\\"\")) {\n return value;\n }\n\n return value.slice(1, -1).replaceAll(/\\\\\"/g, \"\\\"\").replaceAll(/\\\\\\\\/g, \"\\\\\");\n}\n\nfunction concatBytes(\n left: Uint8Array<ArrayBufferLike>,\n right: Uint8Array<ArrayBufferLike>,\n): Uint8Array<ArrayBufferLike> {\n if (left.length === 0) {\n return right;\n }\n\n const next = new Uint8Array(left.length + right.length);\n next.set(left);\n next.set(right, left.length);\n return next;\n}\n\nfunction indexOfAscii(bytes: Uint8Array<ArrayBufferLike>, needle: string): number {\n return indexOfBytes(bytes, encodeAscii(needle));\n}\n\nfunction indexOfBytes(\n bytes: Uint8Array<ArrayBufferLike>,\n needle: Uint8Array<ArrayBufferLike>,\n startOffset = 0,\n): number {\n if (needle.length === 0 || bytes.length - startOffset < needle.length) {\n return -1;\n }\n\n const first = needle[0];\n\n for (let index = startOffset; index <= bytes.length - needle.length; index += 1) {\n if (bytes[index] !== first) {\n continue;\n }\n\n let matched = true;\n\n for (let offset = 1; offset < needle.length; offset += 1) {\n if (bytes[index + offset] !== needle[offset]) {\n matched = false;\n break;\n }\n }\n\n if (matched) {\n return index;\n }\n }\n\n return -1;\n}\n\nfunction startsWithBytes(\n bytes: Uint8Array<ArrayBufferLike>,\n prefix: Uint8Array<ArrayBufferLike>,\n offset = 0,\n): boolean {\n if (bytes.length - offset < prefix.length) {\n return false;\n }\n\n for (let index = 0; index < prefix.length; index += 1) {\n if (bytes[offset + index] !== prefix[index]) {\n return false;\n }\n }\n\n return true;\n}\n\nfunction encodeAscii(value: string): Uint8Array<ArrayBufferLike> {\n return new TextEncoder().encode(value);\n}\n\nfunction decodeAscii(value: Uint8Array<ArrayBufferLike>): string {\n return new TextDecoder(\"utf-8\", { fatal: false }).decode(value);\n}\n\nclass AsyncQueue<T> implements AsyncIterator<T> {\n private readonly values: T[] = [];\n private readonly waits: Array<{\n resolve(result: IteratorResult<T>): void;\n reject(error: unknown): void;\n }> = [];\n private done = false;\n private error: unknown;\n private onCancel: (() => void) | undefined;\n\n setCancel(onCancel: (() => void) | undefined): void {\n this.onCancel = onCancel;\n }\n\n push(value: T): void {\n const wait = this.waits.shift();\n\n if (wait !== undefined) {\n wait.resolve({ done: false, value });\n return;\n }\n\n this.values.push(value);\n }\n\n close(): void {\n this.done = true;\n\n for (const wait of this.waits.splice(0)) {\n wait.resolve({ done: true, value: undefined });\n }\n }\n\n fail(error: unknown): void {\n this.error = error;\n this.done = true;\n\n for (const wait of this.waits.splice(0)) {\n wait.reject(error);\n }\n }\n\n next(): Promise<IteratorResult<T>> {\n if (this.values.length > 0) {\n return Promise.resolve({ done: false, value: this.values.shift() as T });\n }\n\n if (this.error !== undefined) {\n return Promise.reject(this.error);\n }\n\n if (this.done) {\n return Promise.resolve({ done: true, value: undefined });\n }\n\n return new Promise((resolve, reject) => {\n this.waits.push({ resolve, reject });\n });\n }\n\n return(): Promise<IteratorResult<T>> {\n this.done = true;\n this.values.splice(0);\n this.onCancel?.();\n\n for (const wait of this.waits.splice(0)) {\n wait.resolve({ done: true, value: undefined });\n }\n\n return Promise.resolve({ done: true, value: undefined });\n }\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reckona/mreact-router",
3
- "version": "0.0.205",
3
+ "version": "0.0.206",
4
4
  "description": "File-system app router, SSR, actions, and deployment adapters for mreact.",
5
5
  "keywords": [
6
6
  "jsx",
@@ -111,20 +111,20 @@
111
111
  "dependencies": {
112
112
  "@typescript/typescript6": "^6.0.2",
113
113
  "esbuild": ">=0.28.1 <0.29",
114
- "@reckona/mreact": "0.0.205",
115
- "@reckona/mreact-compat": "0.0.205",
116
- "@reckona/mreact-devtools": "0.0.205",
117
- "@reckona/mreact-compiler": "0.0.205",
118
- "@reckona/mreact-query": "0.0.205",
119
- "@reckona/mreact-reactive-core": "0.0.205",
120
- "@reckona/mreact-reactive-dom": "0.0.205",
121
- "@reckona/mreact-server": "0.0.205",
122
- "@reckona/mreact-shared": "0.0.205"
114
+ "@reckona/mreact": "0.0.206",
115
+ "@reckona/mreact-compat": "0.0.206",
116
+ "@reckona/mreact-compiler": "0.0.206",
117
+ "@reckona/mreact-query": "0.0.206",
118
+ "@reckona/mreact-reactive-core": "0.0.206",
119
+ "@reckona/mreact-devtools": "0.0.206",
120
+ "@reckona/mreact-reactive-dom": "0.0.206",
121
+ "@reckona/mreact-shared": "0.0.206",
122
+ "@reckona/mreact-server": "0.0.206"
123
123
  },
124
124
  "peerDependencies": {
125
125
  "vite": ">=8.1.4 <9"
126
126
  },
127
127
  "optionalDependencies": {
128
- "@reckona/mreact-router-native": "0.0.205"
128
+ "@reckona/mreact-router-native": "0.0.206"
129
129
  }
130
130
  }
package/src/multipart.ts CHANGED
@@ -48,6 +48,7 @@ interface PartWriter {
48
48
  const crlf = "\r\n";
49
49
  const headerSeparator = "\r\n\r\n";
50
50
  const maxHeaderBytes = 64 * 1024;
51
+ const maxMultipartBoundaryCharacters = 70;
51
52
  export const defaultMultipartMaxBytes = 10 * 1024 * 1024;
52
53
  export const defaultMultipartMaxParts = 1_000;
53
54
 
@@ -94,6 +95,8 @@ async function parseMultipartRequest(
94
95
  let totalBytes = 0;
95
96
  let totalParts = 0;
96
97
  let started = false;
98
+ let invalidOpeningBoundarySeen = false;
99
+ let firstBoundarySearchOffset = 0;
97
100
  let currentPart: PartWriter | undefined;
98
101
 
99
102
  queue.setCancel(() => {
@@ -113,32 +116,48 @@ async function parseMultipartRequest(
113
116
 
114
117
  while (true) {
115
118
  if (!started) {
116
- const firstBoundaryIndex = indexOfBytes(buffer, firstBoundary);
119
+ const firstBoundaryIndex = indexOfBytes(buffer, firstBoundary, firstBoundarySearchOffset);
117
120
 
118
121
  if (firstBoundaryIndex < 0) {
119
122
  if (next.done) {
123
+ if (invalidOpeningBoundarySeen) {
124
+ throw new Error("Malformed multipart opening boundary.");
125
+ }
126
+
120
127
  throw new Error("Multipart request body does not contain the opening boundary.");
121
128
  }
122
129
 
123
130
  trimBufferToTail(0);
131
+ firstBoundarySearchOffset = 0;
124
132
  break;
125
133
  }
126
134
 
127
- buffer = buffer.slice(firstBoundaryIndex + firstBoundary.length);
128
-
129
- if (startsWithBytes(buffer, closeSuffix)) {
130
- queue.close();
131
- return;
132
- }
135
+ const firstBoundaryEnd = firstBoundaryIndex + firstBoundary.length;
133
136
 
134
- if (!startsWithBytes(buffer, lineSuffix)) {
137
+ if (buffer.length < firstBoundaryEnd + closeSuffix.length) {
135
138
  if (next.done) {
136
139
  throw new Error("Malformed multipart opening boundary.");
137
140
  }
141
+
142
+ if (firstBoundaryIndex > 0) {
143
+ buffer = buffer.slice(firstBoundaryIndex);
144
+ }
145
+ firstBoundarySearchOffset = 0;
138
146
  break;
139
147
  }
140
148
 
141
- buffer = buffer.slice(lineSuffix.length);
149
+ if (startsWithBytes(buffer, closeSuffix, firstBoundaryEnd)) {
150
+ queue.close();
151
+ return;
152
+ }
153
+
154
+ if (!startsWithBytes(buffer, lineSuffix, firstBoundaryEnd)) {
155
+ invalidOpeningBoundarySeen = true;
156
+ firstBoundarySearchOffset = firstBoundaryIndex + 1;
157
+ continue;
158
+ }
159
+
160
+ buffer = buffer.slice(firstBoundaryEnd + lineSuffix.length);
142
161
  started = true;
143
162
  }
144
163
 
@@ -185,25 +204,32 @@ async function parseMultipartRequest(
185
204
  }
186
205
 
187
206
  await writePartChunk(currentPart, buffer.slice(0, delimiterIndex));
188
- await currentPart.writer.close();
189
- buffer = buffer.slice(delimiterIndex + delimiter.length);
190
- currentPart = undefined;
207
+ buffer = buffer.slice(delimiterIndex);
191
208
 
192
- if (startsWithBytes(buffer, closeSuffix)) {
193
- queue.close();
194
- return;
195
- }
209
+ if (buffer.length < delimiter.length + closeSuffix.length) {
210
+ if (next.done) {
211
+ throw new Error("Malformed multipart boundary delimiter.");
212
+ }
196
213
 
197
- if (startsWithBytes(buffer, lineSuffix)) {
198
- buffer = buffer.slice(lineSuffix.length);
199
- continue;
214
+ break;
200
215
  }
201
216
 
202
- if (next.done) {
217
+ const closesMultipart = startsWithBytes(buffer, closeSuffix, delimiter.length);
218
+
219
+ if (!closesMultipart && !startsWithBytes(buffer, lineSuffix, delimiter.length)) {
203
220
  throw new Error("Malformed multipart boundary delimiter.");
204
221
  }
205
222
 
206
- break;
223
+ await currentPart.writer.close();
224
+ currentPart = undefined;
225
+ buffer = buffer.slice(delimiter.length + closeSuffix.length);
226
+
227
+ if (closesMultipart) {
228
+ queue.close();
229
+ return;
230
+ }
231
+
232
+ continue;
207
233
  }
208
234
 
209
235
  if (next.done) {
@@ -399,7 +425,9 @@ function parseMultipartBoundary(contentType: string | null): string | undefined
399
425
  ? value.slice(1, -1)
400
426
  : value;
401
427
 
402
- return unquoted === "" ? undefined : unquoted;
428
+ return unquoted === "" || unquoted.length > maxMultipartBoundaryCharacters
429
+ ? undefined
430
+ : unquoted;
403
431
  }
404
432
 
405
433
  return undefined;
@@ -495,14 +523,15 @@ function indexOfAscii(bytes: Uint8Array<ArrayBufferLike>, needle: string): numbe
495
523
  function indexOfBytes(
496
524
  bytes: Uint8Array<ArrayBufferLike>,
497
525
  needle: Uint8Array<ArrayBufferLike>,
526
+ startOffset = 0,
498
527
  ): number {
499
- if (needle.length === 0 || bytes.length < needle.length) {
528
+ if (needle.length === 0 || bytes.length - startOffset < needle.length) {
500
529
  return -1;
501
530
  }
502
531
 
503
532
  const first = needle[0];
504
533
 
505
- for (let index = 0; index <= bytes.length - needle.length; index += 1) {
534
+ for (let index = startOffset; index <= bytes.length - needle.length; index += 1) {
506
535
  if (bytes[index] !== first) {
507
536
  continue;
508
537
  }
@@ -527,13 +556,14 @@ function indexOfBytes(
527
556
  function startsWithBytes(
528
557
  bytes: Uint8Array<ArrayBufferLike>,
529
558
  prefix: Uint8Array<ArrayBufferLike>,
559
+ offset = 0,
530
560
  ): boolean {
531
- if (bytes.length < prefix.length) {
561
+ if (bytes.length - offset < prefix.length) {
532
562
  return false;
533
563
  }
534
564
 
535
565
  for (let index = 0; index < prefix.length; index += 1) {
536
- if (bytes[index] !== prefix[index]) {
566
+ if (bytes[offset + index] !== prefix[index]) {
537
567
  return false;
538
568
  }
539
569
  }