summaryrefslogtreecommitdiff
path: root/lib/irc/connection_socket.ex
diff options
context:
space:
mode:
Diffstat (limited to 'lib/irc/connection_socket.ex')
-rw-r--r--lib/irc/connection_socket.ex100
1 files changed, 100 insertions, 0 deletions
diff --git a/lib/irc/connection_socket.ex b/lib/irc/connection_socket.ex
new file mode 100644
index 0000000..29c42c0
--- /dev/null
+++ b/lib/irc/connection_socket.ex
@@ -0,0 +1,100 @@
+defmodule Irc.ConnectionSocket do
+ use GenServer
+ require Logger
+ alias Irc.Parser.Line
+
+ import Kernel, except: [send: 2]
+
+ @moduledoc "Underlying wrapper that just parses IRC lines"
+
+ @type error :: :todo
+
+ @type t :: {ConnectionSocket, pid(), reference()}
+
+ @spec connect(tls :: boolean, host :: String.t, port :: Integer.t, options :: Keyword.t) :: {:ok, t} | {:error, error}
+ def connect(tls, host, port, options) do
+ caller = self()
+ case GenServer.start(__MODULE__, [caller, tls, host, port, options]) do
+ {:ok, pid} ->
+ mon = Process.monitor(pid)
+ {:ok, {__MODULE__, pid, mon}}
+ error -> error
+ end
+ end
+
+ @spec close(t) :: :ok
+ def close({__MODULE__, pid, mon}) do
+ case GenServer.call(pid, :close) do
+ :ok ->
+ Process.demonitor(mon, [:flush])
+ error ->
+ error
+ end
+ end
+
+ @spec send(t, iolist) :: :ok
+ def send({__MODULE__, pid, _}, line) do
+ GenServer.call(pid, {:send, line})
+ end
+
+ @doc false
+ def init([caller, tls, host, port, options]) do
+ Process.monitor(caller)
+ module = if tls, do: :ssl, else: :gen_tcp
+ state = %{caller: caller, module: module, host: host, port: port, options: options, socket: nil}
+ case module.connect(String.to_charlist(host), port, options) do
+ {:ok, socket} ->
+ {:ok, Map.put(state, :socket, socket)}
+ error ->
+ {:error, error}
+ end
+ end
+
+ @doc false
+ def handle_call({:send, data}, {caller, _}, state = %{caller: caller, module: module, socket: socket}) do
+ {:reply, module.send(socket, data), state}
+ end
+
+ def handle_call(:close, {caller, _}, state = %{caller: caller, module: module, socket: socket}) do
+ {:stop, :normal, module.close(socket), state = %{state | module: nil, socket: nil}}
+ end
+
+ def handle_call(_, {caller, _}, state = %{caller: caller}) do
+ {:reply, {:error, :invalid_call}, state}
+ end
+
+ def handle_call(_, _, state) do
+ {:noreply, state}
+ end
+
+ @doc false
+ # Received a line from socket
+ def handle_info({module, socket, line}, state = %{caller: caller, module: module, socket: socket}) do
+ Kernel.send(caller, {__MODULE__, self(), Line.parse(line)})
+ {:noreply, state}
+ end
+
+ # Socket is closed
+ def handle_info({closed, socket}, state = %{socket: socket, caller: caller}) when closed in [:tcp_closed, :ssl_closed] do
+ Kernel.send(caller, {__MODULE__, self(), :closed})
+ {:stop, :normal, state}
+ end
+
+ # Socket error
+ # TODO: SSL errors
+ def handle_info({:tcp_error, socket, reason}, state = %{socket: socket, caller: caller}) do
+ Kernel.send(caller, {__MODULE__, self(), {:error, reason}})
+ {:stop, :normal, state}
+ end
+
+ # Caller is down
+ def handle_info({:DOWN, _, :process, caller, reason}, state = %{caller: caller, module: module, socket: socket}) do
+ module.close(socket)
+ {:stop, :normal, state}
+ end
+
+ def terminate(reason, state) do
+ state
+ end
+
+end