這是 2026 iThome 鐵人賽的第 20 篇文章,同時也是 Node.js EventEmitter、stream、net.Socket 一路到 http 模組,這系列文章的最後一篇。這篇文章會把 http 模組一些比較冷門的 methods 跟 events 統整起來
request.on("information")會在 ClientRequest 收到 1xx status code(除了 101 Switching Protocols)的時候觸發,通常包含
server
import net from "net";
const server = net.createServer((socket) =>
socket.on("data", () => {
socket.write("HTTP/1.1 199 WhatTheHack\r\nFoo: bar\r\n\r\n");
}),
);
server.listen(5000);
client
import http from "http";
const clientRequest = http.request({ host: "localhost", port: 5000 });
clientRequest.end();
clientRequest.on("information", console.log);
output
{
statusCode: 199,
statusMessage: 'WhatTheHack',
httpVersion: '1.1',
httpVersionMajor: 1,
httpVersionMinor: 1,
headers: { foo: 'bar' },
rawHeaders: [ 'Foo', 'bar' ]
}
會在第 26 篇文章介紹到
103 Early Hints 最常用的情境就是告訴瀏覽器 "response body 還在準備中,但你可以先載入這些 Link 的資源"
server
import http from "http";
import { readFileSync } from "fs";
import { join } from "path";
const httpServer = http.createServer();
httpServer.listen(5000);
httpServer.on("request", (req, res) => {
if (req.url === "/") {
const indexHTML = readFileSync(join(import.meta.dirname, "index.html"));
res.writeEarlyHints({ link: "</style.css>; rel=preload; as=style" });
setTimeout(() => res.end(indexHTML), 1000);
return;
}
if (req.url === "/style.css") {
res.end("body { color: red; }");
return;
}
res.writeHead(404).end();
});
index.html
<h1>hello world</h1>
瀏覽器打開 http://localhost:5000/ ,發現 103 Early Hints 提供的 /style.css 沒正確被載入

但還是可以用 curl -v http://localhost:5000/ 戳看看
< HTTP/1.1 103 Early Hints
< Link: </style.css>; rel=preload; as=style
<
< HTTP/1.1 200 OK
< Connection: keep-alive
< Keep-Alive: timeout=5
< Content-Length: 20
<
<h1>hello world</h1>
查詢 MDN 文件關於 103 Early Hints 的描述,發現主流瀏覽器都是在 HTTP/2 才有支援~
至於為何 HTTP/1.1 不建議使用呢?原因是ㄧ些老舊的 proxy 不支援 1xx Informational response
如果把 103 Early Hints 當成正常 HTTP response 的話
HTTP/1.1 103 Early Hints
Link: </style.css>; rel=preload; as=style
就有可能把 final rsponse 留在 TCP socket,造成 Response Queue Poisoning
HTTP/1.1 103 Early Hints
Link: </style.css>; rel=preload; as=style
HTTP/1.1 200 OK
Connection: keep-alive
Keep-Alive: timeout=5
Content-Length: 20
<h1>hello world</h1>
通常 MDN 這種面對大眾的文件都會寫的比較隱晦
For compatibility and security reasons, it is recommended to only send HTTP 103 Early Hints responses over HTTP/2 or later unless the client is known to handle informational responses correctly.
我們接著看看 RFC 9110 Section 15.2. Informational 1xx 的介紹
A client MUST be able to parse one or more 1xx responses received prior to a final response, even if the client does not expect one. A user agent MAY ignore unexpected 1xx responses.
調整 Node.js http.Server,回傳兩個 Early Hints
import http from "http";
const httpServer = http.createServer();
httpServer.listen(5000);
httpServer.on("request", (req, res) => {
if (req.url === "/") {
res.writeEarlyHints({ link: "</style.css>; rel=preload; as=style" });
res.writeEarlyHints({ link: "</script.js>; rel=preload; as=script" });
res.end("hello world");
return;
}
res.writeHead(404).end();
});
用 curl -v http://localhost:5000/ 戳看看
< HTTP/1.1 103 Early Hints
< Link: </style.css>; rel=preload; as=style
<
< HTTP/1.1 103 Early Hints
< Link: </script.js>; rel=preload; as=script
<
< HTTP/1.1 200 OK
< Connection: keep-alive
< Keep-Alive: timeout=5
< Content-Length: 11
<
hello world
client 改用 Node.js
import http from "http";
const clientRequest = http.request({ host: "localhost", port: 5000 });
clientRequest.on("information", console.log);
clientRequest.end();
印出以下資訊,Node.js 有正確處理多個 1xx Informational response
{
statusCode: 103,
statusMessage: 'Early Hints',
httpVersion: '1.1',
httpVersionMajor: 1,
httpVersionMinor: 1,
headers: { link: '</style.css>; rel=preload; as=style' },
rawHeaders: [ 'Link', '</style.css>; rel=preload; as=style' ]
}
{
statusCode: 103,
statusMessage: 'Early Hints',
httpVersion: '1.1',
httpVersionMajor: 1,
httpVersionMinor: 1,
headers: { link: '</script.js>; rel=preload; as=script' },
rawHeaders: [ 'Link', '</script.js>; rel=preload; as=script' ]
}
不過很有趣的是,雖然 103 Early Hints 並不在 RFC 9110: HTTP Semantics 的規範內
而是定義在 RFC8297: An HTTP Status Code for Indicating Hints
雖然只是 Proposed Standard,但主流瀏覽器在 HTTP/2 以後都有實作
server.on("checkExpectation")RFC 9110 Section 10.1.1. Expect 有提到
The only expectation defined by this specification is "100-continue" (with no defined parameters).
不過 Expect 在語意上是可以支援其他 Expectation 的,所以 Node.js 預留了一個空間
import http from "http";
const httpServer = http.createServer();
httpServer.listen(5000);
httpServer.on("checkExpectation", (req, res) => {
res.writeHead(417);
res.end(`Sorry, ${req.headers.expect} is not supported`);
});
用 curl -H "Expect: 104-helloworld" -v http://localhost:5000/ 戳看看
< HTTP/1.1 417 Expectation Failed
< Connection: keep-alive
< Keep-Alive: timeout=5
< Transfer-Encoding: chunked
<
Sorry, 104-helloworld is not supported
若 user program 沒有監聽 server.on("checkExpectation"),則 Node.js 預設也會回 417 Expectation Failed
HTTP/1.1 417 Expectation Failed
Connection: keep-alive
Keep-Alive: timeout=5
Transfer-Encoding: chunked
0
server.on("clientError")觸發情境包含但不限於以下:
| Node.js Error Code | Description |
|---|---|
| ERR_HTTP_REQUEST_TIMEOUT | Incomplete request exceeded headersTimeout or requestTimeout |
| HPE_CHUNK_EXTENSIONS_OVERFLOW | Transfer-Encoding: chunked 的 chunked extensions exceeds maximum |
| HPE_HEADER_OVERFLOW | Exceeded maxHeaderSize |
on("clientError") 的情況,直接用 socket.destroy 關閉連線"clientError" 的話,則需要自行調用 socket.destroy 來關閉連線on("upgrade")Node.js 在 ClientRequest 跟 http.Server 分別提供了 on("upgrade") 事件
import http from "http";
const upgradeResponse =
"HTTP/1.1 101 Switching Protocols\r\n" +
"Connection: Upgrade\r\n" +
"Upgrade: Websocket\r\n\r\n";
const httpServer = http.createServer();
httpServer.listen(5000);
httpServer.on("upgrade", (req, socket, head) => {
// ✅ client 會觸發 `request.on("upgrade")`
socket.write(upgradeResponse);
});
// ✅ client 若送 Upgrade 請求,就會觸發 `server.on("upgrade")`
const clientRequest = http.request({
host: "localhost",
port: 5000,
headers: {
connection: "Upgrade",
upgrade: "Websocket",
},
});
clientRequest.end();
clientRequest.on("upgrade", (response, socket, head) => {
// ✅ It's now your responsibility to handle TCP socket
});
在這篇文章,我們學到了
ClientRequest 的 on("information")
server.on("checkExpectation") 的觸發情境跟用法server.on("clientError") 的觸發情境on("upgrade") 的觸發情境跟用法