summaryrefslogtreecommitdiff
path: root/http.zig
blob: 6b86d14e699158533d82e76e000e7cd97f28c270 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
const std = @import("std");
const log = std.log;
const net = std.Io.net;
const Io = std.Io;
const Request = std.http.Server.Request;

const MAX_BUF = 1024;

const Endpoint = enum {
    root_get,
    login_get,
};

pub fn serve(io: std.Io, address: []const u8, port: u16) !void {
    const addr = try net.IpAddress.parse(address, port);
    var server = try addr.listen(io, .{ .reuse_address = true });
    defer server.deinit(io);

    log.info("Start HTTP server at {f}", .{addr});

    while (true) {
        const stream = server.accept(io) catch |err| {
            log.err("failed to accept connection: {s}", .{@errorName(err)});
            continue;
        };
        const thread = std.Thread.spawn(.{}, accept, .{ stream, io }) catch |err| {
            log.err("unable to spawn connection thread: {s}", .{@errorName(err)});
            stream.close(io);
            continue;
        };
        thread.detach();
    }
}

fn accept(stream: net.Stream, io: Io) !void {
    defer stream.close(io);

    log.info("Got new client!", .{});

    var recv_buffer: [1024]u8 = undefined;
    var send_buffer: [100]u8 = undefined;
    var stream_reader = stream.reader(io, &recv_buffer);
    var stream_writer = stream.writer(io, &send_buffer);
    var server = std.http.Server.init(&stream_reader.interface, &stream_writer.interface);
    while (server.reader.state == .ready) {
        var request = server.receiveHead() catch |err| switch (err) {
            error.HttpConnectionClosing => return,
            else => return err,
        };
        try serveHTTP(&request);
    }
}

fn endpointFromRequest(request: *Request) !Endpoint {
    const head = request.head;
    if (head.method == .GET and std.mem.eql(u8, head.target, "/")) {
        return .root_get;
    } else if (head.method == .GET and std.mem.eql(u8, head.target, "/login")) {
        return .login_get;
    }
    return error.InvalidEndpoint;
}

fn serveHTTP(request: *Request) !void {
    log.info("Received request: {s}", .{request.head.target});

    const endpoint = endpointFromRequest(request) catch {
        try respondNotFound(request);
        return;
    };
    switch (endpoint) {
        .root_get => try handleRoot(request),
        .login_get => try handleLogin(request),
    }
}

fn respondNotFound(request: *Request) !void {
    try request.respond("", .{ .status = .not_found });
}

fn handleRoot(request: *Request) !void {
    try request.respond("Welcome to Coinvane!\n", .{});
}

fn handleLogin(request: *Request) !void {
    try request.respond("Login page\n", .{});
}