diff options
Diffstat (limited to 'cli')
-rw-r--r-- | cli/bench/http/deno_http_serve_https.js | 18 | ||||
-rw-r--r-- | cli/tests/unit/serve_test.ts | 359 | ||||
-rw-r--r-- | cli/tests/unit/websocket_test.ts | 16 |
3 files changed, 333 insertions, 60 deletions
diff --git a/cli/bench/http/deno_http_serve_https.js b/cli/bench/http/deno_http_serve_https.js new file mode 100644 index 000000000..cea659e09 --- /dev/null +++ b/cli/bench/http/deno_http_serve_https.js @@ -0,0 +1,18 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. + +const addr = Deno.args[0] ?? "127.0.0.1:4500"; +const [hostname, port] = addr.split(":"); +const { serve } = Deno; + +function readFileSync(file) { + return Deno.readTextFileSync(new URL(file, import.meta.url).pathname); +} + +const CERT = readFileSync("../../tests/testdata/tls/localhost.crt"); +const KEY = readFileSync("../../tests/testdata/tls/localhost.key"); + +function handler() { + return new Response("Hello World"); +} + +serve(handler, { hostname, port, reusePort: true, cert: CERT, key: KEY }); 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(); diff --git a/cli/tests/unit/websocket_test.ts b/cli/tests/unit/websocket_test.ts index 997d8f0df..999eede41 100644 --- a/cli/tests/unit/websocket_test.ts +++ b/cli/tests/unit/websocket_test.ts @@ -43,6 +43,22 @@ Deno.test(async function websocketPingPong() { ws.close(); }); +// TODO(mmastrac): This requires us to ignore bad certs +// Deno.test(async function websocketSecureConnect() { +// const promise = deferred(); +// const ws = new WebSocket("wss://localhost:4243/"); +// assertEquals(ws.url, "wss://localhost:4243/"); +// ws.onerror = (error) => { +// console.log(error); +// fail(); +// }; +// ws.onopen = () => ws.close(); +// ws.onclose = () => { +// promise.resolve(); +// }; +// await promise; +// }); + // https://github.com/denoland/deno/issues/18700 Deno.test( { sanitizeOps: false, sanitizeResources: false }, |