Bun 实现了 WHATWG fetch 标准,并添加了一些扩展以满足服务器端 JavaScript 的需求。
Bun 也实现了 node:http,但更推荐使用 fetch。
发送 HTTP 请求
要发送 HTTP 请求,使用 fetch
const response = await fetch("http://example.com");
console.log(response.status); // => 200
const text = await response.text(); // 或 response.json()、response.formData() 等fetch 也适用于 HTTPS URL。
const response = await fetch("https://example.com");你也可以传递 fetch 一个 Request 对象。
const request = new Request("http://example.com", {
method: "POST",
body: "Hello, world!",
});
const response = await fetch(request);发送 POST 请求
要发送 POST 请求,传递一个对象,将 method 属性设置为 "POST"。
const response = await fetch("http://example.com", {
method: "POST",
body: "Hello, world!",
});body 可以是字符串、FormData 对象、ArrayBuffer、Blob 等。有关更多信息,请参阅 MDN 文档。
代理请求
要代理请求,传递一个对象,将 proxy 属性设置为 URL 字符串:
const response = await fetch("http://example.com", {
proxy: "http://proxy.com",
});你也可以使用对象格式向代理服务器发送自定义头:
const response = await fetch("http://example.com", {
proxy: {
url: "http://proxy.com",
headers: {
"Proxy-Authorization": "Bearer my-token",
"X-Custom-Proxy-Header": "value",
},
},
});headers 在 CONNECT 请求(对于 HTTPS 目标)中直接发送到代理,或在代理请求中(对于 HTTP 目标)发送。如果你提供 Proxy-Authorization 头,它会覆盖代理 URL 中的任何凭据。
自定义头
要设置自定义头,传递一个对象,将 headers 属性设置为对象。
const response = await fetch("http://example.com", {
headers: {
"X-Custom-Header": "value",
},
});你也可以使用 Headers 对象设置头。
const headers = new Headers();
headers.append("X-Custom-Header", "value");
const response = await fetch("http://example.com", {
headers,
});响应体
要读取响应体,使用以下方法之一:
response.text(): Promise<string>:返回一个 Promise,解析响应体为字符串。response.json(): Promise<any>:返回一个 Promise,解析响应体为 JSON 对象。response.formData(): Promise<FormData>:返回一个 Promise,解析响应体为FormData对象。response.bytes(): Promise<Uint8Array>:返回一个 Promise,解析响应体为Uint8Array。response.arrayBuffer(): Promise<ArrayBuffer>:返回一个 Promise,解析响应体为ArrayBuffer。response.blob(): Promise<Blob>:返回一个 Promise,解析响应体为Blob。
流式响应体
你可以使用异步迭代器流式传输响应体。
const response = await fetch("http://example.com");
for await (const chunk of response.body) {
console.log(chunk);
}你也可以更直接地访问 ReadableStream 对象。
const response = await fetch("http://example.com");
const stream = response.body;
const reader = stream.getReader();
const { value, done } = await reader.read();流式请求体
你也可以使用 ReadableStream 在请求体中流式传输数据:
const stream = new ReadableStream({
start(controller) {
controller.enqueue("Hello");
controller.enqueue(" ");
controller.enqueue("World");
controller.close();
},
});
const response = await fetch("http://example.com", {
method: "POST",
body: stream,
});当与 HTTP(S) 一起使用流时:
- 数据直接流式传输到网络,而不会将整个体缓冲到内存中
- 如果连接丢失,流将被取消
- 除非流具有已知大小,否则不会自动设置
Content-Length头
当与 S3 一起使用流时:
- 对于 PUT/POST 请求,Bun 自动使用多部分上传
- 流以块的形式消耗并并行上传
- 可以通过 S3 选项监控进度
带超时获取 URL
要带超时获取 URL,使用 AbortSignal.timeout:
const response = await fetch("http://example.com", {
signal: AbortSignal.timeout(1000),
});取消请求
要取消请求,使用 AbortController:
const controller = new AbortController();
const response = await fetch("http://example.com", {
signal: controller.signal,
});
controller.abort();Unix 域套接字
要使用 Unix 域套接字获取 URL,使用 unix: string 选项:
const response = await fetch("https://hostname/a/path", {
unix: "/var/run/path/to/unix.sock",
method: "POST",
body: JSON.stringify({ message: "Hello from Bun!" }),
headers: {
"Content-Type": "application/json",
},
});TLS
要使用客户端证书,使用 tls 选项:
await fetch("https://example.com", {
tls: {
key: Bun.file("/path/to/key.pem"),
cert: Bun.file("/path/to/cert.pem"),
// ca: [Bun.file("/path/to/ca.pem")],
},
});自定义 TLS 验证
要自定义 TLS 验证,在 tls 中使用 checkServerIdentity 选项
await fetch("https://example.com", {
tls: {
checkServerIdentity: (hostname, peerCertificate) => {
// 如果证书无效则返回 Error
},
},
});这与它在 Node 的 net 模块中的工作方式类似。
禁用 TLS 验证
要禁用 TLS 验证,将 rejectUnauthorized 设置为 false:
await fetch("https://example.com", {
tls: {
rejectUnauthorized: false,
},
});这在使用自签名证书时特别有用,可以避免 SSL 错误,但这会禁用 TLS 验证,应谨慎使用。
请求选项
除了标准 fetch 选项外,Bun 还提供了几个扩展:
const response = await fetch("http://example.com", {
// 控制自动响应解压缩(默认:true)
// 支持 gzip、deflate、brotli (br) 和 zstd
decompress: true,
// 禁用此请求的连接重用
keepalive: false,
// 调试日志级别
verbose: true, // 或 "curl" 以获得更详细的输出
});协议支持
除了 HTTP(S) 之外,Bun 的 fetch 还支持几种额外的协议:
S3 URL - s3://
Bun 支持直接从 S3 存储桶获取。
// 使用环境变量获取凭据
const response = await fetch("s3://my-bucket/path/to/object");
// 或显式传递凭据
const response = await fetch("s3://my-bucket/path/to/object", {
s3: {
accessKeyId: "YOUR_ACCESS_KEY",
secretAccessKey: "YOUR_SECRET_KEY",
region: "us-east-1",
},
});注意:使用 S3 时,只有 PUT 和 POST 方法支持请求体。对于上传,Bun 自动对流式体使用多部分上传。
你可以在 S3 文档中阅读有关 Bun 的 S3 支持的更多信息。
文件 URL - file://
你可以使用 file: 协议获取本地文件:
const response = await fetch("file:///path/to/file.txt");
const text = await response.text();在 Windows 上,路径会自动规范化:
// 在 Windows 上都有效
const response = await fetch("file:///C:/path/to/file.txt");
const response2 = await fetch("file:///c:/path\\to/file.txt");数据 URL - data:
Bun 支持 data: URL 方案:
const response = await fetch("data:text/plain;base64,SGVsbG8sIFdvcmxkIQ==");
const text = await response.text(); // "Hello, World!"Blob URL - blob:
你可以使用 URL.createObjectURL() 创建的 URL 获取 blob:
const blob = new Blob(["Hello, World!"], { type: "text/plain" });
const url = URL.createObjectURL(blob);
const response = await fetch(url);错误处理
Bun 的 fetch 实现包括几个特定的错误情况:
- 对 GET/HEAD 方法使用请求体会抛出错误(这对于 fetch API 是预期的)
- 同时使用
proxy和unix选项会抛出错误 - 当
rejectUnauthorized为 true(或未定义)时,TLS 证书验证失败 - S3 操作可能会抛出与身份验证或权限相关的特定错误
Content-Type 处理
当未显式提供时,Bun 会自动为请求体设置 Content-Type 头:
- 对于
Blob对象,使用 blob 的type - 对于
FormData,设置适当的多部分边界
调试
为了帮助调试,你可以传递 verbose: true 给 fetch:
const response = await fetch("http://example.com", {
verbose: true,
});这会将请求和响应头打印到你的终端:
[fetch] > HTTP/1.1 GET http://example.com/
[fetch] > Connection: keep-alive
[fetch] > User-Agent: Bun/1.3.3
[fetch] > Accept: */*
[fetch] > Host: example.com
[fetch] > Accept-Encoding: gzip, deflate, br, zstd
[fetch] < 200 OK
[fetch] < Content-Encoding: gzip
[fetch] < Age: 201555
[fetch] < Cache-Control: max-age=604800
[fetch] < Content-Type: text/html; charset=UTF-8
[fetch] < Date: Sun, 21 Jul 2024 02:41:14 GMT
[fetch] < Etag: "3147526947+gzip"
[fetch] < Expires: Sun, 28 Jul 2024 02:41:14 GMT
[fetch] < Last-Modified: Thu, 17 Oct 2019 07:18:26 GMT
[fetch] < Server: ECAcc (sac/254F)
[fetch] < Vary: Accept-Encoding
[fetch] < X-Cache: HIT
[fetch] < Content-Length: 648注意:verbose: boolean 不是 Web 标准 fetch API 的一部分,是 Bun 特有的。
性能
在发送 HTTP 请求之前,必须执行 DNS 查找。这可能需要大量时间,特别是如果 DNS 服务器很慢或网络连接很差。
在 DNS 查找之后,必须连接 TCP 套接字,并且可能需要执行 TLS 握手。这也可能需要大量时间。
在请求完成后,使用响应体也可能需要大量时间和内存。
在每一步,Bun 都提供 API 来帮助优化应用程序的性能。
DNS 预取
要预取 DNS 条目,你可以使用 dns.prefetch API。当你知道将来需要连接到主机并希望避免初始 DNS 查找时,此 API 很有用。
import { dns } from "bun";
dns.prefetch("bun.com");DNS 缓存
默认情况下,Bun 在内存中缓存和去重 DNS 查询,最长 30 秒。你可以通过调用 dns.getCacheStats() 查看缓存统计信息:
要了解有关 Bun 中 DNS 缓存的更多信息,请参阅 DNS 缓存 文档。
预连接到主机
要预连接到主机,你可以使用 fetch.preconnect API。当你知道将来需要连接到主机并希望尽早开始初始 DNS 查找、TCP 套接字连接和 TLS 握手时,此 API 很有用。
import { fetch } from "bun";
fetch.preconnect("https://bun.com");注意:在 fetch.preconnect 之后立即调用 fetch 不会使你的请求更快。只有当你知道将来需要连接到主机但尚未准备好发出请求时,预连接才有帮助。
启动时预连接
要在启动时预连接到主机,传递 --fetch-preconnect:
bun --fetch-preconnect https://bun.com ./my-script.ts这有点像 HTML 中的 <link rel="preconnect">。
此功能尚未在 Windows 上实现。如果你有兴趣在 Windows 上使用此功能,请提交问题,我们可以在 Windows 上实现支持。
连接池和 HTTP keep-alive
Bun 自动重用到相同主机的连接。这称为连接池。这可以显著减少建立连接所需的时间。你不需要做任何事情来启用它;它是自动的。
同时连接限制
默认情况下,Bun 将同时 fetch 请求的最大数量限制为 256。我们这样做有几个原因:
- 它提高了整体系统稳定性。操作系统对同时打开的 TCP 套接字数量有上限,通常在几千个。接近这个限制会导致你的整个计算机行为异常。应用程序挂起和崩溃。
- 它鼓励 HTTP Keep-Alive 连接重用。对于短命的 HTTP 请求,最慢的步骤通常是初始连接设置。重用连接可以节省大量时间。
当超过限制时,请求会排队并在下一个请求结束时发送。
你可以通过 BUN_CONFIG_MAX_HTTP_REQUESTS 环境变量增加最大同时连接数:
BUN_CONFIG_MAX_HTTP_REQUESTS=512 bun ./my-script.ts此限制的最大值目前设置为 65,336。最大端口号为 65,535,因此任何一台计算机都很难超过此限制。
响应缓冲
Bun 竭尽全力优化读取响应体的性能。读取响应体最快的方法是使用以下方法之一:
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 将响应体写入磁盘上的文件:
import { write } from "bun";
await write("output.txt", response);实现细节
- 连接池默认启用,但可以使用
keepalive: false按请求禁用。"Connection: close"头也可用于禁用 keep-alive。 - 大文件上传在特定条件下使用操作系统的
sendfile系统调用进行优化:- 文件必须大于 32KB
- 请求不能使用代理
- 在 macOS 上,只有常规文件(不是管道、套接字或设备)可以使用
sendfile - 当不满足这些条件时,或使用 S3/流式上传时,Bun 会将文件读入内存
- 此优化对于 HTTP(不是 HTTPS)请求特别有效,其中文件可以直接从内核发送到网络栈
- S3 操作自动处理签名请求和合并身份验证头
注意:许多这些功能是 Bun 对标准 fetch API 的特定扩展。