blob: b751da6e802387267436e8bf85f2d5b10ecc446e [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
David Reissf32d0fb2010-08-30 22:05:00 +000027-record(thrift_processor, {handler, in_protocol, out_protocol, service}).
David Reissac549552008-06-10 22:56:59 +000028
David Reissf32d0fb2010-08-30 22:05:00 +000029init({Server, ProtoGen, Service, Handler}) when is_function(ProtoGen, 0) ->
30 {ok, IProt, OProt} = ProtoGen(),
31 loop(#thrift_processor{in_protocol = IProt,
32 out_protocol = OProt,
David Reissc11734e2008-06-11 00:59:48 +000033 service = Service,
34 handler = Handler}).
David Reissac549552008-06-10 22:56:59 +000035
David Reissf32d0fb2010-08-30 22:05:00 +000036loop(State = #thrift_processor{in_protocol = IProto,
37 out_protocol = OProto}) ->
38 case thrift_protocol:read(IProto, message_begin) of
David Reissae756f42008-06-10 22:57:11 +000039 #protocol_message_begin{name = Function,
40 type = ?tMessageType_CALL} ->
David Reissf32d0fb2010-08-30 22:05:00 +000041 ok = handle_function(State, list_to_atom(Function)),
42 loop(State);
David Reissdeda1412009-04-02 19:22:31 +000043 #protocol_message_begin{name = Function,
44 type = ?tMessageType_ONEWAY} ->
David Reissf32d0fb2010-08-30 22:05:00 +000045 ok = handle_function(State, list_to_atom(Function)),
46 loop(State);
David Reissc49dd1e2008-06-11 01:02:39 +000047 {error, timeout} ->
David Reissf32d0fb2010-08-30 22:05:00 +000048 thrift_protocol:close_transport(OProto),
David Reissc49dd1e2008-06-11 01:02:39 +000049 ok;
David Reissae756f42008-06-10 22:57:11 +000050 {error, closed} ->
David Reissc11734e2008-06-11 00:59:48 +000051 %% error_logger:info_msg("Client disconnected~n"),
David Reissf32d0fb2010-08-30 22:05:00 +000052 thrift_protocol:close_transport(OProto),
David Reiss80664fe2008-06-11 01:00:30 +000053 exit(shutdown)
David Reissac549552008-06-10 22:56:59 +000054 end.
David Reissae756f42008-06-10 22:57:11 +000055
David Reissf32d0fb2010-08-30 22:05:00 +000056handle_function(State=#thrift_processor{in_protocol = IProto,
57 out_protocol = OProto,
58 handler = Handler,
59 service = Service},
David Reiss1c1ca742008-06-10 22:57:21 +000060 Function) ->
61 InParams = Service:function_info(Function, params_type),
62
David Reissf32d0fb2010-08-30 22:05:00 +000063 {ok, Params} = thrift_protocol:read(IProto, InParams),
David Reiss76f0d112008-06-10 22:57:35 +000064
David Reiss982c72d2008-06-10 22:58:33 +000065 try
David Reiss11d855c2008-06-11 00:59:12 +000066 Result = Handler:handle_function(Function, Params),
David Reissc11734e2008-06-11 00:59:48 +000067 %% {Micro, Result} = better_timer(Handler, handle_function, [Function, Params]),
68 %% error_logger:info_msg("Processed ~p(~p) in ~.4fms~n",
69 %% [Function, Params, Micro/1000.0]),
David Reissf32d0fb2010-08-30 22:05:00 +000070 handle_success(State, Function, Result)
David Reiss982c72d2008-06-10 22:58:33 +000071 catch
David Reissb9561c82010-08-30 22:05:36 +000072 Type:Data when Type =:= throw orelse Type =:= error ->
David Reissf32d0fb2010-08-30 22:05:00 +000073 handle_function_catch(State, Function, Type, Data)
David Reiss4b0534a2010-08-30 22:05:37 +000074 end.
David Reiss5541d652008-06-11 00:57:42 +000075
David Reissc11734e2008-06-11 00:59:48 +000076handle_function_catch(State = #thrift_processor{service = Service},
David Reiss5541d652008-06-11 00:57:42 +000077 Function, ErrType, ErrData) ->
David Reissfe931d12009-03-24 20:02:08 +000078 IsOneway = Service:function_info(Function, reply_type) =:= oneway_void,
David Reiss5541d652008-06-11 00:57:42 +000079
80 case {ErrType, ErrData} of
David Reiss6ce401d2009-03-24 20:01:58 +000081 _ when IsOneway ->
David Reiss4ec777e2008-06-11 01:01:29 +000082 Stack = erlang:get_stacktrace(),
David Reiss5541d652008-06-11 00:57:42 +000083 error_logger:warning_msg(
David Reiss6ce401d2009-03-24 20:01:58 +000084 "oneway void ~p threw error which must be ignored: ~p",
David Reiss4ec777e2008-06-11 01:01:29 +000085 [Function, {ErrType, ErrData, Stack}]),
David Reissf32d0fb2010-08-30 22:05:00 +000086 ok;
David Reiss5541d652008-06-11 00:57:42 +000087
88 {throw, Exception} when is_tuple(Exception), size(Exception) > 0 ->
David Reissc0e4b5b2010-08-30 22:05:12 +000089 %error_logger:warning_msg("~p threw exception: ~p~n", [Function, Exception]),
David Reissf32d0fb2010-08-30 22:05:00 +000090 handle_exception(State, Function, Exception),
91 ok; % we still want to accept more requests from this client
David Reiss5541d652008-06-11 00:57:42 +000092
93 {error, Error} ->
David Reissf32d0fb2010-08-30 22:05:00 +000094 ok = handle_error(State, Function, Error)
David Reiss982c72d2008-06-10 22:58:33 +000095 end.
96
David Reissf32d0fb2010-08-30 22:05:00 +000097handle_success(State = #thrift_processor{out_protocol = OProto,
98 service = Service},
David Reiss982c72d2008-06-10 22:58:33 +000099 Function,
100 Result) ->
David Reiss11d855c2008-06-11 00:59:12 +0000101 ReplyType = Service:function_info(Function, reply_type),
David Reisse1a79982008-06-10 22:58:14 +0000102 StructName = atom_to_list(Function) ++ "_result",
David Reiss11d855c2008-06-11 00:59:12 +0000103
David Reissf32d0fb2010-08-30 22:05:00 +0000104 ok = case Result of
105 {reply, ReplyData} ->
106 Reply = {{struct, [{0, ReplyType}]}, {StructName, ReplyData}},
107 send_reply(OProto, Function, ?tMessageType_REPLY, Reply);
David Reisse1a79982008-06-10 22:58:14 +0000108
David Reissf32d0fb2010-08-30 22:05:00 +0000109 ok when ReplyType == {struct, []} ->
110 send_reply(OProto, Function, ?tMessageType_REPLY, {ReplyType, {StructName}});
David Reiss11d855c2008-06-11 00:59:12 +0000111
David Reissf32d0fb2010-08-30 22:05:00 +0000112 ok when ReplyType == oneway_void ->
113 %% no reply for oneway void
114 ok
115 end.
David Reisse1a79982008-06-10 22:58:14 +0000116
David Reissf32d0fb2010-08-30 22:05:00 +0000117handle_exception(State = #thrift_processor{out_protocol = OProto,
118 service = Service},
David Reiss982c72d2008-06-10 22:58:33 +0000119 Function,
120 Exception) ->
121 ExceptionType = element(1, Exception),
David Reissc11734e2008-06-11 00:59:48 +0000122 %% Fetch a structure like {struct, [{-2, {struct, {Module, Type}}},
123 %% {-3, {struct, {Module, Type}}}]}
David Reisse1a79982008-06-10 22:58:14 +0000124
David Reiss982c72d2008-06-10 22:58:33 +0000125 ReplySpec = Service:function_info(Function, exceptions),
126 {struct, XInfo} = ReplySpec,
127
128 true = is_list(XInfo),
David Reiss982c72d2008-06-10 22:58:33 +0000129
David Reissc11734e2008-06-11 00:59:48 +0000130 %% Assuming we had a type1 exception, we'd get: [undefined, Exception, undefined]
131 %% e.g.: [{-1, type0}, {-2, type1}, {-3, type2}]
David Reiss11d855c2008-06-11 00:59:12 +0000132 ExceptionList = [case Type of
133 ExceptionType -> Exception;
134 _ -> undefined
David Reiss982c72d2008-06-10 22:58:33 +0000135 end
David Reiss11d855c2008-06-11 00:59:12 +0000136 || {_Fid, {struct, {_Module, Type}}} <- XInfo],
137
David Reiss982c72d2008-06-10 22:58:33 +0000138 ExceptionTuple = list_to_tuple([Function | ExceptionList]),
David Reiss11d855c2008-06-11 00:59:12 +0000139
David Reissc11734e2008-06-11 00:59:48 +0000140 % Make sure we got at least one defined
David Reiss982c72d2008-06-10 22:58:33 +0000141 case lists:all(fun(X) -> X =:= undefined end, ExceptionList) of
142 true ->
David Reissf32d0fb2010-08-30 22:05:00 +0000143 ok = handle_unknown_exception(State, Function, Exception);
David Reiss982c72d2008-06-10 22:58:33 +0000144 false ->
David Reissf32d0fb2010-08-30 22:05:00 +0000145 ok = send_reply(OProto, Function, ?tMessageType_REPLY, {ReplySpec, ExceptionTuple})
David Reiss982c72d2008-06-10 22:58:33 +0000146 end.
147
David Reiss920959a2008-06-11 00:56:35 +0000148%%
David Reissc11734e2008-06-11 00:59:48 +0000149%% Called when an exception has been explicitly thrown by the service, but it was
150%% not one of the exceptions that was defined for the function.
David Reiss920959a2008-06-11 00:56:35 +0000151%%
David Reiss982c72d2008-06-10 22:58:33 +0000152handle_unknown_exception(State, Function, Exception) ->
David Reiss920959a2008-06-11 00:56:35 +0000153 handle_error(State, Function, {exception_not_declared_as_thrown,
154 Exception}).
155
David Reissf32d0fb2010-08-30 22:05:00 +0000156handle_error(#thrift_processor{out_protocol = OProto}, Function, Error) ->
David Reissda070672008-06-11 00:56:42 +0000157 Stack = erlang:get_stacktrace(),
158 error_logger:error_msg("~p had an error: ~p~n", [Function, {Error, Stack}]),
David Reiss11d855c2008-06-11 00:59:12 +0000159
David Reiss920959a2008-06-11 00:56:35 +0000160 Message =
161 case application:get_env(thrift, exceptions_include_traces) of
162 {ok, true} ->
163 lists:flatten(io_lib:format("An error occurred: ~p~n",
David Reissda070672008-06-11 00:56:42 +0000164 [{Error, Stack}]));
David Reiss920959a2008-06-11 00:56:35 +0000165 _ ->
166 "An unknown handler error occurred."
167 end,
168 Reply = {?TApplicationException_Structure,
169 #'TApplicationException'{
170 message = Message,
171 type = ?TApplicationException_UNKNOWN}},
David Reissf32d0fb2010-08-30 22:05:00 +0000172 send_reply(OProto, Function, ?tMessageType_EXCEPTION, Reply).
David Reiss982c72d2008-06-10 22:58:33 +0000173
David Reissf32d0fb2010-08-30 22:05:00 +0000174send_reply(OProto, Function, ReplyMessageType, Reply) ->
175 ok = thrift_protocol:write(OProto, #protocol_message_begin{
176 name = atom_to_list(Function),
177 type = ReplyMessageType,
178 seqid = 0}),
179 ok = thrift_protocol:write(OProto, Reply),
180 ok = thrift_protocol:write(OProto, message_end),
181 ok = thrift_protocol:flush_transport(OProto),
182 ok.