Node.js 从列表中查找最快的 HTTP/2 DoH 服务器

·

2 min read

对 DoH(DNS over HTTPS)来说 HTTP/2 尤其有效,因为 HTTP/2 支持多路复用,允许在同一连接上发送多个 DNS 查询,而无需等待之前的响应。这可以大大提高 DNS 解析的性能,尤其是在同时进行多个查询时。

网络上有数百个公开可用的 DoH 服务器,我们可以使用 Node.js HTTP/2 模块来查找在一定阈值(如 1 秒)内响应最快的服务器。

首先,让我们定义一个函数 check_url,它将返回一个 promise,并在 HTTP/2 请求成功时转为 resolve 状态。

const http2 = require('http2');

const timeoutDuration = 1000; // 1 seconds

function check_url(URL) {

    let fnResolve, fnReject;
    let myPromise = new Promise((resolve, reject) => {

        fnResolve = resolve;
        fnReject = reject;
    });

    const client = http2.connect(URL);//https://dns.alidns.com , https://dns.quad9.net

    const req = client.request({ ':path': '/dns-query?dns=AAABAAABAAAAAAAAA3d3dwdleGFtcGxlA2NvbQAAAQAB' });

    req.on('response', (headers, flags) => {
        for (const name in headers) {
            console.log(`${name}: ${headers[name]}`);
        }

        fnResolve('    ' + URL);
    });

    return myPromise;
}

让我们添加一些处理逻辑,当错误或超时发生时将 promise 转为 reject 状态。

    req.on('error', (err) => {
        console.error('ClientHttp2Stream error: ' + err);
        req.close(http2.constants.NGHTTP2_CANCEL);

        fnReject('!!! ' + URL)
    });

    // Set a timeout on the request
    req.setTimeout(timeoutDuration, () => {
        console.error(`ClientHttp2Stream ${URL} timed out`);

        req.destroy();

        fnReject('--- ' + URL)
    });

最后,添加一个处理函数,在请求流完成并关闭时同时关闭 HTTP/2 连接(请记住,HTTP/2 允许在同一连接上进行多个并发流,因此连接不会自动关闭)。

req.on('close', (e) => {
    console.log( 'ClientHttp2Stream is closed' )
    client.close();
})

现在,我们可以使用递归函数逐个检查列表中的 url,并返回响应时间在 1 秒以内的最快服务器。

let dohs = ["https://dns.google/dns-query",
    "https://cloudflare-dns.com/dns-query",
    "https://dns.alidns.com/dns-query",
    "https://dns.quad9.net/dns-query"];

/*
* check_next check doh[index], then doh[++index]...
* show: 
*      0 show urls response within timeoutDuration
*      1 show urls response great than timeoutDuration
*      2 don't show urls, for debug
*/
function check_next(index = 0, { show = 0 } = {}) {
    if (index >= dohs.length || index < 0) return;

    check_url(dohs[index])
        .then(URL => { if (0 === show) console.log(URL) })
        .catch(error => { if (1 === show) console.log(error) })
        .finally(() => {
            //only check_next when last check is done
            check_next(++index, { show })
        });
}

check_next(0, { show: 0 })

运行 Node 代码,获取最快服务器列表

node find-fastest-doh-servers.js
# 输出结果
#    https://dns.alidns.com/dns-query
#    https://dns.quad9.net/dns-query

完整代码在 GitHub Gist 上面

https://gist.github.com/likev/7c26cec0103b3187ddcb793f94ae593c