defmodule Frajtano.Peer do alias Frajtano.Proto require Logger use GenServer, restart: :temporary def start(path) do DynamicSupervisor.start_child(Frajtano.Peer, {__MODULE__, path}) end def start_link(path) do GenServer.start_link(__MODULE__, path) end @impl true def init(path) do {:ok, conn} = :gen_tcp.connect({:local, path}, 0, [:binary, active: :once]) {:ok, %{conn: conn, clients: :queue.new(), buffer: <<>>}} end @impl true def handle_call(packet, from, %{conn: conn, clients: clients} = state) do case :gen_tcp.send(conn, Proto.encode(packet)) do :ok -> {:noreply, %{state | clients: :queue.in(from, clients)}} {:error, :closed} -> {:noreply, state, {:continue, :closed}} {:error, e} -> raise(e) end end defp handle_messages(%{clients: clients, buffer: buffer} = state) do case Proto.decode(buffer) do {nil, buffer} -> {:noreply, %{state | buffer: buffer}} {msg, buffer} -> {{:value, client}, clients} = :queue.out(clients) GenServer.reply(client, {:ok, msg}) handle_messages(%{state | clients: clients, buffer: buffer}) end end @impl true def handle_info({:tcp, conn, msg}, %{conn: conn, buffer: buffer} = state) do :inet.setopts(conn, active: :once) buffer = buffer <> msg handle_messages(%{ state | buffer: buffer }) end @impl true def handle_info({:tcp_closed, _}, state) do {:noreply, state, {:continue, :closed}} end @impl true def handle_continue(:closed, %{clients: clients}) do clients |> :queue.to_list() |> Enum.each(&GenServer.reply(&1, {:error, :closed})) {:stop, :closed, %{}} end def identities(peer) do with {:ok, {:agent_identities_answer, identities}} <- GenServer.call(peer, {:agentc_request_identities, nil}), do: {:ok, identities} end def sign(peer, request) do with {:ok, {:agent_sign_response, signature}} <- GenServer.call(peer, {:agentc_sign_request, request}), do: {:ok, signature} end end