DNS API Reference
The DNS subsystem in HTTPX provides high-level client resolution methods, structured IP address types, thread-safe resolution caching, and low-level resolver controls.
Architecture Overview
httpx.Client
│
┌───────────┴───────────┐
│ │
Normal API Advanced API
│ │
client.resolve() httpx.resolve.Resolver
│ httpx.dns.Cache
└───────────┬───────────┘
│
Canonical DNS
Subsystem1. High-Level Client API (Recommended)
Normal applications resolve hostnames through httpx.Client. The client manages allocator ownership, background I/O handles, and caching automatically.
client.resolve(host, options)
Resolves a hostname or IP address to candidate addresses.
pub fn resolve(
self: *Client,
host: []const u8,
opts: anytype,
) Error!ResolvedAddresses- Parameters:
host: Hostname string (e.g.,"httpbun.com") or numeric IP literal (e.g.,"127.0.0.1","::1").opts: Struct ofResolveOptions(.port,.family,.useCache,.timeoutMs) or.{}for defaults.
- Returns:
ResolvedAddressesowning the returned slice. - Errors:
error.DnsFailed,error.OutOfMemory.
Default Options Semantics
Passing .{} uses documented HTTPX defaults:
- Dual-stack resolution (
.family = .any) in system preference order (RFC 3484). - In-memory cache enabled (
.useCache = true). - Default timeout inherited from client config.
client.resolveUrl(url_str, options)
Extracts the hostname and effective port from a URL string (e.g. 80 for http://, 443 for https://) and resolves it:
var addrs = try client.resolveUrl("https://httpbun.com/get", .{});
defer addrs.deinit();client.resolve(host, options) without a long-lived client
If you need one-off resolution, create a short-lived client:
var tmp = httpx.Client.init(allocator, io, .{});
defer tmp.deinit();
var addrs = try tmp.resolve("httpbun.com", .{});
defer addrs.deinit();2. DNS Options & Result Types
ResolveOptions
Configuration passed to client.resolve:
pub const ResolveOptions = struct {
family: AddressFamilyPreference = .any,
useCache: bool = true,
timeoutMs: ?u64 = null,
port: u16 = 443,
};| Field | Type | Default | Description |
|---|---|---|---|
family | AddressFamilyPreference | .any | Address family filter (.any, .ipv4, .ipv6) |
useCache | bool | true | When true, queries and updates the client's cache |
timeoutMs | ?u64 | null | Optional lookup timeout override |
port | u16 | 443 | Port stamped onto every returned address |
AddressFamilyPreference
pub const AddressFamilyPreference = enum {
any, // Dual-stack: both IPv4 and IPv6
ipv4, // IPv4 addresses only
ipv6, // IPv6 addresses only
};ResolvedAddresses
A collection of resolved addresses with a clear lifecycle API:
pub const ResolvedAddresses = struct {
allocator: Allocator,
items: []Address,
pub fn deinit(self: *ResolvedAddresses) void;
pub fn slice(self: *const ResolvedAddresses) []const Address;
pub fn first(self: *const ResolvedAddresses) ?Address;
pub fn len(self: *const ResolvedAddresses) usize;
pub fn format(self: ResolvedAddresses, writer: anytype) !void;
};- Memory Ownership: Caller owns
ResolvedAddressesand must calldefer addresses.deinit();. - Iteration: Iterate over
addresses.itemsoraddresses.slice(). - Printing: Implements native Zig formatting:zig
std.debug.print("Addresses: {f}\n", .{addresses});
3. Client DNS Configuration
Client-wide DNS parameters are configured in Client.Config.dnsCache during Client.init:
pub const DnsCacheOptions = struct {
enable: bool = true,
ttlMs: i64 = 60_000,
negativeTtlMs: i64 = 5_000,
maxEntries: u32 = 1024,
};| Parameter | Default | Description |
|---|---|---|
enable | true | Activates internal resolution caching |
ttlMs | 60_000 (60s) | Lifetime for successful lookups |
negativeTtlMs | 5_000 (5s) | Lifetime for failed lookups |
maxEntries | 1024 | Maximum bounded entries before eviction |
4. Address Representation (Address)
Defined in src/net/address.zig and re-exported as httpx.Address:
pub const Address = struct {
family: Family,
bytes: [16]u8,
port: u16,
zone: u32 = 0,
pub fn format(self: Address, writer: anytype) !void;
pub fn formatBuf(self: *const Address, buf: []u8) []const u8;
pub fn formatWithPort(self: *const Address, buf: []u8) []const u8;
pub fn toString(self: Address, allocator: Allocator) ![]u8;
pub fn isV4Mapped(self: *const Address) bool;
};Native Formatting
Formats addresses per RFC 5952:
// Native {f} format specifier:
std.debug.print("Target: {f}:{d}\n", .{ addr, addr.port });
// Zero-allocation buffer format:
var buf: [64]u8 = undefined;
const s = addr.formatBuf(&buf);
// Address with port:
var port_buf: [64]u8 = undefined;
const hp = addr.formatWithPort(&port_buf); // e.g. "127.0.0.1:443" or "[::1]:443"5. Advanced Resolver API
For applications requiring direct control over low-level resolvers without instantiating an HTTP client:
httpx.resolve.Resolver
Resolver owning its allocator and IO backend (std.Io first, OS fallback):
var resolver = httpx.resolve.Resolver.init(allocator, io);
const addrs = try resolver.lookup("httpbun.com", 443);
defer allocator.free(addrs);httpx.dns Wire Protocol
Low-level RFC 1035 message encoder and decoder for raw DNS packets:
buildQuery(allocator, id, name, qtype)parseResponse(allocator, msg)resolveA(allocator, io, name)resolveAAAA(allocator, io, name)
6. Concurrency & Stampede Prevention
- Thread-Safety: Lookups through
httpx.Clientare fully thread-safe. - Single-Flight Coalescing: When multiple threads or concurrent requests resolve the same domain concurrently, only a single network query is dispatched; subsequent callers await and share the result.
- Non-Blocking Locks: Network I/O is never executed while holding cache mutexes.
7. Proxy & SOCKS5 DNS Semantics
| Connection Type | Resolution Location | Behavior |
|---|---|---|
| Direct | Local | Resolved via client DNS subsystem |
HTTP Proxy (http://) | Local / Proxy | Proxy connects; TLS uses HTTP CONNECT |
SOCKS5 (socks5://) | Local | Host resolved locally prior to connection |
SOCKS5H (socks5h://) | Remote (Proxy) | Unresolved hostname sent to SOCKS5 proxy |
8. Platform Resolver Differences
- Windows: Direct native
ws2_32.GetAddrInfoWAPI; requires no libc dependency. - Linux / macOS: Uses libc
getaddrinfowhen linked with libc; std.Io HostName lookup when freestanding. - RFC 3484: Both Windows and POSIX OS resolvers perform address sorting according to default address selection rules.
