Skip to content

Bun реализует стандарт WHATWG fetch с некоторыми расширениями для удовлетворения потребностей серверного JavaScript.

Bun также реализует node:http но fetch обычно рекомендуется вместо него.

Отправка HTTP-запроса

Для отправки HTTP-запроса используйте fetch

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

console.log(response.status); // => 200

const text = await response.text(); // или response.json(), response.formData() и т.д.

fetch также работает с HTTPS URL.

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

Вы также можете передать fetch объект Request.

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

const response = await fetch(request);

Отправка POST-запроса

Для отправки POST-запроса передайте объект со свойством method установленным в "POST".

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

body может быть строкой объектом FormData ArrayBuffer Blob и другим. См. документацию MDN для получения дополнительной информации.

Проксирование запросов

Для проксирования запроса передайте объект со свойством proxy установленным в строку URL:

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

Вы также можете использовать формат объекта для отправки пользовательских заголовков на прокси-сервер:

ts
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 установленным в объект.

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

Вы также можете установить заголовки используя объект Headers.

ts
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.

Потоковые тела ответов

Вы можете использовать асинхронные итераторы для потоковой передачи тела ответа.

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

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

Вы также можете более непосредственно получить доступ к объекту ReadableStream.

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

const stream = response.body;

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

Потоковые тела запросов

Вы также можете потоково передавать данные в телах запросов используя ReadableStream:

ts
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:

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

Отмена запроса

Для отмены запроса используйте AbortController:

ts
const controller = new AbortController();

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

controller.abort();

Сокеты доменов Unix

Для получения URL используя сокет домена Unix используйте опцию unix: string:

ts
const response = await fetch("https://hostname/a/path", {
  unix: "/var/run/path/to/unix.sock",
  method: "POST",
  body: JSON.stringify({ message: "Привет от Bun!" }),
  headers: {
    "Content-Type": "application/json",
  },
});

TLS

Для использования клиентского сертификата используйте опцию tls:

ts
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 используйте опцию checkServerIdentity в tls

ts
await fetch("https://example.com", {
  tls: {
    checkServerIdentity: (hostname, peerCertificate) => {
      // Вернуть Error если сертификат недействителен
    },
  },
});

Это похоже на то как это работает в модуле net Node.

Отключение проверки TLS

Для отключения проверки TLS установите rejectUnauthorized в false:

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

Это особенно полезно для избежания ошибок SSL при использовании самоподписанных сертификатов но это отключает проверку TLS и должно использоваться с осторожностью.

Опции запроса

В дополнение к стандартным опциям fetch Bun предоставляет несколько расширений:

ts
const response = await fetch("http://example.com", {
  // Управление автоматической декомпрессией ответа (по умолчанию: true)
  // Поддерживает gzip, deflate, brotli (br) и zstd
  decompress: true,

  // Отключение повторного использования соединения для этого запроса
  keepalive: false,

  // Уровень отладочного логирования
  verbose: true, // или "curl" для более детального вывода
});

Поддержка протоколов

Помимо HTTP(S) fetch в Bun поддерживает несколько дополнительных протоколов:

URL S3 - s3://

Bun поддерживает получение из S3-корзин непосредственно.

ts
// Использование переменных окружения для учётных данных
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",
  },
});

Примечание: Только методы PUT и POST поддерживают тела запросов при использовании S3. Для загрузок Bun автоматически использует многокомпонентную загрузку для потоковых тел.

Вы можете узнать больше о поддержке S3 в Bun в документации S3.

URL файлов - file://

Вы можете получать локальные файлы используя протокол file::

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

В Windows пути автоматически нормализуются:

ts
// Оба работают в Windows
const response = await fetch("file:///C:/path/to/file.txt");
const response2 = await fetch("file:///c:/path\\to/file.txt");

URL данных - data:

Bun поддерживает схему URL data::

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

URL Blob - blob:

Вы можете получать blob используя URL созданные через URL.createObjectURL():

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

Обработка ошибок

Реализация fetch в Bun включает несколько специфичных случаев ошибок:

  • Использование тела запроса с методами GET/HEAD вызовет ошибку (что ожидается для API fetch)
  • Попытка использования обеих опций proxy и unix вместе вызовет ошибку
  • Ошибки проверки TLS-сертификата когда rejectUnauthorized true (или undefined)
  • Операции S3 могут вызывать специфичные ошибки связанные с аутентификацией или разрешениями

Обработка Content-Type

Bun автоматически устанавливает заголовок Content-Type для тел запросов когда не предоставлено явно:

  • Для объектов Blob использует type blob
  • Для FormData устанавливает соответствующую границу multipart

Отладка

Для помощи в отладке вы можете передать verbose: true в fetch:

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

Это выведет заголовки запроса и ответа в ваш терминал:

sh
[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 не является частью веб-стандарта fetch API и специфично для Bun.

Производительность

Перед отправкой HTTP-запроса должна быть выполнена DNS-lookup. Это может занять значительное время особенно если DNS-сервер медленный или сетевое соединение плохое.

После DNS-lookup TCP-сокет должен быть подключён и может потребоваться выполнение TLS-рукопожатия. Это также может занять значительное время.

После завершения запроса потребление тела ответа также может занять значительное время и память.

На каждом этапе Bun предоставляет API для помощи в оптимизации производительности вашего приложения.

Предварительное получение DNS

Для предварительного получения DNS-записи вы можете использовать API dns.prefetch. Этот API полезен когда вы знаете что вам скоро нужно будет подключиться к хосту и хотите избежать начального DNS-запроса.

ts
import { dns } from "bun";

dns.prefetch("bun.com");

Кэширование DNS

По умолчанию Bun кэширует и дублирует DNS-запросы в памяти до 30 секунд. Вы можете увидеть статистику кэша вызвав dns.getCacheStats():

Чтобы узнать больше о кэшировании DNS в Bun см. документацию Кэширование DNS.

Предварительное подключение к хосту

Для предварительного подключения к хосту вы можете использовать API fetch.preconnect. Этот API полезен когда вы знаете что вам скоро нужно будет подключиться к хосту и хотите начать начальный DNS-lookup подключение TCP-сокета и TLS-рукопожатие заранее.

ts
import { fetch } from "bun";

fetch.preconnect("https://bun.com");

Примечание: вызов fetch сразу после fetch.preconnect не сделает ваш запрос быстрее. Предварительное подключение помогает только если вы знаете что вам скоро нужно будет подключиться к хосту но вы ещё не готовы сделать запрос.

Предварительное подключение при запуске

Для предварительного подключения к хосту при запуске вы можете передать --fetch-preconnect:

sh
bun --fetch-preconnect https://bun.com ./my-script.ts

Это похоже на <link rel="preconnect"> в HTML.

Эта функция ещё не реализована в Windows. Если вы заинтересованы в использовании этой функции в Windows пожалуйста создайте issue и мы можем реализовать поддержку для неё в Windows.

Объединение соединений и HTTP keep-alive

Bun автоматически повторно использует соединения с одним хостом. Это известно как объединение соединений. Это может значительно уменьшить время необходимое для установления соединения. Вам не нужно ничего делать для включения этого; это автоматически.

Лимит одновременных соединений

По умолчанию Bun ограничивает максимальное количество одновременных запросов fetch до 256. Мы делаем это по нескольким причинам:

  • Это улучшает общую стабильность системы. Операционные системы имеют верхний лимит на количество одновременных открытых TCP-сокетов обычно в низких тысячах. Приближение к этому лимиту заставляет весь ваш компьютер вести себя странно. Приложения зависают и падают.
  • Это поощряет повторное использование соединений HTTP Keep-Alive. Для коротких HTTP-запросов самым медленным шагом часто является начальная настройка соединения. Повторное использование соединений может сэкономить много времени.

Когда лимит превышен запросы ставятся в очередь и отправляются как только следующий запрос завершается.

Вы можете увеличить максимальное количество одновременных соединений через переменную окружения BUN_CONFIG_MAX_HTTP_REQUESTS:

sh
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 для записи тела ответа в файл на диске:

ts
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 расширениями стандартного API fetch.

Bun от www.bunjs.com.cn