Behaviour for XTurn data-plane plugins.
What problem this solves
A TURN relay forwards media between a client and remote peers. Operators sometimes need to inspect or meter that traffic (abuse guard, QoS counters) without forking the server. Plugins implement this behaviour; xturn attaches them per allocation and calls them on each relayed frame.
This package is only the contract (Plugin, Allocation, Frame). Concrete
plugins live in packages such as
xturn-plugins.
Modes
:active-handle_frame/3may return{:ok, binary},:drop, or{:error, term}. Called concurrently from many processes; keep mutable state outside the returnedstate(for example ETS).:passive-handle_frame/3returns{:ok, new_state}and runs in a dedicated instance process (safe for timers viahandle_info/2).
Main modules
Xirsys.XTurn.Plugin- this behaviourXirsys.XTurn.Plugin.Allocation- context at attach/init timeXirsys.XTurn.Plugin.Frame- per-datagram metadata forhandle_frame/3
RFCs
Plugins sit on the TURN data path (Send Indication, ChannelData, Data Indication). They do not replace STUN/TURN signaling.
Summary
Callbacks
Called once per allocation, at allocation time.
Allocation teardown. Flush and close here. Once only.
Per-frame callback.
Optional message handler for passive plugins.
Which directions this plugin wants. Never called for others.
Called once per attached allocation.
Fixed for the lifetime of the module: :active or :passive.
Types
@type direction() :: :egress | :ingress
Relay direction relative to the TURN client.
:egress- client toward peer (Send Indication / ChannelData outbound):ingress- peer toward client (Data Indication / ChannelData inbound)
@type framing() :: :send_indication | :channel_data | :data_indication
How the payload was carried on the TURN wire.
:send_indication- TURN Send Indication:channel_data- TURN ChannelData:data_indication- TURN Data Indication
Callbacks
@callback attach?( Xirsys.XTurn.Plugin.Allocation.t(), keyword() ) :: boolean()
Called once per allocation, at allocation time.
Return false and no instance is created and nothing is added to this
allocation's chain.
Allocation teardown. Flush and close here. Once only.
@callback handle_frame( payload :: binary(), Xirsys.XTurn.Plugin.Frame.t(), state :: term() ) :: {:ok, binary()} | :drop | {:error, term()} | {:ok, new_state :: term()}
Per-frame callback.
:active- must return{:ok, binary}|:drop|{:error, term}.stateis the immutable value frominit/2; mutable state must be kept by the plugin itself (its own ETS table or process), because this is called concurrently from many processes.:passive- must return{:ok, new_state}. Runs in the instance process.
Optional message handler for passive plugins.
A passive plugin's self() is the instance process, so
Process.send_after(self(), ...) from init/2 or handle_frame/3 is the
supported way to schedule periodic work.
@callback hooks() :: [direction()]
Which directions this plugin wants. Never called for others.
@callback init( Xirsys.XTurn.Plugin.Allocation.t(), keyword() ) :: {:ok, state :: term()} | :ignore
Called once per attached allocation.
For :passive plugins this runs inside the instance process. Returning
:ignore aborts attachment.
@callback mode() :: :active | :passive
Fixed for the lifetime of the module: :active or :passive.