Packages
Wideact is an inter-lingual actor system, enabling different languages to communicate seamlessly over the network. It implements a very primitive protocol, capable of only transmiting messages to named actors. It also serves with semi-persistence, and will save messages for an actor until they ar...
Current section
Files
Jump to
Current section
Files
lib/wideact.ex
defmodule Wideact do
@moduledoc """
This is Wideact: an inter-lingual actor system.
"""
use Application
@doc """
Starts the application with arguments being an array containing only the port number (default: 40444).
"""
def start(_type, args) do
import Supervisor.Spec
Process.register(spawn(StaleMail, :start, []), :stalemail)
children = [
supervisor(Task.Supervisor, [[ name: Wideact.TaskSupervisor ]]),
worker(Task, [ Wideact, :accept, args ])
]
opts = [ strategy: :one_for_one, name: Wideact.Supervisor ]
Supervisor.start_link(children, opts)
end
@doc """
Opens a listener port and starts accepting connections.
"""
def accept(port) do
{ :ok, socket } = :gen_tcp.listen(port,
[ :binary, packet: :line, active: false, reuseaddr: true, send_timeout: 40 ])
loop_acceptor(socket)
end
@doc """
Loops and accepts clients, setting up the consumer connections in the process.
"""
defp loop_acceptor(socket) do
{ :ok, client } = :gen_tcp.accept(socket)
{ :ok, pid } = Task.Supervisor.start_child(Wideact.TaskSupervisor, fn -> setup(client) end)
:gen_tcp.controlling_process(client, pid)
loop_acceptor(socket)
end
@doc """
A receiving loop, checking the stale mail for messages.
"""
defp recvloop(client, name) do
receive do
{ :received_message, sender, message } ->
case :gen_tcp.send(client, "MESSAGE #{sender} #{message}\n") do
:ok ->
send :stalemail, { :got_mail, name }
{ :error, _reason } ->
exit(:normal)
end
_ -> exit(:normal)
after
10 -> send :stalemail, { :new_mail, name, self() }
end
recvloop(client, name)
end
@doc """
Sets the connection up. If the first message isn't a [ CONNECT name ] message, stop the client, because of protocol breach.
"""
defp setup(socket) do
case :gen_tcp.recv(socket, 0) do
{ :ok, data } ->
case data |> String.split(" ", parts: 2) do
[ "CONNECT", name ] ->
send :stalemail, { :new_mailbox, name, self() }
spawn(fn ->
recvloop(socket, name)
end)
serve(socket, name)
_ ->
:gen_tcp.close(socket)
exit(:normal)
end
_ ->
:gen_tcp.close(socket)
exit(:normal)
end
end
@doc """
Serve the content to the socket under a given name.
"""
defp serve(socket, name) do
case :gen_tcp.recv(socket, 0) do
{ :ok, data } ->
case data |> String.split(" ", parts: 3) do
[ "MESSAGE", receiver, message ] ->
send :stalemail, { :pass_message, receiver, message, name }
end
{ :error, _reason } -> nil
end
serve(socket, name)
end
end