@faasjs/http 8.0.0-beta.5 → 8.0.0-beta.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/index.cjs +364 -426
- package/dist/index.d.ts +151 -139
- package/dist/index.mjs +363 -424
- package/package.json +8 -6
package/dist/index.mjs
CHANGED
|
@@ -1,444 +1,383 @@
|
|
|
1
|
-
import { createBrotliCompress,
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
1
|
+
import { createBrotliCompress, createDeflate, createGzip } from "node:zlib";
|
|
2
|
+
import { usePlugin } from "@faasjs/func";
|
|
3
|
+
import { deepMerge } from "@faasjs/node-utils";
|
|
4
|
+
import { createCipheriv, createDecipheriv, createHmac, pbkdf2Sync, randomBytes } from "node:crypto";
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
//#region src/session.ts
|
|
7
7
|
var Session = class {
|
|
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
|
-
hmac.update(signedParts[0]);
|
|
76
|
-
const digest = hmac.digest("hex");
|
|
77
|
-
if (signedParts[1] !== digest) throw Error("Session Not valid");
|
|
78
|
-
const message = Buffer.from(signedParts[0], "base64").toString();
|
|
79
|
-
const parts = message.split("--").map((part2) => Buffer.from(part2, "base64"));
|
|
80
|
-
const cipher = createDecipheriv(
|
|
81
|
-
this.config.cipherName,
|
|
82
|
-
this.secret,
|
|
83
|
-
parts[1]
|
|
84
|
-
);
|
|
85
|
-
const part = Buffer.from(cipher.update(parts[0])).toString("utf8");
|
|
86
|
-
const final = cipher.final("utf8");
|
|
87
|
-
const decrypt = [part, final].join("");
|
|
88
|
-
return JSON.parse(decrypt);
|
|
89
|
-
}
|
|
90
|
-
read(key) {
|
|
91
|
-
return this.content[key];
|
|
92
|
-
}
|
|
93
|
-
write(key, value) {
|
|
94
|
-
if (value === null || typeof value === "undefined") delete this.content[key];
|
|
95
|
-
else this.content[key] = value;
|
|
96
|
-
this.changed = true;
|
|
97
|
-
return this;
|
|
98
|
-
}
|
|
99
|
-
update() {
|
|
100
|
-
if (this.changed)
|
|
101
|
-
this.cookie.write(
|
|
102
|
-
this.config.key,
|
|
103
|
-
this.encode(JSON.stringify(this.content))
|
|
104
|
-
);
|
|
105
|
-
return this;
|
|
106
|
-
}
|
|
8
|
+
content;
|
|
9
|
+
config;
|
|
10
|
+
secret;
|
|
11
|
+
signedSecret;
|
|
12
|
+
cookie;
|
|
13
|
+
changed;
|
|
14
|
+
constructor(cookie, config) {
|
|
15
|
+
this.cookie = cookie;
|
|
16
|
+
if (!config?.secret) cookie.logger?.warn("Session's secret is missing.");
|
|
17
|
+
this.config = Object.assign({
|
|
18
|
+
key: "key",
|
|
19
|
+
secret: randomBytes(128).toString("hex"),
|
|
20
|
+
salt: "salt",
|
|
21
|
+
signedSalt: "signedSalt",
|
|
22
|
+
keylen: 64,
|
|
23
|
+
iterations: 100,
|
|
24
|
+
digest: "sha256",
|
|
25
|
+
cipherName: "aes-256-cbc"
|
|
26
|
+
}, config);
|
|
27
|
+
this.secret = pbkdf2Sync(this.config.secret, this.config.salt, this.config.iterations, this.config.keylen / 2, this.config.digest);
|
|
28
|
+
this.signedSecret = pbkdf2Sync(this.config.secret, this.config.signedSalt, this.config.iterations, this.config.keylen, this.config.digest);
|
|
29
|
+
this.content = Object.create(null);
|
|
30
|
+
}
|
|
31
|
+
invoke(cookie, logger) {
|
|
32
|
+
try {
|
|
33
|
+
this.content = cookie ? this.decode(cookie) : Object.create(null);
|
|
34
|
+
} catch (error) {
|
|
35
|
+
logger?.error(error);
|
|
36
|
+
this.content = Object.create(null);
|
|
37
|
+
}
|
|
38
|
+
this.changed = false;
|
|
39
|
+
}
|
|
40
|
+
encode(text) {
|
|
41
|
+
if (typeof text !== "string") text = JSON.stringify(text);
|
|
42
|
+
const iv = randomBytes(16);
|
|
43
|
+
const cipher = createCipheriv(this.config.cipherName, this.secret, iv);
|
|
44
|
+
const encrypted = Buffer.concat([cipher.update(text), cipher.final()]).toString("base64");
|
|
45
|
+
const main = Buffer.from([encrypted, iv.toString("base64")].join("--")).toString("base64");
|
|
46
|
+
const hmac = createHmac(this.config.digest, this.signedSecret);
|
|
47
|
+
hmac.update(main);
|
|
48
|
+
return `${main}--${hmac.digest("hex")}`;
|
|
49
|
+
}
|
|
50
|
+
decode(text) {
|
|
51
|
+
text = decodeURIComponent(text);
|
|
52
|
+
const signedParts = text.split("--");
|
|
53
|
+
const hmac = createHmac(this.config.digest, this.signedSecret);
|
|
54
|
+
hmac.update(signedParts[0]);
|
|
55
|
+
const digest = hmac.digest("hex");
|
|
56
|
+
if (signedParts[1] !== digest) throw Error("Session Not valid");
|
|
57
|
+
const parts = Buffer.from(signedParts[0], "base64").toString().split("--").map((part) => Buffer.from(part, "base64"));
|
|
58
|
+
const cipher = createDecipheriv(this.config.cipherName, this.secret, parts[1]);
|
|
59
|
+
const decrypt = [Buffer.from(cipher.update(parts[0])).toString("utf8"), cipher.final("utf8")].join("");
|
|
60
|
+
return JSON.parse(decrypt);
|
|
61
|
+
}
|
|
62
|
+
read(key) {
|
|
63
|
+
return this.content[key];
|
|
64
|
+
}
|
|
65
|
+
write(key, value) {
|
|
66
|
+
if (value === null || typeof value === "undefined") delete this.content[key];
|
|
67
|
+
else this.content[key] = value;
|
|
68
|
+
this.changed = true;
|
|
69
|
+
return this;
|
|
70
|
+
}
|
|
71
|
+
update() {
|
|
72
|
+
if (this.changed) this.cookie.write(this.config.key, this.encode(JSON.stringify(this.content)));
|
|
73
|
+
return this;
|
|
74
|
+
}
|
|
107
75
|
};
|
|
108
76
|
|
|
109
|
-
|
|
77
|
+
//#endregion
|
|
78
|
+
//#region src/cookie.ts
|
|
110
79
|
var Cookie = class {
|
|
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
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
if (opts.sameSite) cookie += `SameSite=${opts.sameSite};`;
|
|
169
|
-
this.setCookie[key] = cookie;
|
|
170
|
-
return this;
|
|
171
|
-
}
|
|
172
|
-
headers() {
|
|
173
|
-
if (Object.keys(this.setCookie).length === 0) return {};
|
|
174
|
-
return { "Set-Cookie": Object.values(this.setCookie) };
|
|
175
|
-
}
|
|
80
|
+
session;
|
|
81
|
+
content;
|
|
82
|
+
config;
|
|
83
|
+
logger;
|
|
84
|
+
setCookie;
|
|
85
|
+
constructor(config, logger) {
|
|
86
|
+
this.logger = logger;
|
|
87
|
+
this.config = deepMerge({
|
|
88
|
+
path: "/",
|
|
89
|
+
expires: 31536e3,
|
|
90
|
+
secure: true,
|
|
91
|
+
httpOnly: true,
|
|
92
|
+
session: {}
|
|
93
|
+
}, config);
|
|
94
|
+
this.session = new Session(this, this.config.session);
|
|
95
|
+
this.content = Object.create(null);
|
|
96
|
+
this.setCookie = Object.create(null);
|
|
97
|
+
}
|
|
98
|
+
invoke(cookie, logger) {
|
|
99
|
+
this.content = Object.create(null);
|
|
100
|
+
if (cookie) for (const x of cookie.split(";")) {
|
|
101
|
+
const trimX = x.trim();
|
|
102
|
+
const k = /([^=]+)/.exec(trimX);
|
|
103
|
+
if (k !== null) this.content[k[0]] = decodeURIComponent(trimX.replace(`${k[0]}=`, "").replace(/;$/, ""));
|
|
104
|
+
}
|
|
105
|
+
this.setCookie = Object.create(null);
|
|
106
|
+
this.session.invoke(this.read(this.session.config.key), logger);
|
|
107
|
+
return this;
|
|
108
|
+
}
|
|
109
|
+
read(key) {
|
|
110
|
+
return this.content[key];
|
|
111
|
+
}
|
|
112
|
+
write(key, value, opts) {
|
|
113
|
+
opts = Object.assign(this.config, opts || {});
|
|
114
|
+
let cookie;
|
|
115
|
+
if (value === null || typeof value === "undefined") {
|
|
116
|
+
opts.expires = "Thu, 01 Jan 1970 00:00:01 GMT";
|
|
117
|
+
cookie = `${key}=;`;
|
|
118
|
+
delete this.content[key];
|
|
119
|
+
} else {
|
|
120
|
+
cookie = `${key}=${encodeURIComponent(value)};`;
|
|
121
|
+
this.content[key] = value;
|
|
122
|
+
}
|
|
123
|
+
if (typeof opts.expires === "number") cookie += `max-age=${opts.expires};`;
|
|
124
|
+
else if (typeof opts.expires === "string") cookie += `expires=${opts.expires};`;
|
|
125
|
+
cookie += `path=${opts.path || "/"};`;
|
|
126
|
+
if (opts.domain) cookie += `domain=${opts.domain};`;
|
|
127
|
+
if (opts.secure) cookie += "Secure;";
|
|
128
|
+
if (opts.httpOnly) cookie += "HttpOnly;";
|
|
129
|
+
if (opts.sameSite) cookie += `SameSite=${opts.sameSite};`;
|
|
130
|
+
this.setCookie[key] = cookie;
|
|
131
|
+
return this;
|
|
132
|
+
}
|
|
133
|
+
headers() {
|
|
134
|
+
if (Object.keys(this.setCookie).length === 0) return {};
|
|
135
|
+
return { "Set-Cookie": Object.values(this.setCookie) };
|
|
136
|
+
}
|
|
176
137
|
};
|
|
177
138
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
139
|
+
//#endregion
|
|
140
|
+
//#region src/index.ts
|
|
141
|
+
/**
|
|
142
|
+
* FaasJS's http plugin.
|
|
143
|
+
*
|
|
144
|
+
* [](https://github.com/faasjs/faasjs/blob/main/packages/http/LICENSE)
|
|
145
|
+
* [](https://www.npmjs.com/package/@faasjs/http)
|
|
146
|
+
*
|
|
147
|
+
* ## Install
|
|
148
|
+
*
|
|
149
|
+
* ```sh
|
|
150
|
+
* npm install @faasjs/http
|
|
151
|
+
* ```
|
|
152
|
+
*
|
|
153
|
+
* @packageDocumentation
|
|
154
|
+
*/
|
|
155
|
+
const ContentType = {
|
|
156
|
+
plain: "text/plain",
|
|
157
|
+
html: "text/html",
|
|
158
|
+
xml: "application/xml",
|
|
159
|
+
csv: "text/csv",
|
|
160
|
+
css: "text/css",
|
|
161
|
+
javascript: "application/javascript",
|
|
162
|
+
json: "application/json",
|
|
163
|
+
jsonp: "application/javascript"
|
|
188
164
|
};
|
|
189
|
-
var HttpError = class
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
this.statusCode = statusCode || 500;
|
|
199
|
-
this.message = message;
|
|
200
|
-
}
|
|
165
|
+
var HttpError = class HttpError extends Error {
|
|
166
|
+
statusCode;
|
|
167
|
+
message;
|
|
168
|
+
constructor({ statusCode, message }) {
|
|
169
|
+
super(message);
|
|
170
|
+
if (Error.captureStackTrace) Error.captureStackTrace(this, HttpError);
|
|
171
|
+
this.statusCode = statusCode || 500;
|
|
172
|
+
this.message = message;
|
|
173
|
+
}
|
|
201
174
|
};
|
|
202
|
-
|
|
175
|
+
const Name = "http";
|
|
203
176
|
function stringToStream(text) {
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
}
|
|
214
|
-
});
|
|
177
|
+
return new ReadableStream({ start(controller) {
|
|
178
|
+
try {
|
|
179
|
+
const encoder = new TextEncoder();
|
|
180
|
+
controller.enqueue(encoder.encode(text));
|
|
181
|
+
controller.close();
|
|
182
|
+
} catch (error) {
|
|
183
|
+
controller.error(error);
|
|
184
|
+
}
|
|
185
|
+
} });
|
|
215
186
|
}
|
|
216
187
|
function deepClone(obj) {
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
188
|
+
if (obj === null || typeof obj !== "object") return obj;
|
|
189
|
+
if (Array.isArray(obj)) return JSON.parse(JSON.stringify(obj));
|
|
190
|
+
const clone = {};
|
|
191
|
+
for (const key in obj) {
|
|
192
|
+
if (!Object.hasOwn(obj, key)) continue;
|
|
193
|
+
if (typeof obj[key] === "function") {
|
|
194
|
+
clone[key] = obj[key];
|
|
195
|
+
continue;
|
|
196
|
+
}
|
|
197
|
+
clone[key] = deepClone(obj[key]);
|
|
198
|
+
}
|
|
199
|
+
return clone;
|
|
229
200
|
}
|
|
230
201
|
function createCompressedStream(body, encoding) {
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
controller.error(error);
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
});
|
|
202
|
+
const compressStream = encoding === "br" ? createBrotliCompress() : encoding === "gzip" ? createGzip() : createDeflate();
|
|
203
|
+
return new ReadableStream({ async start(controller) {
|
|
204
|
+
try {
|
|
205
|
+
const compressed = await new Promise((resolve, reject) => {
|
|
206
|
+
const chunks = [];
|
|
207
|
+
compressStream.on("data", (chunk) => chunks.push(chunk));
|
|
208
|
+
compressStream.on("end", () => resolve(Buffer.concat(chunks)));
|
|
209
|
+
compressStream.on("error", reject);
|
|
210
|
+
compressStream.write(Buffer.from(body));
|
|
211
|
+
compressStream.end();
|
|
212
|
+
});
|
|
213
|
+
const chunkSize = 16 * 1024;
|
|
214
|
+
for (let i = 0; i < compressed.length; i += chunkSize) controller.enqueue(compressed.subarray(i, i + chunkSize));
|
|
215
|
+
controller.close();
|
|
216
|
+
} catch (error) {
|
|
217
|
+
controller.error(error);
|
|
218
|
+
}
|
|
219
|
+
} });
|
|
253
220
|
}
|
|
254
221
|
var Http = class {
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
return this;
|
|
411
|
-
}
|
|
412
|
-
/**
|
|
413
|
-
* set Content-Type
|
|
414
|
-
* @param type {string} 类型
|
|
415
|
-
* @param charset {string} 编码
|
|
416
|
-
*/
|
|
417
|
-
setContentType(type, charset = "utf-8") {
|
|
418
|
-
if (ContentType[type])
|
|
419
|
-
this.setHeader("Content-Type", `${ContentType[type]}; charset=${charset}`);
|
|
420
|
-
else this.setHeader("Content-Type", `${type}; charset=${charset}`);
|
|
421
|
-
return this;
|
|
422
|
-
}
|
|
423
|
-
/**
|
|
424
|
-
* set status code
|
|
425
|
-
* @param code {number} 状态码
|
|
426
|
-
*/
|
|
427
|
-
setStatusCode(code) {
|
|
428
|
-
this.response.statusCode = code;
|
|
429
|
-
return this;
|
|
430
|
-
}
|
|
431
|
-
/**
|
|
432
|
-
* set body
|
|
433
|
-
* @param body {*} 内容
|
|
434
|
-
*/
|
|
435
|
-
setBody(body) {
|
|
436
|
-
this.response.body = body;
|
|
437
|
-
return this;
|
|
438
|
-
}
|
|
222
|
+
type = "http";
|
|
223
|
+
name = Name;
|
|
224
|
+
headers = Object.create(null);
|
|
225
|
+
body;
|
|
226
|
+
params = Object.create(null);
|
|
227
|
+
cookie;
|
|
228
|
+
session;
|
|
229
|
+
config;
|
|
230
|
+
response;
|
|
231
|
+
constructor(config) {
|
|
232
|
+
this.name = config?.name || this.type;
|
|
233
|
+
this.config = config?.config || Object.create(null);
|
|
234
|
+
this.cookie = new Cookie(this.config.cookie || {});
|
|
235
|
+
this.session = this.cookie.session;
|
|
236
|
+
this.response = { headers: Object.create(null) };
|
|
237
|
+
}
|
|
238
|
+
async onMount(data, next) {
|
|
239
|
+
data.logger.debug("merge config");
|
|
240
|
+
const prefix = `SECRET_${this.name.toUpperCase()}_`;
|
|
241
|
+
for (let key in process.env) if (key.startsWith(prefix)) {
|
|
242
|
+
const value = process.env[key];
|
|
243
|
+
key = key.replace(prefix, "").toLowerCase();
|
|
244
|
+
if (key.includes("_")) {
|
|
245
|
+
let config = this.config;
|
|
246
|
+
const keys = key.split("_");
|
|
247
|
+
for (const k of keys.slice(0, keys.length - 1)) {
|
|
248
|
+
if (!config[k]) config[k] = Object.create(null);
|
|
249
|
+
config = config[k];
|
|
250
|
+
}
|
|
251
|
+
config[keys[keys.length - 1]] = value;
|
|
252
|
+
} else this.config[key] = value;
|
|
253
|
+
}
|
|
254
|
+
if (!data.config) throw Error(`[${this.name}] Config not found.`);
|
|
255
|
+
if (data.config.plugins?.[this.name || this.type]) this.config = deepMerge(this.config, data.config.plugins[this.name || this.type].config);
|
|
256
|
+
data.logger.debug("prepare cookie & session");
|
|
257
|
+
this.cookie = new Cookie(this.config.cookie || {}, data.logger);
|
|
258
|
+
this.session = this.cookie.session;
|
|
259
|
+
await next();
|
|
260
|
+
}
|
|
261
|
+
async onInvoke(data, next) {
|
|
262
|
+
this.headers = data.event.headers || Object.create(null);
|
|
263
|
+
this.body = data.event.body;
|
|
264
|
+
this.params = data.event.queryString || Object.create(null);
|
|
265
|
+
this.response = { headers: Object.create(null) };
|
|
266
|
+
if (data.event.body) {
|
|
267
|
+
if (this.headers["content-type"]?.includes("application/json") && typeof data.event.body === "string" && data.event.body.length > 1) {
|
|
268
|
+
data.logger.debug("Parse params from json body");
|
|
269
|
+
try {
|
|
270
|
+
this.params = Object.keys(this.params).length ? Object.assign(this.params, JSON.parse(data.event.body)) : JSON.parse(data.event.body);
|
|
271
|
+
} catch (error) {
|
|
272
|
+
data.logger.error("Parse params from json body failed: %s", error.message);
|
|
273
|
+
}
|
|
274
|
+
} else {
|
|
275
|
+
data.logger.debug("Parse params from raw body");
|
|
276
|
+
this.params = data.event.body || Object.create(null);
|
|
277
|
+
}
|
|
278
|
+
if (this.params && typeof this.params === "object" && this.params._) delete this.params._;
|
|
279
|
+
data.event.params = deepClone(this.params);
|
|
280
|
+
data.logger.debug("Params: %j", this.params);
|
|
281
|
+
}
|
|
282
|
+
data.params = data.event.params;
|
|
283
|
+
this.cookie.invoke(this.headers.cookie, data.logger);
|
|
284
|
+
if (this.headers.cookie) {
|
|
285
|
+
data.logger.debug("Cookie: %j", this.cookie.content);
|
|
286
|
+
data.logger.debug("Session: %s %j", this.session.config.key, this.session.content);
|
|
287
|
+
}
|
|
288
|
+
data.cookie = this.cookie;
|
|
289
|
+
data.session = this.session;
|
|
290
|
+
try {
|
|
291
|
+
await next();
|
|
292
|
+
} catch (error) {
|
|
293
|
+
data.response = error;
|
|
294
|
+
}
|
|
295
|
+
this.session.update();
|
|
296
|
+
if (this.response.body && !data.response) data.response = this.response.body;
|
|
297
|
+
if (data.response) if (data.response instanceof Error || data.response.constructor?.name === "Error") {
|
|
298
|
+
data.logger.error(data.response);
|
|
299
|
+
this.response.body = JSON.stringify({ error: { message: data.response.message } });
|
|
300
|
+
try {
|
|
301
|
+
this.response.statusCode = data.response.statusCode || 500;
|
|
302
|
+
} catch (e) {
|
|
303
|
+
data.logger.error(e);
|
|
304
|
+
this.response.statusCode = 500;
|
|
305
|
+
}
|
|
306
|
+
} else if (Object.prototype.toString.call(data.response) === "[object Object]" && data.response.statusCode && data.response.headers) this.response = data.response;
|
|
307
|
+
else if (data.response instanceof ReadableStream) this.response.body = data.response;
|
|
308
|
+
else this.response.body = JSON.stringify({ data: data.response === void 0 ? null : data.response });
|
|
309
|
+
if (!this.response.statusCode) this.response.statusCode = data.response ? 200 : 204;
|
|
310
|
+
this.response.headers = Object.assign({
|
|
311
|
+
"content-type": this.response.body instanceof ReadableStream ? "text/plain; charset=utf-8" : "application/json; charset=utf-8",
|
|
312
|
+
"cache-control": "no-cache, no-store",
|
|
313
|
+
"x-faasjs-request-id": data.context.request_id
|
|
314
|
+
}, this.cookie.headers(), this.response.headers);
|
|
315
|
+
data.response = Object.assign({}, data.response, this.response);
|
|
316
|
+
const originBody = data.response.body;
|
|
317
|
+
data.response.originBody = originBody;
|
|
318
|
+
if (data.response.body instanceof ReadableStream) {
|
|
319
|
+
data.response.isBase64Encoded = true;
|
|
320
|
+
return;
|
|
321
|
+
}
|
|
322
|
+
if (originBody === void 0 && data.response.statusCode === 204) return;
|
|
323
|
+
if (originBody && typeof originBody !== "string") data.response.body = JSON.stringify(originBody);
|
|
324
|
+
else if (originBody === void 0) data.response.body = JSON.stringify({ data: null });
|
|
325
|
+
if (!data.response.body || typeof data.response.body !== "string" || data.response.body.length < 1024) {
|
|
326
|
+
data.response.body = stringToStream(data.response.body);
|
|
327
|
+
return;
|
|
328
|
+
}
|
|
329
|
+
const acceptEncoding = this.headers["accept-encoding"] || this.headers["Accept-Encoding"];
|
|
330
|
+
if (!acceptEncoding || !/(br|gzip|deflate)/.test(acceptEncoding)) return;
|
|
331
|
+
try {
|
|
332
|
+
const encoding = acceptEncoding.includes("br") ? "br" : acceptEncoding.includes("gzip") ? "gzip" : "deflate";
|
|
333
|
+
data.response.headers["Content-Encoding"] = encoding;
|
|
334
|
+
data.response.body = createCompressedStream(originBody, encoding);
|
|
335
|
+
} catch (error) {
|
|
336
|
+
data.logger.error("Compression failed: %s", error.message);
|
|
337
|
+
data.response.body = originBody;
|
|
338
|
+
delete data.response.headers["Content-Encoding"];
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
/**
|
|
342
|
+
* set header
|
|
343
|
+
* @param key {string} key
|
|
344
|
+
* @param value {string} value
|
|
345
|
+
*/
|
|
346
|
+
setHeader(key, value) {
|
|
347
|
+
const headers = this.response.headers || (this.response.headers = Object.create(null));
|
|
348
|
+
headers[key.toLowerCase()] = value;
|
|
349
|
+
return this;
|
|
350
|
+
}
|
|
351
|
+
/**
|
|
352
|
+
* set Content-Type
|
|
353
|
+
* @param type {string} 类型
|
|
354
|
+
* @param charset {string} 编码
|
|
355
|
+
*/
|
|
356
|
+
setContentType(type, charset = "utf-8") {
|
|
357
|
+
if (ContentType[type]) this.setHeader("Content-Type", `${ContentType[type]}; charset=${charset}`);
|
|
358
|
+
else this.setHeader("Content-Type", `${type}; charset=${charset}`);
|
|
359
|
+
return this;
|
|
360
|
+
}
|
|
361
|
+
/**
|
|
362
|
+
* set status code
|
|
363
|
+
* @param code {number} 状态码
|
|
364
|
+
*/
|
|
365
|
+
setStatusCode(code) {
|
|
366
|
+
this.response.statusCode = code;
|
|
367
|
+
return this;
|
|
368
|
+
}
|
|
369
|
+
/**
|
|
370
|
+
* set body
|
|
371
|
+
* @param body {*} 内容
|
|
372
|
+
*/
|
|
373
|
+
setBody(body) {
|
|
374
|
+
this.response.body = body;
|
|
375
|
+
return this;
|
|
376
|
+
}
|
|
439
377
|
};
|
|
440
378
|
function useHttp(config) {
|
|
441
|
-
|
|
379
|
+
return usePlugin(new Http(config));
|
|
442
380
|
}
|
|
443
381
|
|
|
444
|
-
|
|
382
|
+
//#endregion
|
|
383
|
+
export { ContentType, Cookie, Http, HttpError, Session, useHttp };
|