Node.js 之 localhost / 127.0.0.1 解析問題
0 |
踩到一個 Node.js 小問題,用一小段程式重現。
簡單的 HTTP 客戶端測試,一人分飾兩角,預設為伺服器模式,利用內建 http 模組 Bind 127.0.0.1 9527 Port 跑一個簡單的伺服器(永遠回傳 Hello World);若帶入參數 client,則使用 fetch 呼叫 http://localhost:9527
驗證結果:
(學到冷知識:Node 18+ 內建 fetch API,若為早期版本依賴 node-fetch 套件)
const args = process.argv.slice(2);
const clientMode = args.length && args[0] === 'client';
if (clientMode) {
console.log('client test');
// 註:Node 18+ 內建 fetch API,之前版本需 npm install node-fetch
const fetch = require('node-fetch');
fetch('http://localhost:9527').then(res => res.text()).then(console.log);
}
else {
console.log('server mode');
const http = require('http');
http.createServer((req, res) => {
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('Hello World\n');
}).listen(9527, '127.0.0.1');
}
狀況為程式在某些機器測試 OK,有些機器上 fetch() 動作會出現 FetchError: request to http://localhost:9527/ failed, reason: connect ECONNREFUSED ::1:9527
錯誤,但 curl http://localhost:9527
及瀏覽器檢視正常。
由程式碼與錯誤訊息中的 "::1" 可知跟 localhost 用 IPv6 或 IPv4 網址有關。從這篇Node.js 問題回報得到解答:
Node.js 17 版有個 Breaking Change - DNS.lookup 解析 localhost 結果,從 v17 起優先改回傳 IPv6;若伺服器繫結網址寫 127.0.0.1 但客戶端寫 localhost,就會出現 ECONNREFUSED 錯誤。參考
瀏覽器及 curl 不會出錯因為遇到 localhost 時,127.0.0.1 及 ::1 兩個網址都會試。參考
為了驗證這是 v16/v17 的差異,我找了一台有裝 NVM (Node 版本切換器)的 Linux 實測,證實同樣的程式用 v16 跑是好的,在 v17 才會出錯。
【參考資料】
知道原因,解法選項如下:
- bind() 或 listen() 的主機名稱改用 localhost,跟呼叫端統一
- 若伺服器端寫死 127.0.0.1 無法改,fetch 網址也改用 127.0.0.1
- 若伺服器端非 127.0.0.1 不 Bind,fetch 端也堅持要用 localhost (人生好難)... 可加入以下程式指定 IPv4 優先
一樣可以解決問題const dns = require('dns'); dns.setDefaultResultOrder('ipv4first'); fetch('http://localhost:9527').then(res => res.text()).then(console.log);
Issue of localhost resolving behavior breaking change in Node.js 17.
Comments
Be the first to post a comment