diff options
Diffstat (limited to 'cli')
-rw-r--r-- | cli/bench/http/deno_flash_hono_router.js | 2 | ||||
-rw-r--r-- | cli/bench/http/deno_flash_send_file.js | 4 | ||||
-rw-r--r-- | cli/bench/http/deno_http_flash.js | 8 | ||||
-rw-r--r-- | cli/bench/http/deno_reactdom_ssr_flash.jsx | 12 | ||||
-rw-r--r-- | cli/bench/testdata/deno_upgrade_http.js | 8 | ||||
-rw-r--r-- | cli/dts/lib.deno.unstable.d.ts | 97 | ||||
-rw-r--r-- | cli/tests/unit/flash_test.ts | 188 |
7 files changed, 197 insertions, 122 deletions
diff --git a/cli/bench/http/deno_flash_hono_router.js b/cli/bench/http/deno_flash_hono_router.js index 4c3336c63..af6adc9ba 100644 --- a/cli/bench/http/deno_flash_hono_router.js +++ b/cli/bench/http/deno_flash_hono_router.js @@ -7,4 +7,4 @@ const [hostname, port] = addr.split(":"); const app = new Hono(); app.get("/", (c) => c.text("Hello, World!")); -Deno.serve({ fetch: app.fetch, port: Number(port), hostname }); +Deno.serve(app.fetch, { port: Number(port), hostname }); diff --git a/cli/bench/http/deno_flash_send_file.js b/cli/bench/http/deno_flash_send_file.js index 81e8c4991..db2ad7a82 100644 --- a/cli/bench/http/deno_flash_send_file.js +++ b/cli/bench/http/deno_flash_send_file.js @@ -6,9 +6,9 @@ const { serve } = Deno; const path = new URL("../testdata/128k.bin", import.meta.url).pathname; -function fetch() { +function handler() { const file = Deno.openSync(path); return new Response(file.readable); } -serve({ fetch, hostname, port: Number(port) }); +serve(handler, { hostname, port: Number(port) }); diff --git a/cli/bench/http/deno_http_flash.js b/cli/bench/http/deno_http_flash.js index 3823bb9cd..7c486f422 100644 --- a/cli/bench/http/deno_http_flash.js +++ b/cli/bench/http/deno_http_flash.js @@ -4,12 +4,8 @@ const addr = Deno.args[0] || "127.0.0.1:4500"; const [hostname, port] = addr.split(":"); const { serve } = Deno; -function fetch() { +function handler() { return new Response("Hello World"); } -serve({ - fetch, - hostname, - port, -}); +serve(handler, { hostname, port }); diff --git a/cli/bench/http/deno_reactdom_ssr_flash.jsx b/cli/bench/http/deno_reactdom_ssr_flash.jsx index 0d749c634..42c955624 100644 --- a/cli/bench/http/deno_reactdom_ssr_flash.jsx +++ b/cli/bench/http/deno_reactdom_ssr_flash.jsx @@ -18,12 +18,6 @@ const headers = { }, }; -serve( - { - fetch: async () => { - return new Response(await renderToReadableStream(<App />), headers); - }, - hostname, - port, - }, -); +serve({ hostname, port }, async () => { + return new Response(await renderToReadableStream(<App />), headers); +}); diff --git a/cli/bench/testdata/deno_upgrade_http.js b/cli/bench/testdata/deno_upgrade_http.js index e3252ffd1..7274459c9 100644 --- a/cli/bench/testdata/deno_upgrade_http.js +++ b/cli/bench/testdata/deno_upgrade_http.js @@ -1,14 +1,10 @@ const { serve, upgradeHttp } = Deno; const u8 = Deno.core.encode("HTTP/1.1 101 Switching Protocols\r\n\r\n"); -async function fetch(req) { +async function handler(req) { const [conn, _firstPacket] = upgradeHttp(req); await conn.write(u8); await conn.close(); } -serve({ - fetch, - hostname: "127.0.0.1", - port: 9000, -}); +serve(handler, { hostname: "127.0.0.1", port: 9000 }); diff --git a/cli/dts/lib.deno.unstable.d.ts b/cli/dts/lib.deno.unstable.d.ts index 29e64379e..a1817deb0 100644 --- a/cli/dts/lib.deno.unstable.d.ts +++ b/cli/dts/lib.deno.unstable.d.ts @@ -1231,26 +1231,20 @@ declare namespace Deno { export function unrefTimer(id: number): void; /** + * A handler for HTTP requests. Consumes a request and returns a response. + * + * If a handler throws, the server calling the handler will assume the impact + * of the error is isolated to the individual request. It will catch the error + * and if necessary will close the underlying connection. + * * @category HTTP Server */ - export interface ServeInit extends Partial<Deno.ListenOptions> { - /** - * A handler for HTTP requests. Consumes a request and returns a response. - * - * Handler allows `void` or `Promise<void>` return type to enable - * request upgrades using `Deno.upgradeHttp()` API. It is callers responsibility - * to write response manually to the returned connection. Failing to do so - * (or not returning a response without an upgrade) will cause the connection - * to hang. - * - * If a handler throws, the server calling the handler will assume the impact - * of the error is isolated to the individual request. It will catch the error - * and close the underlying connection. - */ - fetch: ( - request: Request, - ) => Response | Promise<Response> | void | Promise<void>; + export type ServeHandler = (request: Request) => Response | Promise<Response>; + /** + * @category HTTP Server + */ + export interface ServeOptions extends Partial<Deno.ListenOptions> { /** An AbortSignal to close the server and all connections. */ signal?: AbortSignal; @@ -1264,7 +1258,7 @@ declare namespace Deno { /** * @category HTTP Server */ - export interface ServeTlsInit extends ServeInit { + export interface ServeTlsOptions extends ServeOptions { /** Server private key in PEM format */ cert: string; @@ -1272,7 +1266,17 @@ declare namespace Deno { key: string; } - /** Serves HTTP requests with the given handler. + /** + * @category HTTP Server + */ + export interface ServeInit { + /** The handler to invoke to process each incoming request. */ + handler: ServeHandler; + } + + /** **UNSTABLE**: new API, yet to be vetted. + * + * Serves HTTP requests with the given handler. * * You can specify an object with a port and hostname option, which is the * address to listen on. The default is port 9000 on hostname "127.0.0.1". @@ -1280,69 +1284,64 @@ declare namespace Deno { * The below example serves with the port 9000. * * ```ts - * Deno.serve({ - * fetch: (_req) => new Response("Hello, world") - * }); + * Deno.serve((_req) => new Response("Hello, world")); * ``` * - * You can change the listening address by the `hostname` and `port` options. - * The below example serves with the port 3000. + * You can change the address to listen on using the `hostname` and `port` + * options. The below example serves on port 3000. * * ```ts - * Deno.serve({ - * fetch: (_req) => new Response("Hello, world"), - * port: 3000 - * }); + * Deno.serve({ port: 3000 }, (_req) => new Response("Hello, world")); * ``` * - * You can close the server by passing a `signal` option. To wait for the server - * to close, await the promise returned from the `Deno.serve` API. + * You can stop the server with an AbortSignal. The abort signal needs to be + * passed as the `signal` option in the options bag. The server aborts when + * the abort signal is aborted. To wait for the server to close, await the + * promise returned from the `Deno.serve` API. * * ```ts * const ac = new AbortController(); * - * Deno.serve({ - * fetch: (_req) => new Response("Hello, world"), - * signal: ac.signal - * }).then(() => { - * console.log("Server closed"); - * }); + * Deno.serve({ signal: ac.signal }, (_req) => new Response("Hello, world")) + * .then(() => console.log("Server closed")); * * console.log("Closing server..."); * ac.abort(); * ``` * - * `Deno.serve` function prints the message `Listening on http://<hostname>:<port>/` - * on start-up by default. If you like to change this message, you can specify - * `onListen` option to override it. + * By default `Deno.serve` prints the message `Listening on http://<hostname>:<port>/` + * on start up. If you like to change this behaviour, you can specify a custom + * `onListen` callback. * * ```ts * Deno.serve({ - * fetch: (_req) => new Response("Hello, world"), * onListen({ port, hostname }) { * console.log(`Server started at http://${hostname}:${port}`); * // ... more info specific to your server .. * }, + * handler: (_req) => new Response("Hello, world"), * }); * ``` * - * To enable TLS you must specify `key` and `cert` options. + * To enable TLS you must specify the `key` and `cert` options. * * ```ts * const cert = "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----\n"; * const key = "-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----\n"; - * Deno.serve({ - * fetch: (_req) => new Response("Hello, world"), - * cert, - * key - * }); - * - * @param options The options. See `ServeInit` and `ServeTlsInit` documentation for details. + * Deno.serve({ cert, key }, (_req) => new Response("Hello, world")); * * @category HTTP Server */ export function serve( - options?: ServeInit | ServeTlsInit, + handler: ServeHandler, + options?: ServeOptions | ServeTlsOptions, + ): Promise<void>; + export function serve( + options: ServeOptions | ServeTlsOptions, + handler: ServeHandler, + ): Promise<void>; + export function serve( + options: ServeInit & (ServeOptions | ServeTlsOptions), ): Promise<void>; /** **UNSTABLE**: new API, yet to be vetter. diff --git a/cli/tests/unit/flash_test.ts b/cli/tests/unit/flash_test.ts index fb96e66be..f59484291 100644 --- a/cli/tests/unit/flash_test.ts +++ b/cli/tests/unit/flash_test.ts @@ -12,11 +12,9 @@ import { assert, assertEquals, assertRejects, - assertStrictEquals, assertThrows, Deferred, deferred, - delay, fail, } from "./test_util.ts"; @@ -37,10 +35,10 @@ function onListen<T>( } Deno.test(async function httpServerInvalidHostname() { - assertThrows( + await assertRejects( () => Deno.serve({ - fetch: (_req) => new Response("ok"), + handler: (_req) => new Response("ok"), hostname: "localhost", }), TypeError, @@ -54,7 +52,7 @@ Deno.test({ permissions: { net: true } }, async function httpServerBasic() { const listeningPromise = deferred(); const server = Deno.serve({ - fetch: async (request) => { + handler: async (request) => { // FIXME(bartlomieju): // make sure that request can be inspected console.log(request); @@ -84,11 +82,103 @@ Deno.test({ permissions: { net: true } }, async function httpServerBasic() { await server; }); +Deno.test({ permissions: { net: true } }, async function httpServerOverload1() { + const ac = new AbortController(); + const promise = deferred(); + const listeningPromise = deferred(); + + const server = Deno.serve({ + port: 4501, + signal: ac.signal, + onListen: onListen(listeningPromise), + onError: createOnErrorCb(ac), + }, async (request) => { + // FIXME(bartlomieju): + // make sure that request can be inspected + console.log(request); + assertEquals(new URL(request.url).href, "http://127.0.0.1:4501/"); + assertEquals(await request.text(), ""); + promise.resolve(); + return new Response("Hello World", { headers: { "foo": "bar" } }); + }); + + await listeningPromise; + const resp = await fetch("http://127.0.0.1:4501/", { + headers: { "connection": "close" }, + }); + await promise; + const clone = resp.clone(); + const text = await resp.text(); + assertEquals(text, "Hello World"); + assertEquals(resp.headers.get("foo"), "bar"); + const cloneText = await clone.text(); + assertEquals(cloneText, "Hello World"); + ac.abort(); + await server; +}); + +Deno.test({ permissions: { net: true } }, async function httpServerOverload2() { + const ac = new AbortController(); + const promise = deferred(); + const listeningPromise = deferred(); + + const server = Deno.serve(async (request) => { + // FIXME(bartlomieju): + // make sure that request can be inspected + console.log(request); + assertEquals(new URL(request.url).href, "http://127.0.0.1:4501/"); + assertEquals(await request.text(), ""); + promise.resolve(); + return new Response("Hello World", { headers: { "foo": "bar" } }); + }, { + port: 4501, + signal: ac.signal, + onListen: onListen(listeningPromise), + onError: createOnErrorCb(ac), + }); + + await listeningPromise; + const resp = await fetch("http://127.0.0.1:4501/", { + headers: { "connection": "close" }, + }); + await promise; + const clone = resp.clone(); + const text = await resp.text(); + assertEquals(text, "Hello World"); + assertEquals(resp.headers.get("foo"), "bar"); + const cloneText = await clone.text(); + assertEquals(cloneText, "Hello World"); + ac.abort(); + await server; +}); + +Deno.test( + { permissions: { net: true } }, + async function httpServerErrorOverloadMissingHandler() { + // @ts-ignore - testing invalid overload + await assertRejects(() => Deno.serve(), TypeError, "handler"); + // @ts-ignore - testing invalid overload + await assertRejects(() => Deno.serve({}), TypeError, "handler"); + await assertRejects( + // @ts-ignore - testing invalid overload + () => Deno.serve({ handler: undefined }), + TypeError, + "handler", + ); + await assertRejects( + // @ts-ignore - testing invalid overload + () => Deno.serve(undefined, { handler: () => {} }), + TypeError, + "handler", + ); + }, +); + Deno.test({ permissions: { net: true } }, async function httpServerPort0() { const ac = new AbortController(); const server = Deno.serve({ - fetch() { + handler() { return new Response("Hello World"); }, port: 0, @@ -120,7 +210,7 @@ Deno.test( try { const server = Deno.serve({ - fetch() { + handler() { return new Response("Hello World"); }, hostname: "0.0.0.0", @@ -145,7 +235,7 @@ Deno.test( let headers: Headers; const server = Deno.serve({ - fetch: async (request) => { + handler: async (request) => { await request.text(); headers = request.headers; promise.resolve(); @@ -182,7 +272,7 @@ Deno.test( let req: Request; const server = Deno.serve({ - fetch: async (request) => { + handler: async (request) => { await request.text(); req = request; promise.resolve(); @@ -226,7 +316,7 @@ Deno.test( const listeningPromise = deferred(); const server = Deno.serve({ - fetch: (request) => { + handler: (request) => { assertEquals(request.body, null); promise.resolve(); return new Response("", { headers: {} }); @@ -270,7 +360,7 @@ Deno.test( const listeningPromise = deferred(); const ac = new AbortController(); const server = Deno.serve({ - fetch: (request) => { + handler: (request) => { assert(!request.body); return new Response(stream.readable); }, @@ -300,7 +390,7 @@ Deno.test( const listeningPromise = deferred(); const ac = new AbortController(); const server = Deno.serve({ - fetch: async (request) => { + handler: async (request) => { const reqBody = await request.text(); assertEquals("hello world", reqBody); return new Response("yo"); @@ -328,7 +418,7 @@ Deno.test({ permissions: { net: true } }, async function httpServerClose() { const ac = new AbortController(); const listeningPromise = deferred(); const server = Deno.serve({ - fetch: () => new Response("ok"), + handler: () => new Response("ok"), port: 4501, signal: ac.signal, onListen: onListen(listeningPromise), @@ -348,7 +438,7 @@ Deno.test( const ac = new AbortController(); const listeningPromise = deferred(); const server = Deno.serve({ - fetch: () => new Response(new Blob([])), + handler: () => new Response(new Blob([])), port: 4501, signal: ac.signal, onListen: onListen(listeningPromise), @@ -369,7 +459,7 @@ Deno.test({ permissions: { net: true } }, async function httpServerWebSocket() { const ac = new AbortController(); const listeningPromise = deferred(); const server = Deno.serve({ - fetch: async (request) => { + handler: async (request) => { const { response, socket, @@ -409,7 +499,7 @@ Deno.test( let headers: Headers; const server = Deno.serve({ - fetch: async (request) => { + handler: async (request) => { headers = request.headers; promise.resolve(); return new Response(""); @@ -448,7 +538,7 @@ Deno.test( let headers: Headers; let text: string; const server = Deno.serve({ - fetch: async (request) => { + handler: async (request) => { headers = request.headers; text = await request.text(); promise.resolve(); @@ -493,7 +583,7 @@ Deno.test( const listeningPromise = deferred(); const server = Deno.serve({ - fetch: () => { + handler: () => { promise.resolve(); return new Response(""); }, @@ -643,7 +733,7 @@ Deno.test( } const finished = Deno.serve({ - fetch: () => { + handler: () => { promise.resolve(); return new Response(periodicStream()); }, @@ -674,7 +764,7 @@ Deno.test( const promise = deferred(); const ac = new AbortController(); const server = Deno.serve({ - fetch: (request) => { + handler: (request) => { assertEquals(request.headers.get("X-Header-Test"), "á"); promise.resolve(); return new Response("hello", { headers: { "X-Header-Test": "Æ" } }); @@ -720,7 +810,7 @@ Deno.test( const ac = new AbortController(); const server = Deno.serve({ - fetch: async (request) => { + handler: async (request) => { // FIXME: // assertEquals(new URL(request.url).href, "http://127.0.0.1:4501/"); assertEquals(await request.text(), ""); @@ -774,7 +864,7 @@ Deno.test( const ac = new AbortController(); const server = Deno.serve({ - fetch: async (request) => { + handler: async (request) => { assertEquals(await request.text(), ""); assertEquals(request.headers.get("cookie"), "foo=bar, bar=foo"); promise.resolve(); @@ -817,7 +907,7 @@ Deno.test( file.close(); const server = Deno.serve({ - fetch: async (request) => { + handler: async (request) => { const f = await Deno.open(tmpFile, { read: true }); promise.resolve(); return new Response(f.readable); @@ -853,7 +943,7 @@ Deno.test( const port = 4501; const server = Deno.serve({ - fetch: () => { + handler: () => { promise.resolve(); return new Response("ok"); }, @@ -888,7 +978,7 @@ Deno.test( const ac = new AbortController(); const server = Deno.serve({ - fetch: async (request) => { + handler: async (request) => { assertEquals(request.body, null); promise.resolve(); return new Response(new Uint8Array([128])); @@ -918,7 +1008,7 @@ Deno.test("upgradeHttp tcp", async () => { const promise2 = deferred(); const ac = new AbortController(); const signal = ac.signal; - const fetch = async (req: Request) => { + const handler = async (req: Request) => { const [conn, _] = await Deno.upgradeHttp(req); await conn.write( @@ -938,7 +1028,7 @@ Deno.test("upgradeHttp tcp", async () => { conn.close(); }; const server = Deno.serve({ - fetch, + handler: handler as any, port: 4501, signal, onListen: onListen(listeningPromise), @@ -980,7 +1070,7 @@ Deno.test( const ac = new AbortController(); const server = Deno.serve({ - fetch: async (request) => { + handler: async (request) => { assertEquals(request.method, "GET"); assertEquals(request.headers.get("host"), "deno.land"); promise.resolve(); @@ -1014,7 +1104,7 @@ Deno.test( const ac = new AbortController(); const server = Deno.serve({ - fetch: async (request) => { + handler: async (request) => { assertEquals(request.method, "GET"); assertEquals(request.headers.get("server"), "hello\tworld"); promise.resolve(); @@ -1048,7 +1138,7 @@ Deno.test( const ac = new AbortController(); const server = Deno.serve({ - fetch: async (request) => { + handler: async (request) => { assertEquals(request.method, "GET"); assertEquals(await request.text(), ""); promise.resolve(); @@ -1084,7 +1174,7 @@ Deno.test( const ac = new AbortController(); const server = Deno.serve({ - fetch: async (request) => { + handler: async (request) => { assertEquals(request.method, "POST"); assertEquals(await request.text(), "I'm a good request."); promise.resolve(); @@ -1130,7 +1220,7 @@ function createServerLengthTest(name: string, testCase: TestCase) { const listeningPromise = deferred(); const server = Deno.serve({ - fetch: async (request) => { + handler: async (request) => { assertEquals(request.method, "GET"); promise.resolve(); return new Response(testCase.body, testCase.headers ?? {}); @@ -1262,7 +1352,7 @@ Deno.test( const ac = new AbortController(); const server = Deno.serve({ - fetch: async (request) => { + handler: async (request) => { assertEquals(request.method, "GET"); promises[reqCount].resolve(); reqCount++; @@ -1325,7 +1415,7 @@ Deno.test( const ac = new AbortController(); const server = Deno.serve({ - fetch: async (request) => { + handler: async (request) => { assertEquals(request.method, "POST"); assertEquals(request.headers.get("content-length"), "5"); assertEquals(await request.text(), "hello"); @@ -1361,7 +1451,7 @@ Deno.test( const ac = new AbortController(); const listeningPromise = deferred(); const server = Deno.serve({ - fetch: () => { + handler: () => { throw new Error("unreachable"); }, port: 4503, @@ -1401,7 +1491,7 @@ Deno.test( const listeningPromise = deferred(); const server = Deno.serve({ - fetch: async (request) => { + handler: async (request) => { assertEquals(request.method, "POST"); assertEquals(await request.text(), "qwert"); promise.resolve(); @@ -1438,7 +1528,7 @@ Deno.test( const listeningPromise = deferred(); const server = Deno.serve({ - fetch: async (r) => { + handler: async (r) => { promise.resolve(); assertEquals(await r.text(), "12345"); return new Response("ok"); @@ -1474,7 +1564,7 @@ Deno.test( const listeningPromise = deferred(); const server = Deno.serve({ - fetch: () => { + handler: () => { promise.resolve(); return new Response("foo bar baz"); }, @@ -1522,7 +1612,7 @@ Deno.test( await file.write(data); file.close(); const server = Deno.serve({ - fetch: async () => { + handler: async () => { const f = await Deno.open(tmpFile, { read: true }); promise.resolve(); return new Response(f.readable, { status: 200 }); @@ -1557,7 +1647,7 @@ Deno.test( file.close(); const server = Deno.serve({ - fetch: async (request) => { + handler: async (request) => { assertEquals(new Uint8Array(await request.arrayBuffer()), data); promise.resolve(); return new Response("ok"); @@ -1594,7 +1684,7 @@ Deno.test( const port = 4501; const server = Deno.serve({ - fetch: () => new Response("Hello World"), + handler: () => new Response("Hello World"), hostname, port, signal: ac.signal, @@ -1629,7 +1719,7 @@ Deno.test( const promise = deferred(); const server = Deno.serve({ - fetch: async (req) => { + handler: async (req) => { assertEquals(await req.text(), ""); promise.resolve(); return new Response("ok"); @@ -1664,7 +1754,7 @@ Deno.test( const listeningPromise = deferred(); const server = Deno.serve({ - fetch: () => { + handler: () => { throw new Error("oops"); }, port: 4503, @@ -1713,7 +1803,7 @@ Deno.test( const listeningPromise = deferred(); const server = Deno.serve({ - fetch: () => { + handler: () => { promise.resolve(); return new Response(null, { status: 304 }); }, @@ -1757,7 +1847,7 @@ Deno.test( const listeningPromise = deferred(); const server = Deno.serve({ - fetch: async (req) => { + handler: async (req) => { promise.resolve(); assertEquals(await req.text(), "hello"); return new Response(null, { status: 304 }); @@ -1818,7 +1908,7 @@ Deno.test( const ac = new AbortController(); const server = Deno.serve({ - fetch: async (req) => { + handler: async (req) => { promise.resolve(); assertEquals(await req.text(), ""); return new Response(null, { status: 304 }); @@ -1872,7 +1962,7 @@ for (const [name, req] of badRequests) { const listeningPromise = deferred(); const server = Deno.serve({ - fetch: () => { + handler: () => { throw new Error("oops"); }, port: 4503, @@ -1917,7 +2007,7 @@ Deno.test( const listeningPromise = deferred(); const server = Deno.serve({ - fetch: () => new Response(null), + handler: () => new Response(null), port: 4503, signal: ac.signal, onListen: onListen(listeningPromise), @@ -1957,7 +2047,7 @@ Deno.test( let reqCount = -1; let timerId: number | undefined; const server = Deno.serve({ - fetch: async (req) => { + handler: async (req) => { reqCount++; if (reqCount === 0) { const msg = new TextEncoder().encode("data: hello\r\n\r\n"); |