Packages

macula

0.8.19
7.1.0 7.0.0 6.0.0 5.2.2 5.2.1 5.2.0 5.1.0 5.0.0 4.8.0 4.7.1 4.7.0 4.6.0 4.5.0 4.4.10 4.4.9 4.4.8 4.4.7 4.4.6 4.4.5 4.4.4 4.4.3 4.4.2 4.4.1 4.4.0 4.3.1 4.3.0 4.2.9 4.2.8 4.2.7 4.2.6 4.2.5 4.2.4 4.2.3 4.2.2 4.2.1 4.2.0 4.1.1 4.1.0 4.0.0 3.16.0 3.15.3 3.15.2 3.15.1 3.14.0 3.13.0 3.12.1 3.12.0 3.11.1 3.11.0 3.10.3 3.10.2 3.10.1 3.9.0 3.8.0 3.7.0 3.5.0 3.4.0 3.3.0 3.2.0 3.1.0 3.0.0 2.1.1 2.1.0 2.0.0 1.5.2 1.5.1 1.4.30 1.4.29 1.4.28 1.4.27 1.4.26 1.4.25 1.4.24 1.4.23 1.4.22 1.4.21 1.4.20 1.4.19 1.4.18 1.4.17 1.4.16 1.4.15 1.4.14 1.4.13 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.3.1 1.3.0 1.2.0 1.1.0 1.0.10 1.0.9 1.0.8 1.0.7 1.0.6 1.0.5 1.0.4 1.0.3 1.0.2 1.0.1 1.0.0 0.48.6 0.48.5 0.48.4 0.48.3 0.48.2 0.48.1 0.48.0 0.47.1 0.47.0 0.46.3 0.46.1 0.46.0 0.45.3 0.45.2 0.45.1 0.45.0 0.44.2 0.44.1 0.44.0 0.43.3 0.43.2 0.43.1 0.43.0 0.42.9 0.42.8 0.42.7 0.42.6 0.42.5 0.42.4 0.42.3 0.42.2 0.42.1 0.42.0 0.41.1 0.41.0 0.40.1 0.40.0 0.39.9 0.39.8 0.39.7 0.39.6 0.39.5 0.39.4 0.39.3 0.39.2 0.39.1 0.39.0 0.38.8 0.38.7 0.38.6 0.38.5 0.38.4 0.38.3 0.38.2 0.38.1 0.38.0 0.37.7 0.37.6 0.37.5 0.37.4 0.37.3 0.37.2 0.37.1 0.37.0 0.36.6 0.36.5 0.36.4 0.36.3 0.36.2 0.36.1 0.36.0 0.35.4 0.35.3 0.35.2 0.35.1 0.35.0 0.34.1 0.34.0 0.33.1 0.33.0 0.32.5 0.32.4 0.32.3 0.32.2 0.32.1 0.32.0 0.31.9 0.31.8 0.31.7 0.31.6 0.31.5 0.31.4 0.31.3 0.31.2 0.31.1 0.31.0 0.30.10 0.30.9 0.30.8 0.30.7 0.30.6 0.30.5 0.30.4 0.30.3 0.30.2 0.30.1 0.30.0 0.29.0 0.28.3 0.28.2 0.28.1 0.28.0 0.27.1 0.27.0 0.26.1 0.26.0 0.25.6 0.25.5 0.25.4 0.25.3 0.25.2 0.25.1 0.25.0 0.24.6 0.24.5 0.24.4 0.24.3 0.24.2 0.24.1 0.24.0 0.23.3 0.23.2 0.23.1 0.23.0 0.22.12 0.22.11 0.22.10 0.22.9 0.22.8 0.22.7 0.22.6 0.22.5 0.22.4 0.22.3 0.22.2 0.22.1 0.22.0 0.21.7 0.21.6 0.21.5 0.21.4 0.21.2 0.21.1 0.21.0 0.20.25 0.20.24 0.20.23 0.20.22 0.20.21 0.20.20 0.20.19 0.20.18 0.20.17 0.20.16 0.20.15 0.20.14 0.20.13 0.20.12 0.20.11 0.20.10 0.20.9 0.20.8 0.20.7 0.20.6 0.20.5 0.20.3 0.20.2 0.20.1 0.20.0 0.19.2 0.19.1 0.19.0 0.18.1 0.18.0 0.17.4 0.17.3 0.17.2 0.17.1 0.17.0 0.16.6 0.16.5 0.16.4 0.16.3 0.16.2 0.16.1 0.16.0 0.15.1 0.15.0 0.14.3 0.14.2 0.14.1 0.14.0 0.12.6 0.12.5 0.12.3 0.11.3 0.10.2 0.10.1 0.10.0 0.9.2 0.9.1 0.9.0 0.8.25 0.8.24 0.8.23 0.8.22 0.8.21 0.8.20 0.8.19 0.8.18 0.8.17 0.8.16 0.8.15 0.8.14 0.8.13 0.8.12 0.8.11 0.8.10 0.8.9 0.8.8 0.8.7 0.8.6 0.8.5 0.8.4 0.8.3 0.8.2 0.8.1 0.8.0 0.7.30 0.7.29 0.7.28 0.7.27 0.7.26 0.7.25 0.7.24 0.7.23 0.7.22 0.7.21 0.7.20 0.7.19 0.7.18 0.7.17 0.7.16 0.7.15 0.7.14 0.7.13 0.7.12 0.7.11 0.7.10 0.7.9 0.7.8 0.7.7 0.7.6 0.7.5 0.7.4 0.7.3 0.7.2 0.7.1 0.7.0 0.6.7 0.6.6 0.6.5 0.6.4 0.6.3 0.6.2 0.6.1 0.6.0 0.5.0 0.4.4 0.4.3 0.4.2 0.4.1 0.4.0 0.3.4 0.3.3 0.3.2 0.3.1

Macula HTTP/3 Mesh SDK — connect, subscribe, publish, call, advertise

Current section

Files

Jump to
macula src macula_quic.erl
Raw

src/macula_quic.erl

%%%-------------------------------------------------------------------
%%% @doc
%%% Main API module for Macula QUIC transport.
%%% Provides a simplified wrapper around the quicer library.
%%% @end
%%%-------------------------------------------------------------------
-module(macula_quic).
-export([
listen/2,
connect/4,
accept/2,
accept_stream/2,
open_stream/1,
send/2,
async_send/2,
recv/2,
close/1
]).
%%%===================================================================
%%% API Functions
%%%===================================================================
%% @doc Start a QUIC listener on the specified port.
%% Options:
%% {cert, CertFile} - Path to PEM certificate file
%% {key, KeyFile} - Path to PEM private key file
%% {alpn, [Protocol]} - List of ALPN protocols (e.g., ["macula"])
%% {peer_unidi_stream_count, N} - Max unidirectional streams
%% {peer_bidi_stream_count, N} - Max bidirectional streams
%% @end
-spec listen(inet:port_number(), list()) -> {ok, pid()} | {error, term()}.
listen(Port, Opts) ->
%% Extract certificate files
CertFile = proplists:get_value(cert, Opts),
KeyFile = proplists:get_value(key, Opts),
AlpnProtocols = proplists:get_value(alpn, Opts, ["macula"]),
PeerUnidiStreamCount = proplists:get_value(peer_unidi_stream_count, Opts, 3),
PeerBidiStreamCount = proplists:get_value(peer_bidi_stream_count, Opts, 100),
%% Build quicer listener options
ListenerOpts = [
{certfile, CertFile},
{keyfile, KeyFile},
{alpn, AlpnProtocols},
{peer_unidi_stream_count, PeerUnidiStreamCount},
{peer_bidi_stream_count, PeerBidiStreamCount},
{idle_timeout_ms, 0} % Disable QUIC idle timeout (application handles keep-alive)
],
%% Start QUIC listener
%% The calling process becomes the owner and will receive connection messages
io:format("[QUIC] Starting listener on port ~p~n", [Port]),
quicer:listen(Port, ListenerOpts).
%% @doc Connect to a QUIC server.
%% Options:
%% {alpn, [Protocol]} - List of ALPN protocols
%% {verify, none | verify_peer} - Certificate verification mode
%% @end
-spec connect(string() | inet:ip_address(), inet:port_number(), list(), timeout()) ->
{ok, pid()} | {error, term()}.
connect(Host, Port, Opts, Timeout) ->
%% Extract options
AlpnProtocols = proplists:get_value(alpn, Opts, ["macula"]),
Verify = proplists:get_value(verify, Opts, none),
%% Build quicer options
QuicerOpts = [
{alpn, AlpnProtocols},
{verify, Verify},
{idle_timeout_ms, 0} % Disable QUIC idle timeout (application handles keep-alive)
],
%% Connect
quicer:connect(Host, Port, QuicerOpts, Timeout).
%% @doc Accept an incoming connection on a listener.
%% After accepting, the connection needs handshake to complete.
-spec accept(pid(), timeout()) -> {ok, pid()} | {error, term()}.
accept(ListenerPid, Timeout) ->
case quicer:accept(ListenerPid, [], Timeout) of
{ok, Conn} ->
%% Complete TLS handshake
case quicer:handshake(Conn) of
{ok, Conn} -> {ok, Conn};
Error -> Error
end;
Error ->
Error
end.
%% @doc Accept an incoming stream on a connection.
-spec accept_stream(pid(), timeout()) -> {ok, pid()} | {error, term()}.
accept_stream(ConnPid, _Timeout) ->
%% accept_stream/2 doesn't take timeout, it returns immediately
quicer:accept_stream(ConnPid, []).
%% @doc Open a new bidirectional stream on a connection.
-spec open_stream(pid()) -> {ok, pid()} | {error, term()}.
open_stream(ConnPid) ->
quicer:start_stream(ConnPid, []).
%% @doc Send data on a stream (blocking).
-spec send(pid(), binary()) -> ok | {error, term()}.
send(StreamPid, Data) ->
case quicer:send(StreamPid, Data) of
{ok, _BytesSent} -> ok;
Error -> Error
end.
%% @doc Send data on a stream asynchronously (non-blocking).
%% This returns immediately without waiting for QUIC flow control.
-spec async_send(pid(), binary()) -> ok | {error, term()}.
async_send(StreamPid, Data) ->
case quicer:async_send(StreamPid, Data) of
{ok, _BytesSent} -> ok;
Error -> Error
end.
%% @doc Receive data from a stream (blocking).
-spec recv(pid(), timeout()) -> {ok, binary()} | {error, term()}.
recv(StreamPid, Timeout) ->
%% quicer sends data as messages, so we need to receive from mailbox
receive
{quic, Data, StreamPid, _Props} ->
{ok, Data}
after Timeout ->
{error, timeout}
end.
%% @doc Close a listener, connection, or stream.
-spec close(pid()) -> ok.
close(Pid) ->
%% quicer uses different close functions based on resource type
%% For simplicity, we try to close as stream first, then connection
try
quicer:close_stream(Pid)
catch
_:_ ->
try
quicer:close_connection(Pid)
catch
_:_ ->
quicer:close_listener(Pid)
end
end,
ok.