Skip to content

Commit

Permalink
feat(cloud-function): support async timeout/globals/sync main/filename;
Browse files Browse the repository at this point in the history
disable eval/FunctionConstructor;
support more global node method; support sync main; support cloud function name in error stack;
  • Loading branch information
maslow committed Nov 4, 2021
1 parent b68df36 commit e5ecf6d
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 16 deletions.
39 changes: 27 additions & 12 deletions packages/cloud-function/src/engine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,6 @@ const defaultRequireFunction: RequireFuncType = (module): any => {
*/
export class FunctionEngine {

/**
* 函数执行超时时间
*/
timeout = 60 * 1000

/**
* 云函数中加载依赖包的函数: require('')
*/
Expand All @@ -37,17 +32,31 @@ export class FunctionEngine {
* @param incomingCtx
* @returns
*/
async run(code: string, context: FunctionContext): Promise<FunctionResult> {
async run(code: string, context: FunctionContext, options: vm.RunningScriptOptions): Promise<FunctionResult> {
const sandbox = this.buildSandbox(context)
const wrapped = this.wrap(code)
const fconsole = sandbox.console

// 调用前计时
const _start_time = process.hrtime.bigint()
try {
const script = new vm.Script(wrapped)
script.runInNewContext(sandbox, { timeout: this.timeout })
const data = await sandbox.__runtime_promise
const script = new vm.Script(wrapped, options)
const result = script.runInNewContext(sandbox, options)

let data = result
if (typeof result?.then === 'function') {
/**
* 由于 vm 内部的 microTasks queue 为空时会直接释放执行环境,后续 await 则会导致工作线程陷入黑洞,
* 故需先给 vm 返回的 promise 设置 then 回调,使 microTasks queue 不为空,以维护 vm 执行环境暂不被释放
*/
const promise = new Promise((resolve, reject) => {
result
.then(resolve)
.catch(reject)
})

data = await promise
}

// 函数执行耗时
const _end_time = process.hrtime.bigint()
Expand Down Expand Up @@ -87,12 +96,18 @@ export class FunctionEngine {
}
return {
__context__: functionContext,
__filename: functionContext.__function_name,
module: _module,
exports: _module.exports,
__runtime_promise: null,
console: fconsole,
require: this.require_func,
Buffer: Buffer
Buffer: Buffer,
setImmediate: setImmediate,
clearImmediate: clearImmediate,
setInterval: setInterval,
clearInterval: clearInterval,
setTimeout: setTimeout,
clearTimeout: clearTimeout
}
}

Expand All @@ -106,7 +121,7 @@ export class FunctionEngine {
const __main__ = exports.main || exports.default
if(!__main__) { throw new Error('FunctionExecError: main function not found') }
if(typeof __main__ !== 'function') { throw new Error('FunctionExecError: main function must be callable')}
__runtime_promise = __main__(__context__ )
__main__(__context__ )
`
return wrapped
}
Expand Down
15 changes: 14 additions & 1 deletion packages/cloud-function/src/function.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ export class CloudFunction {
*/
static require_func: RequireFuncType

/**
* 函数执行超时时间
*/
timeout = 60 * 1000

/**
* 函数结构
*/
Expand Down Expand Up @@ -90,7 +95,15 @@ export class CloudFunction {

const engine = new FunctionEngine(CloudFunction.require_func)

this.result = await engine.run(this.compiledCode, param)
this.result = await engine.run(this.compiledCode, param, {
filename: `CloudFunction.${this.name}`,
timeout: this.timeout,
displayErrors: true,
microtaskMode: 'afterEvaluate',
contextCodeGeneration: {
strings: false
}
} as any)

return this.result
}
Expand Down
14 changes: 11 additions & 3 deletions packages/cloud-function/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { FunctionConsole } from "./console"
import { IncomingHttpHeaders } from "http"
import { Response } from "express"


export type RequireFuncType = (module: string) => any


Expand All @@ -13,10 +14,16 @@ export interface RuntimeContext {
__context__: FunctionContext,
module: { exports: Object },
exports: Object,
__runtime_promise: any,
console: FunctionConsole,
require: RequireFuncType,
Buffer: typeof Buffer
Buffer: typeof Buffer,
setTimeout: typeof setTimeout,
clearTimeout: typeof clearTimeout,
setInterval: typeof setInterval,
clearInterval: typeof clearInterval,
setImmediate: typeof setImmediate,
clearImmediate: typeof clearImmediate
__filename: string
}

/**
Expand All @@ -31,7 +38,8 @@ export interface FunctionContext {
auth?: any,
requestId?: string,
method?: string,
response?: Response
response?: Response,
__function_name?: string
}

/**
Expand Down

0 comments on commit e5ecf6d

Please sign in to comment.