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

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

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

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

```javascript
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 状态。

```javascript
    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 允许在同一连接上进行多个并发流，因此连接不会自动关闭）。

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

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

```javascript
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 代码，获取最快服务器列表

```bash
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](https://gist.github.com/likev/7c26cec0103b3187ddcb793f94ae593c)
