blob: e17eadba2fb233c3d113561ae7252e025cd42142 [file] [log] [blame]
David Reissea2cba82009-03-30 21:35:00 +00001%%
2%% Licensed to the Apache Software Foundation (ASF) under one
3%% or more contributor license agreements. See the NOTICE file
4%% distributed with this work for additional information
5%% regarding copyright ownership. The ASF licenses this file
6%% to you under the Apache License, Version 2.0 (the
7%% "License"); you may not use this file except in compliance
8%% with the License. You may obtain a copy of the License at
9%%
10%% http://www.apache.org/licenses/LICENSE-2.0
11%%
12%% Unless required by applicable law or agreed to in writing,
13%% software distributed under the License is distributed on an
14%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15%% KIND, either express or implied. See the License for the
16%% specific language governing permissions and limitations
17%% under the License.
18%%
19
David Reissac549552008-06-10 22:56:59 +000020-module(thrift_processor).
21
David Reissc11734e2008-06-11 00:59:48 +000022-export([init/1]).
David Reissac549552008-06-10 22:56:59 +000023
24-include("thrift_constants.hrl").
25-include("thrift_protocol.hrl").
26
Sergei Elin45764092022-09-23 23:21:31 +030027-record(thrift_processor, {
28 handler :: module(),
29 protocol :: term(),
30 service :: atom()
31}).
David Reissac549552008-06-10 22:56:59 +000032
David Reiss5ed313d2010-08-30 22:05:57 +000033init({_Server, ProtoGen, Service, Handler}) when is_function(ProtoGen, 0) ->
David Reiss035979f2010-08-30 22:05:38 +000034 {ok, Proto} = ProtoGen(),
Sergei Elin45764092022-09-23 23:21:31 +030035 loop(#thrift_processor{
36 protocol = Proto,
37 service = Service,
38 handler = Handler
39 }).
David Reissac549552008-06-10 22:56:59 +000040
Sergei Elin45764092022-09-23 23:21:31 +030041loop(
42 State0 = #thrift_processor{
43 protocol = Proto0,
44 handler = Handler,
45 service = Service
46 }
47) ->
David Reiss035979f2010-08-30 22:05:38 +000048 {Proto1, MessageBegin} = thrift_protocol:read(Proto0, message_begin),
49 State1 = State0#thrift_processor{protocol = Proto1},
David Robakowskiae971ce2013-08-02 12:16:00 +020050
51 ErrorHandler = fun
Sergei Elin45764092022-09-23 23:21:31 +030052 (HandlerModules) when is_list(HandlerModules) ->
53 thrift_multiplexed_map_wrapper:fetch(?MULTIPLEXED_ERROR_HANDLER_KEY, HandlerModules);
54 (HandlerModule) ->
55 HandlerModule
David Robakowskiae971ce2013-08-02 12:16:00 +020056 end,
57
David Reiss035979f2010-08-30 22:05:38 +000058 case MessageBegin of
Sergei Elin45764092022-09-23 23:21:31 +030059 #protocol_message_begin{
60 name = Function,
61 type = Type,
62 seqid = Seqid
63 } when Type =:= ?tMessageType_CALL; Type =:= ?tMessageType_ONEWAY ->
David Robakowskiae971ce2013-08-02 12:16:00 +020064 case string:tokens(Function, ?MULTIPLEXED_SERVICE_SEPARATOR) of
65 [ServiceName, FunctionName] ->
Sergei Elin45764092022-09-23 23:21:31 +030066 ServiceModule = thrift_multiplexed_map_wrapper:fetch(ServiceName, Service),
David Robakowskiae971ce2013-08-02 12:16:00 +020067 ServiceHandler = thrift_multiplexed_map_wrapper:fetch(ServiceName, Handler),
Sergei Elin45764092022-09-23 23:21:31 +030068 case
69 handle_function(
70 State1#thrift_processor{
71 service = ServiceModule, handler = ServiceHandler
72 },
73 list_to_existing_atom(FunctionName),
74 Seqid
75 )
76 of
77 {State2, ok} ->
78 loop(State2#thrift_processor{service = Service, handler = Handler});
David Robakowskiae971ce2013-08-02 12:16:00 +020079 {_State2, {error, Reason}} ->
Sergei Elin45764092022-09-23 23:21:31 +030080 apply(ErrorHandler(Handler), handle_error, [
81 list_to_existing_atom(Function), Reason
82 ]),
David Robakowskiae971ce2013-08-02 12:16:00 +020083 thrift_protocol:close_transport(Proto1),
84 ok
85 end;
86 _ ->
Sergei Elin45764092022-09-23 23:21:31 +030087 case handle_function(State1, list_to_existing_atom(Function), Seqid) of
88 {State2, ok} ->
89 loop(State2);
David Robakowskiae971ce2013-08-02 12:16:00 +020090 {_State2, {error, Reason}} ->
Sergei Elin45764092022-09-23 23:21:31 +030091 apply(ErrorHandler(Handler), handle_error, [
92 list_to_existing_atom(Function), Reason
93 ]),
David Robakowskiae971ce2013-08-02 12:16:00 +020094 thrift_protocol:close_transport(Proto1),
95 ok
96 end
Roger Meier3f972b12012-05-18 11:35:51 +000097 end;
98 {error, timeout = Reason} ->
Sergei Elin45764092022-09-23 23:21:31 +030099 apply(ErrorHandler(Handler), handle_error, [undefined, Reason]),
David Reiss035979f2010-08-30 22:05:38 +0000100 thrift_protocol:close_transport(Proto1),
David Reissc49dd1e2008-06-11 01:02:39 +0000101 ok;
Roger Meier3f972b12012-05-18 11:35:51 +0000102 {error, closed = Reason} ->
David Reissc11734e2008-06-11 00:59:48 +0000103 %% error_logger:info_msg("Client disconnected~n"),
Sergei Elin45764092022-09-23 23:21:31 +0300104 apply(ErrorHandler(Handler), handle_error, [undefined, Reason]),
Roger Meier3f972b12012-05-18 11:35:51 +0000105 thrift_protocol:close_transport(Proto1),
106 exit(shutdown);
107 {error, Reason} ->
Sergei Elin45764092022-09-23 23:21:31 +0300108 apply(ErrorHandler(Handler), handle_error, [undefined, Reason]),
David Reiss035979f2010-08-30 22:05:38 +0000109 thrift_protocol:close_transport(Proto1),
David Reiss80664fe2008-06-11 01:00:30 +0000110 exit(shutdown)
David Reissac549552008-06-10 22:56:59 +0000111 end.
David Reissae756f42008-06-10 22:57:11 +0000112
Sergei Elin45764092022-09-23 23:21:31 +0300113handle_function(
114 State0 = #thrift_processor{
115 protocol = Proto0,
116 handler = Handler,
117 service = Service
118 },
119 Function,
120 Seqid
121) ->
David Reiss1c1ca742008-06-10 22:57:21 +0000122 InParams = Service:function_info(Function, params_type),
123
David Reiss035979f2010-08-30 22:05:38 +0000124 {Proto1, {ok, Params}} = thrift_protocol:read(Proto0, InParams),
125 State1 = State0#thrift_processor{protocol = Proto1},
David Reiss76f0d112008-06-10 22:57:35 +0000126
David Reiss982c72d2008-06-10 22:58:33 +0000127 try
David Reiss11d855c2008-06-11 00:59:12 +0000128 Result = Handler:handle_function(Function, Params),
David Reissc11734e2008-06-11 00:59:48 +0000129 %% {Micro, Result} = better_timer(Handler, handle_function, [Function, Params]),
130 %% error_logger:info_msg("Processed ~p(~p) in ~.4fms~n",
131 %% [Function, Params, Micro/1000.0]),
Bryan Duxburya2cceb42011-03-02 18:25:24 +0000132 handle_success(State1, Function, Result, Seqid)
David Reiss982c72d2008-06-10 22:58:33 +0000133 catch
Sergei Elin45764092022-09-23 23:21:31 +0300134 Type:Data:Stack when Type =:= throw orelse Type =:= error ->
135 handle_function_catch(State1, Function, Type, Data, Seqid, Stack)
David Reiss4b0534a2010-08-30 22:05:37 +0000136 end.
David Reiss5541d652008-06-11 00:57:42 +0000137
Sergei Elin45764092022-09-23 23:21:31 +0300138handle_function_catch(
139 State = #thrift_processor{service = Service},
140 Function,
141 ErrType,
142 ErrData,
143 Seqid,
144 Stack
145) ->
David Reissfe931d12009-03-24 20:02:08 +0000146 IsOneway = Service:function_info(Function, reply_type) =:= oneway_void,
David Reiss5541d652008-06-11 00:57:42 +0000147
148 case {ErrType, ErrData} of
David Reiss6ce401d2009-03-24 20:01:58 +0000149 _ when IsOneway ->
David Reiss5541d652008-06-11 00:57:42 +0000150 error_logger:warning_msg(
Sergei Elin45764092022-09-23 23:21:31 +0300151 "oneway void ~p threw error which must be ignored: ~p",
152 [Function, {ErrType, ErrData, Stack}]
153 ),
David Reiss035979f2010-08-30 22:05:38 +0000154 {State, ok};
David Reiss5541d652008-06-11 00:57:42 +0000155 {throw, Exception} when is_tuple(Exception), size(Exception) > 0 ->
David Reissc0e4b5b2010-08-30 22:05:12 +0000156 %error_logger:warning_msg("~p threw exception: ~p~n", [Function, Exception]),
Sergei Elin45764092022-09-23 23:21:31 +0300157 handle_exception(State, Function, Exception, Seqid, Stack);
158 % we still want to accept more requests from this client
David Reiss5541d652008-06-11 00:57:42 +0000159
160 {error, Error} ->
Sergei Elin45764092022-09-23 23:21:31 +0300161 handle_error(State, Function, Error, Seqid, Stack)
David Reiss982c72d2008-06-10 22:58:33 +0000162 end.
163
Sergei Elin45764092022-09-23 23:21:31 +0300164handle_success(
165 State = #thrift_processor{service = Service},
166 Function,
167 Result,
168 Seqid
169) ->
170 ReplyType = Service:function_info(Function, reply_type),
David Reisse1a79982008-06-10 22:58:14 +0000171 StructName = atom_to_list(Function) ++ "_result",
David Reiss11d855c2008-06-11 00:59:12 +0000172
David Reiss035979f2010-08-30 22:05:38 +0000173 case Result of
174 {reply, ReplyData} ->
175 Reply = {{struct, [{0, ReplyType}]}, {StructName, ReplyData}},
Bryan Duxburya2cceb42011-03-02 18:25:24 +0000176 send_reply(State, Function, ?tMessageType_REPLY, Reply, Seqid);
David Reiss035979f2010-08-30 22:05:38 +0000177 ok when ReplyType == {struct, []} ->
Bryan Duxburya2cceb42011-03-02 18:25:24 +0000178 send_reply(State, Function, ?tMessageType_REPLY, {ReplyType, {StructName}}, Seqid);
David Reiss035979f2010-08-30 22:05:38 +0000179 ok when ReplyType == oneway_void ->
180 %% no reply for oneway void
181 {State, ok}
182 end.
David Reisse1a79982008-06-10 22:58:14 +0000183
Sergei Elin45764092022-09-23 23:21:31 +0300184handle_exception(
185 State = #thrift_processor{service = Service},
186 Function,
187 Exception,
188 Seqid,
189 Stack
190) ->
David Reiss982c72d2008-06-10 22:58:33 +0000191 ExceptionType = element(1, Exception),
David Reissc11734e2008-06-11 00:59:48 +0000192 %% Fetch a structure like {struct, [{-2, {struct, {Module, Type}}},
193 %% {-3, {struct, {Module, Type}}}]}
David Reisse1a79982008-06-10 22:58:14 +0000194
David Reiss982c72d2008-06-10 22:58:33 +0000195 ReplySpec = Service:function_info(Function, exceptions),
196 {struct, XInfo} = ReplySpec,
197
198 true = is_list(XInfo),
David Reiss982c72d2008-06-10 22:58:33 +0000199
David Reissc11734e2008-06-11 00:59:48 +0000200 %% Assuming we had a type1 exception, we'd get: [undefined, Exception, undefined]
201 %% e.g.: [{-1, type0}, {-2, type1}, {-3, type2}]
Sergei Elin45764092022-09-23 23:21:31 +0300202 ExceptionList = [
203 case Type of
204 ExceptionType -> Exception;
205 _ -> undefined
206 end
207 || {_Fid, {struct, {_Module, Type}}} <- XInfo
208 ],
David Reiss11d855c2008-06-11 00:59:12 +0000209
David Reiss982c72d2008-06-10 22:58:33 +0000210 ExceptionTuple = list_to_tuple([Function | ExceptionList]),
David Reiss11d855c2008-06-11 00:59:12 +0000211
Sergei Elin45764092022-09-23 23:21:31 +0300212 % Make sure we got at least one defined
David Reiss982c72d2008-06-10 22:58:33 +0000213 case lists:all(fun(X) -> X =:= undefined end, ExceptionList) of
214 true ->
Sergei Elin45764092022-09-23 23:21:31 +0300215 handle_unknown_exception(State, Function, Exception, Seqid, Stack);
David Reiss982c72d2008-06-10 22:58:33 +0000216 false ->
Bryan Duxburya2cceb42011-03-02 18:25:24 +0000217 send_reply(State, Function, ?tMessageType_REPLY, {ReplySpec, ExceptionTuple}, Seqid)
David Reiss982c72d2008-06-10 22:58:33 +0000218 end.
219
David Reiss920959a2008-06-11 00:56:35 +0000220%%
David Reissc11734e2008-06-11 00:59:48 +0000221%% Called when an exception has been explicitly thrown by the service, but it was
222%% not one of the exceptions that was defined for the function.
David Reiss920959a2008-06-11 00:56:35 +0000223%%
Sergei Elin45764092022-09-23 23:21:31 +0300224handle_unknown_exception(State, Function, Exception, Seqid, Stack) ->
225 handle_error(State, Function, {exception_not_declared_as_thrown, Exception}, Seqid, Stack).
David Reiss920959a2008-06-11 00:56:35 +0000226
Sergei Elin45764092022-09-23 23:21:31 +0300227handle_error(State, Function, Error, Seqid, Stack) ->
David Reissda070672008-06-11 00:56:42 +0000228 error_logger:error_msg("~p had an error: ~p~n", [Function, {Error, Stack}]),
David Reiss11d855c2008-06-11 00:59:12 +0000229
David Reiss920959a2008-06-11 00:56:35 +0000230 Message =
231 case application:get_env(thrift, exceptions_include_traces) of
232 {ok, true} ->
Sergei Elin45764092022-09-23 23:21:31 +0300233 lists:flatten(
234 io_lib:format(
235 "An error occurred: ~p~n",
236 [{Error, Stack}]
237 )
238 );
David Reiss920959a2008-06-11 00:56:35 +0000239 _ ->
240 "An unknown handler error occurred."
241 end,
Sergei Elin45764092022-09-23 23:21:31 +0300242 Reply =
243 {?TApplicationException_Structure, #'TApplicationException'{
244 message = Message,
245 type = ?TApplicationException_UNKNOWN
246 }},
Bryan Duxburya2cceb42011-03-02 18:25:24 +0000247 send_reply(State, Function, ?tMessageType_EXCEPTION, Reply, Seqid).
David Reiss982c72d2008-06-10 22:58:33 +0000248
Bryan Duxburya2cceb42011-03-02 18:25:24 +0000249send_reply(State = #thrift_processor{protocol = Proto0}, Function, ReplyMessageType, Reply, Seqid) ->
Roger Meier3f972b12012-05-18 11:35:51 +0000250 try
251 {Proto1, ok} = thrift_protocol:write(Proto0, #protocol_message_begin{
Sergei Elin45764092022-09-23 23:21:31 +0300252 name = atom_to_list(Function),
253 type = ReplyMessageType,
254 seqid = Seqid
255 }),
Roger Meier3f972b12012-05-18 11:35:51 +0000256 {Proto2, ok} = thrift_protocol:write(Proto1, Reply),
257 {Proto3, ok} = thrift_protocol:write(Proto2, message_end),
258 {Proto4, ok} = thrift_protocol:flush_transport(Proto3),
259 {State#thrift_processor{protocol = Proto4}, ok}
260 catch
261 error:{badmatch, {_, {error, _} = Error}} ->
262 {State, Error}
263 end.