BunTransport
BunTransport is a high-performance HTTP transport for the
HyperTransport ecosystem, designed for the Bun runtime.
It wraps native fetch and adds:
- concurrency limiting with FIFO queue
- request timeouts via
AbortSignal - automatic cookie handling with TTL and LRU eviction
- response caching for GET/HEAD requests
- stealth / fingerprint mimicry (JA3/JA4)
- header normalization
keepalivesupport- TLS configuration (ciphers,
rejectUnauthorized)
Interface
export interface HyperTransport {
execute(req: TransportRequest): Promise<TransportResponse>;
close?(): Promise<void>;
destroy?(): Promise<void>;
}
BunTransport implements this interface and can be used as a runtime-specific HTTP transport in client libraries.
Features
fetch-based implementation withsignalsupport- automatic merging of cookies from responses and subsequent requests
- cookie cache per domain with configurable TTL
- response cache for GET/HEAD requests (optional, via
hcacher) - request concurrency limiting with FIFO queue
- request timeout support
- stealth mode: browser-like TLS fingerprint emulation (Chrome, Firefox, Safari, Edge)
baseUrlsupport for relative URLs- compatibility with
TransportRequest/TransportResponse
Benchmark
Benchmark results on Bun 1.3.14 (20K requests, 200 concurrent, 60s duration):
Environment: linux 7.1.3-zen2-1-zen | Intel Core i5-8600K @ 3.60GHz
| Rank | Client | RPS | Avg | p50 | p90 | p99 | Errors |
|---|---|---|---|---|---|---|---|
| 1 | bun-fetch | 25.92K | 7.65 ms | 7.69 ms | 10.41 ms | 14.00 ms | 0 |
| 2 | undici | 21.47K | 9.28 ms | 9.54 ms | 12.80 ms | 14.77 ms | 0 |
| 3 | @hyperttp/core | 14.70K | 13.53 ms | 12.03 ms | 17.36 ms | 19.72 ms | 0 |
| 4 | axios | 6.24K | 31.85 ms | 31.83 ms | 33.82 ms | 36.10 ms | 0 |
BunTransport (via @hyperttp/core) delivers 2.4x higher throughput than axios with 2.3x lower latency (p50).
Installation
BunTransport is designed for Bun, so the project must run in the Bun runtime.
bun add @hyperttp/transport-bun
Peer dependencies: @hyperttp/types, hcacher.
Usage
import { BunTransport } from "@hyperttp/transport-bun";
const transport = new BunTransport({
baseUrl: "https://api.example.com",
network: {
timeout: 10_000,
maxConcurrent: 32,
keepAliveTimeout: 30_000,
rejectUnauthorized: true,
},
});
const response = await transport.execute({
url: "/users",
method: "GET",
headers: {},
body: null,
});
console.log(response.status);
console.log(await response.text());
Configuration
BunTransport accepts a BunTransportConfig object:
interface BunTransportConfig extends HttpClientOptions {
baseUrl?: string;
stealth?: StealthOptions;
}
Network options
network?: {
maxConcurrent?: number;
timeout?: number;
keepAliveTimeout?: number;
rejectUnauthorized?: boolean;
cookieCache?: {
enabled?: boolean;
maxSize?: number;
ttl?: number;
};
cache?: {
enabled?: boolean;
maxSize?: number;
ttl?: number;
};
}
| Option | Description |
|---|---|
maxConcurrent |
Maximum number of concurrent active requests (0 = unlimited) |
timeout |
Global request timeout in milliseconds (0 = no timeout) |
keepAliveTimeout |
Enables keepalive mode when provided |
rejectUnauthorized |
Passed to Bun fetch via tls configuration |
cookieCache |
Cookie cache settings: enabled (default true), maxSize (default 256), ttl (default 300s) |
cache |
Response cache settings for GET/HEAD: enabled, maxSize (default 256), ttl (default 30s) |
Cookies
The transport supports persistent cookie storage with TTL and LRU eviction, backed by hcacher.
How it works
- When the server returns
Set-Cookie, cookies are stored per domain with a TTL. - On subsequent requests to the same domain, cookies are automatically attached to the
Cookieheader. - If the user provides their own
Cookieheader, it is merged with stored cookies. - Expired entries are evicted automatically (LRU).
Example
await transport.execute({
url: "/login",
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ login: "demo", password: "secret" }),
});
// Cookies from /login are automatically sent
const res = await transport.execute({
url: "/profile",
method: "GET",
headers: {},
body: null,
});
Response caching
When network.cache is configured, GET and HEAD requests are cached automatically with TTL and LRU eviction.
const transport = new BunTransport({
network: {
cache: {
enabled: true,
maxSize: 512,
ttl: 60_000, // 1 minute
},
},
});
Cached responses are returned instantly on repeated requests to the same URL within the TTL window.
Concurrency limiting
If maxConcurrent > 0, incoming requests are queued until a slot becomes available.
This is useful for:
- preventing server overload
- limiting runtime resource usage
- avoiding traffic spikes
Timeout & cancellation
The transport uses AbortSignal:
- if
req.signalis provided, it is respected - if
timeoutis set,AbortSignal.timeout(...)is created - if both exist, they are combined using
AbortSignal.any(...)
If a request is aborted, the signal reason (or a default AbortError) is thrown.
Stealth / fingerprint mimicry
BunTransport supports browser-like TLS fingerprint emulation to bypass fingerprint-based protections (JA3/JA4).
Supported profiles
| Profile | TLS ciphers | sec-ch-ua | User-Agent |
|---|---|---|---|
| chrome | Chrome suite | Chrome 126 | Chrome 126 |
| firefox | Firefox suite | Firefox-style | Firefox 126 |
| safari | Safari suite | — | Safari 17.0 |
| edge | Chrome suite | Chrome 126 | Edge 126 |
Example
const transport = new BunTransport({
stealth: {
fingerprint: "chrome",
},
});
Stealth presets are applied per-request. Manual headers always take priority over preset values.
Response API
execute() returns a plain TransportResponse object:
interface TransportResponse {
status: number;
url: string;
headers: Record<string, string | string[]>;
body: TransportResponsePayload | null;
}
Body behavior
If body exists, a dump() method may be attached to it, allowing safe stream consumption and resource cleanup.
Internal utilities
fastGetHostname(url: string): string
Fast hostname extraction from a URL with single-entry LRU cache. Handles //, scheme://, user@host, and port stripping.
resolveUrl(baseUrl: string, url: string): string
Resolves a relative URL against a baseUrl. Absolute URLs are returned as-is.
normalizeHeaders(headers): Record<string, string>
Normalizes headers: lowercases all keys, joins arrays (; for cookie, , for others).
Shutdown
BunTransport supports resource cleanup:
await transport.close();
await transport.destroy();
Both methods clear:
- pending request queue
- cookie store
- cookie string cache
- response cache
- active request counter
Notes
- Designed specifically for the Bun runtime, not Node.js.
- Uses
redirect: "manual"for fetch requests. - Minimal interference with request data; only headers, cookies, and signals are normalized or extended.
TransportRequest.bodyis passed directly asBodyInit | null.
License
MIT