Packages
sippet
0.2.3
1.0.16
1.0.15
1.0.14
1.0.13
1.0.12
1.0.11
1.0.10
1.0.9
1.0.8
1.0.7
1.0.6
1.0.5
1.0.4
1.0.3
1.0.2
1.0.1
1.0.0
0.6.4
0.6.3
0.6.2
0.6.1
0.6.0
0.5.9
0.5.8
0.5.7
0.5.6
0.5.5
0.5.4
0.5.3
0.5.2
0.5.1
0.5.0
0.4.9
0.4.8
0.4.7
0.4.6
0.4.5
0.4.4
0.4.3
0.4.2
0.4.1
0.4.0
0.3.9
0.3.8
0.3.7
0.3.6
0.3.5
0.3.4
0.3.3
0.3.2
0.3.1
0.3.0
0.2.9
0.2.8
0.2.7
0.2.6
0.2.5
0.2.4
0.2.3
0.2.2
0.2.1
0.2.0
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
An Elixir Session Initiation Protocol (SIP) stack.
Current section
Files
Jump to
Current section
Files
lib/sippet/transactions/server.ex
defmodule Sippet.Transactions.Server do
alias Sippet.Message, as: Message
alias Sippet.Message.RequestLine, as: RequestLine
alias Sippet.Message.StatusLine, as: StatusLine
alias Sippet.Transactions.Server.State, as: State
@type request :: %Message{start_line: %RequestLine{}}
@type response :: %Message{start_line: %StatusLine{}}
@type reason :: atom | any
@type branch :: binary
@type method :: atom | binary
@type host :: binary
@type dport :: integer
@type t :: %__MODULE__{
branch: branch,
method: method,
sent_by: {host, dport}
}
@type transaction :: t | GenServer.server
defstruct [
branch: nil,
method: nil,
sent_by: nil
]
@doc """
Create a server transaction identifier explicitly.
"""
@spec new(branch, method, {host, dport}) :: t
def new(branch, method, sent_by) do
%__MODULE__{
branch: branch,
method: if(method == :ack, do: :invite, else: method),
sent_by: sent_by
}
end
@doc """
Create a server transaction identifier from an incoming `request` or an
outgoing `response`. If they are related, they will be equal.
"""
@spec new(request | response) :: t
def new(%Message{start_line: %RequestLine{}} = incoming_request) do
method = incoming_request.start_line.method
# Take the topmost via branch
{_version, _protocol, sent_by, %{"branch" => branch}} =
hd(incoming_request.headers.via)
new(branch, method, sent_by)
end
def new(%Message{start_line: %StatusLine{}} = outgoing_response) do
{_sequence, method} = outgoing_response.headers.cseq
# Take the topmost via sent-by and branch
{_version, _protocol, sent_by, %{"branch" => branch}} =
hd(outgoing_response.headers.via)
new(branch, method, sent_by)
end
@doc false
@spec receive_request(GenServer.server, request) :: :ok
def receive_request(server, %Message{start_line: %RequestLine{}} = request),
do: GenStateMachine.cast(server, {:incoming_request, request})
@doc false
@spec send_response(GenServer.server, response) :: :ok
def send_response(server, %Message{start_line: %StatusLine{}} = response),
do: GenStateMachine.cast(server, {:outgoing_response, response})
@doc false
@spec receive_error(GenServer.server, reason) :: :ok
def receive_error(server, reason),
do: GenStateMachine.cast(server, {:error, reason})
defmacro __using__(opts) do
quote location: :keep do
use GenStateMachine, callback_mode: [:state_functions, :state_enter]
alias Sippet.Transactions.Server.State, as: State
require Logger
def init(%State{} = data) do
Logger.info("server transaction #{data.name} started")
initial_state = unquote(opts)[:initial_state]
{:ok, initial_state, data}
end
defp send_response(response, %State{name: name} = data) do
extras = data.extras |> Map.put(:last_response, response)
data = %{data | extras: extras}
Sippet.Transports.send_message(response, name)
data
end
defp receive_request(request, %State{name: name}),
do: Sippet.Core.receive_request(request, name)
def shutdown(reason, %State{name: name} = data) do
Logger.warn("server transaction #{name} shutdown: #{reason}")
Sippet.Core.receive_error(reason, name)
{:stop, :shutdown, data}
end
def timeout(data),
do: shutdown(:timeout, data)
defdelegate reliable?(request), to: Sippet.Transports
def unhandled_event(event_type, event_content,
%State{name: name} = data) do
Logger.error("server transaction #{name} got " <>
"unhandled_event/3: #{inspect event_type}, " <>
"#{inspect event_content}, #{inspect data}")
{:stop, :shutdown, data}
end
end
end
end
defimpl String.Chars, for: Sippet.Transactions.Server do
def to_string(%Sippet.Transactions.Server{} = transaction) do
"#{transaction.branch}/#{transaction.method}/" <>
"#{transaction.sent_by |> elem(0)}:#{transaction.sent_by |> elem(1)}"
end
end