Bun

HTTP 客户端

Bun implements the WHATWG fetch standard, with some extensions to meet the needs of server-side JavaScript.

Bun also implements node:http, but fetch is generally recommended instead.

发送 HTTP 请求

To send an HTTP request, use 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 also works with HTTPS URLs.

const response = await fetch("https://example.com");

You can also pass fetch a Request object.

const request = new Request("http://example.com", {
  method: "POST",
  body: "Hello, world!",
});

const response = await fetch(request);

发送 POST 请求

To send a POST request, pass an object with the method property set to "POST".

const response = await fetch("http://example.com", {
  method: "POST",
  body: "Hello, world!",
});

body can be a string, a FormData object, an ArrayBuffer, a Blob, and more. See the MDN documentation for more information.

代理请求

To proxy a request, pass an object with the proxy property set to a URL.

const response = await fetch("http://example.com", {
  proxy: "http://proxy.com",
});

自定义标头

To set custom headers, pass an object with the headers property set to an object.

const response = await fetch("http://example.com", {
  headers: {
    "X-Custom-Header": "value",
  },
});

You can also set headers using the Headers object.

const headers = new Headers();
headers.append("X-Custom-Header", "value");

const response = await fetch("http://example.com", {
  headers,
});

响应正文

To read the response body, use one of the following methods

  • response.text(): Promise<string>: Returns a promise that resolves with the response body as a string.
  • response.json(): Promise<any>: Returns a promise that resolves with the response body as a JSON object.
  • response.formData(): Promise<FormData>: Returns a promise that resolves with the response body as a FormData object.
  • response.bytes(): Promise<Uint8Array>: Returns a promise that resolves with the response body as a Uint8Array.
  • response.arrayBuffer(): Promise<ArrayBuffer>: Returns a promise that resolves with the response body as an ArrayBuffer.
  • response.blob(): Promise<Blob>: Returns a promise that resolves with the response body as a Blob.

流式传输响应正文

You can use async iterators to stream the response body.

const response = await fetch("http://example.com");

for await (const chunk of response.body) {
  console.log(chunk);
}

You can also more directly access the ReadableStream object.

const response = await fetch("http://example.com");

const stream = response.body;

const reader = stream.getReader();
const { value, done } = await reader.read();

流式传输请求正文

You can also stream data in request bodies using a 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,
});

When using streams with HTTP(S)

  • The data is streamed directly to the network without buffering the entire body in memory
  • If the connection is lost, the stream will be canceled
  • The Content-Length header is not automatically set unless the stream has a known size

When using streams with S3

  • For PUT/POST requests, Bun automatically uses multipart upload
  • The stream is consumed in chunks and uploaded in parallel
  • Progress can be monitored through the S3 options

带超时的 URL 获取

To fetch a URL with a timeout, use AbortSignal.timeout

const response = await fetch("http://example.com", {
  signal: AbortSignal.timeout(1000),
});

取消请求

To cancel a request, use an AbortController

const controller = new AbortController();

const response = await fetch("http://example.com", {
  signal: controller.signal,
});

controller.abort();

Unix 域套接字

To fetch a URL using a Unix domain socket, use the unix: string option

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

To use a client certificate, use the tls option

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 验证

To customize the TLS validation, use the checkServerIdentity option in tls

await fetch("https://example.com", {
  tls: {
    checkServerIdentity: (hostname, peerCertificate) => {
      // Return an Error if the certificate is invalid
    },
  },
});

This is similar to how it works in Node's net module.

禁用 TLS 验证

To disable TLS validation, set rejectUnauthorized to false

await fetch("https://example.com", {
  tls: {
    rejectUnauthorized: false,
  },
});

This is especially useful to avoid SSL errors when using self-signed certificates, but this disables TLS validation and should be used with caution.

请求选项

In addition to the standard fetch options, Bun provides several extensions

const response = await fetch("http://example.com", {
  // Control automatic response decompression (default: true)
  // Supports gzip, deflate, brotli (br), and zstd
  decompress: true,

  // Disable connection reuse for this request
  keepalive: false,

  // Debug logging level
  verbose: true, // or "curl" for more detailed output
});

协议支持

Beyond HTTP(S), Bun's fetch supports several additional protocols

S3 URLs - s3://

Bun supports fetching from S3 buckets directly.

// 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",
  },
});

Note: Only PUT and POST methods support request bodies when using S3. For uploads, Bun automatically uses multipart upload for streaming bodies.

You can read more about Bun's S3 support in the S3 documentation.

File URLs - file://

You can fetch local files using the file: protocol

const response = await fetch("file:///path/to/file.txt");
const text = await response.text();

On Windows, paths are automatically normalized

// 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 URLs - data:

Bun supports the data: URL scheme

const response = await fetch("data:text/plain;base64,SGVsbG8sIFdvcmxkIQ==");
const text = await response.text(); // "Hello, World!"

Blob URLs - blob:

You can fetch blobs using URLs created by URL.createObjectURL()

const blob = new Blob(["Hello, World!"], { type: "text/plain" });
const url = URL.createObjectURL(blob);
const response = await fetch(url);

错误处理

Bun's fetch implementation includes several specific error cases

  • Using a request body with GET/HEAD methods will throw an error (which is expected for the fetch API)
  • Attempting to use both proxy and unix options together will throw an error
  • TLS certificate validation failures when rejectUnauthorized is true (or undefined)
  • S3 operations may throw specific errors related to authentication or permissions

Content-Type 处理

Bun automatically sets the Content-Type header for request bodies when not explicitly provided

  • For Blob objects, uses the blob's type
  • For FormData, sets appropriate multipart boundary

调试

To help with debugging, you can pass verbose: true to fetch

const response = await fetch("http://example.com", {
  verbose: true,
});

This will print the request and response headers to your terminal

[fetch] > HTTP/1.1 GET http://example.com/
[fetch] > Connection: keep-alive
[fetch] > User-Agent: Bun/1.3.0
[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

Note: verbose: boolean is not part of the Web standard fetch API and is specific to 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.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,536。最大端口号为 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 的特定扩展。