Adapters

HTTP Adapter

The HTTP adapter is Rezo’s default and most feature-complete adapter. At 2,590 lines, it wraps Node.js’s native http and https modules with enterprise-grade capabilities including connection pooling, staged timeouts, socket telemetry, TLS fingerprinting, and a 50-attempt safety guard against infinite redirect/retry loops.

import rezo from 'rezo/adapters/http';

Import and Setup

The adapter entry point exports a pre-configured instance and all core classes:

import rezo, {
  Rezo,
  RezoError,
  RezoHeaders,
  RezoFormData,
  RezoCookieJar,
  RezoStealth
} from 'rezo/adapters/http';

// Use the default instance
const { data } = await rezo.get('https://api.example.com/users');

// Create a custom instance with options
const client = new Rezo({
  baseURL: 'https://api.example.com',
  timeout: 10000,
  headers: { 'Accept': 'application/json' }
});

const { data: users } = await client.get('/users');

Features

The HTTP adapter provides full cookie jar support with automatic persistence. Cookies from Set-Cookie response headers are stored in the jar and sent on subsequent requests that match the cookie’s domain and path:

const jar = new RezoCookieJar();
const client = new Rezo({ jar });

// Login -- cookies are automatically stored
await client.post('https://example.com/login', {
  username: 'admin',
  password: 'secret'
});

// Subsequent requests include session cookies automatically
const { data } = await client.get('https://example.com/dashboard');

// Inspect stored cookies
const cookies = jar.getCookiesSync('https://example.com');
console.log(cookies.map(c => `${c.key}=${c.value}`));

// Serialize for persistence across sessions
const serialized = jar.toJSON();

Proxy Support

Full proxy support for HTTP, HTTPS, and SOCKS4/5 protocols:

// HTTP proxy
const { data } = await rezo.get('https://example.com', {
  proxy: {
    protocol: 'http',
    host: '192.168.1.100',
    port: 8080
  }
});

// SOCKS5 proxy with authentication
const { data } = await rezo.get('https://example.com', {
  proxy: {
    protocol: 'socks5',
    host: '192.168.1.100',
    port: 1080,
    auth: {
      username: 'user',
      password: 'pass'
    }
  }
});

// Proxy as URL string
const { data } = await rezo.get('https://example.com', {
  proxy: 'http://user:pass@proxy.example.com:8080'
});

Streaming

Stream responses for processing large payloads without buffering the entire body in memory:

const response = await rezo.stream('https://example.com/large-file');

// response.data is a Node.js Readable stream
for await (const chunk of response.data) {
  process.stdout.write(chunk);
}

File Downloads

Download files to disk with progress tracking:

const response = await rezo.download(
  'https://example.com/archive.zip',
  './downloads/archive.zip'
);

console.log(response.savePath);     // ./downloads/archive.zip
console.log(response.contentLength); // 52428800

With progress events via hooks:

const client = new Rezo({
  hooks: {
    onDownloadProgress: [({ loaded, total, percent }) => {
      console.log(`Downloaded: ${percent.toFixed(1)}%`);
    }]
  }
});

await client.download('https://example.com/archive.zip', './archive.zip');

File Uploads

Upload files with progress tracking:

const form = new RezoFormData();
form.append('file', fs.createReadStream('./report.pdf'), {
  filename: 'report.pdf',
  contentType: 'application/pdf'
});

const response = await rezo.upload('https://example.com/upload', form);

Compression

Automatic decompression for gzip, deflate, brotli, and zstd:

const { data } = await rezo.get('https://example.com/api/data', {
  headers: {
    'Accept-Encoding': 'gzip, deflate, br, zstd'
  }
});

// Response is automatically decompressed regardless of encoding

The adapter sends Accept-Encoding headers by default and transparently decompresses the response body. Compression ratios are tracked in the response’s transfer metadata.

TLS Configuration

Full control over TLS settings, including certificate pinning and Chrome 131+ fingerprint emulation via the stealth module:

// Custom TLS options
const { data } = await rezo.get('https://internal-api.example.com', {
  rejectUnauthorized: false, // skip certificate verification (development only)
  ca: fs.readFileSync('./ca-cert.pem'),
  cert: fs.readFileSync('./client-cert.pem'),
  key: fs.readFileSync('./client-key.pem')
});

// TLS fingerprinting with stealth
const stealth = new RezoStealth({ profile: 'chrome-131' });
const client = new Rezo({ stealth });

const { data } = await client.get('https://bot-protected-site.com');

Agent Pool with Keep-Alive

The adapter uses a global agent pool that maintains persistent connections with keep-alive. Agents are reused across requests to the same origin, reducing connection overhead:

// Keep-alive is enabled by default
// The agent pool automatically manages HTTP and HTTPS agents
const client = new Rezo({
  timeout: 30000
});

// Multiple requests to the same origin reuse the underlying TCP connection
await client.get('https://api.example.com/users');
await client.get('https://api.example.com/posts');
await client.get('https://api.example.com/comments');

Socket Telemetry

Track low-level socket events for debugging and monitoring:

import { getSocketTelemetry } from 'rezo/adapters/http';

const telemetry = getSocketTelemetry();
// Provides socket-level timing data:
// - DNS lookup duration
// - TCP connection time
// - TLS handshake time
// - Time to first byte

Staged Timeouts

Instead of a single timeout, the HTTP adapter supports staged timeouts that apply independently to different phases of the request:

const { data } = await rezo.get('https://slow-api.example.com/data', {
  timeout: {
    lookup: 5000,     // DNS lookup timeout
    connect: 10000,   // TCP connection timeout
    secureConnect: 10000, // TLS handshake timeout
    send: 30000,      // Request body send timeout
    response: 60000   // Time to first response byte
  }
});

Redirect Handling

Automatic redirect following for status codes 301, 302, 303, 307, and 308, with cycle detection:

const { data } = await rezo.get('https://example.com/old-page', {
  maxRedirects: 10,          // default: 5
  followRedirects: true      // default: true
});

// Access the final URL after redirects
console.log(response.request.url); // https://example.com/new-page

The adapter correctly handles method changes on 301/302/303 redirects (POST becomes GET) and preserves the method on 307/308 redirects.

Retry with Backoff

Automatic retries for transient errors with exponential backoff:

const { data } = await rezo.get('https://flaky-api.example.com/data', {
  retry: {
    limit: 3,
    delay: 1000,            // base delay in ms
    maxDelay: 30000,        // cap on delay
    backoff: 'exponential', // exponential backoff
    statusCodes: [429, 500, 502, 503, 504]
  }
});

Rate Limit Wait

When the server returns a 429 Too Many Requests with a Retry-After header, the adapter can automatically wait and retry:

const client = new Rezo({
  rateLimitWait: true // honor Retry-After headers
});

// If the server returns 429 with Retry-After: 5,
// the adapter waits 5 seconds and retries automatically
const { data } = await client.get('https://rate-limited-api.com/data');

Response Caching

Cache responses in memory to avoid redundant network requests:

const client = new Rezo({
  cache: {
    maxAge: 60000,    // cache for 1 minute
    maxEntries: 500   // maximum cached responses
  }
});

// First request hits the network
const { data: first } = await client.get('https://api.example.com/config');

// Second request returns cached response immediately
const { data: second } = await client.get('https://api.example.com/config');

DNS Caching

The adapter integrates with Rezo’s DNS cache to avoid repeated DNS lookups:

const client = new Rezo({
  dnsCache: true // enable DNS caching
});

// DNS results are cached and reused across requests
await client.get('https://api.example.com/endpoint1');
await client.get('https://api.example.com/endpoint2'); // reuses DNS result

50-Attempt Safety Guard

The adapter enforces an absolute maximum of 50 total attempts (combining redirects and retries) per request to prevent infinite loops, regardless of your maxRedirects and retry.limit configuration:

// Even if maxRedirects is 30 and retry.limit is 30,
// the adapter will throw after 50 total attempts
const { data } = await rezo.get('https://example.com', {
  maxRedirects: 30,
  retry: { limit: 30 }
});
// Throws: "Absolute maximum attempts (50) exceeded.
//          This prevents infinite loops from retries and redirects."

Debug Mode

Enable detailed logging of every request phase:

const { data } = await rezo.get('https://api.example.com/users', {
  debug: true
});
// [Rezo Debug] GET https://api.example.com/users
// [Rezo Debug] Request ID: req_abc123
// [Rezo Debug] Request Headers: { ... }
// [Rezo Debug] Response: 200 OK (142.35ms)

// Or track just URLs and redirects
const { data } = await rezo.get('https://example.com/old', {
  trackUrl: true
});
// [Rezo Track] -> GET https://example.com/old
// [Rezo Track]   -> 301 -> https://example.com/new
// [Rezo Track] 200 OK

When to Use This Adapter

The HTTP adapter is the right choice when:

  • You are running on Node.js or Bun
  • You need cookie jar, proxy, or TLS configuration support
  • You want connection pooling and keep-alive for performance
  • You need upload or download progress tracking
  • You want streaming responses with backpressure handling
  • You need the fullest feature set available in Rezo

For HTTP/2 multiplexing, use the HTTP/2 adapter instead. For browser environments, use the Fetch adapter.