blob: 233c309073ee5590e7a2c158a426d3e3031a1499 [file] [log] [blame]
David Reissac549552008-06-10 22:56:59 +00001%%%-------------------------------------------------------------------
2%%% File : thrift_processor.erl
3%%% Author : <todd@lipcon.org>
David Reiss11d855c2008-06-11 00:59:12 +00004%%% Description :
David Reissac549552008-06-10 22:56:59 +00005%%%
6%%% Created : 28 Jan 2008 by <todd@lipcon.org>
7%%%-------------------------------------------------------------------
8-module(thrift_processor).
David Reissc11734e2008-06-11 00:59:48 +00009-author('todd@lipcon.org').
10-author('eletuchy@facebook.com').
David Reissac549552008-06-10 22:56:59 +000011
David Reissc11734e2008-06-11 00:59:48 +000012-export([init/1]).
David Reissac549552008-06-10 22:56:59 +000013
14-include("thrift_constants.hrl").
15-include("thrift_protocol.hrl").
16
David Reissc11734e2008-06-11 00:59:48 +000017-record(thrift_processor, {handler, in_protocol, out_protocol, service}).
David Reissac549552008-06-10 22:56:59 +000018
David Reissc11734e2008-06-11 00:59:48 +000019init({Server, ProtoGen, Service, Handler}) when is_function(ProtoGen, 0) ->
20 {ok, IProt, OProt} = ProtoGen(),
21 loop(#thrift_processor{in_protocol = IProt,
22 out_protocol = OProt,
23 service = Service,
24 handler = Handler}).
David Reissac549552008-06-10 22:56:59 +000025
David Reissc11734e2008-06-11 00:59:48 +000026loop(State = #thrift_processor{in_protocol = IProto,
27 out_protocol = OProto}) ->
David Reissae756f42008-06-10 22:57:11 +000028 case thrift_protocol:read(IProto, message_begin) of
29 #protocol_message_begin{name = Function,
30 type = ?tMessageType_CALL} ->
David Reiss80664fe2008-06-11 01:00:30 +000031 ok = handle_function(State, list_to_atom(Function)),
David Reissae756f42008-06-10 22:57:11 +000032 loop(State);
33 {error, closed} ->
David Reissc11734e2008-06-11 00:59:48 +000034 %% error_logger:info_msg("Client disconnected~n"),
David Reiss80664fe2008-06-11 01:00:30 +000035 thrift_protocol:close_transport(OProto),
36 exit(shutdown)
David Reissac549552008-06-10 22:56:59 +000037 end.
David Reissae756f42008-06-10 22:57:11 +000038
David Reissc11734e2008-06-11 00:59:48 +000039handle_function(State=#thrift_processor{in_protocol = IProto,
40 out_protocol = OProto,
41 handler = Handler,
42 service = Service},
David Reiss1c1ca742008-06-10 22:57:21 +000043 Function) ->
44 InParams = Service:function_info(Function, params_type),
45
46 {ok, Params} = thrift_protocol:read(IProto, InParams),
David Reiss76f0d112008-06-10 22:57:35 +000047
David Reiss982c72d2008-06-10 22:58:33 +000048 try
David Reiss11d855c2008-06-11 00:59:12 +000049 Result = Handler:handle_function(Function, Params),
David Reissc11734e2008-06-11 00:59:48 +000050 %% {Micro, Result} = better_timer(Handler, handle_function, [Function, Params]),
51 %% error_logger:info_msg("Processed ~p(~p) in ~.4fms~n",
52 %% [Function, Params, Micro/1000.0]),
David Reiss982c72d2008-06-10 22:58:33 +000053 handle_success(State, Function, Result)
54 catch
David Reiss5541d652008-06-11 00:57:42 +000055 Type:Data ->
56 handle_function_catch(State, Function, Type, Data)
David Reissc11734e2008-06-11 00:59:48 +000057 end,
58 after_reply(OProto).
David Reiss5541d652008-06-11 00:57:42 +000059
David Reissc11734e2008-06-11 00:59:48 +000060handle_function_catch(State = #thrift_processor{service = Service},
David Reiss5541d652008-06-11 00:57:42 +000061 Function, ErrType, ErrData) ->
62 IsAsync = Service:function_info(Function, reply_type) =:= async_void,
63
64 case {ErrType, ErrData} of
65 _ when IsAsync ->
David Reiss4ec777e2008-06-11 01:01:29 +000066 Stack = erlang:get_stacktrace(),
David Reiss5541d652008-06-11 00:57:42 +000067 error_logger:warning_msg(
68 "async void ~p threw error which must be ignored: ~p",
David Reiss4ec777e2008-06-11 01:01:29 +000069 [Function, {ErrType, ErrData, Stack}]),
David Reiss5541d652008-06-11 00:57:42 +000070 ok;
71
72 {throw, Exception} when is_tuple(Exception), size(Exception) > 0 ->
David Reiss982c72d2008-06-10 22:58:33 +000073 error_logger:warning_msg("~p threw exception: ~p~n", [Function, Exception]),
74 handle_exception(State, Function, Exception),
David Reiss920959a2008-06-11 00:56:35 +000075 ok; % we still want to accept more requests from this client
David Reiss5541d652008-06-11 00:57:42 +000076
77 {error, Error} ->
David Reiss920959a2008-06-11 00:56:35 +000078 ok = handle_error(State, Function, Error)
David Reiss982c72d2008-06-10 22:58:33 +000079 end.
80
David Reissc11734e2008-06-11 00:59:48 +000081handle_success(State = #thrift_processor{out_protocol = OProto,
82 service = Service},
David Reiss982c72d2008-06-10 22:58:33 +000083 Function,
84 Result) ->
David Reiss11d855c2008-06-11 00:59:12 +000085 ReplyType = Service:function_info(Function, reply_type),
David Reisse1a79982008-06-10 22:58:14 +000086 StructName = atom_to_list(Function) ++ "_result",
David Reiss11d855c2008-06-11 00:59:12 +000087
David Reissc11734e2008-06-11 00:59:48 +000088 ok = case Result of
89 {reply, ReplyData} ->
90 Reply = {{struct, [{0, ReplyType}]}, {StructName, ReplyData}},
91 send_reply(OProto, Function, ?tMessageType_REPLY, Reply);
David Reisse1a79982008-06-10 22:58:14 +000092
David Reissc11734e2008-06-11 00:59:48 +000093 ok when ReplyType == {struct, []} ->
94 send_reply(OProto, Function, ?tMessageType_REPLY, {ReplyType, {StructName}});
David Reiss11d855c2008-06-11 00:59:12 +000095
David Reissc11734e2008-06-11 00:59:48 +000096 ok when ReplyType == async_void ->
97 %% no reply for async void
98 ok
99 end.
David Reisse1a79982008-06-10 22:58:14 +0000100
David Reissc11734e2008-06-11 00:59:48 +0000101handle_exception(State = #thrift_processor{out_protocol = OProto,
102 service = Service},
David Reiss982c72d2008-06-10 22:58:33 +0000103 Function,
104 Exception) ->
105 ExceptionType = element(1, Exception),
David Reissc11734e2008-06-11 00:59:48 +0000106 %% Fetch a structure like {struct, [{-2, {struct, {Module, Type}}},
107 %% {-3, {struct, {Module, Type}}}]}
David Reisse1a79982008-06-10 22:58:14 +0000108
David Reiss982c72d2008-06-10 22:58:33 +0000109 ReplySpec = Service:function_info(Function, exceptions),
110 {struct, XInfo} = ReplySpec,
111
112 true = is_list(XInfo),
David Reiss982c72d2008-06-10 22:58:33 +0000113
David Reissc11734e2008-06-11 00:59:48 +0000114 %% Assuming we had a type1 exception, we'd get: [undefined, Exception, undefined]
115 %% e.g.: [{-1, type0}, {-2, type1}, {-3, type2}]
David Reiss11d855c2008-06-11 00:59:12 +0000116 ExceptionList = [case Type of
117 ExceptionType -> Exception;
118 _ -> undefined
David Reiss982c72d2008-06-10 22:58:33 +0000119 end
David Reiss11d855c2008-06-11 00:59:12 +0000120 || {_Fid, {struct, {_Module, Type}}} <- XInfo],
121
David Reiss982c72d2008-06-10 22:58:33 +0000122 ExceptionTuple = list_to_tuple([Function | ExceptionList]),
David Reiss11d855c2008-06-11 00:59:12 +0000123
David Reissc11734e2008-06-11 00:59:48 +0000124 % Make sure we got at least one defined
David Reiss982c72d2008-06-10 22:58:33 +0000125 case lists:all(fun(X) -> X =:= undefined end, ExceptionList) of
126 true ->
127 ok = handle_unknown_exception(State, Function, Exception);
128 false ->
129 ok = send_reply(OProto, Function, ?tMessageType_REPLY, {ReplySpec, ExceptionTuple})
130 end.
131
David Reiss920959a2008-06-11 00:56:35 +0000132%%
David Reissc11734e2008-06-11 00:59:48 +0000133%% Called when an exception has been explicitly thrown by the service, but it was
134%% not one of the exceptions that was defined for the function.
David Reiss920959a2008-06-11 00:56:35 +0000135%%
David Reiss982c72d2008-06-10 22:58:33 +0000136handle_unknown_exception(State, Function, Exception) ->
David Reiss920959a2008-06-11 00:56:35 +0000137 handle_error(State, Function, {exception_not_declared_as_thrown,
138 Exception}).
139
David Reissc11734e2008-06-11 00:59:48 +0000140handle_error(#thrift_processor{out_protocol = OProto}, Function, Error) ->
David Reissda070672008-06-11 00:56:42 +0000141 Stack = erlang:get_stacktrace(),
142 error_logger:error_msg("~p had an error: ~p~n", [Function, {Error, Stack}]),
David Reiss11d855c2008-06-11 00:59:12 +0000143
David Reiss920959a2008-06-11 00:56:35 +0000144 Message =
145 case application:get_env(thrift, exceptions_include_traces) of
146 {ok, true} ->
147 lists:flatten(io_lib:format("An error occurred: ~p~n",
David Reissda070672008-06-11 00:56:42 +0000148 [{Error, Stack}]));
David Reiss920959a2008-06-11 00:56:35 +0000149 _ ->
150 "An unknown handler error occurred."
151 end,
152 Reply = {?TApplicationException_Structure,
153 #'TApplicationException'{
154 message = Message,
155 type = ?TApplicationException_UNKNOWN}},
156 send_reply(OProto, Function, ?tMessageType_EXCEPTION, Reply).
David Reiss982c72d2008-06-10 22:58:33 +0000157
David Reiss982c72d2008-06-10 22:58:33 +0000158send_reply(OProto, Function, ReplyMessageType, Reply) ->
David Reisse1a79982008-06-10 22:58:14 +0000159 ok = thrift_protocol:write(OProto, #protocol_message_begin{
160 name = atom_to_list(Function),
David Reiss982c72d2008-06-10 22:58:33 +0000161 type = ReplyMessageType,
David Reisse1a79982008-06-10 22:58:14 +0000162 seqid = 0}),
163 ok = thrift_protocol:write(OProto, Reply),
164 ok = thrift_protocol:write(OProto, message_end),
David Reiss90b40832008-06-10 22:58:52 +0000165 ok = thrift_protocol:flush_transport(OProto),
David Reisse1a79982008-06-10 22:58:14 +0000166 ok.
David Reiss982c72d2008-06-10 22:58:33 +0000167
David Reissc11734e2008-06-11 00:59:48 +0000168after_reply(OProto) ->
David Reiss7255ed42008-06-11 01:00:04 +0000169 ok = thrift_protocol:flush_transport(OProto)
170 %% ok = thrift_protocol:close_transport(OProto)
171 .