erps v0.7.0 Erps.Server behaviour View Source
Create an Erps server GenServer.
An Erps server is just a GenServer that has its call/2
and cast/2
callbacks
connected to the external network over a transport portocol.
Basic Operation
Presuming you have set up TLS credentials, you can instantiate a server in basically the same way that you would instantiate a GenServer:
defmodule ErpsServer do
use Erps.Server
@port <...>
def start_link(data, opts) do
Erps.Server.start_link(__MODULE__, data, opts)
end
@impl true
def init(init_state), do: {:ok, init_state}
def handle_call(:some_remote_call, state) do
{:reply, :some_remote_response, state}
end
end
NB you must implement a start_link/2
in order to be properly
launched by Erps.Daemon
.
Now you may either access these values as a normal, local
GenServer
, or access them via Erps.Client
. The Erps.Server
module
will not know the difference between a local or a remote call.
{:ok, server} = ErpsServer.start_link()
GenServer.call(server, :some_remote_call) #==> :some_remote_response
{:ok, client} = ErpsClient.start_link()
GenServer.call(client, :some_remote_call) #==> :some_remote_response
Module Options
-
:identifier
a binary identifier for your Erps API endpoint. Maximum 12 bytes, suggested to be human-readable. -
:versions
client semvers which are accepted. See the "requirements" section inVersion
. -
:safe
(see:erlang.binary_to_term/2
), for decoding terms. If set tofalse
, then allows undefined atoms and lambdas to be passed via the protocol. This should be used with extreme caution, as disabling safe mode can be an attack vector. (defaults totrue
) -
:transport
- set the transport module, which must implementTransport
behaviour. If you set it tofalse
, the Erps server will useErps.Transport.None
and act similarly to aGenServer
(with some overhead).
Example
defmodule MyServer do
use Erps.Server, versions: "~> 0.2.4",
identifier: "my_api",
safe: false
def start_link(iv) do
Erps.Server.start_link(__MODULE__, init,
port: ,
tls_opts: [...])
end
def init(iv), do: {:ok, iv}
end
Magical features
The following functions are hoisted to your server module so that you can call them in your code with clarity and less boilerplate:
Link to this section Summary
Types
a from
term that is either a local from
, compatible with GenServer.from/0
or an opaque term that represents a connected remote client.
Functions
Returns a specification to start this module under a supervisor.
disconnects the server, causing it to close.
pushes a message to all connected clients. Causes client Erps.Client.handle_push/2
callbacks to be triggered.
sends a reply to either a local process or a remotely connected client.
starts a server GenServer, not linked to the caller. Most useful for tests.
starts a server GenServer, linked to the caller.
Callbacks
similar to GenServer.handle_call/3
, but handles content from both
local and remote clients.
similar to GenServer.handle_cast/2
, but handles content from both local
and remote clients (if the content has been successfully filtered).
Invoked when an internal callback requests a continuation, using {:noreply, state, {:continue, continuation}}
, or from init/1
using
{:ok, state, {:continue, continuation}}
Invoked to handle general messages sent to the client process.
Invoked to set up the process.
Link to this section Types
from()
View Source
(opaque)
from()
from()
a from
term that is either a local from
, compatible with GenServer.from/0
or an opaque term that represents a connected remote client.
This opaque term is completely compatible with the t:GenServer.from
type.
In other words, you may pass this term to Genserver.reply/2
from any process
and it will correctly route it to the caller whether it be local or remote.
server()
View Source
server() :: GenServer.server()
server() :: GenServer.server()
Link to this section Functions
child_spec(init_arg) View Source
Returns a specification to start this module under a supervisor.
See Supervisor
.
disconnect(server)
View Source
disconnect(server()) :: :ok
disconnect(server()) :: :ok
disconnects the server, causing it to close.
push(srv, push) View Source
pushes a message to all connected clients. Causes client Erps.Client.handle_push/2
callbacks to be triggered.
reply(from, reply)
View Source
reply(GenServer.from(), term()) :: :ok
reply(GenServer.from(), term()) :: :ok
sends a reply to either a local process or a remotely connected client.
The Erps Server holds connection information, so you must supply the Erps
Server pid; though you may use the arity-2 form if you call from within
the action loop of the Erps Server itself, in which case it acts like
GenServer.reply/2
naturally takes the from
value passed in to the second parameter of
handle_call/3
.
start(module, param, opts \\ [])
View Source
start(module(), term(), keyword()) :: GenServer.on_start()
start(module(), term(), keyword()) :: GenServer.on_start()
starts a server GenServer, not linked to the caller. Most useful for tests.
see start_link/3
for a description of avaliable options.
start_link(module, param, options! \\ [])
View Source
start_link(module(), term(), keyword()) :: GenServer.on_start()
start_link(module(), term(), keyword()) :: GenServer.on_start()
starts a server GenServer, linked to the caller.
options
-
:transport
transport module (seeTransport.Api
) -
:tls_opts
options for TLS authorization and encryption. Should include:-
:cacertfile
path to the certificate of your signing authority. -
:certfile
path to the server certificate file. -
:keyfile
path to the signing key. -
:customize_hostname_check
see below.
-
customize_hostname_check
Erlang/OTP doesn't provide a simple mechanism for a server in a two way ssl
connection to verify the identity of the client (which you may want in certain
situations where you own both ends of the connection but not the intermediate
transport layer). See Transport.Tls.handshake/2
and
:public_key.pkix_verify_hostname/3
for more details.
see GenServer.start_link/3
for a description of further options.
Link to this section Callbacks
handle_call(request, from, state)
View Source
(optional)
handle_call(request :: term(), from(), state :: term()) ::
{:reply, reply, new_state}
| {:reply, reply, new_state, timeout() | :hibernate | {:continue, term()}}
| {:noreply, new_state}
| {:noreply, new_state, timeout() | :hibernate | {:continue, term()}}
| {:stop, reason, reply, new_state}
| {:stop, reason, new_state}
when reply: term(), new_state: term(), reason: term()
handle_call(request :: term(), from(), state :: term()) :: {:reply, reply, new_state} | {:reply, reply, new_state, timeout() | :hibernate | {:continue, term()}} | {:noreply, new_state} | {:noreply, new_state, timeout() | :hibernate | {:continue, term()}} | {:stop, reason, reply, new_state} | {:stop, reason, new_state} when reply: term(), new_state: term(), reason: term()
similar to GenServer.handle_call/3
, but handles content from both
local and remote clients.
The from
term might contain a opaque term which represents a return
address for a remote client, but you may use this term as expected in
reply/2
.
Return codes
-
{:reply, reply, new_state}
replies, then updates the state of the Server. -
{:reply, reply, new_state, timeout}
replies, then causes a :timeout message to be sent tohandle_info/2
if no other message comes by -
{:reply, reply, new_state, :hibernate}
replies, then causes a hibernation event (see:erlang.hibernate/3
) -
{:reply, reply, new_state, {:continue, term}}
replies, then causes a continuation to be triggered after the message is handled, it will be sent toc:handle_continue/3
-
and all return codes supported by
GenServer.handle_cast/2
handle_cast(request, state) View Source (optional)
similar to GenServer.handle_cast/2
, but handles content from both local
and remote clients (if the content has been successfully filtered).
Return codes
-
{:noreply, new_state}
continues the loop with new statenew_state
-
{:noreply, new_state, timeout}
causes a :timeout message to be sent tohandle_info/2
if no other message comes by -
{:noreply, new_state, :hibernate}
, causes a hibernation event (see:erlang.hibernate/3
) -
{:noreply, new_state, {:continue, term}}
causes a continuation to be triggered after the message is handled, it will be sent toc:handle_continue/3
-
{:stop, reason, new_state}
terminates the loop, passingnew_state
toterminate/2
, if it's implemented.
handle_continue(continue, state) View Source (optional)
Invoked when an internal callback requests a continuation, using {:noreply, state, {:continue, continuation}}
, or from init/1
using
{:ok, state, {:continue, continuation}}
see: GenServer.handle_continue/2
.
Return codes
see return codes for handle_cast/2
handle_info(msg, state) View Source (optional)
Invoked to handle general messages sent to the client process.
Most useful if the client needs to be attentive to system messages, such as nodedown or monitored processes, but also useful for internal timeouts.
see: GenServer.handle_info/2
.
Return codes
see return codes for handle_cast/2
init(init_arg) View Source
Invoked to set up the process.
Like GenServer.init/1
, this function is called from inside
the process immediately after start_link/3
or start/3
.
Return codes
-
{:ok, state}
a succesful startup of your intialization logic and sets the internal state of your server tostate
. -
{:ok, state, timeout}
the above, plus a :timeout atom will be sent tohandle_info/2
if no other messages come by. -
{:ok, state, :hibernate}
successful startup, followed by a hibernation event (see:erlang.hibernate/3
) -
{:ok, state, {:continue, term}}
successful startup, and causes a continuation to be triggered after the message is handled, sent toc:handle_continue/3
-
:ignore
- Drop the gen_server creation request, because for some reason it shouldn't have started. -
{:stop, reason}
- a failure in creating the gen_server. Results in{:error, reason}
being propagated as the result of the start_link
terminate(reason, state) View Source (optional)
see: GenServer.terminate/2
.