mirror of
https://github.com/valitydev/woody_erlang.git
synced 2024-11-06 02:15:19 +00:00
Allow to set custom codec in client / server (#23)
Which is reponsible for: * (de)serializing requests / responses * providing request / response metadata for events Also permit wider service / args models depending on chosen server / client codecs. Backport #22.
This commit is contained in:
parent
0301168d04
commit
2866b658ea
@ -34,10 +34,10 @@
|
||||
|
||||
%% Thrift
|
||||
-type service_name() :: atom().
|
||||
-type service() :: {module(), service_name()}.
|
||||
-type service() :: {module(), service_name()} | any().
|
||||
-type context() :: woody_context:ctx().
|
||||
-type func() :: atom().
|
||||
-type args() :: tuple().
|
||||
-type args() :: tuple() | any().
|
||||
-type request() :: {service(), func(), args()}.
|
||||
-type result() :: _.
|
||||
-type th_handler() :: {service(), handler(options())}.
|
||||
|
81
src/woody_client_codec.erl
Normal file
81
src/woody_client_codec.erl
Normal file
@ -0,0 +1,81 @@
|
||||
-module(woody_client_codec).
|
||||
|
||||
-type codec() :: module().
|
||||
|
||||
-export_type([codec/0]).
|
||||
|
||||
-export([get_service_name/2]).
|
||||
-export([get_rpc_type/3]).
|
||||
|
||||
-export([write_call/6]).
|
||||
-export([read_result/5]).
|
||||
|
||||
-type buffer() :: iodata().
|
||||
-type service() :: woody:service().
|
||||
-type func() :: woody:func().
|
||||
-type args() :: woody:args().
|
||||
-type seqid() :: non_neg_integer().
|
||||
|
||||
-type result() ::
|
||||
ok
|
||||
| {reply, woody:result()}
|
||||
| {exception, woody:result()}.
|
||||
|
||||
-callback get_service_name(service()) ->
|
||||
woody:service_name().
|
||||
|
||||
-callback get_rpc_type(service(), func()) ->
|
||||
woody:rpc_type().
|
||||
|
||||
-callback write_call(buffer(), service(), func(), args(), seqid()) ->
|
||||
{ok, buffer()}
|
||||
| {error, _Reason}.
|
||||
|
||||
-callback read_result(buffer(), service(), func(), seqid()) ->
|
||||
{ok, result(), _Rest :: buffer()}
|
||||
| {error, _Reason}.
|
||||
|
||||
%%
|
||||
|
||||
-spec get_service_name(codec(), service()) ->
|
||||
woody:service_name().
|
||||
get_service_name(thrift_client_codec, {_Module, Service}) ->
|
||||
Service;
|
||||
get_service_name(Codec, Service) ->
|
||||
Codec:get_service_name(Service).
|
||||
|
||||
-spec get_rpc_type(codec(), service(), func()) ->
|
||||
woody:rpc_type().
|
||||
get_rpc_type(thrift_client_codec, Service, Function) ->
|
||||
woody_util:get_rpc_type(Service, Function);
|
||||
get_rpc_type(Codec, Service, Function) ->
|
||||
Codec:get_rpc_type(Service, Function).
|
||||
|
||||
-spec write_call(codec(), buffer(), service(), func(), args(), seqid()) ->
|
||||
{ok, buffer()}
|
||||
| {error, _Reason}.
|
||||
write_call(thrift_client_codec, Buffer, Service, Function, Args, SeqId) ->
|
||||
thrift_client_codec:write_function_call(
|
||||
Buffer,
|
||||
thrift_strict_binary_codec,
|
||||
Service,
|
||||
Function,
|
||||
Args,
|
||||
SeqId
|
||||
);
|
||||
write_call(Codec, Buffer, Service, Function, Args, SeqId) ->
|
||||
Codec:write_call(Buffer, Service, Function, Args, SeqId).
|
||||
|
||||
-spec read_result(codec(), buffer(), service(), func(), seqid()) ->
|
||||
{ok, result(), _Rest :: buffer()}
|
||||
| {error, _Reason}.
|
||||
read_result(thrift_client_codec, Buffer, Service, Function, SeqId) ->
|
||||
thrift_client_codec:read_function_result(
|
||||
Buffer,
|
||||
thrift_strict_binary_codec,
|
||||
Service,
|
||||
Function,
|
||||
SeqId
|
||||
);
|
||||
read_result(Codec, Buffer, Service, Function, SeqId) ->
|
||||
Codec:read_result(Buffer, Service, Function, SeqId).
|
@ -16,6 +16,7 @@
|
||||
event_handler := woody:ev_handlers(),
|
||||
transport_opts => transport_options(),
|
||||
resolver_opts => woody_resolver:options(),
|
||||
codec => woody_client_codec:codec(),
|
||||
protocol => thrift,
|
||||
transport => http
|
||||
}.
|
||||
@ -45,22 +46,24 @@ child_spec(Options) ->
|
||||
hackney_pool:child_spec(Name, maps:to_list(TransportOpts)).
|
||||
|
||||
-spec call(woody:request(), options(), woody_state:st()) -> woody_client:result().
|
||||
call({Service = {_, ServiceName}, Function, Args}, Opts, WoodyState) ->
|
||||
call({Service, Function, Args}, Opts, WoodyState) ->
|
||||
WoodyContext = woody_state:get_context(WoodyState),
|
||||
ClientCodec = maps:get(codec, Opts, thrift_client_codec),
|
||||
WoodyState1 = woody_state:add_ev_meta(
|
||||
#{
|
||||
service => ServiceName,
|
||||
service => woody_client_codec:get_service_name(ClientCodec, Service),
|
||||
service_schema => Service,
|
||||
function => Function,
|
||||
type => woody_util:get_rpc_type(Service, Function),
|
||||
type => woody_client_codec:get_rpc_type(ClientCodec, Service, Function),
|
||||
args => Args,
|
||||
codec => ClientCodec,
|
||||
deadline => woody_context:get_deadline(WoodyContext),
|
||||
metadata => woody_context:get_meta(WoodyContext)
|
||||
},
|
||||
WoodyState
|
||||
),
|
||||
_ = log_event(?EV_CALL_SERVICE, WoodyState1, #{}),
|
||||
do_call(Service, Function, Args, Opts, WoodyState1).
|
||||
do_call(ClientCodec, Service, Function, Args, Opts, WoodyState1).
|
||||
|
||||
%%
|
||||
%% Internal functions
|
||||
@ -71,18 +74,25 @@ call({Service = {_, ServiceName}, Function, Args}, Opts, WoodyState) ->
|
||||
-type http_headers() :: [{binary(), binary()}].
|
||||
-type header_parse_value() :: none | woody:http_header_val().
|
||||
|
||||
-define(CODEC, thrift_strict_binary_codec).
|
||||
-define(ERROR_RESP_BODY, <<"parse http response body error">>).
|
||||
-define(ERROR_RESP_HEADER, <<"parse http response headers error">>).
|
||||
-define(BAD_RESP_HEADER, <<"reason unknown due to bad ", ?HEADER_PREFIX/binary, "-error- headers">>).
|
||||
|
||||
-define(SERVER_VIOLATION_ERROR,
|
||||
-define(APPLICATION_EXCEPTION_ERROR,
|
||||
{external, result_unexpected, <<
|
||||
"server violated thrift protocol: "
|
||||
"sent TApplicationException (unknown exception) with http code 200"
|
||||
>>}
|
||||
).
|
||||
|
||||
-define(EXCESS_BODY_ERROR(Bytes, Result),
|
||||
{external, result_unexpected,
|
||||
genlib:format(
|
||||
"server violated thrift protocol: excess ~p bytes in response: ~p",
|
||||
[byte_size(Bytes), Result]
|
||||
)}
|
||||
).
|
||||
|
||||
-spec get_transport_opts(options()) -> woody_client_thrift_http_transport:transport_options().
|
||||
get_transport_opts(Opts) ->
|
||||
maps:get(transport_opts, Opts, #{}).
|
||||
@ -91,15 +101,15 @@ get_transport_opts(Opts) ->
|
||||
get_resolver_opts(Opts) ->
|
||||
maps:get(resolver_opts, Opts, #{}).
|
||||
|
||||
-spec do_call(woody:service(), woody:func(), woody:args(), options(), woody_state:st()) -> woody_client:result().
|
||||
do_call(Service, Function, Args, Opts, WoodyState) ->
|
||||
Buffer = ?CODEC:new(),
|
||||
-spec do_call(module(), woody:service(), woody:func(), woody:args(), options(), woody_state:st()) ->
|
||||
woody_client:result().
|
||||
do_call(Codec, Service, Function, Args, Opts, WoodyState) ->
|
||||
Result =
|
||||
case thrift_client_codec:write_function_call(Buffer, ?CODEC, Service, Function, Args, 0) of
|
||||
case woody_client_codec:write_call(Codec, <<>>, Service, Function, Args, 0) of
|
||||
{ok, Buffer1} ->
|
||||
case send_call(Buffer1, Opts, WoodyState) of
|
||||
{ok, Response} ->
|
||||
handle_result(Service, Function, Response);
|
||||
handle_result(Codec, Service, Function, Response);
|
||||
Error ->
|
||||
Error
|
||||
end;
|
||||
@ -109,32 +119,28 @@ do_call(Service, Function, Args, Opts, WoodyState) ->
|
||||
log_result(Result, WoodyState),
|
||||
map_result(Result).
|
||||
|
||||
-spec handle_result(woody:service(), woody:func(), binary()) -> _Result.
|
||||
handle_result(Service, Function, Response) ->
|
||||
Buffer = ?CODEC:new(Response),
|
||||
case thrift_client_codec:read_function_result(Buffer, ?CODEC, Service, Function, 0) of
|
||||
{ok, Result, Leftovers} ->
|
||||
Bytes = ?CODEC:close(Leftovers),
|
||||
-spec handle_result(module(), woody:service(), woody:func(), binary()) -> _Result.
|
||||
handle_result(Codec, Service, Function, Response) ->
|
||||
case woody_client_codec:read_result(Codec, Response, Service, Function, 0) of
|
||||
{ok, Result, <<>>} ->
|
||||
case Result of
|
||||
ok when Bytes == <<>> ->
|
||||
ok ->
|
||||
{ok, ok};
|
||||
{reply, Reply} when Bytes == <<>> ->
|
||||
{reply, Reply} ->
|
||||
{ok, Reply};
|
||||
{exception, #'TApplicationException'{}} when Bytes == <<>> ->
|
||||
{error, {system, ?SERVER_VIOLATION_ERROR}};
|
||||
{exception, Exception} when Bytes == <<>> ->
|
||||
{error, {business, Exception}};
|
||||
_ ->
|
||||
{error, {excess_response_body, Bytes, Result}}
|
||||
{exception, #'TApplicationException'{}} ->
|
||||
{error, {system, ?APPLICATION_EXCEPTION_ERROR}};
|
||||
{exception, Exception} ->
|
||||
{error, {business, Exception}}
|
||||
end;
|
||||
{ok, Result, Leftovers} ->
|
||||
{error, ?EXCESS_BODY_ERROR(Leftovers, Result)};
|
||||
{error, _} = Error ->
|
||||
Error
|
||||
end.
|
||||
|
||||
%% erlfmt-ignore
|
||||
-spec send_call(?CODEC:buffer(), options(), woody_state:st()) ->
|
||||
{ok, binary()} | {error, {system, _}}.
|
||||
|
||||
-spec send_call(iodata(), options(), woody_state:st()) ->
|
||||
{ok, iodata()} | {error, {system, _}}.
|
||||
send_call(Buffer, #{url := Url} = Opts, WoodyState) ->
|
||||
Context = woody_state:get_context(WoodyState),
|
||||
TransportOpts = get_transport_opts(Opts),
|
||||
|
104
src/woody_server_codec.erl
Normal file
104
src/woody_server_codec.erl
Normal file
@ -0,0 +1,104 @@
|
||||
-module(woody_server_codec).
|
||||
|
||||
-type codec() :: module().
|
||||
|
||||
-export_type([codec/0]).
|
||||
-export_type([invocation/0]).
|
||||
|
||||
-export([get_service_name/2]).
|
||||
|
||||
-export([read_call/3]).
|
||||
-export([write_result/6]).
|
||||
|
||||
-export([catch_business_exception/4]).
|
||||
|
||||
-type buffer() :: iodata().
|
||||
-type service() :: woody:service().
|
||||
-type func() :: woody:func().
|
||||
-type args() :: woody:args().
|
||||
-type seqid() :: non_neg_integer().
|
||||
|
||||
-type invocation() :: {woody:rpc_type(), func(), args()}.
|
||||
|
||||
-type result() ::
|
||||
ok
|
||||
| {reply, woody:result()}
|
||||
| {exception, _Name, woody:result()}.
|
||||
|
||||
-callback get_service_name(service()) ->
|
||||
woody:service_name().
|
||||
|
||||
-callback read_call(buffer(), service()) ->
|
||||
{ok, seqid(), invocation(), _Rest :: buffer()}
|
||||
| {error, _Reason}.
|
||||
|
||||
-callback write_result(buffer(), service(), func(), result(), seqid()) ->
|
||||
{ok, buffer()}
|
||||
| {error, _Reason}.
|
||||
|
||||
%%
|
||||
|
||||
-spec get_service_name(codec(), service()) ->
|
||||
woody:service_name().
|
||||
get_service_name(thrift_processor_codec, {_Module, Service}) ->
|
||||
Service;
|
||||
get_service_name(Codec, Service) ->
|
||||
Codec:get_service_name(Service).
|
||||
|
||||
-spec read_call(codec(), buffer(), service()) ->
|
||||
{ok, seqid(), invocation(), _Rest :: buffer()}
|
||||
| {error, _Reason}.
|
||||
read_call(thrift_processor_codec, Buffer, Service) ->
|
||||
case thrift_processor_codec:read_function_call(Buffer, thrift_strict_binary_codec, Service) of
|
||||
{ok, SeqId, {Type, Function, Args}, Rest} ->
|
||||
RpcType = maps:get(Type, #{call => call, oneway => cast}),
|
||||
{ok, SeqId, {RpcType, Function, Args}, Rest};
|
||||
{error, _} = Error ->
|
||||
Error
|
||||
end;
|
||||
read_call(Codec, Buffer, Service) ->
|
||||
Codec:read_call(Buffer, Service).
|
||||
|
||||
-spec write_result(codec(), buffer(), service(), func(), result(), seqid()) ->
|
||||
{ok, buffer()}
|
||||
| {error, _Reason}.
|
||||
write_result(thrift_processor_codec, Buffer, Service, Function, {exception, _Name, {Type, Exception}}, SeqId) ->
|
||||
thrift_processor_codec:write_function_result(
|
||||
Buffer,
|
||||
thrift_strict_binary_codec,
|
||||
Service,
|
||||
Function,
|
||||
{exception, Type, Exception},
|
||||
SeqId
|
||||
);
|
||||
write_result(thrift_processor_codec, Buffer, Service, Function, Res, SeqId) ->
|
||||
thrift_processor_codec:write_function_result(
|
||||
Buffer,
|
||||
thrift_strict_binary_codec,
|
||||
Service,
|
||||
Function,
|
||||
Res,
|
||||
SeqId
|
||||
);
|
||||
write_result(Codec, Buffer, Service, Function, Res, SeqId) ->
|
||||
Codec:write_result(Buffer, Service, Function, Res, SeqId).
|
||||
|
||||
-spec catch_business_exception(codec(), service(), func(), _Exception) ->
|
||||
{exception, _Name :: atom(), _TypedException}
|
||||
| {error, _Reason}.
|
||||
catch_business_exception(thrift_processor_codec, Service, Function, Exception) ->
|
||||
case thrift_processor_codec:match_exception(Service, Function, Exception) of
|
||||
{ok, Type} ->
|
||||
{exception, get_exception_name(Type, Exception), {Type, Exception}};
|
||||
{error, _} = Error ->
|
||||
Error
|
||||
end;
|
||||
catch_business_exception(_Codec, _Service, _Function, _Exception) ->
|
||||
{error, noimpl}.
|
||||
|
||||
-spec get_exception_name(_Type, woody:result()) ->
|
||||
atom().
|
||||
get_exception_name({{struct, exception, {_Mod, Name}}, _}, _) ->
|
||||
Name;
|
||||
get_exception_name(_Type, Exception) ->
|
||||
element(1, Exception).
|
@ -84,7 +84,7 @@ invoke_handler(State) ->
|
||||
handle_result(Result, Reply, MsgType).
|
||||
|
||||
-spec handle_function(woody:handler(woody:options()), woody:func(), woody:args(), woody_state:st()) ->
|
||||
{ok, woody:result()} | no_return().
|
||||
{ok, woody:result()} | {exception, _TypeName, woody:result()} | no_return().
|
||||
handle_function(Handler, Function, Args, WoodyState) ->
|
||||
_ = woody_event_handler:handle_event(?EV_INVOKE_SERVICE_HANDLER, WoodyState, #{}),
|
||||
{Module, Opts} = woody_util:get_mod_opts(Handler),
|
||||
|
@ -71,9 +71,12 @@
|
||||
|
||||
-export_type([options/0]).
|
||||
|
||||
-type codec() :: woody_server_codec:codec().
|
||||
|
||||
-type route_opts() :: #{
|
||||
handlers := list(woody:http_handler(woody:th_handler())),
|
||||
event_handler := woody:ev_handlers(),
|
||||
codec => codec(),
|
||||
protocol => thrift,
|
||||
transport => http,
|
||||
read_body_opts => read_body_opts(),
|
||||
@ -99,6 +102,7 @@
|
||||
-type state() :: #{
|
||||
th_handler => woody:th_handler(),
|
||||
ev_handler := woody:ev_handlers(),
|
||||
codec => module(),
|
||||
read_body_opts := read_body_opts(),
|
||||
handler_limits := handler_limits(),
|
||||
regexp_meta := re_mp(),
|
||||
@ -213,6 +217,7 @@ get_route(_, Handler) ->
|
||||
init_state(Opts = #{}) ->
|
||||
#{
|
||||
ev_handler => maps:get(event_handler, Opts),
|
||||
codec => maps:get(codec, Opts, thrift_processor_codec),
|
||||
read_body_opts => maps:get(read_body_opts, Opts, #{}),
|
||||
handler_limits => maps:get(handler_limits, Opts, #{}),
|
||||
regexp_meta => compile_filter_meta()
|
||||
@ -256,6 +261,7 @@ handle(
|
||||
Req,
|
||||
State = #{
|
||||
url := Url,
|
||||
codec := ProcessorCodec,
|
||||
woody_state := WoodyState,
|
||||
read_body_opts := ReadBodyOpts,
|
||||
th_handler := ThriftHandler
|
||||
@ -265,7 +271,7 @@ handle(
|
||||
case get_body(Req, ReadBodyOpts) of
|
||||
{ok, Body, Req1} when byte_size(Body) > 0 ->
|
||||
_ = handle_event(?EV_SERVER_RECEIVE, WoodyState, #{url => Url, status => ok}),
|
||||
handle_request(Body, ThriftHandler, WoodyState, Req1);
|
||||
handle_request(ProcessorCodec, Body, ThriftHandler, WoodyState, Req1);
|
||||
{ok, <<>>, Req1} ->
|
||||
reply_client_error(400, <<"body empty">>, Req1, State)
|
||||
end,
|
||||
@ -294,8 +300,6 @@ terminate(Reason, _Req, #{ev_handler := EvHandler} = Opts) ->
|
||||
|
||||
%% init functions
|
||||
|
||||
-define(CODEC, thrift_strict_binary_codec).
|
||||
|
||||
%% First perform basic http checks: method, content type, etc,
|
||||
%% then check woody related headers: IDs, deadline, meta.
|
||||
|
||||
@ -474,18 +478,15 @@ do_get_body(Body, Req, Opts) ->
|
||||
do_get_body(<<Body/binary, Body1/binary>>, Req1, Opts)
|
||||
end.
|
||||
|
||||
-spec handle_request(woody:http_body(), woody:th_handler(), woody_state:st(), cowboy_req:req()) -> cowboy_req:req().
|
||||
handle_request(Body, ThriftHandler = {Service, _}, WoodyState, Req) ->
|
||||
-spec handle_request(codec(), woody:http_body(), woody:th_handler(), woody_state:st(), cowboy_req:req()) ->
|
||||
cowboy_req:req().
|
||||
handle_request(Codec, Body, ThriftHandler = {Service, _}, WoodyState, Req) ->
|
||||
ok = woody_monitor_h:set_event(?EV_SERVICE_HANDLER_RESULT, Req),
|
||||
Buffer = ?CODEC:new(Body),
|
||||
case thrift_processor_codec:read_function_call(Buffer, ?CODEC, Service) of
|
||||
{ok, SeqId, Invocation, Leftovers} ->
|
||||
case ?CODEC:close(Leftovers) of
|
||||
<<>> ->
|
||||
handle_invocation(SeqId, Invocation, ThriftHandler, Req, WoodyState);
|
||||
Bytes ->
|
||||
handle_decode_error({excess_response_body, Bytes, Invocation}, Req, WoodyState)
|
||||
end;
|
||||
case woody_server_codec:read_call(Codec, Body, Service) of
|
||||
{ok, SeqId, Invocation, <<>>} ->
|
||||
handle_invocation(Codec, SeqId, Invocation, ThriftHandler, Req, WoodyState);
|
||||
{ok, _SeqId, Invocation, Leftovers} ->
|
||||
handle_error(client_error({excess_request_bytes, Leftovers, Invocation}), Req, WoodyState);
|
||||
{error, Reason} ->
|
||||
handle_decode_error(Reason, Req, WoodyState)
|
||||
end.
|
||||
@ -518,30 +519,31 @@ client_error({bad_function_name, FName}) ->
|
||||
client_error(Reason) ->
|
||||
{client, woody_util:to_binary([<<"thrift decode error: ">>, woody_error:format_details(Reason)])}.
|
||||
|
||||
-spec handle_invocation(integer(), Invocation, woody:th_handler(), cowboy_req:req(), woody_state:st()) ->
|
||||
-spec handle_invocation(codec(), integer(), Invocation, woody:th_handler(), cowboy_req:req(), woody_state:st()) ->
|
||||
cowboy_req:req()
|
||||
when
|
||||
Invocation :: {call | oneway, woody:func(), woody:args()}.
|
||||
handle_invocation(SeqId, {ReplyType, Function, Args}, {Service, Handler}, Req, WoodyState) ->
|
||||
WoodyState1 = add_ev_meta(WoodyState, Service, ReplyType, Function, Args),
|
||||
case ReplyType of
|
||||
Invocation :: woody_server_codec:invocation().
|
||||
handle_invocation(Codec, SeqId, {RpcType, Function, Args}, {Service, Handler}, Req, WoodyState) ->
|
||||
WoodyState1 = add_ev_meta(WoodyState, Codec, Service, RpcType, Function, Args),
|
||||
case RpcType of
|
||||
call ->
|
||||
Result = handle_call(Handler, Service, Function, Args, WoodyState1),
|
||||
handle_result(Result, Service, Function, SeqId, Req, WoodyState1);
|
||||
oneway ->
|
||||
Result = handle_call(Codec, Handler, Service, Function, Args, WoodyState1),
|
||||
handle_result(Codec, Result, Service, Function, SeqId, Req, WoodyState1);
|
||||
cast ->
|
||||
Req1 = reply(200, Req, WoodyState1),
|
||||
_Result = handle_call(Handler, Service, Function, Args, WoodyState1),
|
||||
_Result = handle_call(Codec, Handler, Service, Function, Args, WoodyState1),
|
||||
Req1
|
||||
end.
|
||||
|
||||
-type call_result() ::
|
||||
ok
|
||||
| {reply, woody:result()}
|
||||
| {exception, _TypeName, _Exception}
|
||||
| {exception, _Name :: binary(), _Exception :: woody:result()}
|
||||
| {error, {system, woody_error:system_error()}}.
|
||||
|
||||
-spec handle_call(woody:handler(_), woody:service(), woody:func(), woody:args(), woody_state:st()) -> call_result().
|
||||
handle_call(Handler, Service, Function, Args, WoodyState) ->
|
||||
-spec handle_call(codec(), woody:handler(_), woody:service(), woody:func(), woody:args(), woody_state:st()) ->
|
||||
call_result().
|
||||
handle_call(Codec, Handler, Service, Function, Args, WoodyState) ->
|
||||
try
|
||||
Result = call_handler(Handler, Function, Args, WoodyState),
|
||||
_ = handle_event(
|
||||
@ -550,35 +552,42 @@ handle_call(Handler, Service, Function, Args, WoodyState) ->
|
||||
#{status => ok, result => Result}
|
||||
),
|
||||
case Result of
|
||||
{ok, ok} -> ok;
|
||||
{ok, Reply} -> {reply, Reply}
|
||||
{ok, Reply} ->
|
||||
{reply, Reply};
|
||||
{exception, _Name, BusinessException} ->
|
||||
_ = handle_event(
|
||||
?EV_SERVICE_HANDLER_RESULT,
|
||||
WoodyState,
|
||||
#{status => error, class => business, result => BusinessException}
|
||||
),
|
||||
Result
|
||||
end
|
||||
catch
|
||||
throw:Exception:Stack ->
|
||||
process_handler_throw(Exception, Stack, Service, Function, WoodyState);
|
||||
process_handler_throw(Codec, Exception, Stack, Service, Function, WoodyState);
|
||||
Class:Reason:Stack ->
|
||||
process_handler_error(Class, Reason, Stack, WoodyState)
|
||||
end.
|
||||
|
||||
-spec call_handler(woody:handler(_), woody:func(), woody:args(), woody_state:st()) ->
|
||||
{ok, woody:result()} | no_return().
|
||||
{ok, woody:result()} | {exception, _Name, woody:result()} | no_return().
|
||||
call_handler(Handler, Function, Args, WoodyState) ->
|
||||
woody_server_thrift_handler:handle_function(Handler, Function, Args, WoodyState).
|
||||
|
||||
-spec process_handler_throw(_Exception, woody_error:stack(), woody:service(), woody:func(), woody_state:st()) ->
|
||||
{exception, _TypeName, _Exception}
|
||||
-spec process_handler_throw(codec(), _Ex, woody_error:stack(), woody:service(), woody:func(), woody_state:st()) ->
|
||||
{exception, _Name, woody:result()}
|
||||
| {error, {system, woody_error:system_error()}}.
|
||||
process_handler_throw(Exception, Stack, Service, Function, WoodyState) ->
|
||||
case thrift_processor_codec:match_exception(Service, Function, Exception) of
|
||||
{ok, TypeName} ->
|
||||
process_handler_throw(Codec, ThrownException, Stack, Service, Function, WoodyState) ->
|
||||
case woody_server_codec:catch_business_exception(Codec, Service, Function, ThrownException) of
|
||||
{exception, _Name, _TypedException} = Result ->
|
||||
_ = handle_event(
|
||||
?EV_SERVICE_HANDLER_RESULT,
|
||||
WoodyState,
|
||||
#{status => error, class => business, result => Exception}
|
||||
#{status => error, class => business, result => ThrownException}
|
||||
),
|
||||
{exception, TypeName, Exception};
|
||||
Result;
|
||||
{error, _} ->
|
||||
process_handler_error(throw, Exception, Stack, WoodyState)
|
||||
process_handler_error(throw, ThrownException, Stack, WoodyState)
|
||||
end.
|
||||
|
||||
-spec process_handler_error(_Class :: atom(), _Reason, woody_error:stack(), woody_state:st()) ->
|
||||
@ -599,35 +608,27 @@ process_handler_error(Class, Reason, Stack, WoodyState) ->
|
||||
Error = {internal, result_unexpected, format_unexpected_error(Class, Reason, Stack)},
|
||||
{error, {system, Error}}.
|
||||
|
||||
-spec handle_result(call_result(), woody:service(), woody:func(), integer(), Req, woody_state:st()) -> Req when
|
||||
-spec handle_result(module(), call_result(), woody:service(), woody:func(), integer(), Req, woody_state:st()) ->
|
||||
Req
|
||||
when
|
||||
Req :: cowboy_req:req().
|
||||
handle_result(Res, Service, Function, SeqId, Req, WoodyState) when Res == ok; element(1, Res) == reply ->
|
||||
Buffer = ?CODEC:new(),
|
||||
case thrift_processor_codec:write_function_result(Buffer, ?CODEC, Service, Function, Res, SeqId) of
|
||||
{ok, Buffer1} ->
|
||||
Response = ?CODEC:close(Buffer1),
|
||||
handle_result(Codec, Res, Service, Function, SeqId, Req, WoodyState) when Res == ok; element(1, Res) == reply ->
|
||||
case woody_server_codec:write_result(Codec, <<>>, Service, Function, Res, SeqId) of
|
||||
{ok, Response} ->
|
||||
reply(200, cowboy_req:set_resp_body(Response, Req), WoodyState);
|
||||
{error, Reason} ->
|
||||
handle_encode_error(Reason, Req, WoodyState)
|
||||
end;
|
||||
handle_result(Res = {exception, TypeName, Exception}, Service, Function, SeqId, Req, WoodyState) ->
|
||||
Buffer = ?CODEC:new(),
|
||||
case thrift_processor_codec:write_function_result(Buffer, ?CODEC, Service, Function, Res, SeqId) of
|
||||
{ok, Buffer1} ->
|
||||
ExceptionName = get_exception_name(TypeName, Exception),
|
||||
Response = ?CODEC:close(Buffer1),
|
||||
handle_error({business, {ExceptionName, Response}}, Req, WoodyState);
|
||||
handle_result(Codec, {exception, Name, _Exception} = Result, Service, Function, SeqId, Req, WoodyState) ->
|
||||
case woody_server_codec:write_result(Codec, <<>>, Service, Function, Result, SeqId) of
|
||||
{ok, Response} ->
|
||||
handle_error({business, {genlib:to_binary(Name), Response}}, Req, WoodyState);
|
||||
{error, Reason} ->
|
||||
handle_encode_error(Reason, Req, WoodyState)
|
||||
end;
|
||||
handle_result({error, Error}, _Service, _Function, _SeqId, Req, WoodyState) ->
|
||||
handle_result(_Codec, {error, Error}, _Service, _Function, _SeqId, Req, WoodyState) ->
|
||||
handle_error(Error, Req, WoodyState).
|
||||
|
||||
get_exception_name({{struct, exception, {_Mod, Name}}, _}, _) ->
|
||||
genlib:to_binary(Name);
|
||||
get_exception_name(_TypeName, Exception) ->
|
||||
genlib:to_binary(element(1, Exception)).
|
||||
|
||||
-spec handle_encode_error(_Reason, cowboy_req:req(), woody_state:st()) -> cowboy_req:req().
|
||||
handle_encode_error(Reason, Req, WoodyState) ->
|
||||
_ = handle_event(
|
||||
@ -641,22 +642,19 @@ handle_encode_error(Reason, Req, WoodyState) ->
|
||||
Error = {internal, result_unexpected, format_unexpected_error(error, Reason, [])},
|
||||
handle_error({system, Error}, Req, WoodyState).
|
||||
|
||||
add_ev_meta(WoodyState, Service = {_, ServiceName}, ReplyType, Function, Args) ->
|
||||
add_ev_meta(WoodyState, Codec, Service, RpcType, Function, Args) ->
|
||||
woody_state:add_ev_meta(
|
||||
#{
|
||||
service => ServiceName,
|
||||
service => woody_server_codec:get_service_name(Codec, Service),
|
||||
service_schema => Service,
|
||||
function => Function,
|
||||
args => Args,
|
||||
type => get_rpc_reply_type(ReplyType),
|
||||
type => RpcType,
|
||||
deadline => woody_context:get_deadline(woody_state:get_context(WoodyState))
|
||||
},
|
||||
WoodyState
|
||||
).
|
||||
|
||||
get_rpc_reply_type(oneway) -> cast;
|
||||
get_rpc_reply_type(call) -> call.
|
||||
|
||||
format_unexpected_error(Class, Reason, Stack) ->
|
||||
woody_util:to_binary(
|
||||
[Class, ":", woody_error:format_details(Reason), " ", genlib_format:format_stacktrace(Stack)]
|
||||
|
Loading…
Reference in New Issue
Block a user