defmodule Floof do @moduledoc """ Documentation for `Floof`. """ require Logger def accept(port) do {:ok, socket} = :gen_tcp.listen(port, [ :binary, packet: 4, active: false, reuseaddr: true ]) Logger.info("Accepting connections on #{port}") loop_acceptor(socket) end defp loop_acceptor(socket) do {:ok, client} = :gen_tcp.accept(socket) {:ok, pid} = Task.Supervisor.start_child( Floof.TaskSupervisor, fn -> serve(client, []) end) :ok = :gen_tcp.controlling_process(client, pid) # switch to active now that we handed over the client :inet.setopts(client, [{:active, true}]) send(Floof.Distributor, {:register, pid}) loop_acceptor(socket) end defp serve(client, backlog) do receive do {:tcp, _, message} -> {:ok, dcd} = :FloofProtocol.decode(:ProtoMessage, message) backlog = handle_incoming(client, backlog, dcd) serve(client, backlog) {:fwdxfer, dcdmessage} -> dcdid = get_xfer_id dcdmessage handle_outgoing(client, dcdid) serve(client, [{dcdid, dcdmessage} | backlog]) {:havewe, id, res} -> dat = {:Summary, if res do :rejectpush else :pull end, id} {:ok, encd} = :FloofProtocol.encode(:ProtoMessage, {:summary, dat}) :ok = client.send(encd) serve(client, backlog) {:SessionPushed, key} -> {backlog, added_ids} = extract_backlog(key, backlog, []) for id <- added_ids do dat = {:Summary, :requestpull, id} {:ok, encd} = :FloofProtocol.encode(:ProtoMessage, {:summary, dat}) :ok = client.send(encd) end serve(client, backlog) x -> Logger.warn("unable to handle request: #{inspect(x)}") serve(client, backlog) end end defp handle_incoming(client, backlog, dcd) do Logger.debug("got packet #{inspect(dcd)}") case dcd do {:session, {:SessionModify, {:attach, key}}} -> if Floof.SessionManager.attach(key, self()) do send self(), {:SessionPushed, key} backlog else backlog end {:summary, {:Summary, direction, id}} -> case direction do :requestpull -> send Floof.Distributor, {:havewe, self(), id} backlog :pull -> case List.keytake(backlog, id, 0) do {{_, item}, backlog2} -> {:ok, encd} = :FloofProtocol.encode(:ProtoMessage, {:xfer, item}) :ok = client.send(encd) backlog2 nil -> backlog end :rejectpush -> List.keydelete(backlog, id, 0) end {:xfer, xf} -> send Floof.Distributor, xf backlog end end defp handle_outgoing(client, dcdid) do dat = {:Summary, :requestpull, dcdid} {:ok, encd} = :FloofProtocol.encode(:ProtoMessage, {:summary, dat}) :ok = client.send(encd) end def get_xfer_id({:XferBlob, _, _, _, _, _, xfinner}) do {:ok, {:XferInner, id, _, _, _, _}} = :FloofProtocol.decode(:XferInner, xfinner) id end defp extract_backlog(key, backlog, added_ids) do case Floof.SessionManager.pop(key) do :empty -> {backlog, added_ids} {:value, {id, entry}} -> extract_backlog(key, [{id, entry} | backlog], [id | added_ids]) end end end