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(); // or response.json(), response.formData(), etc.
fetch
也适用于 HTTPS URL。
const response = await fetch("https://example.com");
您还可以将 Request
对象传递给 fetch
。
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",
});
自定义标头
要设置自定义标头,请传递一个对象,并将 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,该 Promise 解析为字符串格式的响应主体。response.json(): Promise<any>
:返回一个 Promise,该 Promise 解析为 JSON 对象格式的响应主体。response.formData(): Promise<FormData>
:返回一个 Promise,该 Promise 解析为FormData
对象格式的响应主体。response.bytes(): Promise<Uint8Array>
:返回一个 Promise,该 Promise 解析为Uint8Array
格式的响应主体。response.arrayBuffer(): Promise<ArrayBuffer>
:返回一个 Promise,该 Promise 解析为ArrayBuffer
格式的响应主体。response.blob(): Promise<Blob>
:返回一个 Promise,该 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) => {
// Return an Error if the certificate is invalid
},
},
});
这与 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", {
// Control automatic response decompression (default: true)
decompress: true,
// Disable connection reuse for this request
keepalive: false,
// Debug logging level
verbose: true, // or "curl" for more detailed output
});
协议支持
除了 HTTP(S) 之外,Bun 的 fetch 还支持多种其他协议
S3 URL - s3://
Bun 支持直接从 S3 存储桶中获取数据。
// Using environment variables for credentials
const response = await fetch("s3://my-bucket/path/to/object");
// Or passing credentials explicitly
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 上,路径会自动规范化
// Both work on Windows
const response = await fetch("file:///C:/path/to/file.txt");
const response2 = await fetch("file:///c:/path\\to/file.txt");
Data 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
,设置适当的多部分边界 - 对于 JSON 对象,设置
application/json
调试
为了帮助调试,您可以将 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.2.5
[fetch] > Accept: */*
[fetch] > Host: example.com
[fetch] > Accept-Encoding: gzip, deflate, br
[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.sh");
DNS 缓存
默认情况下,Bun 会在内存中缓存和去重 DNS 查询长达 30 秒。您可以通过调用 dns.getCacheStats()
来查看缓存统计信息
要了解有关 Bun 中的 DNS 缓存的更多信息,请参阅 DNS 缓存 文档。
预连接到主机
要预连接到主机,您可以使用 fetch.preconnect
API。当您知道您很快需要连接到主机并希望提前启动初始 DNS 查找、TCP 套接字连接和 TLS 握手时,此 API 非常有用。
import { fetch } from "bun";
fetch.preconnect("https://bun.net.cn");
注意:在 fetch.preconnect
之后立即调用 fetch
不会使您的请求更快。仅当您知道您很快需要连接到主机,但您尚未准备好发出请求时,预连接才会有所帮助。
启动时预连接
要在启动时预连接到主机,您可以传递 --fetch-preconnect
bun --fetch-preconnect https://bun.net.cn ./my-script.ts
这有点像 HTML 中的 <link rel="preconnect">
。
此功能尚未在 Windows 上实现。如果您有兴趣在 Windows 上使用此功能,请提交 issue,我们可以实现对 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)请求尤其有效,在 HTTP 请求中,文件可以直接从内核发送到网络堆栈
- S3 操作会自动处理签名请求和合并身份验证标头
注意:这些功能中的许多是 Bun 对标准 fetch API 的特定扩展。