summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cli/bench/async_ops.js19
-rw-r--r--cli/bench/tcp.js22
-rw-r--r--cli/tests/integration/mod.rs4
-rw-r--r--cli/tests/testdata/044_bad_resource.ts.out2
-rw-r--r--cli/tests/testdata/wasm_unreachable.out2
-rw-r--r--cli/tests/unit/fetch_test.ts57
-rw-r--r--cli/tests/unit/http_test.ts200
-rw-r--r--core/01_core.js3
-rw-r--r--core/ops.rs14
-rw-r--r--core/runtime.rs122
-rw-r--r--ext/web/timers.rs2
-rw-r--r--ext/websocket/lib.rs2
-rw-r--r--ops/lib.rs10
-rw-r--r--runtime/js/40_testing.js1
-rw-r--r--runtime/ops/web_worker.rs2
15 files changed, 295 insertions, 167 deletions
diff --git a/cli/bench/async_ops.js b/cli/bench/async_ops.js
new file mode 100644
index 000000000..ac4eb6858
--- /dev/null
+++ b/cli/bench/async_ops.js
@@ -0,0 +1,19 @@
+// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
+const queueMicrotask = globalThis.queueMicrotask || process.nextTick;
+let [total, count] = typeof Deno !== "undefined"
+ ? Deno.args
+ : [process.argv[2], process.argv[3]];
+
+total = total ? parseInt(total, 0) : 50;
+count = count ? parseInt(count, 10) : 100000;
+
+async function bench(fun) {
+ const start = Date.now();
+ for (let i = 0; i < count; i++) await fun();
+ const elapsed = Date.now() - start;
+ const rate = Math.floor(count / (elapsed / 1000));
+ console.log(`time ${elapsed} ms rate ${rate}`);
+ if (--total) queueMicrotask(() => bench(fun));
+}
+
+bench(() => Deno.core.opAsync("op_void_async"));
diff --git a/cli/bench/tcp.js b/cli/bench/tcp.js
new file mode 100644
index 000000000..826f5f87b
--- /dev/null
+++ b/cli/bench/tcp.js
@@ -0,0 +1,22 @@
+// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
+
+const listener = Deno.listen({ port: 4500 });
+const response = new TextEncoder().encode(
+ "HTTP/1.1 200 OK\r\nContent-Length: 12\r\n\r\nHello World\n",
+);
+
+// Accept a connection and write packets as fast as possible.
+async function acceptWrite() {
+ const conn = await listener.accept();
+ try {
+ while (true) {
+ await conn.write(response);
+ }
+ } catch {
+ // Pass
+ }
+ conn.close();
+}
+
+await acceptWrite();
+await acceptWrite();
diff --git a/cli/tests/integration/mod.rs b/cli/tests/integration/mod.rs
index 84ec34f2b..143bda819 100644
--- a/cli/tests/integration/mod.rs
+++ b/cli/tests/integration/mod.rs
@@ -928,7 +928,7 @@ async fn test_resolve_dns() {
let out = String::from_utf8_lossy(&output.stdout);
assert!(!output.status.success());
assert!(err.starts_with("Check file"));
- assert!(err.contains(r#"error: Uncaught (in promise) PermissionDenied: Requires net access to "127.0.0.1:4553""#));
+ assert!(err.contains(r#"error: Uncaught PermissionDenied: Requires net access to "127.0.0.1:4553""#));
assert!(out.is_empty());
}
@@ -950,7 +950,7 @@ async fn test_resolve_dns() {
let out = String::from_utf8_lossy(&output.stdout);
assert!(!output.status.success());
assert!(err.starts_with("Check file"));
- assert!(err.contains(r#"error: Uncaught (in promise) PermissionDenied: Requires net access to "127.0.0.1:4553""#));
+ assert!(err.contains(r#"error: Uncaught PermissionDenied: Requires net access to "127.0.0.1:4553""#));
assert!(out.is_empty());
}
diff --git a/cli/tests/testdata/044_bad_resource.ts.out b/cli/tests/testdata/044_bad_resource.ts.out
index 33c95fc44..b208f9d25 100644
--- a/cli/tests/testdata/044_bad_resource.ts.out
+++ b/cli/tests/testdata/044_bad_resource.ts.out
@@ -1,2 +1,2 @@
-[WILDCARD]error: Uncaught (in promise) BadResource: Bad resource ID
+[WILDCARD]error: Uncaught [WILDCARD] BadResource: Bad resource ID
[WILDCARD]
diff --git a/cli/tests/testdata/wasm_unreachable.out b/cli/tests/testdata/wasm_unreachable.out
index cbb6b6519..7fb5e309d 100644
--- a/cli/tests/testdata/wasm_unreachable.out
+++ b/cli/tests/testdata/wasm_unreachable.out
@@ -1,3 +1,3 @@
-error: Uncaught (in promise) RuntimeError: unreachable
+error: Uncaught [WILDCARD] RuntimeError: unreachable
at <anonymous> (wasm://wasm/d1c677ea:1:41)
at [WILDCARD]/wasm_unreachable.js:[WILDCARD]
diff --git a/cli/tests/unit/fetch_test.ts b/cli/tests/unit/fetch_test.ts
index 175431e76..cbb2cd680 100644
--- a/cli/tests/unit/fetch_test.ts
+++ b/cli/tests/unit/fetch_test.ts
@@ -1202,47 +1202,44 @@ Deno.test({}, function fetchWritableRespProps() {
assertEquals(new_.headers.get("x-deno"), "foo");
});
-function returnHostHeaderServer(addr: string): Deno.Listener {
- const [hostname, port] = addr.split(":");
- const listener = Deno.listen({
- hostname,
- port: Number(port),
- }) as Deno.Listener;
-
- listener.accept().then(async (conn: Deno.Conn) => {
- const 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,
- }),
- );
- });
-
- httpConn.close();
- });
-
- return listener;
-}
-
Deno.test(
{ permissions: { net: true } },
async function fetchFilterOutCustomHostHeader(): Promise<
void
> {
const addr = "127.0.0.1:4511";
- const listener = returnHostHeaderServer(addr);
+ 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);
},
diff --git a/cli/tests/unit/http_test.ts b/cli/tests/unit/http_test.ts
index b52fbdec9..eeb75c934 100644
--- a/cli/tests/unit/http_test.ts
+++ b/cli/tests/unit/http_test.ts
@@ -45,11 +45,12 @@ async function writeRequestAndReadResponse(conn: Deno.Conn): Promise<string> {
}
Deno.test({ permissions: { net: true } }, async function httpServerBasic() {
+ let httpConn: Deno.HttpConn;
const promise = (async () => {
const listener = Deno.listen({ port: 4501 });
const conn = await listener.accept();
listener.close();
- const httpConn = Deno.serveHttp(conn);
+ httpConn = Deno.serveHttp(conn);
const reqEvent = await httpConn.nextRequest();
assert(reqEvent);
const { request, respondWith } = reqEvent;
@@ -58,7 +59,6 @@ Deno.test({ permissions: { net: true } }, async function httpServerBasic() {
await respondWith(
new Response("Hello World", { headers: { "foo": "bar" } }),
);
- httpConn.close();
})();
const resp = await fetch("http://127.0.0.1:4501/", {
@@ -71,6 +71,8 @@ Deno.test({ permissions: { net: true } }, async function httpServerBasic() {
const cloneText = await clone.text();
assertEquals(cloneText, "Hello World");
await promise;
+
+ httpConn!.close();
});
// https://github.com/denoland/deno/issues/15107
@@ -88,7 +90,7 @@ Deno.test(
const { request } = e;
request.text();
headers = request.headers;
- httpConn.close();
+ httpConn!.close();
})();
const conn = await Deno.connect({ port: 2333 });
@@ -120,7 +122,7 @@ Deno.test(
await respondWith(new Response("Hello World")); // Closes request
assertThrows(() => request.headers, TypeError, "request closed");
- httpConn.close();
+ httpConn!.close();
})();
const conn = await Deno.connect({ port: 2334 });
@@ -138,17 +140,17 @@ Deno.test(
Deno.test(
{ permissions: { net: true } },
async function httpServerGetRequestBody() {
+ let httpConn: Deno.HttpConn;
const promise = (async () => {
const listener = Deno.listen({ port: 4501 });
const conn = await listener.accept();
listener.close();
- const httpConn = Deno.serveHttp(conn);
+ httpConn = Deno.serveHttp(conn);
const e = await httpConn.nextRequest();
assert(e);
const { request, respondWith } = e;
assertEquals(request.body, null);
await respondWith(new Response("", { headers: {} }));
- httpConn.close();
})();
const conn = await Deno.connect({ port: 4501 });
@@ -166,6 +168,7 @@ Deno.test(
conn.close();
await promise;
+ httpConn!.close();
},
);
@@ -178,23 +181,24 @@ Deno.test(
writer.write(new TextEncoder().encode("world"));
writer.close();
+ let httpConn: Deno.HttpConn;
+ const listener = Deno.listen({ port: 4501 });
const promise = (async () => {
- const listener = Deno.listen({ port: 4501 });
const conn = await listener.accept();
- const httpConn = Deno.serveHttp(conn);
+ httpConn = Deno.serveHttp(conn);
const evt = await httpConn.nextRequest();
assert(evt);
const { request, respondWith } = evt;
assert(!request.body);
await respondWith(new Response(stream.readable));
- httpConn.close();
- listener.close();
})();
const resp = await fetch("http://127.0.0.1:4501/");
const respBody = await resp.text();
assertEquals("hello world", respBody);
await promise;
+ httpConn!.close();
+ listener.close();
},
);
@@ -240,17 +244,16 @@ Deno.test(
Deno.test(
{ permissions: { net: true } },
async function httpServerStreamDuplex() {
+ let httpConn: Deno.HttpConn;
+ const listener = Deno.listen({ port: 4501 });
const promise = (async () => {
- const listener = Deno.listen({ port: 4501 });
const conn = await listener.accept();
- const httpConn = Deno.serveHttp(conn);
+ httpConn = Deno.serveHttp(conn);
const evt = await httpConn.nextRequest();
assert(evt);
const { request, respondWith } = evt;
assert(request.body);
await respondWith(new Response(request.body));
- httpConn.close();
- listener.close();
})();
const ts = new TransformStream();
@@ -269,10 +272,13 @@ Deno.test(
const chunk2 = await reader.read();
assert(!chunk2.done);
assertEquals(chunk2.value, new Uint8Array([2]));
+
await writable.close();
const chunk3 = await reader.read();
assert(chunk3.done);
await promise;
+ httpConn!.close();
+ listener.close();
},
);
@@ -351,18 +357,17 @@ Deno.test(
Deno.test(
{ permissions: { net: true } },
async function httpServerRegressionHang() {
+ let httpConn: Deno.HttpConn;
+ const listener = Deno.listen({ port: 4501 });
const promise = (async () => {
- const listener = Deno.listen({ port: 4501 });
const conn = await listener.accept();
- const httpConn = Deno.serveHttp(conn);
+ httpConn = Deno.serveHttp(conn);
const event = await httpConn.nextRequest();
assert(event);
const { request, respondWith } = event;
const reqBody = await request.text();
assertEquals("request", reqBody);
await respondWith(new Response("response"));
- httpConn.close();
- listener.close();
})();
const resp = await fetch("http://127.0.0.1:4501/", {
@@ -372,6 +377,9 @@ Deno.test(
const respBody = await resp.text();
assertEquals("response", respBody);
await promise;
+
+ httpConn!.close();
+ listener.close();
},
);
@@ -410,7 +418,7 @@ Deno.test(
cancelReason!,
);
assert(cancelReason!);
- httpConn.close();
+ httpConn!.close();
listener.close();
})();
@@ -468,22 +476,23 @@ Deno.test(
Deno.test(
{ permissions: { net: true } },
async function httpServerEmptyBlobResponse() {
+ let httpConn: Deno.HttpConn;
+ const listener = Deno.listen({ port: 4501 });
const promise = (async () => {
- const listener = Deno.listen({ port: 4501 });
const conn = await listener.accept();
- const httpConn = Deno.serveHttp(conn);
+ httpConn = Deno.serveHttp(conn);
const event = await httpConn.nextRequest();
assert(event);
const { respondWith } = event;
await respondWith(new Response(new Blob([])));
- httpConn.close();
- listener.close();
})();
const resp = await fetch("http://127.0.0.1:4501/");
const respBody = await resp.text();
assertEquals("", respBody);
await promise;
+ httpConn!.close();
+ listener.close();
},
);
@@ -633,14 +642,14 @@ Deno.test(
}).pipeThrough(new TextEncoderStream());
}
+ let httpConn: Deno.HttpConn;
const listener = Deno.listen({ port: 4501 });
const finished = (async () => {
const conn = await listener.accept();
- const httpConn = Deno.serveHttp(conn);
+ httpConn = Deno.serveHttp(conn);
const requestEvent = await httpConn.nextRequest();
const { respondWith } = requestEvent!;
await respondWith(new Response(periodicStream()));
- httpConn.close();
})();
// start a client
@@ -651,6 +660,8 @@ Deno.test(
await finished;
clientConn.close();
+
+ httpConn!.close();
listener.close();
},
);
@@ -658,11 +669,12 @@ Deno.test(
Deno.test(
{ permissions: { net: true } },
async function httpRequestLatin1Headers() {
+ let httpConn: Deno.HttpConn;
const promise = (async () => {
const listener = Deno.listen({ port: 4501 });
const conn = await listener.accept();
listener.close();
- const httpConn = Deno.serveHttp(conn);
+ httpConn = Deno.serveHttp(conn);
const reqEvent = await httpConn.nextRequest();
assert(reqEvent);
const { request, respondWith } = reqEvent;
@@ -670,7 +682,6 @@ Deno.test(
await respondWith(
new Response("", { headers: { "X-Header-Test": "Æ" } }),
);
- httpConn.close();
})();
const clientConn = await Deno.connect({ port: 4501 });
@@ -688,11 +699,14 @@ Deno.test(
let responseText = "";
const buf = new Uint8Array(1024);
let read;
+
while ((read = await clientConn.read(buf)) !== null) {
+ httpConn!.close();
for (let i = 0; i < read; i++) {
responseText += String.fromCharCode(buf[i]);
}
}
+
clientConn.close();
assert(/\r\n[Xx]-[Hh]eader-[Tt]est: Æ\r\n/.test(responseText));
@@ -704,18 +718,18 @@ Deno.test(
Deno.test(
{ permissions: { net: true } },
async function httpServerRequestWithoutPath() {
+ let httpConn: Deno.HttpConn;
+ const listener = Deno.listen({ port: 4501 });
const promise = (async () => {
- const listener = Deno.listen({ port: 4501 });
const conn = await listener.accept();
listener.close();
- const httpConn = Deno.serveHttp(conn);
+ httpConn = Deno.serveHttp(conn);
const reqEvent = await httpConn.nextRequest();
assert(reqEvent);
const { request, respondWith } = reqEvent;
assertEquals(new URL(request.url).href, "http://127.0.0.1:4501/");
assertEquals(await request.text(), "");
await respondWith(new Response());
- httpConn.close();
})();
const clientConn = await Deno.connect({ port: 4501 });
@@ -744,6 +758,7 @@ Deno.test(
await writeRequest(clientConn);
clientConn.close();
await promise;
+ httpConn!.close();
},
);
@@ -868,11 +883,12 @@ Deno.test(function httpUpgradeWebSocketWithoutUpgradeHeader() {
Deno.test(
{ permissions: { net: true } },
async function httpCookieConcatenation() {
+ let httpConn: Deno.HttpConn;
const promise = (async () => {
const listener = Deno.listen({ port: 4501 });
const conn = await listener.accept();
listener.close();
- const httpConn = Deno.serveHttp(conn);
+ httpConn = Deno.serveHttp(conn);
const reqEvent = await httpConn.nextRequest();
assert(reqEvent);
const { request, respondWith } = reqEvent;
@@ -880,7 +896,6 @@ Deno.test(
assertEquals(await request.text(), "");
assertEquals(request.headers.get("cookie"), "foo=bar; bar=foo");
await respondWith(new Response("ok"));
- httpConn.close();
})();
const resp = await fetch("http://127.0.0.1:4501/", {
@@ -893,6 +908,7 @@ Deno.test(
const text = await resp.text();
assertEquals(text, "ok");
await promise;
+ httpConn!.close();
},
);
@@ -910,7 +926,7 @@ Deno.test({ permissions: { net: true } }, async function httpServerPanic() {
httpConn.nextRequest();
await client.write(encoder.encode("\r\n\r\n"));
- httpConn.close();
+ httpConn!.close();
client.close();
listener.close();
@@ -923,21 +939,23 @@ Deno.test(
const file = await Deno.open(tmpFile, { write: true, read: true });
await file.write(new Uint8Array(70 * 1024).fill(1)); // 70kb sent in 64kb + 6kb chunks
file.close();
+
+ let httpConn: Deno.HttpConn;
+ const listener = Deno.listen({ port: 4503 });
const promise = (async () => {
- const listener = Deno.listen({ port: 4503 });
const conn = await listener.accept();
- const httpConn = Deno.serveHttp(conn);
+ httpConn = Deno.serveHttp(conn);
const ev = await httpConn.nextRequest();
const { respondWith } = ev!;
const f = await Deno.open(tmpFile, { read: true });
await respondWith(new Response(f.readable, { status: 200 }));
- httpConn.close();
- listener.close();
})();
const resp = await fetch("http://127.0.0.1:4503/");
const body = await resp.arrayBuffer();
assertEquals(body.byteLength, 70 * 1024);
await promise;
+ httpConn!.close();
+ listener.close();
},
);
@@ -976,7 +994,7 @@ Deno.test(
}
assert(didThrow);
- httpConn.close();
+ httpConn!.close();
listener.close();
client.close();
},
@@ -1018,7 +1036,7 @@ Deno.test(
await respondWith(res).catch((error: Error) => errors.push(error));
- httpConn.close();
+ httpConn!.close();
listener.close();
assert(errors.length >= 1);
@@ -1047,7 +1065,7 @@ Deno.test(
)),
]);
- httpConn.close();
+ httpConn!.close();
listener.close();
clientConn.close();
},
@@ -1133,10 +1151,11 @@ Deno.test(
const hostname = "localhost";
const port = 4501;
+ let httpConn: Deno.HttpConn;
+ const listener = Deno.listen({ hostname, port });
async function server() {
- const listener = Deno.listen({ hostname, port });
const tcpConn = await listener.accept();
- const httpConn = Deno.serveHttp(tcpConn);
+ httpConn = Deno.serveHttp(tcpConn);
const promises = new Array(10).fill(null).map(async (_, i) => {
const event = await httpConn.nextRequest();
assert(event);
@@ -1146,8 +1165,6 @@ Deno.test(
await event.respondWith(response);
});
await Promise.all(promises);
- httpConn.close();
- listener.close();
}
async function client() {
@@ -1159,6 +1176,8 @@ Deno.test(
}
await Promise.all([server(), delay(100).then(client)]);
+ httpConn!.close();
+ listener.close();
},
);
@@ -1213,16 +1232,15 @@ Deno.test(
const hostname = "localhost";
const port = 4501;
+ let httpConn: Deno.HttpConn;
+ const listener = Deno.listen({ hostname, port });
async function server() {
- const listener = Deno.listen({ hostname, port });
const tcpConn = await listener.accept();
- const httpConn = Deno.serveHttp(tcpConn);
+ httpConn = Deno.serveHttp(tcpConn);
const event = await httpConn.nextRequest() as Deno.RequestEvent;
assert(event.request.body);
const response = new Response();
await event.respondWith(response);
- httpConn.close();
- listener.close();
}
async function client() {
@@ -1237,17 +1255,20 @@ Deno.test(
}
await Promise.all([server(), client()]);
+ httpConn!.close();
+ listener.close();
},
);
Deno.test(
{ permissions: { net: true } },
async function httpServerRespondNonAsciiUint8Array() {
+ let httpConn: Deno.HttpConn;
+ const listener = Deno.listen({ port: 4501 });
const promise = (async () => {
- const listener = Deno.listen({ port: 4501 });
const conn = await listener.accept();
listener.close();
- const httpConn = Deno.serveHttp(conn);
+ httpConn = Deno.serveHttp(conn);
const e = await httpConn.nextRequest();
assert(e);
const { request, respondWith } = e;
@@ -1255,7 +1276,6 @@ Deno.test(
await respondWith(
new Response(new Uint8Array([128]), {}),
);
- httpConn.close();
})();
const resp = await fetch("http://localhost:4501/");
@@ -1264,6 +1284,7 @@ Deno.test(
assertEquals(new Uint8Array(body), new Uint8Array([128]));
await promise;
+ httpConn!.close();
},
);
@@ -1276,11 +1297,12 @@ Deno.test(
async function httpServerOnUnixSocket() {
const filePath = Deno.makeTempFileSync();
+ let httpConn: Deno.HttpConn;
const promise = (async () => {
const listener = Deno.listen({ path: filePath, transport: "unix" });
const conn = await listener.accept();
listener.close();
- const httpConn = Deno.serveHttp(conn);
+ httpConn = Deno.serveHttp(conn);
const reqEvent = await httpConn.nextRequest();
assert(reqEvent);
const { request, respondWith } = reqEvent;
@@ -1289,7 +1311,6 @@ Deno.test(
assertEquals(decodeURIComponent(url.host), filePath);
assertEquals(url.pathname, "/path/name");
await respondWith(new Response("", { headers: {} }));
- httpConn.close();
})();
// fetch() does not supports unix domain sockets yet https://github.com/denoland/deno/issues/8821
@@ -1307,6 +1328,7 @@ Deno.test(
conn.close();
await promise;
+ httpConn!.close();
},
);
@@ -1324,9 +1346,10 @@ Deno.test({
const data = { hello: "deno", now: "with", compressed: "body" };
+ let httpConn: Deno.HttpConn;
async function server() {
const tcpConn = await listener.accept();
- const httpConn = Deno.serveHttp(tcpConn);
+ httpConn = Deno.serveHttp(tcpConn);
const e = await httpConn.nextRequest();
assert(e);
const { request, respondWith } = e;
@@ -1335,7 +1358,6 @@ Deno.test({
headers: { "content-type": "application/json" },
});
await respondWith(response);
- httpConn.close();
listener.close();
}
@@ -1361,6 +1383,7 @@ Deno.test({
}
await Promise.all([server(), client()]);
+ httpConn!.close();
},
});
@@ -1374,9 +1397,10 @@ Deno.test({
const data = { hello: "deno", now: "with", compressed: "body" };
+ let httpConn: Deno.HttpConn;
async function server() {
const tcpConn = await listener.accept();
- const httpConn = Deno.serveHttp(tcpConn);
+ httpConn = Deno.serveHttp(tcpConn);
const e = await httpConn.nextRequest();
assert(e);
const { request, respondWith } = e;
@@ -1385,7 +1409,6 @@ Deno.test({
headers: { "content-type": "application/json" },
});
await respondWith(response);
- httpConn.close();
listener.close();
}
@@ -1413,6 +1436,7 @@ Deno.test({
}
await Promise.all([server(), client()]);
+ httpConn!.close();
},
});
@@ -1423,10 +1447,11 @@ Deno.test({
const hostname = "localhost";
const port = 4501;
+ let httpConn: Deno.HttpConn;
async function server() {
const listener = Deno.listen({ hostname, port });
const tcpConn = await listener.accept();
- const httpConn = Deno.serveHttp(tcpConn);
+ httpConn = Deno.serveHttp(tcpConn);
const e = await httpConn.nextRequest();
assert(e);
const { request, respondWith } = e;
@@ -1438,7 +1463,6 @@ Deno.test({
},
);
await respondWith(response);
- httpConn.close();
listener.close();
}
@@ -1464,6 +1488,7 @@ Deno.test({
}
await Promise.all([server(), client()]);
+ httpConn!.close();
},
});
@@ -1474,10 +1499,11 @@ Deno.test({
const hostname = "localhost";
const port = 4501;
+ let httpConn: Deno.HttpConn;
async function server() {
const listener = Deno.listen({ hostname, port });
const tcpConn = await listener.accept();
- const httpConn = Deno.serveHttp(tcpConn);
+ httpConn = Deno.serveHttp(tcpConn);
const e = await httpConn.nextRequest();
assert(e);
const { request, respondWith } = e;
@@ -1492,7 +1518,6 @@ Deno.test({
},
);
await respondWith(response);
- httpConn.close();
listener.close();
}
@@ -1518,6 +1543,7 @@ Deno.test({
}
await Promise.all([server(), client()]);
+ httpConn!.close();
},
});
@@ -1528,10 +1554,11 @@ Deno.test({
const hostname = "localhost";
const port = 4501;
+ let httpConn: Deno.HttpConn;
async function server() {
const listener = Deno.listen({ hostname, port });
const tcpConn = await listener.accept();
- const httpConn = Deno.serveHttp(tcpConn);
+ httpConn = Deno.serveHttp(tcpConn);
const e = await httpConn.nextRequest();
assert(e);
const { request, respondWith } = e;
@@ -1543,7 +1570,6 @@ Deno.test({
},
);
await respondWith(response);
- httpConn.close();
listener.close();
}
@@ -1569,6 +1595,7 @@ Deno.test({
}
await Promise.all([server(), client()]);
+ httpConn!.close();
},
});
@@ -1579,10 +1606,11 @@ Deno.test({
const hostname = "localhost";
const port = 4501;
+ let httpConn: Deno.HttpConn;
async function server() {
const listener = Deno.listen({ hostname, port });
const tcpConn = await listener.accept();
- const httpConn = Deno.serveHttp(tcpConn);
+ httpConn = Deno.serveHttp(tcpConn);
const e = await httpConn.nextRequest();
assert(e);
const { request, respondWith } = e;
@@ -1597,7 +1625,6 @@ Deno.test({
},
);
await respondWith(response);
- httpConn.close();
listener.close();
}
@@ -1627,6 +1654,7 @@ Deno.test({
}
await Promise.all([server(), client()]);
+ httpConn!.close();
},
});
@@ -1637,10 +1665,11 @@ Deno.test({
const hostname = "localhost";
const port = 4501;
+ let httpConn: Deno.HttpConn;
async function server() {
const listener = Deno.listen({ hostname, port });
const tcpConn = await listener.accept();
- const httpConn = Deno.serveHttp(tcpConn);
+ httpConn = Deno.serveHttp(tcpConn);
const e = await httpConn.nextRequest();
assert(e);
const { request, respondWith } = e;
@@ -1655,7 +1684,6 @@ Deno.test({
},
);
await respondWith(response);
- httpConn.close();
listener.close();
}
@@ -1684,6 +1712,7 @@ Deno.test({
}
await Promise.all([server(), client()]);
+ httpConn!.close();
},
});
@@ -1694,10 +1723,11 @@ Deno.test({
const hostname = "localhost";
const port = 4501;
+ let httpConn: Deno.HttpConn;
async function server() {
const listener = Deno.listen({ hostname, port });
const tcpConn = await listener.accept();
- const httpConn = Deno.serveHttp(tcpConn);
+ httpConn = Deno.serveHttp(tcpConn);
const e = await httpConn.nextRequest();
assert(e);
const { request, respondWith } = e;
@@ -1712,7 +1742,6 @@ Deno.test({
},
);
await respondWith(response);
- httpConn.close();
listener.close();
}
@@ -1738,6 +1767,7 @@ Deno.test({
}
await Promise.all([server(), client()]);
+ httpConn!.close();
},
});
@@ -1748,10 +1778,11 @@ Deno.test({
const hostname = "localhost";
const port = 4501;
+ let httpConn: Deno.HttpConn;
async function server() {
const listener = Deno.listen({ hostname, port });
const tcpConn = await listener.accept();
- const httpConn = Deno.serveHttp(tcpConn);
+ httpConn = Deno.serveHttp(tcpConn);
const e = await httpConn.nextRequest();
assert(e);
const { request, respondWith } = e;
@@ -1766,7 +1797,6 @@ Deno.test({
},
);
await respondWith(response);
- httpConn.close();
listener.close();
}
@@ -1792,6 +1822,7 @@ Deno.test({
}
await Promise.all([server(), client()]);
+ httpConn!.close();
},
});
@@ -1807,9 +1838,10 @@ Deno.test({
const data = { hello: "deno", now: "with", compressed: "body" };
+ let httpConn: Deno.HttpConn;
async function server() {
const tcpConn = await listener.accept();
- const httpConn = Deno.serveHttp(tcpConn);
+ httpConn = Deno.serveHttp(tcpConn);
const e = await httpConn.nextRequest();
assert(e);
const { request, respondWith } = e;
@@ -1825,7 +1857,6 @@ Deno.test({
{ headers: { "content-type": "application/json" } },
);
await respondWith(response);
- httpConn.close();
listener.close();
}
@@ -1852,6 +1883,7 @@ Deno.test({
}
await Promise.all([server(), client()]);
+ httpConn!.close();
},
});
@@ -1867,9 +1899,10 @@ Deno.test({
const data = { hello: "deno", now: "with", compressed: "body" };
+ let httpConn: Deno.HttpConn;
async function server() {
const tcpConn = await listener.accept();
- const httpConn = Deno.serveHttp(tcpConn);
+ httpConn = Deno.serveHttp(tcpConn);
const e = await httpConn.nextRequest();
assert(e);
const { request, respondWith } = e;
@@ -1885,7 +1918,6 @@ Deno.test({
{ headers: { "content-type": "application/json" } },
);
await respondWith(response);
- httpConn.close();
listener.close();
}
@@ -1913,6 +1945,7 @@ Deno.test({
}
await Promise.all([server(), client()]);
+ httpConn!.close();
},
});
@@ -1924,10 +1957,11 @@ Deno.test({
const port = 4501;
let contentLength: string;
+ let httpConn: Deno.HttpConn;
async function server() {
const listener = Deno.listen({ hostname, port });
const tcpConn = await listener.accept();
- const httpConn = Deno.serveHttp(tcpConn);
+ httpConn = Deno.serveHttp(tcpConn);
const e = await httpConn.nextRequest();
assert(e);
const { request, respondWith } = e;
@@ -1948,7 +1982,6 @@ Deno.test({
},
);
await respondWith(response);
- httpConn.close();
listener.close();
}
@@ -1977,6 +2010,7 @@ Deno.test({
}
await Promise.all([server(), client()]);
+ httpConn!.close();
},
});
@@ -1988,10 +2022,11 @@ Deno.test({
const port = 4501;
let contentLength: string;
+ let httpConn: Deno.HttpConn;
async function server() {
const listener = Deno.listen({ hostname, port });
const tcpConn = await listener.accept();
- const httpConn = Deno.serveHttp(tcpConn);
+ httpConn = Deno.serveHttp(tcpConn);
const e = await httpConn.nextRequest();
assert(e);
const { request, respondWith } = e;
@@ -2007,7 +2042,6 @@ Deno.test({
},
);
await respondWith(response);
- httpConn.close();
listener.close();
}
@@ -2037,6 +2071,7 @@ Deno.test({
}
await Promise.all([server(), client()]);
+ httpConn!.close();
},
});
@@ -2048,10 +2083,11 @@ Deno.test({
const port = 4501;
let contentLength: string;
+ let httpConn: Deno.HttpConn;
async function server() {
const listener = Deno.listen({ hostname, port });
const tcpConn = await listener.accept();
- const httpConn = Deno.serveHttp(tcpConn);
+ httpConn = Deno.serveHttp(tcpConn);
const e = await httpConn.nextRequest();
assert(e);
const { request, respondWith } = e;
@@ -2068,7 +2104,6 @@ Deno.test({
},
);
await respondWith(response);
- httpConn.close();
listener.close();
}
@@ -2095,6 +2130,7 @@ Deno.test({
}
await Promise.all([server(), client()]);
+ httpConn!.close();
},
});
@@ -2248,7 +2284,7 @@ Deno.test("upgradeHttp unix", {
const resp = new Response(null, { status: 101 });
await respondWith(resp);
await promise;
- httpConn.close();
+ httpConn!.close();
})();
await Promise.all([server, client()]);
diff --git a/core/01_core.js b/core/01_core.js
index ab9722bc1..fda3e4977 100644
--- a/core/01_core.js
+++ b/core/01_core.js
@@ -160,10 +160,11 @@
function opAsync(opName, ...args) {
const promiseId = nextPromiseId++;
+ let p = setPromise(promiseId);
const maybeError = ops[opName](promiseId, ...args);
// Handle sync error (e.g: error parsing args)
if (maybeError) return unwrapOpResult(maybeError);
- let p = PromisePrototypeThen(setPromise(promiseId), unwrapOpResult);
+ p = PromisePrototypeThen(p, unwrapOpResult);
if (opCallTracingEnabled) {
// Capture a stack trace by creating a new `Error` object. We remove the
// first 6 characters (the `Error\n` prefix) to get just the stack trace.
diff --git a/core/ops.rs b/core/ops.rs
index d22a703bd..c14fcdd7b 100644
--- a/core/ops.rs
+++ b/core/ops.rs
@@ -28,17 +28,25 @@ use std::task::Poll;
/// turn of the event loop, which is too late for certain ops.
pub struct OpCall<T>(MaybeDone<Pin<Box<dyn Future<Output = T>>>>);
+pub enum EagerPollResult<T> {
+ Ready(T),
+ Pending(OpCall<T>),
+}
+
impl<T> OpCall<T> {
/// Wraps a future, and polls the inner future immediately.
/// This should be the default choice for ops.
- pub fn eager(fut: impl Future<Output = T> + 'static) -> Self {
+ pub fn eager(fut: impl Future<Output = T> + 'static) -> EagerPollResult<T> {
let boxed = Box::pin(fut) as Pin<Box<dyn Future<Output = T>>>;
let mut inner = maybe_done(boxed);
let waker = noop_waker();
let mut cx = Context::from_waker(&waker);
let mut pinned = Pin::new(&mut inner);
- let _ = pinned.as_mut().poll(&mut cx);
- Self(inner)
+ let poll = pinned.as_mut().poll(&mut cx);
+ match poll {
+ Poll::Ready(_) => EagerPollResult::Ready(pinned.take_output().unwrap()),
+ _ => EagerPollResult::Pending(Self(inner)),
+ }
}
/// Wraps a future; the inner future is polled the usual way (lazily).
diff --git a/core/runtime.rs b/core/runtime.rs
index 4aad2bd76..2c16ddeb8 100644
--- a/core/runtime.rs
+++ b/core/runtime.rs
@@ -2145,14 +2145,54 @@ impl JsRealm {
#[inline]
pub fn queue_async_op(
- scope: &v8::Isolate,
+ state: Rc<RefCell<OpState>>,
+ scope: &mut v8::HandleScope,
+ deferred: bool,
op: impl Future<Output = (v8::Global<v8::Context>, PromiseId, OpId, OpResult)>
+ 'static,
) {
- let state_rc = JsRuntime::state(scope);
- let mut state = state_rc.borrow_mut();
- state.pending_ops.push(OpCall::eager(op));
- state.have_unpolled_ops = true;
+ match OpCall::eager(op) {
+ // This calls promise.resolve() before the control goes back to userland JS. It works something
+ // along the lines of:
+ //
+ // function opresolve(promiseId, ...) {
+ // getPromise(promiseId).resolve(...);
+ // }
+ // const p = setPromise();
+ // op.op_async(promiseId, ...); // Calls `opresolve`
+ // return p;
+ EagerPollResult::Ready((context, promise_id, op_id, mut resp))
+ if !deferred =>
+ {
+ let args = &[
+ v8::Integer::new(scope, promise_id).into(),
+ resp.to_v8(scope).unwrap(),
+ ];
+
+ let realm = JsRealm::new(context);
+ let js_recv_cb_handle =
+ realm.state(scope).borrow().js_recv_cb.clone().unwrap();
+ state.borrow().tracker.track_async_completed(op_id);
+
+ let tc_scope = &mut v8::TryCatch::new(scope);
+ let js_recv_cb = js_recv_cb_handle.open(tc_scope);
+ let this = v8::undefined(tc_scope).into();
+ js_recv_cb.call(tc_scope, this, args);
+ }
+ EagerPollResult::Ready(op) => {
+ let ready = OpCall::ready(op);
+ let state_rc = JsRuntime::state(scope);
+ let mut state = state_rc.borrow_mut();
+ state.pending_ops.push(ready);
+ state.have_unpolled_ops = true;
+ }
+ EagerPollResult::Pending(op) => {
+ let state_rc = JsRuntime::state(scope);
+ let mut state = state_rc.borrow_mut();
+ state.pending_ops.push(op);
+ state.have_unpolled_ops = true;
+ }
+ }
}
#[cfg(test)]
@@ -2194,7 +2234,7 @@ pub mod tests {
dispatch_count: Arc<AtomicUsize>,
}
- #[op]
+ #[op(deferred)]
async fn op_test(
rc_op_state: Rc<RefCell<OpState>>,
control: u8,
@@ -2256,41 +2296,6 @@ pub mod tests {
}
#[test]
- fn test_dispatch() {
- let (mut runtime, dispatch_count) = setup(Mode::Async);
- runtime
- .execute_script(
- "filename.js",
- r#"
- let control = 42;
- Deno.core.opAsync("op_test", control);
- async function main() {
- Deno.core.opAsync("op_test", control);
- }
- main();
- "#,
- )
- .unwrap();
- assert_eq!(dispatch_count.load(Ordering::Relaxed), 2);
- }
-
- #[test]
- fn test_op_async_promise_id() {
- let (mut runtime, _dispatch_count) = setup(Mode::Async);
- runtime
- .execute_script(
- "filename.js",
- r#"
- const p = Deno.core.opAsync("op_test", 42);
- if (p[Symbol.for("Deno.core.internalPromiseId")] == undefined) {
- throw new Error("missing id on returned promise");
- }
- "#,
- )
- .unwrap();
- }
-
- #[test]
fn test_ref_unref_ops() {
let (mut runtime, _dispatch_count) = setup(Mode::Async);
runtime
@@ -2345,6 +2350,41 @@ pub mod tests {
}
#[test]
+ fn test_dispatch() {
+ let (mut runtime, dispatch_count) = setup(Mode::Async);
+ runtime
+ .execute_script(
+ "filename.js",
+ r#"
+ let control = 42;
+ Deno.core.opAsync("op_test", control);
+ async function main() {
+ Deno.core.opAsync("op_test", control);
+ }
+ main();
+ "#,
+ )
+ .unwrap();
+ assert_eq!(dispatch_count.load(Ordering::Relaxed), 2);
+ }
+
+ #[test]
+ fn test_op_async_promise_id() {
+ let (mut runtime, _dispatch_count) = setup(Mode::Async);
+ runtime
+ .execute_script(
+ "filename.js",
+ r#"
+ const p = Deno.core.opAsync("op_test", 42);
+ if (p[Symbol.for("Deno.core.internalPromiseId")] == undefined) {
+ throw new Error("missing id on returned promise");
+ }
+ "#,
+ )
+ .unwrap();
+ }
+
+ #[test]
fn test_dispatch_no_zero_copy_buf() {
let (mut runtime, dispatch_count) = setup(Mode::AsyncZeroCopy(false));
runtime
diff --git a/ext/web/timers.rs b/ext/web/timers.rs
index d9ceef875..f6b2cc9e7 100644
--- a/ext/web/timers.rs
+++ b/ext/web/timers.rs
@@ -93,7 +93,7 @@ pub fn op_timer_handle(state: &mut OpState) -> ResourceId {
/// Waits asynchronously until either `millis` milliseconds have passed or the
/// [`TimerHandle`] resource given by `rid` has been canceled.
-#[op]
+#[op(deferred)]
pub async fn op_sleep(
state: Rc<RefCell<OpState>>,
millis: u64,
diff --git a/ext/websocket/lib.rs b/ext/websocket/lib.rs
index 515f798ac..fad217585 100644
--- a/ext/websocket/lib.rs
+++ b/ext/websocket/lib.rs
@@ -420,7 +420,7 @@ pub async fn op_ws_send(
Ok(())
}
-#[op]
+#[op(deferred)]
pub async fn op_ws_close(
state: Rc<RefCell<OpState>>,
rid: ResourceId,
diff --git a/ops/lib.rs b/ops/lib.rs
index f75972c51..a41ba0320 100644
--- a/ops/lib.rs
+++ b/ops/lib.rs
@@ -51,6 +51,7 @@ struct MacroArgs {
is_unstable: bool,
is_v8: bool,
must_be_fast: bool,
+ deferred: bool,
}
impl syn::parse::Parse for MacroArgs {
@@ -62,7 +63,7 @@ impl syn::parse::Parse for MacroArgs {
let vars: Vec<_> = vars.iter().map(Ident::to_string).collect();
let vars: Vec<_> = vars.iter().map(String::as_str).collect();
for var in vars.iter() {
- if !["unstable", "v8", "fast"].contains(var) {
+ if !["unstable", "v8", "fast", "deferred"].contains(var) {
return Err(syn::Error::new(
input.span(),
"Ops expect #[op] or #[op(unstable)]",
@@ -73,6 +74,7 @@ impl syn::parse::Parse for MacroArgs {
is_unstable: vars.contains(&"unstable"),
is_v8: vars.contains(&"v8"),
must_be_fast: vars.contains(&"fast"),
+ deferred: vars.contains(&"deferred"),
})
}
}
@@ -84,6 +86,7 @@ pub fn op(attr: TokenStream, item: TokenStream) -> TokenStream {
is_unstable,
is_v8,
must_be_fast,
+ deferred,
} = margs;
let func = syn::parse::<syn::ItemFn>(item).expect("expected a function");
let name = &func.sig.ident;
@@ -110,7 +113,7 @@ pub fn op(attr: TokenStream, item: TokenStream) -> TokenStream {
let asyncness = func.sig.asyncness.is_some();
let is_async = asyncness || is_future(&func.sig.output);
let v8_body = if is_async {
- codegen_v8_async(&core, &func, margs, asyncness)
+ codegen_v8_async(&core, &func, margs, asyncness, deferred)
} else {
codegen_v8_sync(&core, &func, margs)
};
@@ -173,6 +176,7 @@ fn codegen_v8_async(
f: &syn::ItemFn,
margs: MacroArgs,
asyncness: bool,
+ deferred: bool,
) -> TokenStream2 {
let MacroArgs { is_v8, .. } = margs;
let special_args = f
@@ -256,7 +260,7 @@ fn codegen_v8_async(
};
#pre_result
- #core::_ops::queue_async_op(scope, async move {
+ #core::_ops::queue_async_op(state, scope, #deferred, async move {
let result = #result_fut
#result_wrapper
(context, promise_id, op_id, #core::_ops::to_op_result(get_class, result))
diff --git a/runtime/js/40_testing.js b/runtime/js/40_testing.js
index e3a6ce324..c1ce3e726 100644
--- a/runtime/js/40_testing.js
+++ b/runtime/js/40_testing.js
@@ -147,6 +147,7 @@
// cleared can actually be removed from resource table, otherwise
// false positives may occur (https://github.com/denoland/deno/issues/4591)
await opSanitizerDelay();
+ await opSanitizerDelay();
}
if (shouldSkipSanitizers(desc)) return;
diff --git a/runtime/ops/web_worker.rs b/runtime/ops/web_worker.rs
index 184ebfddb..3c85d7b9e 100644
--- a/runtime/ops/web_worker.rs
+++ b/runtime/ops/web_worker.rs
@@ -39,7 +39,7 @@ fn op_worker_post_message(
Ok(())
}
-#[op]
+#[op(deferred)]
async fn op_worker_recv_message(
state: Rc<RefCell<OpState>>,
) -> Result<Option<JsMessageData>, AnyError> {