mirror of
https://github.com/denoland/deno.git
synced 2025-03-03 09:31:22 -05:00
perf(cli/body): improve .arrayBuffer() speed (#6669)
This commit is contained in:
parent
cb98a59452
commit
e4899b6ba4
5 changed files with 48 additions and 23 deletions
|
@ -18,6 +18,11 @@ import { MultipartParser } from "./fetch/multipart.ts";
|
|||
const { TextEncoder, TextDecoder } = encoding;
|
||||
const DenoBlob = blob.DenoBlob;
|
||||
|
||||
interface BodyMeta {
|
||||
contentType: string;
|
||||
size?: number;
|
||||
}
|
||||
|
||||
function validateBodyType(owner: Body, bodySource: BodyInit | null): boolean {
|
||||
if (isTypedArray(bodySource)) {
|
||||
return true;
|
||||
|
@ -40,11 +45,17 @@ function validateBodyType(owner: Body, bodySource: BodyInit | null): boolean {
|
|||
}
|
||||
|
||||
async function bufferFromStream(
|
||||
stream: ReadableStreamReader
|
||||
stream: ReadableStreamReader,
|
||||
size?: number
|
||||
): Promise<ArrayBuffer> {
|
||||
const encoder = new TextEncoder();
|
||||
const buffer = new Buffer();
|
||||
|
||||
if (size) {
|
||||
// grow to avoid unnecessary allocations & copies
|
||||
buffer.grow(size);
|
||||
}
|
||||
|
||||
while (true) {
|
||||
const { done, value } = await stream.read();
|
||||
|
||||
|
@ -71,14 +82,13 @@ export const BodyUsedError =
|
|||
|
||||
export class Body implements domTypes.Body {
|
||||
protected _stream: ReadableStreamImpl<string | ArrayBuffer> | null;
|
||||
|
||||
constructor(
|
||||
protected _bodySource: BodyInit | null,
|
||||
readonly contentType: string
|
||||
) {
|
||||
#contentType: string;
|
||||
#size: number | undefined;
|
||||
constructor(protected _bodySource: BodyInit | null, meta: BodyMeta) {
|
||||
validateBodyType(this, _bodySource);
|
||||
this._bodySource = _bodySource;
|
||||
this.contentType = contentType;
|
||||
this.#contentType = meta.contentType;
|
||||
this.#size = meta.size;
|
||||
this._stream = null;
|
||||
}
|
||||
|
||||
|
@ -111,15 +121,15 @@ export class Body implements domTypes.Body {
|
|||
|
||||
public async blob(): Promise<Blob> {
|
||||
return new DenoBlob([await this.arrayBuffer()], {
|
||||
type: this.contentType,
|
||||
type: this.#contentType,
|
||||
});
|
||||
}
|
||||
|
||||
// ref: https://fetch.spec.whatwg.org/#body-mixin
|
||||
public async formData(): Promise<FormData> {
|
||||
const formData = new FormData();
|
||||
if (hasHeaderValueOf(this.contentType, "multipart/form-data")) {
|
||||
const params = getHeaderValueParams(this.contentType);
|
||||
if (hasHeaderValueOf(this.#contentType, "multipart/form-data")) {
|
||||
const params = getHeaderValueParams(this.#contentType);
|
||||
|
||||
// ref: https://tools.ietf.org/html/rfc2046#section-5.1
|
||||
const boundary = params.get("boundary")!;
|
||||
|
@ -128,7 +138,7 @@ export class Body implements domTypes.Body {
|
|||
|
||||
return multipartParser.parse();
|
||||
} else if (
|
||||
hasHeaderValueOf(this.contentType, "application/x-www-form-urlencoded")
|
||||
hasHeaderValueOf(this.#contentType, "application/x-www-form-urlencoded")
|
||||
) {
|
||||
// From https://github.com/github/fetch/blob/master/fetch.js
|
||||
// Copyright (c) 2014-2016 GitHub, Inc. MIT License
|
||||
|
@ -184,7 +194,7 @@ export class Body implements domTypes.Body {
|
|||
enc.encode(this._bodySource).buffer as ArrayBuffer
|
||||
);
|
||||
} else if (this._bodySource instanceof ReadableStreamImpl) {
|
||||
return bufferFromStream(this._bodySource.getReader());
|
||||
return bufferFromStream(this._bodySource.getReader(), this.#size);
|
||||
} else if (
|
||||
this._bodySource instanceof FormData ||
|
||||
this._bodySource instanceof URLSearchParams
|
||||
|
|
|
@ -110,8 +110,9 @@ export class Response extends Body.Body implements domTypes.Response {
|
|||
}
|
||||
|
||||
const contentType = headers.get("content-type") || "";
|
||||
const size = Number(headers.get("content-length")) || undefined;
|
||||
|
||||
super(body, contentType);
|
||||
super(body, { contentType, size });
|
||||
|
||||
this.url = url;
|
||||
this.statusText = statusText;
|
||||
|
|
|
@ -71,7 +71,7 @@ export class Request extends body.Body implements domTypes.Request {
|
|||
}
|
||||
|
||||
const contentType = headers.get("content-type") || "";
|
||||
super(b, contentType);
|
||||
super(b, { contentType });
|
||||
this.headers = headers;
|
||||
|
||||
// readonly attribute ByteString method;
|
||||
|
|
|
@ -3,9 +3,10 @@ import { unitTest, assertEquals, assert } from "./test_util.ts";
|
|||
|
||||
// just a hack to get a body object
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
function buildBody(body: any): Body {
|
||||
function buildBody(body: any, headers?: Headers): Body {
|
||||
const stub = new Request("", {
|
||||
body: body,
|
||||
headers,
|
||||
});
|
||||
return stub as Body;
|
||||
}
|
||||
|
@ -40,10 +41,7 @@ unitTest(
|
|||
);
|
||||
const text = await response.text();
|
||||
|
||||
const body = buildBody(text);
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
(body as any).contentType = "multipart/form-data;boundary=boundary";
|
||||
const body = buildBody(text, response.headers);
|
||||
|
||||
const formData = await body.formData();
|
||||
assert(formData.has("field_1"));
|
||||
|
@ -60,10 +58,7 @@ unitTest(
|
|||
);
|
||||
const text = await response.text();
|
||||
|
||||
const body = buildBody(text);
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
(body as any).contentType = "application/x-www-form-urlencoded";
|
||||
const body = buildBody(text, response.headers);
|
||||
|
||||
const formData = await body.formData();
|
||||
assert(formData.has("field_1"));
|
||||
|
|
|
@ -851,6 +851,25 @@ unitTest(
|
|||
}
|
||||
);
|
||||
|
||||
unitTest(
|
||||
{ perms: { net: true } },
|
||||
async function fetchResponseContentLength(): Promise<void> {
|
||||
const body = new Uint8Array(2 ** 16);
|
||||
const headers = new Headers([["content-type", "application/octet-stream"]]);
|
||||
const res = await fetch("http://localhost:4545/echo_server", {
|
||||
body: body,
|
||||
method: "POST",
|
||||
headers,
|
||||
});
|
||||
assertEquals(Number(res.headers.get("content-length")), body.byteLength);
|
||||
|
||||
const blob = await res.blob();
|
||||
// Make sure Body content-type is correctly set
|
||||
assertEquals(blob.type, "application/octet-stream");
|
||||
assertEquals(blob.size, body.byteLength);
|
||||
}
|
||||
);
|
||||
|
||||
unitTest(function fetchResponseConstructorNullBody(): void {
|
||||
const nullBodyStatus = [204, 205, 304];
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue