diff --git a/http/server.ts b/http/server.ts index 9ca6c4a896..da335a58cd 100644 --- a/http/server.ts +++ b/http/server.ts @@ -8,7 +8,12 @@ import { BufReader, BufState, BufWriter } from "../io/bufio.ts"; import { TextProtoReader } from "../textproto/mod.ts"; import { STATUS_TEXT } from "./http_status.ts"; import { assert, fail } from "../testing/asserts.ts"; -import { deferred, Deferred, MuxAsyncIterator } from "../util/async.ts"; +import { + collectUint8Arrays, + deferred, + Deferred, + MuxAsyncIterator +} from "../util/async.ts"; function bufWriter(w: Writer): BufWriter { if (w instanceof BufWriter) { @@ -94,28 +99,6 @@ export async function writeResponse(w: Writer, r: Response): Promise { await writer.flush(); } -async function readAllIterator( - it: AsyncIterableIterator -): Promise { - const chunks = []; - let len = 0; - for await (const chunk of it) { - chunks.push(chunk); - len += chunk.length; - } - if (chunks.length === 0) { - // No need for copy - return chunks[0]; - } - const collected = new Uint8Array(len); - let offset = 0; - for (let chunk of chunks) { - collected.set(chunk, offset); - offset += chunk.length; - } - return collected; -} - export class ServerRequest { url: string; method: string; @@ -203,7 +186,7 @@ export class ServerRequest { // Read the body of the request into a single Uint8Array public async body(): Promise { - return readAllIterator(this.bodyStream()); + return collectUint8Arrays(this.bodyStream()); } async respond(r: Response): Promise { diff --git a/util/async.ts b/util/async.ts index f9f2477d06..15edb1b42d 100644 --- a/util/async.ts +++ b/util/async.ts @@ -83,3 +83,28 @@ export class MuxAsyncIterator implements AsyncIterable { return this.iterate(); } } + +/** Collects all Uint8Arrays from an AsyncIterable and retuns a single + * Uint8Array with the concatenated contents of all the collected arrays. + */ +export async function collectUint8Arrays( + it: AsyncIterable +): Promise { + const chunks = []; + let length = 0; + for await (const chunk of it) { + chunks.push(chunk); + length += chunk.length; + } + if (chunks.length === 1) { + // No need to copy. + return chunks[0]; + } + const collected = new Uint8Array(length); + let offset = 0; + for (let chunk of chunks) { + collected.set(chunk, offset); + offset += chunk.length; + } + return collected; +} diff --git a/util/async_test.ts b/util/async_test.ts index c704002d41..adaac1e22f 100644 --- a/util/async_test.ts +++ b/util/async_test.ts @@ -1,7 +1,7 @@ // Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. import { test, runIfMain } from "../testing/mod.ts"; -import { assertEquals } from "../testing/asserts.ts"; -import { MuxAsyncIterator, deferred } from "./async.ts"; +import { assert, assertEquals, assertStrictEq } from "../testing/asserts.ts"; +import { collectUint8Arrays, deferred, MuxAsyncIterator } from "./async.ts"; test(async function asyncDeferred(): Promise { const d = deferred(); @@ -31,4 +31,43 @@ test(async function asyncMuxAsyncIterator(): Promise { assertEquals(results.size, 6); }); +test(async function collectUint8Arrays0(): Promise { + async function* gen(): AsyncIterableIterator {} + const result = await collectUint8Arrays(gen()); + assert(result instanceof Uint8Array); + assertEquals(result.length, 0); +}); + +test(async function collectUint8Arrays0(): Promise { + async function* gen(): AsyncIterableIterator {} + const result = await collectUint8Arrays(gen()); + assert(result instanceof Uint8Array); + assertStrictEq(result.length, 0); +}); + +test(async function collectUint8Arrays1(): Promise { + const buf = new Uint8Array([1, 2, 3]); + async function* gen(): AsyncIterableIterator { + yield buf; + } + const result = await collectUint8Arrays(gen()); + assertStrictEq(result, buf); + assertStrictEq(result.length, 3); +}); + +test(async function collectUint8Arrays4(): Promise { + async function* gen(): AsyncIterableIterator { + yield new Uint8Array([1, 2, 3]); + yield new Uint8Array([]); + yield new Uint8Array([4, 5]); + yield new Uint8Array([6]); + } + const result = await collectUint8Arrays(gen()); + assert(result instanceof Uint8Array); + assertStrictEq(result.length, 6); + for (let i = 0; i < 6; i++) { + assertStrictEq(result[i], i + 1); + } +}); + runIfMain(import.meta);