diff options
author | Matt Mastracci <matthew@mastracci.com> | 2024-02-10 13:22:13 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-02-10 20:22:13 +0000 |
commit | f5e46c9bf2f50d66a953fa133161fc829cecff06 (patch) | |
tree | 8faf2f5831c1c7b11d842cd9908d141082c869a5 /cli/tests/unit/fetch_test.ts | |
parent | d2477f780630a812bfd65e3987b70c0d309385bb (diff) |
chore: move cli/tests/ -> tests/ (#22369)
This looks like a massive PR, but it's only a move from cli/tests ->
tests, and updates of relative paths for files.
This is the first step towards aggregate all of the integration test
files under tests/, which will lead to a set of integration tests that
can run without the CLI binary being built.
While we could leave these tests under `cli`, it would require us to
keep a more complex directory structure for the various test runners. In
addition, we have a lot of complexity to ignore various test files in
the `cli` project itself (cargo publish exclusion rules, autotests =
false, etc).
And finally, the `tests/` folder will eventually house the `test_ffi`,
`test_napi` and other testing code, reducing the size of the root repo
directory.
For easier review, the extremely large and noisy "move" is in the first
commit (with no changes -- just a move), while the remainder of the
changes to actual files is in the second commit.
Diffstat (limited to 'cli/tests/unit/fetch_test.ts')
-rw-r--r-- | cli/tests/unit/fetch_test.ts | 2071 |
1 files changed, 0 insertions, 2071 deletions
diff --git a/cli/tests/unit/fetch_test.ts b/cli/tests/unit/fetch_test.ts deleted file mode 100644 index 84e94de0d..000000000 --- a/cli/tests/unit/fetch_test.ts +++ /dev/null @@ -1,2071 +0,0 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -import { - assert, - assertEquals, - assertRejects, - assertThrows, - delay, - fail, - unimplemented, -} from "./test_util.ts"; -import { Buffer } from "@test_util/std/io/buffer.ts"; - -const listenPort = 4506; - -Deno.test( - { permissions: { net: true } }, - async function fetchRequiresOneArgument() { - await assertRejects( - fetch as unknown as () => Promise<void>, - TypeError, - ); - }, -); - -Deno.test({ permissions: { net: true } }, async function fetchProtocolError() { - await assertRejects( - async () => { - await fetch("ftp://localhost:21/a/file"); - }, - TypeError, - "not supported", - ); -}); - -function findClosedPortInRange( - minPort: number, - maxPort: number, -): number | never { - let port = minPort; - - // If we hit the return statement of this loop - // that means that we did not throw an - // AddrInUse error when we executed Deno.listen. - while (port < maxPort) { - try { - const listener = Deno.listen({ port }); - listener.close(); - return port; - } catch (_e) { - port++; - } - } - - unimplemented( - `No available ports between ${minPort} and ${maxPort} to test fetch`, - ); -} - -Deno.test( - // TODO(bartlomieju): reenable this test - // https://github.com/denoland/deno/issues/18350 - { ignore: Deno.build.os === "windows", permissions: { net: true } }, - async function fetchConnectionError() { - const port = findClosedPortInRange(4000, 9999); - await assertRejects( - async () => { - await fetch(`http://localhost:${port}`); - }, - TypeError, - "error trying to connect", - ); - }, -); - -Deno.test( - { permissions: { net: true } }, - async function fetchDnsError() { - await assertRejects( - async () => { - await fetch("http://nil/"); - }, - TypeError, - "error trying to connect", - ); - }, -); - -Deno.test( - { permissions: { net: true } }, - async function fetchInvalidUriError() { - await assertRejects( - async () => { - await fetch("http://<invalid>/"); - }, - TypeError, - ); - }, -); - -Deno.test( - { permissions: { net: true } }, - async function fetchMalformedUriError() { - await assertRejects( - async () => { - const url = new URL("http://{{google/"); - await fetch(url); - }, - TypeError, - ); - }, -); - -Deno.test({ permissions: { net: true } }, async function fetchJsonSuccess() { - const response = await fetch("http://localhost:4545/assets/fixture.json"); - const json = await response.json(); - assertEquals(json.name, "deno"); -}); - -Deno.test({ permissions: { net: false } }, async function fetchPerm() { - await assertRejects(async () => { - await fetch("http://localhost:4545/assets/fixture.json"); - }, Deno.errors.PermissionDenied); -}); - -Deno.test({ permissions: { net: true } }, async function fetchUrl() { - const response = await fetch("http://localhost:4545/assets/fixture.json"); - assertEquals(response.url, "http://localhost:4545/assets/fixture.json"); - const _json = await response.json(); -}); - -Deno.test({ permissions: { net: true } }, async function fetchURL() { - const response = await fetch( - new URL("http://localhost:4545/assets/fixture.json"), - ); - assertEquals(response.url, "http://localhost:4545/assets/fixture.json"); - const _json = await response.json(); -}); - -Deno.test({ permissions: { net: true } }, async function fetchHeaders() { - const response = await fetch("http://localhost:4545/assets/fixture.json"); - const headers = response.headers; - assertEquals(headers.get("Content-Type"), "application/json"); - const _json = await response.json(); -}); - -Deno.test({ permissions: { net: true } }, async function fetchBlob() { - const response = await fetch("http://localhost:4545/assets/fixture.json"); - const headers = response.headers; - const blob = await response.blob(); - assertEquals(blob.type, headers.get("Content-Type")); - assertEquals(blob.size, Number(headers.get("Content-Length"))); -}); - -Deno.test( - { permissions: { net: true } }, - async function fetchBodyUsedReader() { - const response = await fetch( - "http://localhost:4545/assets/fixture.json", - ); - assert(response.body !== null); - - const reader = response.body.getReader(); - // Getting a reader should lock the stream but does not consume the body - // so bodyUsed should not be true - assertEquals(response.bodyUsed, false); - reader.releaseLock(); - await response.json(); - assertEquals(response.bodyUsed, true); - }, -); - -Deno.test( - { permissions: { net: true } }, - async function fetchBodyUsedCancelStream() { - const response = await fetch( - "http://localhost:4545/assets/fixture.json", - ); - assert(response.body !== null); - - assertEquals(response.bodyUsed, false); - const promise = response.body.cancel(); - assertEquals(response.bodyUsed, true); - await promise; - }, -); - -Deno.test({ permissions: { net: true } }, async function fetchAsyncIterator() { - const response = await fetch("http://localhost:4545/assets/fixture.json"); - const headers = response.headers; - - assert(response.body !== null); - let total = 0; - for await (const chunk of response.body) { - assert(chunk instanceof Uint8Array); - total += chunk.length; - } - - assertEquals(total, Number(headers.get("Content-Length"))); -}); - -Deno.test({ permissions: { net: true } }, async function fetchBodyReader() { - const response = await fetch("http://localhost:4545/assets/fixture.json"); - const headers = response.headers; - assert(response.body !== null); - const reader = response.body.getReader(); - let total = 0; - while (true) { - const { done, value } = await reader.read(); - if (done) break; - assert(value); - assert(value instanceof Uint8Array); - total += value.length; - } - - assertEquals(total, Number(headers.get("Content-Length"))); -}); - -Deno.test( - { permissions: { net: true } }, - async function fetchBodyReaderBigBody() { - const data = "a".repeat(10 << 10); // 10mb - const response = await fetch("http://localhost:4545/echo_server", { - method: "POST", - body: data, - }); - assert(response.body !== null); - const reader = await response.body.getReader(); - let total = 0; - while (true) { - const { done, value } = await reader.read(); - if (done) break; - assert(value); - total += value.length; - } - - assertEquals(total, data.length); - }, -); - -Deno.test({ permissions: { net: true } }, async function responseClone() { - const response = await fetch("http://localhost:4545/assets/fixture.json"); - const response1 = response.clone(); - assert(response !== response1); - assertEquals(response.status, response1.status); - assertEquals(response.statusText, response1.statusText); - const u8a = new Uint8Array(await response.arrayBuffer()); - const u8a1 = new Uint8Array(await response1.arrayBuffer()); - for (let i = 0; i < u8a.byteLength; i++) { - assertEquals(u8a[i], u8a1[i]); - } -}); - -Deno.test( - { permissions: { net: true } }, - async function fetchMultipartFormDataSuccess() { - const response = await fetch( - "http://localhost:4545/multipart_form_data.txt", - ); - const formData = await response.formData(); - assert(formData.has("field_1")); - assertEquals(formData.get("field_1")!.toString(), "value_1 \r\n"); - assert(formData.has("field_2")); - const file = formData.get("field_2") as File; - assertEquals(file.name, "file.js"); - - assertEquals(await file.text(), `console.log("Hi")`); - }, -); - -Deno.test( - { permissions: { net: true } }, - async function fetchMultipartFormBadContentType() { - const response = await fetch( - "http://localhost:4545/multipart_form_bad_content_type", - ); - assert(response.body !== null); - - await assertRejects( - async () => { - await response.formData(); - }, - TypeError, - "Body can not be decoded as form data", - ); - }, -); - -Deno.test( - { permissions: { net: true } }, - async function fetchURLEncodedFormDataSuccess() { - const response = await fetch( - "http://localhost:4545/subdir/form_urlencoded.txt", - ); - const formData = await response.formData(); - assert(formData.has("field_1")); - assertEquals(formData.get("field_1")!.toString(), "Hi"); - assert(formData.has("field_2")); - assertEquals(formData.get("field_2")!.toString(), "<Deno>"); - }, -); - -Deno.test( - { permissions: { net: true } }, - async function fetchInitFormDataBinaryFileBody() { - // Some random bytes - // deno-fmt-ignore - const binaryFile = new Uint8Array([108,2,0,0,145,22,162,61,157,227,166,77,138,75,180,56,119,188,177,183]); - const response = await fetch("http://localhost:4545/echo_multipart_file", { - method: "POST", - body: binaryFile, - }); - const resultForm = await response.formData(); - const resultFile = resultForm.get("file") as File; - - assertEquals(resultFile.type, "application/octet-stream"); - assertEquals(resultFile.name, "file.bin"); - assertEquals(new Uint8Array(await resultFile.arrayBuffer()), binaryFile); - }, -); - -Deno.test( - { permissions: { net: true } }, - async function fetchInitFormDataMultipleFilesBody() { - const files = [ - { - // deno-fmt-ignore - content: new Uint8Array([137,80,78,71,13,10,26,10, 137, 1, 25]), - type: "image/png", - name: "image", - fileName: "some-image.png", - }, - { - // deno-fmt-ignore - content: new Uint8Array([108,2,0,0,145,22,162,61,157,227,166,77,138,75,180,56,119,188,177,183]), - name: "file", - fileName: "file.bin", - expectedType: "application/octet-stream", - }, - { - content: new TextEncoder().encode("deno land"), - type: "text/plain", - name: "text", - fileName: "deno.txt", - }, - ]; - const form = new FormData(); - form.append("field", "value"); - for (const file of files) { - form.append( - file.name, - new Blob([file.content], { type: file.type }), - file.fileName, - ); - } - const response = await fetch("http://localhost:4545/echo_server", { - method: "POST", - body: form, - }); - const resultForm = await response.formData(); - assertEquals(form.get("field"), resultForm.get("field")); - for (const file of files) { - const inputFile = form.get(file.name) as File; - const resultFile = resultForm.get(file.name) as File; - assertEquals(inputFile.size, resultFile.size); - assertEquals(inputFile.name, resultFile.name); - assertEquals(file.expectedType || file.type, resultFile.type); - assertEquals( - new Uint8Array(await resultFile.arrayBuffer()), - file.content, - ); - } - }, -); - -Deno.test( - { - permissions: { net: true }, - }, - async function fetchWithRedirection() { - const response = await fetch("http://localhost:4546/assets/hello.txt"); - assertEquals(response.status, 200); - assertEquals(response.statusText, "OK"); - assertEquals(response.url, "http://localhost:4545/assets/hello.txt"); - const body = await response.text(); - assert(body.includes("Hello world!")); - }, -); - -Deno.test( - { - permissions: { net: true }, - }, - async function fetchWithRelativeRedirection() { - const response = await fetch( - "http://localhost:4545/run/001_hello.js", - ); - assertEquals(response.status, 200); - assertEquals(response.statusText, "OK"); - const body = await response.text(); - assert(body.includes("Hello")); - }, -); - -Deno.test( - { - permissions: { net: true }, - }, - async function fetchWithRelativeRedirectionUrl() { - const cases = [ - ["end", "http://localhost:4550/a/b/end"], - ["/end", "http://localhost:4550/end"], - ]; - for (const [loc, redUrl] of cases) { - const response = await fetch("http://localhost:4550/a/b/c", { - headers: new Headers([["x-location", loc]]), - }); - assertEquals(response.url, redUrl); - assertEquals(response.redirected, true); - assertEquals(response.status, 404); - assertEquals(await response.text(), ""); - } - }, -); - -Deno.test( - { - permissions: { net: true }, - }, - async function fetchWithInfRedirection() { - await assertRejects( - () => fetch("http://localhost:4549"), - TypeError, - "redirect", - ); - }, -); - -Deno.test( - { permissions: { net: true } }, - async function fetchInitStringBody() { - const data = "Hello World"; - const response = await fetch("http://localhost:4545/echo_server", { - method: "POST", - body: data, - }); - const text = await response.text(); - assertEquals(text, data); - assert(response.headers.get("content-type")!.startsWith("text/plain")); - }, -); - -Deno.test( - { permissions: { net: true } }, - async function fetchRequestInitStringBody() { - const data = "Hello World"; - const req = new Request("http://localhost:4545/echo_server", { - method: "POST", - body: data, - }); - const response = await fetch(req); - const text = await response.text(); - assertEquals(text, data); - }, -); - -Deno.test( - { permissions: { net: true } }, - async function fetchSeparateInit() { - // related to: https://github.com/denoland/deno/issues/10396 - const req = new Request("http://localhost:4545/run/001_hello.js"); - const init = { - method: "GET", - }; - req.headers.set("foo", "bar"); - const res = await fetch(req, init); - assertEquals(res.status, 200); - await res.text(); - }, -); - -Deno.test( - { permissions: { net: true } }, - async function fetchInitTypedArrayBody() { - const data = "Hello World"; - const response = await fetch("http://localhost:4545/echo_server", { - method: "POST", - body: new TextEncoder().encode(data), - }); - const text = await response.text(); - assertEquals(text, data); - }, -); - -Deno.test( - { permissions: { net: true } }, - async function fetchInitArrayBufferBody() { - const data = "Hello World"; - const response = await fetch("http://localhost:4545/echo_server", { - method: "POST", - body: new TextEncoder().encode(data).buffer, - }); - const text = await response.text(); - assertEquals(text, data); - }, -); - -Deno.test( - { permissions: { net: true } }, - async function fetchInitURLSearchParamsBody() { - const data = "param1=value1¶m2=value2"; - const params = new URLSearchParams(data); - const response = await fetch("http://localhost:4545/echo_server", { - method: "POST", - body: params, - }); - const text = await response.text(); - assertEquals(text, data); - assert( - response.headers - .get("content-type")! - .startsWith("application/x-www-form-urlencoded"), - ); - }, -); - -Deno.test({ permissions: { net: true } }, async function fetchInitBlobBody() { - const data = "const a = 1 🦕"; - const blob = new Blob([data], { - type: "text/javascript", - }); - const response = await fetch("http://localhost:4545/echo_server", { - method: "POST", - body: blob, - }); - const text = await response.text(); - assertEquals(text, data); - assert(response.headers.get("content-type")!.startsWith("text/javascript")); -}); - -Deno.test( - { permissions: { net: true } }, - async function fetchInitFormDataBody() { - const form = new FormData(); - form.append("field", "value"); - const response = await fetch("http://localhost:4545/echo_server", { - method: "POST", - body: form, - }); - const resultForm = await response.formData(); - assertEquals(form.get("field"), resultForm.get("field")); - }, -); - -Deno.test( - { permissions: { net: true } }, - async function fetchInitFormDataBlobFilenameBody() { - const form = new FormData(); - form.append("field", "value"); - form.append( - "file", - new Blob([new TextEncoder().encode("deno")]), - "file name", - ); - const response = await fetch("http://localhost:4545/echo_server", { - method: "POST", - body: form, - }); - const resultForm = await response.formData(); - assertEquals(form.get("field"), resultForm.get("field")); - const file = resultForm.get("file"); - assert(file instanceof File); - assertEquals(file.name, "file name"); - }, -); - -Deno.test( - { permissions: { net: true } }, - async function fetchInitFormDataFileFilenameBody() { - const form = new FormData(); - form.append("field", "value"); - form.append( - "file", - new File([new Blob([new TextEncoder().encode("deno")])], "file name"), - ); - const response = await fetch("http://localhost:4545/echo_server", { - method: "POST", - body: form, - }); - const resultForm = await response.formData(); - assertEquals(form.get("field"), resultForm.get("field")); - const file = resultForm.get("file"); - assert(file instanceof File); - assertEquals(file.name, "file name"); - }, -); - -Deno.test( - { permissions: { net: true } }, - async function fetchInitFormDataTextFileBody() { - const fileContent = "deno land"; - const form = new FormData(); - form.append("field", "value"); - form.append( - "file", - new Blob([new TextEncoder().encode(fileContent)], { - type: "text/plain", - }), - "deno.txt", - ); - const response = await fetch("http://localhost:4545/echo_server", { - method: "POST", - body: form, - }); - const resultForm = await response.formData(); - assertEquals(form.get("field"), resultForm.get("field")); - - const file = form.get("file") as File; - const resultFile = resultForm.get("file") as File; - - assertEquals(file.size, resultFile.size); - assertEquals(file.name, resultFile.name); - assertEquals(file.type, resultFile.type); - assertEquals(await file.text(), await resultFile.text()); - }, -); - -Deno.test({ permissions: { net: true } }, async function fetchUserAgent() { - const data = "Hello World"; - const response = await fetch("http://localhost:4545/echo_server", { - method: "POST", - body: new TextEncoder().encode(data), - }); - assertEquals(response.headers.get("user-agent"), `Deno/${Deno.version.deno}`); - await response.text(); -}); - -function bufferServer(addr: string): Promise<Buffer> { - const [hostname, port] = addr.split(":"); - const listener = Deno.listen({ - hostname, - port: Number(port), - }) as Deno.Listener; - return listener.accept().then(async (conn: Deno.Conn) => { - const buf = new Buffer(); - const p1 = buf.readFrom(conn); - const p2 = conn.write( - new TextEncoder().encode( - "HTTP/1.0 404 Not Found\r\nContent-Length: 2\r\n\r\nNF", - ), - ); - // Wait for both an EOF on the read side of the socket and for the write to - // complete before closing it. Due to keep-alive, the EOF won't be sent - // until the Connection close (HTTP/1.0) response, so readFrom() can't - // proceed write. Conversely, if readFrom() is async, waiting for the - // write() to complete is not a guarantee that we've read the incoming - // request. - await Promise.all([p1, p2]); - conn.close(); - listener.close(); - return buf; - }); -} - -Deno.test( - { - permissions: { net: true }, - }, - async function fetchRequest() { - const addr = `127.0.0.1:${listenPort}`; - const bufPromise = bufferServer(addr); - const response = await fetch(`http://${addr}/blah`, { - method: "POST", - headers: [ - ["Hello", "World"], - ["Foo", "Bar"], - ], - }); - await response.arrayBuffer(); - assertEquals(response.status, 404); - assertEquals(response.headers.get("Content-Length"), "2"); - - const actual = new TextDecoder().decode((await bufPromise).bytes()); - const expected = [ - "POST /blah HTTP/1.1\r\n", - "content-length: 0\r\n", - "hello: World\r\n", - "foo: Bar\r\n", - "accept: */*\r\n", - "accept-language: *\r\n", - `user-agent: Deno/${Deno.version.deno}\r\n`, - "accept-encoding: gzip, br\r\n", - `host: ${addr}\r\n\r\n`, - ].join(""); - assertEquals(actual, expected); - }, -); - -Deno.test( - { - permissions: { net: true }, - }, - async function fetchRequestAcceptHeaders() { - const addr = `127.0.0.1:${listenPort}`; - const bufPromise = bufferServer(addr); - const response = await fetch(`http://${addr}/blah`, { - method: "POST", - headers: [ - ["Accept", "text/html"], - ["Accept-Language", "en-US"], - ], - }); - await response.arrayBuffer(); - assertEquals(response.status, 404); - assertEquals(response.headers.get("Content-Length"), "2"); - - const actual = new TextDecoder().decode((await bufPromise).bytes()); - const expected = [ - "POST /blah HTTP/1.1\r\n", - "content-length: 0\r\n", - "accept: text/html\r\n", - "accept-language: en-US\r\n", - `user-agent: Deno/${Deno.version.deno}\r\n`, - "accept-encoding: gzip, br\r\n", - `host: ${addr}\r\n\r\n`, - ].join(""); - assertEquals(actual, expected); - }, -); - -Deno.test( - { - permissions: { net: true }, - }, - async function fetchPostBodyString() { - const addr = `127.0.0.1:${listenPort}`; - const bufPromise = bufferServer(addr); - const body = "hello world"; - const response = await fetch(`http://${addr}/blah`, { - method: "POST", - headers: [ - ["Hello", "World"], - ["Foo", "Bar"], - ], - body, - }); - await response.arrayBuffer(); - assertEquals(response.status, 404); - assertEquals(response.headers.get("Content-Length"), "2"); - - const actual = new TextDecoder().decode((await bufPromise).bytes()); - const expected = [ - "POST /blah HTTP/1.1\r\n", - "hello: World\r\n", - "foo: Bar\r\n", - "content-type: text/plain;charset=UTF-8\r\n", - "accept: */*\r\n", - "accept-language: *\r\n", - `user-agent: Deno/${Deno.version.deno}\r\n`, - "accept-encoding: gzip, br\r\n", - `host: ${addr}\r\n`, - `content-length: ${body.length}\r\n\r\n`, - body, - ].join(""); - assertEquals(actual, expected); - }, -); - -Deno.test( - { - permissions: { net: true }, - }, - async function fetchPostBodyTypedArray() { - const addr = `127.0.0.1:${listenPort}`; - const bufPromise = bufferServer(addr); - const bodyStr = "hello world"; - const body = new TextEncoder().encode(bodyStr); - const response = await fetch(`http://${addr}/blah`, { - method: "POST", - headers: [ - ["Hello", "World"], - ["Foo", "Bar"], - ], - body, - }); - await response.arrayBuffer(); - assertEquals(response.status, 404); - assertEquals(response.headers.get("Content-Length"), "2"); - - const actual = new TextDecoder().decode((await bufPromise).bytes()); - const expected = [ - "POST /blah HTTP/1.1\r\n", - "hello: World\r\n", - "foo: Bar\r\n", - "accept: */*\r\n", - "accept-language: *\r\n", - `user-agent: Deno/${Deno.version.deno}\r\n`, - "accept-encoding: gzip, br\r\n", - `host: ${addr}\r\n`, - `content-length: ${body.byteLength}\r\n\r\n`, - bodyStr, - ].join(""); - assertEquals(actual, expected); - }, -); - -Deno.test( - { - permissions: { net: true }, - }, - async function fetchUserSetContentLength() { - const addr = `127.0.0.1:${listenPort}`; - const bufPromise = bufferServer(addr); - const response = await fetch(`http://${addr}/blah`, { - method: "POST", - headers: [ - ["Content-Length", "10"], - ], - }); - await response.arrayBuffer(); - assertEquals(response.status, 404); - assertEquals(response.headers.get("Content-Length"), "2"); - - const actual = new TextDecoder().decode((await bufPromise).bytes()); - const expected = [ - "POST /blah HTTP/1.1\r\n", - "content-length: 0\r\n", - "accept: */*\r\n", - "accept-language: *\r\n", - `user-agent: Deno/${Deno.version.deno}\r\n`, - "accept-encoding: gzip, br\r\n", - `host: ${addr}\r\n\r\n`, - ].join(""); - assertEquals(actual, expected); - }, -); - -Deno.test( - { - permissions: { net: true }, - }, - async function fetchUserSetTransferEncoding() { - const addr = `127.0.0.1:${listenPort}`; - const bufPromise = bufferServer(addr); - const response = await fetch(`http://${addr}/blah`, { - method: "POST", - headers: [ - ["Transfer-Encoding", "chunked"], - ], - }); - await response.arrayBuffer(); - assertEquals(response.status, 404); - assertEquals(response.headers.get("Content-Length"), "2"); - - const actual = new TextDecoder().decode((await bufPromise).bytes()); - const expected = [ - "POST /blah HTTP/1.1\r\n", - "content-length: 0\r\n", - `host: ${addr}\r\n`, - "accept: */*\r\n", - "accept-language: *\r\n", - `user-agent: Deno/${Deno.version.deno}\r\n`, - "accept-encoding: gzip, br\r\n\r\n", - ].join(""); - assertEquals(actual, expected); - }, -); - -Deno.test( - { - permissions: { net: true }, - }, - async function fetchWithNonAsciiRedirection() { - const response = await fetch("http://localhost:4545/non_ascii_redirect", { - redirect: "manual", - }); - assertEquals(response.status, 301); - assertEquals(response.headers.get("location"), "/redirect®"); - await response.text(); - }, -); - -Deno.test( - { - permissions: { net: true }, - }, - async function fetchWithManualRedirection() { - const response = await fetch("http://localhost:4546/", { - redirect: "manual", - }); // will redirect to http://localhost:4545/ - assertEquals(response.status, 301); - assertEquals(response.url, "http://localhost:4546/"); - assertEquals(response.type, "basic"); - assertEquals(response.headers.get("Location"), "http://localhost:4545/"); - await response.body!.cancel(); - }, -); - -Deno.test( - { - permissions: { net: true }, - }, - async function fetchWithErrorRedirection() { - await assertRejects( - () => - fetch("http://localhost:4546/", { - redirect: "error", - }), - TypeError, - "redirect", - ); - }, -); - -Deno.test(function responseRedirect() { - const redir = Response.redirect("http://example.com/newLocation", 301); - assertEquals(redir.status, 301); - assertEquals(redir.statusText, ""); - assertEquals(redir.url, ""); - assertEquals( - redir.headers.get("Location"), - "http://example.com/newLocation", - ); - assertEquals(redir.type, "default"); -}); - -Deno.test(function responseRedirectTakeURLObjectAsParameter() { - const redir = Response.redirect(new URL("https://example.com/")); - assertEquals( - redir.headers.get("Location"), - "https://example.com/", - ); -}); - -Deno.test(async function responseWithoutBody() { - const response = new Response(); - assertEquals(await response.arrayBuffer(), new ArrayBuffer(0)); - const blob = await response.blob(); - assertEquals(blob.size, 0); - assertEquals(await blob.arrayBuffer(), new ArrayBuffer(0)); - assertEquals(await response.text(), ""); - await assertRejects(async () => { - await response.json(); - }); -}); - -Deno.test({ permissions: { net: true } }, async function fetchBodyReadTwice() { - const response = await fetch("http://localhost:4545/assets/fixture.json"); - - // Read body - const _json = await response.json(); - assert(_json); - - // All calls after the body was consumed, should fail - const methods = ["json", "text", "formData", "arrayBuffer"] as const; - for (const method of methods) { - try { - await response[method](); - fail( - "Reading body multiple times should failed, the stream should've been locked.", - ); - } catch { - // pass - } - } -}); - -Deno.test( - { permissions: { net: true } }, - async function fetchBodyReaderAfterRead() { - const response = await fetch( - "http://localhost:4545/assets/fixture.json", - ); - assert(response.body !== null); - const reader = await response.body.getReader(); - while (true) { - const { done, value } = await reader.read(); - if (done) break; - assert(value); - } - - try { - response.body.getReader(); - fail("The stream should've been locked."); - } catch { - // pass - } - }, -); - -Deno.test( - { permissions: { net: true } }, - async function fetchBodyReaderWithCancelAndNewReader() { - const data = "a".repeat(1 << 10); - const response = await fetch("http://localhost:4545/echo_server", { - method: "POST", - body: data, - }); - assert(response.body !== null); - const firstReader = await response.body.getReader(); - - // Acquire reader without reading & release - await firstReader.releaseLock(); - - const reader = await response.body.getReader(); - - let total = 0; - while (true) { - const { done, value } = await reader.read(); - if (done) break; - assert(value); - total += value.length; - } - - assertEquals(total, data.length); - }, -); - -Deno.test( - { permissions: { net: true } }, - async function fetchBodyReaderWithReadCancelAndNewReader() { - const data = "a".repeat(1 << 10); - - const response = await fetch("http://localhost:4545/echo_server", { - method: "POST", - body: data, - }); - assert(response.body !== null); - const firstReader = await response.body.getReader(); - - // Do one single read with first reader - const { value: firstValue } = await firstReader.read(); - assert(firstValue); - await firstReader.releaseLock(); - - // Continue read with second reader - const reader = await response.body.getReader(); - let total = firstValue.length || 0; - while (true) { - const { done, value } = await reader.read(); - if (done) break; - assert(value); - total += value.length; - } - assertEquals(total, data.length); - }, -); - -Deno.test( - { permissions: { net: true } }, - async function fetchResourceCloseAfterStreamCancel() { - const res = await fetch("http://localhost:4545/assets/fixture.json"); - assert(res.body !== null); - - // After ReadableStream.cancel is called, resource handle must be closed - // The test should not fail with: Test case is leaking resources - await res.body.cancel(); - }, -); - -Deno.test( - { permissions: { net: true } }, - async function fetchNullBodyStatus() { - const nullBodyStatus = [101, 204, 205, 304]; - - for (const status of nullBodyStatus) { - const headers = new Headers([["x-status", String(status)]]); - const res = await fetch("http://localhost:4545/echo_server", { - body: "deno", - method: "POST", - headers, - }); - assertEquals(res.body, null); - assertEquals(res.status, status); - } - }, -); - -Deno.test( - { permissions: { net: true } }, - async function fetchResponseContentLength() { - 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); - }, -); - -Deno.test(function fetchResponseConstructorNullBody() { - const nullBodyStatus = [204, 205, 304]; - - for (const status of nullBodyStatus) { - try { - new Response("deno", { status }); - fail("Response with null body status cannot have body"); - } catch (e) { - assert(e instanceof TypeError); - assertEquals( - e.message, - "Response with null body status cannot have body", - ); - } - } -}); - -Deno.test(function fetchResponseConstructorInvalidStatus() { - const invalidStatus = [100, 600, 199, null, "", NaN]; - - for (const status of invalidStatus) { - try { - // deno-lint-ignore ban-ts-comment - // @ts-ignore - new Response("deno", { status }); - fail(`Invalid status: ${status}`); - } catch (e) { - assert(e instanceof RangeError); - assert( - e.message.endsWith( - "is not equal to 101 and outside the range [200, 599].", - ), - ); - } - } -}); - -Deno.test(function fetchResponseEmptyConstructor() { - const response = new Response(); - assertEquals(response.status, 200); - assertEquals(response.body, null); - assertEquals(response.type, "default"); - assertEquals(response.url, ""); - assertEquals(response.redirected, false); - assertEquals(response.ok, true); - assertEquals(response.bodyUsed, false); - assertEquals([...response.headers], []); -}); - -Deno.test( - { permissions: { net: true, read: true } }, - async function fetchCustomHttpClientParamCertificateSuccess(): Promise< - void - > { - const caCert = Deno.readTextFileSync("cli/tests/testdata/tls/RootCA.pem"); - const client = Deno.createHttpClient({ caCerts: [caCert] }); - const response = await fetch("https://localhost:5545/assets/fixture.json", { - client, - }); - const json = await response.json(); - assertEquals(json.name, "deno"); - client.close(); - }, -); - -Deno.test( - { permissions: { net: true, read: true } }, - function createHttpClientAcceptPoolIdleTimeout() { - const client = Deno.createHttpClient({ - poolIdleTimeout: 1000, - }); - client.close(); - }, -); - -Deno.test( - { permissions: { net: true } }, - async function fetchCustomClientUserAgent(): Promise< - void - > { - const data = "Hello World"; - const client = Deno.createHttpClient({}); - const response = await fetch("http://localhost:4545/echo_server", { - client, - method: "POST", - body: new TextEncoder().encode(data), - }); - assertEquals( - response.headers.get("user-agent"), - `Deno/${Deno.version.deno}`, - ); - await response.text(); - client.close(); - }, -); - -Deno.test( - { - permissions: { net: true }, - }, - async function fetchPostBodyReadableStream() { - const addr = `127.0.0.1:${listenPort}`; - const bufPromise = bufferServer(addr); - const stream = new TransformStream(); - const writer = stream.writable.getWriter(); - // transformer writes don't resolve until they are read, so awaiting these - // will cause the transformer to hang, as the suspend the transformer, it - // is also illogical to await for the reads, as that is the whole point of - // streams is to have a "queue" which gets drained... - writer.write(new TextEncoder().encode("hello ")); - writer.write(new TextEncoder().encode("world")); - writer.close(); - const response = await fetch(`http://${addr}/blah`, { - method: "POST", - headers: [ - ["Hello", "World"], - ["Foo", "Bar"], - ], - body: stream.readable, - }); - await response.arrayBuffer(); - assertEquals(response.status, 404); - assertEquals(response.headers.get("Content-Length"), "2"); - - const actual = new TextDecoder().decode((await bufPromise).bytes()); - const expected = [ - "POST /blah HTTP/1.1\r\n", - "hello: World\r\n", - "foo: Bar\r\n", - "accept: */*\r\n", - "accept-language: *\r\n", - `user-agent: Deno/${Deno.version.deno}\r\n`, - "accept-encoding: gzip, br\r\n", - `host: ${addr}\r\n`, - `transfer-encoding: chunked\r\n\r\n`, - "B\r\n", - "hello world\r\n", - "0\r\n\r\n", - ].join(""); - assertEquals(actual, expected); - }, -); - -Deno.test({}, function fetchWritableRespProps() { - const original = new Response("https://deno.land", { - status: 404, - headers: { "x-deno": "foo" }, - }); - const new_ = new Response("https://deno.land", original); - assertEquals(original.status, new_.status); - assertEquals(new_.headers.get("x-deno"), "foo"); -}); - -Deno.test( - { permissions: { net: true } }, - async function fetchFilterOutCustomHostHeader(): Promise< - void - > { - const addr = `127.0.0.1:${listenPort}`; - const [hostname, port] = addr.split(":"); - const listener = Deno.listen({ - hostname, - port: Number(port), - }) as Deno.Listener; - - let httpConn: Deno.HttpConn; - listener.accept().then(async (conn: Deno.Conn) => { - httpConn = Deno.serveHttp(conn); - - await httpConn.nextRequest() - .then(async (requestEvent: Deno.RequestEvent | null) => { - const hostHeader = requestEvent?.request.headers.get("Host"); - const headersToReturn = hostHeader - ? { "Host": hostHeader } - : undefined; - - await requestEvent?.respondWith( - new Response("", { - status: 200, - headers: headersToReturn, - }), - ); - }); - }); - - const response = await fetch(`http://${addr}/`, { - headers: { "Host": "example.com" }, - }); - await response.text(); - listener.close(); - httpConn!.close(); - - assertEquals(response.headers.get("Host"), addr); - }, -); - -Deno.test( - { permissions: { net: true } }, - async function fetchNoServerReadableStreamBody() { - const completed = Promise.withResolvers<void>(); - const failed = Promise.withResolvers<void>(); - const body = new ReadableStream({ - start(controller) { - controller.enqueue(new Uint8Array([1])); - setTimeout(async () => { - // This is technically a race. If the fetch has failed by this point, the enqueue will - // throw. If not, it will succeed. Windows appears to take a while to time out the fetch, - // so we will just wait for that here before we attempt to enqueue so it's consistent - // across platforms. - await failed.promise; - assertThrows(() => controller.enqueue(new Uint8Array([2]))); - completed.resolve(); - }, 1000); - }, - }); - const nonExistentHostname = "http://localhost:47582"; - await assertRejects(async () => { - await fetch(nonExistentHostname, { body, method: "POST" }); - }, TypeError); - failed.resolve(); - await completed.promise; - }, -); - -Deno.test( - { permissions: { net: true } }, - async function fetchHeadRespBody() { - const res = await fetch("http://localhost:4545/echo_server", { - method: "HEAD", - }); - assertEquals(res.body, null); - }, -); - -Deno.test( - { permissions: { read: true, net: true } }, - async function fetchClientCertWrongPrivateKey(): Promise<void> { - await assertRejects(async () => { - const client = Deno.createHttpClient({ - certChain: "bad data", - privateKey: await Deno.readTextFile( - "cli/tests/testdata/tls/localhost.key", - ), - }); - await fetch("https://localhost:5552/assets/fixture.json", { - client, - }); - }, Deno.errors.InvalidData); - }, -); - -Deno.test( - { permissions: { read: true, net: true } }, - async function fetchClientCertBadPrivateKey(): Promise<void> { - await assertRejects(async () => { - const client = Deno.createHttpClient({ - certChain: await Deno.readTextFile( - "cli/tests/testdata/tls/localhost.crt", - ), - privateKey: "bad data", - }); - await fetch("https://localhost:5552/assets/fixture.json", { - client, - }); - }, Deno.errors.InvalidData); - }, -); - -Deno.test( - { permissions: { read: true, net: true } }, - async function fetchClientCertNotPrivateKey(): Promise<void> { - await assertRejects(async () => { - const client = Deno.createHttpClient({ - certChain: await Deno.readTextFile( - "cli/tests/testdata/tls/localhost.crt", - ), - privateKey: "", - }); - await fetch("https://localhost:5552/assets/fixture.json", { - client, - }); - }, Deno.errors.InvalidData); - }, -); - -Deno.test( - { permissions: { read: true, net: true } }, - async function fetchCustomClientPrivateKey(): Promise< - void - > { - const data = "Hello World"; - const caCert = await Deno.readTextFile("cli/tests/testdata/tls/RootCA.crt"); - const client = Deno.createHttpClient({ - certChain: await Deno.readTextFile( - "cli/tests/testdata/tls/localhost.crt", - ), - privateKey: await Deno.readTextFile( - "cli/tests/testdata/tls/localhost.key", - ), - caCerts: [caCert], - }); - const response = await fetch("https://localhost:5552/echo_server", { - client, - method: "POST", - body: new TextEncoder().encode(data), - }); - assertEquals( - response.headers.get("user-agent"), - `Deno/${Deno.version.deno}`, - ); - await response.text(); - client.close(); - }, -); - -Deno.test( - { permissions: { net: true } }, - async function fetchAbortWhileUploadStreaming(): Promise<void> { - const abortController = new AbortController(); - try { - await fetch( - "http://localhost:5552/echo_server", - { - method: "POST", - body: new ReadableStream({ - pull(controller) { - abortController.abort(); - controller.enqueue(new Uint8Array([1, 2, 3, 4])); - }, - }), - signal: abortController.signal, - }, - ); - fail("Fetch didn't reject."); - } catch (error) { - assert(error instanceof DOMException); - assertEquals(error.name, "AbortError"); - assertEquals(error.message, "The signal has been aborted"); - } - }, -); - -Deno.test( - { permissions: { net: true } }, - async function fetchAbortWhileUploadStreamingWithReason(): Promise<void> { - const abortController = new AbortController(); - const abortReason = new Error(); - try { - await fetch( - "http://localhost:5552/echo_server", - { - method: "POST", - body: new ReadableStream({ - pull(controller) { - abortController.abort(abortReason); - controller.enqueue(new Uint8Array([1, 2, 3, 4])); - }, - }), - signal: abortController.signal, - }, - ); - fail("Fetch didn't reject."); - } catch (error) { - assertEquals(error, abortReason); - } - }, -); - -Deno.test( - { permissions: { net: true } }, - async function fetchAbortWhileUploadStreamingWithPrimitiveReason(): Promise< - void - > { - const abortController = new AbortController(); - try { - await fetch( - "http://localhost:5552/echo_server", - { - method: "POST", - body: new ReadableStream({ - pull(controller) { - abortController.abort("Abort reason"); - controller.enqueue(new Uint8Array([1, 2, 3, 4])); - }, - }), - signal: abortController.signal, - }, - ); - fail("Fetch didn't reject."); - } catch (error) { - assertEquals(error, "Abort reason"); - } - }, -); - -Deno.test( - { permissions: { net: true } }, - async function fetchHeaderValueShouldNotPanic() { - for (let i = 0; i < 0x21; i++) { - if (i === 0x09 || i === 0x0A || i === 0x0D || i === 0x20) { - continue; // these header value will be normalized, will not cause an error. - } - // ensure there will be an error instead of panic. - await assertRejects(() => - fetch("http://localhost:4545/echo_server", { - method: "HEAD", - headers: { "val": String.fromCharCode(i) }, - }), TypeError); - } - await assertRejects(() => - fetch("http://localhost:4545/echo_server", { - method: "HEAD", - headers: { "val": String.fromCharCode(127) }, - }), TypeError); - }, -); - -Deno.test( - { permissions: { net: true } }, - async function fetchHeaderNameShouldNotPanic() { - const validTokens = - "!#$%&'*+-.0123456789ABCDEFGHIJKLMNOPQRSTUWVXYZ^_`abcdefghijklmnopqrstuvwxyz|~" - .split(""); - for (let i = 0; i <= 255; i++) { - const token = String.fromCharCode(i); - if (validTokens.includes(token)) { - continue; - } - // ensure there will be an error instead of panic. - await assertRejects(() => - fetch("http://localhost:4545/echo_server", { - method: "HEAD", - headers: { [token]: "value" }, - }), TypeError); - } - await assertRejects(() => - fetch("http://localhost:4545/echo_server", { - method: "HEAD", - headers: { "": "value" }, - }), TypeError); - }, -); - -Deno.test( - { permissions: { net: true, read: true } }, - async function fetchSupportsHttpsOverIpAddress() { - const caCert = await Deno.readTextFile("cli/tests/testdata/tls/RootCA.pem"); - const client = Deno.createHttpClient({ caCerts: [caCert] }); - const res = await fetch("https://localhost:5546/http_version", { client }); - assert(res.ok); - assertEquals(await res.text(), "HTTP/1.1"); - client.close(); - }, -); - -Deno.test( - { permissions: { net: true, read: true } }, - async function fetchSupportsHttp1Only() { - const caCert = await Deno.readTextFile("cli/tests/testdata/tls/RootCA.pem"); - const client = Deno.createHttpClient({ caCerts: [caCert] }); - const res = await fetch("https://localhost:5546/http_version", { client }); - assert(res.ok); - assertEquals(await res.text(), "HTTP/1.1"); - client.close(); - }, -); - -Deno.test( - { permissions: { net: true, read: true } }, - async function fetchSupportsHttp2() { - const caCert = await Deno.readTextFile("cli/tests/testdata/tls/RootCA.pem"); - const client = Deno.createHttpClient({ caCerts: [caCert] }); - const res = await fetch("https://localhost:5547/http_version", { client }); - assert(res.ok); - assertEquals(await res.text(), "HTTP/2.0"); - client.close(); - }, -); - -Deno.test( - { permissions: { net: true, read: true } }, - async function fetchForceHttp1OnHttp2Server() { - const client = Deno.createHttpClient({ http2: false, http1: true }); - await assertRejects( - () => fetch("http://localhost:5549/http_version", { client }), - TypeError, - ); - client.close(); - }, -); - -Deno.test( - { permissions: { net: true, read: true } }, - async function fetchForceHttp2OnHttp1Server() { - const client = Deno.createHttpClient({ http2: true, http1: false }); - await assertRejects( - () => fetch("http://localhost:5548/http_version", { client }), - TypeError, - ); - client.close(); - }, -); - -Deno.test( - { permissions: { net: true, read: true } }, - async function fetchPrefersHttp2() { - const caCert = await Deno.readTextFile("cli/tests/testdata/tls/RootCA.pem"); - const client = Deno.createHttpClient({ caCerts: [caCert] }); - const res = await fetch("https://localhost:5545/http_version", { client }); - assert(res.ok); - assertEquals(await res.text(), "HTTP/2.0"); - client.close(); - }, -); - -Deno.test( - { permissions: { net: true, read: true } }, - async function createHttpClientAllowHost() { - const client = Deno.createHttpClient({ - allowHost: true, - }); - const res = await fetch("http://localhost:4545/echo_server", { - headers: { - "host": "example.com", - }, - client, - }); - assert(res.ok); - assertEquals(res.headers.get("host"), "example.com"); - await res.body?.cancel(); - client.close(); - }, -); - -Deno.test( - { permissions: { net: true } }, - async function createHttpClientExplicitResourceManagement() { - using client = Deno.createHttpClient({}); - const response = await fetch("http://localhost:4545/assets/fixture.json", { - client, - }); - const json = await response.json(); - assertEquals(json.name, "deno"); - }, -); - -Deno.test( - { permissions: { net: true } }, - async function createHttpClientExplicitResourceManagementDoubleClose() { - using client = Deno.createHttpClient({}); - const response = await fetch("http://localhost:4545/assets/fixture.json", { - client, - }); - const json = await response.json(); - assertEquals(json.name, "deno"); - // Close the client even though we declared it with `using` to confirm that - // the cleanup done as per `Symbol.dispose` will not throw any errors. - client.close(); - }, -); - -Deno.test({ permissions: { read: false } }, async function fetchFilePerm() { - await assertRejects(async () => { - await fetch(import.meta.resolve("../testdata/subdir/json_1.json")); - }, Deno.errors.PermissionDenied); -}); - -Deno.test( - { permissions: { read: false } }, - async function fetchFilePermDoesNotExist() { - await assertRejects(async () => { - await fetch(import.meta.resolve("./bad.json")); - }, Deno.errors.PermissionDenied); - }, -); - -Deno.test( - { permissions: { read: true } }, - async function fetchFileBadMethod() { - await assertRejects( - async () => { - await fetch( - import.meta.resolve("../testdata/subdir/json_1.json"), - { - method: "POST", - }, - ); - }, - TypeError, - "Fetching files only supports the GET method. Received POST.", - ); - }, -); - -Deno.test( - { permissions: { read: true } }, - async function fetchFileDoesNotExist() { - await assertRejects( - async () => { - await fetch(import.meta.resolve("./bad.json")); - }, - TypeError, - ); - }, -); - -Deno.test( - { permissions: { read: true } }, - async function fetchFile() { - const res = await fetch( - import.meta.resolve("../testdata/subdir/json_1.json"), - ); - assert(res.ok); - const fixture = await Deno.readTextFile( - "cli/tests/testdata/subdir/json_1.json", - ); - assertEquals(await res.text(), fixture); - }, -); - -Deno.test( - { permissions: { net: true } }, - async function fetchContentLengthPost() { - const response = await fetch("http://localhost:4545/content_length", { - method: "POST", - }); - const length = await response.text(); - assertEquals(length, 'Some("0")'); - }, -); - -Deno.test( - { permissions: { net: true } }, - async function fetchContentLengthPut() { - const response = await fetch("http://localhost:4545/content_length", { - method: "PUT", - }); - const length = await response.text(); - assertEquals(length, 'Some("0")'); - }, -); - -Deno.test( - { permissions: { net: true } }, - async function fetchContentLengthPatch() { - const response = await fetch("http://localhost:4545/content_length", { - method: "PATCH", - }); - const length = await response.text(); - assertEquals(length, "None"); - }, -); - -Deno.test( - { permissions: { net: true } }, - async function fetchContentLengthPostWithStringBody() { - const response = await fetch("http://localhost:4545/content_length", { - method: "POST", - body: "Hey!", - }); - const length = await response.text(); - assertEquals(length, 'Some("4")'); - }, -); - -Deno.test( - { permissions: { net: true } }, - async function fetchContentLengthPostWithBufferBody() { - const response = await fetch("http://localhost:4545/content_length", { - method: "POST", - body: new TextEncoder().encode("Hey!"), - }); - const length = await response.text(); - assertEquals(length, 'Some("4")'); - }, -); - -Deno.test(async function staticResponseJson() { - const data = { hello: "world" }; - const resp = Response.json(data); - assertEquals(resp.status, 200); - assertEquals(resp.headers.get("content-type"), "application/json"); - const res = await resp.json(); - assertEquals(res, data); -}); - -function invalidServer(addr: string, body: Uint8Array): Deno.Listener { - const [hostname, port] = addr.split(":"); - const listener = Deno.listen({ - hostname, - port: Number(port), - }) as Deno.Listener; - - (async () => { - for await (const conn of listener) { - const p1 = conn.read(new Uint8Array(2 ** 14)); - const p2 = conn.write(body); - - await Promise.all([p1, p2]); - conn.close(); - } - })(); - - return listener; -} - -Deno.test( - { permissions: { net: true } }, - async function fetchWithInvalidContentLengthAndTransferEncoding(): Promise< - void - > { - const addr = `127.0.0.1:${listenPort}`; - const data = "a".repeat(10 << 10); - - const body = new TextEncoder().encode( - `HTTP/1.1 200 OK\r\nContent-Length: ${ - Math.round(data.length * 2) - }\r\nTransfer-Encoding: chunked\r\n\r\n${ - data.length.toString(16) - }\r\n${data}\r\n0\r\n\r\n`, - ); - - // if transfer-encoding is sent, content-length is ignored - // even if it has an invalid value (content-length > totalLength) - const listener = invalidServer(addr, body); - const response = await fetch(`http://${addr}/`); - - const res = await response.arrayBuffer(); - const buf = new TextEncoder().encode(data); - assertEquals(res.byteLength, buf.byteLength); - assertEquals(new Uint8Array(res), buf); - - listener.close(); - }, -); - -Deno.test( - // TODO(bartlomieju): reenable this test - // https://github.com/denoland/deno/issues/18350 - { ignore: Deno.build.os === "windows", permissions: { net: true } }, - async function fetchWithInvalidContentLength(): Promise< - void - > { - const addr = `127.0.0.1:${listenPort}`; - const data = "a".repeat(10 << 10); - - const body = new TextEncoder().encode( - `HTTP/1.1 200 OK\r\nContent-Length: ${ - Math.round(data.length / 2) - }\r\nContent-Length: ${data.length}\r\n\r\n${data}`, - ); - - // It should fail if multiple content-length headers with different values are sent - const listener = invalidServer(addr, body); - await assertRejects( - async () => { - await fetch(`http://${addr}/`); - }, - TypeError, - "invalid content-length parsed", - ); - - listener.close(); - }, -); - -Deno.test( - { permissions: { net: true } }, - async function fetchWithInvalidContentLength2(): Promise< - void - > { - const addr = `127.0.0.1:${listenPort}`; - const data = "a".repeat(10 << 10); - - const contentLength = data.length / 2; - const body = new TextEncoder().encode( - `HTTP/1.1 200 OK\r\nContent-Length: ${contentLength}\r\n\r\n${data}`, - ); - - const listener = invalidServer(addr, body); - const response = await fetch(`http://${addr}/`); - - // If content-length < totalLength, a maximum of content-length bytes - // should be returned. - const res = await response.arrayBuffer(); - const buf = new TextEncoder().encode(data); - assertEquals(res.byteLength, contentLength); - assertEquals(new Uint8Array(res), buf.subarray(contentLength)); - - listener.close(); - }, -); - -Deno.test( - { permissions: { net: true } }, - async function fetchWithInvalidContentLength3(): Promise< - void - > { - const addr = `127.0.0.1:${listenPort}`; - const data = "a".repeat(10 << 10); - - const contentLength = data.length * 2; - const body = new TextEncoder().encode( - `HTTP/1.1 200 OK\r\nContent-Length: ${contentLength}\r\n\r\n${data}`, - ); - - const listener = invalidServer(addr, body); - const response = await fetch(`http://${addr}/`); - // If content-length > totalLength, a maximum of content-length bytes - // should be returned. - await assertRejects( - async () => { - await response.arrayBuffer(); - }, - Error, - "end of file before message length reached", - ); - - listener.close(); - }, -); - -Deno.test( - { permissions: { net: true } }, - async function fetchBlobUrl(): Promise<void> { - const blob = new Blob(["ok"], { type: "text/plain" }); - const url = URL.createObjectURL(blob); - assert(url.startsWith("blob:"), `URL was ${url}`); - const res = await fetch(url); - assertEquals(res.url, url); - assertEquals(res.status, 200); - assertEquals(res.headers.get("content-length"), "2"); - assertEquals(res.headers.get("content-type"), "text/plain"); - 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; - }, -); - -Deno.test( - { permissions: { net: true } }, - async function fetchConstructorClones() { - const req = new Request("https://example.com", { - method: "POST", - body: "foo", - }); - assertEquals(await req.text(), "foo"); - await assertRejects(() => req.text()); - - const req2 = new Request(req, { method: "PUT", body: "bar" }); // should not have any impact on req - await assertRejects(() => req.text()); - assertEquals(await req2.text(), "bar"); - - assertEquals(req.method, "POST"); - assertEquals(req2.method, "PUT"); - - assertEquals(req.headers.get("x-foo"), null); - assertEquals(req2.headers.get("x-foo"), null); - req2.headers.set("x-foo", "bar"); // should not have any impact on req - assertEquals(req.headers.get("x-foo"), null); - assertEquals(req2.headers.get("x-foo"), "bar"); - }, -); - -Deno.test( - // TODO(bartlomieju): reenable this test - // https://github.com/denoland/deno/issues/18350 - { ignore: Deno.build.os === "windows", permissions: { net: true } }, - async function fetchRequestBodyErrorCatchable() { - const listener = Deno.listen({ hostname: "127.0.0.1", port: listenPort }); - const server = (async () => { - const conn = await listener.accept(); - listener.close(); - const buf = new Uint8Array(256); - const n = await conn.read(buf); - const data = new TextDecoder().decode(buf.subarray(0, n!)); // this is the request headers + first body chunk - assert(data.startsWith("POST / HTTP/1.1\r\n")); - assert(data.endsWith("1\r\na\r\n")); - const n2 = await conn.read(buf); - assertEquals(n2, 6); // this is the second body chunk - const n3 = await conn.read(buf); - assertEquals(n3, null); // the connection now abruptly closes because the client has errored - conn.close(); - })(); - - const stream = new ReadableStream({ - async start(controller) { - controller.enqueue(new TextEncoder().encode("a")); - await delay(1000); - controller.enqueue(new TextEncoder().encode("b")); - await delay(1000); - controller.error(new Error("foo")); - }, - }); - - const err = await assertRejects(() => - fetch(`http://localhost:${listenPort}/`, { - body: stream, - method: "POST", - }) - ); - - assert(err instanceof TypeError, `err was not a TypeError ${err}`); - assert(err.cause, `err.cause was null ${err}`); - assert( - err.cause instanceof Error, - `err.cause was not an Error ${err.cause}`, - ); - assertEquals(err.cause.message, "foo"); - - await server; - }, -); - -Deno.test( - { permissions: { net: true } }, - async function fetchRequestBodyEmptyStream() { - const body = new ReadableStream({ - start(controller) { - controller.enqueue(new Uint8Array([])); - controller.close(); - }, - }); - - await assertRejects( - async () => { - const controller = new AbortController(); - const promise = fetch("http://localhost:4545/echo_server", { - body, - method: "POST", - signal: controller.signal, - }); - try { - controller.abort(); - } catch (e) { - console.log(e); - fail("abort should not throw"); - } - await promise; - }, - DOMException, - "The signal has been aborted", - ); - }, -); - -Deno.test("Request with subarray TypedArray body", async () => { - const body = new Uint8Array([1, 2, 3, 4, 5]).subarray(1); - const req = new Request("https://example.com", { method: "POST", body }); - const actual = new Uint8Array(await req.arrayBuffer()); - const expected = new Uint8Array([2, 3, 4, 5]); - assertEquals(actual, expected); -}); - -Deno.test("Response with subarray TypedArray body", async () => { - const body = new Uint8Array([1, 2, 3, 4, 5]).subarray(1); - const req = new Response(body); - const actual = new Uint8Array(await req.arrayBuffer()); - const expected = new Uint8Array([2, 3, 4, 5]); - assertEquals(actual, expected); -}); |