r1319@opsdev009 (orig r70531): cpiro | 2007-11-17 18:10:20 -0800
[amiethrift.git] / lib / erl / src / transport / tErlAcceptor.erl
blobbcfcb9a5f5268f01ddc5f0997dfb28a87f7af759
1 %%% Copyright (c) 2007- Facebook
2 %%% Distributed under the Thrift Software License
3 %%%
4 %%% See accompanying file LICENSE or visit the Thrift site at:
5 %%% http://developers.facebook.com/thrift/
7 -module(tErlAcceptor).
9 -include("oop.hrl").
10 -include("thrift.hrl").
11 -include("tApplicationException.hrl").
12 -include("transport/tTransportException.hrl").
13 -include("protocol/tProtocolException.hrl").
14 -include("transport/tServerSocket.hrl").
15 -include("transport/tErlAcceptor.hrl").
17 -include_lib("kernel/include/inet.hrl").
19 -behavior(oop).
21 -export([attr/4, super/0, inspect/1]).
23 -export([new/3, accept/4]).
25 %%%
26 %%% define attributes
27 %%% 'super' is required unless ?MODULE is a base class
28 %%%
30 ?DEFINE_ATTR(super);
31 ?DEFINE_ATTR(serverPid);
32 ?DEFINE_ATTR(transportFactory);
33 ?DEFINE_ATTR(protocolFactory).
35 %%%
36 %%% behavior callbacks
37 %%%
39 %%% super() -> SuperModule = atom()
40 %%% | none
42 super() ->
43 tServerTransport.
45 %%% inspect(This) -> string()
47 inspect(This) ->
48 ?FORMAT_ATTR(serverPid) ++ ", " ++
49 ?FORMAT_ATTR(transportFactory) ++ ", " ++
50 ?FORMAT_ATTR(protocolFactory).
52 %%%
53 %%% class methods
54 %%%
56 new(ServerPid, TF, PF) ->
57 Super = (super()):new(),
58 #?MODULE{super = Super,
59 serverPid = ServerPid,
60 transportFactory = TF,
61 protocolFactory = PF
64 %%%
65 %%% instance methods
66 %%%
68 accept(This, ListenSocket, GP, Handler) ->
69 ServerPid = oop:get(This, serverPid),
71 case catch gen_tcp:accept(ListenSocket) of
72 {ok, Socket} ->
73 ?C0(ServerPid, effectful_new_acceptor), % cast to create new acceptor
75 AddrString = render_addr(Socket),
76 ?INFO("thrift connection accepted from ~s", [AddrString]),
78 Client = oop:start_new(tSocket, []),
79 ?R1(Client, effectful_setHandle, Socket),
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),
89 %% start_new(, ...)
90 Processor = oop:start_new(tErlProcessor, [GP, Handler]),
92 try
93 receive_loop(This, Processor, Prot, Prot)
94 catch
95 exit:{timeout, _} ->
96 ?INFO("thrift connection timed out from ~s", [AddrString]);
98 %% cpiro: i think the extra entry on the stack is always from receive_loop
99 %% the below case shouldn't happen then? if we move this catch inside
100 %% we'll probably need this case and not the next one
102 %% exit:{thrift_exception, E} ->
103 %% handle_exception(E, AddrString, no2);
105 exit:{{thrift_exception, E}, Stack1} ->
106 handle_exception(E, AddrString, Stack1);
108 Class:Else ->
109 ?ERROR("some other error ~p in tErlAcceptor: ~p", [Class, Else])
110 end,
111 exit(normal);
113 Else ->
114 R = thrift_utils:sformat("accept() failed: ~p", [Else]),
115 tException:throw(tTransportException, [R])
116 end.
119 handle_exception(E, AddrString, Stack1) ->
120 case tException:read(E) of
121 none -> % not a tException
122 ?ERROR("not really a tException: ~p", [exit, E]);
124 {tProtocolException, ?tProtocolException_BAD_VERSION, _} ->
125 ?INFO("thrift missing version from ~s", [AddrString]);
127 {tTransportException, ?tTransportException_NOT_OPEN, _} ->
128 ?INFO("thrift connection closed from ~s", [AddrString]);
130 _ ->
131 Where = "thrift tErlAcceptor caught a tException",
132 ?ERROR("~s", [tException:inspect_with_backtrace(E, Where, Stack1)])
133 end.
135 %% always calls itself ... only way to escape is through an exit
136 receive_loop(This, Processor, Iprot, Oprot) ->
137 case ?R2(Processor, process, Iprot, Oprot) of
138 {error, Reason} ->
139 case tException:read(Reason) of
140 none ->
141 ?ERROR("thrift handler returned something weird: {error, ~p}", [Reason]);
142 _ ->
143 Where = "thrift processor/handler caught a tException",
144 ?ERROR("~s", [tException:inspect_with_backtrace(Reason, Where)])
145 end,
146 receive_loop(This, Processor, Iprot, Oprot);
147 Value ->
148 ?INFO("thrift request: ~p", [Value]),
149 receive_loop(This, Processor, Iprot, Oprot)
150 end.
152 %% helper functions
154 %% @param Socket the socket in question
155 %% TODO(cpiro): there probably needs to be a switch for DoLookup somewhere prominent and outside the lib,
156 %% probably at the "application" level
157 render_addr(Socket) ->
158 DoLookup = true,
159 {ok, {Peer, Port}} = inet:peername(Socket),
161 case Peer of
162 _ when DoLookup ->
163 case catch inet:gethostbyaddr(Peer) of
164 {ok, Hostent} ->
165 thrift_utils:sformat("~s:~p", [Hostent#hostent.h_name, Port]);
166 _ ->
167 "??"
168 end;
170 {A,B,C,D} when not DoLookup ->
171 thrift_utils:sformat("~p.~p.~p.~p:~p", [A,B,C,D,Port])
172 end.