Bun implementa lo standard WHATWG fetch, con alcune estensioni per soddisfare le esigenze del JavaScript lato server.
Bun implementa anche node:http, ma fetch è generalmente consigliato invece.
Inviare una richiesta HTTP
Per inviare una richiesta HTTP, usa fetch
const response = await fetch("http://example.com");
console.log(response.status); // => 200
const text = await response.text(); // oppure response.json(), response.formData(), ecc.fetch funziona anche con URL HTTPS.
const response = await fetch("https://example.com");Puoi anche passare a fetch un oggetto Request.
const request = new Request("http://example.com", {
method: "POST",
body: "Hello, world!",
});
const response = await fetch(request);Inviare una richiesta POST
Per inviare una richiesta POST, passa un oggetto con la proprietà method impostata a "POST".
const response = await fetch("http://example.com", {
method: "POST",
body: "Hello, world!",
});body può essere una stringa, un oggetto FormData, un ArrayBuffer, un Blob e altro. Consulta la documentazione MDN per maggiori informazioni.
Proxy delle richieste
Per fare proxy di una richiesta, passa un oggetto con la proprietà proxy impostata a una stringa URL:
const response = await fetch("http://example.com", {
proxy: "http://proxy.com",
});Puoi anche usare un formato oggetto per inviare header personalizzati al server proxy:
const response = await fetch("http://example.com", {
proxy: {
url: "http://proxy.com",
headers: {
"Proxy-Authorization": "Bearer my-token",
"X-Custom-Proxy-Header": "value",
},
},
});Gli headers vengono inviati direttamente al proxy nelle richieste CONNECT (per target HTTPS) o nella richiesta proxy (per target HTTP). Se fornisci un header Proxy-Authorization, questo sovrascrive qualsiasi credenziale nell'URL del proxy.
Header personalizzati
Per impostare header personalizzati, passa un oggetto con la proprietà headers impostata a un oggetto.
const response = await fetch("http://example.com", {
headers: {
"X-Custom-Header": "value",
},
});Puoi anche impostare header usando l'oggetto Headers.
const headers = new Headers();
headers.append("X-Custom-Header", "value");
const response = await fetch("http://example.com", {
headers,
});Corpi delle risposte
Per leggere il corpo della risposta, usa uno dei seguenti metodi:
response.text(): Promise<string>: Restituisce una promise che si risolve con il corpo della risposta come stringa.response.json(): Promise<any>: Restituisce una promise che si risolve con il corpo della risposta come oggetto JSON.response.formData(): Promise<FormData>: Restituisce una promise che si risolve con il corpo della risposta come oggettoFormData.response.bytes(): Promise<Uint8Array>: Restituisce una promise che si risolve con il corpo della risposta comeUint8Array.response.arrayBuffer(): Promise<ArrayBuffer>: Restituisce una promise che si risolve con il corpo della risposta comeArrayBuffer.response.blob(): Promise<Blob>: Restituisce una promise che si risolve con il corpo della risposta comeBlob.
Streaming dei corpi delle risposte
Puoi usare iteratori asincroni per fare streaming del corpo della risposta.
const response = await fetch("http://example.com");
for await (const chunk of response.body) {
console.log(chunk);
}Puoi anche accedere più direttamente all'oggetto ReadableStream.
const response = await fetch("http://example.com");
const stream = response.body;
const reader = stream.getReader();
const { value, done } = await reader.read();Streaming dei corpi delle richieste
Puoi anche trasmettere in streaming i dati nei corpi delle richieste usando un 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,
});Quando usi stream con HTTP(S):
- I dati vengono trasmessi direttamente in rete senza memorizzare l'intero corpo in memoria
- Se la connessione viene persa, lo stream sarà cancellato
- L'header
Content-Lengthnon viene impostato automaticamente a meno che lo stream abbia una dimensione nota
Quando usi stream con S3:
- Per richieste PUT/POST, Bun usa automaticamente upload multipart
- Lo stream viene consumato in chunk e caricato in parallelo
- Il progresso può essere monitorato attraverso le opzioni S3
Recuperare un URL con timeout
Per recuperare un URL con timeout, usa AbortSignal.timeout:
const response = await fetch("http://example.com", {
signal: AbortSignal.timeout(1000),
});Annullare una richiesta
Per annullare una richiesta, usa un AbortController:
const controller = new AbortController();
const response = await fetch("http://example.com", {
signal: controller.signal,
});
controller.abort();Socket di dominio Unix
Per recuperare un URL usando un socket di dominio Unix, usa l'opzione 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
Per usare un certificato client, usa l'opzione 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")],
},
});Validazione TLS personalizzata
Per personalizzare la validazione TLS, usa l'opzione checkServerIdentity in tls
await fetch("https://example.com", {
tls: {
checkServerIdentity: (hostname, peerCertificate) => {
// Restituisci un Error se il certificato non è valido
},
},
});Questo è simile a come funziona nel modulo net di Node.
Disabilitare la validazione TLS
Per disabilitare la validazione TLS, imposta rejectUnauthorized a false:
await fetch("https://example.com", {
tls: {
rejectUnauthorized: false,
},
});Questo è particolarmente utile per evitare errori SSL quando si usano certificati autofirmati, ma questo disabilita la validazione TLS e dovrebbe essere usato con cautela.
Opzioni della richiesta
Oltre alle opzioni standard di fetch, Bun fornisce diverse estensioni:
const response = await fetch("http://example.com", {
// Controlla la decompressione automatica della risposta (default: true)
// Supporta gzip, deflate, brotli (br) e zstd
discomprime: true,
// Disabilita il riutilizzo della connessione per questa richiesta
keepalive: false,
// Livello di log di debug
verbose: true, // oppure "curl" per output più dettagliato
});Supporto del protocollo
Oltre a HTTP(S), il fetch di Bun supporta diversi protocolli aggiuntivi:
URL S3 - s3://
Bun supporta il recupero da bucket S3 direttamente.
// Usando variabili d'ambiente per le credenziali
const response = await fetch("s3://my-bucket/path/to/object");
// O passano credenziali esplicitamente
const response = await fetch("s3://my-bucket/path/to/object", {
s3: {
accessKeyId: "YOUR_ACCESS_KEY",
secretAccessKey: "YOUR_SECRET_KEY",
region: "us-east-1",
},
});Nota: Solo i metodi PUT e POST supportano i corpi delle richieste quando si usa S3. Per i caricamenti, Bun usa automaticamente upload multipart per gli stream body.
Puoi leggere di più sul supporto S3 di Bun nella documentazione S3.
URL file - file://
Puoi recuperare file locali usando il protocollo file::
const response = await fetch("file:///path/to/file.txt");
const text = await response.text();Su Windows, i percorsi vengono normalizzati automaticamente:
// Entrambi funzionano su Windows
const response = await fetch("file:///C:/path/to/file.txt");
const response2 = await fetch("file:///c:/path\\to/file.txt");URL data - data:
Bun supporta lo schema URL data::
const response = await fetch("data:text/plain;base64,SGVsbG8sIFdvcmxkIQ==");
const text = await response.text(); // "Hello, World!"URL blob - blob:
Puoi recuperare blob usando URL creati da URL.createObjectURL():
const blob = new Blob(["Hello, World!"], { type: "text/plain" });
const url = URL.createObjectURL(blob);
const response = await fetch(url);Gestione degli errori
L'implementazione di fetch di Bun include diversi casi di errore specifici:
- L'uso di un corpo della richiesta con metodi GET/HEAD genererà un errore (che è previsto per l'API fetch)
- Il tentativo di usare sia le opzioni
proxycheunixinsieme genererà un errore - Fallimenti della validazione del certificato TLS quando
rejectUnauthorizedè true (o non definito) - Le operazioni S3 possono generare errori specifici relativi all'autenticazione o ai permessi
Gestione del Content-Type
Bun imposta automaticamente l'header Content-Type per i corpi delle richieste quando non esplicitamente fornito:
- Per oggetti
Blob, usa iltypedel blob - Per
FormData, imposta il boundary multipart appropriato
Debug
Per aiutare con il debug, puoi passare verbose: true a fetch:
const response = await fetch("http://example.com", {
verbose: true,
});Questo stamperà gli header della richiesta e della risposta sul tuo terminale:
[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: 648Nota: verbose: boolean non fa parte dello standard web dell'API fetch ed è specifico di Bun.
Prestazioni
Prima che una richiesta HTTP possa essere inviata, deve essere eseguita la ricerca DNS. Questo può richiedere un tempo significativo, specialmente se il server DNS è lento o la connessione di rete è scarsa.
Dopo la ricerca DNS, il socket TCP deve essere connesso e l'handshake TLS potrebbe dover essere eseguito. Anche questo può richiedere un tempo significativo.
Dopo che la richiesta è completata, consumare il corpo della risposta può richiedere anche un tempo significativo e memoria.
In ogni fase, Bun fornisce API per aiutarti a ottimizzare le prestazioni della tua applicazione.
Prefetch DNS
Per prefetchare una voce DNS, puoi usare l'API dns.prefetch. Questa API è utile quando sai che dovrai connetterti a un host presto e vuoi evitare la ricerca DNS iniziale.
import { dns } from "bun";
dns.prefetch("bun.com");Cache DNS
Di default, Bun memorizza nella cache e deduplica le query DNS in memoria per fino a 30 secondi. Puoi vedere le statistiche della cache chiamando dns.getCacheStats():
Per saperne di più sulla cache DNS in Bun, consulta la documentazione sulla cache DNS.
Preconnettersi a un host
Per preconnetterti a un host, puoi usare l'API fetch.preconnect. Questa API è utile quando sai che dovrai connetterti a un host presto e vuoi iniziare la ricerca DNS iniziale, la connessione del socket TCP e l'handshake TLS presto.
import { fetch } from "bun";
fetch.preconnect("https://bun.com");Nota: chiamare fetch immediatamente dopo fetch.preconnect non renderà la tua richiesta più veloce. La preconnessione aiuta solo se sai che dovrai connetterti a un host presto, ma non sei ancora pronto per fare la richiesta.
Preconnessione all'avvio
Per preconnetterti a un host all'avvio, puoi passare --fetch-preconnect:
bun --fetch-preconnect https://bun.com ./my-script.tsQuesto è un po' come <link rel="preconnect"> in HTML.
Questa funzionalità non è ancora implementata su Windows. Se sei interessato a usare questa funzionalità su Windows, per favore apri un issue e possiamo implementare il supporto per essa su Windows.
Connection pooling e HTTP keep-alive
Bun riutilizza automaticamente le connessioni allo stesso host. Questo è noto come connection pooling. Questo può ridurre significativamente il tempo necessario per stabilire una connessione. Non devi fare nulla per abilitare questo; è automatico.
Limite di connessioni simultanee
Di default, Bun limita il numero massimo di richieste fetch simultanee a 256. Lo facciamo per diversi motivi:
- Migliora la stabilità generale del sistema. I sistemi operativi hanno un limite superiore sul numero di socket TCP aperti simultaneamente, di solito nelle migliaia. Avvicinarsi a questo limite fa comportare in modo strano l'intero computer. Le applicazioni si bloccano e crashano.
- Incoraggia il riutilizzo delle connessioni HTTP Keep-Alive. Per richieste HTTP di breve durata, il passaggio più lento è spesso la configurazione iniziale della connessione. Riutilizzare le connessioni può far risparmiare molto tempo.
Quando il limite viene superato, le richieste vengono accodate e inviate non appena termina la prossima richiesta.
Puoi aumentare il numero massimo di connessioni simultanee tramite la variabile d'ambiente BUN_CONFIG_MAX_HTTP_REQUESTS:
BUN_CONFIG_MAX_HTTP_REQUESTS=512 bun ./my-script.tsIl valore massimo per questo limite è attualmente impostato a 65.336. Il numero di porta massimo è 65.535, quindi è abbastanza difficile per qualsiasi computer superare questo limite.
Buffering della risposta
Bun fa di tutto per ottimizzare le prestazioni della lettura del corpo della risposta. Il modo più veloce per leggere il corpo della risposta è usare uno di questi metodi:
response.text(): Promise<string>response.json(): Promise<any>response.formData(): Promise<FormData>response.bytes(): Promise<Uint8Array>response.arrayBuffer(): Promise<ArrayBuffer>response.blob(): Promise<Blob>
Puoi anche usare Bun.write per scrivere il corpo della risposta su un file su disco:
import { write } from "bun";
await write("output.txt", response);Dettagli dell'implementazione
- Il connection pooling è abilitato di default ma può essere disabilitato per richiesta con
keepalive: false. L'header"Connection: close"può anche essere usato per disabilitare keep-alive. - I caricamenti di file grandi sono ottimizzati usando la syscall
sendfiledel sistema operativo sotto condizioni specifiche:- Il file deve essere più grande di 32KB
- La richiesta non deve usare un proxy
- Su macOS, solo file regolari (non pipe, socket o dispositivi) possono usare
sendfile - Quando queste condizioni non sono soddisfatte, o quando si usano caricamenti S3/streaming, Bun legge il file in memoria
- Questa ottimizzazione è particolarmente efficace per richieste HTTP (non HTTPS) dove il file può essere inviato direttamente dal kernel allo stack di rete
- Le operazioni S3 gestiscono automaticamente la firma delle richieste e l'unione degli header di autenticazione
Nota: Molte di queste funzionalità sono estensioni specifiche di Bun all'API fetch standard.