% @hidden -module(kafe_conn). -compile([{parse_transform, lager_transform}]). -behaviour(gen_server). -include("../include/kafe.hrl"). %-define(SERVER, ?MODULE). %% ------------------------------------------------------------------ %% API Function Exports %% ------------------------------------------------------------------ -export([start_link/2]). %% ------------------------------------------------------------------ %% gen_server Function Exports %% ------------------------------------------------------------------ -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). %% ------------------------------------------------------------------ %% API Function Definitions %% ------------------------------------------------------------------ start_link(Addr, Port) -> gen_server:start_link(?MODULE, {Addr, Port}, []). %% ------------------------------------------------------------------ %% gen_server Function Definitions %% ------------------------------------------------------------------ init({Addr, Port}) -> SndBuf = doteki:get_env([kafe, socket, sndbuf], ?DEFAULT_SOCKET_SNDBUF), RecBuf = doteki:get_env([kafe, socker, recbuf], ?DEFAULT_SOCKET_RECBUF), Buffer = lists:max([SndBuf, RecBuf, doteki:get_env([kafe, socket, buffer], max(SndBuf, RecBuf))]), case gen_tcp:connect(Addr, Port, [{mode, binary}, {active, once}, {sndbuf, SndBuf}, {recbuf, RecBuf}, {buffer, Buffer}]) of {ok, Socket} -> lager:debug("Connect to broker @ ~s:~p", [bucinet:ip_to_string(Addr), Port]), ApiVersion = doteki:get_env([kafe, api_version], ?DEFAULT_API_VERSION), CorrelationID = doteki:get_env([kafe, correlation_id], ?DEFAULT_CORRELATION_ID), ClientID = doteki:get_env([kafe, client_id], ?DEFAULT_CLIENT_ID), {ok, #{ ip => Addr, port => Port, socket => Socket, api_version => ApiVersion, correlation_id => CorrelationID, client_id => ClientID, requests => orddict:new(), parts => <<>>, sndbuf => SndBuf, recbuf => RecBuf, buffer => Buffer }}; {error, Reason} -> lager:debug("Connection faild to ~p:~p : ~p", [bucinet:ip_to_string(Addr), Port, Reason]), {stop, Reason} end. handle_call({call, Request, RequestParams, Response}, From, State) -> send_request(erlang:apply(Request, RequestParams ++ [State]), From, Response, State); handle_call(alive, _From, #{socket := Socket, sndbuf := SndBuf, recbuf := RecBuf, buffer := Buffer} = State) -> case inet:setopts(Socket, [{active, once}, {sndbuf, SndBuf}, {recbuf, RecBuf}, {buffer, Buffer}]) of ok -> {reply, ok, State}; {error, _} = Reason -> {reply, Reason, State} end; handle_call(_Request, _From, State) -> {reply, ok, State}. handle_cast(_Msg, State) -> {noreply, State}. handle_info( {tcp, _, <> = Packet}, #{parts := <<>>} = State ) when Size =< byte_size(Remainder) -> <> = Packet, kafe_protocol:response(<>, maps:update(parts, <<>>, State)); handle_info( {tcp, _, Part}, #{parts := <> = Parts} = State ) when byte_size(<>) >= Size -> <> = <>, kafe_protocol:response(<>, maps:update(parts, <<>>, State)); handle_info( {tcp, Socket, Part}, #{parts := Parts, sndbuf := SndBuf, recbuf := RecBuf, buffer := Buffer} = State ) -> case inet:setopts(Socket, [{active, once}, {sndbuf, SndBuf}, {recbuf, RecBuf}, {buffer, Buffer}]) of ok -> {noreply, maps:update(parts, <>, State)}; {error, _} = Reason -> {stop, Reason, State} end; handle_info({tcp_closed, Socket}, State) -> lager:debug("Connections close ~p ...", [Socket]), {stop, normal, State}; handle_info(Info, State) -> lager:debug("Invalid message : ~p", [Info]), lager:debug("--- State ~p", [State]), {noreply, State}. terminate(_Reason, #{socket := Socket}) -> lager:debug("Terminate..."), _ = gen_tcp:close(Socket), ok. code_change(_OldVsn, State, _Extra) -> {ok, State}. %% ------------------------------------------------------------------ %% Internal Function Definitions %% ------------------------------------------------------------------ send_request(#{packet := Packet, state := State2, api_version := ApiVersion}, From, Handler, #{correlation_id := CorrelationId, requests := Requests, socket := Socket, sndbuf := SndBuf, recbuf := RecBuf, buffer := Buffer} = State1) -> case gen_tcp:send(Socket, Packet) of ok -> case inet:setopts(Socket, [{active, once}, {sndbuf, SndBuf}, {recbuf, RecBuf}, {buffer, Buffer}]) of ok -> {noreply, maps:update( requests, orddict:store(CorrelationId, #{from => From, handler => Handler, socket => Socket, api_version => ApiVersion}, Requests), State2)}; {error, _} = Error -> {stop, abnormal, Error, State1} end; {error, _} = Error -> {stop, abnormal, Error, State1} end.