#writing simple std.http.Server
1 messages · Page 1 of 1 (latest)
Might be more complicated than you're looking for (there's also some threading, routing and logging stuff going on) but here's my http server implementation: https://github.com/bcrist/shittip/blob/main/src/server.zig
Mostly I just figured out the interface using the tests in std but there's also some documentation in the 0.12 release notes: https://ziglang.org/download/0.12.0/release-notes.html#Rework-Server-Entirely
thank you very much 🙂 thats so stupid that this short doc is not in the official std doc :/
var server: std.net.Server = undefined;
_ = try server.accept();
``` to what should i init my std.net.Server ?
instead of undefined
because that makes server.accept() fail
std.net.Address.listen() returns a std.net.Server
I feel like to find answers to that I have to navigate the std for hours
how am i supposed to know that ?
Yeah, I don't know it took me a while too. Pretty sure there's a few tests somewhere in the std that spin up a local server and that's how I figured it out, but now I can't find where those tests actually are
ah, std/net/test.zig but that file will be empty if you're looking at the std folder distributed with a prebuilt compiler binary - you have to check out the actual zig repo
okok ty im having a look at all of that 🙂
const std = @import("std");
pub fn main() !void {
const addr = try std.net.Address.parseIp("127.0.0.1", 4242);
var server: std.net.Server = try std.net.Address.listen(addr, .{ .reuse_address = true, .reuse_port = true });
var idx: usize = 0;
while (true) {
var buffer: [1024]u8 = undefined;
var conn = try server.accept(); // blocking call
idx += 1;
defer conn.stream.close();
var http_server_with_client = std.http.Server.init(conn, &buffer);
while (http_server_with_client.state == .ready) {
std.debug.print("{d}\n", .{idx});
// Read request
var req = try http_server_with_client.receiveHead();
_ = try req.reader();
// Send response
try req.respond("bonjour", .{});
}
}
}
im coming up with this simple server which work fine when using curl or web browser, but when using wrk I get the following error:
error: HttpConnectionClosing on receiveHead()
i think it is realted to keep-alive but no idea why
I'm not familiar with wrk, is it this? https://github.com/wg/wrk
HttpConnectionClosing is what's returned when the client closes the connection "normally." For HTTP/1.0 I believe the default is no keep-alive unless negotiated with the header, whereas the default for HTTP/1.1 is to use keep-alive unless disabled by the header
yes it is this one
I am also interested in how to make an std.http.Server, but I'm not entirely sure how to get the std.net.Server.Connection parameter in https://ziglang.org/documentation/0.13.0/std/#std.http.Server.init 🤔
// Read request
var req = http_server_with_client.receiveHead() catch |err| {
std.debug.print("error: {any}\n", .{err});
break;
};
_ = try req.reader();
// Send response
try req.respond("bonjour", .{});
i ended up doing this to handle the error but im not sure its the right way of handling it right ?
look at my code i achieved it somehow ahahaha
The correct way to handle it is to close the connection on the server side. e.g. in my library linked above: ```fn handle_connection(..., connection: std.net.Server.Connection) void {
var header_buf: [65536]u8 = undefined;
var server = std.http.Server.init(connection, &header_buf);
defer server.connection.stream.close();
... server.receiveHead() catch |err| switch (err) {
...
error.HttpConnectionClosing => return,
} ...
}
/// All this does is just get the http request (Request line + headers) from a client/browser, and print them here in the server, and sends an html page to the client/browser.
pub fn do_server(allocator: std.mem.Allocator) !void {
const address = std.net.Address.initIp4(.{ 127, 0, 0, 1 }, 8080); //localhost
var server = try address.listen(.{});
defer server.deinit();
var request_buf: [8192]u8 = undefined; //Not sure what the size for a request http header could be. Guessing 8192 bytes.
while (true) {
std.debug.print("Ready to listen to a client...\n", .{});
var connection = try server.accept();
const size = try connection.stream.read(&request_buf);
const request_slice = request_buf[0..size];
const request_line: ?[]const u8 = l: {
break :l request_slice[0 .. std.mem.indexOfPosLinear(u8, request_slice, 0, "\r\n") orelse break :l null];
};
std.debug.print("Request line:\n{?s}\nHeaders:\n", .{request_line});
var header_it = std.http.HeaderIterator.init(request_slice);
while (header_it.next()) |kv_pair| {
std.debug.print("{s} = {s}\r\n", .{ kv_pair.name, kv_pair.value });
}
const an_html_webpage_fmt =
\\<!DOCTYPE html>
\\<html>
\\<head>
\\ <title>Title of Page</title>
\\</head>
\\<body>
\\ Hello <b>{any}!</b> You have visited this site.<br>
\\ Request line: <b>{?s}</b> <br>
\\</body>
\\</html>
\\
;
const an_html_webpage = try std.fmt.allocPrint(allocator, an_html_webpage_fmt, .{ connection.address, request_line });
defer allocator.free(an_html_webpage);
connection.stream.writer().print("HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nContent-Length: {}\r\n\r\n{s}", .{ an_html_webpage.len, an_html_webpage }) catch continue;
}
}
Oh ok. I did the same too but without using std.http.Server (Just std.net.Server)
why the 65536 bytes long buffer tho ?
it's arbitrary, I figured 2^16 should be plenty for most uses
net is just tcp no ?