summaryrefslogtreecommitdiff
path: root/cli/js/web/body.ts
diff options
context:
space:
mode:
Diffstat (limited to 'cli/js/web/body.ts')
-rw-r--r--cli/js/web/body.ts92
1 files changed, 5 insertions, 87 deletions
diff --git a/cli/js/web/body.ts b/cli/js/web/body.ts
index ffe3f0e59..7a5b63d32 100644
--- a/cli/js/web/body.ts
+++ b/cli/js/web/body.ts
@@ -3,6 +3,7 @@ import * as encoding from "./text_encoding.ts";
import * as domTypes from "./dom_types.d.ts";
import { ReadableStreamImpl } from "./streams/readable_stream.ts";
import { getHeaderValueParams, hasHeaderValueOf } from "./util.ts";
+import { MultipartParser } from "./fetch/multipart.ts";
// only namespace imports work for now, plucking out what we need
const { TextEncoder, TextDecoder } = encoding;
@@ -130,98 +131,15 @@ export class Body implements domTypes.Body {
// ref: https://fetch.spec.whatwg.org/#body-mixin
public async formData(): Promise<FormData> {
const formData = new FormData();
- const enc = new TextEncoder();
if (hasHeaderValueOf(this.contentType, "multipart/form-data")) {
const params = getHeaderValueParams(this.contentType);
- if (!params.has("boundary")) {
- // TypeError is required by spec
- throw new TypeError("multipart/form-data must provide a boundary");
- }
+
// ref: https://tools.ietf.org/html/rfc2046#section-5.1
const boundary = params.get("boundary")!;
- const dashBoundary = `--${boundary}`;
- const delimiter = `\r\n${dashBoundary}`;
- const closeDelimiter = `${delimiter}--`;
-
- const body = await this.text();
- let bodyParts: string[];
- const bodyEpilogueSplit = body.split(closeDelimiter);
- if (bodyEpilogueSplit.length < 2) {
- bodyParts = [];
- } else {
- // discard epilogue
- const bodyEpilogueTrimmed = bodyEpilogueSplit[0];
- // first boundary treated special due to optional prefixed \r\n
- const firstBoundaryIndex = bodyEpilogueTrimmed.indexOf(dashBoundary);
- if (firstBoundaryIndex < 0) {
- throw new TypeError("Invalid boundary");
- }
- const bodyPreambleTrimmed = bodyEpilogueTrimmed
- .slice(firstBoundaryIndex + dashBoundary.length)
- .replace(/^[\s\r\n\t]+/, ""); // remove transport-padding CRLF
- // trimStart might not be available
- // Be careful! body-part allows trailing \r\n!
- // (as long as it is not part of `delimiter`)
- bodyParts = bodyPreambleTrimmed
- .split(delimiter)
- .map((s): string => s.replace(/^[\s\r\n\t]+/, ""));
- // TODO: LWSP definition is actually trickier,
- // but should be fine in our case since without headers
- // we should just discard the part
- }
- for (const bodyPart of bodyParts) {
- const headers = new Headers();
- const headerOctetSeperatorIndex = bodyPart.indexOf("\r\n\r\n");
- if (headerOctetSeperatorIndex < 0) {
- continue; // Skip unknown part
- }
- const headerText = bodyPart.slice(0, headerOctetSeperatorIndex);
- const octets = bodyPart.slice(headerOctetSeperatorIndex + 4);
+ const body = new Uint8Array(await this.arrayBuffer());
+ const multipartParser = new MultipartParser(body, boundary);
- // TODO: use textproto.readMIMEHeader from deno_std
- const rawHeaders = headerText.split("\r\n");
- for (const rawHeader of rawHeaders) {
- const sepIndex = rawHeader.indexOf(":");
- if (sepIndex < 0) {
- continue; // Skip this header
- }
- const key = rawHeader.slice(0, sepIndex);
- const value = rawHeader.slice(sepIndex + 1);
- headers.set(key, value);
- }
- if (!headers.has("content-disposition")) {
- continue; // Skip unknown part
- }
- // Content-Transfer-Encoding Deprecated
- const contentDisposition = headers.get("content-disposition")!;
- const partContentType = headers.get("content-type") || "text/plain";
- // TODO: custom charset encoding (needs TextEncoder support)
- // const contentTypeCharset =
- // getHeaderValueParams(partContentType).get("charset") || "";
- if (!hasHeaderValueOf(contentDisposition, "form-data")) {
- continue; // Skip, might not be form-data
- }
- const dispositionParams = getHeaderValueParams(contentDisposition);
- if (!dispositionParams.has("name")) {
- continue; // Skip, unknown name
- }
- const dispositionName = dispositionParams.get("name")!;
- if (dispositionParams.has("filename")) {
- const filename = dispositionParams.get("filename")!;
- const blob = new DenoBlob([enc.encode(octets)], {
- type: partContentType,
- });
- // TODO: based on spec
- // https://xhr.spec.whatwg.org/#dom-formdata-append
- // https://xhr.spec.whatwg.org/#create-an-entry
- // Currently it does not mention how I could pass content-type
- // to the internally created file object...
- formData.append(dispositionName, blob, filename);
- } else {
- formData.append(dispositionName, octets);
- }
- }
- return formData;
+ return multipartParser.parse();
} else if (
hasHeaderValueOf(this.contentType, "application/x-www-form-urlencoded")
) {