fetch.ts•58.2 kB
// The initial set of tests were taken from Deno fetch unit test suite
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
// https://github.com/denoland/deno/blob/main/LICENSE.md
import { wrapInTests } from "./js_builtins/testHelpers";
import { assert, expect } from "chai";
import { action, ActionCtx, query } from "./_generated/server";
import { api } from "./_generated/api";
export const fromQuery = query(async () => {
await fetch("http://localhost:4545/assets/fixture.json");
});
export const checkForAbort = query({
args: {},
handler: async (ctx) => {
const abort = await ctx.db.query("triggerAbort" as any).first();
return abort !== null;
},
});
async function fetchAbortTest(ctx: ActionCtx) {
const controller = new AbortController();
void (async () => {
while (!(await ctx.runQuery(api.fetch.checkForAbort))) {
await new Promise((resolve) => setTimeout(resolve, 10));
}
controller.abort();
})();
const responseFetch = fetch("http://localhost:4548/pause", {
method: "GET",
signal: controller.signal,
});
await expect(responseFetch).to.be.rejectedWith("AbortError");
}
export const fetchAbort = action(async (ctx) => {
return await wrapInTests({
fetchAbortTest: () => fetchAbortTest(ctx),
});
});
export default action(async () => {
return await wrapInTests({
fetchConstructorClones,
fetchRequiresOneArgument,
fetchProtocolError,
fetchDnsError,
fetchInvalidUriError,
fetchMalformedUriError,
fetchJson,
fetchURL,
fetchHeaders,
fetchBlob,
fetchBodyUsedReader,
// This does not currently pass because we're not tracking this state between
// the Response and the ReadableStream
// fetchBodyUsedCancelStream,
fetchAsyncIterator,
fetchBodyReader,
fetchBodyReaderBigBody,
fetchBodyReaderMultiPartBody,
responseClone,
fetchMultipartFormDataSuccess,
fetchMultipartFormBadContentType,
fetchURLEncodedFormDataSuccess,
fetchInitFormDataBinaryFileBody,
fetchInitFormDataMultipleFilesBody,
fetchWithRedirection,
// fetchWithRelativeRedirection,
fetchRedirectPostToGet,
fetchWithRelativeRedirectionUrl,
fetchWithInfRedirection,
fetchCrossOriginRedirectStripsAuthorization,
fetchRedirectBody,
// fetchRedirectBodyStream,
fetchInitStringBody,
fetchRequestInitStringBody,
fetchSeparateInit,
fetchInitTypedArrayBody,
fetchInitArrayBufferBody,
fetchInitURLSearchParamsBody,
fetchInitBlobBody,
fetchInitFormDataBody,
fetchInitFormDataBlobFilenameBody,
fetchInitFormDataTextFileBody,
fetchUserAgent,
// fetchRequest,
// fetchRequestAcceptHeaders,
// fetchPostBodyString,
// fetchPostBodyTypedArray,
// fetchUserSetContentLength,
// fetchUserSetTransferEncoding,
// fetchWithNonAsciiRedirection,
fetchWithManualRedirection,
fetchWithErrorRedirection,
responseRedirect,
responseRedirectTakeURLObjectAsParameter,
responseWithoutBody,
fetchBodyReadTwice,
fetchBodyReaderAfterRead,
fetchBodyReaderWithCancelAndNewReader,
fetchBodyReaderWithReadCancelAndNewReader,
// fetchResourceCloseAfterStreamCancel,
// fetchNullBodyStatus,
fetchResponseContentLength,
// fetchResponseConstructorNullBody,
// fetchResponseConstructorInvalidStatus,
// fetchResponseEmptyConstructor,
// fetchCustomHttpClientParamCertificateSuccess,
// fetchPostBodyReadableStream,
// fetchWritableRespProps,
// fetchFilterOutCustomHostHeader
// fetchHeadRespBody,
// fetchClientCertWrongPrivateKey,
// fetchClientCertBadPrivateKey,
// fetchClientCertNotPrivateKey,
// fetchCustomClientPrivateKey,
// fetchAbortWhileUploadStreaming,
// fetchAbortWhileUploadStreamingWithReason,
// fetchAbortWhileUploadStreamingWithPrimitiveReason,
// fetchHeaderValueShouldNotPanic,
// fetchHeaderNameShouldNotPanic,
// fetchSupportsHttp1Only,
// fetchSupportsHttp2,
// fetchPrefersHttp2,
// fetchFilePerm,
// fetchFilePermDoesNotExist,
// fetchFileBadMethod,
// fetchFileDoesNotExist,
// fetchFile,
// fetchContentLengthPost,
// fetchContentLengthPut,
// fetchContentLengthPatch,
// fetchContentLengthPostWithStringBody,
// fetchContentLengthPostWithBufferBody,
// staticResponseJson,
// fetchWithInvalidContentLengthAndTransferEncoding,
// fetchWithInvalidContentLength,
// fetchWithInvalidContentLength2,
// fetchBlobUrl,
fetchResponseStreamIsLockedWhileReading,
fetchResponseStreamIsLockedWhileReadingBlob,
fetchForbidden,
fetchOlaf,
fetchBodyTextDecoderStream,
});
});
async function fetchRequiresOneArgument() {
await expect(
// @ts-expect-error intentionally pass no arguments
fetch(),
).to.be.rejectedWith(TypeError, "Request URL is undefined");
}
async function fetchProtocolError() {
await expect(fetch("ftp://localhost:21/a/file")).to.be.rejectedWith(
TypeError,
"Unsupported URL scheme",
);
}
async function fetchDnsError() {
await expect(fetch("http://invalid/")).to.be.rejectedWith(
/dns error: failed to lookup address information/,
);
}
async function fetchInvalidUriError() {
await expect(fetch("http://<invalid>/")).to.be.rejectedWith(/Invalid URL/);
}
async function fetchMalformedUriError() {
const url = new URL("http://{{google/");
await expect(fetch(url)).to.be.rejectedWith(/Parsed Url is not a valid Uri/);
}
async function fetchJson() {
const response = await fetch("http://localhost:4545/assets/fixture.json");
assert.strictEqual(response.ok, true);
assert.strictEqual(response.status, 200);
assert.strictEqual(response.statusText, "OK");
assert.strictEqual(response.type, "basic");
const json = await response.json();
assert.strictEqual(json.name, "convex");
}
async function fetchURL() {
const response = await fetch(
new URL("http://localhost:4545/assets/fixture.json"),
);
assert.strictEqual(response.url, "http://localhost:4545/assets/fixture.json");
const _json = await response.json();
}
async function fetchHeaders() {
const response = await fetch("http://localhost:4545/assets/fixture.json");
const headers = response.headers;
assert.strictEqual(headers.get("Content-Type"), "application/json");
const _json = await response.json();
}
async function fetchBlob() {
const response = await fetch("http://localhost:4545/assets/fixture.json");
const headers = response.headers;
const blob = await response.blob();
assert.strictEqual(blob.type, headers.get("Content-Type"));
assert.strictEqual(blob.size, Number(headers.get("Content-Length")));
assert.strictEqual(await blob.text(), '{"name":"convex"}');
}
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
assert.strictEqual(response.bodyUsed, false);
reader.releaseLock();
await response.json();
assert.strictEqual(response.bodyUsed, true);
}
// async function fetchBodyUsedCancelStream() {
// const response = await fetch("http://localhost:4545/assets/fixture.json");
// assert(response.body !== null);
// assert.strictEqual(response.bodyUsed, false);
// const promise = response.body.cancel();
// assert.strictEqual(response.bodyUsed, true);
// await promise;
// }
async function fetchAsyncIterator() {
const response = await fetch("http://localhost:4545/assets/fixture.json");
const headers = response.headers;
assert(response.body !== null);
let total = 0;
// @ts-expect-error Type 'ReadableStream<Uint8Array>' must have a '[Symbol.asyncIterator]()' method that returns an async iterator.
for await (const chunk of response.body) {
assert(chunk instanceof Uint8Array);
total += chunk.length;
}
assert.strictEqual(total, Number(headers.get("Content-Length")));
}
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;
// eslint-disable-next-line no-constant-condition
while (true) {
const { done, value } = await reader.read();
if (done) break;
assert(value);
assert(value instanceof Uint8Array);
total += value.length;
}
assert.strictEqual(total, Number(headers.get("Content-Length")));
}
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;
// eslint-disable-next-line no-constant-condition
while (true) {
const { done, value } = await reader.read();
if (done) break;
assert(value);
total += value.length;
}
assert.strictEqual(total, data.length);
}
// Covers streaming fetch, because /echo_server returns the request stream
// as the response stream.
async function fetchBodyReaderMultiPartBody() {
const encoder = new TextEncoder();
const decoder = new TextDecoder();
const { readable, writable } = new TransformStream();
const writer = writable.getWriter();
const response = await fetch("http://localhost:4545/echo_server", {
method: "POST",
body: readable,
});
// Note we're doing full-duplex streaming, with the response status coming
// back before the request body has started sending.
assert(response.ok);
assert(response.body !== null);
const reader = response.body.getReader();
await writer.write(encoder.encode("Hello "));
const chunk1 = await reader.read();
assert.strictEqual(decoder.decode(chunk1.value), "Hello ");
assert.strictEqual(chunk1.done, false);
await writer.write(encoder.encode("World!"));
const chunk2 = await reader.read();
assert.strictEqual(decoder.decode(chunk2.value), "World!");
assert.strictEqual(chunk2.done, false);
await writer.close();
const chunk3 = await reader.read();
assert.strictEqual(chunk3.value, undefined);
assert.strictEqual(chunk3.done, true);
}
async function responseClone() {
const response = await fetch("http://localhost:4545/assets/fixture.json");
const response1 = response.clone();
assert(response !== response1);
assert.strictEqual(response.status, response1.status);
assert.strictEqual(response.statusText, response1.statusText);
const body = await response.arrayBuffer();
const body1 = await response1.arrayBuffer();
assert.deepEqual(body, body1);
}
async function fetchMultipartFormDataSuccess() {
const response = await fetch("http://localhost:4545/multipart_form_data.txt");
const formData = await response.formData();
assert(formData.has("field_1"));
assert.strictEqual(formData.get("field_1")!.toString(), "value_1 \r\n");
assert(formData.has("field_2"));
const file = formData.get("field_2") as File;
assert.strictEqual(file.name, "file.js");
assert.strictEqual(await file.text(), `console.log("Hi")`);
}
async function fetchMultipartFormBadContentType() {
const response = await fetch(
"http://localhost:4545/multipart_form_bad_content_type",
);
// assert(response.body !== null);
await expect(response.formData()).to.be.rejectedWith(
TypeError,
"Body cannot be decoded as form data",
);
}
async function fetchURLEncodedFormDataSuccess() {
const response = await fetch(
"http://localhost:4545/subdir/form_urlencoded.txt",
);
const formData = await response.formData();
assert(formData.has("field_1"));
assert.strictEqual(formData.get("field_1")!.toString(), "Hi");
assert(formData.has("field_2"));
assert.strictEqual(formData.get("field_2")!.toString(), "<Convex>");
}
async function fetchInitFormDataBinaryFileBody() {
// Some random bytes
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;
assert.strictEqual(resultFile.type, "application/octet-stream");
assert.strictEqual(resultFile.name, "file.bin");
assert.deepEqual(new Uint8Array(await resultFile.arrayBuffer()), binaryFile);
}
async function fetchInitFormDataMultipleFilesBody() {
const files = [
{
content: new Uint8Array([137, 80, 78, 71, 13, 10, 26, 10, 137, 1, 25]),
type: "image/png",
name: "image",
fileName: "some-image.png",
},
{
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();
assert.strictEqual(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;
assert.strictEqual(inputFile.size, resultFile.size);
assert.strictEqual(inputFile.name, resultFile.name);
assert.strictEqual(file.expectedType || file.type, resultFile.type);
assert.deepEqual(
new Uint8Array(await resultFile.arrayBuffer()),
file.content,
);
}
}
async function fetchWithRedirection() {
const response = await fetch("http://localhost:4545/assets/hello.txt");
assert.strictEqual(response.status, 200);
assert.strictEqual(response.statusText, "OK");
assert.strictEqual(response.url, "http://localhost:4545/assets/fixture.json");
const body = await response.text();
assert.strictEqual(body, '{"name":"convex"}');
}
// async function fetchWithRelativeRedirection() {
// const response = await fetch("http://localhost:4545/run/001_hello.js");
// assert.strictEqual(response.status, 200);
// assert.strictEqual(response.statusText, "OK");
// const body = await response.text();
// assert(body.includes("Hello"));
// }
async function fetchCrossOriginRedirectStripsAuthorization() {
const headers = {
authorization: "Bearer convex-admin",
"x-location": "http://localhost:4547/print_auth",
};
const directResponse = await fetch("http://localhost:4547/print_auth", {
headers,
});
assert.strictEqual(directResponse.status, 200);
const directBody = await directResponse.text();
assert.strictEqual(directBody, '{"auth":"Bearer convex-admin"}');
const response = await fetch("http://localhost:4545/assets/hello.txt", {
headers,
});
assert.strictEqual(response.status, 200);
assert.strictEqual(response.url, "http://localhost:4547/print_auth");
const body = await response.text();
assert.strictEqual(body, '{"auth":"None"}');
}
async function fetchRedirectBody() {
const response = await fetch("http://localhost:4545/redirect_body", {
method: "POST",
body: "Should be forwarded",
});
assert.strictEqual(response.status, 200);
assert.strictEqual(response.url, "http://localhost:4545/echo_server");
const body = await response.text();
assert.strictEqual(body, "Should be forwarded");
}
// // This test should pass, but it flakes very infrequently, for reasons that
// // look like a race in the hyper library?
// // When it flakes, the error is:
// expected promise to be rejected with 'TypeError' but it was rejected with
// 'Error: error sending request for url (http://localhost:4545/redirect_body):
// error writing a body to connection: Broken pipe (os error 32)'
//
// async function fetchRedirectBodyStream() {
// const stream = new Blob(["Should not be forwarded"]).stream();
// await expect(
// fetch("http://localhost:4545/redirect_body", {
// method: "POST",
// body: stream,
// })
// ).to.be.rejectedWith(TypeError, "fetch cannot redirect with streamed body");
// }
async function fetchRedirectPostToGet() {
const response = await fetch("http://localhost:4545/post_redirect_to_get", {
method: "POST",
headers: { "x-location": "/assets/fixture.json" },
});
assert.strictEqual(response.status, 200);
assert.strictEqual(response.url, "http://localhost:4545/assets/fixture.json");
const body = await response.text();
assert(body.includes('{"name":"convex"}'));
}
async function fetchWithRelativeRedirectionUrl() {
const cases = [
["end", "http://localhost:4545/a/b/end"],
["/end", "http://localhost:4545/end"],
];
for (const [loc, redUrl] of cases) {
const response = await fetch("http://localhost:4545/a/b/c", {
headers: new Headers([["x-location", loc]]),
});
assert.strictEqual(response.url, redUrl);
assert.strictEqual(response.redirected, true);
assert.strictEqual(response.status, 404);
assert.strictEqual(await response.text(), "");
}
}
async function fetchWithInfRedirection() {
await expect(
fetch("http://localhost:4545/assets/hello.txt", {
headers: { "x-location": "/assets/hello.txt" },
}),
).to.be.rejectedWith(TypeError, "redirect");
}
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();
assert.strictEqual(text, data);
assert(response.headers.get("content-type")!.startsWith("text/plain"));
}
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();
assert.strictEqual(text, data);
}
async function fetchSeparateInit() {
// related to: https://github.com/denoland/deno/issues/10396
const req = new Request("http://localhost:4545/assets/fixture.json");
const init = {
method: "GET",
};
req.headers.set("foo", "bar");
const res = await fetch(req, init);
assert.strictEqual(res.status, 200);
await res.text();
}
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();
assert.strictEqual(text, data);
}
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();
assert.strictEqual(text, data);
}
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();
assert.strictEqual(text, data);
assert(
response.headers
.get("content-type")!
.startsWith("application/x-www-form-urlencoded"),
);
}
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();
assert.strictEqual(text, data);
assert(response.headers.get("content-type")!.startsWith("text/javascript"));
}
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();
assert.strictEqual(form.get("field"), resultForm.get("field"));
}
async function fetchInitFormDataBlobFilenameBody() {
const form = new FormData();
form.append("field", "value");
form.append("file", new Blob([new TextEncoder().encode("convex")]));
const response = await fetch("http://localhost:4545/echo_server", {
method: "POST",
body: form,
});
const resultForm = await response.formData();
assert.strictEqual(form.get("field"), resultForm.get("field"));
const file = resultForm.get("file");
assert(file instanceof File);
assert.strictEqual(file.name, "blob");
}
async function fetchInitFormDataTextFileBody() {
const fileContent = "friends of convex";
const form = new FormData();
form.append("field", "value");
form.append(
"file",
new Blob([new TextEncoder().encode(fileContent)], {
type: "text/plain",
}),
"convex.txt",
);
const response = await fetch("http://localhost:4545/echo_server", {
method: "POST",
body: form,
});
const resultForm = await response.formData();
assert.strictEqual(form.get("field"), resultForm.get("field"));
const file = form.get("file") as File;
const resultFile = resultForm.get("file") as File;
assert.strictEqual(file.size, resultFile.size);
assert.strictEqual(file.name, resultFile.name);
assert.strictEqual(file.type, resultFile.type);
assert.strictEqual(await file.text(), await resultFile.text());
}
async function fetchUserAgent() {
const data = "Hello World";
const response = await fetch("http://localhost:4545/echo_server", {
method: "POST",
body: new TextEncoder().encode(data),
});
assert.equal(response.headers.get("user-agent"), `Convex/1.0`);
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;
// });
// }
// async function fetchRequest() {
// const addr = "127.0.0.1:4501";
// 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);
// }
// async function fetchRequestAcceptHeaders() {
// const addr = "127.0.0.1:4501";
// 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);
// }
// async function fetchPostBodyString() {
// const addr = "127.0.0.1:4511";
// 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);
// }
// async function fetchPostBodyTypedArray() {
// const addr = "127.0.0.1:4503";
// 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);
// }
// async function fetchUserSetContentLength() {
// const addr = "127.0.0.1:4501";
// 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);
// }
// async function fetchUserSetTransferEncoding() {
// const addr = "127.0.0.1:4501";
// 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);
// }
// async function fetchWithNonAsciiRedirection() {
// const response = await fetch("http://localhost:4545/non_ascii_redirect", {
// redirect: "manual",
// });
// assert.strictEqual(response.status, 301);
// assert.strictEqual(response.headers.get("location"), "/redirect®");
// await response.text();
// }
async function fetchWithManualRedirection() {
const response = await fetch("http://localhost:4545/assets/hello.txt", {
redirect: "manual",
});
assert.strictEqual(response.status, 301);
assert.strictEqual(response.statusText, "Moved Permanently");
assert.strictEqual(response.url, "http://localhost:4545/assets/hello.txt");
assert.strictEqual(response.type, "basic");
assert.strictEqual(response.headers.get("Location"), "/assets/fixture.json");
await response.body!.cancel();
}
async function fetchWithErrorRedirection() {
await expect(
fetch("http://localhost:4545/assets/hello.txt", {
redirect: "error",
}),
).to.be.rejectedWith(TypeError, "redirect");
}
function responseRedirect() {
const redir = Response.redirect("http://example.com/newLocation", 301);
assert.strictEqual(redir.status, 301);
assert.strictEqual(redir.statusText, "");
assert.strictEqual(redir.url, "");
assert.strictEqual(
redir.headers.get("Location"),
"http://example.com/newLocation",
);
assert.strictEqual(redir.type, "default");
}
function responseRedirectTakeURLObjectAsParameter() {
const redir = Response.redirect(new URL("https://example.com/"));
assert.strictEqual(redir.headers.get("Location"), "https://example.com/");
}
async function responseWithoutBody() {
const response = new Response();
assert.deepEqual(await response.arrayBuffer(), new ArrayBuffer(0));
const blob = await response.blob();
assert.strictEqual(blob.size, 0);
assert.deepEqual(await blob.arrayBuffer(), new ArrayBuffer(0));
assert.strictEqual(await response.text(), "");
await expect(response.json()).to.be.rejectedWith(
"Unexpected end of JSON input",
);
}
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]();
assert(
false,
"Reading body multiple times should failed, the stream should've been locked.",
);
} catch {
// pass
}
}
}
async function fetchBodyReaderAfterRead() {
const response = await fetch("http://localhost:4545/assets/fixture.json");
assert(response.body !== null);
const reader = await response.body.getReader();
// eslint-disable-next-line no-constant-condition
while (true) {
const { done, value } = await reader.read();
if (done) break;
assert(value);
}
assert.throws(
() => response.body!.getReader(),
"This stream has already been locked",
);
}
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;
// eslint-disable-next-line no-constant-condition
while (true) {
const { done, value } = await reader.read();
if (done) break;
assert(value);
total += value.length;
}
assert.strictEqual(total, data.length);
}
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;
// eslint-disable-next-line no-constant-condition
while (true) {
const { done, value } = await reader.read();
if (done) break;
assert(value);
total += value.length;
}
assert.strictEqual(total, data.length);
}
// 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();
// }
// 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);
// }
// }
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,
});
assert.strictEqual(
Number(res.headers.get("content-length")),
body.byteLength,
);
const blob = await res.blob();
// Make sure Body content-type is correctly set
assert.strictEqual(blob.type, "application/octet-stream");
assert.strictEqual(blob.size, body.byteLength);
}
// 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"
// );
// }
// }
// }
// 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]."
// )
// );
// }
// }
// }
// 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], []);
// }
// 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();
// }
// 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();
// }
// async function fetchPostBodyReadableStream() {
// const addr = "127.0.0.1:4511";
// 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`,
// "6\r\n",
// "hello \r\n",
// "5\r\n",
// "world\r\n",
// "0\r\n\r\n",
// ].join("");
// assertEquals(actual, expected);
// }
// 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");
// }
// async function fetchFilterOutCustomHostHeader(): Promise<void> {
// const addr = "127.0.0.1:4511";
// 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);
// }
// async function fetchNoServerReadableStreamBody() {
// const done = deferred();
// const body = new ReadableStream({
// start(controller) {
// controller.enqueue(new Uint8Array([1]));
// setTimeout(() => {
// controller.enqueue(new Uint8Array([2]));
// done.resolve();
// }, 1000);
// },
// });
// const nonExistantHostname = "http://localhost:47582";
// await assertRejects(async () => {
// await fetch(nonExistantHostname, { body, method: "POST" });
// }, TypeError);
// await done;
// }
// async function fetchHeadRespBody() {
// const res = await fetch("http://localhost:4545/echo_server", {
// method: "HEAD",
// });
// assert.strictEqual(res.body, null);
// }
// 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);
// }
// 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);
// }
// 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);
// }
// 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();
// }
// async function fetchAbortWhileUploadStreaming(): Promise<void> {
// const abortController = new AbortController();
// try {
// await fetch("http://localhost:4545/echo_server", {
// method: "POST",
// body: new ReadableStream({
// pull(_controller) {
// abortController.abort();
// // controller.enqueue(new Uint8Array([1, 2, 3, 4]));
// },
// }),
// signal: abortController.signal,
// });
// expect.fail("Fetch didn't reject.");
// } catch (error) {
// expect(error).to.be.instanceOf(Error);
// if (error instanceof Error) {
// expect(error.message).to.equal("AbortError");
// }
// // expect(error).to.be.instanceOf(DOMException);
// // expect(error.name).to.equal("AbortError");
// // expect(error.message).to.equal("The signal has been aborted");
// }
// }
// 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);
// }
// }
// 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");
// }
// }
// 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
// );
// }
// 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
// );
// }
// 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();
// }
// 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();
// }
// 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();
// }
// async function fetchFilePerm() {
// await assertRejects(async () => {
// await fetch(import.meta.resolve("../testdata/subdir/json_1.json"));
// }, Deno.errors.PermissionDenied);
// }
// async function fetchFilePermDoesNotExist() {
// await assertRejects(async () => {
// await fetch(import.meta.resolve("./bad.json"));
// }, Deno.errors.PermissionDenied);
// }
// 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."
// );
// }
// async function fetchFileDoesNotExist() {
// await assertRejects(async () => {
// await fetch(import.meta.resolve("./bad.json"));
// }, TypeError);
// }
// 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);
// }
// async function fetchContentLengthPost() {
// const response = await fetch("http://localhost:4545/content_length", {
// method: "POST",
// });
// const length = await response.text();
// assertEquals(length, 'Some("0")');
// }
// async function fetchContentLengthPut() {
// const response = await fetch("http://localhost:4545/content_length", {
// method: "PUT",
// });
// const length = await response.text();
// assertEquals(length, 'Some("0")');
// }
// async function fetchContentLengthPatch() {
// const response = await fetch("http://localhost:4545/content_length", {
// method: "PATCH",
// });
// const length = await response.text();
// assertEquals(length, "None");
// }
// 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")');
// }
// 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")');
// }
// 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;
// }
// async function fetchWithInvalidContentLengthAndTransferEncoding(): Promise<void> {
// const addr = "127.0.0.1:4516";
// 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();
// }
// async function fetchWithInvalidContentLength(): Promise<void> {
// const addr = "127.0.0.1:4518";
// 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();
// }
// async function fetchWithInvalidContentLength2(): Promise<void> {
// const addr = "127.0.0.1:4519";
// 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();
// }
// async function fetchBlobUrl(): Promise<void> {
// const blob = new Blob(["ok"], { type: "text/plain" });
// const url = URL.createObjectURL(blob);
// const res = await fetch(url);
// assert(res.url.startsWith("blob:http://js-unit-tests/"));
// assertEquals(res.status, 200);
// assertEquals(res.headers.get("content-length"), "2");
// assertEquals(res.headers.get("content-type"), "text/plain");
// assertEquals(await res.text(), "ok");
// }
async function fetchResponseStreamIsLockedWhileReading() {
const response = await fetch("http://localhost:4545/echo_server", {
body: new Uint8Array(5000),
method: "POST",
});
assert.strictEqual(response.body!.locked, false);
const promise = response.arrayBuffer();
assert.strictEqual(response.body!.locked, true);
await promise;
}
async function fetchResponseStreamIsLockedWhileReadingBlob() {
const response = await fetch("http://localhost:4545/echo_server", {
body: new Uint8Array(5000),
method: "POST",
});
assert.strictEqual(response.body!.locked, false);
const promise = response.blob();
assert.strictEqual(response.body!.locked, true);
await promise;
}
async function fetchConstructorClones() {
const req = new Request("https://example.com", {
method: "POST",
body: "foo",
});
assert.strictEqual(await req.text(), "foo");
await expect(req.text()).to.be.rejectedWith(
TypeError,
/body stream already read/,
);
const req2 = new Request(req, { method: "PUT", body: "bar" }); // should not have any impact on req
assert.strictEqual(await req2.text(), "bar");
await expect(req2.text()).to.be.rejectedWith(
TypeError,
/body stream already read/,
);
assert.strictEqual(req.method, "POST");
assert.strictEqual(req2.method, "PUT");
assert.strictEqual(req.headers.get("x-foo"), null);
assert.strictEqual(req2.headers.get("x-foo"), null);
req2.headers.set("x-foo", "bar"); // should not have any impact on req
assert.strictEqual(req.headers.get("x-foo"), null);
assert.strictEqual(req2.headers.get("x-foo"), "bar");
}
async function fetchForbidden() {
// TODO(presley): See if we can throw type error when we reject the promise
// from rust
await expect(fetch("http://localhost:4545/proxy_reject")).to.be.rejectedWith(
"Request to http://localhost:4545/proxy_reject forbidden",
);
}
export const fetchInParallel = action(async () => {
const parallelFetches = [
fetch("http://localhost:4546/timeout"),
fetch("http://localhost:4546/echo_server", {
method: "POST",
body: new TextEncoder().encode("hello world"),
}),
];
const response = await Promise.race(parallelFetches);
assert(response.ok, await response.text());
});
// Regression test.
export const fetchBlockedOnTimeouts = action(async () => {
const fetchWithTimeout = async () => {
setTimeout(
() => {
// AbortController stuff.
// This never runs.
},
10 * 60 * 1000,
);
await fetch("http://localhost:4546/echo_server", {
method: "POST",
body: new TextEncoder().encode("hello world"),
});
};
// 10 setTimeouts queued up before the fetches.
// When limited parallelism in actions applied to setTimeouts, the setTimeouts
// would block the fetches.
await Promise.all(
Array(10)
.fill(0)
.map(() => fetchWithTimeout()),
);
});
export const danglingFetch = action(() => {
// eslint-disable-next-line @typescript-eslint/no-floating-promises
fetch("http://localhost:4546/echo_server");
});
export const fetchTimeout = action(async () => {
const request = new Request("http://localhost:4546/timeout");
const result = await fetch(request);
throw new Error(`fetch should not complete: ${result}`);
});
export const fetchUnendingRequest = action(async () => {
const request = new Request("http://localhost:4546/echo_server", {
method: "POST",
body: new ReadableStream(),
});
const response = await fetch(request);
await response.text();
throw new Error(`fetch should not complete`);
});
// Regression test for https://webtechsurvey.com/response-header/x-olaf
async function fetchOlaf() {
const response = await fetch("http://localhost:4545/echo_server", {
method: "POST",
headers: { "X-Olaf": "⛄" },
});
assert.strictEqual(response.headers.get("X-Olaf"), "â\x9B\x84");
}
async function fetchBodyTextDecoderStream() {
const data = JSON.stringify({
hello: "world",
foo: "bar",
baz: "qux",
});
const response = await fetch("http://localhost:4545/echo_server", {
method: "POST",
body: data,
});
assert(response.body !== null);
const decodedStream = response.body!.pipeThrough(new TextDecoderStream());
const reader = decodedStream.getReader();
let decodedBody = "";
// eslint-disable-next-line no-constant-condition
while (true) {
const { done, value } = await reader.read();
if (done) break;
assert(value);
decodedBody += value;
}
assert.strictEqual(decodedBody, data);
}