1 %%% Copyright (c) 2007- Facebook
2 %%% Distributed under the Thrift Software License
4 %%% See accompanying file LICENSE or visit the Thrift site at:
5 %%% http://developers.facebook.com/thrift/
10 -include("thrift.hrl").
11 -include("tApplicationException.hrl").
12 -include("transport/tTransportException.hrl").
13 -include("transport/tServerSocket.hrl").
14 -include("transport/tErlAcceptor.hrl").
16 -include_lib("kernel/include/inet.hrl").
20 -export([attr
/4, super
/0, inspect
/1]).
22 -export([new
/3, accept
/4]).
26 %%% 'super' is required unless ?MODULE is a base class
30 ?
DEFINE_ATTR(serverPid
);
31 ?
DEFINE_ATTR(transportFactory
);
32 ?
DEFINE_ATTR(protocolFactory
).
35 %%% behavior callbacks
38 %%% super() -> SuperModule = atom()
44 %%% inspect(This) -> string()
47 ?
FORMAT_ATTR(serverPid
) ++ ", " ++
48 ?
FORMAT_ATTR(transportFactory
) ++ ", " ++
49 ?
FORMAT_ATTR(protocolFactory
).
55 new(ServerPid
, TF
, PF
) ->
56 Super
= (super()):new(),
57 #?MODULE
{super
= Super
,
58 serverPid
= ServerPid
,
59 transportFactory
= TF
,
67 accept(This
, ListenSocket
, GP
, Handler
) ->
68 ServerPid
= oop:get(This
, serverPid
),
70 case catch gen_tcp:accept(ListenSocket
) of
72 ?
C0(ServerPid
, effectful_new_acceptor
), %% cast to create new acceptor
74 AddrString
= render_addr(Socket
),
75 ?
INFO("thrift connection accepted from ~s", [AddrString
]),
77 %% start_new(tSocket, [])
78 Client
= oop:start_new(tSocket
, []),
79 ?
R1(Client
, effectful_setHandle
, Socket
), %% TODO(cpiro): should we just let this be a param to the constructor?
81 %% cpiro: OPAQUE!! Trans = Client
82 TF
= oop:get(This
, transportFactory
),
83 Trans
= ?
F1(TF
, getTransport
, Client
),
85 %% cpiro: OPAQUE!! Prot = start_new(tBinaryProtocol, [Trans])
86 PF
= oop:get(This
, protocolFactory
),
87 Prot
= ?
F1(PF
, getProtocol
, Trans
),
90 Processor
= oop:start_new(tErlProcessor
, [GP
, Handler
]), %% TODO
92 case receive_loop(This
, Processor
, Prot
, Prot
) of
94 ?
INFO("thrift connection timed out from ~s", [AddrString
]);
96 ?
INFO("thrift connection closed from ~s", [AddrString
]);
98 ?
ERROR("unhandled ~p in tErlAcceptor: ~p", [Class
, Else
])
103 R
= thrift_utils:sformat("accept() failed: ~p", [Else
]),
104 exit(tTransportException:new(R
))
107 receive_loop(This
, Processor
, Iprot
, Oprot
) ->
108 try ?
R2(Processor
, process, Iprot
, Oprot
) of
109 {error
, TAE
} when is_record(TAE
, tApplicationException
),
110 TAE#tApplicationException
.type
== ?tApplicationException_HANDLER_ERROR
->
111 ?
ERROR("thrift handler returned an error: ~p", [oop:get(TAE
, message
)]),
112 receive_loop(This
, Processor
, Iprot
, Oprot
);
114 ?
INFO("thrift request: ~p", [Value
]),
115 receive_loop(This
, Processor
, Iprot
, Oprot
)
120 %% the following clause must be last
121 %% cpiro: would be best to implement an is_a/2 guard BIF
122 %% cpiro: breaks if it's a subclass of tTransportException
123 %% since unnest_record knows nothing about oop
125 case thrift_utils:unnest_record(Else
, tTransportException
) of
126 {ok
, TTE
} when TTE#tTransportException
.type
== ?tTransportException_NOT_OPEN
->
135 %% @param Socket the socket in question
136 %% TODO(cpiro): there probably needs to be a switch for DoLookup somewhere prominent and outside the lib,
137 %% probably at the "application" level
138 render_addr(Socket
) ->
140 {ok
, {Peer
, Port
}} = inet:peername(Socket
),
144 case catch inet:gethostbyaddr(Peer
) of
146 thrift_utils:sformat("~s:~p", [Hostent#hostent
.h_name
, Port
]);
151 {A
,B
,C
,D
} when not DoLookup
->
152 thrift_utils:sformat("~p.~p.~p.~p:~p", [A
,B
,C
,D
,Port
])