blob: fa33d3bc03332f03ed81021342f7805e93feaa01 [file] [log] [blame]
David Reissac549552008-06-10 22:56:59 +00001%%%-------------------------------------------------------------------
2%%% File : thrift_processor.erl
3%%% Author : <todd@lipcon.org>
4%%% Description :
5%%%
6%%% Created : 28 Jan 2008 by <todd@lipcon.org>
7%%%-------------------------------------------------------------------
8-module(thrift_processor).
9
10-export([start/4,init/4]).
11
12-include("thrift_constants.hrl").
13-include("thrift_protocol.hrl").
14
15-record(state, {handler, in_protocol, out_protocol, service}).
16
17start(IProt, OProt, Service, Handler) ->
18 spawn(thrift_processor, init, [IProt, OProt, Service, Handler]).
19
20init(IProt, OProt, Service, Handler) ->
21 io:format("Processor started~n"),
22 loop(#state{in_protocol = IProt,
23 out_protocol = OProt,
24 service = Service,
25 handler = Handler}).
26
27loop(State = #state{in_protocol = IProto,
28 out_protocol = OProto}) ->
David Reissae756f42008-06-10 22:57:11 +000029 case thrift_protocol:read(IProto, message_begin) of
30 #protocol_message_begin{name = Function,
31 type = ?tMessageType_CALL} ->
David Reissd93521b2008-06-10 22:58:07 +000032 ok= handle_function(State, list_to_atom(Function)),
David Reissae756f42008-06-10 22:57:11 +000033 loop(State);
34 {error, closed} ->
David Reiss76f0d112008-06-10 22:57:35 +000035 error_logger:info_msg("Client disconnected~n"),
David Reissae756f42008-06-10 22:57:11 +000036 ok
David Reissac549552008-06-10 22:56:59 +000037 end.
David Reissae756f42008-06-10 22:57:11 +000038
David Reiss1c1ca742008-06-10 22:57:21 +000039
40handle_function(State = #state{in_protocol = IProto,
41 out_protocol = OProto,
42 handler = Handler,
43 service = Service},
44 Function) ->
45 InParams = Service:function_info(Function, params_type),
46
47 {ok, Params} = thrift_protocol:read(IProto, InParams),
David Reiss76f0d112008-06-10 22:57:35 +000048
David Reiss982c72d2008-06-10 22:58:33 +000049 try
50 {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]),
53 handle_success(State, Function, Result)
54 catch
55 throw:Exception when is_tuple(Exception), size(Exception) > 0 ->
56 error_logger:warning_msg("~p threw exception: ~p~n", [Function, Exception]),
57 handle_exception(State, Function, Exception),
58 ok % we still want to accept more requests from this client
59 end.
60
61handle_success(State = #state{out_protocol = OProto,
62 service = Service},
63 Function,
64 Result) ->
David Reiss1c1ca742008-06-10 22:57:21 +000065 ReplyType = Service:function_info(Function, reply_type),
David Reisse1a79982008-06-10 22:58:14 +000066 StructName = atom_to_list(Function) ++ "_result",
David Reiss1c1ca742008-06-10 22:57:21 +000067
68 case Result of
David Reisse1a79982008-06-10 22:58:14 +000069 {reply, ReplyData} ->
70 Reply = {{struct, [{0, ReplyType}]}, {StructName, ReplyData}},
David Reiss982c72d2008-06-10 22:58:33 +000071 ok = send_reply(OProto, Function, ?tMessageType_REPLY, Reply);
David Reisse1a79982008-06-10 22:58:14 +000072
73 ok when ReplyType == {struct, []} ->
David Reiss982c72d2008-06-10 22:58:33 +000074 ok = send_reply(OProto, Function, ?tMessageType_REPLY, {ReplyType, {StructName}})
David Reiss1c1ca742008-06-10 22:57:21 +000075 end,
76 ok.
David Reisse1a79982008-06-10 22:58:14 +000077
David Reiss982c72d2008-06-10 22:58:33 +000078handle_exception(State = #state{out_protocol = OProto,
79 service = Service},
80 Function,
81 Exception) ->
82 ExceptionType = element(1, Exception),
83 % Fetch a structure like {struct, [{-2, {struct, {Module, Type}}},
84 % {-3, {struct, {Module, Type}}}]}
David Reisse1a79982008-06-10 22:58:14 +000085
David Reiss982c72d2008-06-10 22:58:33 +000086 ReplySpec = Service:function_info(Function, exceptions),
87 {struct, XInfo} = ReplySpec,
88
89 true = is_list(XInfo),
90
91 % e.g.: [{-1, type0}, {-2, type1}, {-3, type2}]
92 XPairs = [{Fid, Type} || {Fid, {struct, {_Module, Type}}} <- XInfo],
93
94 Mapper = fun({Fid, Type}) ->
95 case Type of
96 ExceptionType ->
97 Exception;
98 _ ->
99 undefined
100 end
101 end,
102 % Assuming we had a type1 exception, we get: [undefined, Exception, undefined]
103 ExceptionList = lists:map(Mapper, XPairs),
104 ExceptionTuple = list_to_tuple([Function | ExceptionList]),
105
106 % Make sure we got at least one defined
107 case lists:all(fun(X) -> X =:= undefined end, ExceptionList) of
108 true ->
109 ok = handle_unknown_exception(State, Function, Exception);
110 false ->
111 ok = send_reply(OProto, Function, ?tMessageType_REPLY, {ReplySpec, ExceptionTuple})
112 end.
113
114handle_unknown_exception(State, Function, Exception) ->
115 io:format("Unknown exception!~n"),
116 ok.
117
118
119send_reply(OProto, Function, ReplyMessageType, Reply) ->
David Reisse1a79982008-06-10 22:58:14 +0000120 ok = thrift_protocol:write(OProto, #protocol_message_begin{
121 name = atom_to_list(Function),
David Reiss982c72d2008-06-10 22:58:33 +0000122 type = ReplyMessageType,
David Reisse1a79982008-06-10 22:58:14 +0000123 seqid = 0}),
124 ok = thrift_protocol:write(OProto, Reply),
125 ok = thrift_protocol:write(OProto, message_end),
David Reiss90b40832008-06-10 22:58:52 +0000126 ok = thrift_protocol:flush_transport(OProto),
David Reisse1a79982008-06-10 22:58:14 +0000127 ok.
David Reiss982c72d2008-06-10 22:58:33 +0000128
129
130%%
131% This is the same as timer:tc except that timer:tc appears to catch
132% exceptions when it shouldn't!
133%%
134better_timer(Module, Function, Args) ->
135 T1 = erlang:now(),
136 Result = apply(Module, Function, Args),
137 T2 = erlang:now(),
138 {timer:now_diff(T2, T1), Result}.