Packages
phoenix
1.1.3
1.8.9
1.8.8
1.8.7
1.8.6
1.8.5
1.8.4
1.8.3
1.8.2
1.8.1
1.8.0
1.8.0-rc.4
1.8.0-rc.3
1.8.0-rc.2
1.8.0-rc.1
1.8.0-rc.0
1.7.24
1.7.23
1.7.22
1.7.21
1.7.20
1.7.19
1.7.18
1.7.17
1.7.16
1.7.15
1.7.14
1.7.13
1.7.12
1.7.11
1.7.10
1.7.9
1.7.8
1.7.7
1.7.6
1.7.5
1.7.4
1.7.3
1.7.2
1.7.1
1.7.0
1.7.0-rc.3
1.7.0-rc.2
1.7.0-rc.1
1.7.0-rc.0
1.6.17
1.6.16
1.6.15
1.6.14
1.6.13
1.6.12
1.6.11
1.6.10
1.6.9
1.6.8
1.6.7
1.6.6
1.6.5
1.6.4
1.6.3
1.6.2
1.6.1
1.6.0
1.6.0-rc.1
1.6.0-rc.0
1.5.15
1.5.14
1.5.13
1.5.12
1.5.11
1.5.10
1.5.9
1.5.8
1.5.7
1.5.6
1.5.5
1.5.4
1.5.3
1.5.2
1.5.1
1.5.0
1.5.0-rc.0
1.4.18
1.4.17
1.4.16
1.4.15
1.4.14
1.4.13
1.4.12
1.4.11
1.4.10
1.4.9
1.4.8
1.4.7
1.4.6
1.4.5
1.4.4
1.4.3
1.4.2
1.4.1
1.4.0
1.4.0-rc.3
1.4.0-rc.2
1.4.0-rc.1
1.4.0-rc.0
1.3.5
1.3.4
1.3.3
1.3.2
1.3.1
1.3.0
1.3.0-rc.3
1.3.0-rc.2
1.3.0-rc.1
1.3.0-rc.0
1.2.5
1.2.4
1.2.3
1.2.2
1.2.1
1.2.0
1.2.0-rc.1
1.2.0-rc.0
1.1.9
1.1.8
1.1.7
1.1.6
1.1.5
1.1.4
1.1.3
1.1.2
1.1.1
1.1.0
1.0.6
1.0.5
1.0.4
1.0.3
1.0.2
1.0.1
1.0.0
0.17.1
0.17.0
0.16.1
0.16.0
0.15.0
0.14.0
0.13.1
0.13.0
0.12.0
0.11.0
0.10.0
0.9.0
0.8.0
0.7.2
0.7.1
0.7.0
0.6.2
0.6.1
0.6.0
0.5.0
0.4.1
0.4.0
0.3.1
0.3.0
0.2.11
0.2.10
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.0
Productive. Reliable. Fast. A productive web framework that does not compromise speed or maintainability.
Security advisory:
This version has known vulnerabilities.
View advisories
Current section
Files
Jump to
Current section
Files
lib/phoenix/socket.ex
defmodule Phoenix.Socket do
@moduledoc ~S"""
Defines a socket and its state.
`Phoenix.Socket` is used as a module for establishing and maintaining
the socket state via the `Phoenix.Socket` struct.
Once connected to a socket, incoming and pubsub events are routed
to channels. The incoming client data is routed to channels via transports.
It is the responsibility of the socket to tie transports and channels
together.
By default, Phoenix supports both websockets and longpoll transports.
For example:
transport :websocket, Phoenix.Transports.WebSocket
The command above means incoming socket connections can be done via
the WebSocket transport. Events are routed by topic to channels:
channel "rooms:lobby", MyApp.LobbyChannel
See `Phoenix.Channel` for more information on channels. Check each
transport module to check the options specific to each transport.
## Socket Behaviour
Socket handlers are mounted in Endpoints and must define two callbacks:
* `connect/2` - receives the socket params and authenticates the connection.
Must return a `Phoenix.Socket` struct, often with custom assigns.
* `id/1` - receives the socket returned by `connect/2` and returns the
id of this connection as a string. The `id` is used to identify socket
connections, often to a particular user, allowing us to force disconnections.
For sockets requiring no authentication, `nil` can be returned.
## Examples
defmodule MyApp.UserSocket do
use Phoenix.Socket
transport :websocket, Phoenix.Transports.WebSocket
channel "rooms:*", MyApp.RoomChannel
def connect(params, socket) do
{:ok, assign(socket, :user_id, params["user_id"])}
end
def id(socket), do: "users_socket:#{socket.assigns.user_id}"
end
# Disconnect all user's socket connections and their multiplexed channels
MyApp.Endpoint.broadcast("users_socket:" <> user.id, "disconnect", %{})
## Socket Fields
* `id` - The string id of the socket
* `assigns` - The map of socket assigns, default: `%{}`
* `channel` - The current channel module
* `channel_pid` - The channel pid
* `endpoint` - The endpoint module where this socket originated, for example: `MyApp.Endpoint`
* `handler` - The socket module where this socket originated, for example: `MyApp.UserSocket`
* `joined` - If the socket has effectively joined the channel
* `pubsub_server` - The registered name of the socket's pubsub server
* `ref` - The latest ref sent by the client
* `topic` - The string topic, for example `"rooms:123"`
* `transport` - The socket's transport, for example: `Phoenix.Transports.WebSocket`
* `transport_pid` - The pid of the socket's transport process
* `transport_name` - The socket's transport, for example: `:websocket`
* `serializer` - The serializer for socket messages,
for example: `Phoenix.Transports.WebSocketSerializer`
## Custom transports
See the `Phoenix.Socket.Transport` documentation for more information on
writing your own transports.
"""
use Behaviour
alias Phoenix.Socket
@doc """
Receives the socket params and authenticates the connection.
## Socket params and assigns
Socket params are passed from the client and can
be used to verify and authenticate a user. After
verification, you can put default assigns into
the socket that will be set for all channels, ie
{:ok, assign(socket, :user_id, verified_user_id)}
To deny connection, return `:error`.
See `Phoenix.Token` documentation for examples in
performing token verification on connect.
"""
defcallback connect(params :: map, Socket.t) :: {:ok, Socket.t} | :error
@doc ~S"""
Identifies the socket connection.
Socket id's are topics that allow you to identify all sockets for a given user:
def id(socket), do: "users_socket:#{socket.assigns.user_id}"
Would allow you to broadcast a "disconnect" event and terminate
all active sockets and channels for a given user:
MyApp.Endpoint.broadcast("users_socket:" <> user.id, "disconnect", %{})
Returning `nil` makes this socket anonymous.
"""
defcallback id(Socket.t) :: String.t | nil
defmodule InvalidMessageError do
@moduledoc """
Raised when the socket message is invalid.
"""
defexception [:message]
end
@type t :: %Socket{id: nil,
assigns: %{},
channel: atom,
channel_pid: pid,
endpoint: atom,
handler: atom,
joined: boolean,
pubsub_server: atom,
ref: term,
topic: String.t,
transport: atom,
transport_name: atom,
serializer: atom,
transport_pid: pid}
defstruct id: nil,
assigns: %{},
channel: nil,
channel_pid: nil,
endpoint: nil,
handler: nil,
joined: false,
pubsub_server: nil,
ref: nil,
topic: nil,
transport: nil,
transport_pid: nil,
transport_name: nil,
serializer: nil
defmacro __using__(_) do
quote do
@behaviour Phoenix.Socket
import unquote(__MODULE__)
Module.register_attribute(__MODULE__, :phoenix_channels, accumulate: true)
@phoenix_transports %{}
@before_compile unquote(__MODULE__)
end
end
defmacro __before_compile__(env) do
transports = Module.get_attribute(env.module, :phoenix_transports)
channels = Module.get_attribute(env.module, :phoenix_channels)
transport_defs =
for {name, {mod, conf}} <- transports do
quote do
def __transport__(unquote(name)) do
{unquote(mod), unquote(conf)}
end
end
end
channel_defs =
for {topic_pattern, module, opts} <- channels do
topic_pattern
|> to_topic_match
|> defchannel(module, opts[:via])
end
quote do
def __transports__, do: unquote(Macro.escape(transports))
unquote(transport_defs)
unquote(channel_defs)
def __channel__(_topic, _transport), do: nil
end
end
defp to_topic_match(topic_pattern) do
case String.split(topic_pattern, "*") do
[prefix, ""] -> quote do: <<unquote(prefix) <> _rest>>
[bare_topic] -> bare_topic
_ -> raise ArgumentError, "channels using splat patterns must end with *"
end
end
defp defchannel(topic_match, channel_module, nil) do
quote do
def __channel__(unquote(topic_match), _transport), do: unquote(channel_module)
end
end
defp defchannel(topic_match, channel_module, transports) do
quote do
def __channel__(unquote(topic_match), transport)
when transport in unquote(List.wrap(transports)), do: unquote(channel_module)
end
end
@doc """
Adds key/value pair to socket assigns.
## Examples
iex> socket.assigns[:token]
nil
iex> socket = assign(socket, :token, "bar")
iex> socket.assigns[:token]
"bar"
"""
def assign(socket = %Socket{}, key, value) do
update_in socket.assigns, &Map.put(&1, key, value)
end
@doc """
Defines a channel matching the given topic and transports.
* `topic_pattern` - The string pattern, for example "rooms:*", "users:*", "system"
* `module` - The channel module handler, for example `MyApp.RoomChannel`
* `opts` - The optional list of options, see below
## Options
* `:via` - the transport adapters to accept on this channel.
Defaults `[:websocket, :longpoll]`
## Examples
channel "topic1:*", MyChannel
channel "topic2:*", MyChannel, via: [:websocket]
channel "topic", MyChannel, via: [:longpoll]
## Topic Patterns
The `channel` macro accepts topic patterns in two flavors. A splat argument
can be provided as the last character to indicate a "topic:subtopic" match. If
a plain string is provied, only that topic will match the channel handler.
Most use-cases will use the "topic:*" pattern to allow more versatile topic
scoping.
See `Phoenix.Channel` for more information
"""
defmacro channel(topic_pattern, module, opts \\ []) do
# Tear the alias to simply store the root in the AST.
# This will make Elixir unable to track the dependency
# between endpoint <-> socket and avoid recompiling the
# endpoint (alongside the whole project ) whenever the
# socket changes.
module = tear_alias(module)
quote do
@phoenix_channels {unquote(topic_pattern), unquote(module), unquote(opts)}
end
end
defp tear_alias({:__aliases__, meta, [h|t]}) do
alias = {:__aliases__, meta, [h]}
quote do
Module.concat([unquote(alias)|unquote(t)])
end
end
defp tear_alias(other), do: other
@doc """
Defines a transport with configuration.
## Examples
# customize default `:websocket` transport options
transport :websocket, Phoenix.Transports.WebSocket,
timeout: 10_000
# define separate transport, using websocket handler
transport :websocket_slow_clients, Phoenix.Transports.WebSocket,
timeout: 60_000
"""
defmacro transport(name, module, config \\ []) do
quote do
@phoenix_transports Phoenix.Socket.__transport__(
@phoenix_transports, unquote(name), unquote(module), unquote(config))
end
end
@doc false
def __transport__(transports, name, module, config) do
config = Keyword.merge(module.default_config() , config)
Map.update(transports, name, {module, config}, fn {dup_module, _} ->
raise ArgumentError,
"duplicate transports (#{inspect dup_module} and #{inspect module}) defined for #{inspect name}."
end)
end
end