View Source Minch behaviour (Minch v0.1.0)
A WebSocket client build around Mint.WebSocket
.
Usage
Supervised client
defmodule EchoClient do
use Minch
require Logger
def start_link(init_arg) do
Minch.start_link(__MODULE__, init_arg, name: __MODULE__)
end
@impl true
def init(_init_arg) do
{:ok, %{connected?: false}}
end
@impl true
def connect(_state) do
url = "wss://ws.postman-echo.com/raw"
headers = [{"authorization", "bearer: example"}]
# don't do this in production
options = [transport_opts: [{:verify, :verify_none}]]
{url, headers, options}
end
@impl true
def handle_connect(state) do
Logger.info("connected")
Process.send_after(self(), :produce, 5000)
{:reply, {:text, "welcome"}, %{state | connected?: true}}
end
@impl true
def handle_disconnect(reason, state) do
Logger.warning("disconnected: #{inspect(reason)}")
{:reconnect, 1000, %{state | connected?: false}}
end
@impl true
def handle_info(:produce, state) do
Process.send_after(self(), :produce, 5000)
{:reply, {:text, DateTime.utc_now() |> DateTime.to_iso8601()}, state}
end
@impl true
def handle_frame(frame, state) do
Logger.info(inspect(frame))
{:ok, state}
end
end
Simple client
url = "wss://ws.postman-echo.com/raw"
headers = []
# don't do this in production
options = [transport_opts: [{:verify, :verify_none}]]
IO.puts("checking ping to #{url}...")
case Minch.connect(url, headers, options) do
{:ok, pid, ref} ->
Minch.send_frame(pid, {:text, to_string(System.monotonic_time())})
case Minch.receive_frame(ref, 5000) do
{:text, start} ->
ping =
System.convert_time_unit(
System.monotonic_time() - String.to_integer(start),
:native,
:millisecond
)
IO.puts("#{ping}ms")
:timeout ->
IO.puts("timeout")
end
Minch.close(pid)
{:error, error} ->
IO.puts("connection error: #{inspect(error)}")
end
Summary
Callbacks
Invoked to retrieve the connection details.
Invoked to handle a successful connection.
Invoked to handle a disconnect from the server or a failed connection attempt.
Invoked to handle an incoming WebSocket frame.
Invoked to handle all other messages.
Invoked when the client process is started.
Invoked when the client process is about to exit.
Types
@type client() :: GenServer.server()
Callbacks
@callback connect(state :: term()) :: url | {url, headers} | {url, headers, options} when url: String.t() | URI.t(), headers: Mint.Types.headers(), options: Keyword.t()
Invoked to retrieve the connection details.
See Minch.Conn.open/3
.
@callback handle_connect(state :: term()) :: {:ok, new_state} | {:reply, frame :: Mint.WebSocket.frame(), new_state} when new_state: term()
Invoked to handle a successful connection.
@callback handle_disconnect(reason :: term(), state :: term()) :: {:ok, new_state} | {:reconnect, backoff :: pos_integer(), new_state} when new_state: term()
Invoked to handle a disconnect from the server or a failed connection attempt.
Returning {:reconnect, backoff, state}
will schedule a reconnect after backoff
milliseconds.
Returning {:ok, state}
will keep the client in the disconnected state. Later you can instruct
the client to reconnect by sending it a message and returning {:reconnect, state}
from the
handle_info/2
callback.
@callback handle_frame(frame :: Mint.WebSocket.frame(), state :: term()) :: {:ok, new_state} | {:reply, frame :: Mint.WebSocket.frame(), new_state} when new_state: term()
Invoked to handle an incoming WebSocket frame.
A "close" frame will cause the connection to be closed but you'll still receive the frame.
Other than that, Minch does not intercept nor handle control frames automatically.
@callback handle_info(msg :: :timeout | term(), state :: term()) :: {:ok, new_state} | {:reply, frame :: Mint.WebSocket.frame(), new_state} | {:reconnect, new_state} when new_state: term()
Invoked to handle all other messages.
Invoked when the client process is started.
@callback terminate(reason, state :: term()) :: term() when reason: :normal | :shutdown | {:shutdown, term()} | term()
Invoked when the client process is about to exit.
Functions
@spec close(pid()) :: :ok
Closes a connection opened by connect/3
.
@spec connect(String.t() | URI.t(), Mint.Types.headers(), Keyword.t()) :: {:ok, pid(), Mint.Types.request_ref()} | {:error, Mint.WebSocket.error()}
Connects to a WebSocket server.
Once connected, the function will return the connection's PID and the request reference.
Use the send_frame/2
function to send WebSocket frames to the server.
Incoming WebSocket frames will be sent to the caller's mailbox as a tuple {:frame, request_ref, frame}
.
You can use the receive_frame/2
function to receive these frames.
Use Process.monitor/1
to get notified when the connection is closed by the server.
Example
iex> {:ok, pid, ref} = Minch.connect("wss://ws.postman-echo.com/raw", [], transport_opts: [{:verify, :verify_none}])
{:ok, _pid, _ref}
iex> Minch.send_frame(pid, {:text, "hello"})
:ok
iex> Minch.receive_frame(ref, 5000)
{:text, "hello"}
iex> Minch.close(pid)
:ok
@spec receive_frame(Mint.Types.request_ref(), timeout()) :: Mint.WebSocket.frame() | :timeout
Receives an incoming WebSocket frame.
See connect/2
.
@spec send_frame(client(), Mint.WebSocket.frame() | Mint.WebSocket.shorthand_frame()) :: :ok | {:error, Mint.WebSocket.error()}
Sends a WebSocket frame.
@spec start_link(module(), any(), GenServer.options()) :: GenServer.on_start()
Starts a Minch
client process linked to the current process.