forked from oakserver/oak
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathhttp_server_native_request.ts
147 lines (128 loc) · 3.89 KB
/
http_server_native_request.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
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
// Copyright 2018-2023 the oak authors. All rights reserved. MIT license.
import type {
RequestEvent,
ServerRequest,
ServerRequestBody,
UpgradeWebSocketFn,
UpgradeWebSocketOptions,
} from "./types.d.ts";
// deno-lint-ignore no-explicit-any
export const DomResponse: typeof Response = (globalThis as any).Response ??
class MockResponse {};
const maybeUpgradeWebSocket: UpgradeWebSocketFn | undefined =
"upgradeWebSocket" in Deno
// deno-lint-ignore no-explicit-any
? (Deno as any).upgradeWebSocket.bind(Deno)
: undefined;
export function isNativeRequest(r: ServerRequest): r is NativeRequest {
return r instanceof NativeRequest;
}
export interface NativeRequestOptions {
conn?: Deno.Conn;
upgradeWebSocket?: UpgradeWebSocketFn;
}
/** An internal oak abstraction for handling a Deno native request. Most users
* of oak do not need to worry about this abstraction. */
export class NativeRequest implements ServerRequest {
#conn?: Deno.Conn;
// deno-lint-ignore no-explicit-any
#reject!: (reason?: any) => void;
#request: Request;
#requestPromise: Promise<void>;
#resolve!: (value: Response) => void;
#resolved = false;
#upgradeWebSocket?: UpgradeWebSocketFn;
constructor(
requestEvent: RequestEvent,
options: NativeRequestOptions = {},
) {
const { conn } = options;
this.#conn = conn;
// this allows for the value to be explicitly undefined in the options
this.#upgradeWebSocket = "upgradeWebSocket" in options
? options["upgradeWebSocket"]
: maybeUpgradeWebSocket;
this.#request = requestEvent.request;
const p = new Promise<Response>((resolve, reject) => {
this.#resolve = resolve;
this.#reject = reject;
});
this.#requestPromise = requestEvent.respondWith(p);
}
get body(): ReadableStream<Uint8Array> | null {
// when shimming with undici under Node.js, this is a
// `ControlledAsyncIterable`
// deno-lint-ignore no-explicit-any
return this.#request.body as any;
}
get donePromise(): Promise<void> {
return this.#requestPromise;
}
get headers(): Headers {
return this.#request.headers;
}
get method(): string {
return this.#request.method;
}
get remoteAddr(): string | undefined {
return (this.#conn?.remoteAddr as Deno.NetAddr)?.hostname;
}
get request(): Request {
return this.#request;
}
get url(): string {
try {
const url = new URL(this.#request.url);
return this.#request.url.replace(url.origin, "");
} catch {
// we don't care about errors, we just want to fall back
}
return this.#request.url;
}
get rawUrl(): string {
return this.#request.url;
}
// deno-lint-ignore no-explicit-any
error(reason?: any): void {
if (this.#resolved) {
throw new Error("Request already responded to.");
}
this.#reject(reason);
this.#resolved = true;
}
getBody(): ServerRequestBody {
return {
// when emitting to Node.js, the body is not compatible, and thought it
// doesn't run at runtime, it still gets type checked.
// deno-lint-ignore no-explicit-any
body: this.#request.body as any,
readBody: async () => {
const ab = await this.#request.arrayBuffer();
return new Uint8Array(ab);
},
};
}
respond(response: Response): Promise<void> {
if (this.#resolved) {
throw new Error("Request already responded to.");
}
this.#resolve(response);
this.#resolved = true;
return this.#requestPromise;
}
upgrade(options?: UpgradeWebSocketOptions): WebSocket {
if (this.#resolved) {
throw new Error("Request already responded to.");
}
if (!this.#upgradeWebSocket) {
throw new TypeError("Upgrading web sockets not supported.");
}
const { response, socket } = this.#upgradeWebSocket(
this.#request,
options,
);
this.#resolve(response);
this.#resolved = true;
return socket;
}
}