1 | import * as flags from "https://deno.land/std/flags/mod.ts";
|
2 |
|
3 | class AdaptedFetchEvent extends Event implements FetchEvent {
|
4 | #event: Deno.RequestEvent;
|
5 | #request: Request;
|
6 |
|
7 | constructor(type: string, eventInitDict?: FetchEventInit);
|
8 | constructor(event: Deno.RequestEvent, hostname: string | null);
|
9 | constructor(event: string | Deno.RequestEvent, hostname?: string | null | FetchEventInit) {
|
10 | super('fetch');
|
11 | if (typeof event === 'string' || typeof hostname === 'object') throw Error('Overload not implemented');
|
12 | this.#event = event;
|
13 |
|
14 | // Workaround for immutable headers.
|
15 | // Instead of copying the request, we return a proxy that returns a custom headers object:
|
16 | const headers = new Headers([
|
17 | ...this.#event.request.headers,
|
18 | ...hostname ? [['x-forwarded-for', hostname]] : [],
|
19 | ]);
|
20 |
|
21 | this.#request = new Proxy(this.#event.request, {
|
22 | get(target, prop) {
|
23 | return prop === 'headers' ? headers : Reflect.get(target, prop);
|
24 | },
|
25 | });
|
26 | }
|
27 | get request(): Request { return this.#request };
|
28 | respondWith(r: Response | Promise<Response>): void {
|
29 | this.#event.respondWith(r);
|
30 | }
|
31 | waitUntil(_f: any): void {
|
32 |
|
33 | }
|
34 |
|
35 | get clientId(): string { return '' };
|
36 | get preloadResponse(): Promise<any> { return Promise.resolve() };
|
37 | get replacesClientId(): string { return '' };
|
38 | get resultingClientId(): string { return '' };
|
39 | }
|
40 |
|
41 |
|
42 | Object.defineProperty(self, 'FetchEvent', {
|
43 | configurable: false,
|
44 | enumerable: false,
|
45 | writable: false,
|
46 | value: AdaptedFetchEvent,
|
47 | });
|
48 |
|
49 | const NAME = 'Deno Fetch Event Adapter';
|
50 |
|
51 | (async () => {
|
52 | let server: Deno.Listener;
|
53 |
|
54 | if (!self.location) {
|
55 | throw new Error(`${NAME}: When using Deno Fetch Event Adapter, a --location is required.`)
|
56 | }
|
57 |
|
58 | if (self.location.protocol === 'https:' || self.location.port === '433') {
|
59 | const { cert: certFile, key: keyFile } = flags.parse(Deno.args, {
|
60 | alias: {
|
61 | cert: ['c', 'cert-file'],
|
62 | key: ['k', 'key-file'],
|
63 | }
|
64 | });
|
65 |
|
66 | if (!certFile || !keyFile) {
|
67 | throw new Error(`${NAME}: When using HTTPS or port 443, a --cert and --key are required.`);
|
68 | }
|
69 |
|
70 | server = Deno.listenTls({
|
71 | hostname: self.location.hostname,
|
72 | port: Number(self.location.port || 443),
|
73 | certFile,
|
74 | keyFile,
|
75 | });
|
76 | } else {
|
77 | server = Deno.listen({
|
78 | hostname: self.location.hostname,
|
79 | port: Number(self.location.port || 80),
|
80 | });
|
81 | }
|
82 |
|
83 | for await (const conn of server) {
|
84 | (async () => {
|
85 | try {
|
86 | for await (const event of Deno.serveHttp(conn)) {
|
87 | const { hostname } = conn.remoteAddr as Deno.NetAddr;
|
88 | self.dispatchEvent(new AdaptedFetchEvent(event, hostname));
|
89 | }
|
90 | } catch (error) {
|
91 | self.dispatchEvent(new ErrorEvent('error', {
|
92 | message: error?.message,
|
93 | filename: import.meta.url,
|
94 | error,
|
95 | }));
|
96 | }
|
97 | })();
|
98 | }
|
99 | })();
|
100 |
|
101 |
|
102 | declare global {
|
103 | |
104 |
|
105 |
|
106 |
|
107 |
|
108 | interface ExtendableEvent extends Event {
|
109 | waitUntil(f: any): void;
|
110 | }
|
111 |
|
112 | interface ExtendableEventInit extends EventInit {
|
113 | }
|
114 |
|
115 | var ExtendableEvent: {
|
116 | prototype: ExtendableEvent;
|
117 | new(type: string, eventInitDict?: ExtendableEventInit): ExtendableEvent;
|
118 | };
|
119 |
|
120 | interface FetchEventInit extends ExtendableEventInit {
|
121 | clientId?: string;
|
122 | preloadResponse?: Promise<any>;
|
123 | replacesClientId?: string;
|
124 | request: Request;
|
125 | resultingClientId?: string;
|
126 | }
|
127 |
|
128 | var FetchEvent: {
|
129 | prototype: FetchEvent;
|
130 | new(type: string, eventInitDict: FetchEventInit): FetchEvent;
|
131 | };
|
132 |
|
133 | |
134 |
|
135 |
|
136 |
|
137 |
|
138 | interface FetchEvent extends ExtendableEvent {
|
139 | readonly clientId: string;
|
140 | readonly preloadResponse: Promise<any>;
|
141 | readonly replacesClientId: string;
|
142 | readonly request: Request;
|
143 | readonly resultingClientId: string;
|
144 | respondWith(r: Response | Promise<Response>): void;
|
145 | }
|
146 |
|
147 | interface Window {
|
148 | FetchEvent: new (type: string, eventInitDict: FetchEventInit) => FetchEvent;
|
149 | }
|
150 |
|
151 | function addEventListener(type: 'fetch', handler: (event: FetchEvent) => void): void;
|
152 | function addEventListener(type: 'error', handler: (event: ErrorEvent) => void): void;
|
153 | }
|
154 |
|