Packages
smppex
0.0.3
3.3.0
3.2.4
3.2.3
3.2.2
3.2.1
3.2.0
retired
3.1.0
retired
3.0.6
3.0.5
3.0.4
3.0.3
3.0.2
3.0.1
3.0.0
2.4.0
2.3.3
2.3.2
2.3.1
2.3.0
2.2.9
2.2.8
2.2.7
2.2.6
2.2.5
2.2.4
2.2.3
2.2.2
2.2.1
2.2.0
2.1.0
2.0.1
2.0.0
1.0.1
0.3.2
0.3.1
0.3.0
0.2.1
0.2.0
0.1.12
0.1.11
0.1.10
0.1.9
0.1.8
0.1.7
0.1.6
0.1.5
0.1.4
0.1.3
0.1.2
0.1.1
0.1.0
0.0.5
0.0.4
0.0.3
0.0.2
0.0.1
SMPP 3.4 protocol and framework implemented in Elixir
Current section
Files
Jump to
Current section
Files
lib/smppex/session.ex
defmodule SMPPEX.Session do
@behaviour :ranch_protocol
use GenServer
require Logger
alias SMPPEX.Protocol, as: SMPP
alias SMPPEX.SMPPHandler
def send_pdu(pid, pdu) do
send_pdus(pid, [pdu])
end
def send_pdus(pid, pdus) do
GenServer.cast(pid, {:send_pdus, pdus})
end
def stop(pid) do
GenServer.cast(pid, :stop)
end
def start_link(ref, socket, transport, opts) do
:proc_lib.start_link(__MODULE__, :init, [ref, socket, transport, opts])
end
def init(ref, socket, transport, opts) do
session_factory = :proplists.get_value(:handler, opts)
case session_factory.(ref, socket, transport, self) do
{:ok, session} ->
:ok = :proc_lib.init_ack({:ok, self})
:ok = :ranch.accept_ack(ref)
state = %{
ref: ref,
socket: socket,
transport: transport,
session: session,
buffer: <<>>
}
wait_for_data(state)
SMPPHandler.after_init(session)
:gen_server.enter_loop(__MODULE__, [], state)
other ->
:ok = :proc_lib.init_ack({:error, other})
end
end
def wait_for_data(state) do
:ok = state.transport.setopts(state.socket, [{:active, :once}])
end
def handle_info(message, state) do
{ok, closed, error} = state.transport.messages
socket = state.socket
case message do
{^ok, ^socket, data} ->
handle_data(state, data)
{^closed, ^socket} ->
handle_socket_closed(state)
{^error, ^socket, reason} ->
handle_socket_error(state, reason)
other ->
Logger.info("Unrecognized message: #{inspect other}")
end
end
def handle_cast({:send_pdus, pdus}, state) do
{:noreply, do_send_pdus(state, pdus)}
end
def handle_cast(:stop, state) do
do_stop(state)
end
defp do_send_pdu(state, pdu) do
case SMPP.build(pdu) do
{:ok, binary} ->
state.transport.send(state.socket, binary)
error -> error
end
end
defp do_send_pdus(state, []), do: state
defp do_send_pdus(state, [pdu | pdus]) do
new_session = SMPPHandler.handle_send_pdu_result(state.session, pdu, do_send_pdu(state, pdu))
do_send_pdus(%{state | session: new_session}, pdus)
end
defp handle_data(state, data) do
full_data = state.buffer <> data
parse_pdus(state, full_data)
end
defp parse_pdus(state, data) do
case SMPP.parse(data) do
{:ok, nil, data} ->
new_state = %{state | buffer: data}
wait_for_data(state)
{:noreply, new_state}
{:ok, parse_result, rest_data} ->
handle_parse_result(state, parse_result, rest_data)
{:error, error} ->
handle_parse_error(state, error)
end
end
defp handle_parse_error(state, error) do
SMPPHandler.handle_parse_error(state.session, error)
do_stop(state)
end
defp handle_parse_result(state, parse_result, rest_data) do
case SMPPHandler.handle_pdu(state.session, parse_result) do
:ok ->
parse_pdus(state, rest_data)
{:ok, session} ->
parse_pdus(%{state | session: session}, rest_data)
{:ok, session, pdus} ->
new_state = do_send_pdus(%{ state | session: session }, pdus)
parse_pdus(new_state, rest_data)
{:stop, session, pdus} ->
new_state = do_send_pdus(%{ state | session: session }, pdus)
do_stop(new_state)
:stop ->
do_stop(state)
end
end
defp handle_socket_closed(state) do
SMPPHandler.handle_socket_closed(state.session)
do_stop(state)
end
defp handle_socket_error(state, reason) do
SMPPHandler.handle_socket_error(state.session, reason)
do_stop(state)
end
defp do_stop(state) do
_ = state.transport.close(state.socket)
SMPPHandler.handle_stop(state.session)
{:stop, :normal, state}
end
end