erps v0.7.0 Erps.Client behaviour View Source
Create an Erps client GenServer.
The best way to think of an Erps client is that it is a GenServer that forwards
its call/2
and cast/2
callbacks to a remote GenServer over a LAN or WAN.
This callbacks would normally be provided by standard GenServer.call/2
and
GenServer.cast/2
semantics over erlang distribution but sometimes you just
don't want that (see Erps
).
Basic operation
Presuming you have set up an Erps server GenServer on some host at @hostname
,
you can connect the client and the server simply by instantiating the server
module.
Example
defmodule ErpsClient do
use Erps.Client
@hostname <...>
@port <...>
def start_link, do: Erps.Client.start_link(__MODULE__, :ok,
server: @hostname, port: @port, tls_opts: [...])
def init(init_state), do: {:ok, init_state}
end
{:ok, client} = ErpsClient.start_link
GenServer.call(client, :some_remote_call)
# => :some_remote_response
Module options
-
:version
the version of your Erps API messages. Should be a SemVer string. seeVersion
for more information. -
:identifier
(optional) a binary identifier for your Erps API endpoint. Maximum 36 bytes, suggested to be human-readable. This must match the identifier on the server in order for there to be a successful connection. -
: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
)
Example
defmodule MyClient do
use Erps.Client, version: "0.2.4",
identifier: "my_api",
safe: false
def start_link(iv) do
Erps.Client.start_link(__MODULE__, init,
server: "my_api-server.example.com",
port: 4747,
transport: Transport.Tls,
tls_opts: [...])
end
def init(iv), do: {:ok, iv}
end
Important Notes
- Calls are non-blocking for the Erps clients (and can be for the servers, if you so implement them). You may issue multiple, asynchronous calls across the network from different processes and they will be routed correctly and will only interfere with each other in terms of the connection arbitration overhead.
-
Calls reply immediately with the atom
:disconnected
if the erps client is not currently connected to its server. -
handle_continue/2
is currently not supported. This is a limitation in theConnection
library.
Link to this section Summary
Functions
triggers a connection.
triggers a disconnection.
returns the current socket in use by the server.
starts a client GenServer, not linked to the caller. Most useful for tests.
starts a client GenServer, linked to the caller.
Callbacks
called when a connection has been successfully established.
Invoked to handle general messages sent to the client process.
Invoked to handle Erps.Server.push/2
messages.
Invoked to set up the process.
Invoked when the client is about to exit.
Link to this section Types
noreply_response() View Source
reply_cache()
View Source
reply_cache() :: %{optional(non_neg_integer()) => reply_ref()}
reply_cache() :: %{optional(non_neg_integer()) => reply_ref()}
reply_ref()
View Source
reply_ref() :: %{from: GenServer.from(), ttl: DateTime.t()}
reply_ref() :: %{from: GenServer.from(), ttl: DateTime.t()}
Link to this section Functions
connect(server)
View Source
connect(GenServer.server()) :: :ok
connect(GenServer.server()) :: :ok
triggers a connection.
Only use this function after using disconnect/1
disconnect(server)
View Source
disconnect(GenServer.server()) :: :ok
disconnect(GenServer.server()) :: :ok
triggers a disconnection.
Useful to silence persistent Erps clients during maintenance phases. Note: this might cause upstream errors as consumers of the Erps service will emit errors on calls and casts.
socket(server)
View Source
socket(GenServer.server()) :: Transport.socket()
socket(GenServer.server()) :: Transport.socket()
returns the current socket in use by the server.
This may be a TCP socket or an SSL socket, or another interface depending on what transport strategy you're using.
start(module, state, opts) View Source
starts a client GenServer, not linked to the caller. Most useful for tests.
see start_link/3
for a description of avaliable options.
start_link(module, state, options!) View Source
starts a client GenServer, linked to the caller.
Will attempt to contact the server over the specified transport strategy. If the connection fails, the client will be placed in an invalid state until connection succeeds, with a reconnect interval specified in the module options.
options
-
:server
IP address of the target server (required) -
:port
IP port of the target server (required) -
:transport
module for communication transport strategy -
:keepalive
time interval for sending a TCP/IP keepalive token. -
:tls_opts
options for setting up a TLS connection.-
:cacertfile
path to the certificate of your signing authority. (required) -
:certfile
path to the server certificate file. (required forTransport.Tls
) -
:keyfile
path to the signing key. (required forTransport.Tls
) -
:customize_hostname_check
it's very likely that you might get tls failures if you are relying on the OTP builtin hostname checks. This OTP ssl feature lets you override it for something custom. See:ssl.client_option/0
-
-
:reply_ttl
the maximum amount of time that client should wait forcall
replies. Units in ms, defaults to5000
. -
forward_callers: true
causes the client to adopt the universe of the caller. seeMultiverses
for details
see GenServer.start_link/3
for a description of further options.
Link to this section Callbacks
handle_connect(socket, state)
View Source
(optional)
handle_connect(socket :: Transport.socket(), state :: term()) ::
noreply_response()
handle_connect(socket :: Transport.socket(), state :: term()) :: noreply_response()
called when a connection has been successfully established.
Return codes
-
{:ok, state}
update the state of the connection. -
{:error, reason}
stop the connection, with reasonreason
.
handle_info(msg, state)
View Source
(optional)
handle_info(msg :: :timeout | term(), state :: term()) :: noreply_response()
handle_info(msg :: :timeout | term(), state :: term()) :: noreply_response()
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_push/2
handle_push(push, state)
View Source
(optional)
handle_push(push :: term(), state :: term()) :: noreply_response()
handle_push(push :: term(), state :: term()) :: noreply_response()
Invoked to handle Erps.Server.push/2
messages.
push
is the push message sent by a Erps.Server.push/2
and state
is the
current state of the Erps.Client
.
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
) -
{:disconnect, reason, new_state}
, causes a disconnection. -
{:stop, reason, new_state}
terminates the loop, passingnew_state
toterminate/2
, if it's implemented.
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
) -
: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)
Invoked when the client is about to exit.
This would usually occur due to handle_push/2
returning a
{:stop, reason, new_state}
tuple, but also if the TCP connection
happens to go down.