reagent - socket acceptor pool
==============================
**reagent** is a socket acceptor pool for Elixir that leverages the
[socket](https://github.com/meh/elixir-socket) library and its protocols to
provide an easy way to implement servers.
Getting started
---------------
To define a reagent you first have to define a module using the reagent
behaviour. This will define some basic functions you can extend and other
helpers on the module and will make it startable as a reagent.
```elixir
defmodule Test do
use Reagent.Behaviour
end
```
When you want to start a server running the defined reagent, you have to call
`Reagent.start`. It takes as first parameter the module implementing the
behaviour and as second parameter a listener descriptor.
Listener descriptors contain the definition of the listener, including port,
whether they're secure or not, other socket options and starting environment.
Reagent behaviour
-----------------
A reagent to do anything useful has to either implement `handle/1` or `start/1`.
`handle/1` is called by the default `start/1` and it gets called as a
replacement for the acceptor process. It gets called with a
`Reagent.Connection` record.
This is usually useful to implement simple protocols when you don't need a full
blown `gen_server` or similar to handle a connection.
If you want more complex connection handling you can define `start/1`, it gets
called with a `Reagent.Connection` record as well and must return `{ :ok, pid
}` or `{ :error, reason }`. The returned process will be made owner of the
socket and be used as reference for the connection itself.
You can also define `accept/1` which gets called with the `Reagent.Listener`
and allows you more fine grained socket acception.
Simple example
--------------
```elixir
defmodule Echo do
use Reagent.Behaviour
def handle(conn) do
case conn |> Socket.Stream.recv! do
nil ->
:closed
data ->
conn |> Socket.Stream.send! data
handle(conn)
end
end
end
```
This is a simple implementation of an echo server.
To start it on port 8080 just run `Reagent.start Echo, port: 8080`.
Complex example
---------------
```elixir
defmodule Echo do
use Reagent.Behaviour
def start(connection) do
:gen_server.start __MODULE__, connection, []
end
use GenServer.Behaviour
def init(connection) do
{ :ok, connection }
end
# this message is sent when the socket has been completely accepted and the
# process has been made owner of the socket, you don't need to wait for it
# when implementing handle because it's internally handled
def handle_info({ Reagent, :ack }, connection) do
connection |> Socket.active!
{ :noreply, connection }
end
def handle_info({ :tcp, _, data }, connection) do
connection |> Socket.Stream.send! data
{ :noreply, connection }
end
def handle_info({ :tcp_closed, _ }, _connection) do
{ :stop, :normal, _connection }
end
end
```
This is the implementation of a full-blown `gen_server` based echo server
(which is obviously overkill).
As with the simple example you just start it with `Reagent.start Echo, port:
8080`.