#writing simple std.http.Server

1 messages · Page 1 of 1 (latest)

keen lintel
#

Hello, I tried to write an http server using the std documentation but it I find it really hard to to understand what to do + all the ressources online are for old zig versions, someone has a working std.http.Server with 0.13.0 or more recent or just some hints ? I cannot even init the server :/

mossy gale
#

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

GitHub

Another shitty HTTP server. Contribute to bcrist/shittip development by creating an account on GitHub.

keen lintel
#

thank you very much 🙂 thats so stupid that this short doc is not in the official std doc :/

keen lintel
#
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

mossy gale
#

std.net.Address.listen() returns a std.net.Server

keen lintel
#

I feel like to find answers to that I have to navigate the std for hours

keen lintel
mossy gale
#

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

keen lintel
#

okok ty im having a look at all of that 🙂

keen lintel
# mossy gale Yeah, I don't know it took me a while too. Pretty sure there's a few tests some...
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

mossy gale
#

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

keen lintel
pure oxide
keen lintel
#
// 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 ?

keen lintel
mossy gale
# keen lintel ``` // Read request var req = http_server_with_client.receiveHead() ...

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,
} ...

}

pure oxide
#
/// 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)

keen lintel
mossy gale
#

it's arbitrary, I figured 2^16 should be plenty for most uses