-
-
Notifications
You must be signed in to change notification settings - Fork 2.5k
/
node.ts
103 lines (93 loc) · 3.17 KB
/
node.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
import type { RouteData } from '../../@types/astro';
import type { SerializedSSRManifest, SSRManifest } from './types';
import * as fs from 'fs';
import { IncomingMessage } from 'http';
import { TLSSocket } from 'tls';
import { deserializeManifest } from './common.js';
import { App, type MatchOptions } from './index.js';
const clientAddressSymbol = Symbol.for('astro.clientAddress');
function createRequestFromNodeRequest(req: NodeIncomingMessage, body?: Uint8Array): Request {
const protocol =
req.socket instanceof TLSSocket || req.headers['x-forwarded-proto'] === 'https'
? 'https'
: 'http';
const hostname = req.headers.host || req.headers[':authority'];
const url = `${protocol}://${hostname}${req.url}`;
const rawHeaders = req.headers as Record<string, any>;
const entries = Object.entries(rawHeaders);
const method = req.method || 'GET';
const request = new Request(url, {
method,
headers: new Headers(entries),
body: ['HEAD', 'GET'].includes(method) ? null : body,
});
if (req.socket?.remoteAddress) {
Reflect.set(request, clientAddressSymbol, req.socket.remoteAddress);
}
return request;
}
class NodeIncomingMessage extends IncomingMessage {
/**
* The read-only body property of the Request interface contains a ReadableStream with the body contents that have been added to the request.
*/
body?: any | undefined;
}
export class NodeApp extends App {
match(req: NodeIncomingMessage | Request, opts: MatchOptions = {}) {
return super.match(req instanceof Request ? req : createRequestFromNodeRequest(req), opts);
}
render(req: NodeIncomingMessage | Request, routeData?: RouteData, locals?: object) {
if (typeof req.body === 'string' && req.body.length > 0) {
return super.render(
req instanceof Request ? req : createRequestFromNodeRequest(req, Buffer.from(req.body)),
routeData,
locals
);
}
if (typeof req.body === 'object' && req.body !== null && Object.keys(req.body).length > 0) {
return super.render(
req instanceof Request
? req
: createRequestFromNodeRequest(req, Buffer.from(JSON.stringify(req.body))),
routeData,
locals
);
}
if ('on' in req) {
let body = Buffer.from([]);
let reqBodyComplete = new Promise((resolve, reject) => {
req.on('data', (d) => {
body = Buffer.concat([body, d]);
});
req.on('end', () => {
resolve(body);
});
req.on('error', (err) => {
reject(err);
});
});
return reqBodyComplete.then(() => {
return super.render(
req instanceof Request ? req : createRequestFromNodeRequest(req, body),
routeData,
locals
);
});
}
return super.render(
req instanceof Request ? req : createRequestFromNodeRequest(req),
routeData,
locals
);
}
}
export async function loadManifest(rootFolder: URL): Promise<SSRManifest> {
const manifestFile = new URL('./manifest.json', rootFolder);
const rawManifest = await fs.promises.readFile(manifestFile, 'utf-8');
const serializedManifest: SerializedSSRManifest = JSON.parse(rawManifest);
return deserializeManifest(serializedManifest);
}
export async function loadApp(rootFolder: URL): Promise<NodeApp> {
const manifest = await loadManifest(rootFolder);
return new NodeApp(manifest);
}