-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathapp.ts
129 lines (108 loc) · 3.09 KB
/
app.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
import {
serve,
serveTLS,
HTTPSOptions,
HTTPOptions,
Server,
EventEmitter,
} from "./deps.ts";
import { Request } from "./request.ts";
import { Response } from "./response.ts";
import { Middleware } from "./types.d.ts";
import { Context } from "./context.ts";
import { compose } from "./compose.ts";
interface ApplicationOptions {
proxy?: boolean;
env?: string;
keys?: string[];
}
export class App extends EventEmitter {
server: Server | undefined;
middleware: Middleware[] = [];
silent: undefined | boolean = undefined;
keys: string[] | undefined;
proxy: boolean = false;
secure: boolean = false;
env: string = "development";
context: Context;
request: Request;
response: Response;
constructor(options?: ApplicationOptions) {
super();
if (options) {
this.proxy = options.proxy ?? false;
this.keys = options.keys;
if (options.env) {
this.env = options.env;
} else if (Deno.env.get("DENO_ENV") !== undefined) {
this.env = Deno.env.get("DENO_ENV")!;
}
}
this.context = Object.create(Context);
this.request = Object.create(Request);
this.response = Object.create(Response);
}
public listen(
options?: number | string | HTTPOptions | HTTPSOptions,
): Server {
switch (typeof options) {
case "undefined":
options = { port: 80 };
break;
case "number":
options = { port: options };
break;
}
const isTls = typeof options !== "string" &&
typeof (options as HTTPSOptions).certFile !== "undefined";
this.server = isTls ? serveTLS(options as HTTPSOptions) : serve(options);
this.secure = isTls;
this.handle(this.server);
return this.server;
}
private async handle(server: Server): Promise<void> {
const fnMiddleware = compose(this.middleware);
if (!this.listenerCount("error")) this.on("error", this.onerror);
for await (const req of server) {
const request = new Request(req, this.proxy, this.secure);
const response = Object.assign(
new Response(request),
this.response,
);
const ctx = Object.assign(
new Context(this, req, request, response),
{
state: {},
},
this.context,
);
this.handleRequest(ctx, fnMiddleware);
}
}
onerror(err: any): void {
if (!(err instanceof Error)) {
throw new TypeError(`non-error thrown: ${err}`);
}
if (404 === (err as any).status || (err as any).expose) return;
if (this.silent) return;
const msg = err.stack || err.toString();
console.error();
console.error(msg.replace(/^/gm, " "));
console.error();
}
public use(fn: Middleware): App {
this.middleware.push(fn);
return this;
}
private handleRequest(ctx: Context, fnMiddleware: any) {
ctx.res.status = 404;
const onerror = (err: Error) => ctx.onerror(err);
const handleResponse = () => this.respond(ctx);
return fnMiddleware(ctx).then(handleResponse).catch(onerror);
}
private async respond(
ctx: Context,
) {
return ctx.req.respond(ctx.response.toServerResponse());
}
}