std.http.Server — Servidor HTTP
O std.http.Server permite construir servidores HTTP/1.1 diretamente na biblioteca padrão do Zig, sem dependências externas. Ele processa requisições de forma síncrona, parseando cabeçalhos, métodos e caminhos, e permite enviar respostas com status, cabeçalhos e corpo customizados. Para servidores em produção, combine com threads ou pools para atender múltiplos clientes simultaneamente.
Visão Geral
const std = @import("std");
const http = std.http;
O servidor HTTP do Zig funciona sobre o std.net.Server (TCP), adicionando a camada de parsing e geração do protocolo HTTP.
Fluxo Básico
- Cria um
net.Server(TCP listener) - Aceita uma conexão TCP
- Cria um
http.Serversobre essa conexão - Recebe o cabeçalho da requisição com
receiveHead - Envia a resposta com
respondourespondStreaming
Funções Principais
Criação
// Cria servidor HTTP sobre uma conexão TCP aceita
pub fn init(connection: net.Server.Connection, buf: []u8) Server
Recebimento de Requisições
// Recebe e parseia o cabeçalho da requisição
pub fn receiveHead(self: *Server) !Request
// A struct Request contém:
pub const Request = struct {
pub const Head = struct {
method: http.Method,
target: []const u8,
version: http.Version,
};
head: Head,
// Envia resposta simples
pub fn respond(self: *Request, body: []const u8, options: ResponseOptions) !void
// Inicia resposta streaming
pub fn respondStreaming(self: *Request, options: ResponseOptions) !ResponseWriter
};
Opções de Resposta
pub const ResponseOptions = struct {
status: http.Status = .ok,
extra_headers: []const http.Header = &.{},
transfer_encoding: TransferEncoding = .none,
};
Exemplo 1: Servidor HTTP com Roteamento
const std = @import("std");
const http = std.http;
const net = std.net;
fn handleRequest(request: *http.Server.Request) !void {
const target = request.head.target;
const method = request.head.method;
if (method == .GET and std.mem.eql(u8, target, "/")) {
try request.respond(
\\<html><body>
\\<h1>Bem-vindo ao servidor Zig!</h1>
\\<p><a href="/sobre">Sobre</a> | <a href="/api/status">Status API</a></p>
\\</body></html>
, .{
.extra_headers = &.{
.{ .name = "content-type", .value = "text/html; charset=utf-8" },
},
});
} else if (method == .GET and std.mem.eql(u8, target, "/sobre")) {
try request.respond("Servidor HTTP escrito em Zig", .{
.extra_headers = &.{
.{ .name = "content-type", .value = "text/plain; charset=utf-8" },
},
});
} else if (method == .GET and std.mem.eql(u8, target, "/api/status")) {
try request.respond(
\\{"status":"ok","versao":"1.0.0"}
, .{
.extra_headers = &.{
.{ .name = "content-type", .value = "application/json" },
},
});
} else {
try request.respond("404 - Página não encontrada", .{
.status = .not_found,
.extra_headers = &.{
.{ .name = "content-type", .value = "text/plain; charset=utf-8" },
},
});
}
}
pub fn main() !void {
const addr = try net.Address.parseIp4("127.0.0.1", 8080);
var server = try addr.listen(.{ .reuse_address = true });
defer server.deinit();
std.debug.print("Servidor em http://127.0.0.1:8080\n", .{});
while (true) {
var connection = server.accept() catch continue;
var buf: [8192]u8 = undefined;
var http_server = http.Server.init(connection, &buf);
while (true) {
var request = http_server.receiveHead() catch break;
handleRequest(&request) catch break;
}
}
}
Exemplo 2: API REST com JSON
const std = @import("std");
const http = std.http;
const net = std.net;
fn responderJson(request: *http.Server.Request, body: []const u8, status: http.Status) !void {
try request.respond(body, .{
.status = status,
.extra_headers = &.{
.{ .name = "content-type", .value = "application/json" },
.{ .name = "access-control-allow-origin", .value = "*" },
},
});
}
fn handleApi(request: *http.Server.Request, allocator: std.mem.Allocator) !void {
const target = request.head.target;
const method = request.head.method;
if (method == .GET and std.mem.startsWith(u8, target, "/api/usuarios")) {
const resposta = try std.json.stringifyAlloc(allocator, .{
.usuarios = [_]struct { id: u32, nome: []const u8 }{
.{ .id = 1, .nome = "Ana" },
.{ .id = 2, .nome = "Bruno" },
},
}, .{ .whitespace = .indent_2 });
defer allocator.free(resposta);
try responderJson(request, resposta, .ok);
} else if (method == .GET and std.mem.eql(u8, target, "/api/health")) {
try responderJson(request, "{\"status\":\"healthy\"}", .ok);
} else {
try responderJson(
request,
"{\"error\":\"Rota não encontrada\"}",
.not_found,
);
}
}
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
const addr = try net.Address.parseIp4("127.0.0.1", 3000);
var server = try addr.listen(.{ .reuse_address = true });
defer server.deinit();
std.debug.print("API em http://127.0.0.1:3000\n", .{});
while (true) {
var connection = server.accept() catch continue;
var buf: [8192]u8 = undefined;
var http_server = http.Server.init(connection, &buf);
while (true) {
var request = http_server.receiveHead() catch break;
handleApi(&request, allocator) catch break;
}
}
}
Exemplo 3: Servidor com Logging e Middleware
const std = @import("std");
const http = std.http;
const net = std.net;
fn logRequest(method: http.Method, target: []const u8, status: http.Status) void {
std.debug.print("[{s}] {s} -> {d}\n", .{
@tagName(method),
target,
@intFromEnum(status),
});
}
fn servirArquivoEstatico(request: *http.Server.Request) !void {
const target = request.head.target;
// Mapa simples de rotas para conteúdo
const rotas = [_]struct { path: []const u8, content: []const u8, mime: []const u8 }{
.{ .path = "/", .content = "<h1>Home</h1>", .mime = "text/html" },
.{ .path = "/style.css", .content = "body { font-family: sans-serif; }", .mime = "text/css" },
.{ .path = "/app.js", .content = "console.log('Zig server');", .mime = "application/javascript" },
};
for (rotas) |rota| {
if (std.mem.eql(u8, target, rota.path)) {
try request.respond(rota.content, .{
.extra_headers = &.{
.{ .name = "content-type", .value = rota.mime },
},
});
logRequest(request.head.method, target, .ok);
return;
}
}
try request.respond("Not Found", .{ .status = .not_found });
logRequest(request.head.method, target, .not_found);
}
pub fn main() !void {
const addr = try net.Address.parseIp4("0.0.0.0", 8080);
var server = try addr.listen(.{ .reuse_address = true });
defer server.deinit();
std.debug.print("Servidor estático em http://0.0.0.0:8080\n", .{});
while (true) {
var connection = server.accept() catch continue;
var buf: [8192]u8 = undefined;
var http_server = http.Server.init(connection, &buf);
while (true) {
var request = http_server.receiveHead() catch break;
servirArquivoEstatico(&request) catch break;
}
}
}
Módulos Relacionados
- std.http — Visão geral do módulo HTTP
- std.http.Client — Cliente HTTP
- std.net — Networking de baixo nível
- std.json — Serialização JSON
- std.Thread — Threads para concorrência