fetch 标准,同时进行了扩展以满足服务端 JavaScript 的需求。
Bun 也实现了 node:http,但通常推荐使用 fetch。
发送 HTTP 请求
要发送 HTTP 请求,请使用fetch:
fetch 也支持 HTTPS URL。
Request 对象给 fetch。
发送 POST 请求
要发送 POST 请求,传入一个method 属性值为 "POST" 的对象。
body 可以是字符串、FormData 对象、ArrayBuffer、Blob 或 MDN 文档中列出的其他请求体类型。
代理请求
要代理请求,传入一个对象,并将proxy 属性设置为 URL 字符串、URL 实例,或将其设置为一个 url 为字符串或 URL 的对象:
headers 会直接在 CONNECT 请求(针对 HTTPS 目标)或代理请求(针对 HTTP 目标)中发送给代理。如果你提供了 Proxy-Authorization 头,它会覆盖代理 URL 中的任何凭据。
自定义请求头
要设置自定义请求头,传入一个带有headers 属性的对象。
响应体
要读取响应体,使用以下方法之一:response.text(): Promise<string>:返回包含响应体文本的 Promise。response.json(): Promise<any>:返回包含响应体 JSON 对象的 Promise。response.formData(): Promise<FormData>:返回包含响应体 FormData 对象的 Promise。response.bytes(): Promise<Uint8Array>:返回包含响应体Uint8Array的 Promise。response.arrayBuffer(): Promise<ArrayBuffer>:返回包含响应体ArrayBuffer的 Promise。response.blob(): Promise<Blob>:返回包含响应体Blob的 Promise。
流式响应体
你可以使用异步迭代器来流式读取响应体。ReadableStream。
流式请求体
你也可以使用ReadableStream 来流式传输请求体数据:
- 数据会直接流式传输到网络,而不会将整个请求体缓存在内存中
- 如果连接断开,流会被取消
- 除非流的大小已知,否则不会自动设置
Content-Length头
- 对于 PUT/POST 请求,Bun 会自动使用分块上传
- 流以块的形式消费,并行上传
- 可以通过 S3 选项监控上传进度
带超时的 URL 请求
要带超时发送请求,使用AbortSignal.timeout:
取消请求
要取消请求,使用AbortController:
Unix 域套接字
要通过 Unix 域套接字请求 URL,使用unix: string 选项:
TLS
使用客户端证书时,使用tls 选项:
自定义 TLS 验证
要自定义 TLS 验证,请在tls 中使用 checkServerIdentity 选项:
tls 模块中的同名选项。
禁用 TLS 验证
要禁用 TLS 验证,将rejectUnauthorized 设为 false:
请求选项
除了标准的 fetch 选项,Bun 还提供了几个扩展:协议支持
除了 HTTP(S) 之外,Bun 的 fetch 还支持多种其他协议:S3 URL - s3://
Bun 支持直接从 S3 桶读取。
本地文件 URL - file://
你可以用 file: 协议获取本地文件:
Data URL - data:
Bun 支持 data: URL 方案:
Blob URL - blob:
你可以使用 URL.createObjectURL() 创建的 URL 来获取 Blob:
错误处理
Bun 的 fetch 实现包含多个特定错误场景:- 对 GET/HEAD 方法使用请求体会抛出错误(这是 fetch API 的预期行为)
- 同时使用
proxy和unix选项会抛出错误 - 当
rejectUnauthorized为true(或未定义)时,TLS 证书验证失败 - S3 操作可能会抛出与身份验证或权限相关的特定错误
Content-Type 处理
当未明确提供时,Bun 会自动为请求体设置Content-Type 头:
- 对于
Blob对象,使用 Blob 的type - 对于
FormData,设置合适的 multipart 边界
调试
要进行调试,请将verbose: true 传递给 fetch:
verbose: boolean 是 Bun 特有的扩展,不属于 Web 标准的 fetch API。
性能
在发送 HTTP 请求之前,Bun 必须解析 DNS、连接 TCP 套接字,有时还要完成 TLS 握手。每个步骤都需要时间,尤其是在 DNS 服务器速度较慢或网络连接质量较差时。请求完成后,读取响应体同样需要时间和内存。 Bun 提供了用于优化这些步骤的 API。DNS 预读取
当你知道即将连接某个主机,并希望避免初始 DNS 查询时,可以使用dns.prefetch。
DNS 缓存
默认情况下,Bun 会在内存中缓存并去重 DNS 查询,最长缓存 30 秒。dns.getCacheStats() 会返回缓存统计信息。
参见 DNS 缓存。
预连接到主机
fetch.preconnect 会在你准备向某个主机发送请求之前,启动 DNS 查询、TCP 套接字连接和 TLS 握手。
fetch.preconnect 后立即调用 fetch 并不会让请求更快。只有在确定主机和发送请求之间存在时间间隔时,预连接才能发挥作用。
启动时预连接
要在启动时预连接到某个主机,请传入--fetch-preconnect:
--fetch-preconnect 类似于 HTML 中的 <link rel="preconnect">。Windows 尚未实现此功能;如果你需要在 Windows 上使用,请提交 issue。
连接池与 HTTP 长连接
Bun 会自动复用连接到同一主机的连接。这称为连接池,可以显著减少建立连接所需的时间。同时连接数限制
默认情况下,Bun 将同时进行的fetch 请求数限制为 256,原因有二:
- 改善系统整体稳定性。操作系统对同时打开的 TCP 套接字数有上限,通常在几千左右。接近该限制时,会导致整台电脑异常,应用挂起或崩溃。
- 鼓励 HTTP Keep-Alive 的连接复用。对于短时 HTTP 请求,最慢的步骤往往是初次连接。连接复用可节省大量时间。
BUN_CONFIG_MAX_HTTP_REQUESTS 环境变量:
响应体缓冲
读取响应体最快的方式是使用以下方法之一:response.text(): Promise<string>response.json(): Promise<any>response.formData(): Promise<FormData>response.bytes(): Promise<Uint8Array>response.arrayBuffer(): Promise<ArrayBuffer>response.blob(): Promise<Blob>
Bun.write 将响应体写入磁盘文件:
实现细节
- 默认启用连接池,但可以通过
keepalive: false或"Connection: close"请求头针对单个请求禁用。 - 在特定条件下,大文件上传会使用操作系统的
sendfile系统调用进行优化:- 文件必须大于 32KB
- 请求不能使用代理
- 在 macOS 上,只有普通文件(不包括管道、套接字或设备)可以使用
sendfile - 不满足这些条件,或使用 S3/流式上传时,Bun 会改为将文件读入内存
- 对于 HTTP(而非 HTTPS)请求,这项优化尤其有效,因为文件可以直接从内核发送到网络协议栈
- S3 操作会自动处理请求签名并合并身份验证请求头