So, I’m building an SMTP client that I’d like to be able to be called from different processes and have different state for each process. The SMTP client needs to track for example if the HELO command has been used or not. How do I handle state?
My current code is this:
defmodule Client do
@moduledoc """
Documentation for `Smtpclient`.
"""
use Agent
def start_link(_opts) do
Agent.start_link(fn -> %{} end, name: __MODULE__)
end
def connect(pid, tld) do
set_value(pid, "tld", tld)
{:ok, records} = DNS.resolve(tld, :mx)
Enum.sort_by(records, fn(r) -> elem(r, 0) end)
sock = do_connect(records, 0)
set_socket(pid, sock)
set_value(pid, "hostname", tld)
{:ok, sock}
end
@spec helo(any) :: any
def helo(pid) do
already_helloed = get_value(pid, "already_helloed")
if already_helloed do # if already helloed return early
{:error, "already helloed"}
end
sock = get_socket(pid)
tld = get_value(pid, "tld")
sock |> Socket.Stream.send!("HELO #{tld}")
set_value(pid, "already_helloed", true)
sock |> Socket.Stream.recv!
end
def noop(pid) do
try do
sock = get_socket(pid)
sock |> Socket.Stream.send!("NOOP")
sock |> Socket.Stream.recv!
rescue
Socker.Error -> {:error, :no_connection}
end
end
def quit(sock) do
try do
sock |> Socket.Stream.send!("QUIT")
sock |> Socket.Stream.recv!
sock |> Socket.close!()
rescue
Socket.Error -> {:error, "something is wrong"}
end
end
def rctp_to(pid, email) do
try do
sock = get_socket(pid)
sock |> Socket.Stream.send!("RCPT_TO #{email}")
sock |> Socket.Stream.recv!
rescue
Socker.Error -> {:error, "something is wrong"}
end
end
defp set_socket(pid, sock) do
Agent.update(pid, &Map.put(&1, "socket", sock))
end
defp get_socket(pid) do
Agent.get(pid, &Map.get(&1, "socket"))
end
defp set_value(pid, key, value) do
Agent.update(pid, &Map.put(&1, key, value))
end
defp get_value(pid, key) do
Agent.get(pid, &Map.get(&1, key))
end
defp do_connect(hostnames, i) do
try do
host = elem(Enum.at(hostnames, i), 1)
Socket.TCP.connect!(to_string(host), 25, packet: :line)
rescue
Socket.Error ->
if Enum.at(hostnames, i) == nil do
{:error, "all mx servers down"}
end
do_connect(hostnames, i + 1)
end
end
end