Node.js:HTTP
http 是 Node.js 处理 HTTP/1.1 的核心模块。接口按流设计,支持分块传输等特性,不会把整份请求或响应先缓冲完再交给你。
const http = require('http');
const hostname = '127.0.0.1';
const port = 3000;
const server = http.createServer((req, res) => {
res.statusCode = 200;
res.setHeader('Content-Type', 'text/plain; charset=utf-8');
res.end('你好,李兆!');
});
server.listen(port, hostname, () => {
console.log(`服务器运行在 http://${hostname}:${port}/`);
});
监听 0.0.0.0 会绑到所有网卡,本机浏览器应打开 http://127.0.0.1:3000/。HTTPS 用 https 模块;HTTP/2 用 http2,不要和本模块混成一套 API。一次性客户端也可用全局 fetch(Node 18+)。
http 类
HTTP 模块提供下列类。
Server 类
继承自 net.Server。http.createServer() 通常返回这个实例。
请求回调里解析查询串,用 WHATWG URL,不要再用已废弃的 url.parse():
const http = require('http');
const hostname = '127.0.0.1';
const port = 3000;
const server = http.createServer((req, res) => {
res.statusCode = 200;
res.statusMessage = 'OK';
res.setHeader('Content-Type', 'text/plain; charset=utf-8');
const urlObj = new URL(req.url, 'http://' + req.headers.host);
res.write(urlObj.searchParams.get('name') || 'Lizhao01!');
res.end();
});
server.listen(port, hostname, () => {
console.log(`服务器运行在 http://${hostname}:${port}/`);
});
createServer 传入的函数等价于监听 request。不要再 server.on('request', ...) 绑一次,否则每个请求会进两个处理器。
checkContinue 事件
收到带 Expect: 100-continue 的请求时触发。未监听时服务器自动回 100 Continue。
若客户端应继续发主体,调用 response.writeContinue();否则回适当状态(例如 400)。处理本事件后 不会 再触发 request。
checkExpectation 事件
Expect 不是 100-continue 时触发。未监听则自动回 417 Expectation Failed。处理后不会再触发 request。
clientError 事件
客户端连接出错。默认会立刻销毁 socket。
close 事件
服务器关闭时触发。
connect 事件
客户端发 HTTP CONNECT 时触发。未监听则关掉该连接。
connection 事件
新的 TCP 流建立时触发(与 net.Server 的 connection 相同;较新文档也写作 connect)。
request 事件
每收到一个 HTTP 请求触发。keep-alive 下 一条 TCP 连接上可以有多次 request。
upgrade 事件
客户端发 Upgrade 时触发。未监听则关掉该连接。WebSocket 握手走这条。
close([callback])
停止接收新连接。已有连接不会立刻掐断。
listen()
开始监听。可传端口、主机、回调,或 Unix socket 路径。
setTimeout()
设置超时毫秒。超时触发 timeout 事件。
server.setTimeout([msecs][, callback]);
listening
布尔值,是否正在监听。
maxHeadersCount
请求头个数上限,默认 2000。设为 0 表示不限制。
timeout
socket 无活动超时,毫秒。默认 120000(2 分钟)。0 关闭该超时。
只影响 之后新建 的连接。
keepAliveTimeout
发完最后一个响应后,还等多久入站数据才销毁 socket,默认 5000。keep-alive 期间收到新数据会重置计时。0 关闭该行为。同样只影响新建连接。
ClientRequest 类
http.request()、http.get() 返回的对象,表示一个已排队请求头的客户端请求。头仍可用 setHeader、getHeader、removeHeader 改,直到写出为止。
收到响应时触发 response,参数是 http.IncomingMessage。读主体:在 response 回调里听 data、end,或当可读流用。
Node 不会核对 Content-Length 是否等于实际发出的主体长度。
abort 事件
请求被客户端终止时触发(旧 abort() 路径)。现行应听 close,并用 destroy() 终止。
connect 事件
服务器响应 CONNECT 时触发。未监听则关闭连接。
continue 事件
服务器回了 100 Continue(通常因为请求带了 Expect: 100-continue),表示可以开始发主体。
response 事件
收到响应时触发,只一次。
socket 事件
socket 分配给该请求后触发。
timeout 事件
底层 socket 空闲超时。只是通知,请求要自己 destroy()。
upgrade 事件
服务器响应 Upgrade 时触发。未监听则关闭连接。
abort()
已废弃。改用 request.destroy()。
end()
结束发送。未发完的主体会刷新;分块传输会写结束块 0\r\n\r\n。
request.end([data[, encoding]][, callback]);
有 data 时等于 write 再 end。callback 在请求流结束时调用。
flushHeaders()
立刻把请求头发出去。默认会等到 end() 或第一块 body,以便和数据打成一个 TCP 包。第一块数据要很久才有时,用 flushHeaders() 提前开请求。
getHeader(name)
读尚未发出的请求头。名称 不区分 大小写。
removeHeader(name)
从待发头里去掉一项。
setHeader(name, value)
设置单个头;已存在则覆盖。同名多个头用字符串数组。
setNoDelay([noDelay])
socket 连上后调用 socket.setNoDelay()。
setSocketKeepAlive()
socket 连上后调用 socket.setKeepAlive()。
request.setSocketKeepAlive([enable][, initialDelay]);
setTimeout(timeout[, callback])
socket 连上后调用 socket.setTimeout()。返回 request。
write()
写一块请求体。多次调用即可。这种情况下创建请求时建议用分块:Transfer-Encoding: chunked。
request.write(chunk[, encoding][, callback]);
chunk:string 或 Bufferencoding:默认'utf8',仅字符串有效callback:这块刷新后调用
aborted
已废弃。是否已终止请看 request.destroyed。旧值曾是终止时刻的毫秒时间戳。
connection
已废弃,等同 request.socket。end 之后不要再依赖它。协议解析绑在 socket 上,socket 不会再触发普通的 readable。
socket
底层 socket。
ServerResponse 类
HTTP 服务器内部创建,实现 stream.Writable(不是继承)。
close 事件
在 response.end() 或还能刷新之前,底层连接被掐掉时触发。
finish 事件
响应头和主体最后一块已交给操作系统发送时触发,不表示客户端已经收到。之后响应对象上不再有别的事件。
addTrailers(headers)
加尾部头。只有分块编码才会发出去;HTTP/1.0 等会丢弃。先在头里声明 Trailer:
response.writeHead(200, {
'Content-Type': 'text/plain',
Trailer: 'Content-MD5'
});
response.write(fileData);
response.addTrailers({ 'Content-MD5': '7895bf4b8828b55ceaf47747b4bca667' });
response.end();
end()
声明头和主体都发完。每个响应都必须调用。
response.end([data][, encoding][, callback]);
getHeader()
读已排队、尚未发给客户端的响应头。名称不区分大小写。
response.getHeader(name);
getHeaderNames()
当前响应头名称数组,均为小写。
getHeaders()
当前响应头的浅拷贝。改数组值会直接改到内部状态。键是小写头名。
返回对象 没有 Object.prototype,没有 toString、hasOwnProperty。
hasHeader()
是否已设置名为 name 的头。不区分大小写。
response.hasHeader(name);
removeHeader()
从隐式发送队列里去掉一个头。
response.removeHeader(name);
setHeader()
设置隐式响应头,已存在则覆盖。非法字符抛 TypeError。
response.setHeader(name, value);
response.setHeader('Set-Cookie', ['type=ninja', 'language=javascript']);
与 writeHead() 合并时,writeHead() 优先。
setTimeout()
设置 socket 超时。有回调则作为 timeout 监听器。
response.setTimeout(msecs[, callback]);
write()
写一块响应体,可多次调用。若还没 writeHead(),会切到隐式响应头并刷新。
response.write(chunk[, encoding][, callback]);
HEAD 请求、以及 204、304 响应不能带消息体。
第一次 write 会把缓冲的头和第一块主体发出去;之后才持续传。响应能否尽快结束,和第一块数据何时写出有关。
writeContinue()
向客户端发 HTTP/1.1 100 Continue。
writeHead()
发送响应头。整条消息里只能调用一次,且必须在 end() 之前。
response.writeHead(statusCode[, statusMessage][, headers]);
若已经 write()、end(),会先处理隐式头再走到这里。
connection、socket
底层 socket。connection 是旧名,用 socket。
finished
已废弃。用 writableEnded(已调用 end)和 writableFinished(底层写完)。
headersSent
只读。头是否已发出。
sendDate
为 true 且没有 Date 头时自动补日期。默认 true。HTTP 响应需要日期头,只应在测试里关掉。
statusCode
隐式响应头时,刷新头用的状态码。头发出后表示实际发出的码。
statusMessage
隐式响应头时的原因短语;undefined 则用该状态码的标准短语。
IncomingMessage 类
由 http.Server 或 http.ClientRequest 创建,分别作为 request、response 事件的第一个参数。可读流,用来读状态、头和数据。
aborted 事件
已废弃。请求被终止且 socket 关闭时曾触发。改听 close。
close 事件
底层连接关闭。每个消息一次。
destroy()
销毁收到该消息的 socket。
message.destroy([error]);
传入 error 会在 socket 上触发 error。
setTimeout()
调用 message.socket.setTimeout(msecs, callback)。
message.setTimeout(msecs, callback);
headers
请求头或响应头对象,键为小写。set-cookie 在较新 Node 里是数组;重复的其他头会合并成逗号分隔字符串。原始大小写和重复项见 rawHeaders。
httpVersion
服务器上:客户端声明的 HTTP 版本;客户端上:对端服务器的版本。如 '1.1'。
httpVersionMajor、httpVersionMinor
版本号的整数部分。
method(只读)
请求方法字符串。仅服务器收到的请求上有。
rawHeaders
原始头列表:偶数下标是键,奇数是值。名称保持原大小写,重复项不合并。
trailers
尾部头对象,只在 end 之后才有值。
rawTrailers
原始尾部头键值列表,同样只在 end 时赋值。
socket
关联的 net.Socket。
statusCode
三位响应状态码。仅客户端收到的响应上有。
statusMessage
原因短语。仅客户端响应上有。
url
请求 URL 字符串,只含请求行里的路径和查询,没有协议和主机。仅服务器请求上有。拼完整 URL:new URL(req.url, 'http://' + req.headers.host)。
Agent 类
管理客户端连接的复用。按主机和端口排队请求,队列空了才把 socket 销毁或放回池。是否入池看 keepAlive。
池里的 TCP Keep-Alive 是开的,但服务器仍会关掉空闲连接;那时要从池里拿掉,下次再新建。有的服务器不允许一条连接上复用多个请求,这时每个请求都是新连接。
连接被任一方关闭就离开池。池里闲置的 socket 会释放,这样没有请求时进程不必空转。
Agent 不用了应 destroy(),否则 keep-alive 的 socket 会占操作系统资源。
new Agent([options])
keepAlive:布尔。为true时没有请求也留着 socket。自定义 Agent 默认仍是false。http.globalAgent从 Node 19 起默认keepAlive: true。keepAliveMsecs:整数,默认 1000。TCP Keep-Alive 探测的初始延迟(keepAlive为假时无效)。maxSockets:每个来源最多多少并发 socket,默认Infinity。maxFreeSockets:空闲最多留多少,默认 256,仅keepAlive为真时有效。timeout:socket 超时毫秒。globalAgent 在 Node 19+ 默认 5000。
http.request() 默认用 http.globalAgent。要改行为就自己 new http.Agent(...):
const http = require('http');
const agent = new http.Agent({
keepAlive: true,
keepAliveMsecs: 500,
maxSockets: 88,
maxFreeSockets: 126
});
createConnection(options[, callback])
为请求创建 socket 或流。默认类似 net.createConnection()。自定义 Agent 可覆盖。
host:默认'localhost'port:端口path:Unix socket 路径localAddress、localPort:本地绑定family:4或6hints:传给dns.lookup()的 hintslookup:默认dns.lookup()callback(err, stream)
keepSocketAlive(socket)
请求用完 socket、代理还想留着时调用。
reuseSocket(socket, request)
keep-alive 的 socket 再次绑到请求时调用。
destroy()
毁掉代理正在用的 socket。启用了 keepAlive 、确定不再用时应该显式关掉。
getName(options)
给一组选项算唯一名,用来判断连接能否复用。HTTP 一般是 host:port:localAddress 或带 family;HTTPS 还会把 CA、证书等算进去。
sockets、freeSockets、requests
当前在用的 socket、空闲池、尚未分到 socket 的请求队列。不要改这些对象。
maxFreeSockets、maxSockets
可读写的上限,含义同构造选项。
http 属性
METHODS
支持的 HTTP 方法名数组。
STATUS_CODES
状态码到短描述的映射。
globalAgent
所有客户端请求的默认 Agent。
maxHeaderSize(只读)
单个 HTTP 头允许的最大字节数。现行默认 16KB(旧版文档写 8KB)。可用启动参数 --max-http-header-size 改。创建 server、request 时也可传 maxHeaderSize 覆盖。
http 方法
createServer([options][, requestListener])
返回 http.Server。requestListener 自动听 request。
const http = require('http');
const hostname = '127.0.0.1';
const port = 3000;
const server = http.createServer((req, res) => {
res.statusCode = 200;
res.setHeader('Content-Type', 'text/plain; charset=utf-8');
res.end('你好,Lizhao!');
});
server.listen(port, hostname, () => {
console.log(`服务器运行在 http://${hostname}:${port}/`);
});
node ./index.js 后浏览器打开 http://127.0.0.1:3000/。
request()
发 HTTP 请求,返回 http.ClientRequest。
http.request(options[, callback]);
http.request(url[, options][, callback]);
常用 options:
protocol:默认'http:'host、hostname:默认'localhost'。同时有时 hostname 优先family:4或6port:默认80localAddress、socketPathmethod:默认'GET'path:默认'/',含查询串。含空字符会抛错headers、auth(user:pass基本认证)agent:undefined用 globalAgent;传入 Agent 则用它;false则新建一个默认 AgentcreateConnection:不用 agent 时自己造 sockettimeout:socket 超时毫秒
options 可以是对象、字符串或 URL。字符串按 URL 解析。出错(DNS、TCP、HTTP 解析)在返回的请求上触发 error;没有监听器就会变成未捕获异常。
const http = require('http');
const request = http.request('http://127.0.0.1:3000/', (res) => {
res.on('data', (chunk) => {
console.log(`响应主体: ${chunk}`);
});
res.on('end', () => {
console.log('请求结束!');
});
});
request.on('error', (err) => {
console.error(err);
});
request.end();
必须调用 request.end(),即使没有 body。上面示例里不要对 GET 再 write('ok'),除非服务器真的在读 GET 主体。
get()
类似 request(),方法固定为 GET,并且 会自动 end()。