Client and server development based on std.http (Zig 0.16.0).
Capability Boundaries
✅ Strengths
- HTTP client requests (GET/POST/PUT/DELETE)
- Custom request headers, request bodies, query parameters
- HTTP server setup and routing
- WebSocket client and server
- TLS/HTTPS connections
- Connection pool management and compression
⚠️ Prerequisites
- Confirm the Zig version (
zig version) - For HTTPS, the system needs CA certificates
❌ Out of Scope
- Non-HTTP network programming → use
zig-0.16's std.net - Zig language fundamentals → use the
zig-0.16skill
When to Use
- "Help me write an HTTP client"
- "How do I set up an HTTP server?"
- "Call a REST API with Zig"
Data Privacy
This skill does not collect, store, or transmit any user data.
Workflow
Step 1. Initialize Client — std.http.Client{ .allocator = allocator }
Step 2. Configure the request — URL, method, headers, request body
Step 3. Send the request — client.fetch() or the low-level API
Step 4. Handle the response — status code, response body
Step 5. Resource cleanup — client.deinit()
HTTP Client
Quick GET
var client: std.http.Client = .{ .allocator = allocator };
defer client.deinit();
const result = try client.fetch(.{
.location = .{ .url = "https://api.example.com/data" },
});
std.debug.print("Status: {d}\n", .{@intFromEnum(result.status)});
GET and read the response body
var body_buf: [65536]u8 = undefined;
var body_writer: std.Io.Writer = .fixed(&body_buf);
const result = try client.fetch(.{
.location = .{ .url = "https://api.example.com/data" },
.response_writer = &body_writer,
});
const body = body_writer.buffered();
std.debug.print("Body: {s}\n", .{body});
POST JSON
const payload = "{\"key\": \"value\"}";
var buf: [4096]u8 = undefined;
var body_writer: std.Io.Writer = .fixed(&buf);
const result = try client.fetch(.{
.location = .{ .url = "https://api.example.com/submit" },
.method = .POST,
.payload = payload,
.response_writer = &body_writer,
.headers = &.{
.{ .name = "Content-Type", .value = "application/json" },
},
});
With query parameters
var uri = std.Uri.parse("https://api.example.com/search")?;
uri.query = "q=zig&limit=10";
const result = try client.fetch(.{ .location = .{ .uri = uri } });
Custom request headers
const result = try client.fetch(.{
.location = .{ .url = "https://api.example.com/protected" },
.headers = &.{
.{ .name = "Authorization", .value = "Bearer token123" },
.{ .name = "Accept", .value = "application/json" },
},
});
File upload (multipart)
var form = std.http.Client.Fetch.Form.init(allocator);
defer form.deinit();
try form.field("name", "value");
try form.field("file", file_contents, .{ .filename = "data.txt" });
const result = try client.fetch(.{
.location = .{ .url = "https://api.example.com/upload" },
.method = .POST,
.form = form,
});
Low-level request (finer-grained control)
var req = try client.request(.GET, uri, .{}, .{});
defer req.deinit();
// 发送请求头
try req.send(.{});
// 发送请求体(可选)
try req.writeAll("body data");
// 开始读取响应
try req.finish();
try req.wait();
// 读取响应体
var buf: [4096]u8 = undefined;
var reader_writer: std.Io.Writer = .fixed(&buf);
try req.reader().?.readAllWriter(&reader_writer);
Connection pool
try client.pool.enable(); // 启用连接池
// 多个请求复用连接
for (urls) |url| {
const result = try client.fetch(.{ .location = .{ .url = url } });
_ = result;
}
HTTP Server
Basic server
pub fn main() !void {
var gpa: std.heap.DebugAllocator(.{}) = .init;
defer _ = gpa.deinit();
const allocator = gpa.allocator();
var server = std.http.Server.init(allocator, .{
.reuse_port = true,
});
defer server.deinit();
const addr = try std.net.Address.parseIp("127.0.0.1", 8080);
try server.listen(addr);
while (true) {
var response_buf: [65536]u8 = undefined;
var req = try server.accept(.{ .response_buffer = &response_buf });
defer req.deinit();
// 响应 Hello World
try req.respond("Hello, World!\n", .{});
}
}
Route dispatch
while (true) {
var buf: [65536]u8 = undefined;
var req = try server.accept(.{ .response_buffer = &buf });
defer req.deinit();
const url = req.request.target;
if (std.mem.eql(u8, url, "/api/hello")) {
try req.respond("{\"msg\": \"hello\"}", .{ .content_type = .json });
} else if (std.mem.eql(u8, url, "/api/status")) {
try req.respond("{\"status\": \"ok\"}", .{ .content_type = .json });
} else {
try req.respond("Not Found", .{ .status = .not_found });
}
}
JSON response
const body = "{\"name\": \"server\", \"version\": \"1.0\"}";
try req.respond(body, .{ .content_type = .json });
Read the request body
var req_body: [4096]u8 = undefined;
const body_len = try req.readAll(&req_body);
const body = req_body[0..body_len];
// 处理 body...
try req.respond("OK", .{});
WebSocket
// 服务端升级到 WebSocket
var ws = try req.upgradeToWebSocket(.{});
defer ws.deinit();
while (true) {
const frame = try ws.readFrame() orelse break;
std.debug.print("Received: {s}\n", .{frame.payload});
try ws.writeFrame("echo: " ++ frame.payload);
}
Gotchas
- std.http is experimental in 0.16 — the API may change in future versions
- Client must be deinit'ed —
client.deinit()to release the connection pool and resources - Response body size limit —
fetch()By default there is no size limit, but be careful of overflow when providing your own fixed buffer - HTTPS requires system CAs — in containers or environments without CAs, you may need to configure
server.ca_bundle - Server.listen's addr — use
127.0.0.1for IPv4,[::1]
FAQ
Q: How do I set a timeout?
A: Use the low-level API (client.request + req.wait()) to control timeouts.fetch() The high-level API does not currently support timeouts.
Q: Maximum number of concurrent connections? A: 128 by default. Once the connection pool is enabled, reuse is managed automatically, with no explicit limit.
Q: Is HTTP/2 supported? A: std.http currently only supports HTTP/1.1. HTTP/2 support is planned.