Packages

Erlang implementation of ZeroMQ Transport Protocol (ZMTP)

Current section

Files

Jump to
chumak src chumak_rep.erl
Raw

src/chumak_rep.erl

%% This Source Code Form is subject to the terms of the Mozilla Public
%% License, v. 2.0. If a copy of the MPL was not distributed with this
%% file, You can obtain one at http://mozilla.org/MPL/2.0/.
%% @doc ZeroMQ Rep Pattern for Erlang
%%
%% This pattern implement REP especification
%% from: http://rfc.zeromq.org/spec:28/REQREP#toc4
-module(chumak_rep).
-behaviour(chumak_pattern).
-export([valid_peer_type/1, init/1, peer_flags/1, accept_peer/2, peer_ready/3,
send/3, recv/2,
send_multipart/3, recv_multipart/2, peer_recv_message/3,
queue_ready/3, peer_disconected/2, identity/1]).
%% state for a pattern always to be module name.
-record(chumak_rep, {
identity :: string(),
pending_recv=nil :: nil | {from, From::term()},
state=idle :: idle | wait_req,
lb :: list(),
last_recv_peer=nil :: nil | pid()
}).
valid_peer_type(req) -> valid;
valid_peer_type(dealer) -> valid;
valid_peer_type(_) -> invalid.
init(Identity) ->
State = #chumak_rep{
identity=Identity,
lb=chumak_lb:new()
},
{ok, State}.
identity(#chumak_rep{identity=I}) -> I.
peer_flags(_State) ->
{rep, [incomming_queue]}.
accept_peer(State, PeerPid) ->
NewLb = chumak_lb:put(State#chumak_rep.lb, PeerPid),
{reply, {ok, PeerPid}, State#chumak_rep{lb=NewLb}}.
peer_ready(State, _PeerPid, _Identity) ->
{noreply, State}.
send(#chumak_rep{last_recv_peer=nil}=State, _Data, _From) ->
{reply, {error, efsm}, State};
send(#chumak_rep{last_recv_peer=LastRecvPeer}=State, Data, _From)
when is_pid(LastRecvPeer) ->
chumak_peer:send(LastRecvPeer, [<<>>, Data]),
{reply, ok, State#chumak_rep{last_recv_peer=nil}}.
recv(#chumak_rep{state=idle, lb=LB}=State, From) ->
case chumak_lb:get(LB) of
none ->
{noreply, State#chumak_rep{state=wait_req, pending_recv={from, From}}};
{NewLB, PeerPid} ->
direct_recv(State#chumak_rep{lb=NewLB}, PeerPid, PeerPid, From)
end;
recv(State, _From) ->
{reply, {error, efsm}, State}.
send_multipart(State, _Multipart, _From) ->
{reply, {error, not_implemented_yet}, State}.
recv_multipart(State, _From) ->
{reply, {error, not_implemented_yet}, State}.
peer_recv_message(State, _Message, _From) ->
%% This function will never called, because use incomming_queue property
{noreply, State}.
queue_ready(#chumak_rep{state=wait_req, pending_recv={from, PendingRecv}}=State, _Identity, PeerPid) ->
FutureState = State#chumak_rep{state=idle, pending_recv=nil},
case recv_from_peer(PeerPid) of
{ok, Message} ->
gen_server:reply(PendingRecv, {ok, Message}),
{noreply, FutureState#chumak_rep{last_recv_peer=PeerPid}};
{error, Reason} ->
gen_server:reply(PendingRecv, {error, Reason}),
{noreply, FutureState};
empty ->
gen_server:reply(PendingRecv, {error, queue_empty}),
{noreply, FutureState}
end;
queue_ready(State, _Identity, _PeerPid) ->
%% Not used in iddle state
{noreply, State}.
peer_disconected(#chumak_rep{lb=LB}=State, PeerPid) ->
NewLB = chumak_lb:delete(LB, PeerPid),
{noreply, State#chumak_rep{lb=NewLB}}.
%% implement direct recv from peer queues
direct_recv(#chumak_rep{lb=LB}=State, FirstPeerPid, PeerPid, From) ->
case recv_from_peer(PeerPid) of
{ok, Message} ->
{reply, {ok, Message}, State#chumak_rep{last_recv_peer=PeerPid}};
{error, Reason} ->
{reply, {error, Reason}, State};
empty ->
case chumak_lb:get(LB) of
{NewLB, FirstPeerPid} ->
{noreply, State#chumak_rep{state=wait_req, pending_recv={from, From}, lb=NewLB}};
{NewLB, OtherPeerPid} ->
direct_recv(State#chumak_rep{lb=NewLB}, FirstPeerPid, OtherPeerPid, From)
end
end.
recv_from_peer(PeerPid) ->
case chumak_peer:incomming_queue_out(PeerPid) of
{out, Messages} ->
decode_messages(Messages);
empty ->
empty
end.
decode_messages([<<>>|Tail])->
{ok, binary:list_to_bin(Tail)};
decode_messages([Delimiter|_Tail]) ->
error_logger:warning_report({
invalid_delimiter_frame,
{pattern, rep},
{obtained_frame, Delimiter},
{expected_frame, <<>>}
}),
{error, invalid_delimiter_frame}.