如何在没有 http 服务器的情况下在内部使用 koa app 回调?

How to use koa app callback internally without an http server?

我有一个基于 koa 的 js 应用程序并致力于 server-side 渲染。在应用程序的其余部分,我从 运行 连接 SSR 的同一台服务器加载一些脚本。虽然加载可以通过标准的 HTTP 请求,但速度会不必要地慢。所以我想简单地调用 koa 提供的 app.callback() 来将请求解析为请求文件的字符串。对于 SSR,我使用的是 jsdom,它加载文件并作为浏览器处理它们。

问题是我无法得到结果,resp。 body 的“服务器响应”。我试图模拟服务器请求和服务器响应,然后只读取套接字。我不确定这是否是最好的方法,但它似乎可行。但是,当我 运行 应用程序时,我只得到两个块 - 用于大型 froont-end 文件的字符串缓冲区。第一个块是 headers。其次是部分代码,但只有 64kB。我无法获得下一块。模拟的可写流似乎停止了。也许它被塞住了,但我不确定为什么以及是否完全没有。

如果我尝试加载小于 64kB 的脚本,请求会被正确解析并且流会结束。

const jsdom = require("jsdom");
const { JSDOM } = jsdom;
const { Writable } = require("stream");
const http = require("http");
const fetch = async (url, options) => {
        const response = [];
        let finishedReolve;
        const finished = new Promise(resolve => (finishedReolve = resolve));
        const socket = new Writable({
                write: (data, encoding, cb) => {
                        console.log("write", data);
                        // outputs 2 times - 1. headers, 2. 64kB part of requested code
                        response.push(data);
                        cb();
                        return true;
                },
                destroy(err, cb) {
                        // Never gets called
                        console.log("destroy", err);
                        cb();
                        finishedReolve();
                },
                final(cb) {
                        // Never gets called
                        console.log("final");
                        cb();
                        finishedReolve();
                },
        });
        const req = new http.IncomingMessage(socket);
        req.method = "GET";
        const parsedURL = new URL(url);
        req.url = parsedURL.pathname;
        const res = new http.ServerResponse(req);
        res.assignSocket(req.socket);
        res.on("prefinish", () => {
                // Never gets called
                finishedReolve();
        });
        await this.callback()(req, res);
        await finished;
        return response[0];
};
class CustomResourceLoader extends jsdom.ResourceLoader {
        fetch(url, options) {
                return fetch(url, options);
        }
}
const dom = await JSDOM.fromFile(index_html_path, {
        url: domain + ctx.req.url,
        runScripts: "dangerously",
        resources: new CustomResourceLoader(),
});

流有什么问题?有没有其他方法如何使用 app.callback() 来获取 koa 应用程序的输出?

问题是,可读流正在发送 64kB 的块,但我的可写流 highWaterMark 设置为默认的 16kB。这(可能)导致可读流在写入更多可写可以处理的数据后等待耗尽,这是在第一个 64kB 块之后。

解决方案是将 highWaterMark 设置为比可读流更多的值,例如

highWaterMark: 1024 * 64 * 2,

所以套接字看起来像这样:

const socket = new Writable({
        highWaterMark: 1024 * 64 * 2,
        write: (data, encoding, cb) => {
                response.push(data);
                cb();
                return true;
        },
        destroy(err, cb) {
                console.log("destroy", err);
                cb();
                finishedReolve();
        },
        final(cb) {
                console.log("final");
                cb();
                finishedReolve();
        },
});

另外,为了完成,我需要听听那个奇怪的事件 prefinish