quic_dist (quic v1.4.3)

View Source

Erlang distribution protocol implementation over QUIC.

This module implements the Erlang distribution protocol callbacks using QUIC as the transport layer. It provides:

- Connection establishment via TLS 1.3 (built into QUIC) - Multiple streams for parallel message delivery - Head-of-line blocking avoidance - Connection migration for NAT traversal - 0-RTT reconnection for fast session resumption

Configuration

Enable QUIC distribution in vm.args:

   -proto_dist quic
   -epmd_module quic_epmd
   -start_epmd false

Configure in sys.config:

   {quic, [
     {dist, [
       {cert_file, "/path/to/cert.pem"},
       {key_file, "/path/to/key.pem"},
       {cacert_file, "/path/to/ca.pem"},
       {verify, verify_peer}
     ]}
   ]}

Summary

Functions

Accept a connection from the distribution listener.

Handle an accepted connection. Called by net_kernel when a new connection is accepted.

Register to accept incoming user streams from a node. Joins the acceptor pool for the node. Multiple processes can register as acceptors. Incoming streams are assigned to acceptors using round-robin selection.

Return the address family to use.

Drop any pending connect-option overrides for Node.

Close the distribution listener.

Close a user stream gracefully. This sends a FIN to the peer. When both sides have sent FIN, the owner receives {quic_dist_stream, StreamRef, closed}.

Transfer stream ownership to another process. The new owner will receive all subsequent messages for this stream.

Look up the pending connect-option overrides for Node without consuming them. Returns an empty map if none are registered.

Get the distribution controller for a connected node. Returns {ok, ControllerPid} if the node is connected, {error, not_connected} otherwise.

Check if a node name is valid.

List all user streams across all connected nodes.

List user streams for a specific connected node.

Start listening for incoming distribution connections.

Start listening with options.

Open a bidirectional user stream to a connected node. Returns {ok, StreamRef} on success where StreamRef can be used with send/2,3 and close_stream/1. The caller becomes the stream owner.

Open a bidirectional user stream with options. Options: {priority, 16..255} - Stream priority (default: 128, lower = higher priority) Note: priorities 0-15 are reserved for distribution

Reset/cancel a user stream immediately (notifies peer). Uses default error code 0.

Reset/cancel a user stream with a specific error code. The peer receives the reset notification immediately.

Check if this distribution module should be used for the given node. Returns true if the node name is valid and we can potentially connect.

Send data on a user stream. Equivalent to send(StreamRef, Data, false).

Send data on a user stream. When Fin is true, this marks the end of data on this stream (half-close).

Register per-node connect-time option overrides for the next setup/5 attempt against Node. The map is merged on top of the defaults that connect_to_node/7 builds, so callers can override any key, including socket_backend and socket_adapter to route the underlying UDP packets through a custom transport (for example a MASQUE CONNECT-UDP tunnel), or external_psk to authenticate this peer with a different identity/secret than the cluster-wide default. See docs/PSK.md for the PSK option shapes.

Set up an outgoing distribution connection. Called by net_kernel to establish a connection to another node.

Stop accepting incoming user streams from a node. Removes the calling process from the acceptor pool.

Types

stream_info/0

-type stream_info() ::
          #{ref => stream_ref(),
            node => node(),
            stream_id => non_neg_integer(),
            owner => pid(),
            priority => 16..255,
            recv_fin => boolean(),
            send_fin => boolean()}.

stream_opt/0

-type stream_opt() :: {priority, 16..255}.

stream_ref/0

-type stream_ref() :: {quic_dist_stream, node(), non_neg_integer()}.

Functions

accept(Listen)

-spec accept(Listen :: term()) -> AcceptPid :: pid().

Accept a connection from the distribution listener.

accept_connection(AcceptPid, Socket, MyNode, Allowed, SetupTime)

-spec accept_connection(AcceptPid :: pid(),
                        Socket :: term(),
                        MyNode :: node(),
                        Allowed :: term(),
                        SetupTime :: non_neg_integer()) ->
                           pid().

Handle an accepted connection. Called by net_kernel when a new connection is accepted.

accept_streams(Node)

-spec accept_streams(Node :: node()) -> ok | {error, term()}.

Register to accept incoming user streams from a node. Joins the acceptor pool for the node. Multiple processes can register as acceptors. Incoming streams are assigned to acceptors using round-robin selection.

When a new stream arrives, one acceptor receives: {quic_dist_stream, StreamRef, {data, Data, Fin}}

The acceptor automatically becomes the stream owner (implicit ownership). Use controlling_process/2 to transfer ownership to a worker process.

If no acceptors are registered, incoming streams are refused with RESET.

address()

-spec address() -> #net_address{address :: term(), host :: term(), protocol :: term(), family :: term()}.

Return the address family to use.

clear_connect_options(Node)

-spec clear_connect_options(node()) -> ok.

Drop any pending connect-option overrides for Node.

close(Listen)

-spec close(Listen :: term()) -> ok.

Close the distribution listener.

close_stream(StreamRef)

-spec close_stream(StreamRef :: stream_ref()) -> ok | {error, term()}.

Close a user stream gracefully. This sends a FIN to the peer. When both sides have sent FIN, the owner receives {quic_dist_stream, StreamRef, closed}.

controlling_process(StreamRef, NewOwner)

-spec controlling_process(StreamRef :: stream_ref(), NewOwner :: pid()) -> ok | {error, term()}.

Transfer stream ownership to another process. The new owner will receive all subsequent messages for this stream.

get_connect_options(Node)

-spec get_connect_options(node()) -> map().

Look up the pending connect-option overrides for Node without consuming them. Returns an empty map if none are registered.

get_controller(Node)

-spec get_controller(Node :: node()) -> {ok, pid()} | {error, not_connected | not_quic_connection}.

Get the distribution controller for a connected node. Returns {ok, ControllerPid} if the node is connected, {error, not_connected} otherwise.

is_node_name(Node)

-spec is_node_name(atom()) -> boolean().

Check if a node name is valid.

list_streams()

-spec list_streams() -> [stream_info()].

List all user streams across all connected nodes.

list_streams(Node)

-spec list_streams(Node :: node()) -> [stream_info()].

List user streams for a specific connected node.

listen(Name)

-spec listen(Name :: atom()) ->
                {ok, {LSocket :: term(), TcpAddress :: term(), Creation :: non_neg_integer()}} |
                {error, Reason :: term()}.

Start listening for incoming distribution connections.

listen(Name, Opts)

-spec listen(Name :: atom(), Opts :: map()) ->
                {ok, {LSocket :: term(), TcpAddress :: term(), Creation :: non_neg_integer()}} |
                {error, Reason :: term()}.

Start listening with options.

open_stream(Node)

-spec open_stream(Node :: node()) -> {ok, stream_ref()} | {error, term()}.

Open a bidirectional user stream to a connected node. Returns {ok, StreamRef} on success where StreamRef can be used with send/2,3 and close_stream/1. The caller becomes the stream owner.

open_stream(Node, Options)

-spec open_stream(Node :: node(), Options :: [stream_opt()]) -> {ok, stream_ref()} | {error, term()}.

Open a bidirectional user stream with options. Options: {priority, 16..255} - Stream priority (default: 128, lower = higher priority) Note: priorities 0-15 are reserved for distribution

reset_stream(StreamRef)

-spec reset_stream(StreamRef :: stream_ref()) -> ok | {error, term()}.

Reset/cancel a user stream immediately (notifies peer). Uses default error code 0.

reset_stream(StreamRef, ErrorCode)

-spec reset_stream(StreamRef :: stream_ref(), ErrorCode :: non_neg_integer()) -> ok | {error, term()}.

Reset/cancel a user stream with a specific error code. The peer receives the reset notification immediately.

select(Node)

-spec select(node()) -> boolean().

Check if this distribution module should be used for the given node. Returns true if the node name is valid and we can potentially connect.

send(StreamRef, Data)

-spec send(StreamRef :: stream_ref(), Data :: iodata()) -> ok | {error, term()}.

Send data on a user stream. Equivalent to send(StreamRef, Data, false).

send(StreamRef, Data, Fin)

-spec send(StreamRef :: stream_ref(), Data :: iodata(), Fin :: boolean()) -> ok | {error, term()}.

Send data on a user stream. When Fin is true, this marks the end of data on this stream (half-close).

set_connect_options(Node, Opts)

-spec set_connect_options(node(), map()) -> ok.

Register per-node connect-time option overrides for the next setup/5 attempt against Node. The map is merged on top of the defaults that connect_to_node/7 builds, so callers can override any key, including socket_backend and socket_adapter to route the underlying UDP packets through a custom transport (for example a MASQUE CONNECT-UDP tunnel), or external_psk to authenticate this peer with a different identity/secret than the cluster-wide default. See docs/PSK.md for the PSK option shapes.

The entry is consumed once (the first matching connect_to_node call clears it). Use clear_connect_options/1 to drop it without triggering a connect.

setup(Node, Type, MyNode, LongOrShortNames, SetupTime)

-spec setup(Node :: node(),
            Type :: atom(),
            MyNode :: node(),
            LongOrShortNames :: shortnames | longnames,
            SetupTime :: non_neg_integer()) ->
               pid().

Set up an outgoing distribution connection. Called by net_kernel to establish a connection to another node.

stop_accepting(Node)

-spec stop_accepting(Node :: node()) -> ok | {error, term()}.

Stop accepting incoming user streams from a node. Removes the calling process from the acceptor pool.