--- /dev/null
+const std = @import("std");
+const net = std.net;
+const posix = std.posix;
+const linux = std.os.linux;
+const lmdb = @import("lmdb");
+
+fn test_lmdb(env: *lmdb.Env) !void {
+ const txn = try env.txn();
+ defer txn.abort();
+
+ const sessions = try txn.dbi("sessions", u64, u64);
+ var cursor = try sessions.cursor();
+
+ var key: u64 = undefined;
+ var user_id_maybe = cursor.get(&key, .First);
+
+ while (user_id_maybe) |user_id| {
+ _ = user_id;
+ user_id_maybe = cursor.get(&key, .Next);
+ }
+}
+
+pub fn main() !void {
+ const address = try std.net.Address.parseIp("127.0.0.1", 5882);
+
+ const tpe: u32 = posix.SOCK.STREAM | posix.SOCK.NONBLOCK;
+ const protocol = posix.IPPROTO.TCP;
+ const listener = try posix.socket(address.any.family, tpe, protocol);
+ defer posix.close(listener);
+
+ const BACKLOG = 256;
+
+ try posix.setsockopt(listener, posix.SOL.SOCKET, posix.SO.REUSEADDR, &std.mem.toBytes(@as(c_int, 1)));
+ try posix.bind(listener, &address.any, address.getOsSockLen());
+ try posix.listen(listener, BACKLOG);
+
+ // epoll_create1 takes flags. We aren't using any in these examples
+ const efd = try posix.epoll_create1(0);
+ defer posix.close(efd);
+
+ {
+ // monitor our listening socket
+ var event = linux.epoll_event{ .events = linux.EPOLL.IN, .data = .{ .fd = listener } };
+ try posix.epoll_ctl(efd, linux.EPOLL.CTL_ADD, listener, &event);
+ }
+
+ var env = lmdb.Env.open("db", 1024 * 100);
+ defer env.close();
+
+ var ready_list: [BACKLOG]linux.epoll_event = undefined;
+ while (true) {
+ const ready_count = posix.epoll_wait(efd, &ready_list, -1);
+ for (ready_list[0..ready_count]) |ready| {
+ const ready_socket = ready.data.fd;
+ if (ready_socket == listener) {
+ const client_socket = try posix.accept(listener, null, null, posix.SOCK.NONBLOCK);
+ errdefer posix.close(client_socket);
+ var event = linux.epoll_event{ .events = linux.EPOLL.IN, .data = .{ .fd = client_socket } };
+ try posix.epoll_ctl(efd, linux.EPOLL.CTL_ADD, client_socket, &event);
+ } else {
+ var closed = false;
+ var buf: [4096]u8 = undefined;
+ const read = posix.read(ready_socket, &buf) catch 0;
+ if (read == 0) {
+ closed = true;
+ } else {
+ // std.debug.print("[{d}] got: {s}\n", .{ ready_socket, buf[0..read] });
+
+ try test_lmdb(&env);
+
+ _ = try posix.write(ready_socket, "HTTP/1.1 200 OK\r\nContent-Type: text/html; charset=UTF-8\r\nContent-Length: 5\r\n\r\nabcde");
+ }
+
+ if (closed or ready.events & linux.EPOLL.RDHUP == linux.EPOLL.RDHUP) {
+ posix.close(ready_socket);
+ }
+ }
+ }
+ }
+}
// db {{{
const Prng = struct {
- prng: std.Random.DefaultPrng = std.Random.DefaultPrng.init(0),
+ var prng: std.Random.DefaultPrng = std.Random.DefaultPrng.init(0);
- pub fn gen_id(self: *Prng, dbi: anytype) Id {
- var id = self.prng.next();
+ pub fn gen_id(dbi: anytype) Id {
+ var id = Prng.prng.next();
while (dbi.has(id)) {
- id = self.prng.next();
+ id = Prng.prng.next();
}
return id;
}
};
-var prng = Prng{};
// }}}
pub fn register_user(env: *lmdb.Env, username: []const u8, password: []const u8) !void {
const username_array = try Username.fromSlice(username);
- var user_id: Id = undefined;
-
- if (env.txn()) |*txn| {
- defer txn.commit();
- if (txn.dbi("users", Id, User)) |users| {
- user_id = prng.gen_id(users);
- users.put(user_id, User{
- .username = username_array,
- .password_hash = try hash_password(password),
- });
- }
+ const txn = try env.txn();
+ defer {
+ txn.commit();
+ env.sync();
}
- if (env.txn()) |txn| {
- defer txn.commit();
- if (txn.dbi("user_ids", @TypeOf(username_array.buffer), Id)) |user_ids| {
- user_ids.put(username_array.buffer, user_id);
- }
- }
+ const users = try txn.dbi("users", Id, User);
+ const user_id = Prng.gen_id(users);
+ users.put(user_id, User{
+ .username = username_array,
+ .password_hash = try hash_password(password),
+ });
- env.sync();
+ const user_ids = try txn.dbi("user_ids", Username, Id);
+ user_ids.put(username_array, user_id);
}
-pub fn login_user(env: *lmdb.Env, username: []const u8, password: []const u8) ?SessionToken {
- const username_array = Username.fromSlice(username) catch return null;
-
- var user_id_maybe: ?Id = null;
- var user_maybe: ?User = null;
+pub fn login_user(env: *lmdb.Env, username: []const u8, password: []const u8) !SessionToken {
+ const username_array = try Username.fromSlice(username);
- if (env.txn()) |txn| {
- defer txn.abort();
- if (txn.dbi("user_ids", @TypeOf(username_array.buffer), Id)) |user_ids| {
- user_id_maybe = user_ids.get(username_array.buffer);
- std.debug.print("id: {?}\n", .{user_id_maybe});
- }
+ const txn = try env.txn();
+ defer {
+ txn.commit();
+ env.sync();
}
- if (user_id_maybe) |user_id| {
- if (env.txn()) |txn| {
- defer txn.abort();
- if (txn.dbi("users", Id, User)) |users| {
- user_maybe = users.get(user_id_maybe.?);
- }
+ const user_ids = try txn.dbi("user_ids", Username, Id);
+ const user_id = user_ids.get(username_array) orelse return error.UnknownUsername;
+ std.debug.print("id: {}\n", .{user_id});
+
+ const users = try txn.dbi("users", Id, User);
+ if (users.get(user_id)) |user| {
+ if (verify_password(password, user.password_hash)) {
+ const sessions = try txn.dbi("sessions", Id, Id);
+ const session_token = Prng.gen_id(sessions);
+ sessions.put(session_token, user_id);
+ return session_token;
+ } else {
+ return error.IncorrectPassword;
}
+ } else {
+ return error.UserNotFound;
+ }
+}
- if (user_maybe) |user| {
- if (verify_password(password, user.password_hash)) {
- if (env.txn()) |txn| {
- defer txn.commit();
- if (txn.dbi("sessions", Id, Id)) |sessions| {
- const session_token = prng.gen_id(sessions);
- sessions.put(session_token, user_id);
- return session_token;
- }
- }
- }
- }
+fn logout_user(env: *lmdb.Env, session_token: SessionToken) !void {
+ const txn = try env.txn();
+ defer {
+ txn.commit();
+ env.sync();
}
- return null;
+ const sessions = try txn.dbi("sessions", Id, Id);
+ sessions.del(session_token);
}
-fn logout_user(env: *lmdb.Env, session_token: SessionToken) void {
- if (env.txn()) |txn| {
- defer txn.commit();
- if (txn.dbi("sessions", Id, Id)) |sessions| {
- sessions.del(session_token);
- }
+fn get_session_user(env: *lmdb.Env, session_token: SessionToken) !User {
+ const txn = try env.txn();
+ defer txn.abort();
+
+ const sessions = try txn.dbi("sessions", Id, Id);
+ const users = try txn.dbi("users", Id, User);
+
+ if (sessions.get(session_token)) |user_id| {
+ return users.get(user_id) orelse error.UnknownUser;
+ } else {
+ return error.SessionNotFound;
}
}
-fn get_session_user(env: *lmdb.Env, session_token: SessionToken) ?User {
- var user_id_maybe: ?Id = null;
+// }}}
- if (env.txn()) |txn| {
- defer txn.abort();
- if (txn.dbi("sessions", Id, Id)) |sessions| {
- user_id_maybe = sessions.get(session_token);
- }
- }
+fn list_users(env: *lmdb.Env) !void {
+ const txn = try env.txn();
+ defer txn.abort();
- if (user_id_maybe) |user_id| {
- if (env.txn()) |txn| {
- defer txn.abort();
- if (txn.dbi("users", Id, User)) |users| {
- return users.get(user_id);
- }
- }
+ const users = try txn.dbi("users", Id, User);
+ var cursor = try users.cursor();
+
+ var key: Id = undefined;
+ var user_maybe = cursor.get(&key, .First);
+
+ while (user_maybe) |user| {
+ std.debug.print("[{}] {s}\n", .{ key, user.username.constSlice() });
+
+ user_maybe = cursor.get(&key, .Next);
}
+}
+fn list_user_ids(env: *lmdb.Env) !void {
+ const txn = try env.txn();
+ defer txn.abort();
- return null;
+ const user_ids = try txn.dbi("user_ids", Username, Id);
+ var cursor = try user_ids.cursor();
+
+ var key: Username = undefined;
+ var user_id_maybe = cursor.get(&key, .First);
+
+ while (user_id_maybe) |user_id| {
+ std.debug.print("[{s}] {}\n", .{ key.constSlice(), user_id });
+
+ user_id_maybe = cursor.get(&key, .Next);
+ }
}
-// }}}
+fn list_sessions(env: *lmdb.Env) !void {
+ const txn = try env.txn();
+ defer txn.abort();
-fn list_users(env: *lmdb.Env) void {
- if (env.txn()) |txn| {
- defer txn.abort();
- if (txn.dbi("users", Id, User)) |users| {
- var cursor = users.cursor();
+ const sessions = try txn.dbi("sessions", SessionToken, Id);
+ var cursor = try sessions.cursor();
- var key: Id = undefined;
- var user_maybe = cursor.get(&key, .First);
+ var key: SessionToken = undefined;
+ var user_id_maybe = cursor.get(&key, .First);
- while (user_maybe) |user| {
- std.debug.print("[{}] {s}\n", .{ key, user.username.constSlice() });
+ while (user_id_maybe) |user_id| {
+ std.debug.print("[{}] {}\n", .{ key, user_id });
- user_maybe = cursor.get(&key, .Next);
- }
- }
+ user_id_maybe = cursor.get(&key, .Next);
}
}
defer server.deinit();
// lmdb
- var env = lmdb.Env.open("db", 1024 * 1024 * 10);
+ var env = lmdb.Env.open("db", 1024 * 100);
defer env.close();
- list_users(&env);
+ std.debug.print("Users:\n", .{});
+ try list_users(&env);
+ std.debug.print("User IDs:\n", .{});
+ try list_user_ids(&env);
+ std.debug.print("Sessions:\n", .{});
+ try list_sessions(&env);
accept: while (true) {
const conn = try server.accept();
.user = user,
.session_token = session_token,
};
+ } else |err| {
+ std.debug.print("get_session_user err: {}\n", .{err});
}
// TODO: delete session token
// TODO: add changeable headers (set, delete cookies)
.{ .name = "Set-Cookie", .value = cookie_buffer.constSlice() },
},
});
- } else {
+ } else |err| {
+ std.debug.print("login_user err: {}\n", .{err});
try redirect(&req, "/login");
}
} else if (std.mem.eql(u8, req.head.target, "/logout")) {
if (logged_in) |login| {
- logout_user(&env, login.session_token);
+ try logout_user(&env, login.session_token);
try req.respond("", .{
.status = .see_other,
.extra_headers = &.{