From 569287b15b6482a39f2c816f103574c3b35351f8 Mon Sep 17 00:00:00 2001 From: Marcos Casagrande Date: Tue, 4 Oct 2022 15:48:50 +0200 Subject: perf(ext/fetch): consume body using ops (#16038) This commit adds a fast path to `Request` and `Response` that make consuming request bodies much faster when using `Body#text`, `Body#arrayBuffer`, and `Body#blob`, if the body is a FastStream. Because the response bodies for `fetch` are FastStream, this speeds up consuming `fetch` response bodies significantly. --- cli/tests/unit/fetch_test.ts | 16 +++++++++ cli/tests/unit/http_test.ts | 81 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 97 insertions(+) (limited to 'cli/tests') diff --git a/cli/tests/unit/fetch_test.ts b/cli/tests/unit/fetch_test.ts index 36c1926f2..e2ff0d5e0 100644 --- a/cli/tests/unit/fetch_test.ts +++ b/cli/tests/unit/fetch_test.ts @@ -1789,3 +1789,19 @@ Deno.test( assertEquals(await res.text(), "ok"); }, ); + +Deno.test( + { permissions: { net: true } }, + async function fetchResponseStreamIsLockedWhileReading() { + const response = await fetch("http://localhost:4545/echo_server", { + body: new Uint8Array(5000), + method: "POST", + }); + + assertEquals(response.body!.locked, false); + const promise = response.arrayBuffer(); + assertEquals(response.body!.locked, true); + + await promise; + }, +); diff --git a/cli/tests/unit/http_test.ts b/cli/tests/unit/http_test.ts index 3de93076e..7bb16aecc 100644 --- a/cli/tests/unit/http_test.ts +++ b/cli/tests/unit/http_test.ts @@ -2292,6 +2292,87 @@ Deno.test("upgradeHttp unix", { await Promise.all([server, client()]); }); +Deno.test( + { permissions: { net: true } }, + async function httpServerReadLargeBodyWithContentLength() { + const TLS_PACKET_SIZE = 16 * 1024 + 256; + // We want the body to be read in multiple packets + const body = "aa\n" + "deno.land large body\n".repeat(TLS_PACKET_SIZE) + + "zz"; + + let httpConn: Deno.HttpConn; + const promise = (async () => { + const listener = Deno.listen({ port: 4501 }); + const conn = await listener.accept(); + listener.close(); + httpConn = Deno.serveHttp(conn); + const reqEvent = await httpConn.nextRequest(); + assert(reqEvent); + const { request, respondWith } = reqEvent; + assertEquals(await request.text(), body); + await respondWith(new Response(body)); + })(); + + const resp = await fetch("http://127.0.0.1:4501/", { + method: "POST", + headers: { "connection": "close" }, + body, + }); + const text = await resp.text(); + assertEquals(text, body); + await promise; + + httpConn!.close(); + }, +); + +Deno.test( + { permissions: { net: true } }, + async function httpServerReadLargeBodyWithTransferChunked() { + const TLS_PACKET_SIZE = 16 * 1024 + 256; + + // We want the body to be read in multiple packets + const chunks = [ + "aa\n", + "deno.land large body\n".repeat(TLS_PACKET_SIZE), + "zz", + ]; + + const body = chunks.join(""); + + const stream = new TransformStream(); + const writer = stream.writable.getWriter(); + for (const chunk of chunks) { + writer.write(new TextEncoder().encode(chunk)); + } + writer.close(); + + let httpConn: Deno.HttpConn; + const promise = (async () => { + const listener = Deno.listen({ port: 4501 }); + const conn = await listener.accept(); + listener.close(); + httpConn = Deno.serveHttp(conn); + const reqEvent = await httpConn.nextRequest(); + assert(reqEvent); + const { request, respondWith } = reqEvent; + assertEquals(await request.text(), body); + await respondWith(new Response(body)); + })(); + + const resp = await fetch("http://127.0.0.1:4501/", { + method: "POST", + headers: { "connection": "close" }, + body: stream.readable, + }); + const text = await resp.text(); + assertEquals(text, body); + await promise; + + httpConn!.close(); + }, +); + function chunkedBodyReader(h: Headers, r: BufReader): Deno.Reader { // Based on https://tools.ietf.org/html/rfc2616#section-19.4.6 const tp = new TextProtoReader(r); -- cgit v1.2.3