Skip to content
muhammad-fiazPublic

About

httpx.zig is a production-ready, high-performance HTTP client and server library for Zig, designed for building modern, robust, and scalable networked applications.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

96 stars

Watchers

0 watching

Forks

Repository files navigation

httpx.zig logo

Documentation Zig Version GitHub stars GitHub issues GitHub pull requests GitHub last commit License CI Supported Platforms CodeQL Latest Release Sponsor GitHub Sponsors Repo Visitors

A Fast, High-Performance HTTP Client and Server Library for Zig.

Documentation | API Reference | Quick Start | Contributing

httpx.zig is a modern, high-performance HTTP library for Zig, providing everything needed to build fast and reliable networked applications, including HTTP clients, servers, APIs, web services, reverse proxies, and full-featured websites.

Tip

If you build with httpx.zig, make sure to give it a star.

Note

Project maturity: This project is under active development. It provides a comprehensive HTTP client and server implementation with modern protocol, networking, security, and performance features, and contributions are welcome.

Custom HTTP/2, HTTP/3, TLS, Streaming, and Parsing implementation: Zig's standard library does not provide HTTP/2, HTTP/3, QUIC, ALPN negotiation, OpenAPI documentation UI, full HTML/XML DOM parsing, or built-in progress download engines. httpx.zig implements these subsystems natively in Zig, including:

  • TLS 1.3 server engine + TLS 1.2/1.3 client (RFC 5246 / RFC 8446) — a custom server-side TLS 1.3 engine (handshake, X25519 key exchange, ChaCha20-Poly1305 / AES-128-GCM / AES-256-GCM record encryption, ALPN negotiation per RFC 7301 with HTTP/1.1 fallback, X.509 parsing/verification, P-256 ECDSA certificates); the HTTPS client transport builds on std.crypto.tls (TLS 1.2/1.3) with httpx verification policy (SNI, SAN/hostname checks, system + custom trust stores)
  • HPACK header compression (RFC 7541) with Without Indexing / Never Indexed security for HTTP/2
  • HTTP/2 stream multiplexing, flow control (WINDOW_UPDATE), SETTINGS enforcement, GOAWAY/RST_STREAM, PRIORITY, CONTINUATION frames, PING, and HTTP/1.x connection pooling (RFC 7540; HTTP/2 connections are per-request for now)
  • QPACK header compression (RFC 9204) with static/dynamic tables and decoder/encoder stream instructions for HTTP/3
  • QUIC transport frame encoding/decoding (RFC 9000) with RESET_STREAM/STOP_SENDING cancellation, version negotiation, and transport parameters
  • HTTP/3 frame types, SETTINGS, GOAWAY, and CONNECTION_CLOSE handling
  • Streaming Downloader & File Manager with zero-config loaders.zig progress bars, dynamic speed & ETA estimation, range-based resumption, atomic updates, and cryptographic verification (SHA-256, SHA-384, SHA-512, MD5, SHA-1)
  • Unified DOM Engine & Web Resource Inspector for HTML5, XML, RSS/Atom/JSON feeds, robots.txt, sitemaps, and CSS selector queries with zero manual per-element memory freeing
  • Self-Hosted Interactive Documentation supporting Swagger UI, ReDoc, Scalar, and GraphiQL with embedded offline assets

Related Zig projects:

  • For Env.zig (.env parsing), check out env.zig.
  • For TUI support, check out tui.zig.
  • For ZON file format support, check out zon.zig.
  • For Spinners/loading/progress bar support, check out loaders.zig.
  • For Terminal colors & text styles support, check out tint.zig.
  • For MCP support, check out mcp.zig.
  • For API framework support, check out api.zig.
  • For Web framework support, check out zix.
  • For archive/compression support, check out archive.zig.
  • For compression file format support, check out zigx.
  • For CUDA support, check out cuda.zig.
  • For Simplified build.zig config support, check out buildx.zig.
  • For SQLite (zig-native implementation) support, check out sqlite.zig.
  • For File downloading support, check out downloader.zig.
  • For update checker/auto-updater support, check out updater.zig.
  • For Numerical computing support, check out num.zig.
  • For Logging support, check out logly.zig.
  • For Data validation and serialization support, check out zigantic.
  • For UUID support, check out uuid.zig.
  • For Key-Value database support, check out zkv.zig.
  • For Terminal color & text styles support, check out hint.zig.
  • For Brotli compression support, check out brotli.zig.
  • For Zstd compression support, check out zstd.zig.
  • For Tree-Sitter support, check out tree-sitter.zig

Features (click to expand)
Feature Description
Protocol Support Full client + server runtime across HTTP/1.0, HTTP/1.1, HTTP/2, and HTTP/3: HTTP/2 over TCP/TLS with ALPN h2, stream multiplexing, flow control, SETTINGS, and trailers; HTTP/3 over QUIC/UDP with native TLS 1.3, ALPN h3, QPACK encoder/decoder streams, dynamic table management, request/response streaming, and TLS 1.3 0-RTT early-data resumption.
Header Compression HPACK (RFC 7541) for HTTP/2; QPACK (RFC 9204) for HTTP/3 with static and dynamic table management.
HTTP/2 & HTTP/3 ALPN Server-side ALPN negotiation during the TLS handshake with graceful HTTP/1.1 fallback; the native client offers ALPN too, so explicit .http2 over TLS negotiates h2 end to end, and .http3 over QUIC negotiates h3 end to end.
Stream Multiplexing HTTP/2 and HTTP/3 stream state machines with flow control (WINDOW_UPDATE / MAX_STREAM_DATA), SETTINGS enforcement, GOAWAY/RST_STREAM, and trailers.
Connection Pooling Automatic reuse of TCP keep-alive connections, multiplexed HTTP/2 and HTTP/3 connections, and TLS session resumption caching.
Unified DOM & Web Parsing Native parser for HTML5, XML, RSS/Atom/JSON feeds, robots.txt, and sitemaps with zero-leak arena architecture.
Streaming Downloader Resumable chunked file downloader powered by loaders.zig progress bars, ETA calculation, and hash verification.
Pattern-based Routing Intuitive server routing with typed parameters (/users/{id:int}), slugs, catch-alls, groups, mounting, named routes + reversing, 404/405 handling, and OpenAPI integration.
Middleware Stack Built-in middleware for CORS, security headers (Helmet), recovery, logging, rate limiting, and CSRF, plus health endpoints.
TLS/SSL Native TLS 1.3 server engine + native TLS 1.3 client (RFC 8446) plus TLS 1.2/1.3 via the std-based HTTPS/1.1 transport: server ALPN (RFC 7301) with HTTP/1.1 fallback, native client ALPN (h2 for HTTP/2 over TLS, h3 for HTTP/3 over QUIC), X25519 ECDHE, AEAD ciphers, X.509 parsing/verification (P-256 ECDSA server certs), system/custom trust stores, hostname checks, mutual TLS enforcement (required/optional client certificates, presented via high-level .tls = .{ .clientCertPem, .clientKeyPem }), TLS 1.3 PSK session resumption (psk_dhe_ke NST tickets) + HelloRetryRequest, and complete TLS 1.3 0-RTT early data with bounded server-side anti-replay cache (ReplayCache) and safe HTTP method policy.
Static Files & SPA High-performance static file serving with ETag, cache control, conditional GET, MIME detection, and SPA HTML5 fallback.
Interactive API Docs Auto-generated OpenAPI 3.1 specifications with embedded Swagger UI, ReDoc, Scalar, and GraphiQL interfaces.
Streaming & Realtime Chunked transfer responses with optional trailers, Server-Sent Events (SSE), and WebSocket frame support.
Conditional Requests ETag and Last-Modified static file serving with If-None-Match revalidation.
DNS Resolution Resolution with caching and concurrent resolver coalescing.
Cookie APIs First-class request/response cookie jar and header helpers for both client and server contexts.
Security & Hardening Security headers (Helmet), CSRF token helpers, and CRLF injection defenses.
Multipart Form Data RFC 2046 streaming multipart body builder and parser for text fields and large file uploads.
FTP & FTPS FTP client and server with PASV/EPSV, directory listing, streaming uploads/downloads, and resumption (explicit FTPS returns a typed error until TLS wiring lands).
Concurrency & Workers Thread-safe bounded WorkerPool and parallel client requests (getAll, requestAll).
Proxy Support Client-side HTTP forward proxy (CONNECT with Proxy-Authorization auth, 407 → error.ProxyAuthRequired) and SOCKS4/4a plus SOCKS5/SOCKS5h tunneling with remote-DNS delegation.
Structured Logging Zero-allocation level-filtered structured logger supporting custom sinks and terminal formatting.
Cross-Platform Sockets Robust non-blocking Windows socket handling with WSAEWOULDBLOCK retry, plus MSG_NOSIGNAL on POSIX.
Observability & Metrics Prometheus text exposition (/metrics), live request/duration histograms, status counters, and zero-alloc snapshots.
File Watcher & Live Reload Event-driven directory watching (next() ?WatchEvent, changeCount()) with bounded event queues, cross-platform notifications, and hot/warm reload.

Prerequisites and Supported Platforms (click to expand)

Prerequisites

Requirement Version Notes
Zig 0.17.0 (required) Download from ziglang.org
Operating System Windows 10+, Linux, macOS Cross-platform networking support

[!IMPORTANT] Zig 0.17.0 is required. This project targets the stable Zig 0.17.0 release. Please use Zig 0.17.0 for all builds. Zig 0.16.0 is not supported by this release.


Supported Platforms

Platform x86_64 (64-bit) aarch64 (ARM64) x86 (32-bit)
Linux Yes Yes Yes
Windows Yes Yes Yes
macOS Yes Yes (Apple Silicon) No

Cross-Compilation

# Build for Linux ARM64 from Windows
zig build -Dtarget=aarch64-linux

# Build for Windows from Linux
zig build -Dtarget=x86_64-windows

# Build for macOS Apple Silicon from Linux
zig build -Dtarget=aarch64-macos

# Build for 32-bit Windows
zig build -Dtarget=x86-windows

Installation

Method 1: Zig Fetch (Recommended)

Latest Release (v0.2.2)

zig fetch --save https://github.com/muhammad-fiaz/httpx.zig/archive/refs/tags/0.2.2.tar.gz

Previous Release (v0.2.1)

zig fetch --save https://github.com/muhammad-fiaz/httpx.zig/archive/refs/tags/0.2.1.tar.gz

Warning

Zig 0.17 is required by v0.2.2. Zig 0.16 is deprecated and supported only by v0.2.1, and Zig 0.15 is deprecated and supported only by v0.0.7. New projects should use Zig 0.17.0+ with httpx.zig v0.2.2.

Method 2: Zig Fetch (Latest Development Build)

Use this for the latest development build from the main branch:

zig fetch --save git+https://github.com/muhammad-fiaz/httpx.zig.git

Method 3: Manual build.zig.zon Configuration

.dependencies = .{
    .httpx = .{
        .url = "https://github.com/muhammad-fiaz/httpx.zig/archive/refs/tags/0.2.2.tar.gz",
        .hash = "...", // Run `zig fetch --save <url>` to generate the hash.
    },
},

Method 4: Local Source Checkout

git clone https://github.com/muhammad-fiaz/httpx.zig.git
cd httpx.zig
zig build

To use a local checkout from another project:

.dependencies = .{
    .httpx = .{
        .path = "../httpx.zig",
    },
},

Wire into build.zig

const httpx_dep = b.dependency("httpx", .{
    .target = target,
    .optimize = optimize,
});
exe.root_module.addImport("httpx", httpx_dep.module("httpx"));

Note

If you encounter file exists in modules 'X' and 'X0', try removing .target = target and .optimize = optimize from your dependency options in the root build.zig and use .{} instead. This can help prevent duplicate module instances when multiple dependencies resolve the same module. See Zig issue #36694.

Quick Start

Global Functions (Zero-Config, No Allocator Required)

const std = @import("std");
const httpx = @import("httpx");

pub fn main() !void {
    // 1. Primary unified fetch API (supports GET, POST, headers, typed JSON)
    var resp = try httpx.fetch("https://httpbun.com/get", .{});
    defer resp.deinit();
    std.debug.print("GET Status: {d}, Body: {s}\n", .{ resp.status, resp.bytes() });

    // 2. POST with strongly typed Zig struct (serialized via std.json)
    const CreateUser = struct { name: []const u8, email: []const u8 };

    var post = try httpx.fetch("https://httpbun.com/post", .{
        .method = .POST,
        .json = CreateUser{ .name = "Alice", .email = "alice@example.com" },
    });
    defer post.deinit();

    // 3. Strongly typed response decoding (httpbun echoes our JSON under "json")
    const Echo = struct { json: ?CreateUser = null };
    const echo = try post.json(Echo);
    if (echo.json) |user| std.debug.print("User: {s} <{s}>\n", .{ user.name, user.email });

    // 4. Convenience verb shortcuts
    var del = try httpx.delete("https://httpbun.com/delete", .{});
    defer del.deinit();
}

Client Usage (Full Config)

HTTPX uses one consistent options-struct rule: fundamental inputs (URL, host, source data) and long-lived resources (allocator, io) stay positional; all configuration, behavior, limits, and settings go inside the final .{} argument:

client.get(url, .{ .timeoutMs = 10_000 });
client.resolve("httpbun.com", .{ .port = 443 });
const std = @import("std");
const httpx = @import("httpx");

pub fn main() !void {
    var gpa: std.heap.DebugAllocator(.{}) = .init;
    defer _ = gpa.deinit();
    const allocator = gpa.allocator();
    const io = std.Io.Threaded.global_single_threaded.io();

    // Create client with full config (all options use camelCase)
    var client = httpx.Client.init(allocator, io, .{
        .timeoutMs = 10_000,
        .followRedirects = true,
        .maxRedirects = 5,
        .maxRetries = 3,
        .retryDelayMs = 500,
        .retryStatusCodes = &.{ 502, 503, 504 },
        .dnsCache = .{ .enable = true, .ttlMs = 60_000 },
    });
    defer client.deinit();

    // Unified fetch request (GET)
    var response = try client.fetch("https://httpbun.com/get", .{});
    defer response.deinit();

    // Unified fetch request (POST with JSON)
    var post = try client.fetch("https://httpbun.com/post", .{
        .method = .POST,
        .json = .{ .name = "John", .role = "engineer" },
    });
    defer post.deinit();

    // HTTPS with TLS options
    var tls_resp = try client.fetch("https://httpbun.com/get", .{
        .tls = .{ .verify = .none }, // dev only
    });
    defer tls_resp.deinit();

    // Graceful close (purge connection pool)
    client.close();

    // Full reset (close + clear DNS cache)
    client.reset();
}

Batch Requests

// Parallel requests - getAll (arrays and slices accepted directly).
// Each Response must be deinited; the slice itself is freed with the
// caller's allocator (page_allocator for these global helpers).
const urls = [_][]const u8{
    "https://httpbun.com/get",
    "https://httpbun.com/headers",
};
var results = try httpx.getAll(urls);
defer {
    for (results) |*r| r.deinit();
    std.heap.page_allocator.free(results);
}

// Parallel requests - requestAll
const reqs = [_]httpx.RequestOptions{
    .{ .method = .GET, .url = "https://httpbun.com/get" },
    .{ .method = .GET, .url = "https://httpbun.com/headers" },
};
var batch = try httpx.requestAll(reqs);
defer {
    for (batch) |*r| r.deinit();
    std.heap.page_allocator.free(batch);
}

File Downloads & Progress Reporting

httpx.zig includes a streaming download, resume, and file verification subsystem powered by loaders.zig for terminal progress bars:

const std = @import("std");
const httpx = @import("httpx");

pub fn main() !void {
    var gpa: std.heap.DebugAllocator(.{}) = .init;
    defer _ = gpa.deinit();
    const allocator = gpa.allocator();
    const io = std.Io.Threaded.global_single_threaded.io();

    var client = httpx.Client.init(allocator, io, .{});
    defer client.deinit();

    const sampleUrl = "https://ontheline.trincoll.edu/images/bookdown/sample-local-pdf.pdf";

    // 1. Zero-config download with automatic filename & loaders.zig progress bar
    const res = try client.download(sampleUrl, .{
        .path = "downloads/",
        .progress = .auto,
        .existing = .overwrite,
        .createDirs = true,
    });
    std.debug.print("Downloaded: {s} ({d} bytes)\n", .{ res.destinationPath(), res.downloadedBytes });

    // 2. Download with in-flight cryptographic SHA-256 verification
    const verifiedRes = try client.download(sampleUrl, .{
        .path = "downloads/sample.pdf",
        .verify = .{
            .sha256 = "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad",
            .minSize = 100,
            .maxSize = 50 * 1024 * 1024,
        },
        .atomic = true, // downloads to temp file first, renames on valid hash
    });

    // 3. Inspect remote file metadata without downloading (size, filename, ranges)
    const fileInfo = try client.lookupFileInfo(sampleUrl, .{});
    var sizeStrBuf: [32]u8 = undefined;
    std.debug.print("Remote file: {s}, size: {s}\n", .{ fileInfo.fileName(), fileInfo.formatSize(&sizeStrBuf) });

    // 4. Resume partial download via HTTP Range: bytes=X- (clean non-reserved keyword name)
    const resumedRes = try client.download(sampleUrl, .{
        .path = "downloads/sample.pdf",
        .existing = .resumePartial,
        .maxRetries = 3,
    });

    // 5. Safe file updater with rollback backup
    const updateRes = try client.updateFile(sampleUrl, .{
        .path = "bin/app.bin",
        .backupExisting = true,
        .backupSuffix = ".bak",
    });

    // 6. Native FTP Download with progress
    const ftpRes = try httpx.ftp.download(allocator, .{
        .host = "ftp.example.com",
        .remotePath = "/pub/archive.tar.gz",
        .destinationPath = "downloads/",
        .progress = .auto,
    });
}

Parsing & Inspection (Internal Tree-sitter & DOM Engine)

httpx.zig includes a comprehensive document parsing, DOM manipulation, and CSS selector inspection engine.

Note

HTTPX parses HTML, XML, and templates with Tree-sitter grammars defined in their owning modules (parsing/html.zig, parsing/xml.zig, web/templates/parser.zig); DOM/AST semantics are built directly from those syntax trees. Applications interact exclusively with HTTPX's public APIs (httpx.Parser, response.html(), doc.select()). Tree-sitter is strictly an internal implementation detail and never needs to be imported by application code.

const std = @import("std");
const httpx = @import("httpx");

pub fn main() !void {
    var gpa: std.heap.DebugAllocator(.{}) = .init;
    defer _ = gpa.deinit();
    const allocator = gpa.allocator();


    // 1. Initialize unified parser with reusable allocator & configuration
    var p = httpx.Parser.init(allocator, .{});

    // 2. Parse HTML directly
    var doc = try p.parseHtml("<html><head><title>My Page</title></head><body><h1 class='title'>Hello</h1><a href='/link'>Click</a></body></html>");
    defer doc.deinit();

    // Fluent zero-allocator navigation
    const title = try doc.title();
    const links = try doc.links();
    var h1_nodes = try doc.select("h1.title");
    defer h1_nodes.deinit();

    // 3. Parse RSS / Atom / JSON Feed
    var feed = try p.parseFeed(xml_feed_str, null);
    defer feed.deinit();

    // 4. Parse robots.txt
    var robots = try p.parseRobots("User-agent: *\nDisallow: /admin/\n");
    defer robots.deinit();
    const allowed = robots.isAllowed("MyBot", "/public");

    // 5. Parse Sitemap XML
    var sitemap = try p.parseSitemap(sitemap_xml_str);
    defer sitemap.deinit();
}

Server

const std = @import("std");
const httpx = @import("httpx");

fn hello(ctx: *httpx.Context) anyerror!httpx.Response {
    return ctx.renderJson(.{ .message = "Hello!" });
}

fn page(ctx: *httpx.Context) anyerror!httpx.Response {
    return ctx.html("<h1>Welcome</h1>");
}

pub fn main() !void {
    var gpa: std.heap.DebugAllocator(.{}) = .init;
    defer _ = gpa.deinit();
    const allocator = gpa.allocator();
    const io = std.Io.Threaded.global_single_threaded.io();

    var server = try httpx.Server.init(allocator, io, .{
        .host = "127.0.0.1",
        .port = 8080,
        .portStrategy = .incremental, // auto-increments port (8081, 8082, ...) if 8080 is busy
    });
    defer server.deinit();

    try server.get("/hello", hello);
    try server.get("/page", page);
    server.run();
}

Modern App Facade & Zig-Native REST Framework

httpx.zig provides a high-level App facade and a type-driven Rest subsystem. REST routes automatically bind request payloads, execute typed handlers, serialize response structs to JSON, and derive an OpenAPI 3.1 specification via compile-time reflection.

const std = @import("std");
const httpx = @import("httpx");

const CreateItemRequest = struct {
    name: []const u8,
    price: u64,
};

const ItemResponse = struct {
    id: u64,
    name: []const u8,
    price: u64,
};

fn createItem(ctx: *httpx.Context, req: CreateItemRequest) !httpx.ApiResult(ItemResponse) {
    _ = ctx;
    return .created(.{
        .id = 101,
        .name = req.name,
        .price = req.price,
    });
}

fn getItem(ctx: *httpx.Context, req: struct { id: u64 }) !httpx.ApiResult(ItemResponse) {
    _ = ctx;
    return .ok(.{
        .id = req.id,
        .name = "Widget",
        .price = 499,
    });
}

pub fn main() !void {
    var gpa: std.heap.DebugAllocator(.{}) = .init;
    defer _ = gpa.deinit();
    const allocator = gpa.allocator();
    const io = std.Io.Threaded.global_single_threaded.io();

    var app = try httpx.App.init(allocator, io, .{
        .host = "127.0.0.1",
        .port = 8080,
    });
    defer app.deinit();

    // Register typed REST endpoints (drives router + OpenAPI 3.1 schema)
    try app.restPost("/api/items", createItem);
    try app.restGet("/api/items/{id}", getItem);

    // Mount interactive documentation UIs with local embedded vendor assets
    try app.docs(.{
        .openapi = .{ .enabled = true, .path = "/openapi.json", .title = "Items API", .version = "1.0.0" },
        .swaggerUi = .{ .enabled = true, .path = "/docs" },
        .redoc = .{ .enabled = true, .path = "/redoc" },
        .scalar = .{ .enabled = true, .path = "/scalar" },
    });

    try app.run();
}

TLS Server

const std = @import("std");
const httpx = @import("httpx");

fn handler(_: httpx.tls.Request) anyerror!httpx.tls.Response {
    return .{ .body = "Hello over TLS!" };
}

pub fn main() !void {
    var gpa: std.heap.DebugAllocator(.{}) = .init;
    defer _ = gpa.deinit();
    const allocator = gpa.allocator();
    const io = std.Io.Threaded.global_single_threaded.io();

    var tls_listener = try httpx.tls.Listener.init(allocator, io, .{
        .port = 8443,
        .defaultIdentity = .{
            .certChainPem = @embedFile("cert.pem"),
            .privateKeyPem = @embedFile("key.pem"),
        },
    });
    defer tls_listener.deinit();

    // Blocking accept loop — use requestShutdown() to break out
    try tls_listener.run(handler);
}

Server Lifecycle

// Blocking — runs until requestShutdown() or stop() is called
server.run();

// Non-blocking — spawns a thread, returns handle for join()
const thread = try server.start();

// Pause accepting new connections (existing connections continue)
server.pause();

// Resume accepting new connections
server.resumeAccepting();

// Graceful shutdown — finishes in-flight requests, then stops
server.requestShutdown();

// Immediate shutdown — closes listener and all connections now
server.stop();

Server Metrics & Prometheus Observability

HTTPX servers feature dynamic Prometheus v0.0.4 text exposition and thread-safe snapshots:

// Mount dynamic Prometheus endpoint
try server.metrics("/metrics");

// Query point-in-time server snapshot (zero-allocation)
const snap = server.snapshot();
std.debug.print("Uptime: {d}ms, Requests: {d}, Errors: {d}, Error Rate: {d:.2}%\n", .{
    snap.uptimeMs,
    snap.requestsTotal,
    snap.errorsTotal,
    snap.errorRate() * 100.0,
});

// Query point-in-time metrics snapshot
const m_snap = server.metricsSnapshot();
std.debug.print("Avg Latency: {d:.3}ms\n", .{m_snap.averageLatencyMs()});

Visiting /metrics provides standard Prometheus metrics:

  • http_requests_total, http_responses_total, http_errors_total, http_timeouts_total
  • http_bytesIn_total, http_bytesOut_total
  • http_active_connections, http_active_requests
  • http_requests_by_method_total{method="..."}, http_responses_by_status_total{status="..."}
  • http_request_duration_seconds_bucket{le="..."}, _sum, _count

Live File Watcher & Reload Engine

Native OS events (ReadDirectoryChangesW / inotify / kqueue) with recursive watching and rename pairing. The walk prunes generated trees (node_modules, .git, .zig-cache, zig-out, zig-pkg, .cache, dist) at any depth, so watching a repository root does not drown in dependency files.

Each changed file is classified into a reload strategy, and the browser client acts on it over SSE:

Change Strategy Browser behaviour
.css hotReload Stylesheet <link>s are cache-busted and refetched in place; no document reload, no lost script state
.html, .htm, templates, assets warmReload Document reload
.json, .env, .toml, .yaml, .conf coldReload Document reload
.zig restart Document reload

Setting liveReload = true on the server wires it up for you: the SSE endpoint is mounted at liveReloadPath, and the client script is injected into every HTML response — handler-written pages, static files, and the site generator alike.

var server = try httpx.Server.init(allocator, io, .{
    .host = "127.0.0.1",
    .port = 8080,
    .liveReload = true,      // SSE endpoint + client injection
    .watch = true,           // watch watchDir
    .watchDir = "./public",
});

onChange is invoked on the watcher thread with no internal lock held, so a callback may safely call next(), changeCount(), or stop():

var watcher = try httpx.static.Watcher.init(allocator, io, .{
    .dirPath = "./public",
    .pollIntervalMs = 50,
    .onChange = onFileChanged,
});
defer watcher.deinit();

try watcher.start();

If you build the page yourself, the same script is available directly:

const html = try httpx.static.reload.inject(allocator, body, "/__httpx_liveReload");

TLS Listener Lifecycle

// Blocking accept loop
try tls_listener.run(handler);

// Graceful shutdown (via the wrapped server)
tls_listener.server.requestShutdown();

// Immediate shutdown
tls_listener.stop();

Client Lifecycle

// Graceful close — purges the connection pool
client.close();

// Full reset — close + clear DNS cache
client.reset();

Client Retry

Configure automatic retries for failed or retryable requests:

var client = httpx.Client.init(allocator, io, .{
    .maxRetries = 3,                // retry up to 3 times (4 total attempts)
    .retryDelayMs = 500,            // base delay between retries
    .retryStatusCodes = &.{ 502, 503, 504 }, // status codes that trigger retry
});

The delay between retries increases linearly: retryDelayMs * (attempt + 1).

DNS Resolution

var resolver = httpx.resolve.Resolver.init(allocator, io);
const addrs = try resolver.lookup("example.com", .{ .port = 443 });
defer allocator.free(addrs);

Context Methods & Response Types

fn handler(ctx: *httpx.Context) anyerror!httpx.Response {
    // 1. Query parameters & Cookies
    const page = ctx.queryParam("page") orelse "1";
    const token = ctx.cookie("session");

    // 2. Remote address
    const addr = ctx.remoteAddress() orelse "unknown";

    // 3. Rich Responses
    if (std.mem.eql(u8, page, "html")) return ctx.html("<h1>Welcome</h1>");
    if (std.mem.eql(u8, page, "text")) return ctx.text("Plain text response");
    if (std.mem.eql(u8, page, "xml")) return ctx.xml("<data>sample</data>");
    if (std.mem.eql(u8, page, "rss")) return ctx.rss("<rss version=\"2.0\"><channel></channel></rss>");
    if (std.mem.eql(u8, page, "atom")) return ctx.atom("<feed xmlns=\"http://www.w3.org/2005/Atom\"></feed>");
    if (std.mem.eql(u8, page, "robots")) return ctx.robots("User-agent: *\nAllow: /");
    if (std.mem.eql(u8, page, "sitemap")) return ctx.sitemap("<urlset></urlset>");
    if (std.mem.eql(u8, page, "binary")) return ctx.binary(&[_]u8{ 0x01, 0x02, 0x03 }, "application/octet-stream");

    return ctx.renderJson(.{ .page = page, .addr = addr, .token = token });
}

Examples

The examples/ directory contains runnable examples demonstrating all features of httpx.zig:

Client:

Server:

Download & File Inspection:

Parsing & Inspection (Internal Tree-sitter & DOM Engine):

  • html_client - Client fetch and automatic response.html() parsing
  • html_select - CSS selector engine queries (tag, .class, #id, [attr], combinators)
  • html_extract - High-level extraction helpers (title, text, links, forms, images)
  • html_stream - Streaming reader input parsing
  • html_file - HTML file parsing and node inspection
  • html_transform - Structural mutation, attribute updating, and XSS-safe serialization
  • parse_html - HTML DOM, CSS Selectors, RSS feeds, robots.txt, and sitemaps

File Watching, Static Assets & Live Reload:

  • file_watcher - OS-native file monitoring (Windows ReadDirectoryChangesW, Linux inotify, macOS kqueue) with rename pairing and browser live reload
  • live_reload - Live reload dev server with CSS hot reload vs HTML page reload
  • static_site - Static site directory mounting with ETag caching and conditional GET
  • spa_server - Single Page Application server with client-side route fallback
  • development_server - Unified dev server combining watcher, live reload, and incremental parsing

Protocol:

Templates & Website:

Advanced:

Static Assets (for SPA example):

To run any example:

zig build run-<example-name>
# e.g., zig build run-simple-get
# e.g., zig build run-spa-fallback

Validation Matrix

# Host runtime validation
zig build test
zig build run-all-examples   # Runs sequentially to prevent parallel compiler OOM

# Cross-target library compile validation
zig build build-all-examples -Dtarget=x86_64-linux-gnu

To validate Linux runtime behavior, run the cross-compiled artifacts on Linux/WSL (a foreign-target zig build test only compiles; it does not execute):

zig build test -Dtarget=x86_64-linux-gnu
zig build run-simple-get -Dtarget=x86_64-linux-gnu

For explicit cross-target compilation:

# Compile tests for 32-bit Windows
zig build test -Dtarget=x86-windows-gnu

# Compile an example for macOS ARM64
zig build run-simple-get -Dtarget=aarch64-macos

Performance

Run benchmarks:

zig build bench

Benchmark target: x86_64-windows, ReleaseFast (measured 2026-10-02).

Benchmark Category Avg Latency Throughput Target
headers_parse Core Operations 257.49 ns/op 3883591 ops/sec x86_64-windows
uri_parse Core Operations 35.65 ns/op 28046635 ops/sec x86_64-windows
status_lookup Core Operations 2.23 ns/op 449147518 ops/sec x86_64-windows
method_lookup Core Operations 19.74 ns/op 50650119 ops/sec x86_64-windows
http1_request_head Core Operations 24.58 ns/op 40687786 ops/sec x86_64-windows
http1_header_block Core Operations 263.30 ns/op 3797967 ops/sec x86_64-windows
router_static_match Routing 1.02 µs/op 977952 ops/sec x86_64-windows
router_param_match Routing 1.06 µs/op 944143 ops/sec x86_64-windows
router_dispatch Routing 1.03 µs/op 970782 ops/sec x86_64-windows
router_typed_match Routing 1.14 µs/op 880393 ops/sec x86_64-windows
router_miss_404 Routing 1.81 µs/op 550989 ops/sec x86_64-windows
router_reverse Routing 68.65 ns/op 14565687 ops/sec x86_64-windows
json_stringify Serialization 245.93 ns/op 4066174 ops/sec x86_64-windows
json_parse Serialization 312.18 ns/op 3203298 ops/sec x86_64-windows
basic_auth_encode Security 24.20 ns/op 41328974 ops/sec x86_64-windows
basic_auth_decode Security 23.31 ns/op 42897466 ops/sec x86_64-windows
bearer_token_parse Security 7.82 ns/op 127813494 ops/sec x86_64-windows
gzip_compress Compression 53.11 µs/op 18830 ops/sec x86_64-windows
gzip_decompress Compression 6.64 µs/op 150626 ops/sec x86_64-windows
deflate_compress Compression 63.54 µs/op 15737 ops/sec x86_64-windows
deflate_decompress Compression 6.48 µs/op 154405 ops/sec x86_64-windows
html_parse Parsing 20.13 µs/op 49668 ops/sec x86_64-windows
template_parse Parsing 2.90 µs/op 345360 ops/sec x86_64-windows
template_render Parsing 1.46 µs/op 683819 ops/sec x86_64-windows
template_incremental Parsing 22.94 µs/op 43597 ops/sec x86_64-windows
json_feed_parse Parsing 3.78 µs/op 264404 ops/sec x86_64-windows
live_reload_inject Parsing 230.33 ns/op 4341596 ops/sec x86_64-windows
watcher_scan Watcher 1.14 ms/op 879 ops/sec x86_64-windows
watcher_deps Watcher 4.21 µs/op 237375 ops/sec x86_64-windows
worker_pool_submit Concurrency 228.54 ns/op 4375553 ops/sec x86_64-windows
concurrency_queue Concurrency 37.93 ns/op 26365676 ops/sec x86_64-windows
dns_cache_hit DNS 48.49 ns/op 20623531 ops/sec x86_64-windows
h2_frame_header Protocols 1.09 ns/op 921523093 ops/sec x86_64-windows
hpack_int_encode Protocols 1.04 ns/op 963131332 ops/sec x86_64-windows
hpack_int_decode Protocols 1.42 ns/op 704508147 ops/sec x86_64-windows
h3_varint_encode Protocols 1.58 ns/op 632739191 ops/sec x86_64-windows
h3_varint_decode Protocols 1.59 ns/op 627817330 ops/sec x86_64-windows
tls_record_seal TLS 1.62 µs/op 619051 ops/sec x86_64-windows
tls_cert_parse TLS 1.07 µs/op 938620 ops/sec x86_64-windows
client_server_get Network 387.84 µs/op 2578 req/sec x86_64-windows
h2_pooled_get Network 55.89 µs/op 17891 req/sec x86_64-windows
h3_get Network 205.89 ms/op 4 req/sec x86_64-windows
tls_full_handshake TLS 4.35 ms/op 230 ops/sec x86_64-windows
tls_resumed_handshake TLS 2.94 ms/op 339 ops/sec x86_64-windows

See docs/reference/benchmarks.md for full methodology and detailed analysis.

Contributing

Contributions are welcome! Please:

  1. Fork the repository
  2. Create a feature branch
  3. Add tests for new functionality
  4. Ensure all tests pass: zig build test
  5. Submit a pull request

Project Structure

httpx.zig/
├── src/
│   ├── httpx.zig                    # Public API entry point & re-exports
│   ├── client/                      # HTTP client
│   │   ├── client.zig               # Client struct, connection pooling, retry
│   │   ├── request.zig              # Raw request API, TLS options, auto-TLS
│   │   ├── cookies.zig              # Client cookie jar
│   │   └── download.zig             # File download, resume, verify, batch
│   ├── server/
│   │   └── lifecycle.zig            # Server struct, Config, run/stop/start, Ctrl+C
│   ├── web/
│   │   ├── router/                  # Router, Context, Response, pattern matching
│   │   ├── middleware/              # CORS, Helmet, rate-limit, auth, CSRF, proxy
│   │   ├── static_files/            # Static file serving, ETag, MIME detection
│   │   ├── spa/                     # SPA HTML5 fallback serving
│   │   ├── openapi/                 # OpenAPI 3.1 spec generation
│   │   ├── docs/                    # Swagger UI, ReDoc, Scalar, GraphiQL
│   │   ├── graphql/                 # GraphQL schema, resolvers, mount
│   │   ├── sse/                     # Server-Sent Events writer/parser
│   │   ├── websocket/               # WebSocket handshake & frames
│   │   ├── multipart/               # Multipart form encoder/parser
│   │   ├── health/                  # Health check endpoints
│   │   ├── metrics/                 # Metrics registry
│   │   ├── auth/                    # Basic & Bearer auth helpers
│   │   ├── templates/               # Flask-style template engine (Tree-sitter syntax, cached AST)
│   │   ├── site/                    # File-based website routing
│   │   ├── watcher/                 # File watcher + browser live-reload client
│   │   ─   ─   ─   ─   backend.zig          # Watcher facade, index, queue, debounce
│   │   ─   ─   ─   ─   events.zig           # Event model, classification, coalescing
│   │   ─   ─   ─   ─   client.zig           # Browser-side SSE live-reload script
│   │   ─   ─   ─   ─   dependency.zig       # Dependency graph for reload invalidation
│   │   ─   ─   ─   ─   windows/linux/macos.zig  # Native backends (RDC/inotify/kqueue)
│   ├── protocols/
│   │   ├── http1/                   # HTTP/1.x parser & writer
│   │   ├── http2/                   # HTTP/2 frame, HPACK, transport
│   │   ├── http3/                   # HTTP/3 frame, connection
│   │   ├── quic/                    # QUIC varint, packet, crypto
│   │   ├── tls/                     # TLS 1.3 server engine + native client, QUIC-TLS, ALPN, mTLS
│   │   ├── ftp/                     # FTP client & server
│   │   └── common/                  # Shared protocol utilities
│   ├── net/
│   │   ├── resolve.zig              # DNS resolver with caching
│   │   ├── address.zig              # Network address abstraction
│   │   ├── socks5.zig               # SOCKS5/5h proxy tunneling
│   │   ├── socks4.zig               # SOCKS4/4a proxy tunneling
│   │   ├── proxy.zig                # HTTP proxy support
│   │   ├── connectivity.zig         # Connectivity probing
│   │   └── dns/                     # DNS protocol + cache
│   ├── sockets/
│   │   ├── tcp.zig                  # Cross-platform TCP socket (IOCP/epoll)
│   │   ├── udp.zig                  # UDP socket
│   │   └── sys.zig                  # Raw syscall layer + error mapping
│   ├── compression/                 # gzip, brotli, zstd, deflate
│   ├── concurrency/                 # WorkerPool, parallel requests
│   ├── parsing/                     # HTML/XML DOM, CSS selectors, feeds
│   │   ├── html.zig                 # HTML5 parser
│   │   ├── xml.zig                  # XML parser
│   │   ├── selector.zig             # CSS selector engine
│   │   ├── dom.zig                  # DOM tree traversal
│   │   ├── document.zig             # Document abstraction
│   │   ├── extract.zig              # Content extraction
│   │   ├── feed.zig                 # RSS/Atom/JSON feed parser
│   │   ├── robots.zig               # robots.txt parser
│   │   └── sitemap.zig              # Sitemap parser
│   ├── common/                      # Method, Status, Headers, URI, Logger, clock
│   ├── utils/                       # MIME detection (mime.zig), filesystem helpers (fs.zig)
│   └── assets/                      # Embedded UI assets (Swagger, ReDoc, GraphiQL)
├── examples/                        # 80+ runnable examples
│   ├── simpleGet.zig               # Basic HTTP GET
│   ├── postJson.zig                # POST with JSON body
│   ├── simpleServer.zig            # Minimal HTTP server
│   ├── graphqlServer.zig           # GraphQL + REST + OpenAPI
│   ├── tlsGet.zig                  # HTTPS with TLS
│   ├── download.zig                 # File download with progress
│   └── ...                          # 70+ more (see examples/ dir)
├── bench/
│   └── main.zig                     # Microbenchmarks
├── docs/                            # VitePress documentation site
├── build.zig                        # Build system
├── build.zig.zon                    # Package metadata
├── README.md
├── SECURITY.md
├── LICENSE
└── CONTRIBUTING.md

License

MIT License - see LICENSE for details.

About

httpx.zig is a production-ready, high-performance HTTP client and server library for Zig, designed for building modern, robust, and scalable networked applications.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

96 stars

Watchers

0 watching

Forks

Releases

Sponsor this project

Packages

Used by

Contributors

Languages