summaryrefslogtreecommitdiff
path: root/cli/tests/unit/serve_test.ts
diff options
context:
space:
mode:
Diffstat (limited to 'cli/tests/unit/serve_test.ts')
-rw-r--r--cli/tests/unit/serve_test.ts359
1 files changed, 299 insertions, 60 deletions
diff --git a/cli/tests/unit/serve_test.ts b/cli/tests/unit/serve_test.ts
index 32d436d04..8344f1be5 100644
--- a/cli/tests/unit/serve_test.ts
+++ b/cli/tests/unit/serve_test.ts
@@ -2,6 +2,7 @@
// deno-lint-ignore-file
+import { assertMatch } from "https://deno.land/std@v0.42.0/testing/asserts.ts";
import { Buffer, BufReader, BufWriter } from "../../../test_util/std/io/mod.ts";
import { TextProtoReader } from "../testdata/run/textproto.ts";
import {
@@ -31,6 +32,27 @@ function onListen<T>(
};
}
+Deno.test(async function httpServerShutsDownPortBeforeResolving() {
+ const ac = new AbortController();
+ const listeningPromise = deferred();
+
+ const server = Deno.serve({
+ handler: (_req) => new Response("ok"),
+ port: 4501,
+ signal: ac.signal,
+ onListen: onListen(listeningPromise),
+ });
+
+ await listeningPromise;
+ assertThrows(() => Deno.listen({ port: 4501 }));
+
+ ac.abort();
+ await server;
+
+ const listener = Deno.listen({ port: 4501 });
+ listener!.close();
+});
+
Deno.test(async function httpServerCanResolveHostnames() {
const ac = new AbortController();
const listeningPromise = deferred();
@@ -120,6 +142,71 @@ Deno.test({ permissions: { net: true } }, async function httpServerBasic() {
await server;
});
+Deno.test({ permissions: { net: true } }, async function httpServerOnError() {
+ const ac = new AbortController();
+ const promise = deferred();
+ const listeningPromise = deferred();
+ let requestStash: Request | null;
+
+ const server = Deno.serve({
+ handler: async (request: Request) => {
+ requestStash = request;
+ await new Promise((r) => setTimeout(r, 100));
+ throw "fail";
+ },
+ port: 4501,
+ signal: ac.signal,
+ onListen: onListen(listeningPromise),
+ onError: () => {
+ return new Response("failed: " + requestStash!.url, { status: 500 });
+ },
+ });
+
+ await listeningPromise;
+ const resp = await fetch("http://127.0.0.1:4501/", {
+ headers: { "connection": "close" },
+ });
+ const text = await resp.text();
+ ac.abort();
+ await server;
+
+ assertEquals(text, "failed: http://127.0.0.1:4501/");
+});
+
+Deno.test(
+ { permissions: { net: true } },
+ async function httpServerOnErrorFails() {
+ const ac = new AbortController();
+ const promise = deferred();
+ const listeningPromise = deferred();
+ let requestStash: Request | null;
+
+ const server = Deno.serve({
+ handler: async (request: Request) => {
+ requestStash = request;
+ await new Promise((r) => setTimeout(r, 100));
+ throw "fail";
+ },
+ port: 4501,
+ signal: ac.signal,
+ onListen: onListen(listeningPromise),
+ onError: () => {
+ throw "again";
+ },
+ });
+
+ await listeningPromise;
+ const resp = await fetch("http://127.0.0.1:4501/", {
+ headers: { "connection": "close" },
+ });
+ const text = await resp.text();
+ ac.abort();
+ await server;
+
+ assertEquals(text, "Internal Server Error");
+ },
+);
+
Deno.test({ permissions: { net: true } }, async function httpServerOverload1() {
const ac = new AbortController();
const promise = deferred();
@@ -238,7 +325,7 @@ Deno.test(
console.log = (msg) => {
try {
const match = msg.match(/Listening on http:\/\/localhost:(\d+)\//);
- assert(!!match);
+ assert(!!match, `Didn't match ${msg}`);
const port = +match[1];
assert(port > 0 && port < 65536);
} finally {
@@ -301,6 +388,109 @@ Deno.test(
},
);
+function createUrlTest(
+ name: string,
+ methodAndPath: string,
+ host: string | null,
+ expected: string,
+) {
+ Deno.test(`httpServerUrl${name}`, async () => {
+ const listeningPromise: Deferred<number> = deferred();
+ const urlPromise = deferred();
+ const ac = new AbortController();
+ const server = Deno.serve({
+ handler: async (request: Request) => {
+ urlPromise.resolve(request.url);
+ return new Response("");
+ },
+ port: 0,
+ signal: ac.signal,
+ onListen: ({ port }: { port: number }) => {
+ listeningPromise.resolve(port);
+ },
+ onError: createOnErrorCb(ac),
+ });
+
+ const port = await listeningPromise;
+ const conn = await Deno.connect({ port });
+
+ const encoder = new TextEncoder();
+ const body = `${methodAndPath} HTTP/1.1\r\n${
+ host ? ("Host: " + host + "\r\n") : ""
+ }Content-Length: 5\r\n\r\n12345`;
+ const writeResult = await conn.write(encoder.encode(body));
+ assertEquals(body.length, writeResult);
+
+ try {
+ const expectedResult = expected.replace("HOST", "localhost").replace(
+ "PORT",
+ `${port}`,
+ );
+ assertEquals(await urlPromise, expectedResult);
+ } finally {
+ ac.abort();
+ await server;
+ conn.close();
+ }
+ });
+}
+
+createUrlTest("WithPath", "GET /path", null, "http://HOST:PORT/path");
+createUrlTest(
+ "WithPathAndHost",
+ "GET /path",
+ "deno.land",
+ "http://deno.land/path",
+);
+createUrlTest(
+ "WithAbsolutePath",
+ "GET http://localhost/path",
+ null,
+ "http://localhost/path",
+);
+createUrlTest(
+ "WithAbsolutePathAndHost",
+ "GET http://localhost/path",
+ "deno.land",
+ "http://localhost/path",
+);
+createUrlTest(
+ "WithPortAbsolutePath",
+ "GET http://localhost:1234/path",
+ null,
+ "http://localhost:1234/path",
+);
+createUrlTest(
+ "WithPortAbsolutePathAndHost",
+ "GET http://localhost:1234/path",
+ "deno.land",
+ "http://localhost:1234/path",
+);
+createUrlTest(
+ "WithPortAbsolutePathAndHostWithPort",
+ "GET http://localhost:1234/path",
+ "deno.land:9999",
+ "http://localhost:1234/path",
+);
+
+createUrlTest("WithAsterisk", "OPTIONS *", null, "*");
+createUrlTest(
+ "WithAuthorityForm",
+ "CONNECT deno.land:80",
+ null,
+ "deno.land:80",
+);
+
+// TODO(mmastrac): These should probably be 400 errors
+createUrlTest("WithInvalidAsterisk", "GET *", null, "*");
+createUrlTest("WithInvalidNakedPath", "GET path", null, "path");
+createUrlTest(
+ "WithInvalidNakedAuthority",
+ "GET deno.land:1234",
+ null,
+ "deno.land:1234",
+);
+
Deno.test(
{ permissions: { net: true } },
async function httpServerGetRequestBody() {
@@ -536,7 +726,10 @@ Deno.test({ permissions: { net: true } }, async function httpServerWebSocket() {
response,
socket,
} = Deno.upgradeWebSocket(request);
- socket.onerror = () => fail();
+ socket.onerror = (e) => {
+ console.error(e);
+ fail();
+ };
socket.onmessage = (m) => {
socket.send(m.data);
socket.close(1001);
@@ -553,7 +746,10 @@ Deno.test({ permissions: { net: true } }, async function httpServerWebSocket() {
const def = deferred();
const ws = new WebSocket("ws://localhost:4501");
ws.onmessage = (m) => assertEquals(m.data, "foo");
- ws.onerror = () => fail();
+ ws.onerror = (e) => {
+ console.error(e);
+ fail();
+ };
ws.onclose = () => def.resolve();
ws.onopen = () => ws.send("foo");
@@ -564,6 +760,50 @@ Deno.test({ permissions: { net: true } }, async function httpServerWebSocket() {
Deno.test(
{ permissions: { net: true } },
+ async function httpServerWebSocketCanAccessRequest() {
+ const ac = new AbortController();
+ const listeningPromise = deferred();
+ const server = Deno.serve({
+ handler: async (request) => {
+ const {
+ response,
+ socket,
+ } = Deno.upgradeWebSocket(request);
+ socket.onerror = (e) => {
+ console.error(e);
+ fail();
+ };
+ socket.onmessage = (m) => {
+ socket.send(request.url.toString());
+ socket.close(1001);
+ };
+ return response;
+ },
+ port: 4501,
+ signal: ac.signal,
+ onListen: onListen(listeningPromise),
+ onError: createOnErrorCb(ac),
+ });
+
+ await listeningPromise;
+ const def = deferred();
+ const ws = new WebSocket("ws://localhost:4501");
+ ws.onmessage = (m) => assertEquals(m.data, "http://localhost:4501/");
+ ws.onerror = (e) => {
+ console.error(e);
+ fail();
+ };
+ ws.onclose = () => def.resolve();
+ ws.onopen = () => ws.send("foo");
+
+ await def;
+ ac.abort();
+ await server;
+ },
+);
+
+Deno.test(
+ { permissions: { net: true } },
async function httpVeryLargeRequest() {
const promise = deferred();
const listeningPromise = deferred();
@@ -682,47 +922,46 @@ Deno.test(
},
);
-// FIXME: auto request body reading is intefering with passing it as response.
-// Deno.test(
-// { permissions: { net: true } },
-// async function httpServerStreamDuplex() {
-// const promise = deferred();
-// const ac = new AbortController();
-
-// const server = Deno.serve(request => {
-// assert(request.body);
-
-// promise.resolve();
-// return new Response(request.body);
-// }, { port: 2333, signal: ac.signal });
-
-// const ts = new TransformStream();
-// const writable = ts.writable.getWriter();
-
-// const resp = await fetch("http://127.0.0.1:2333/", {
-// method: "POST",
-// body: ts.readable,
-// });
-
-// await promise;
-// assert(resp.body);
-// const reader = resp.body.getReader();
-// await writable.write(new Uint8Array([1]));
-// const chunk1 = await reader.read();
-// assert(!chunk1.done);
-// assertEquals(chunk1.value, new Uint8Array([1]));
-// await writable.write(new Uint8Array([2]));
-// 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);
-
-// ac.abort();
-// await server;
-// },
-// );
+Deno.test(
+ { permissions: { net: true } },
+ async function httpServerStreamDuplex() {
+ const promise = deferred();
+ const ac = new AbortController();
+
+ const server = Deno.serve((request) => {
+ assert(request.body);
+
+ promise.resolve();
+ return new Response(request.body);
+ }, { port: 2333, signal: ac.signal });
+
+ const ts = new TransformStream();
+ const writable = ts.writable.getWriter();
+
+ const resp = await fetch("http://127.0.0.1:2333/", {
+ method: "POST",
+ body: ts.readable,
+ });
+
+ await promise;
+ assert(resp.body);
+ const reader = resp.body.getReader();
+ await writable.write(new Uint8Array([1]));
+ const chunk1 = await reader.read();
+ assert(!chunk1.done);
+ assertEquals(chunk1.value, new Uint8Array([1]));
+ await writable.write(new Uint8Array([2]));
+ 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);
+
+ ac.abort();
+ await server;
+ },
+);
Deno.test(
{ permissions: { net: true } },
@@ -867,10 +1106,10 @@ Deno.test(
let responseText = new TextDecoder("iso-8859-1").decode(buf);
clientConn.close();
- assert(/\r\n[Xx]-[Hh]eader-[Tt]est: Æ\r\n/.test(responseText));
-
ac.abort();
await server;
+
+ assertMatch(responseText, /\r\n[Xx]-[Hh]eader-[Tt]est: Æ\r\n/);
},
);
@@ -1355,12 +1594,11 @@ createServerLengthTest("autoResponseWithKnownLengthEmpty", {
expects_con_len: true,
});
-// FIXME: https://github.com/denoland/deno/issues/15892
-// createServerLengthTest("autoResponseWithUnknownLengthEmpty", {
-// body: stream(""),
-// expects_chunked: true,
-// expects_con_len: false,
-// });
+createServerLengthTest("autoResponseWithUnknownLengthEmpty", {
+ body: stream(""),
+ expects_chunked: true,
+ expects_con_len: false,
+});
Deno.test(
{ permissions: { net: true } },
@@ -1841,6 +2079,7 @@ Deno.test(
method: "GET",
headers: { "connection": "close" },
});
+ assertEquals(resp.status, 204);
assertEquals(resp.headers.get("Content-Length"), null);
} finally {
ac.abort();
@@ -2162,11 +2401,11 @@ Deno.test(
count++;
return new Response(`hello world ${count}`);
}, {
- async onListen() {
- const res1 = await fetch("http://localhost:9000/");
+ async onListen({ port }: { port: number }) {
+ const res1 = await fetch(`http://localhost:${port}/`);
assertEquals(await res1.text(), "hello world 1");
- const res2 = await fetch("http://localhost:9000/");
+ const res2 = await fetch(`http://localhost:${port}/`);
assertEquals(await res2.text(), "hello world 2");
promise.resolve();
@@ -2199,13 +2438,13 @@ Deno.test(
return new Response("ok");
},
signal: ac.signal,
- onListen: onListen(listeningPromise),
+ onListen: ({ port }: { port: number }) => listeningPromise.resolve(port),
onError: createOnErrorCb(ac),
});
try {
- await listeningPromise;
- const resp = await fetch("http://localhost:9000/", {
+ const port = await listeningPromise;
+ const resp = await fetch(`http://localhost:${port}/`, {
headers: { connection: "close" },
method: "POST",
body: '{"sus":true}',
@@ -2238,8 +2477,8 @@ Deno.test(
},
}),
), {
- async onListen() {
- const res1 = await fetch("http://localhost:9000/");
+ async onListen({ port }) {
+ const res1 = await fetch(`http://localhost:${port}/`);
assertEquals((await res1.text()).length, 40 * 50_000);
promise.resolve();