treesummaryrefslogcommitdiff
path: root/src/main.zig
blob: 3616370d51a9e37e272936d85cc6566ab25bbf1c (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
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
const std = @import("std");
const Io = std.Io;
const WebSocket = std.http.Server.WebSocket;

var m = std.atomic.Mutex.unlocked;

var clients: std.ArrayList(*WebSocket) = undefined;

fn broadcast(sm: WebSocket.SmallMessage) void {
    if (!m.tryLock()) return;
    for (clients.items) |c| {
        c.writeMessage(sm.data, sm.opcode) catch {};
    }
    m.unlock();
}

fn handle_websocket(alloc: std.mem.Allocator, websocket: *WebSocket) void {
    if (!m.tryLock()) return;
    clients.append(alloc, websocket) catch {};
    m.unlock();

    const i = clients.items.len;
    defer _ = clients.swapRemove(i);

    websocket.writeMessage("welcome", .text) catch return;

    while (true) {
        const sm = websocket.readSmallMessage() catch break;
        broadcast(sm);
    }
}

fn handle_request(alloc: std.mem.Allocator, io: Io, stream: Io.net.Stream) void {
    var recv_buffer: [999]u8 = undefined;
    var send_buffer: [100]u8 = undefined;

    defer stream.close(io);

    var connection_br = stream.reader(io, &recv_buffer);
    var connection_bw = stream.writer(io, &send_buffer);
    var server = std.http.Server.init(&connection_br.interface, &connection_bw.interface);

    while (true) {
        var req = server.receiveHead() catch break;

        switch (req.upgradeRequested()) {
            .websocket => |ws| {
                var websocket = req.respondWebSocket(.{ .key = ws.? }) catch break;

                handle_websocket(alloc, &websocket);
            },
            else => {
                req.respond(
                    \\ <script>
                    \\ const socket = new WebSocket("wss://yap.ps.run");
                    \\ socket.addEventListener("open", (event) => {
                    \\   socket.send("Hello Server!");
                    \\ });
                    \\ socket.addEventListener("message", (event) => {
                    \\   console.log("Message from server ", event.data);
                    \\ });
                    \\ </script>
                    \\ <p>hallo</p>
                , .{ .status = .ok }) catch break;
            },
        }
    }

    // std.debug.print("closing http thread\n", .{});
}

pub fn main(init: std.process.Init) !void {
    // Prints to stderr, unbuffered, ignoring potential errors.
    std.debug.print("All your {s} are belong to us.\n", .{"codebase"});

    // This is appropriate for anything that lives as long as the process.
    const arena: std.mem.Allocator = init.arena.allocator();

    clients = try .initCapacity(arena, 10);

    // Accessing command line arguments:
    const args = try init.minimal.args.toSlice(arena);
    for (args) |arg| {
        std.log.info("arg: {s}", .{arg});
    }

    // In order to do I/O operations need an `Io` instance.
    const io = init.io;

    var port: u16 = 10010;
    if (init.environ_map.get("PORT")) |s| {
        if (std.fmt.parseInt(u16, s, 10)) |p| {
            port = p;
        } else |e| {
            std.debug.print("{}\n", .{e});
        }
    }

    const address = try std.Io.net.IpAddress.parseIp4("0.0.0.0", port);
    var net_server = try address.listen(io, .{ .reuse_address = true });

    while (true) {
        const stream = try net_server.accept(io);

        _ = io.async(handle_request, .{ arena, io, stream });

        // std.debug.print("created http thread\n", .{});
    }
}