implement WHOWAS using Cache to store history

This commit is contained in:
tali 2024-01-31 11:52:18 -05:00
parent 41b7fde963
commit c3ea3aea6d
7 changed files with 57 additions and 19 deletions

View File

@ -4,8 +4,9 @@ Logging.init_pretty_writer stderr
Lwt_main.run
(Server.run {
port = 6667;
tcp_listen_backlog = 8;
listen_backlog = 8;
ping_interval = 60;
whowas_history_len = 1000;
hostname = "irc.tali.software";
(* TODO: motd *)
})

View File

@ -61,7 +61,7 @@ let reply t (num, params) =
| None -> "*"
in
let always_trailing = match num with
| "301" | "311" | "312" | "319" | "332" | "353" -> true
| "301" | "311" | "312" | "314" | "319" | "332" | "353" -> true
| _ -> false
in
Outbox.send t.outbox
@ -634,6 +634,26 @@ let on_msg_whois t nick =
list_whois t user;
Ok ()
let list_whowas t nick limit =
List.iter_up_to ~limit
(fun (nick, { username; hostname; realname }) ->
reply t ("314", [nick; username; hostname; "*"; realname]))
(Router.whowas t.router nick);
reply t ("369", [nick; "End of WHOWAS"])
let on_msg_whowas t nick count =
let* _me = require_registered t in
let limit =
try
let n = Option.get (int_of_string_opt count) in
if n <= 0 then invalid_arg "count <= 0";
n
with Invalid_argument _ ->
max_int
in
list_whowas t nick limit;
Ok ()
(* welcome and quit *)
@ -834,8 +854,11 @@ let dispatch t = function
| "AWAY", args -> on_msg_away t (concat_args args)
| "MODE", tgt :: args when tgt <> "" -> on_msg_mode t tgt args
| "WHO", mask :: _ when mask <> "" -> on_msg_who t mask
| "WHOIS", ([] | [""] | (_ :: "" :: _)) -> Error nonicknamegiven
| "WHOIS", ([nick] | (_ :: nick :: _)) -> on_msg_whois t nick
| "WHOIS", ([] | [""] | _ :: "" :: _) -> Error nonicknamegiven
| "WHOIS", ([nick] | _ :: nick :: _) -> on_msg_whois t nick
| "WHOWAS", ([] | "" :: _) -> Error nonicknamegiven
| "WHOWAS", [nick] -> on_msg_whowas t nick ""
| "WHOWAS", nick :: count :: _ -> on_msg_whowas t nick count
| ("USER" | "JOIN" | "NAMES" | "PART" | "KICK" | "MODE" | "WHO") as cmd, _ ->
Error (needmoreparams cmd)
(* TODO: "LIST" *)
@ -847,7 +870,6 @@ let dispatch t = function
(* TODO: "HELP" *)
(* TODO: "INFO" *)
(* TODO: "NOTICE" *)
(* TODO: "WHOWAS" *)
(* TODO: "KILL" *)
(* TODO: "REHASH" *)
(* TODO: "RESTART" *)

View File

@ -5,9 +5,11 @@ include (val Logging.sublogs logger "Router")
type t = router
let make () =
{ users = Hashtbl.create 4096;
channels = Hashtbl.create 4096 }
let make ~whowas_history_len = {
users = Hashtbl.create 1024;
channels = Hashtbl.create 1024;
whowas = Cache.make whowas_history_len;
}
let is_nick_available t nick =
not (Hashtbl.mem t.users (string_ci nick))
@ -18,6 +20,9 @@ let find_user t nick =
let find_chan t name =
Hashtbl.find t.channels (string_ci name)
let whowas t nick =
Cache.find_all t.whowas (string_ci nick)
let relay ~(from : user) (msg : Msg.t) tgts =
let msg =
if msg.prefix = No_prefix then

View File

@ -42,4 +42,5 @@ and priv =
type router = {
users : (string_ci, user) Hashtbl.t;
channels : (string_ci, chan) Hashtbl.t;
whowas : (string_ci, name * userinfo) Cache.t;
}

View File

@ -6,14 +6,14 @@ include (val Logging.sublogs logger "Server")
type ping_wheel = Connection.t Wheel.t
let listener ~(port : int) ~(backlog : int) : (fd * sockaddr) Lwt_stream.t =
let listener ~(port : int) ~(listen_backlog : int) : (fd * sockaddr) Lwt_stream.t =
let sock : fd Lwt.t =
let fd = Lwt_unix.socket PF_INET SOCK_STREAM 0 in
Lwt_unix.setsockopt fd SO_KEEPALIVE false;
Lwt_unix.setsockopt fd SO_REUSEPORT true;
let srv_adr = Unix.ADDR_INET (Unix.inet_addr_any, port) in
let* () = Lwt_unix.bind fd srv_adr in
Lwt_unix.listen fd backlog;
Lwt_unix.listen fd listen_backlog;
info (fun m -> m "listening on %a" pp_sockaddr srv_adr);
Lwt.return fd
in
@ -94,26 +94,30 @@ let handle_client
type config = {
port : int;
tcp_listen_backlog : int;
listen_backlog : int;
ping_interval : int;
whowas_history_len : int;
hostname : string;
(* TODO: motd *)
}
let run (cfg : config) : unit Lwt.t =
let run { port; listen_backlog; ping_interval;
whowas_history_len; hostname } : unit Lwt.t
=
let server_info =
Server_info.make
~hostname:cfg.hostname
~hostname
(* ~motd *)
in
let router : Router.t =
Router.make ()
Router.make
~whowas_history_len
in
let ping_wheel : _ Wheel.t =
Wheel.make
cfg.ping_interval
ping_interval
in
let on_tick () =
@ -145,8 +149,8 @@ let run (cfg : config) : unit Lwt.t =
Lwt_stream.iter
on_con
(listener
~port:cfg.port
~backlog:cfg.tcp_listen_backlog)
~port
~listen_backlog)
in
listener_promise <&> pinger_promise

View File

@ -9,7 +9,7 @@ let%expect_test _ =
with e -> print_endline (Printexc.to_string e)
in
let router = Router.make () in
let router = Router.make ~whowas_history_len:1000 in
let u1 =
User.make "beenie"

View File

@ -27,7 +27,12 @@ let set_nick t new_nick =
t.nick_key <- string_ci new_nick;
end
let register t ~router = Hashtbl.add router.users t.nick_key t
let register t ~router =
begin
Hashtbl.add router.users t.nick_key t;
Cache.add router.whowas t.nick_key (t.nick, t.userinfo);
end
let unregister t ~router = Hashtbl.remove router.users t.nick_key
let is_registered t ~router = Hashtbl.mem router.users t.nick_key