smppex v0.3.1 SMPPEX.ESME behaviour

Module for implementing custom SMPP ESME entities.

SMPPEX.ESME represents a GenServer process which spawns and interacts with SMPPEX.Session ranch_protocol. The session is spawned under control of ranch supervision tree. The session makes all requests to the ESME process syncronously (via GenServer.call), while the ESME process makes only asyncronous(via GenServer.cast) requests to the session.

This is made intentionally since this allows:

  • to avoid any kind of deadlocks while the session and the ESME process interact actively;
  • to control incoming SMPP message rate to avoid overflooding;
  • not to lose any control over connection because of the asyncronous nature of TCP implementation in OTP.

To implement an ESME entitiy, one should implement several callbacks (SMPPEX.ESME behaviour). The most proper way to do it is to use SMPPEX.ESME:

defmodule MyESME do
  use SMPPEX.ESME

  # ...Callback implementation

end

In this case all callbacks have reasonable defaults.

Summary

Functions

Makes a syncronous call to ESME

Makes an asyncronous call to ESME

Sends reply to previously received PDU from the ESME

Sends outcoming PDU from the ESME

Stops ESME asyncronously

Asyncronously executes the passed lambda passing SMPP session(SMPPEX.Session) to it directly

Callbacks

Invoked for handling call/3 calls

Invoked for handling cast/2 calls

Invoked for handling generic messages sent to the ESME process

Invoked when ESME was stopped abnormally and wasn’t able to handle sent PDUs without resps in handle_stop

Invoked when the ESME receives an incoming PDU (which is not a response PDU)

Invoked when the ESME receives a response to a previously sent PDU

Invoked when the ESME does not receive a response to a previously sent PDU for the specified timeout

Invoked when the SMPP session successfully sent PDU to transport or failed to do this

Invoked when the SMPP session is about to stop

Invoked when the ESME is started after connection to SMSC successfully established

Types

request :: term
send_pdu_result :: :ok | {:error, term}
state :: term

Functions

call(esme, request, timeout \\ 5000)

Specs

call(esme :: pid, arg :: term, timeout) :: term

Makes a syncronous call to ESME.

The call is handled by handle_call/3 ESME callback.

cast(esme, request)

Specs

cast(pid, term) :: :ok

Makes an asyncronous call to ESME.

The call is handled by handle_cast/2 ESME callback.

reply(esme, pdu, reply_pdu)

Specs

reply(esme :: pid, pdu :: SMPPEX.Pdu.t, reply_pdu :: SMPPEX.Pdu.t) :: :ok

Sends reply to previously received PDU from the ESME.

The whole command is sent to the ESME asyncronously. The further lifecycle of the response PDU can be traced through callbacks.

send_pdu(esme, pdu)

Specs

send_pdu(esme :: pid, pdu :: SMPPEX.Pdu.t) :: :ok

Sends outcoming PDU from the ESME.

The whole command is sent to the ESME asyncronously. The further lifecycle of the PDU can be traced through callbacks.

start_link(host, port, arg, opts \\ [])

Specs

start_link(host :: term, port :: non_neg_integer, {module, args :: term}, opts :: Keyword.t) :: GenServer.on_start

Starts ESME entitiy.

The function does not return until ESME successfully connects to the specified host and port and initializes or fails.

module is the callback module which should implement SMPPEX.ESME behaviour. args is the argument passed to the init callback. opts is a keyword list of different options:

  • :transport is Ranch transport used for TCP connection: either ranch_tcp (the default) or ranch_ssl;
  • :gen_server_opts is a list of options passed directly to the underlying GenServer.start_link call, the default is [];
  • :timeout is timeout for the whole connect and initialization process. The default is 5000 ms;
  • :esme_opts is a keyword list of ESME options:

    • :timer_resolution is interval of internal ticks on which time related events happen, like checking timeouts for pdus, checking SMPP timers, etc. The default is 100 ms;
    • :enquire_link_limit is value for enquire_link SMPP timer, i.e. the interval of SMPP session inactivity after which enquire_link PDU is send to “ping” the connetion. The default value is 30000 ms;
    • :enquire_link_resp_limit is the maximum time for which ESME waits for enquire_link PDU response. If the response is not received within this interval of time and no activity from the peer occurs, the session is then considered dead and the ESME stops. The default value is 30000 ms;
    • :inactivity_limit is the maximum time for which the peer is allowed not to send PDUs (which are not response PDUs). If no such PDUs are received within this interval of time, ESME stops. The default is infinity ms;
    • :response_limit is the maximum time to wait for a response for a previously sent PDU. If the response is not received within this interval, handle_resp_timeout callback is triggered for the original pdu. If the response is received later, it is discarded. The default value is 60000 ms. If :esme_opts list of options is ommited, all options take their default values.

The whole opts argument may also be ommited in order to start ESME with the defaults.

The returned value is either {:ok, pid} or {:error, reason}.

stop(esme)

Specs

stop(esme :: pid) :: :ok

Stops ESME asyncronously.

The very moment of the SMPP session termination can be traced via handle_stop callback.

with_session(esme, fun)

Specs

with_session(esme :: pid, (smpp_session :: pid -> any)) :: :ok

Asyncronously executes the passed lambda passing SMPP session(SMPPEX.Session) to it directly.

This function can be used for uncommon cases like sending PDUs bypassing timers or sequence_number assignment.

Callbacks

handle_call(request, from, state)

Specs

handle_call(request, from :: GenServer.from, state) ::
  {:reply, reply :: term, state} |
  {:noreply, state}

Invoked for handling call/3 calls.

The callback is called syncronously for handling.

The returned values have the same meaning as in GenServer handle_call callback (but note that only two kinds of responses are possible). In case of delaying a reply ({:noreply, state} callback result) it can be later send using GenServer.reply(from, reply)

handle_cast(request, state)

Specs

handle_cast(request, state) :: state

Invoked for handling cast/2 calls.

The callback is called asyncronously.

The returned value is used as the new state.

handle_info(request, state)

Specs

handle_info(request, state) :: state

Invoked for handling generic messages sent to the ESME process.

The returned value is used as the new state.

handle_lost_pdus(esme, reason, pdus)

Specs

handle_lost_pdus(esme :: pid, reason :: term, pdus :: [SMPPEX.Pdu.t]) :: any

Invoked when ESME was stopped abnormally and wasn’t able to handle sent PDUs without resps in handle_stop.

Since the ESME is already stopped, the callback does not receive state, but the former pid of ESME, the reason of its termination and unconfirmed PDUs.

The returned value is ignored.

handle_pdu(pdu, state)

Specs

handle_pdu(pdu :: SMPPEX.Pdu.t, state) :: state

Invoked when the ESME receives an incoming PDU (which is not a response PDU).

The returned value is used as the new state.

handle_resp(pdu, original_pdu, state)

Specs

handle_resp(pdu :: SMPPEX.Pdu.t, original_pdu :: SMPPEX.Pdu.t, state) :: state

Invoked when the ESME receives a response to a previously sent PDU.

pdu argument contains the received response PDU, original_pdu contains the previously sent pdu for which the handled response is received.

The returned value is used as the new state.

handle_resp_timeout(pdu, state)

Specs

handle_resp_timeout(pdu :: SMPPEX.Pdu.t, state) :: state

Invoked when the ESME does not receive a response to a previously sent PDU for the specified timeout.

pdu argument contains the PDU for which no response was received. If the response will be received later it will be dropped (with an info log message).

The returned value is used as the new state.

handle_send_pdu_result(pdu, send_pdu_result, state)

Specs

handle_send_pdu_result(pdu :: SMPPEX.Pdu.t, send_pdu_result :: SMPPEX.SMPPHandler.send_pdu_result, state) :: state

Invoked when the SMPP session successfully sent PDU to transport or failed to do this.

pdu argument contains the PDU for which send status is reported. send_pdu_result can be either :ok or {:error, reason}.

The returned value is used as the new state.

handle_stop(reason, lost_pdus, state)

Specs

handle_stop(reason :: term, lost_pdus :: [SMPPEX.Pdu.t], state) :: {exit_reason :: term, state}

Invoked when the SMPP session is about to stop.

lost_pdus contains sent PDUs which have not received resps (and will never receive since the session terminates).

reason contains one of the following:

  • :custom — session manually stopped by call to ESME.stop;
  • {:parse_error, error} — error in parsing incoming SMPP packet occured;
  • :socket_closed — peer closed socket;
  • {:socket_error, error} — socket error occured;
  • {:timers, reason} — session closed by timers.

The return value is {stop_reason, new_state}. The session GenServer will stop with stop_reason.

init(args)

Specs

init(args :: term) ::
  {:ok, state} |
  {:stop, reason :: term}

Invoked when the ESME is started after connection to SMSC successfully established.

args argument is taken directly from start_link call, which does not return until init finishes. The return value should be either {:ok, state}, then ESME will successfully start and returned state will be later passed to the other callbacks, or {:stop, reason}, then ESME GenServer will stop with the returned reason.