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