CCCPaste

d0e20dd112607

-module(ip_fetcher_app).
-behaviour(application).

-export([start/2, stop/1]).

start(_StartType, _StartArgs) ->
ip_fetcher_sup:start_link().

stop(_State) ->
ok.

%% ===================================================================
%% Supervisor
%% ===================================================================

-module(ip_fetcher_sup).
-behaviour(supervisor).

-export([start_link/0, init/1]).

start_link() ->
supervisor:start_link({local, ?MODULE}, ?MODULE, []).

init([]) ->
SupFlags = #{
strategy => one_for_one,
intensity => 3, % max restarts
period => 5 % seconds
},

ChildSpec = #{
id => ip_fetcher,
start => {ip_fetcher_worker, start_link, []},
restart => transient, % only restart on abnormal exit
shutdown => 5000, % 5 seconds graceful shutdown
type => worker,
modules => [ip_fetcher_worker]
},

{ok, {SupFlags, [ChildSpec]}}.

%% ===================================================================
%% Worker
%% ===================================================================

-module(ip_fetcher_worker).
-behaviour(gen_server).

-export([start_link/0]).
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).

start_link() ->
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).

init([]) ->
% Start the work immediately
self() ! fetch_ip,
{ok, #{}}.

handle_call(_Request, _From, State) ->
{reply, ok, State}.

handle_cast(_Msg, State) ->
{noreply, State}.

handle_info(fetch_ip, State) ->
case do_fetch_ip() of
{ok, Ip} ->
io:format("~s~n", [Ip]),
% Normal exit — transient worker will NOT be restarted
{stop, normal, State};
{error, Reason} ->
io:format("Failed to connect – will be restarted by supervisor: ~p~n", [Reason]),
% Abnormal exit — supervisor WILL restart us (up to 3 times in 5s)
{stop, {connect_failed, Reason}, State}
end;

handle_info(_Info, State) ->
{noreply, State}.

terminate(_Reason, _State) ->
ok.

code_change(_OldVsn, State, _Extra) ->
{ok, State}.

%% Internal function to fetch IP
do_fetch_ip() ->
Host = "icanhazip.com",
Port = 80,
Timeout = 10000,

case gen_tcp:connect(Host, Port, [binary, {active, false}], Timeout) of
{ok, Socket} ->
Request = "GET / HTTP/1.0\r\nHost: icanhazip.com\r\nConnection: close\r\n\r\n",
ok = gen_tcp:send(Socket, Request),

case recv_all(Socket, <<>>) of
{ok, Data} ->
% Extract just the IP (last line)
Lines = binary:split(Data, <<"\n">>, [global, trim]),
Ip = lists:last(Lines),
gen_tcp:close(Socket),
{ok, binary_to_list(Ip)};
Error ->
gen_tcp:close(Socket),
Error
end;
Error ->
Error
end.

recv_all(Socket, Acc) ->
case gen_tcp:recv(Socket, 0, 10000) of
{ok, Data} ->
recv_all(Socket, <<Acc/binary, Data/binary>>);
{error, closed} ->
{ok, Acc};
{error, _} = Err ->
Err
end.