Added a very complicated AsyncIterableReader class.

The class was already implemented in
"https://deno.land/std@0.144.0/streams/conversion.ts".  So, I litterally
lost 1 day of work!  I hate myself.
This commit is contained in:
didier amyot committed 2023-06-12 12:02:15 -04:00
1 parent 17a1b4bed3
commit 0d627c9051
125 files changed
+26943 -83

No files matched your search

+162
View File
@@ -0,0 +1,162 @@
// Copyright 2018-2023 the oak authors. All rights reserved. MIT license.
import type { Application, State } from "./application.ts";
import { NativeRequest } from "./http_server_native_request.ts";
import type { HttpConn, Listener, Server } from "./types.d.ts";
import { assert, isListenTlsOptions } from "./util.ts";
// this is included so when down-emitting to npm/Node.js, ReadableStream has
// async iterators
declare global {
// deno-lint-ignore no-explicit-any
interface ReadableStream<R = any> {
[Symbol.asyncIterator](options?: {
preventCancel?: boolean;
}): AsyncIterableIterator<R>;
}
}
export type Respond = (r: Response | Promise<Response>) => void;
// This type is part of Deno, but not part of lib.dom.d.ts, therefore add it here
// so that type checking can occur properly under `lib.dom.d.ts`.
interface ReadableStreamDefaultControllerCallback<R> {
(controller: ReadableStreamDefaultController<R>): void | PromiseLike<void>;
}
const serveHttp: (conn: Deno.Conn) => HttpConn = "serveHttp" in Deno
// deno-lint-ignore no-explicit-any
? (Deno as any).serveHttp.bind(Deno)
: undefined;
/** The oak abstraction of the Deno native HTTP server which is used internally
* for handling native HTTP requests. Generally users of oak do not need to
* worry about this class. */
// deno-lint-ignore no-explicit-any
export class HttpServer<AS extends State = Record<string, any>>
implements Server<NativeRequest> {
#app: Application<AS>;
#closed = false;
#listener?: Deno.Listener;
#httpConnections: Set<HttpConn> = new Set();
#options: Deno.ListenOptions | Deno.ListenTlsOptions;
constructor(
app: Application<AS>,
options: Deno.ListenOptions | Deno.ListenTlsOptions,
) {
if (!("serveHttp" in Deno)) {
throw new Error(
"The native bindings for serving HTTP are not available.",
);
}
this.#app = app;
this.#options = options;
}
get app(): Application<AS> {
return this.#app;
}
get closed(): boolean {
return this.#closed;
}
close(): void {
this.#closed = true;
if (this.#listener) {
this.#listener.close();
this.#listener = undefined;
}
for (const httpConn of this.#httpConnections) {
try {
httpConn.close();
} catch (error) {
if (!(error instanceof Deno.errors.BadResource)) {
throw error;
}
}
}
this.#httpConnections.clear();
}
listen(): Listener {
return (this.#listener = isListenTlsOptions(this.#options)
? Deno.listenTls(this.#options)
: Deno.listen(this.#options)) as Listener;
}
#trackHttpConnection(httpConn: HttpConn): void {
this.#httpConnections.add(httpConn);
}
#untrackHttpConnection(httpConn: HttpConn): void {
this.#httpConnections.delete(httpConn);
}
[Symbol.asyncIterator](): AsyncIterableIterator<NativeRequest> {
const start: ReadableStreamDefaultControllerCallback<NativeRequest> = (
controller,
) => {
// deno-lint-ignore no-this-alias
const server = this;
async function serve(conn: Deno.Conn) {
const httpConn = serveHttp(conn);
server.#trackHttpConnection(httpConn);
while (true) {
try {
const requestEvent = await httpConn.nextRequest();
if (requestEvent === null) {
return;
}
const nativeRequest = new NativeRequest(requestEvent, { conn });
controller.enqueue(nativeRequest);
// if we await here, this becomes blocking, and really all we want
// it to dispatch any errors that occur on the promise
nativeRequest.donePromise.catch((error) => {
server.app.dispatchEvent(new ErrorEvent("error", { error }));
});
} catch (error) {
server.app.dispatchEvent(new ErrorEvent("error", { error }));
}
if (server.closed) {
server.#untrackHttpConnection(httpConn);
httpConn.close();
controller.close();
}
}
}
const listener = this.#listener;
assert(listener);
async function accept() {
while (true) {
try {
const conn = await listener!.accept();
serve(conn);
} catch (error) {
if (!server.closed) {
server.app.dispatchEvent(new ErrorEvent("error", { error }));
}
}
if (server.closed) {
controller.close();
return;
}
}
}
accept();
};
const stream = new ReadableStream<NativeRequest>({ start });
return stream[Symbol.asyncIterator]();
}
}