summaryrefslogtreecommitdiff
path: root/cli/js/web/fetch/multipart.ts
diff options
context:
space:
mode:
authorMarcos Casagrande <marcoscvp90@gmail.com>2020-06-08 18:08:26 +0200
committerGitHub <noreply@github.com>2020-06-08 18:08:26 +0200
commitd9071339443a1da3fbb4c65fa19b4f65328da2f3 (patch)
treef31b69e1701ba445870e0505e4d034005b4ea8f0 /cli/js/web/fetch/multipart.ts
parent4feccdd3b7d7b1c4ef63c4ccc572a52403135df0 (diff)
fix(cli/web/fetch): multipart/form-data request body support for binary files (#5886)
Diffstat (limited to 'cli/js/web/fetch/multipart.ts')
-rw-r--r--cli/js/web/fetch/multipart.ts81
1 files changed, 81 insertions, 0 deletions
diff --git a/cli/js/web/fetch/multipart.ts b/cli/js/web/fetch/multipart.ts
index 792f9b5ee..654d4a0ea 100644
--- a/cli/js/web/fetch/multipart.ts
+++ b/cli/js/web/fetch/multipart.ts
@@ -1,5 +1,8 @@
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
+import { Buffer } from "../../buffer.ts";
+import { bytesSymbol } from "../blob.ts";
+import { DomFileImpl } from "../dom_file.ts";
import { DenoBlob } from "../blob.ts";
import { TextEncoder, TextDecoder } from "../text_encoding.ts";
import { getHeaderValueParams } from "../util.ts";
@@ -14,6 +17,84 @@ interface MultipartHeaders {
disposition: Map<string, string>;
}
+export class MultipartBuilder {
+ readonly boundary: string;
+ readonly formData: FormData;
+ readonly writer: Buffer;
+ constructor(formData: FormData, boundary?: string) {
+ this.boundary = boundary ?? this.#createBoundary();
+ this.formData = formData;
+ this.writer = new Buffer();
+ }
+
+ getContentType(): string {
+ return `multipart/form-data; boundary=${this.boundary}`;
+ }
+
+ getBody(): Uint8Array {
+ for (const [fieldName, fieldValue] of this.formData.entries()) {
+ if (fieldValue instanceof DomFileImpl) {
+ this.#writeFile(fieldName, fieldValue);
+ } else this.#writeField(fieldName, fieldValue as string);
+ }
+
+ this.writer.writeSync(encoder.encode(`\r\n--${this.boundary}--`));
+
+ return this.writer.bytes();
+ }
+
+ #createBoundary = (): string => {
+ return (
+ "----------" +
+ Array.from(Array(32))
+ .map(() => Math.random().toString(36)[2] || 0)
+ .join("")
+ );
+ };
+
+ #writeHeaders = (headers: string[][]): void => {
+ let buf = this.writer.empty() ? "" : "\r\n";
+
+ buf += `--${this.boundary}\r\n`;
+ for (const [key, value] of headers) {
+ buf += `${key}: ${value}\r\n`;
+ }
+ buf += `\r\n`;
+
+ this.writer.write(encoder.encode(buf));
+ };
+
+ #writeFileHeaders = (
+ field: string,
+ filename: string,
+ type?: string
+ ): void => {
+ const headers = [
+ [
+ "Content-Disposition",
+ `form-data; name="${field}"; filename="${filename}"`,
+ ],
+ ["Content-Type", type || "application/octet-stream"],
+ ];
+ return this.#writeHeaders(headers);
+ };
+
+ #writeFieldHeaders = (field: string): void => {
+ const headers = [["Content-Disposition", `form-data; name="${field}"`]];
+ return this.#writeHeaders(headers);
+ };
+
+ #writeField = (field: string, value: string): void => {
+ this.#writeFieldHeaders(field);
+ this.writer.writeSync(encoder.encode(value));
+ };
+
+ #writeFile = (field: string, value: DomFileImpl): void => {
+ this.#writeFileHeaders(field, value.name, value.type);
+ this.writer.writeSync(value[bytesSymbol]);
+ };
+}
+
export class MultipartParser {
readonly boundary: string;
readonly boundaryChars: Uint8Array;