Skip to content

Commit

Permalink
feat: use Deno.serve() instead of Deno.serveHttp()
Browse files Browse the repository at this point in the history
  • Loading branch information
kitsonk committed Mar 5, 2024
1 parent 1011b87 commit 014019b
Show file tree
Hide file tree
Showing 12 changed files with 305 additions and 275 deletions.
3 changes: 1 addition & 2 deletions _examples/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@ import {
} from "jsr:@oak/commons@0.7/server_sent_event";

import { auth, immutable, Router } from "../mod.ts";
import { createHttpError, Status } from "../deps.ts";
import { assert } from "../util.ts";
import { assert, createHttpError, Status } from "../deps.ts";

// A mock datastore where we index our books based on id.
const BOOK_DB: Record<string, Book> = {
Expand Down
12 changes: 6 additions & 6 deletions context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,12 @@ import {
Status,
UserAgent,
} from "./deps.ts";
import {
type Addr,
type Deserializer,
type UpgradeWebSocketOptions,
type WebSocketUpgrade,
} from "./types.ts";
import type { Deserializer } from "./types.ts";
import type {
Addr,
UpgradeWebSocketOptions,
WebSocketUpgrade,
} from "./types_internal.ts";

interface ContextOptions<BodyType, Params extends Record<string, string>> {
cookies: SecureCookieMap;
Expand Down
2 changes: 1 addition & 1 deletion deno.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
".": "./mod.ts",
"./context": "./context.ts",
"./handlers": "./handlers.ts",
"./http_server_native": "./http_server_native.ts",
"./http_server_deno": "./http_server_deno.ts",
"./router": "./router.ts",
"./types": "./types.ts"
},
Expand Down
1 change: 1 addition & 0 deletions deps.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// Copyright 2022-2024 the oak authors. All rights reserved.

export { assert } from "jsr:@std/assert@0.218/assert";
export {
type Data as SigningData,
type Key as SigningKey,
Expand Down
166 changes: 166 additions & 0 deletions http_server_deno.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
// Copyright 2022-2024 the oak authors. All rights reserved.

import type {
Addr,
Listener,
RequestEvent as _RequestEvent,
ServeOptions,
Server,
ServeTlsOptions,
UpgradeWebSocketOptions,
} from "./types_internal.ts";
import { createPromiseWithResolvers } from "./util.ts";

// `Deno.serve()` API

interface ServeHandlerInfo {
remoteAddr: Deno.NetAddr;
}

type ServeHandler = (
request: Request,
info: ServeHandlerInfo,
) => Response | Promise<Response>;

interface HttpServer extends AsyncDisposable {
finished: Promise<void>;
ref(): void;
unref(): void;
shutdown(): Promise<void>;
}

interface ServeInit {
handler: ServeHandler;
}

const serve:
| ((
options: ServeInit & (ServeOptions | ServeTlsOptions),
) => HttpServer)
| undefined = "Deno" in globalThis && "serve" in globalThis.Deno
? globalThis.Deno.serve.bind(globalThis.Deno)
: undefined;

class RequestEvent implements _RequestEvent {
#addr: Addr;
//deno-lint-ignore no-explicit-any
#reject: (reason?: any) => void;
#request: Request;
#resolve: (value: Response) => void;
#resolved = false;
#response: Promise<Response>;

get addr(): Addr {
return this.#addr;
}

get request(): Request {
return this.#request;
}

get response(): Promise<Response> {
return this.#response;
}

constructor(request: Request, { remoteAddr }: ServeHandlerInfo) {
this.#addr = remoteAddr;
this.#request = request;
const { resolve, reject, promise } = createPromiseWithResolvers<Response>();
this.#resolve = resolve;
this.#reject = reject;
this.#response = promise;
}

//deno-lint-ignore no-explicit-any
error(reason?: any): void {
if (this.#resolved) {
throw new Error("Request already responded to.");
}
this.#resolved = true;
this.#reject(reason);
}

respond(response: Response): void {
if (this.#resolved) {
throw new Error("Request already responded to.");
}
this.#resolved = true;
this.#resolve(response);
}

upgrade(options?: UpgradeWebSocketOptions | undefined): WebSocket {
if (this.#resolved) {
throw new Error("Request already responded to.");
}
const { response, socket } = Deno.upgradeWebSocket(this.#request, options);
this.respond(response);
return socket;
}
}

/** An abstraction for Deno's built in HTTP Server that is used to manage
* HTTP requests in a uniform way. */
export default class DenoServer implements Server {
#closed = false;
#controller?: ReadableStreamDefaultController<RequestEvent>;
#httpServer?: HttpServer;
#options: Omit<ServeOptions | ServeTlsOptions, "signal">;
#stream?: ReadableStream<RequestEvent>;

get closed(): boolean {
return this.#closed;
}

constructor(options: Omit<ServeOptions | ServeTlsOptions, "signal">) {
this.#options = options;
}

async close(): Promise<void> {
if (this.#closed) {
return;
}

if (this.#httpServer) {
this.#httpServer.unref();
await this.#httpServer.shutdown();
this.#httpServer = undefined;
}
this.#controller?.close();
this.#closed = true;
}

listen(): Promise<Listener> {
if (this.#httpServer) {
throw new Error("Server already listening.");
}
const { onListen, ...options } = this.#options;
const { promise, resolve } = createPromiseWithResolvers<Listener>();
this.#stream = new ReadableStream<RequestEvent>({
start: (controller) => {
this.#controller = controller;
this.#httpServer = serve?.({
handler: (req, info) => {
const requestEvent = new RequestEvent(req, info);
controller.enqueue(requestEvent);
return requestEvent.response;
},
onListen({ hostname, port }) {
if (onListen) {
onListen({ hostname, port });
}
resolve({ addr: { transport: "tcp", hostname, port } });
},
...options,
});
},
});
return promise;
}

[Symbol.asyncIterator](): AsyncIterableIterator<RequestEvent> {
if (!this.#stream) {
throw new TypeError("Server hasn't started listening.");
}
return this.#stream[Symbol.asyncIterator]();
}
}
141 changes: 0 additions & 141 deletions http_server_native.ts

This file was deleted.

2 changes: 1 addition & 1 deletion mod.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
export { type Context } from "./context.ts";
export { type SigningData, type SigningKey } from "./deps.ts";
export { auth, immutable } from "./handlers.ts";
export { NativeHttpServer } from "./http_server_native.ts";
export { default as DenoServer } from "./http_server_deno.ts";
export {
HandledEvent,
NotFoundEvent,
Expand Down
8 changes: 3 additions & 5 deletions router.test.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
// Copyright 2022-2024 the oak authors. All rights reserved.

import { Status } from "./deps.ts";
import { assertEquals } from "./deps_test.ts";
import { assert, assertEquals } from "./deps_test.ts";
import { Router, RouterRequestEvent } from "./router.ts";
import { assert } from "./util.ts";

Deno.test({
name: "Router - basic usage",
Expand Down Expand Up @@ -99,13 +98,12 @@ Deno.test({
);
return promise;
});
router.addEventListener("listen", ({ port, hostname }) => {
router.addEventListener("listen", ({ hostname, port }) => {
rp.push(fetch(`http://${hostname}:${port}/`).then((r) => r.text()));
rp.push(fetch(`http://${hostname}:${port}/`).then((r) => r.text()));
setTimeout(() => abortController.abort(), 100);
});
await router.listen({ signal });
// deno-lint-ignore no-explicit-any
return Promise.all(rp) as Promise<any>;
return Promise.all(rp).then(() => {});
},
});
Loading

0 comments on commit 014019b

Please sign in to comment.