View Source Fresh behaviour (Fresh v0.4.4)

This module provides a high-level interface for managing WebSocket connections.

It simplifies implementing WebSocket clients, allowing you to easily establish and manage connections with WebSocket servers.

Usage

To use this module, follow these steps:

  1. Use the use Fresh macro in your WebSocket client module to automatically configure the necessary callbacks and functionality:

    defmodule MyWebSocketClient do
      use Fresh
    
      # ...callbacks and functionalities.
    end
  2. Implement callback functions to handle connection events, received data frames, and more, as specified in the documentation.

  3. Start WebSocket connections using start_link/1 or start/1 with the desired configuration options:

    MyWebSocketClient.start_link(uri: "wss://example.com/socket", state: %{}, opts: [
      name: {:local, :my_connection}
    ])

How to Supervise

For effective management of WebSocket connections, consider supervising your WebSocket client processes. You can add your WebSocket client module as a child to a supervisor, allowing the supervisor to monitor and restart the WebSocket client process in case of failures.

children = [
  {MyWebSocketClient,
   uri: "wss://example.com/socket",
   state: %{},
   opts: [
     name: {:local, :my_connection}
   ]}

  # ...other child specifications
]

Supervisor.start_link(children, strategy: :one_for_one)

Reconnection

In scenarios where the WebSocket connection is lost or encounters an error, you can configure reconnection behaviour using handle_disconnect/3 and handle_error/2 callbacks. Depending on your requirements, you can implement logic to automatically reconnect to the server or take other appropriate actions.

Automatic Reconnection and Backoff

Fresh uses exponential backoff (with a fixed factor of 1.5) strategy for reconnection attempts. This means that after a connection loss, it waits for a brief interval before attempting to reconnect, gradually increasing the time between reconnection attempts until a maximum limit is reached.

The exponential backoff strategy helps prevent overwhelming the server with rapid reconnection attempts and allows for a more graceful recovery when the server is temporarily unavailable.

The default backoff parameters are as follows:

  • Initial Backoff Interval: 250 milliseconds
  • Maximum Backoff Interval: 30 seconds

You can customize these parameters by including them in your WebSocket client's configuration, as shown in the "Example Configuration" section of the option/0 documentation.

Summary

Types

Represents the response for all connection handle callbacks.

Represents control frames in a WebSocket connection.

Represents data frames in a WebSocket connection.

Represents various error scenarios that can occur during WebSocket communication.

Represents the response of a generic callback and enables you to manage the state.

Represents optional configurations for WebSocket connections. Available options include

Represents the state of the given module, which can be anything.

Callbacks

Callback invoked when a WebSocket connection is successfully established.

Callback invoked when a control frame is received from the server.

Callback invoked when the WebSocket connection is being disconnected.

Callback invoked when an error is encountered during WebSocket communication, allowing you to define custom error handling logic for various scenarios.

Callback invoked when a data frame is received from the server.

Callback invoked when an incomprehensible message is received.

Callback invoked when the WebSocket process is about to terminate.

Functions

This macro simplifies the implementation of WebSocket client.

Sends a WebSocket close frame to the server.

Checks if the connection is available.

Sends a WebSocket frame to the server.

Starts a WebSocket connection without linking the process.

Starts a WebSocket connection and links the process.

Types

Link to this type

connection_handle_response()

View Source
@type connection_handle_response() ::
  {:ignore, state()}
  | {:reconnect, initial :: state()}
  | {:close, reason :: term()}
  | :reconnect
  | :close

Represents the response for all connection handle callbacks.

@type control_frame() :: {:ping, binary()} | {:pong, binary()}

Represents control frames in a WebSocket connection.

@type data_frame() :: {:text, String.t()} | {:binary, binary()}

Represents data frames in a WebSocket connection.

@type error() ::
  {:connecting_failed, Mint.Types.error()}
  | {:upgrading_failed, Mint.WebSocket.error()}
  | {:streaming_failed, Mint.Types.error()}
  | {:establishing_failed, Mint.WebSocket.error()}
  | {:processing_failed, term()}
  | {:decoding_failed, any()}
  | {:encoding_failed, any()}
  | {:casting_failed, Mint.WebSocket.error()}

Represents various error scenarios that can occur during WebSocket communication.

Link to this type

generic_handle_response()

View Source
@type generic_handle_response() ::
  {:ok, state()}
  | {:reply, Mint.WebSocket.frame() | [Mint.WebSocket.frame()], state()}
  | {:close, code :: non_neg_integer(), reason :: binary(), state()}

Represents the response of a generic callback and enables you to manage the state.

@type option() ::
  {:name, :gen_statem.server_name()}
  | {:headers, Mint.Types.headers()}
  | {:transport_opts, keyword()}
  | {:mint_upgrade_opts, keyword()}
  | {:ping_interval, non_neg_integer()}
  | {:error_logging, boolean()}
  | {:info_logging, boolean()}
  | {:backoff_initial, non_neg_integer()}
  | {:backoff_max, non_neg_integer()}
  | {:hibernate_after, timeout()}

Represents optional configurations for WebSocket connections. Available options include:

  • :name - Registers a name for the WebSocket connection, allowing you to refer to it later using a name.
  • :headers - Specifies a list of headers to include in the WebSocket connection request. These headers will be sent during the connection upgrade.
  • :transport_opts - Additional options to pass to the transport layer used for the WebSocket connection. Consult the Mint.HTTP documentation for more informations.
  • :mint_upgrade_opts - Extra options to provide to Mint.WebSocket during the WebSocket upgrade process. Consult the Mint.WebSocket documentation for additional information.
  • :ping_interval - This option is used for keeping the WebSocket connection alive by sending empty ping frames at regular intervals, specified in milliseconds. The default value is 30000 (30 seconds). To disable ping frames, set this option to 0.
  • :error_logging - Allows toggling logging for error messages. Enabled by default.
  • :info_logging - Allows toggling logging for informational messages. Enabled by default.
  • :backoff_initial - Specifies the initial backoff time, in milliseconds, used between reconnection attempts. The default value is 250 (250 milliseconds).
  • :backoff_max - Sets the maximum time interval, in milliseconds, used between reconnection attempts. The default value is 30000 (30 seconds).
  • :hibernate_after - Specifies a timeout value, in milliseconds, which the WebSocket connection process will enter hibernation if there is no activity. Hibernation is disabled by default.

Example Configuration

[
  name: {:local, Example.Connection},
  headers: [{"Authorization", "Bearer token"}],
  ping_interval: 60_000,
  error_logging: false,
  backoff_initial: 5_000,
  backoff_max: 60_000,
  hibernate_after: 600_000
]
@type state() :: any()

Represents the state of the given module, which can be anything.

Callbacks

Link to this callback

handle_connect(status, headers, state)

View Source
@callback handle_connect(status, headers, state()) :: generic_handle_response()
when status: Mint.Types.status(), headers: Mint.Types.headers()

Callback invoked when a WebSocket connection is successfully established.

Parameters

  • status - The status received during the connection upgrade.
  • headers - The headers received during the connection upgrade.
  • state - The current state of the module.

Example

def handle_connect(_status, _headers, state) do
  payload = "connection up!"
  {:reply, [{:text, payload}], state}
end
Link to this callback

handle_control(frame, state)

View Source
@callback handle_control(frame :: control_frame(), state()) :: generic_handle_response()

Callback invoked when a control frame is received from the server.

Parameters

  • frame - The received WebSocket frame, which is a control frame.
  • state - The current state of the module.

Example

def handle_control({:ping, message}, state) do
  IO.puts("Received ping with content: #{message}!")
  {:ok, state}
end

def handle_control({:pong, message}, state) do
  IO.puts("Received pong with content: #{message}!")
  {:ok, state}
end
Link to this callback

handle_disconnect(code, reason, state)

View Source
@callback handle_disconnect(code, reason, state()) :: connection_handle_response()
when code: non_neg_integer() | nil, reason: binary() | nil

Callback invoked when the WebSocket connection is being disconnected.

Parameters

  • code (optional) - The disconnection code, if available. It should be a non-negative integer.
  • reason (optional) - The reason for the disconnection, if available. It should be a binary.
  • state - The current state of the module.

Example

def handle_disconnect(1002, _reason, _state), do: :reconnect
def handle_disconnect(_code, _reason, _state), do: :close
Link to this callback

handle_error(error, state)

View Source
@callback handle_error(error(), state()) :: connection_handle_response()

Callback invoked when an error is encountered during WebSocket communication, allowing you to define custom error handling logic for various scenarios.

Parameters

  • error - The encountered error.
  • state - The current state of the module.

Example

def handle_error({error, _reason}, state)
    when error in [:encoding_failed, :casting_failed],
    do: {:ignore, state}

def handle_error(_error, _state), do: :reconnect
@callback handle_in(frame :: data_frame(), state()) :: generic_handle_response()

Callback invoked when a data frame is received from the server.

Parameters

  • frame - The received WebSocket frame, which is a data frame.
  • state - The current state of the module.

Example

def handle_in({:text, message}, state) do
  %{"data" => updated_data} = Jason.decode!(message)
  {:ok, updated_data}
end

def handle_in({:binary, _message}, state) do
  {:reply, [{:text, "i prefer text :)"}], state}
end
Link to this callback

handle_info(data, state)

View Source
@callback handle_info(data :: any(), state()) :: generic_handle_response()

Callback invoked when an incomprehensible message is received.

Parameters

  • data - The received message, which can be any term.
  • state - The current state of the module.

Example

def handle_info({:reply, message}, state) do
  {:reply, [{:text, message}], state}
end

Later can be used like:

send(:ws_conn, {:reply, "hello!"})
Link to this callback

handle_terminate(reason, state)

View Source (since 0.2.0)
@callback handle_terminate(reason :: any(), state()) :: ignored :: any()

Callback invoked when the WebSocket process is about to terminate.

The return value of this callback is always disregarded.

Parameters

  • reason - The reason for the termination. It can be any term.
  • state - The current state of the module.

Example

def handle_terminate(reason, _state) do
  IO.puts("Process is terminating with reason: #{inspect(reason)}")
end

Functions

Link to this macro

__using__(opts)

View Source (macro)

This macro simplifies the implementation of WebSocket client.

It automatically configures child_spec/1, start/1 and start_link/1 for the module, and provides handlers for all required callbacks, which can be overridden.

Link to this function

close(pid, code, reason)

View Source (since 0.2.1)
@spec close(:gen_statem.server_ref(), non_neg_integer(), binary()) :: :ok

Sends a WebSocket close frame to the server.

Parameters

  • pid - The reference to the WebSocket connection process.
  • code - An integer representing the WebSocket close code.
  • reason - A binary string providing the reason for closing the WebSocket connection.

Returns

This function always returns :ok.

Example

iex> Fresh.close(:ws_conn, 1000, "Normal Closure")
:ok
Link to this function

open?(pid)

View Source (since 0.4.2)
@spec open?(:gen_statem.server_ref()) :: boolean()

Checks if the connection is available.

Parameters

  • pid - The reference to the WebSocket connection process.

Returns

A boolean/0 representing the state of the connection.

Example

iex> Fresh.open?(:ws_conn)
true

Sends a WebSocket frame to the server.

Parameters

  • pid - The reference to the WebSocket connection process.
  • frame - The WebSocket frame to send.

Returns

This function always returns :ok.

Example

iex> Fresh.send(:ws_conn, {:text, "hi!"})
:ok
Link to this function

start(uri, module, state, opts)

View Source
@spec start(binary(), module(), any(), [option()]) :: :gen_statem.start_ret()

Starts a WebSocket connection without linking the process.

This function is similar to start_link/4 but does not link the process. Refer to start_link/4 for parameters details.

Link to this function

start_link(uri, module, state, opts)

View Source
@spec start_link(binary(), module(), any(), [option()]) :: :gen_statem.start_ret()

Starts a WebSocket connection and links the process.

Parameters

  • uri - The URI to connect to as a binary.
  • module - The module that implements the WebSocket client behaviour.
  • state - The initial state to be passed to the module when it starts.
  • opts - A list of options to configure the WebSocket connection. Refer to option/0 for available options.

Example

iex> Fresh.start_link("wss://example.com/socket", Example.WebSocket, %{}, name: {:local, :ws_conn})
{:ok, #PID<0.233.0>}