Packages

WebSocket protocol library for Erlang (RFC 6455 / RFC 8441 / RFC 9220)

Current section

Files

Jump to
erlang_ws src ws.erl
Raw

src/ws.erl

%% Copyright 2026 Benoit Chesneau
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
%% You may obtain a copy of the License at
%%
%% http://www.apache.org/licenses/LICENSE-2.0
%%
%% Unless required by applicable law or agreed to in writing, software
%% distributed under the License is distributed on an "AS IS" BASIS,
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and
%% limitations under the License.
%% @doc Public entry point for the ws application.
%%
%% ws:accept/5 — start a server-side session after the embedder has
%% validated the H1 / H2 / H3 upgrade.
%% ws:connect/2 — convenience client connect for H1 (plain TCP / TLS).
%% ws:send/2, ws:close/2, ws:close/3, ws:stop/1 — per-session control.
-module(ws).
-export([accept/5, accept/6]).
-export([connect/2]).
-export([send/2, close/2, close/3, stop/1]).
-type transport_mod() :: module().
-type handle() :: term().
-type accept_opts() :: #{
parser_opts => map(),
idle_timeout => timeout(),
close_timeout => timeout()
}.
-export_type([transport_mod/0, handle/0, accept_opts/0]).
%% @doc Start a WebSocket session for an already-upgraded server stream.
%%
%% TransportMod — module implementing `ws_transport'.
%% Handle — transport-specific handle (for `ws_transport_gen_tcp'
%% this is a `gen_tcp:socket()').
%% Req — request metadata map the handler will receive; keys
%% `method', `path', `headers', `subprotocols',
%% `extensions', `peer', ... are by convention.
%% HandlerMod — module implementing `ws_handler'.
%% HandlerOpts — opaque, passed straight to `HandlerMod:init/2'.
-spec accept(transport_mod(), handle(), Req :: map(), module(), term()) ->
{ok, pid()} | {error, term()}.
accept(TransportMod, Handle, Req, HandlerMod, HandlerOpts) ->
accept(TransportMod, Handle, Req, HandlerMod, HandlerOpts, #{}).
-spec accept(transport_mod(), handle(), map(), module(), term(), accept_opts()) ->
{ok, pid()} | {error, term()}.
accept(TransportMod, Handle, Req, HandlerMod, HandlerOpts, Opts) ->
StartOpts = maps:merge(Opts, #{
transport => TransportMod,
handle => Handle,
role => server,
handler => HandlerMod,
handler_opts => HandlerOpts,
req => Req
}),
%% Use `start/1' (no link) so the session survives its initiator —
%% embedders typically spawn a short-lived upgrade handler that
%% exits after calling `ws:accept/5'.
case ws_session:start(StartOpts) of
{ok, Pid} ->
case TransportMod:controlling_process(Handle, Pid) of
ok ->
ok = ws_session:activate(Pid),
{ok, Pid};
{error, _} = E ->
ws_session:stop(Pid),
E
end;
Err -> Err
end.
%% @doc Client connect for `ws://' / `wss://' URLs over HTTP/1.1.
-spec connect(binary() | string(), map()) -> {ok, pid()} | {error, term()}.
connect(Url, Opts) ->
ws_client:connect(Url, Opts).
send(Pid, Frames) -> ws_session:send(Pid, Frames).
close(Pid, Code) -> ws_session:close(Pid, Code).
close(Pid, Code, Reason) -> ws_session:close(Pid, Code, Reason).
stop(Pid) -> ws_session:stop(Pid).