Phoenix.Socket.Transport behaviour (Phoenix v1.6.0-rc.1) View Source
Outlines the Socket <-> Transport communication.
This module specifies a behaviour that all sockets must implement.
Phoenix.Socket
is just one possible implementation of a socket
that multiplexes events over multiple channels. Developers can
implement their own sockets as long as they implement the behaviour
outlined here.
Developers interested in implementing custom transports must invoke the socket API defined in this module. This module also provides many conveniences that invokes the underlying socket API to make it easier to build custom transports.
Booting sockets
Whenever your endpoint starts, it will automatically invoke the
child_spec/1
on each listed socket and start that specification
under the endpoint supervisor.
Since the socket supervision tree is started by the endpoint, any custom transport must be started after the endpoint in a supervision tree.
Operating sockets
Sockets are operated by a transport. When a transport is defined, it usually receives a socket module and the module will be invoked when certain events happen at the transport level.
Whenever the transport receives a new connection, it should invoke
the connect/1
callback with a map of metadata. Different sockets
may require different metadatas.
If the connection is accepted, the transport can move the connection
to another process, if so desires, or keep using the same process. The
process responsible for managing the socket should then call init/1
.
For each message received from the client, the transport must call
handle_in/2
on the socket. For each informational message the
transport receives, it should call handle_info/2
on the socket.
Transports can optionally implement handle_control/2
for handling
control frames such as :ping
and :pong
.
On termination, terminate/2
must be called. A special atom with
reason :closed
can be used to specify that the client terminated
the connection.
Example
Here is a simple echo socket implementation:
defmodule EchoSocket do
@behaviour Phoenix.Socket.Transport
def child_spec(opts) do
# We won't spawn any process, so let's return a dummy task
%{id: Task, start: {Task, :start_link, [fn -> :ok end]}, restart: :transient}
end
def connect(state) do
# Callback to retrieve relevant data from the connection.
# The map contains options, params, transport and endpoint keys.
{:ok, state}
end
def init(state) do
# Now we are effectively inside the process that maintains the socket.
{:ok, state}
end
def handle_in({text, _opts}, state) do
{:reply, :ok, {:text, text}, state}
end
def handle_info(_, state) do
{:ok, state}
end
def terminate(_reason, _state) do
:ok
end
end
It can be mounted in your endpoint like any other socket:
socket "/socket", EchoSocket, websocket: true, longpoll: true
You can now interact with the socket under /socket/websocket
and /socket/longpoll
.
Security
This module also provides functions to enable a secure environment
on transports that, at some point, have access to a Plug.Conn
.
The functionality provided by this module helps in performing "origin" header checks and ensuring only SSL connections are allowed.
Link to this section Summary
Callbacks
Returns a child specification for socket management.
Connects to the socket.
Handles incoming control frames.
Handles incoming socket messages.
Handles info messages.
Initializes the socket state.
Invoked on termination.
Functions
Checks the origin request header against the list of allowed origins.
Checks the Websocket subprotocols request header against the allowed subprotocols.
Runs the code reloader if enabled.
Extracts connection information from conn
and returns a map.
Forces SSL in the socket connection.
Logs the transport request.
Link to this section Types
Specs
state() :: term()
Link to this section Callbacks
Specs
child_spec(keyword()) :: :supervisor.child_spec()
Returns a child specification for socket management.
This is invoked only once per socket regardless of the number of transports and should be responsible for setting up any process structure used exclusively by the socket regardless of transports.
Each socket connection is started by the transport
and the process that controls the socket likely
belongs to the transport. However, some sockets spawn
new processes, such as Phoenix.Socket
which spawns
channels, and this gives the ability to start a
supervision tree associated to the socket.
It receives the socket options from the endpoint, for example:
socket "/my_app", MyApp.Socket, shutdown: 5000
means child_spec([shutdown: 5000])
will be invoked.
Specs
Connects to the socket.
The transport passes a map of metadata and the socket
returns {:ok, state}
or :error
. The state must be
stored by the transport and returned in all future
operations.
This function is used for authorization purposes and it may be invoked outside of the process that effectively runs the socket.
In the default Phoenix.Socket
implementation, the
metadata expects the following keys:
:endpoint
- the application endpoint:transport
- the transport name:params
- the connection parameters:options
- a keyword list of transport options, often given by developers when configuring the transport. It must include a:serializer
field with the list of serializers and their requirements
Specs
handle_control({message :: term(), opts :: keyword()}, state()) :: {:ok, state()} | {:reply, :ok | :error, {opcode :: atom(), message :: term()}, state()} | {:stop, reason :: term(), state()}
Handles incoming control frames.
The message is represented as {payload, options}
. It must
return one of:
{:ok, state}
- continues the socket with no reply{:reply, status, reply, state}
- continues the socket with reply{:stop, reason, state}
- stops the socket
Control frames only supported when using websockets.
The options
contains an opcode
key, this will be either :ping
or
:pong
.
If a control frame doesn't have a payload, then the payload value
will be nil
.
Specs
handle_in({message :: term(), opts :: keyword()}, state()) :: {:ok, state()} | {:reply, :ok | :error, {opcode :: atom(), message :: term()}, state()} | {:stop, reason :: term(), state()}
Handles incoming socket messages.
The message is represented as {payload, options}
. It must
return one of:
{:ok, state}
- continues the socket with no reply{:reply, status, reply, state}
- continues the socket with reply{:stop, reason, state}
- stops the socket
The reply
is a tuple contain an opcode
atom and a message that can
be any term. The built-in websocket transport supports both :text
and
:binary
opcode and the message must be always iodata. Long polling only
supports text opcode.
Specs
handle_info(message :: term(), state()) :: {:ok, state()} | {:push, {opcode :: atom(), message :: term()}, state()} | {:stop, reason :: term(), state()}
Handles info messages.
The message is a term. It must return one of:
{:ok, state}
- continues the socket with no reply{:push, reply, state}
- continues the socket with reply{:stop, reason, state}
- stops the socket
The reply
is a tuple contain an opcode
atom and a message that can
be any term. The built-in websocket transport supports both :text
and
:binary
opcode and the message must be always iodata. Long polling only
supports text opcode.
Specs
Initializes the socket state.
This must be executed from the process that will effectively operate the socket.
Specs
Invoked on termination.
If reason
is :closed
, it means the client closed the socket. This is
considered a :normal
exit signal, so linked process will not automatically
exit. See Process.exit/2
for more details on exit signals.
Link to this section Functions
check_origin(conn, handler, endpoint, opts, sender \\ &Plug.Conn.send_resp/1)
View SourceChecks the origin request header against the list of allowed origins.
Should be called by transports before connecting when appropriate. If the origin header matches the allowed origins, no origin header was sent or no origin was configured, it will return the given connection.
Otherwise a 403 Forbidden response will be sent and the connection halted. It is a noop if the connection has been halted.
Checks the Websocket subprotocols request header against the allowed subprotocols.
Should be called by transports before connecting when appropriate. If the sec-websocket-protocol header matches the allowed subprotocols, it will put sec-websocket-protocol response header and return the given connection. If no sec-websocket-protocol header was sent it will return the given connection.
Otherwise a 403 Forbidden response will be sent and the connection halted. It is a noop if the connection has been halted.
Runs the code reloader if enabled.
Extracts connection information from conn
and returns a map.
Keys are retrieved from the optional transport option :connect_info
.
This functionality is transport specific. Please refer to your transports'
documentation for more information.
The supported keys are:
:peer_data
- the result ofPlug.Conn.get_peer_data/1
:trace_context_headers
- a list of all trace context headers:x_headers
- a list of all request headers that have an "x-" prefix:uri
- a%URI{}
derived from the conn:user_agent
- the value of the "user-agent" request header
Forces SSL in the socket connection.
Uses the endpoint configuration to decide so. It is a noop if the connection has been halted.
Logs the transport request.
Available for transports that generate a connection.