我设法为我的自定义 node.js 服务器编写扩展程序,允许使用 VM模块运行AJAX脚本。我也成功地说服VM承认存在 require ,这对于我的脚本有用是必要的。
但是我遇到了一个非常奇怪和棘手的问题。
当我运行AJAX脚本"通常"时,通过使用 child_process.spawn()将其交给子进程,我的所有异步API调用都按预期工作,并且我的回调链运行完成。对于我目前正在测试的脚本,它使用 fs.readdir()来启动回调链,它在子进程模式下工作,将目录列表返回给HTML客户端发出最初的AJAX请求。
但是当我使用 vm.script()运行AJAX脚本,然后运行 script.runInContext()时,会发生一些奇怪的事情。脚本到达我的fs.readdir()调用,但从不调用回调!整个脚本在这一点上停滞不前!
由于相同的V8引擎在子进程和VM模式下都运行代码,我有点疑惑,为什么AJAX脚本在子进程模式下启动时工作正常,但无法启动在VM模式下启动时的回调链。
我能想到的唯一原因,为什么VM不喜欢我的AJAX脚本,因为回调被定义为闭包的内部函数,而外部函数在fs.readdir之后结束( )打电话。但是,如果是这样的话,为什么脚本在node.js调试器中运行时完美运行,并在AJAX请求后由我的服务器传递给子进程?
如果在VM下运行的代码中调用,VM的文档没有提及fs调用被阻止,如果我事先得知这样的阻塞,我就不会开始这条开发路径了。已经实施。
那是怎么回事?
编辑:这是处理传递给脚本的参数的代码。它调用了前面定义的函数getCmdArgs(),我不需要在这里列出它,因为它已经过测试和工作。它适用于子进程模式和VM模式,并进行严格的测试,以查看为脚本提供其数据所需的参数传递变量是否实际存在 返回一个合适的返回参数(如果它们存在,则存在问题)。再一次,这部分有效。
//Get the arguments passed to the script - new dual mode version! See function notes above.
var args = getCmdArgs();
//Check to see if the script is being run in a VM sandbox by the server ...
if (args[0] == "RunInVM")
runMode = "V";
else
runMode = "P";
//End if/else
接下来,一旦脚本参数传递给脚本,就有时间处理它们,并找出正在引用的目录。我使用匿名路径引用,以" root"开头,这个想法是客户端不需要知道它在服务器上的哪个位置分配了允许的存储空间,只是它有存储空间它被允许访问的光盘。一旦给定,客户端可以随意操作该目录树,并且子进程允许这一切的脚本版本工作。所以,这是参数的处理方式:
//Set our root directory, to which all other directory references will be attached ...
//ROOTPATH is set at the beginning of the script, and is known only to the script, not the client
var dirRoot = ROOTPATH;
//Now get the arguments that were passed to the script ...
var tmpChk = args[2];
var queryMarker = tmpChk.indexOf("?");
if (queryMarker != -1)
{
//Strip off the initial "?"
var actualQuery = tmpChk.substring(queryMarker+1);
//Separate the x=y parts from each other
var queryParts = actualQuery.split("=");
var argType = queryParts[0];
var argData = queryParts[1];
if (argType === "dir")
{
requestedDir = argData;
//Here, we have an argument of the form "dir=xxx". Find out what "xxx" is.
if (argData === "root")
dirName = dirRoot;
else
{
//Here, we have an argument of the form "dir=root/xxx". Find out what "xxx" is, and make it a subdirectory of our
//chosen root directory.
var subIndex = argData.indexOf("root/");
var subDir = argData.substring(subIndex+5);
subDir = subDir.replace(/\x2F/g, path.sep);
subDir = qs.unescape(subDir);
dirName = dirRoot+path.sep+subDir;
//Also, insert an entry for the parent directory into our custom DirEntry array ...
var newEntry = new DirEntry();
newEntry.fileName = "(Parent Directory)";
newEntry.fileType = "DIR";
//Remember we're using the UNIX separator for our GET call!
var pdIdx = requestedDir.lastIndexOf("/");
var pd = requestedDir.substring(0,pdIdx);
//This is the secure path to the parent directory
newEntry.filePath = pd;
myDirEntries[idx2++] = newEntry;
//End if/else
}
//End if
}
}
上面生成了要返回的初始目录条目(通过自定义DirEntry()对象),再次,这一切都有效。它还确定在搜索服务器存储时要引用的路径。
此时,我们已准备好滚动,我们相应地调用fs.readdir():
//Get entire contents of dir in one go!
fs.readdir(dirName, DirContentsCallback);
现在,当在子进程模式下运行时,执行上述函数,并且正确启动回调函数DirContentsCallBack(),它是回调链的第一部分。但是当我尝试在VM模式下运行它时,这并没有发生。回调是永远不会被称为。现在如果fs.readdir()实际执行了,并且已经生成了错误,那么肯定会调用我的回调,并且错误参数是非空的?相反,整个脚本只是停止并挂起。
我插入了一系列console.log()调用以检查脚本的进度,以查看是否曾执行过回调链的任何部分,并且没有触发console.log()调用。我的整个回调链停滞不前。如果我在子进程模式下运行脚本,通过将其传递给cp.spawn(),这不会发生 - 相反,我最终得到一个完整的AJAX事务,并且一旦客户端收到,我的目录内容很好地显示在客户端中脚本生成的JSON数据。更奇怪的是,相同的数据在两种模式下传递给脚本 - 我已经反复检查过这种情况。然而,在虚拟机模式下尝试执行fs.readdir()的那一刻,在子进程模式下完美运行并生成正确检查条目的目标目录路径的数据将崩溃到静默暂停状态。
不是说实话,我不知道为什么我需要打扰发布一系列先前的代码,这些代码都是有效的,当DOESN&#T T在VM模式下工作的部分是最后一行时许多。我打开这个问题,在我看来完全足以提醒人们注意这个问题。但是,如果这意味着我收到了有意义的答案,那么就这样吧 - 浪费时间发布可行的代码。