From 8cf076bf5f4c2c3f4a12c22e97a75e337d5fa08a Mon Sep 17 00:00:00 2001 From: Alain Zscheile Date: Sat, 26 Nov 2022 00:54:03 +0100 Subject: [PATCH] Floof.serve should be always tail-recursive --- lib/floof.ex | 109 ++++++++++++++++++++++++++++----------------------- 1 file changed, 61 insertions(+), 48 deletions(-) diff --git a/lib/floof.ex b/lib/floof.ex index c21398c..43bfccd 100644 --- a/lib/floof.ex +++ b/lib/floof.ex @@ -17,9 +17,12 @@ defmodule Floof do ] ++ opts ) - Logger.info("Accepting connections on #{port}") - loop_acceptor(socket) - :ok = :gen_tcp.close(socket) + try do + Logger.info("Accepting connections on #{port}") + loop_acceptor(socket) + after + :ok = :gen_tcp.close(socket) + end end defp loop_acceptor(socket) do @@ -45,60 +48,70 @@ defmodule Floof do active: true ) - Floof.Distributor.register(self()) - Logger.info("Established connection to #{inspect(host)}:#{port}") + try do + Floof.Distributor.register(self()) + Logger.info("Established connection to #{inspect(host)}:#{port}") - if sesskey != nil do - {:ok, encd} = :FloofProtocol.encode(:ProtoMessage, {:session, {:attach, sesskey}}) - :ok = :gen_tcp.send(socket, encd) + if sesskey != nil do + {:ok, encd} = :FloofProtocol.encode(:ProtoMessage, {:session, {:attach, sesskey}}) + :ok = :gen_tcp.send(socket, encd) + end + + serve(socket, %{}, nil) + after + :ok = :gen_tcp.close(socket) end - - serve(socket, %{}, nil) - :ok = :gen_tcp.close(socket) end defp serve(client, backlog, sesskey) do - receive do - {:tcp, _, message} -> - message = - if :erlang.is_binary(message) do - message - else - :erlang.list_to_binary(message) + {backlog, sesskey} = + receive do + {:tcp, _, message} -> + message = + if :erlang.is_binary(message) do + message + else + :erlang.list_to_binary(message) + end + + {:ok, dcd} = :FloofProtocol.decode(:ProtoMessage, message) + backlog = handle_incoming(client, backlog, sesskey, dcd) + {backlog, sesskey} + + {:fwdxfer, {dcdhash, dcd}} -> + backlog = + if sesskey != nil do + # make sure that stuff gets handled uniformly + Floof.SessionManager.set(sesskey, dcdhash, dcd) + else + :ok = send_summary(client, :requestpull, [dcdhash]) + Map.put(backlog, dcdhash, dcd) + end + + {backlog, sesskey} + + {:SessionPushed, sesskey} -> + :ok = send_summary(client, :requestpull, Floof.SessionManager.peek(sesskey)) + {backlog, sesskey} + + {:SessionDetached, _} -> + {backlog, nil} + + {:tcp_closed, _} -> + # put all the backlogged stuff back if possible + if sesskey != nil do + Floof.SessionManager.set_multi(sesskey, backlog) + Floof.SessionManager.detach(sesskey, self()) end - {:ok, dcd} = :FloofProtocol.decode(:ProtoMessage, message) - backlog = handle_incoming(client, backlog, sesskey, dcd) - serve(client, backlog, sesskey) + :erlang.exit(:normal) - {:fwdxfer, {dcdhash, dcd}} -> - if sesskey != nil do - # make sure that stuff gets handled uniformly - Floof.SessionManager.set(sesskey, dcdhash, dcd) - serve(client, backlog, sesskey) - else - :ok = send_summary(client, :requestpull, [dcdhash]) - serve(client, Map.put(backlog, dcdhash, dcd), sesskey) - end + x -> + Logger.warn("unable to handle request: #{inspect(x)}") + {backlog, sesskey} + end - {:SessionPushed, sesskey} -> - :ok = send_summary(client, :requestpull, Floof.SessionManager.peek(sesskey)) - serve(client, backlog, sesskey) - - {:SessionDetached, _} -> - serve(client, backlog, nil) - - {:tcp_closed, _} -> - # put all the backlogged stuff back if possible - if sesskey != nil do - Floof.SessionManager.set_multi(sesskey, backlog) - Floof.SessionManager.detach(sesskey, self()) - end - - x -> - Logger.warn("unable to handle request: #{inspect(x)}") - serve(client, backlog, sesskey) - end + serve(client, backlog, sesskey) end defp handle_incoming(client, backlog, sesskey, dcd) do