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 的特定擴展。