Current section
Files
Jump to
Current section
Files
src/sock_wrapper.erl
%%%===================================================================
%%% Socket Wrapper
%%% Provides Wade WebSocket functions with proper timeouts to avoid infinite blocking
%%%===================================================================
-module(sock_wrapper).
-behaviour(gen_server).
%% API
-export([start_link/0, connect_with_timeout/3, connect_with_timeout/4]).
%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).
-define(DEFAULT_TIMEOUT, 15000). % 15 seconds
%% @doc Start the socket wrapper service
-spec start_link() -> {ok, pid()} | {error, term()}.
start_link() ->
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
%% @doc Connect to WebSocket with default timeout
-spec connect_with_timeout(string(), pos_integer(), string()) -> {ok, pid()} | {error, term()}.
connect_with_timeout(Host, Port, Path) ->
connect_with_timeout(Host, Port, Path, ?DEFAULT_TIMEOUT).
%% @doc Connect to WebSocket with custom timeout
-spec connect_with_timeout(string(), pos_integer(), string(), pos_integer()) -> {ok, pid()} | {error, term()}.
connect_with_timeout(Host, Port, Path, Timeout) ->
gen_server:call(?MODULE, {connect, Host, Port, Path, Timeout}, Timeout + 1000).
init([]) ->
{ok, #{}}.
handle_call({connect, Host, Port, Path, Timeout}, _From, State) ->
io:format("Opening WebSocket connection to ~s:~p~s with timeout ~pms~n", [Host, Port, Path, Timeout]),
Parent = self(),
Ref = make_ref(),
% Spawn worker process
Worker = spawn_link(fun() ->
try
Result = wade_ws_client:start_link(Host, Port, Path),
Parent ! {Ref, Result}
catch
Class:Error:Stack ->
Parent ! {Ref, {error, {exception, Class, Error, Stack}}}
end
end),
% Wait for result with timeout
Result = receive
{Ref, Res} ->
case Res of
{ok, WsPid} when is_pid(WsPid) ->
io:format("WebSocket connection successful to ~s:~p~s~n", [Host, Port, Path]),
{ok, WsPid};
{error, Reason} ->
io:format("WebSocket connection failed to ~s:~p~s: ~p~n", [Host, Port, Path, Reason]),
{error, Reason}
end
after Timeout ->
% Kill the worker
unlink(Worker),
exit(Worker, kill),
io:format("WebSocket connection timeout to ~s:~p~s after ~pms~n", [Host, Port, Path, Timeout]),
{error, timeout}
end,
{reply, Result, State}.
handle_cast(_Msg, State) ->
{noreply, State}.
handle_info(_Info, State) ->
{noreply, State}.
terminate(_Reason, _State) ->
ok.
code_change(_OldVsn, State, _Extra) ->
{ok, State}.