mirror of
https://github.com/valitydev/woody_erlang.git
synced 2024-11-06 02:15:19 +00:00
upgrade: +alpine +rebar +dialyzer +deps (#149)
This commit is contained in:
parent
58f56b4624
commit
f2cd30883d
13
Makefile
13
Makefile
@ -7,9 +7,12 @@ UTILS_PATH := build_utils
|
||||
# with handling of the varriable in build_utils is fixed
|
||||
TEMPLATES_PATH := .
|
||||
SERVICE_NAME := woody
|
||||
BUILD_IMAGE_TAG := 07d3946f8f005782697de20270ac58cdcd18b011
|
||||
|
||||
CALL_W_CONTAINER := all submodules compile xref lint test bench dialyze clean distclean
|
||||
BUILD_IMAGE_NAME := build-erlang
|
||||
BUILD_IMAGE_TAG := c60896ef07d41e7ae2e5f9b6ce845a60ad79acc7
|
||||
|
||||
CALL_W_CONTAINER := all submodules compile xref lint test bench dialyze clean distclean \
|
||||
check_format format
|
||||
|
||||
.PHONY: $(CALL_W_CONTAINER)
|
||||
|
||||
@ -36,6 +39,12 @@ xref: submodules
|
||||
lint: compile
|
||||
elvis rock
|
||||
|
||||
check_format:
|
||||
$(REBAR) fmt -c
|
||||
|
||||
format:
|
||||
$(REBAR) fmt -w
|
||||
|
||||
clean:
|
||||
$(REBAR) clean
|
||||
$(REBAR) as test clean
|
||||
|
@ -1 +1 @@
|
||||
Subproject commit 5cb25f049c719a608276a99fc4fbe852187019ca
|
||||
Subproject commit e1318727d4d0c3e48f5122bf3197158b6695f50e
|
23
rebar.config
23
rebar.config
@ -21,9 +21,9 @@
|
||||
% Common project dependencies.
|
||||
{deps, [
|
||||
{cowboy, "2.7.0"},
|
||||
{hackney, "1.15.2"},
|
||||
{hackney, "1.17.0"},
|
||||
{gproc , "0.8.0"},
|
||||
{cache , "2.2.0"},
|
||||
{cache , "2.3.3"},
|
||||
{thrift, {git, "https://github.com/rbkmoney/thrift_erlang.git", {branch, "master"}}},
|
||||
{snowflake, {git, "https://github.com/rbkmoney/snowflake.git", {branch, "master"}}},
|
||||
{genlib, {git, "https://github.com/rbkmoney/genlib.git", {branch, "master"}}}
|
||||
@ -49,6 +49,7 @@
|
||||
|
||||
{profiles, [
|
||||
{test, [
|
||||
{extra_src_dirs, [{"test", [{recursive, true}]}]},
|
||||
{plugins, [
|
||||
{rebar3_bench, "0.2.1"}
|
||||
]},
|
||||
@ -67,17 +68,27 @@
|
||||
{gen, "erlang:app_prefix=woody"}
|
||||
]},
|
||||
{deps, [
|
||||
{proper, "1.2.0"},
|
||||
{how_are_you , {git, "https://github.com/rbkmoney/how_are_you.git", {ref, "8f11d17"}}},
|
||||
{cth_readable, "1.4.9"},
|
||||
{proper, "1.3.0"},
|
||||
{how_are_you , {git, "https://github.com/rbkmoney/how_are_you.git", {branch, "master"}}},
|
||||
{damsel , {git, "https://github.com/rbkmoney/damsel.git", {ref, "8911ac3"}}},
|
||||
{mg_proto , {git, "https://github.com/rbkmoney/machinegun_proto.git", {ref, "ebae56f"}}}
|
||||
]},
|
||||
{dialyzer, [
|
||||
{plt_extra_apps, [how_are_you, eunit]}
|
||||
{plt_extra_apps, [how_are_you, eunit, proper, common_test, cth_readable]}
|
||||
]}
|
||||
]}
|
||||
]}.
|
||||
|
||||
{plugins, [
|
||||
{rebar3_thrift_compiler, {git, "https://github.com/rbkmoney/rebar3_thrift_compiler.git", {tag, "0.3.1"}}}
|
||||
{rebar3_thrift_compiler, {git, "https://github.com/rbkmoney/rebar3_thrift_compiler.git", {branch, "master"}}},
|
||||
{erlfmt, "0.9.0"}
|
||||
]}.
|
||||
|
||||
{erlfmt, [
|
||||
{print_width, 120},
|
||||
{files, [
|
||||
"{src,include,test}/*.{hrl,erl}",
|
||||
"test/*/*.{hrl,erl}"
|
||||
]}
|
||||
]}.
|
||||
|
49
rebar.lock
49
rebar.lock
@ -1,7 +1,7 @@
|
||||
{"1.1.0",
|
||||
{"1.2.0",
|
||||
[{<<"bear">>,{pkg,<<"bear">>,<<"0.8.7">>},2},
|
||||
{<<"cache">>,{pkg,<<"cache">>,<<"2.2.0">>},0},
|
||||
{<<"certifi">>,{pkg,<<"certifi">>,<<"2.5.1">>},1},
|
||||
{<<"cache">>,{pkg,<<"cache">>,<<"2.3.3">>},0},
|
||||
{<<"certifi">>,{pkg,<<"certifi">>,<<"2.5.3">>},1},
|
||||
{<<"cg_mon">>,
|
||||
{git,"https://github.com/rbkmoney/cg_mon.git",
|
||||
{ref,"5a87a37694e42b6592d3b4164ae54e0e87e24e18"}},
|
||||
@ -14,43 +14,58 @@
|
||||
1},
|
||||
{<<"genlib">>,
|
||||
{git,"https://github.com/rbkmoney/genlib.git",
|
||||
{ref,"54920e768a71f121304a5eda547ee60295398f3c"}},
|
||||
{ref,"4565a8d73f34a0b78cca32c9cd2b97d298bdadf8"}},
|
||||
0},
|
||||
{<<"gproc">>,{pkg,<<"gproc">>,<<"0.8.0">>},0},
|
||||
{<<"hackney">>,{pkg,<<"hackney">>,<<"1.15.2">>},0},
|
||||
{<<"hackney">>,{pkg,<<"hackney">>,<<"1.17.0">>},0},
|
||||
{<<"how_are_you">>,
|
||||
{git,"https://github.com/rbkmoney/how_are_you.git",
|
||||
{ref,"8f11d17eeb6eb74096da7363a9df272fd3099718"}},
|
||||
0},
|
||||
{<<"idna">>,{pkg,<<"idna">>,<<"6.0.0">>},1},
|
||||
{<<"idna">>,{pkg,<<"idna">>,<<"6.1.1">>},1},
|
||||
{<<"metrics">>,{pkg,<<"metrics">>,<<"1.0.1">>},1},
|
||||
{<<"mimerl">>,{pkg,<<"mimerl">>,<<"1.2.0">>},1},
|
||||
{<<"parse_trans">>,{pkg,<<"parse_trans">>,<<"3.3.0">>},2},
|
||||
{<<"parse_trans">>,{pkg,<<"parse_trans">>,<<"3.3.1">>},1},
|
||||
{<<"ranch">>,{pkg,<<"ranch">>,<<"1.7.1">>},1},
|
||||
{<<"snowflake">>,
|
||||
{git,"https://github.com/rbkmoney/snowflake.git",
|
||||
{ref,"7f379ad5e389e1c96389a8d60bae8117965d6a6d"}},
|
||||
{ref,"de159486ef40cec67074afe71882bdc7f7deab72"}},
|
||||
0},
|
||||
{<<"ssl_verify_fun">>,{pkg,<<"ssl_verify_fun">>,<<"1.1.5">>},1},
|
||||
{<<"ssl_verify_fun">>,{pkg,<<"ssl_verify_fun">>,<<"1.1.6">>},1},
|
||||
{<<"thrift">>,
|
||||
{git,"https://github.com/rbkmoney/thrift_erlang.git",
|
||||
{ref,"846a0819d9b6d09d0c31f160e33a78dbad2067b4"}},
|
||||
0},
|
||||
{<<"unicode_util_compat">>,{pkg,<<"unicode_util_compat">>,<<"0.4.1">>},2}]}.
|
||||
{<<"unicode_util_compat">>,{pkg,<<"unicode_util_compat">>,<<"0.7.0">>},1}]}.
|
||||
[
|
||||
{pkg_hash,[
|
||||
{<<"bear">>, <<"16264309AE5D005D03718A5C82641FCC259C9E8F09ADEB6FD79CA4271168656F">>},
|
||||
{<<"cache">>, <<"3C11DBF4CD8FCD5787C95A5FB2A04038E3729CFCA0386016EEA8C953AB48A5AB">>},
|
||||
{<<"certifi">>, <<"867CE347F7C7D78563450A18A6A28A8090331E77FA02380B4A21962A65D36EE5">>},
|
||||
{<<"cache">>, <<"B23A5FE7095445A88412A6E614C933377E0137B44FFED77C9B3FEF1A731A20B2">>},
|
||||
{<<"certifi">>, <<"70BDD7E7188C804F3A30EE0E7C99655BC35D8AC41C23E12325F36AB449B70651">>},
|
||||
{<<"cowboy">>, <<"91ED100138A764355F43316B1D23D7FF6BDB0DE4EA618CB5D8677C93A7A2F115">>},
|
||||
{<<"cowlib">>, <<"FD0FF1787DB84AC415B8211573E9A30A3EBE71B5CBFF7F720089972B2319C8A4">>},
|
||||
{<<"gproc">>, <<"CEA02C578589C61E5341FCE149EA36CCEF236CC2ECAC8691FBA408E7EA77EC2F">>},
|
||||
{<<"hackney">>, <<"07E33C794F8F8964EE86CEBEC1A8ED88DB5070E52E904B8F12209773C1036085">>},
|
||||
{<<"idna">>, <<"689C46CBCDF3524C44D5F3DDE8001F364CD7608A99556D8FBD8239A5798D4C10">>},
|
||||
{<<"hackney">>, <<"717EA195FD2F898D9FE9F1CE0AFCC2621A41ECFE137FAE57E7FE6E9484B9AA99">>},
|
||||
{<<"idna">>, <<"8A63070E9F7D0C62EB9D9FCB360A7DE382448200FBBD1B106CC96D3D8099DF8D">>},
|
||||
{<<"metrics">>, <<"25F094DEA2CDA98213CECC3AEFF09E940299D950904393B2A29D191C346A8486">>},
|
||||
{<<"mimerl">>, <<"67E2D3F571088D5CFD3E550C383094B47159F3EEE8FFA08E64106CDF5E981BE3">>},
|
||||
{<<"parse_trans">>, <<"09765507A3C7590A784615CFD421D101AEC25098D50B89D7AA1D66646BC571C1">>},
|
||||
{<<"parse_trans">>, <<"16328AB840CC09919BD10DAB29E431DA3AF9E9E7E7E6F0089DD5A2D2820011D8">>},
|
||||
{<<"ranch">>, <<"6B1FAB51B49196860B733A49C07604465A47BDB78AA10C1C16A3D199F7F8C881">>},
|
||||
{<<"ssl_verify_fun">>, <<"6EAF7AD16CB568BB01753DBBD7A95FF8B91C7979482B95F38443FE2C8852A79B">>},
|
||||
{<<"unicode_util_compat">>, <<"D869E4C68901DD9531385BB0C8C40444EBF624E60B6962D95952775CAC5E90CD">>}]}
|
||||
{<<"ssl_verify_fun">>, <<"CF344F5692C82D2CD7554F5EC8FD961548D4FD09E7D22F5B62482E5AEAEBD4B0">>},
|
||||
{<<"unicode_util_compat">>, <<"BC84380C9AB48177092F43AC89E4DFA2C6D62B40B8BD132B1059ECC7232F9A78">>}]},
|
||||
{pkg_hash_ext,[
|
||||
{<<"bear">>, <<"534217DCE6A719D59E54FB0EB7A367900DBFC5F85757E8C1F94269DF383F6D9B">>},
|
||||
{<<"cache">>, <<"44516CE6FA03594D3A2AF025DD3A87BFE711000EB730219E1DDEFC816E0AA2F4">>},
|
||||
{<<"certifi">>, <<"ED516ACB3929B101208A9D700062D520F3953DA3B6B918D866106FFA980E1C10">>},
|
||||
{<<"cowboy">>, <<"04FD8C6A39EDC6AAA9C26123009200FC61F92A3A94F3178C527B70B767C6E605">>},
|
||||
{<<"cowlib">>, <<"79F954A7021B302186A950A32869DBC185523D99D3E44CE430CD1F3289F41ED4">>},
|
||||
{<<"gproc">>, <<"580ADAFA56463B75263EF5A5DF4C86AF321F68694E7786CB057FD805D1E2A7DE">>},
|
||||
{<<"hackney">>, <<"64C22225F1EA8855F584720C0E5B3CD14095703AF1C9FBC845BA042811DC671C">>},
|
||||
{<<"idna">>, <<"92376EB7894412ED19AC475E4A86F7B413C1B9FBB5BD16DCCD57934157944CEA">>},
|
||||
{<<"metrics">>, <<"69B09ADDDC4F74A40716AE54D140F93BEB0FB8978D8636EADED0C31B6F099F16">>},
|
||||
{<<"mimerl">>, <<"F278585650AA581986264638EBF698F8BB19DF297F66AD91B18910DFC6E19323">>},
|
||||
{<<"parse_trans">>, <<"07CD9577885F56362D414E8C4C4E6BDF10D43A8767ABB92D24CBE8B24C54888B">>},
|
||||
{<<"ranch">>, <<"451D8527787DF716D99DC36162FCA05934915DB0B6141BBDAC2EA8D3C7AFC7D7">>},
|
||||
{<<"ssl_verify_fun">>, <<"BDB0D2471F453C88FF3908E7686F86F9BE327D065CC1EC16FA4540197EA04680">>},
|
||||
{<<"unicode_util_compat">>, <<"25EEE6D67DF61960CF6A794239566599B09E17E668D3700247BC498638152521">>}]}
|
||||
].
|
||||
|
@ -6,14 +6,14 @@
|
||||
%% Types
|
||||
|
||||
%% Dapper RPC
|
||||
-type req_id() :: binary().
|
||||
-type span_id() :: req_id().
|
||||
-type trace_id() :: req_id().
|
||||
-type req_id() :: binary().
|
||||
-type span_id() :: req_id().
|
||||
-type trace_id() :: req_id().
|
||||
-type parent_id() :: req_id().
|
||||
|
||||
-type rpc_id() :: #{
|
||||
span_id => span_id(),
|
||||
trace_id => trace_id(),
|
||||
span_id => span_id(),
|
||||
trace_id => trace_id(),
|
||||
parent_id => parent_id()
|
||||
}.
|
||||
|
||||
@ -24,49 +24,59 @@
|
||||
|
||||
-type millisec() :: woody_deadline:millisec().
|
||||
-type deadline() :: woody_deadline:deadline().
|
||||
|
||||
-export_type([deadline/0, millisec/0]).
|
||||
|
||||
-type cert() :: woody_cert:cert().
|
||||
-type common_name() :: woody_cert:common_name().
|
||||
|
||||
-export_type([cert/0, common_name/0]).
|
||||
|
||||
%% Thrift
|
||||
-type service_name() :: atom().
|
||||
-type service() :: {module(), service_name()}.
|
||||
-type func() :: atom().
|
||||
-type args() :: tuple().
|
||||
-type request() :: {service(), func(), args()}.
|
||||
-type result() :: _.
|
||||
-type th_handler() :: {service(), handler(options())}.
|
||||
-type service() :: {module(), service_name()}.
|
||||
-type func() :: atom().
|
||||
-type args() :: tuple().
|
||||
-type request() :: {service(), func(), args()}.
|
||||
-type result() :: _.
|
||||
-type th_handler() :: {service(), handler(options())}.
|
||||
|
||||
-export_type([request/0, result/0, service/0, service_name/0, func/0, args/0, th_handler/0]).
|
||||
|
||||
-type rpc_type() :: call | cast.
|
||||
|
||||
-export_type([rpc_type/0]).
|
||||
|
||||
%% Generic
|
||||
-type options() :: any().
|
||||
-type options() :: any().
|
||||
-type handler(Opts) :: {module(), Opts} | module().
|
||||
-type ev_handler() :: handler(options()).
|
||||
-type ev_handler() :: handler(options()).
|
||||
-type ev_handlers() :: ev_handler() | [ev_handler()].
|
||||
|
||||
-export_type([handler/1, ev_handler/0, ev_handlers/0, options/0]).
|
||||
|
||||
-type role() :: client | server.
|
||||
-type url() :: binary() | string().
|
||||
-type path() :: '_' | iodata(). %% cowboy_router:route_match()
|
||||
-type role() :: client | server.
|
||||
-type url() :: binary() | string().
|
||||
%% cowboy_router:route_match()
|
||||
-type path() :: '_' | iodata().
|
||||
-type http_handler(Handler) :: {path(), Handler}.
|
||||
|
||||
-export_type([role/0, url/0, path/0, http_handler/1]).
|
||||
|
||||
-type http_code() :: integer().
|
||||
-type http_code() :: integer().
|
||||
-type http_header_name() :: binary().
|
||||
-type http_header_val() :: binary().
|
||||
-type http_headers() :: #{http_header_name() => http_header_val()}.
|
||||
-type http_body() :: binary().
|
||||
-type http_header_val() :: binary().
|
||||
-type http_headers() :: #{http_header_name() => http_header_val()}.
|
||||
-type http_body() :: binary().
|
||||
|
||||
-export_type([http_code/0, http_header_name/0, http_header_val/0, http_headers/0, http_body/0]).
|
||||
|
||||
%% copy-paste from OTP supervisor
|
||||
-type sup_ref() :: (Name :: atom())
|
||||
| {Name :: atom(), Node :: node()}
|
||||
| {'global', Name :: atom()}
|
||||
| {'via', Module :: module(), Name :: any()}
|
||||
| pid().
|
||||
-type sup_ref() ::
|
||||
(Name :: atom())
|
||||
| {Name :: atom(), Node :: node()}
|
||||
| {'global', Name :: atom()}
|
||||
| {'via', Module :: module(), Name :: any()}
|
||||
| pid().
|
||||
|
||||
-export_type([sup_ref/0]).
|
||||
|
@ -15,6 +15,7 @@
|
||||
%%%
|
||||
|
||||
-module(woody_api_hay).
|
||||
|
||||
-behaviour(hay_metrics_handler).
|
||||
|
||||
%% how_are_you callbacks
|
||||
@ -32,7 +33,6 @@
|
||||
|
||||
%% Internal types
|
||||
|
||||
|
||||
-type state() :: options().
|
||||
-type metric() :: how_are_you:metric().
|
||||
-type metric_key() :: how_are_you:metric_key().
|
||||
@ -67,15 +67,15 @@ get_ranch_info() ->
|
||||
|
||||
get_active_connections() ->
|
||||
F = fun({Ref, Info}) ->
|
||||
Nconns = case lists:keyfind(active_connections, 1, Info) of
|
||||
false -> 0;
|
||||
{_, N} -> N
|
||||
end,
|
||||
Nconns =
|
||||
case lists:keyfind(active_connections, 1, Info) of
|
||||
false -> 0;
|
||||
{_, N} -> N
|
||||
end,
|
||||
{Ref, Nconns}
|
||||
end,
|
||||
lists:map(F, get_ranch_info()).
|
||||
|
||||
-spec gauge(metric_key(), metric_value()) ->
|
||||
metric().
|
||||
-spec gauge(metric_key(), metric_value()) -> metric().
|
||||
gauge(Key, Value) ->
|
||||
how_are_you:metric_construct(gauge, Key, Value).
|
||||
|
@ -1,14 +1,16 @@
|
||||
-module(woody_caching_client).
|
||||
|
||||
-include("woody_defs.hrl").
|
||||
|
||||
%% API
|
||||
-export_type([cache_control/0]).
|
||||
-export_type([cache_options/0]).
|
||||
-export_type([options /0]).
|
||||
-export_type([options/0]).
|
||||
|
||||
-export([child_spec/2]).
|
||||
-export([start_link/1]).
|
||||
-export([call /3]).
|
||||
-export([call /4]).
|
||||
-export([call/3]).
|
||||
-export([call/4]).
|
||||
|
||||
%% Internal API
|
||||
-export([call_safe/4]).
|
||||
@ -16,40 +18,39 @@
|
||||
%%
|
||||
%% API
|
||||
%%
|
||||
-type cache_control() :: cache | {cache_for, TimeoutMs::non_neg_integer()} | no_cache.
|
||||
-type cache_control() :: cache | {cache_for, TimeoutMs :: non_neg_integer()} | no_cache.
|
||||
|
||||
-type cache_options() :: #{
|
||||
local_name => atom(),
|
||||
type => set | ordered_set,
|
||||
policy => lru | mru,
|
||||
memory => integer(),
|
||||
size => integer(),
|
||||
n => integer(),
|
||||
ttl => integer(), %% seconds
|
||||
check => integer(),
|
||||
stats => function() | {module(), atom()},
|
||||
heir => atom() | pid()
|
||||
type => set | ordered_set,
|
||||
policy => lru | mru,
|
||||
memory => integer(),
|
||||
size => integer(),
|
||||
n => integer(),
|
||||
%% seconds
|
||||
ttl => integer(),
|
||||
check => integer(),
|
||||
stats => function() | {module(), atom()},
|
||||
heir => atom() | pid()
|
||||
}.
|
||||
|
||||
-type options() :: #{
|
||||
workers_name := atom(),
|
||||
cache := cache_options(),
|
||||
woody_client := woody_client:options(),
|
||||
joint_control => joint | no_joint
|
||||
workers_name := atom(),
|
||||
cache := cache_options(),
|
||||
woody_client := woody_client:options(),
|
||||
joint_control => joint | no_joint
|
||||
}.
|
||||
|
||||
-spec child_spec(atom(), options()) ->
|
||||
supervisor:child_spec().
|
||||
-spec child_spec(atom(), options()) -> supervisor:child_spec().
|
||||
child_spec(ChildID, Options) ->
|
||||
#{
|
||||
id => ChildID,
|
||||
start => {?MODULE, start_link, [Options]},
|
||||
restart => permanent,
|
||||
type => supervisor
|
||||
id => ChildID,
|
||||
start => {?MODULE, start_link, [Options]},
|
||||
restart => permanent,
|
||||
type => supervisor
|
||||
}.
|
||||
|
||||
-spec start_link(options()) ->
|
||||
genlib_gen:start_ret().
|
||||
-spec start_link(options()) -> genlib_gen:start_ret().
|
||||
start_link(Options) ->
|
||||
genlib_adhoc_supervisor:start_link(
|
||||
#{strategy => one_for_one},
|
||||
@ -61,21 +62,20 @@ start_link(Options) ->
|
||||
).
|
||||
|
||||
-spec call(woody:request(), cache_control(), options()) ->
|
||||
{ok, woody:result()} |
|
||||
{exception, woody_error:business_error()} |
|
||||
no_return().
|
||||
{ok, woody:result()}
|
||||
| {exception, woody_error:business_error()}
|
||||
| no_return().
|
||||
call(Request, CacheControl, Options) ->
|
||||
call(Request, CacheControl, Options, woody_context:new()).
|
||||
|
||||
-spec call(woody:request(), cache_control(), options(), woody_context:ctx()) ->
|
||||
{ok, woody:result()} |
|
||||
{exception, woody_error:business_error()} |
|
||||
no_return().
|
||||
{ok, woody:result()}
|
||||
| {exception, woody_error:business_error()}
|
||||
| no_return().
|
||||
call(Request, CacheControl, #{joint_control := joint} = Options, Context) ->
|
||||
Task =
|
||||
fun(_) ->
|
||||
call_safe(Request, CacheControl, Options, Context)
|
||||
end,
|
||||
Task = fun(_) ->
|
||||
call_safe(Request, CacheControl, Options, Context)
|
||||
end,
|
||||
woody_joint_workers:do(workers_ref(Options), Request, Task, woody_context:get_deadline(Context));
|
||||
call(Request, CacheControl, Options, Context) ->
|
||||
call_safe(Request, CacheControl, Options, Context).
|
||||
@ -85,7 +85,7 @@ call(Request, CacheControl, Options, Context) ->
|
||||
%%
|
||||
|
||||
-spec call_safe(woody:request(), cache_control(), options(), woody_context:ctx()) ->
|
||||
{ok, woody:result()}
|
||||
{ok, woody:result()}
|
||||
| {exception, woody_error:business_error()}.
|
||||
call_safe(Request, CacheControl, Options, Context) ->
|
||||
Meta = add_thrift_meta(Request, new_meta(Options, Context)),
|
||||
@ -97,35 +97,35 @@ call_safe(Request, CacheControl, Options, Context) ->
|
||||
end.
|
||||
|
||||
-spec do_call(woody:request(), cache_control(), options(), woody_context:ctx(), map()) ->
|
||||
{ok, woody:result()}
|
||||
{ok, woody:result()}
|
||||
| {exception, woody_error:business_error()}.
|
||||
do_call(Request, CacheControl, Options, Context, Meta) ->
|
||||
Result = case get_from_cache(Request, CacheControl, Options) of
|
||||
OK={ok, _CacheResult} ->
|
||||
% cache hit
|
||||
ok = emit_event(?EV_CLIENT_CACHE_HIT, Meta, Context, Options),
|
||||
OK;
|
||||
not_found ->
|
||||
% cache miss
|
||||
ok = emit_event(?EV_CLIENT_CACHE_MISS, Meta, Context, Options),
|
||||
case woody_client:call(Request, woody_client_options(Options), Context) of
|
||||
{ok, CallResult} ->
|
||||
% cache update
|
||||
ok = emit_event(?EV_CLIENT_CACHE_UPDATE, Meta#{result => CallResult}, Context, Options),
|
||||
ok = update_cache(Request, CallResult, CacheControl, Options),
|
||||
{ok, CallResult};
|
||||
Exception = {exception, _} ->
|
||||
Exception
|
||||
end
|
||||
end,
|
||||
Result =
|
||||
case get_from_cache(Request, CacheControl, Options) of
|
||||
OK = {ok, _CacheResult} ->
|
||||
% cache hit
|
||||
ok = emit_event(?EV_CLIENT_CACHE_HIT, Meta, Context, Options),
|
||||
OK;
|
||||
not_found ->
|
||||
% cache miss
|
||||
ok = emit_event(?EV_CLIENT_CACHE_MISS, Meta, Context, Options),
|
||||
case woody_client:call(Request, woody_client_options(Options), Context) of
|
||||
{ok, CallResult} ->
|
||||
% cache update
|
||||
ok = emit_event(?EV_CLIENT_CACHE_UPDATE, Meta#{result => CallResult}, Context, Options),
|
||||
ok = update_cache(Request, CallResult, CacheControl, Options),
|
||||
{ok, CallResult};
|
||||
Exception = {exception, _} ->
|
||||
Exception
|
||||
end
|
||||
end,
|
||||
ok = emit_event(?EV_CLIENT_CACHE_RESULT, Meta#{result => Result}, Context, Options),
|
||||
Result.
|
||||
|
||||
%%
|
||||
%% local
|
||||
%%
|
||||
-spec get_from_cache(_Key, cache_control(), options()) ->
|
||||
not_found | {ok, _Value}.
|
||||
-spec get_from_cache(_Key, cache_control(), options()) -> not_found | {ok, _Value}.
|
||||
get_from_cache(_, no_cache, _) ->
|
||||
not_found;
|
||||
get_from_cache(Key, CacheControl, Options) ->
|
||||
@ -139,8 +139,7 @@ get_from_cache(Key, CacheControl, Options) ->
|
||||
{ok, Value}
|
||||
end.
|
||||
|
||||
-spec update_cache(_Key, _Value, cache_control(), options()) ->
|
||||
ok.
|
||||
-spec update_cache(_Key, _Value, cache_control(), options()) -> ok.
|
||||
update_cache(_, _, no_cache, _) ->
|
||||
ok;
|
||||
update_cache(Key, Value, cache, Options) ->
|
||||
@ -150,80 +149,68 @@ update_cache(Key, Value, {cache_for, LifetimeMs}, Options) ->
|
||||
|
||||
%%
|
||||
|
||||
-spec workers_reg_name(options()) ->
|
||||
genlib_gen:reg_name().
|
||||
-spec workers_reg_name(options()) -> genlib_gen:reg_name().
|
||||
workers_reg_name(#{workers_name := Name}) ->
|
||||
{local, Name}.
|
||||
|
||||
-spec workers_ref(options()) ->
|
||||
genlib_gen:ref().
|
||||
-spec workers_ref(options()) -> genlib_gen:ref().
|
||||
workers_ref(#{workers_name := Name}) ->
|
||||
Name.
|
||||
|
||||
-spec cache_child_spec(atom(), options()) ->
|
||||
supervisor:child_spec().
|
||||
-spec cache_child_spec(atom(), options()) -> supervisor:child_spec().
|
||||
cache_child_spec(ChildID, Options) ->
|
||||
#{
|
||||
id => ChildID,
|
||||
start => {cache, start_link, [cache_name(Options), cache_options(Options)]},
|
||||
restart => permanent,
|
||||
type => supervisor
|
||||
id => ChildID,
|
||||
start => {cache, start_link, [cache_name(Options), cache_options(Options)]},
|
||||
restart => permanent,
|
||||
type => supervisor
|
||||
}.
|
||||
|
||||
-spec cache_name(options()) ->
|
||||
atom().
|
||||
-spec cache_name(options()) -> atom().
|
||||
cache_name(#{cache := #{local_name := Name}}) ->
|
||||
Name.
|
||||
|
||||
-spec cache_options(options()) ->
|
||||
list().
|
||||
-spec cache_options(options()) -> list().
|
||||
cache_options(#{cache := Options}) ->
|
||||
maps:to_list(Options).
|
||||
|
||||
-spec woody_client_options(options()) ->
|
||||
woody_client:options().
|
||||
-spec woody_client_options(options()) -> woody_client:options().
|
||||
woody_client_options(#{woody_client := Options}) ->
|
||||
Options.
|
||||
|
||||
-spec now_ms() ->
|
||||
integer().
|
||||
-spec now_ms() -> integer().
|
||||
now_ms() ->
|
||||
% The cache library uses os:timestamp/0 to get the current time, so just do the same
|
||||
os:system_time(millisecond).
|
||||
|
||||
-spec emit_event(woody_event_handler:event(), map(), woody_context:ctx(), options()) ->
|
||||
ok.
|
||||
-spec emit_event(woody_event_handler:event(), map(), woody_context:ctx(), options()) -> ok.
|
||||
emit_event(Event, Meta, #{rpc_id := RPCID}, Options) ->
|
||||
_ = woody_event_handler:handle_event(woody_event_handler(Options), Event, RPCID, Meta),
|
||||
ok.
|
||||
|
||||
-spec woody_event_handler(options()) ->
|
||||
woody:ev_handlers().
|
||||
-spec woody_event_handler(options()) -> woody:ev_handlers().
|
||||
woody_event_handler(#{woody_client := #{event_handler := EventHandler}}) ->
|
||||
EventHandler.
|
||||
|
||||
-spec url(options()) ->
|
||||
woody:url().
|
||||
-spec url(options()) -> woody:url().
|
||||
url(#{woody_client := #{url := URL}}) ->
|
||||
URL.
|
||||
|
||||
-spec new_meta(options(), woody_context:ctx()) ->
|
||||
map().
|
||||
-spec new_meta(options(), woody_context:ctx()) -> map().
|
||||
new_meta(Options, Context) ->
|
||||
#{
|
||||
role => client,
|
||||
role => client,
|
||||
metadata => woody_context:get_meta(Context),
|
||||
url => url(Options)
|
||||
url => url(Options)
|
||||
}.
|
||||
|
||||
%% FIXME протекающая абстракция
|
||||
-spec add_thrift_meta(woody:request(), map()) ->
|
||||
map().
|
||||
-spec add_thrift_meta(woody:request(), map()) -> map().
|
||||
add_thrift_meta({Service = {_, ServiceName}, Function, Args}, Meta) ->
|
||||
Meta#{
|
||||
service => ServiceName,
|
||||
service => ServiceName,
|
||||
service_schema => Service,
|
||||
function => Function,
|
||||
type => woody_util:get_rpc_type(Service, Function),
|
||||
args => Args
|
||||
function => Function,
|
||||
type => woody_util:get_rpc_type(Service, Function),
|
||||
args => Args
|
||||
}.
|
||||
|
@ -6,20 +6,18 @@
|
||||
-export([get_common_name/1]).
|
||||
|
||||
-opaque cert() :: public_key:der_encoded() | #'OTPCertificate'{} | undefined.
|
||||
|
||||
-type common_name() :: binary().
|
||||
|
||||
-export_type([cert/0, common_name/0]).
|
||||
|
||||
%%% API
|
||||
|
||||
-spec from_req(cowboy_req:req()) ->
|
||||
cert().
|
||||
|
||||
-spec from_req(cowboy_req:req()) -> cert().
|
||||
from_req(Req) ->
|
||||
cowboy_req:cert(Req).
|
||||
|
||||
-spec get_common_name(cert()) ->
|
||||
common_name() | undefined.
|
||||
|
||||
-spec get_common_name(cert()) -> common_name() | undefined.
|
||||
get_common_name(undefined) ->
|
||||
undefined;
|
||||
get_common_name(Cert) when is_binary(Cert) ->
|
||||
@ -35,9 +33,11 @@ get_common_name(#'OTPCertificate'{tbsCertificate = TbsCert}) ->
|
||||
%%% Internal functions
|
||||
|
||||
get_cn_from_rdn({rdnSequence, RDNSeq}) ->
|
||||
[to_binary(V) ||
|
||||
ATVs <- RDNSeq,
|
||||
#'AttributeTypeAndValue'{type = ?'id-at-commonName', value = {_T, V}} <- ATVs];
|
||||
[
|
||||
to_binary(V)
|
||||
|| ATVs <- RDNSeq,
|
||||
#'AttributeTypeAndValue'{type = ?'id-at-commonName', value = {_T, V}} <- ATVs
|
||||
];
|
||||
get_cn_from_rdn(_) ->
|
||||
[].
|
||||
|
||||
|
@ -7,48 +7,49 @@
|
||||
|
||||
%% API
|
||||
-export([child_spec/1]).
|
||||
-export([call /2]).
|
||||
-export([call /3]).
|
||||
-export([call/2]).
|
||||
-export([call/3]).
|
||||
|
||||
%% Types
|
||||
-type options() :: #{
|
||||
url := woody:url(),
|
||||
event_handler := woody:ev_handlers(),
|
||||
protocol => thrift,
|
||||
transport => http,
|
||||
url := woody:url(),
|
||||
event_handler := woody:ev_handlers(),
|
||||
protocol => thrift,
|
||||
transport => http,
|
||||
%% Set to override protocol handler module selection, useful for test purposes, rarely
|
||||
%% if ever needed otherwise.
|
||||
protocol_handler_override => module(),
|
||||
%% Implementation-specific options
|
||||
_ => _
|
||||
_ => _
|
||||
}.
|
||||
|
||||
-export_type([options/0]).
|
||||
|
||||
%% Internal API
|
||||
-type result() ::
|
||||
{ok , woody:result ()} |
|
||||
{error , woody_error:error()}.
|
||||
{ok, woody:result()}
|
||||
| {error, woody_error:error()}.
|
||||
|
||||
-export_type([result/0]).
|
||||
|
||||
%%
|
||||
%% API
|
||||
%%
|
||||
-spec child_spec(options()) ->
|
||||
supervisor:child_spec().
|
||||
-spec child_spec(options()) -> supervisor:child_spec().
|
||||
child_spec(Options) ->
|
||||
woody_client_behaviour:child_spec(Options).
|
||||
|
||||
-spec call(woody:request(), options()) ->
|
||||
{ok, woody:result()} |
|
||||
{exception, woody_error:business_error()} |
|
||||
no_return().
|
||||
{ok, woody:result()}
|
||||
| {exception, woody_error:business_error()}
|
||||
| no_return().
|
||||
call(Request, Options) ->
|
||||
call(Request, Options, woody_context:new()).
|
||||
|
||||
-spec call(woody:request(), options(), woody_context:ctx()) ->
|
||||
{ok, woody:result()} |
|
||||
{exception, woody_error:business_error()} |
|
||||
no_return().
|
||||
{ok, woody:result()}
|
||||
| {exception, woody_error:business_error()}
|
||||
| no_return().
|
||||
call(Request, Options = #{event_handler := EvHandler}, Context) ->
|
||||
Child = woody_context:new_child(Context),
|
||||
WoodyState = woody_state:new(client, Child, EvHandler),
|
||||
@ -64,14 +65,13 @@ call(Request, Options = #{event_handler := EvHandler}, Context) ->
|
||||
%%
|
||||
%% Internal functions
|
||||
%%
|
||||
-spec call_safe(woody:request(), options(), woody_state:st()) ->
|
||||
result().
|
||||
-spec call_safe(woody:request(), options(), woody_state:st()) -> result().
|
||||
call_safe(Request, Options, WoodyState) ->
|
||||
_ = woody_event_handler:handle_event(?EV_CLIENT_BEGIN, WoodyState, #{}),
|
||||
try woody_client_behaviour:call(Request, Options, WoodyState) of
|
||||
Resp = {ok, _} ->
|
||||
Resp;
|
||||
Error = {error, {Type, _}} when Type =:= system ; Type =:= business ->
|
||||
Error = {error, {Type, _}} when Type =:= system; Type =:= business ->
|
||||
Error
|
||||
catch
|
||||
Class:Reason:Stacktrace ->
|
||||
@ -85,10 +85,10 @@ call_safe(Request, Options, WoodyState) ->
|
||||
handle_client_error(Class, Error, Stacktrace, WoodyState) ->
|
||||
Details = woody_error:format_details(Error),
|
||||
_ = woody_event_handler:handle_event(?EV_INTERNAL_ERROR, WoodyState, #{
|
||||
error => woody_util:to_binary([?EV_CALL_SERVICE, " error"]),
|
||||
class => Class,
|
||||
reason => Details,
|
||||
stack => Stacktrace,
|
||||
final => false
|
||||
error => woody_util:to_binary([?EV_CALL_SERVICE, " error"]),
|
||||
class => Class,
|
||||
reason => Details,
|
||||
stack => Stacktrace,
|
||||
final => false
|
||||
}),
|
||||
{error, {system, {internal, result_unexpected, <<"client error: ", Details/binary>>}}}.
|
||||
|
@ -1,20 +1,18 @@
|
||||
-module(woody_client_behaviour).
|
||||
|
||||
-export([child_spec/1]).
|
||||
-export([call /3]).
|
||||
-export([call/3]).
|
||||
|
||||
%% Behaviour definition
|
||||
-callback call(woody:request(), woody_client:options(), woody_state:st()) -> woody_client:result().
|
||||
-callback call(woody:request(), woody_client:options(), woody_state:st()) -> woody_client:result().
|
||||
-callback child_spec(woody_client:options()) -> supervisor:child_spec().
|
||||
|
||||
-spec child_spec(woody_client:options()) ->
|
||||
supervisor:child_spec().
|
||||
-spec child_spec(woody_client:options()) -> supervisor:child_spec().
|
||||
child_spec(Options) ->
|
||||
Handler = woody_util:get_protocol_handler(client, Options),
|
||||
Handler:child_spec(Options).
|
||||
|
||||
-spec call(woody:request(), woody_client:options(), woody_state:st()) ->
|
||||
woody_client:result().
|
||||
-spec call(woody:request(), woody_client:options(), woody_state:st()) -> woody_client:result().
|
||||
call(Request, Options, WoodyState) ->
|
||||
Handler = woody_util:get_protocol_handler(client, Options),
|
||||
Handler:call(Request, Options, WoodyState).
|
||||
|
@ -28,11 +28,11 @@ increment_counter(Key) ->
|
||||
|
||||
-spec increment_counter(any(), number()) -> ok | {error, term()}.
|
||||
increment_counter([hackney, _Host, _], _) ->
|
||||
ok; % we don't need per host metrics
|
||||
% we don't need per host metrics
|
||||
ok;
|
||||
increment_counter(Key, Value) ->
|
||||
update_metric(counter, Key, Value).
|
||||
|
||||
|
||||
-spec decrement_counter(any()) -> ok | {error, term()}.
|
||||
decrement_counter(Key) ->
|
||||
decrement_counter(Key, 1).
|
||||
@ -47,7 +47,7 @@ update_histogram(Key, Value) ->
|
||||
|
||||
-spec update_gauge(any(), number()) -> ok | {error, term()}.
|
||||
update_gauge(Key, Value) ->
|
||||
update_metric(gauge, Key, Value).
|
||||
update_metric(gauge, Key, Value).
|
||||
|
||||
-spec update_meter(any(), number()) -> ok | {error, term()}.
|
||||
update_meter(Key, Value) ->
|
||||
@ -72,7 +72,6 @@ update_metric(Type, Key0, Value) ->
|
||||
tag_key(Key) when is_list(Key) ->
|
||||
[woody, client | Key].
|
||||
|
||||
|
||||
is_allowed_metric([hackney_pool, _, Metric]) ->
|
||||
lists:member(Metric, get_allowed_pool_metrics());
|
||||
is_allowed_metric(Key) ->
|
||||
|
@ -3,20 +3,21 @@
|
||||
-behaviour(woody_client_behaviour).
|
||||
|
||||
-include_lib("thrift/include/thrift_constants.hrl").
|
||||
|
||||
-include("woody_defs.hrl").
|
||||
|
||||
%% woody_client_behaviour callback
|
||||
-export([call /3]).
|
||||
-export([call/3]).
|
||||
-export([child_spec/1]).
|
||||
|
||||
%% Types
|
||||
-type options() :: #{
|
||||
url := woody:url(),
|
||||
event_handler := woody:ev_handlers(),
|
||||
url := woody:url(),
|
||||
event_handler := woody:ev_handlers(),
|
||||
transport_opts => woody_client_thrift_http_transport:transport_options(),
|
||||
resolver_opts => woody_resolver:options(),
|
||||
protocol => thrift,
|
||||
transport => http
|
||||
resolver_opts => woody_resolver:options(),
|
||||
protocol => thrift,
|
||||
transport => http
|
||||
}.
|
||||
|
||||
-type thrift_client() :: term().
|
||||
@ -26,24 +27,22 @@
|
||||
%%
|
||||
%% API
|
||||
%%
|
||||
-spec child_spec(options()) ->
|
||||
supervisor:child_spec().
|
||||
-spec child_spec(options()) -> supervisor:child_spec().
|
||||
child_spec(Options) ->
|
||||
woody_client_thrift_http_transport:child_spec(get_transport_opts(Options)).
|
||||
|
||||
-spec call(woody:request(), options(), woody_state:st()) ->
|
||||
woody_client:result().
|
||||
-spec call(woody:request(), options(), woody_state:st()) -> woody_client:result().
|
||||
call({Service = {_, ServiceName}, Function, Args}, Opts, WoodyState) ->
|
||||
WoodyContext = woody_state:get_context(WoodyState),
|
||||
WoodyState1 = woody_state:add_ev_meta(
|
||||
#{
|
||||
service => ServiceName,
|
||||
service => ServiceName,
|
||||
service_schema => Service,
|
||||
function => Function,
|
||||
type => woody_util:get_rpc_type(Service, Function),
|
||||
args => Args,
|
||||
deadline => woody_context:get_deadline(WoodyContext),
|
||||
metadata => woody_context:get_meta(WoodyContext)
|
||||
function => Function,
|
||||
type => woody_util:get_rpc_type(Service, Function),
|
||||
args => Args,
|
||||
deadline => woody_context:get_deadline(WoodyContext),
|
||||
metadata => woody_context:get_meta(WoodyContext)
|
||||
},
|
||||
WoodyState
|
||||
),
|
||||
@ -53,8 +52,7 @@ call({Service = {_, ServiceName}, Function, Args}, Opts, WoodyState) ->
|
||||
%%
|
||||
%% Internal functions
|
||||
%%
|
||||
-spec make_thrift_client(woody:service(), options(), woody_state:st()) ->
|
||||
thrift_client().
|
||||
-spec make_thrift_client(woody:service(), options(), woody_state:st()) -> thrift_client().
|
||||
make_thrift_client(Service, Opts = #{url := Url}, WoodyState) ->
|
||||
{ok, Protocol} = thrift_binary_protocol:new(
|
||||
woody_client_thrift_http_transport:new(
|
||||
@ -68,20 +66,19 @@ make_thrift_client(Service, Opts = #{url := Url}, WoodyState) ->
|
||||
{ok, Client} = thrift_client:new(Protocol, Service),
|
||||
Client.
|
||||
|
||||
-spec get_transport_opts(options()) ->
|
||||
woody_client_thrift_http_transport:transport_options().
|
||||
-spec get_transport_opts(options()) -> woody_client_thrift_http_transport:transport_options().
|
||||
get_transport_opts(Opts) ->
|
||||
maps:get(transport_opts, Opts, #{}).
|
||||
|
||||
-spec get_resolver_opts(options()) ->
|
||||
woody_resolver:options().
|
||||
-spec get_resolver_opts(options()) -> woody_resolver:options().
|
||||
get_resolver_opts(Opts) ->
|
||||
maps:get(resolver_opts, Opts, #{}).
|
||||
|
||||
-spec do_call(thrift_client(), woody:func(), woody:args(), woody_state:st()) ->
|
||||
woody_client:result().
|
||||
-spec do_call(thrift_client(), woody:func(), woody:args(), woody_state:st()) -> woody_client:result().
|
||||
do_call(Client, Function, Args, WoodyState) ->
|
||||
{ClientNext, Result} = try thrift_client:call(Client, Function, tuple_to_list(Args))
|
||||
{ClientNext, Result} =
|
||||
try
|
||||
thrift_client:call(Client, Function, tuple_to_list(Args))
|
||||
catch
|
||||
throw:{Client1, {exception, #'TApplicationException'{}}} ->
|
||||
{Client1, {error, {system, get_server_violation_error()}}};
|
||||
@ -105,8 +102,7 @@ log_result({error, {business, ThriftExcept}}, WoodyState) ->
|
||||
log_result({error, Result}, WoodyState) ->
|
||||
log_event(?EV_SERVICE_RESULT, WoodyState, #{status => error, class => system, result => Result}).
|
||||
|
||||
-spec map_result(woody_client:result() | {error, _ThriftError}) ->
|
||||
woody_client:result().
|
||||
-spec map_result(woody_client:result() | {error, _ThriftError}) -> woody_client:result().
|
||||
map_result(Res = {ok, _}) ->
|
||||
Res;
|
||||
map_result(Res = {error, {Type, _}}) when Type =:= business orelse Type =:= system ->
|
||||
|
@ -1,23 +1,25 @@
|
||||
-module(woody_client_thrift_http_transport).
|
||||
|
||||
-behaviour(thrift_transport).
|
||||
|
||||
-dialyzer(no_undefined_callbacks).
|
||||
|
||||
-include("woody_defs.hrl").
|
||||
|
||||
-include_lib("hackney/include/hackney_lib.hrl").
|
||||
|
||||
%% API
|
||||
-export([new /4]).
|
||||
-export([new/4]).
|
||||
-export([child_spec/1]).
|
||||
|
||||
%% Thrift transport callbacks
|
||||
-export([read/2, write/2, flush/1, close/1]).
|
||||
|
||||
|
||||
%% Types
|
||||
|
||||
%% See hackney:request/5 for available options.
|
||||
-type transport_options() :: map().
|
||||
|
||||
-export_type([transport_options/0]).
|
||||
|
||||
-define(DEFAULT_TRANSPORT_OPTIONS, #{
|
||||
@ -31,20 +33,20 @@
|
||||
}).
|
||||
|
||||
-type woody_transport() :: #{
|
||||
url := woody:url(),
|
||||
woody_state := woody_state:st(),
|
||||
url := woody:url(),
|
||||
woody_state := woody_state:st(),
|
||||
transport_options := transport_options(),
|
||||
resolver_options := woody_resolver:options(),
|
||||
write_buffer := binary(),
|
||||
read_buffer := binary()
|
||||
resolver_options := woody_resolver:options(),
|
||||
write_buffer := binary(),
|
||||
read_buffer := binary()
|
||||
}.
|
||||
|
||||
-type error() :: {error, {system, woody_error:system_error()}}.
|
||||
-type error() :: {error, {system, woody_error:system_error()}}.
|
||||
-type header_parse_value() :: none | woody:http_header_val().
|
||||
|
||||
-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(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">>).
|
||||
|
||||
%%
|
||||
%% API
|
||||
@ -53,17 +55,16 @@
|
||||
thrift_transport:t_transport() | no_return().
|
||||
new(Url, Opts, ResOpts, WoodyState) ->
|
||||
{ok, Transport} = thrift_transport:new(?MODULE, #{
|
||||
url => Url,
|
||||
url => Url,
|
||||
transport_options => Opts,
|
||||
resolver_options => ResOpts,
|
||||
woody_state => WoodyState,
|
||||
write_buffer => <<>>,
|
||||
read_buffer => <<>>
|
||||
resolver_options => ResOpts,
|
||||
woody_state => WoodyState,
|
||||
write_buffer => <<>>,
|
||||
read_buffer => <<>>
|
||||
}),
|
||||
Transport.
|
||||
|
||||
-spec child_spec(transport_options()) ->
|
||||
supervisor:child_spec().
|
||||
-spec child_spec(transport_options()) -> supervisor:child_spec().
|
||||
child_spec(Options) ->
|
||||
Name = maps:get(pool, Options, undefined),
|
||||
hackney_pool:child_spec(Name, maps:to_list(Options)).
|
||||
@ -71,15 +72,11 @@ child_spec(Options) ->
|
||||
%%
|
||||
%% Thrift transport callbacks
|
||||
%%
|
||||
-spec write(woody_transport(), binary()) ->
|
||||
{woody_transport(), ok}.
|
||||
write(Transport = #{write_buffer := WBuffer}, Data) when
|
||||
is_binary(WBuffer), is_binary(Data)
|
||||
->
|
||||
-spec write(woody_transport(), binary()) -> {woody_transport(), ok}.
|
||||
write(Transport = #{write_buffer := WBuffer}, Data) when is_binary(WBuffer), is_binary(Data) ->
|
||||
{Transport#{write_buffer => <<WBuffer/binary, Data/binary>>}, ok}.
|
||||
|
||||
-spec read(woody_transport(), pos_integer()) ->
|
||||
{woody_transport(), {ok, binary()}}.
|
||||
-spec read(woody_transport(), pos_integer()) -> {woody_transport(), {ok, binary()}}.
|
||||
read(Transport = #{read_buffer := RBuffer}, Len) when is_binary(RBuffer) ->
|
||||
Give = min(byte_size(RBuffer), Len),
|
||||
<<Data:Give/binary, RBuffer1/binary>> = RBuffer,
|
||||
@ -87,25 +84,29 @@ read(Transport = #{read_buffer := RBuffer}, Len) when is_binary(RBuffer) ->
|
||||
Transport1 = Transport#{read_buffer => RBuffer1},
|
||||
{Transport1, Response}.
|
||||
|
||||
-spec flush(woody_transport()) ->
|
||||
{woody_transport(), ok | error()}.
|
||||
flush(Transport = #{
|
||||
url := Url,
|
||||
woody_state := WoodyState,
|
||||
transport_options := Options,
|
||||
resolver_options := ResOpts,
|
||||
write_buffer := WBuffer,
|
||||
read_buffer := RBuffer
|
||||
}) when is_binary(WBuffer), is_binary(RBuffer) ->
|
||||
case handle_result(
|
||||
send(Url, WBuffer, Options, ResOpts, WoodyState),
|
||||
WoodyState
|
||||
) of
|
||||
-spec flush(woody_transport()) -> {woody_transport(), ok | error()}.
|
||||
flush(
|
||||
Transport = #{
|
||||
url := Url,
|
||||
woody_state := WoodyState,
|
||||
transport_options := Options,
|
||||
resolver_options := ResOpts,
|
||||
write_buffer := WBuffer,
|
||||
read_buffer := RBuffer
|
||||
}
|
||||
) when is_binary(WBuffer), is_binary(RBuffer) ->
|
||||
case
|
||||
handle_result(
|
||||
send(Url, WBuffer, Options, ResOpts, WoodyState),
|
||||
WoodyState
|
||||
)
|
||||
of
|
||||
{ok, Response} ->
|
||||
{Transport#{
|
||||
read_buffer => Response,
|
||||
write_buffer => <<>>
|
||||
}, ok};
|
||||
read_buffer => Response,
|
||||
write_buffer => <<>>
|
||||
},
|
||||
ok};
|
||||
Error ->
|
||||
{Transport#{read_buffer => <<>>, write_buffer => <<>>}, Error}
|
||||
end.
|
||||
@ -122,7 +123,7 @@ send(Url, Body, Options, ResOpts, WoodyState) ->
|
||||
% reusing keep-alive connections to dead hosts
|
||||
case woody_resolver:resolve_url(Url, WoodyState, ResOpts) of
|
||||
{ok, {OldUrl, NewUrl}} ->
|
||||
Headers = add_host_header(OldUrl, make_woody_headers(Context)),
|
||||
Headers = add_host_header(OldUrl, make_woody_headers(Context)),
|
||||
Options1 = set_defaults(Options),
|
||||
Options2 = set_timeouts(Options1, Context),
|
||||
HeaderList = maps:to_list(Headers),
|
||||
@ -154,15 +155,17 @@ set_timeouts(Options, Context) ->
|
||||
%% It is intentional, that application can override the timeout values
|
||||
%% calculated from the deadline (first option value in the list takes
|
||||
%% the precedence).
|
||||
maps:merge(#{
|
||||
connect_timeout => ConnectTimeout,
|
||||
send_timeout => SendTimeout,
|
||||
recv_timeout => Timeout
|
||||
}, Options)
|
||||
maps:merge(
|
||||
#{
|
||||
connect_timeout => ConnectTimeout,
|
||||
send_timeout => SendTimeout,
|
||||
recv_timeout => Timeout
|
||||
},
|
||||
Options
|
||||
)
|
||||
end.
|
||||
|
||||
-spec deadline_to_timeout(woody:deadline()) ->
|
||||
non_neg_integer().
|
||||
-spec deadline_to_timeout(woody:deadline()) -> non_neg_integer().
|
||||
deadline_to_timeout(Deadline) ->
|
||||
try
|
||||
woody_deadline:to_timeout(Deadline)
|
||||
@ -171,14 +174,15 @@ deadline_to_timeout(Deadline) ->
|
||||
0
|
||||
end.
|
||||
|
||||
-define(DEFAULT_CONNECT_AND_SEND_TIMEOUT, 1000). %% millisec
|
||||
%% millisec
|
||||
-define(DEFAULT_CONNECT_AND_SEND_TIMEOUT, 1000).
|
||||
|
||||
calc_timeouts(Timeout) ->
|
||||
%% It is assumed that connect and send timeouts each
|
||||
%% should take no more than 20% of the total request time
|
||||
%% and in any case no more, than DEFAULT_CONNECT_AND_SEND_TIMEOUT together.
|
||||
case Timeout div 5 of
|
||||
T when (T*2) > ?DEFAULT_CONNECT_AND_SEND_TIMEOUT ->
|
||||
T when (T * 2) > ?DEFAULT_CONNECT_AND_SEND_TIMEOUT ->
|
||||
?DEFAULT_CONNECT_AND_SEND_TIMEOUT;
|
||||
T ->
|
||||
T
|
||||
@ -187,26 +191,25 @@ calc_timeouts(Timeout) ->
|
||||
is_deadline_reached(Context) ->
|
||||
woody_deadline:is_reached(woody_context:get_deadline(Context)).
|
||||
|
||||
-spec close(woody_transport()) ->
|
||||
{woody_transport(), ok}.
|
||||
-spec close(woody_transport()) -> {woody_transport(), ok}.
|
||||
close(Transport) ->
|
||||
{Transport#{}, ok}.
|
||||
|
||||
%%
|
||||
%% Internal functions
|
||||
%%
|
||||
-spec handle_result(_, woody_state:st()) ->
|
||||
{ok, woody:http_body()} | error().
|
||||
-spec handle_result(_, woody_state:st()) -> {ok, woody:http_body()} | error().
|
||||
handle_result({ok, 200, Headers, Ref}, WoodyState) ->
|
||||
Meta = case check_error_reason(Headers, 200, WoodyState) of
|
||||
<<>> -> #{};
|
||||
Reason -> #{reason => Reason}
|
||||
end,
|
||||
Meta =
|
||||
case check_error_reason(Headers, 200, WoodyState) of
|
||||
<<>> -> #{};
|
||||
Reason -> #{reason => Reason}
|
||||
end,
|
||||
_ = log_event(?EV_CLIENT_RECEIVE, WoodyState, Meta#{status => ok, code => 200}),
|
||||
get_body(hackney:body(Ref), WoodyState);
|
||||
handle_result({ok, Code, Headers, Ref}, WoodyState) ->
|
||||
{Class, Details} = check_error_headers(Code, Headers, WoodyState),
|
||||
_ = log_event(?EV_CLIENT_RECEIVE, WoodyState, #{status=>error, code=>Code, reason=>Details}),
|
||||
_ = log_event(?EV_CLIENT_RECEIVE, WoodyState, #{status => error, code => Code, reason => Details}),
|
||||
%% Free the connection
|
||||
case hackney:skip_body(Ref) of
|
||||
ok ->
|
||||
@ -220,25 +223,25 @@ handle_result({error, {closed, _}}, WoodyState) ->
|
||||
_ = log_event(?EV_CLIENT_RECEIVE, WoodyState, #{status => error, reason => Reason}),
|
||||
{error, {system, {external, result_unknown, Reason}}};
|
||||
handle_result({error, Reason}, WoodyState) when
|
||||
Reason =:= timeout ;
|
||||
Reason =:= econnaborted ;
|
||||
Reason =:= enetreset ;
|
||||
Reason =:= econnreset ;
|
||||
Reason =:= eshutdown ;
|
||||
Reason =:= etimedout ;
|
||||
Reason =:= timeout;
|
||||
Reason =:= econnaborted;
|
||||
Reason =:= enetreset;
|
||||
Reason =:= econnreset;
|
||||
Reason =:= eshutdown;
|
||||
Reason =:= etimedout;
|
||||
Reason =:= closed
|
||||
->
|
||||
BinReason = woody_util:to_binary(Reason),
|
||||
_ = log_event(?EV_CLIENT_RECEIVE, WoodyState, #{status => error, reason => BinReason}),
|
||||
{error, {system, {external, result_unknown, BinReason}}};
|
||||
handle_result({error, Reason}, WoodyState) when
|
||||
Reason =:= econnrefused ;
|
||||
Reason =:= connect_timeout ;
|
||||
Reason =:= checkout_timeout;
|
||||
Reason =:= enetdown ;
|
||||
Reason =:= enetunreach ;
|
||||
Reason =:= ehostunreach ;
|
||||
Reason =:= eacces ;
|
||||
Reason =:= econnrefused;
|
||||
Reason =:= connect_timeout;
|
||||
Reason =:= checkout_timeout;
|
||||
Reason =:= enetdown;
|
||||
Reason =:= enetunreach;
|
||||
Reason =:= ehostunreach;
|
||||
Reason =:= eacces;
|
||||
element(1, Reason) =:= resolve_failed
|
||||
->
|
||||
BinReason = woody_error:format_details(Reason),
|
||||
@ -251,8 +254,7 @@ handle_result({error, Reason}, WoodyState) ->
|
||||
_ = log_event(?EV_CLIENT_RECEIVE, WoodyState, #{status => error, reason => Details}),
|
||||
{error, {system, {internal, result_unexpected, Details}}}.
|
||||
|
||||
-spec get_body({ok, woody:http_body()} | {error, atom()}, woody_state:st()) ->
|
||||
{ok, woody:http_body()} | error().
|
||||
-spec get_body({ok, woody:http_body()} | {error, atom()}, woody_state:st()) -> {ok, woody:http_body()} | error().
|
||||
get_body(B = {ok, _}, _) ->
|
||||
B;
|
||||
get_body({error, Reason}, WoodyState) ->
|
||||
@ -266,8 +268,7 @@ check_error_headers(502, Headers, WoodyState) ->
|
||||
check_error_headers(Code, Headers, WoodyState) ->
|
||||
{get_error_class(Code), check_error_reason(Headers, Code, WoodyState)}.
|
||||
|
||||
-spec get_error_class(woody:http_code()) ->
|
||||
woody_error:class().
|
||||
-spec get_error_class(woody:http_code()) -> woody_error:class().
|
||||
get_error_class(503) ->
|
||||
resource_unavailable;
|
||||
get_error_class(504) ->
|
||||
@ -290,13 +291,11 @@ check_502_error_class(Bad, _, WoodyState) ->
|
||||
_ = log_internal_error(?ERROR_RESP_HEADER, ["unknown ", ?HEADER_E_CLASS, " header value: ", Bad], WoodyState),
|
||||
{result_unexpected, ?BAD_RESP_HEADER}.
|
||||
|
||||
-spec check_error_reason(woody:http_headers(), woody:http_code(), woody_state:st()) ->
|
||||
woody_error:details().
|
||||
-spec check_error_reason(woody:http_headers(), woody:http_code(), woody_state:st()) -> woody_error:details().
|
||||
check_error_reason(Headers, Code, WoodyState) ->
|
||||
do_check_error_reason(get_header_value(?HEADER_E_REASON, Headers), Code, WoodyState).
|
||||
|
||||
-spec do_check_error_reason(header_parse_value(), woody:http_code(), woody_state:st()) ->
|
||||
woody_error:details().
|
||||
-spec do_check_error_reason(header_parse_value(), woody:http_code(), woody_state:st()) -> woody_error:details().
|
||||
do_check_error_reason(none, 200, _WoodyState) ->
|
||||
<<>>;
|
||||
do_check_error_reason(none, Code, WoodyState) ->
|
||||
@ -305,8 +304,7 @@ do_check_error_reason(none, Code, WoodyState) ->
|
||||
do_check_error_reason(Reason, _, _) ->
|
||||
Reason.
|
||||
|
||||
-spec get_error_class_header_value(woody:http_headers()) ->
|
||||
header_parse_value().
|
||||
-spec get_error_class_header_value(woody:http_headers()) -> header_parse_value().
|
||||
get_error_class_header_value(Headers) ->
|
||||
case get_header_value(?HEADER_E_CLASS, Headers) of
|
||||
None when None =:= none orelse None =:= multiple ->
|
||||
@ -315,34 +313,30 @@ get_error_class_header_value(Headers) ->
|
||||
genlib_string:to_lower(Value)
|
||||
end.
|
||||
|
||||
-spec get_header_value(woody:http_header_name(), woody:http_headers()) ->
|
||||
header_parse_value().
|
||||
-spec get_header_value(woody:http_header_name(), woody:http_headers()) -> header_parse_value().
|
||||
get_header_value(Name, Headers) ->
|
||||
% Couldn't find a way to easily do same with maps
|
||||
HeaderList = maps:to_list(Headers),
|
||||
case [V || {K, V} <- HeaderList, Name =:= genlib_string:to_lower(K)] of
|
||||
[Value] -> Value;
|
||||
[] -> none
|
||||
[] -> none
|
||||
end.
|
||||
|
||||
-spec make_woody_headers(woody_context:ctx()) ->
|
||||
woody:http_headers().
|
||||
-spec make_woody_headers(woody_context:ctx()) -> woody:http_headers().
|
||||
make_woody_headers(Context) ->
|
||||
add_optional_headers(Context, #{
|
||||
<<"content-type">> => ?CONTENT_TYPE_THRIFT,
|
||||
<<"accept">> => ?CONTENT_TYPE_THRIFT,
|
||||
?HEADER_RPC_ROOT_ID => woody_context:get_rpc_id(trace_id , Context),
|
||||
?HEADER_RPC_ID => woody_context:get_rpc_id(span_id , Context),
|
||||
<<"content-type">> => ?CONTENT_TYPE_THRIFT,
|
||||
<<"accept">> => ?CONTENT_TYPE_THRIFT,
|
||||
?HEADER_RPC_ROOT_ID => woody_context:get_rpc_id(trace_id, Context),
|
||||
?HEADER_RPC_ID => woody_context:get_rpc_id(span_id, Context),
|
||||
?HEADER_RPC_PARENT_ID => woody_context:get_rpc_id(parent_id, Context)
|
||||
}).
|
||||
|
||||
-spec add_optional_headers(woody_context:ctx(), woody:http_headers()) ->
|
||||
woody:http_headers().
|
||||
-spec add_optional_headers(woody_context:ctx(), woody:http_headers()) -> woody:http_headers().
|
||||
add_optional_headers(Context, Headers) ->
|
||||
add_deadline_header(Context, add_metadata_headers(Context, Headers)).
|
||||
|
||||
-spec add_metadata_headers(woody_context:ctx(), woody:http_headers()) ->
|
||||
woody:http_headers().
|
||||
-spec add_metadata_headers(woody_context:ctx(), woody:http_headers()) -> woody:http_headers().
|
||||
add_metadata_headers(Context, Headers) ->
|
||||
maps:fold(fun add_metadata_header/3, Headers, woody_context:get_meta(Context)).
|
||||
|
||||
|
@ -3,26 +3,28 @@
|
||||
-behaviour(woody_client_behaviour).
|
||||
|
||||
-include_lib("thrift/include/thrift_constants.hrl").
|
||||
|
||||
-include("woody_defs.hrl").
|
||||
|
||||
%% woody_client_behaviour callback
|
||||
-export([call /3]).
|
||||
-export([call/3]).
|
||||
-export([child_spec/1]).
|
||||
|
||||
%% Types
|
||||
-type options() :: #{
|
||||
url := woody:url(),
|
||||
event_handler := woody:ev_handlers(),
|
||||
url := woody:url(),
|
||||
event_handler := woody:ev_handlers(),
|
||||
transport_opts => transport_options(),
|
||||
resolver_opts => woody_resolver:options(),
|
||||
protocol => thrift,
|
||||
transport => http
|
||||
resolver_opts => woody_resolver:options(),
|
||||
protocol => thrift,
|
||||
transport => http
|
||||
}.
|
||||
|
||||
%% See hackney:request/5 for available options.
|
||||
-type transport_options() :: map().
|
||||
|
||||
-define(DEFAULT_CONNECT_AND_SEND_TIMEOUT, 1000). %% millisec
|
||||
%% millisec
|
||||
-define(DEFAULT_CONNECT_AND_SEND_TIMEOUT, 1000).
|
||||
-define(DEFAULT_TRANSPORT_OPTIONS, #{
|
||||
connect_options => [
|
||||
% Turn TCP_NODELAY on.
|
||||
@ -36,26 +38,24 @@
|
||||
%%
|
||||
%% API
|
||||
%%
|
||||
-spec child_spec(options()) ->
|
||||
supervisor:child_spec().
|
||||
-spec child_spec(options()) -> supervisor:child_spec().
|
||||
child_spec(Options) ->
|
||||
TransportOpts = get_transport_opts(Options),
|
||||
Name = maps:get(pool, TransportOpts, undefined),
|
||||
hackney_pool:child_spec(Name, maps:to_list(TransportOpts)).
|
||||
|
||||
-spec call(woody:request(), options(), woody_state:st()) ->
|
||||
woody_client:result().
|
||||
-spec call(woody:request(), options(), woody_state:st()) -> woody_client:result().
|
||||
call({Service = {_, ServiceName}, Function, Args}, Opts, WoodyState) ->
|
||||
WoodyContext = woody_state:get_context(WoodyState),
|
||||
WoodyState1 = woody_state:add_ev_meta(
|
||||
#{
|
||||
service => ServiceName,
|
||||
service => ServiceName,
|
||||
service_schema => Service,
|
||||
function => Function,
|
||||
type => woody_util:get_rpc_type(Service, Function),
|
||||
args => Args,
|
||||
deadline => woody_context:get_deadline(WoodyContext),
|
||||
metadata => woody_context:get_meta(WoodyContext)
|
||||
function => Function,
|
||||
type => woody_util:get_rpc_type(Service, Function),
|
||||
args => Args,
|
||||
deadline => woody_context:get_deadline(WoodyContext),
|
||||
metadata => woody_context:get_meta(WoodyContext)
|
||||
},
|
||||
WoodyState
|
||||
),
|
||||
@ -72,9 +72,9 @@ call({Service = {_, ServiceName}, Function, Args}, Opts, WoodyState) ->
|
||||
-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(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,
|
||||
{external, result_unexpected, <<
|
||||
@ -83,36 +83,33 @@ call({Service = {_, ServiceName}, Function, Args}, Opts, WoodyState) ->
|
||||
>>}
|
||||
).
|
||||
|
||||
-spec get_transport_opts(options()) ->
|
||||
woody_client_thrift_http_transport:transport_options().
|
||||
-spec get_transport_opts(options()) -> woody_client_thrift_http_transport:transport_options().
|
||||
get_transport_opts(Opts) ->
|
||||
maps:get(transport_opts, Opts, #{}).
|
||||
|
||||
-spec get_resolver_opts(options()) ->
|
||||
woody_resolver:options().
|
||||
-spec get_resolver_opts(options()) -> woody_resolver:options().
|
||||
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().
|
||||
-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(),
|
||||
Result = case thrift_client_codec:write_function_call(Buffer, ?CODEC, Service, Function, Args, 0) of
|
||||
{ok, Buffer1} ->
|
||||
case send_call(Buffer1, Opts, WoodyState) of
|
||||
{ok, Response} ->
|
||||
handle_result(Service, Function, Response);
|
||||
Error ->
|
||||
Error
|
||||
end;
|
||||
Error ->
|
||||
Error
|
||||
end,
|
||||
Result =
|
||||
case thrift_client_codec:write_function_call(Buffer, ?CODEC, Service, Function, Args, 0) of
|
||||
{ok, Buffer1} ->
|
||||
case send_call(Buffer1, Opts, WoodyState) of
|
||||
{ok, Response} ->
|
||||
handle_result(Service, Function, Response);
|
||||
Error ->
|
||||
Error
|
||||
end;
|
||||
Error ->
|
||||
Error
|
||||
end,
|
||||
log_result(Result, WoodyState),
|
||||
map_result(Result).
|
||||
|
||||
-spec handle_result(woody:service(), woody:func(), binary()) ->
|
||||
_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
|
||||
@ -134,9 +131,11 @@ handle_result(Service, Function, Response) ->
|
||||
Error
|
||||
end.
|
||||
|
||||
%% erlfmt-ignore
|
||||
-spec send_call(?CODEC:buffer(), options(), woody_state:st()) ->
|
||||
{ok, binary()} | {error, {system, _}}.
|
||||
send_call(Buffer, Opts = #{url := Url}, WoodyState) ->
|
||||
|
||||
send_call(Buffer, #{url := Url} = Opts, WoodyState) ->
|
||||
Context = woody_state:get_context(WoodyState),
|
||||
TransportOpts = get_transport_opts(Opts),
|
||||
ResolverOpts = get_resolver_opts(Opts),
|
||||
@ -177,11 +176,14 @@ set_timeouts(Options, Context) ->
|
||||
%% It is intentional, that application can override the timeout values
|
||||
%% calculated from the deadline (first option value in the list takes
|
||||
%% the precedence).
|
||||
maps:merge(#{
|
||||
connect_timeout => ConnectTimeout,
|
||||
send_timeout => SendTimeout,
|
||||
recv_timeout => Timeout
|
||||
}, Options)
|
||||
maps:merge(
|
||||
#{
|
||||
connect_timeout => ConnectTimeout,
|
||||
send_timeout => SendTimeout,
|
||||
recv_timeout => Timeout
|
||||
},
|
||||
Options
|
||||
)
|
||||
end.
|
||||
|
||||
calc_timeouts(Timeout) ->
|
||||
@ -189,30 +191,27 @@ calc_timeouts(Timeout) ->
|
||||
%% should take no more than 20% of the total request time
|
||||
%% and in any case no more, than DEFAULT_CONNECT_AND_SEND_TIMEOUT together.
|
||||
case max(0, Timeout) div 5 of
|
||||
T when (T*2) > ?DEFAULT_CONNECT_AND_SEND_TIMEOUT ->
|
||||
T when (T * 2) > ?DEFAULT_CONNECT_AND_SEND_TIMEOUT ->
|
||||
?DEFAULT_CONNECT_AND_SEND_TIMEOUT;
|
||||
T ->
|
||||
T
|
||||
end.
|
||||
|
||||
-spec make_woody_headers(woody_context:ctx()) ->
|
||||
http_headers().
|
||||
-spec make_woody_headers(woody_context:ctx()) -> http_headers().
|
||||
make_woody_headers(Context) ->
|
||||
add_optional_headers(Context, [
|
||||
{<<"content-type">> , ?CONTENT_TYPE_THRIFT},
|
||||
{<<"accept">> , ?CONTENT_TYPE_THRIFT},
|
||||
{?HEADER_RPC_ROOT_ID , woody_context:get_rpc_id(trace_id , Context)},
|
||||
{?HEADER_RPC_ID , woody_context:get_rpc_id(span_id , Context)},
|
||||
{?HEADER_RPC_PARENT_ID , woody_context:get_rpc_id(parent_id, Context)}
|
||||
{<<"content-type">>, ?CONTENT_TYPE_THRIFT},
|
||||
{<<"accept">>, ?CONTENT_TYPE_THRIFT},
|
||||
{?HEADER_RPC_ROOT_ID, woody_context:get_rpc_id(trace_id, Context)},
|
||||
{?HEADER_RPC_ID, woody_context:get_rpc_id(span_id, Context)},
|
||||
{?HEADER_RPC_PARENT_ID, woody_context:get_rpc_id(parent_id, Context)}
|
||||
]).
|
||||
|
||||
-spec add_optional_headers(woody_context:ctx(), http_headers()) ->
|
||||
http_headers().
|
||||
-spec add_optional_headers(woody_context:ctx(), http_headers()) -> http_headers().
|
||||
add_optional_headers(Context, Headers) ->
|
||||
add_deadline_header(Context, add_metadata_headers(Context, Headers)).
|
||||
|
||||
-spec add_metadata_headers(woody_context:ctx(), http_headers()) ->
|
||||
http_headers().
|
||||
-spec add_metadata_headers(woody_context:ctx(), http_headers()) -> http_headers().
|
||||
add_metadata_headers(Context, Headers) ->
|
||||
maps:fold(fun add_metadata_header/3, Headers, woody_context:get_meta(Context)).
|
||||
|
||||
@ -232,13 +231,13 @@ do_add_deadline_header(Deadline, Headers) ->
|
||||
add_host_header(#hackney_url{netloc = Netloc}, Headers) ->
|
||||
[{<<"Host">>, Netloc} | Headers].
|
||||
|
||||
-spec handle_response(_, woody_state:st()) ->
|
||||
{ok, woody:http_body()} | {error, {system, woody_error:system_error()}}.
|
||||
-spec handle_response(_, woody_state:st()) -> {ok, woody:http_body()} | {error, {system, woody_error:system_error()}}.
|
||||
handle_response({ok, 200, Headers, Ref}, WoodyState) ->
|
||||
Meta = case check_error_reason(Headers, 200, WoodyState) of
|
||||
<<>> -> #{};
|
||||
Reason -> #{reason => Reason}
|
||||
end,
|
||||
Meta =
|
||||
case check_error_reason(Headers, 200, WoodyState) of
|
||||
<<>> -> #{};
|
||||
Reason -> #{reason => Reason}
|
||||
end,
|
||||
_ = log_event(?EV_CLIENT_RECEIVE, WoodyState, Meta#{status => ok, code => 200}),
|
||||
get_body(hackney:body(Ref), WoodyState);
|
||||
handle_response({ok, Code, Headers, Ref}, WoodyState) ->
|
||||
@ -257,25 +256,25 @@ handle_response({error, {closed, _}}, WoodyState) ->
|
||||
_ = log_event(?EV_CLIENT_RECEIVE, WoodyState, #{status => error, reason => Reason}),
|
||||
{error, {system, {external, result_unknown, Reason}}};
|
||||
handle_response({error, Reason}, WoodyState) when
|
||||
Reason =:= timeout ;
|
||||
Reason =:= econnaborted ;
|
||||
Reason =:= enetreset ;
|
||||
Reason =:= econnreset ;
|
||||
Reason =:= eshutdown ;
|
||||
Reason =:= etimedout ;
|
||||
Reason =:= timeout;
|
||||
Reason =:= econnaborted;
|
||||
Reason =:= enetreset;
|
||||
Reason =:= econnreset;
|
||||
Reason =:= eshutdown;
|
||||
Reason =:= etimedout;
|
||||
Reason =:= closed
|
||||
->
|
||||
BinReason = woody_util:to_binary(Reason),
|
||||
_ = log_event(?EV_CLIENT_RECEIVE, WoodyState, #{status => error, reason => BinReason}),
|
||||
{error, {system, {external, result_unknown, BinReason}}};
|
||||
handle_response({error, Reason}, WoodyState) when
|
||||
Reason =:= econnrefused ;
|
||||
Reason =:= connect_timeout ;
|
||||
Reason =:= checkout_timeout;
|
||||
Reason =:= enetdown ;
|
||||
Reason =:= enetunreach ;
|
||||
Reason =:= ehostunreach ;
|
||||
Reason =:= eacces ;
|
||||
Reason =:= econnrefused;
|
||||
Reason =:= connect_timeout;
|
||||
Reason =:= checkout_timeout;
|
||||
Reason =:= enetdown;
|
||||
Reason =:= enetunreach;
|
||||
Reason =:= ehostunreach;
|
||||
Reason =:= eacces;
|
||||
element(1, Reason) =:= resolve_failed
|
||||
->
|
||||
BinReason = woody_error:format_details(Reason),
|
||||
@ -288,13 +287,11 @@ handle_response({error, Reason}, WoodyState) ->
|
||||
_ = log_event(?EV_CLIENT_RECEIVE, WoodyState, #{status => error, reason => Details}),
|
||||
{error, {system, {internal, result_unexpected, Details}}}.
|
||||
|
||||
-spec check_error_reason(http_headers(), woody:http_code(), woody_state:st()) ->
|
||||
woody_error:details().
|
||||
-spec check_error_reason(http_headers(), woody:http_code(), woody_state:st()) -> woody_error:details().
|
||||
check_error_reason(Headers, Code, WoodyState) ->
|
||||
do_check_error_reason(get_header_value(?HEADER_E_REASON, Headers), Code, WoodyState).
|
||||
|
||||
-spec do_check_error_reason(header_parse_value(), woody:http_code(), woody_state:st()) ->
|
||||
woody_error:details().
|
||||
-spec do_check_error_reason(header_parse_value(), woody:http_code(), woody_state:st()) -> woody_error:details().
|
||||
do_check_error_reason(none, 200, _WoodyState) ->
|
||||
<<>>;
|
||||
do_check_error_reason(none, Code, WoodyState) ->
|
||||
@ -310,8 +307,7 @@ check_error_headers(502, Headers, WoodyState) ->
|
||||
check_error_headers(Code, Headers, WoodyState) ->
|
||||
{get_error_class(Code), check_error_reason(Headers, Code, WoodyState)}.
|
||||
|
||||
-spec get_error_class(woody:http_code()) ->
|
||||
woody_error:class().
|
||||
-spec get_error_class(woody:http_code()) -> woody_error:class().
|
||||
get_error_class(503) ->
|
||||
resource_unavailable;
|
||||
get_error_class(504) ->
|
||||
@ -342,8 +338,7 @@ check_502_error_class(Bad, _, WoodyState) ->
|
||||
),
|
||||
{result_unexpected, ?BAD_RESP_HEADER}.
|
||||
|
||||
-spec get_error_class_header_value(http_headers()) ->
|
||||
header_parse_value().
|
||||
-spec get_error_class_header_value(http_headers()) -> header_parse_value().
|
||||
get_error_class_header_value(Headers) ->
|
||||
case get_header_value(?HEADER_E_CLASS, Headers) of
|
||||
None when None =:= none orelse None =:= multiple ->
|
||||
@ -352,12 +347,11 @@ get_error_class_header_value(Headers) ->
|
||||
genlib_string:to_lower(Value)
|
||||
end.
|
||||
|
||||
-spec get_header_value(woody:http_header_name(), http_headers()) ->
|
||||
header_parse_value().
|
||||
-spec get_header_value(woody:http_header_name(), http_headers()) -> header_parse_value().
|
||||
get_header_value(Name, Headers) ->
|
||||
case lists:dropwhile(fun ({K, _}) -> Name /= genlib_string:to_lower(K) end, Headers) of
|
||||
case lists:dropwhile(fun({K, _}) -> Name /= genlib_string:to_lower(K) end, Headers) of
|
||||
[{_, Value} | _] -> Value;
|
||||
[] -> none
|
||||
[] -> none
|
||||
end.
|
||||
|
||||
-spec get_body({ok, woody:http_body()} | {error, atom()}, woody_state:st()) ->
|
||||
@ -375,8 +369,7 @@ log_result({error, {business, ThriftExcept}}, WoodyState) ->
|
||||
log_result({error, Result}, WoodyState) ->
|
||||
log_event(?EV_SERVICE_RESULT, WoodyState, #{status => error, class => system, result => Result}).
|
||||
|
||||
-spec map_result(woody_client:result() | {error, _ThriftError}) ->
|
||||
woody_client:result().
|
||||
-spec map_result(woody_client:result() | {error, _ThriftError}) -> woody_client:result().
|
||||
map_result(Res = {ok, _}) ->
|
||||
Res;
|
||||
map_result(Res = {error, {Type, _}}) when Type =:= business orelse Type =:= system ->
|
||||
|
@ -33,54 +33,46 @@
|
||||
-export_type([meta_key/0]).
|
||||
|
||||
-type ctx() :: #{
|
||||
rpc_id := woody:rpc_id(),
|
||||
deadline := woody:deadline(),
|
||||
meta => meta(),
|
||||
cert => woody:cert()
|
||||
rpc_id := woody:rpc_id(),
|
||||
deadline := woody:deadline(),
|
||||
meta => meta(),
|
||||
cert => woody:cert()
|
||||
}.
|
||||
|
||||
-type meta_value() :: binary().
|
||||
-type meta_key() :: binary().
|
||||
-type meta() :: #{meta_key() => meta_value()}.
|
||||
-type meta_key() :: binary().
|
||||
-type meta() :: #{meta_key() => meta_value()}.
|
||||
|
||||
-define(ROOT_REQ_PARENT_ID, <<"undefined">>).
|
||||
|
||||
|
||||
%%
|
||||
%% API
|
||||
%%
|
||||
-spec new() ->
|
||||
ctx().
|
||||
-spec new() -> ctx().
|
||||
new() ->
|
||||
new(new_req_id()).
|
||||
|
||||
-spec new(woody:req_id() | woody:rpc_id()) ->
|
||||
ctx().
|
||||
-spec new(woody:req_id() | woody:rpc_id()) -> ctx().
|
||||
new(Id) ->
|
||||
new(Id, undefined).
|
||||
|
||||
-spec new(woody:rpc_id() | woody:trace_id(), meta() | undefined) ->
|
||||
ctx().
|
||||
-spec new(woody:rpc_id() | woody:trace_id(), meta() | undefined) -> ctx().
|
||||
new(Id, Meta) ->
|
||||
new(Id, Meta, undefined).
|
||||
|
||||
-spec new(woody:rpc_id() | woody:trace_id(), meta() | undefined, woody:deadline()) ->
|
||||
ctx().
|
||||
-spec new(woody:rpc_id() | woody:trace_id(), meta() | undefined, woody:deadline()) -> ctx().
|
||||
new(Id, Meta, Deadline) ->
|
||||
make_ctx(expand_rpc_id(Id), Meta, Deadline).
|
||||
|
||||
-spec new_child(ctx()) ->
|
||||
ctx().
|
||||
-spec new_child(ctx()) -> ctx().
|
||||
new_child(Context = #{rpc_id := #{trace_id := TraceId, span_id := SpanId}}) ->
|
||||
Context#{rpc_id => new_rpc_id(SpanId, TraceId, new_req_id())}.
|
||||
|
||||
-spec add_meta(ctx(), meta()) ->
|
||||
ctx().
|
||||
-spec add_meta(ctx(), meta()) -> ctx().
|
||||
add_meta(Context, Meta) ->
|
||||
Context#{meta => append_meta(get_meta(Context), Meta)}.
|
||||
|
||||
-spec get_meta(ctx()) ->
|
||||
meta().
|
||||
-spec get_meta(ctx()) -> meta().
|
||||
get_meta(Context) ->
|
||||
case maps:get(meta, Context, undefined) of
|
||||
undefined ->
|
||||
@ -89,45 +81,37 @@ get_meta(Context) ->
|
||||
Meta
|
||||
end.
|
||||
|
||||
-spec get_meta(meta_key(), ctx()) ->
|
||||
binary() | undefined.
|
||||
-spec get_meta(meta_key(), ctx()) -> binary() | undefined.
|
||||
get_meta(MetaKey, Context) ->
|
||||
maps:get(MetaKey, maps:get(meta, Context, #{}), undefined).
|
||||
|
||||
|
||||
-spec get_rpc_id(ctx()) ->
|
||||
woody:rpc_id() | no_return().
|
||||
-spec get_rpc_id(ctx()) -> woody:rpc_id() | no_return().
|
||||
get_rpc_id(#{rpc_id := RpcId}) ->
|
||||
RpcId;
|
||||
get_rpc_id( _) ->
|
||||
get_rpc_id(_) ->
|
||||
error(badarg).
|
||||
|
||||
-spec get_rpc_id(woody:dapper_id(), ctx()) ->
|
||||
woody:req_id() | undefined | no_return().
|
||||
-spec get_rpc_id(woody:dapper_id(), ctx()) -> woody:req_id() | undefined | no_return().
|
||||
get_rpc_id(Key, Context) ->
|
||||
maps:get(Key, get_rpc_id(Context), undefined).
|
||||
|
||||
-spec new_rpc_id(woody:span_id()) ->
|
||||
woody:rpc_id().
|
||||
-spec new_rpc_id(woody:span_id()) -> woody:rpc_id().
|
||||
new_rpc_id(SpanId) ->
|
||||
new_rpc_id(?ROOT_REQ_PARENT_ID, new_req_id(), SpanId).
|
||||
|
||||
-spec new_rpc_id(woody:parent_id(), woody:trace_id(), woody:span_id()) ->
|
||||
woody:rpc_id().
|
||||
-spec new_rpc_id(woody:parent_id(), woody:trace_id(), woody:span_id()) -> woody:rpc_id().
|
||||
new_rpc_id(ParentId, TraceId, SpanId) ->
|
||||
#{
|
||||
parent_id => ParentId,
|
||||
trace_id => TraceId,
|
||||
span_id => SpanId
|
||||
trace_id => TraceId,
|
||||
span_id => SpanId
|
||||
}.
|
||||
|
||||
-spec new_req_id() ->
|
||||
woody:req_id().
|
||||
-spec new_req_id() -> woody:req_id().
|
||||
new_req_id() ->
|
||||
genlib:to_binary(new_unique_int()).
|
||||
|
||||
-spec new_unique_int() ->
|
||||
pos_integer().
|
||||
-spec new_unique_int() -> pos_integer().
|
||||
new_unique_int() ->
|
||||
try snowflake:new(?MODULE) of
|
||||
<<Id:64>> ->
|
||||
@ -141,45 +125,38 @@ new_unique_int() ->
|
||||
woody_error:raise(system, {internal, resource_unavailable, BinReason})
|
||||
end.
|
||||
|
||||
-spec set_deadline(woody:deadline(), ctx()) ->
|
||||
ctx().
|
||||
-spec set_deadline(woody:deadline(), ctx()) -> ctx().
|
||||
set_deadline(Deadline, Context) ->
|
||||
Context#{deadline => Deadline}.
|
||||
|
||||
-spec get_deadline(ctx()) ->
|
||||
woody:deadline().
|
||||
-spec get_deadline(ctx()) -> woody:deadline().
|
||||
get_deadline(#{deadline := Deadline}) ->
|
||||
Deadline.
|
||||
|
||||
-spec set_cert(woody:cert(), ctx()) ->
|
||||
ctx().
|
||||
-spec set_cert(woody:cert(), ctx()) -> ctx().
|
||||
set_cert(Cert, Context) ->
|
||||
Context#{cert => Cert}.
|
||||
|
||||
-spec get_cert(ctx()) ->
|
||||
woody:cert().
|
||||
-spec get_cert(ctx()) -> woody:cert().
|
||||
get_cert(#{cert := Cert}) ->
|
||||
Cert;
|
||||
get_cert(_) ->
|
||||
undefined.
|
||||
|
||||
-spec get_common_name(ctx()) ->
|
||||
woody:common_name() | undefined.
|
||||
-spec get_common_name(ctx()) -> woody:common_name() | undefined.
|
||||
get_common_name(Ctx) ->
|
||||
woody_cert:get_common_name(get_cert(Ctx)).
|
||||
|
||||
%%
|
||||
%% Internal functions
|
||||
%%
|
||||
-spec expand_rpc_id(woody:rpc_id() | woody:trace_id()) ->
|
||||
woody:rpc_id().
|
||||
-spec expand_rpc_id(woody:rpc_id() | woody:trace_id()) -> woody:rpc_id().
|
||||
expand_rpc_id(RpcId = #{}) ->
|
||||
RpcId;
|
||||
expand_rpc_id(TraceId) ->
|
||||
new_rpc_id(TraceId).
|
||||
|
||||
-spec make_ctx(woody:rpc_id(), meta() | undefined, woody:deadline()) ->
|
||||
ctx() | no_return().
|
||||
-spec make_ctx(woody:rpc_id(), meta() | undefined, woody:deadline()) -> ctx() | no_return().
|
||||
make_ctx(RpcId = #{span_id := _, parent_id := _, trace_id := _}, Meta, Deadline) ->
|
||||
_ = genlib_map:foreach(fun check_req_id_limit/2, RpcId),
|
||||
init_meta(#{rpc_id => RpcId, deadline => Deadline}, Meta);
|
||||
@ -197,8 +174,7 @@ init_meta(Context, undefined) ->
|
||||
init_meta(Context, Meta) ->
|
||||
Context#{meta => Meta}.
|
||||
|
||||
-spec append_meta(meta(), map()) ->
|
||||
meta() | no_return().
|
||||
-spec append_meta(meta(), map()) -> meta() | no_return().
|
||||
append_meta(MetaBase, MetaNew) ->
|
||||
Meta = maps:merge(MetaNew, MetaBase),
|
||||
SizeSum = maps:size(MetaBase) + maps:size(MetaNew),
|
||||
|
@ -15,22 +15,22 @@
|
||||
|
||||
%% Types
|
||||
-type millisec() :: 0..1000.
|
||||
-type deadline() :: {calendar:datetime(), millisec()} | undefined. %% deadline may be not set for a request,
|
||||
%% that's why 'undefined' is here as well.
|
||||
%% deadline may be not set for a request,
|
||||
-type deadline() :: {calendar:datetime(), millisec()} | undefined.
|
||||
|
||||
%% that's why 'undefined' is here as well.
|
||||
-export_type([deadline/0, millisec/0]).
|
||||
|
||||
%%
|
||||
%% API
|
||||
%%
|
||||
-spec is_reached(deadline()) ->
|
||||
boolean().
|
||||
-spec is_reached(deadline()) -> boolean().
|
||||
is_reached(undefined) ->
|
||||
false;
|
||||
is_reached(Deadline) ->
|
||||
unow() >= to_unixtime_ms(Deadline).
|
||||
|
||||
-spec to_timeout(deadline()) ->
|
||||
timeout().
|
||||
-spec to_timeout(deadline()) -> timeout().
|
||||
to_timeout(undefined) ->
|
||||
infinity;
|
||||
to_timeout(Deadline) ->
|
||||
@ -41,16 +41,14 @@ to_timeout(Deadline) ->
|
||||
erlang:error(deadline_reached, [Deadline])
|
||||
end.
|
||||
|
||||
-spec from_timeout(timeout()) ->
|
||||
deadline().
|
||||
-spec from_timeout(timeout()) -> deadline().
|
||||
from_timeout(infinity) ->
|
||||
undefined;
|
||||
from_timeout(TimeoutMillisec) ->
|
||||
DeadlineMillisec = unow() + TimeoutMillisec,
|
||||
from_unixtime_ms(DeadlineMillisec).
|
||||
|
||||
-spec to_binary(deadline()) ->
|
||||
binary().
|
||||
-spec to_binary(deadline()) -> binary().
|
||||
to_binary(Deadline = undefined) ->
|
||||
erlang:error(bad_deadline, [Deadline]);
|
||||
to_binary(Deadline) ->
|
||||
@ -63,8 +61,7 @@ to_binary(Deadline) ->
|
||||
erlang:error({bad_deadline, {Error, Stacktrace}}, [Deadline])
|
||||
end.
|
||||
|
||||
-spec from_binary(binary()) ->
|
||||
deadline().
|
||||
-spec from_binary(binary()) -> deadline().
|
||||
from_binary(Bin) ->
|
||||
ok = assert_is_utc(Bin),
|
||||
Str = erlang:binary_to_list(Bin),
|
||||
@ -89,8 +86,7 @@ from_unixtime_ms(DeadlineMillisec) ->
|
||||
%% Internal functions
|
||||
%%
|
||||
|
||||
-spec assert_is_utc(binary()) ->
|
||||
ok | no_return().
|
||||
-spec assert_is_utc(binary()) -> ok | no_return().
|
||||
assert_is_utc(Bin) ->
|
||||
Size0 = erlang:byte_size(Bin),
|
||||
Size1 = Size0 - 1,
|
||||
@ -106,8 +102,7 @@ assert_is_utc(Bin) ->
|
||||
erlang:error({bad_deadline, not_utc}, [Bin])
|
||||
end.
|
||||
|
||||
-spec unow() ->
|
||||
millisec().
|
||||
-spec unow() -> millisec().
|
||||
unow() ->
|
||||
% We must use OS time for communications with external systems
|
||||
% erlang:system_time/1 may have a various difference with global time to prevent time warp.
|
||||
|
@ -2,44 +2,44 @@
|
||||
-define(_woody_defs_included, yeah).
|
||||
|
||||
%% HTTP headers
|
||||
-define(CONTENT_TYPE_THRIFT , <<"application/x-thrift">>).
|
||||
-define(CONTENT_TYPE_THRIFT, <<"application/x-thrift">>).
|
||||
|
||||
%% Woody-specific HTTP headers
|
||||
-define(HEADER_PREFIX , <<"woody.">>).
|
||||
-define(HEADER_RPC_ID , <<?HEADER_PREFIX/binary, "span-id">>).
|
||||
-define(HEADER_RPC_PARENT_ID , <<?HEADER_PREFIX/binary, "parent-id">>).
|
||||
-define(HEADER_RPC_ROOT_ID , <<?HEADER_PREFIX/binary, "trace-id">>).
|
||||
-define(HEADER_E_CLASS , <<?HEADER_PREFIX/binary, "error-class">>).
|
||||
-define(HEADER_E_REASON , <<?HEADER_PREFIX/binary, "error-reason">>).
|
||||
-define(HEADER_DEADLINE , <<?HEADER_PREFIX/binary, "deadline">>).
|
||||
-define(HEADER_META_PREFIX , <<?HEADER_PREFIX/binary, "meta.">>).
|
||||
-define(HEADER_META_RE , <<"woody\\.meta\\.">>).
|
||||
-define(HEADER_PREFIX, <<"woody.">>).
|
||||
-define(HEADER_RPC_ID, <<?HEADER_PREFIX/binary, "span-id">>).
|
||||
-define(HEADER_RPC_PARENT_ID, <<?HEADER_PREFIX/binary, "parent-id">>).
|
||||
-define(HEADER_RPC_ROOT_ID, <<?HEADER_PREFIX/binary, "trace-id">>).
|
||||
-define(HEADER_E_CLASS, <<?HEADER_PREFIX/binary, "error-class">>).
|
||||
-define(HEADER_E_REASON, <<?HEADER_PREFIX/binary, "error-reason">>).
|
||||
-define(HEADER_DEADLINE, <<?HEADER_PREFIX/binary, "deadline">>).
|
||||
-define(HEADER_META_PREFIX, <<?HEADER_PREFIX/binary, "meta.">>).
|
||||
-define(HEADER_META_RE, <<"woody\\.meta\\.">>).
|
||||
|
||||
%% Events
|
||||
-define(EV_CALL_SERVICE , 'call service').
|
||||
-define(EV_SERVICE_RESULT , 'service result').
|
||||
-define(EV_CALL_SERVICE, 'call service').
|
||||
-define(EV_SERVICE_RESULT, 'service result').
|
||||
|
||||
-define(EV_CLIENT_BEGIN , 'client begin').
|
||||
-define(EV_CLIENT_SEND , 'client send').
|
||||
-define(EV_CLIENT_RESOLVE_BEGIN , 'client resolve begin').
|
||||
-define(EV_CLIENT_RESOLVE_RESULT , 'client resolve result').
|
||||
-define(EV_CLIENT_RECEIVE , 'client receive').
|
||||
-define(EV_CLIENT_END , 'client end').
|
||||
-define(EV_CLIENT_BEGIN, 'client begin').
|
||||
-define(EV_CLIENT_SEND, 'client send').
|
||||
-define(EV_CLIENT_RESOLVE_BEGIN, 'client resolve begin').
|
||||
-define(EV_CLIENT_RESOLVE_RESULT, 'client resolve result').
|
||||
-define(EV_CLIENT_RECEIVE, 'client receive').
|
||||
-define(EV_CLIENT_END, 'client end').
|
||||
|
||||
-define(EV_CLIENT_CACHE_BEGIN , 'client cache begin').
|
||||
-define(EV_CLIENT_CACHE_HIT , 'client cache hit').
|
||||
-define(EV_CLIENT_CACHE_MISS , 'client cache miss').
|
||||
-define(EV_CLIENT_CACHE_UPDATE , 'client cache update').
|
||||
-define(EV_CLIENT_CACHE_RESULT , 'client cache result').
|
||||
-define(EV_CLIENT_CACHE_END , 'client cache end').
|
||||
-define(EV_CLIENT_CACHE_BEGIN, 'client cache begin').
|
||||
-define(EV_CLIENT_CACHE_HIT, 'client cache hit').
|
||||
-define(EV_CLIENT_CACHE_MISS, 'client cache miss').
|
||||
-define(EV_CLIENT_CACHE_UPDATE, 'client cache update').
|
||||
-define(EV_CLIENT_CACHE_RESULT, 'client cache result').
|
||||
-define(EV_CLIENT_CACHE_END, 'client cache end').
|
||||
|
||||
-define(EV_SERVER_RECEIVE , 'server receive').
|
||||
-define(EV_SERVER_SEND , 'server send').
|
||||
-define(EV_SERVER_RECEIVE, 'server receive').
|
||||
-define(EV_SERVER_SEND, 'server send').
|
||||
|
||||
-define(EV_INVOKE_SERVICE_HANDLER , 'invoke service handler').
|
||||
-define(EV_SERVICE_HANDLER_RESULT , 'service handler result').
|
||||
-define(EV_INVOKE_SERVICE_HANDLER, 'invoke service handler').
|
||||
-define(EV_SERVICE_HANDLER_RESULT, 'service handler result').
|
||||
|
||||
-define(EV_INTERNAL_ERROR , 'internal error').
|
||||
-define(EV_TRACE , 'trace event').
|
||||
-define(EV_INTERNAL_ERROR, 'internal error').
|
||||
-define(EV_TRACE, 'trace event').
|
||||
|
||||
-endif.
|
||||
|
@ -14,17 +14,16 @@
|
||||
|
||||
-type type() :: business | system.
|
||||
-type error() ::
|
||||
{business , business_error()} |
|
||||
{system , system_error ()}.
|
||||
{business, business_error()}
|
||||
| {system, system_error()}.
|
||||
|
||||
-type business_error() :: _Error.
|
||||
-type system_error () :: {source(), class(), details()}.
|
||||
-type system_error() :: {source(), class(), details()}.
|
||||
|
||||
-type source () :: internal | external.
|
||||
-type class () :: resource_unavailable | result_unexpected | result_unknown.
|
||||
-type source() :: internal | external.
|
||||
-type class() :: resource_unavailable | result_unexpected | result_unknown.
|
||||
-type details() :: binary().
|
||||
|
||||
|
||||
-type erlang_except() :: throw | error | exit.
|
||||
-type stack() :: list().
|
||||
|
||||
@ -33,15 +32,12 @@
|
||||
%%
|
||||
%% API
|
||||
%%
|
||||
-spec raise(type(), business_error() | system_error()) ->
|
||||
no_return().
|
||||
-spec raise(type(), business_error() | system_error()) -> no_return().
|
||||
raise(business, Except) ->
|
||||
erlang:throw(Except);
|
||||
raise(system, {Source, Class, Details}) ->
|
||||
erlang:error({woody_error, {Source, Class, Details}}).
|
||||
|
||||
-spec format_details(term()) ->
|
||||
details().
|
||||
-spec format_details(term()) -> details().
|
||||
format_details(Error) ->
|
||||
genlib:to_binary(io_lib:format("~9999p", [Error])).
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -8,6 +8,7 @@
|
||||
-type options() :: #{
|
||||
formatter_opts => woody_event_formatter:opts()
|
||||
}.
|
||||
|
||||
-export_type([options/0]).
|
||||
|
||||
%%
|
||||
@ -16,8 +17,8 @@
|
||||
-spec handle_event(Event, RpcId, Meta, Opts) -> ok when
|
||||
Event :: woody_event_handler:event(),
|
||||
RpcId :: woody:rpc_id() | undefined,
|
||||
Meta :: woody_event_handler:event_meta(),
|
||||
Opts :: options().
|
||||
Meta :: woody_event_handler:event_meta(),
|
||||
Opts :: options().
|
||||
handle_event(Event, RpcId, Meta, Opts) ->
|
||||
EHOpts = get_event_handler_opts(Opts),
|
||||
{Level, {Format, Msg}} = woody_event_handler:format_event(Event, Meta, RpcId, EHOpts),
|
||||
@ -25,12 +26,12 @@ handle_event(Event, RpcId, Meta, Opts) ->
|
||||
_ = error_logger:Function(Format, Msg),
|
||||
ok.
|
||||
|
||||
get_logger_function(Level) when Level =:= debug ; Level =:= info ->
|
||||
get_logger_function(Level) when Level =:= debug; Level =:= info ->
|
||||
info_msg;
|
||||
get_logger_function(warning) ->
|
||||
warning_msg;
|
||||
get_logger_function(error) ->
|
||||
get_logger_function(error) ->
|
||||
error_msg.
|
||||
|
||||
get_event_handler_opts(Opts) ->
|
||||
Opts.
|
||||
Opts.
|
||||
|
@ -24,26 +24,24 @@
|
||||
-export([worker_start_link/3]).
|
||||
-export([worker_init/4]).
|
||||
|
||||
-type deadline() :: woody_deadline:deadline().
|
||||
-type deadline() :: woody_deadline:deadline().
|
||||
-type task(Result) :: fun((deadline()) -> Result).
|
||||
-type id() :: _.
|
||||
-type exception() :: {atom(), term(), list()}.
|
||||
-type id() :: _.
|
||||
-type exception() :: {atom(), term(), list()}.
|
||||
|
||||
%%
|
||||
%% API
|
||||
%%
|
||||
-spec child_spec(atom(), genlib_gen:reg_name()) ->
|
||||
supervisor:child_spec().
|
||||
-spec child_spec(atom(), genlib_gen:reg_name()) -> supervisor:child_spec().
|
||||
child_spec(ChildID, RegName) ->
|
||||
#{
|
||||
id => ChildID,
|
||||
start => {?MODULE, start_link, [RegName]},
|
||||
restart => permanent,
|
||||
type => supervisor
|
||||
id => ChildID,
|
||||
start => {?MODULE, start_link, [RegName]},
|
||||
restart => permanent,
|
||||
type => supervisor
|
||||
}.
|
||||
|
||||
-spec start_link(genlib_gen:reg_name()) ->
|
||||
genlib_gen:start_ret().
|
||||
-spec start_link(genlib_gen:reg_name()) -> genlib_gen:start_ret().
|
||||
start_link(RegName) ->
|
||||
genlib_adhoc_supervisor:start_link(
|
||||
RegName,
|
||||
@ -51,31 +49,27 @@ start_link(RegName) ->
|
||||
[worker_child_spec(worker)]
|
||||
).
|
||||
|
||||
-spec do(genlib_gen:ref(), id(), task(Result), deadline()) ->
|
||||
Result.
|
||||
-spec do(genlib_gen:ref(), id(), task(Result), deadline()) -> Result.
|
||||
do(Ref, ID, Task, Deadline) ->
|
||||
do(Ref, ID, Task, Deadline, 10).
|
||||
|
||||
%%
|
||||
%% Internal API
|
||||
%%
|
||||
-spec worker_child_spec(atom()) ->
|
||||
supervisor:child_spec().
|
||||
-spec worker_child_spec(atom()) -> supervisor:child_spec().
|
||||
worker_child_spec(ChildID) ->
|
||||
#{
|
||||
id => ChildID,
|
||||
start => {?MODULE, worker_start_link, []},
|
||||
restart => temporary,
|
||||
type => worker
|
||||
id => ChildID,
|
||||
start => {?MODULE, worker_start_link, []},
|
||||
restart => temporary,
|
||||
type => worker
|
||||
}.
|
||||
|
||||
-spec worker_start_link(id(), task(_), deadline()) ->
|
||||
genlib_gen:start_ret().
|
||||
-spec worker_start_link(id(), task(_), deadline()) -> genlib_gen:start_ret().
|
||||
worker_start_link(ID, Task, Deadline) ->
|
||||
proc_lib:start_link(?MODULE, worker_init, [ID, self(), Task, Deadline], deadline_to_timeout(Deadline)).
|
||||
|
||||
-spec worker_init(id(), pid(), task(_), deadline()) ->
|
||||
ok.
|
||||
-spec worker_init(id(), pid(), task(_), deadline()) -> ok.
|
||||
worker_init(ID, Parent, Task, Deadline) ->
|
||||
Self = self(),
|
||||
case gproc:reg_or_locate({n, l, ID}) of
|
||||
@ -92,8 +86,7 @@ worker_init(ID, Parent, Task, Deadline) ->
|
||||
%%
|
||||
%% local
|
||||
%%
|
||||
-spec do(genlib_gen:ref(), id(), task(Result), deadline(), non_neg_integer()) ->
|
||||
Result.
|
||||
-spec do(genlib_gen:ref(), id(), task(Result), deadline(), non_neg_integer()) -> Result.
|
||||
do(Ref, ID, Task, Deadline, 0) ->
|
||||
erlang:error(fatal_retrying_error, [Ref, ID, Task, Deadline]);
|
||||
do(Ref, ID, Task, Deadline, Attempts) ->
|
||||
@ -108,7 +101,7 @@ do(Ref, ID, Task, Deadline, Attempts) ->
|
||||
% когда соединяются запросы с разными дедлайнами
|
||||
case woody_deadline:is_reached(Deadline) of
|
||||
false -> do(Ref, ID, Task, Deadline);
|
||||
true -> erlang:error(deadline_reached, [Ref, ID, Task, Deadline])
|
||||
true -> erlang:error(deadline_reached, [Ref, ID, Task, Deadline])
|
||||
end;
|
||||
{error, {exception, {Class, Error, Stacktrace}}} ->
|
||||
erlang:Class({Error, Stacktrace});
|
||||
@ -117,7 +110,7 @@ do(Ref, ID, Task, Deadline, Attempts) ->
|
||||
end.
|
||||
|
||||
-spec wait_for_result(pid(), deadline()) ->
|
||||
{ok, _Result}
|
||||
{ok, _Result}
|
||||
| {error, deadline_reached | race_detected | {worker_error, _Reason} | {exception, exception()}}.
|
||||
wait_for_result(Pid, Deadline) ->
|
||||
Timeout = deadline_to_timeout(Deadline),
|
||||
@ -131,8 +124,7 @@ wait_for_result(Pid, Deadline) ->
|
||||
erlang:demonitor(MRef, [flush]),
|
||||
{error, {exception, Exception}};
|
||||
%% произошла гонка
|
||||
{'DOWN', MRef, process, Pid, Reason}
|
||||
when Reason =:= normal; Reason =:= noproc ->
|
||||
{'DOWN', MRef, process, Pid, Reason} when Reason =:= normal; Reason =:= noproc ->
|
||||
{error, race_detected};
|
||||
%% упал воркер по таймауту
|
||||
{'DOWN', MRef, process, Pid, deadline_reached} ->
|
||||
@ -145,30 +137,25 @@ wait_for_result(Pid, Deadline) ->
|
||||
{error, deadline_reached}
|
||||
end.
|
||||
|
||||
-spec broadcast_result(_Result) ->
|
||||
ok.
|
||||
-spec broadcast_result(_Result) -> ok.
|
||||
broadcast_result(Result) ->
|
||||
receive
|
||||
{?MODULE, wait_for_result, MRef, Pid} ->
|
||||
Pid ! {?MODULE, broadcast_result, MRef, Result},
|
||||
broadcast_result(Result)
|
||||
after 0 ->
|
||||
ok
|
||||
after 0 -> ok
|
||||
end.
|
||||
|
||||
-spec sync_with_employer(timeout()) ->
|
||||
ok.
|
||||
-spec sync_with_employer(timeout()) -> ok.
|
||||
sync_with_employer(Timeout) ->
|
||||
receive
|
||||
{?MODULE, wait_for_result, MRef, Pid} ->
|
||||
self() ! {?MODULE, wait_for_result, MRef, Pid},
|
||||
ok
|
||||
after Timeout ->
|
||||
exit(deadline_reached)
|
||||
after Timeout -> exit(deadline_reached)
|
||||
end.
|
||||
|
||||
-spec deadline_to_timeout(deadline()) ->
|
||||
timeout().
|
||||
-spec deadline_to_timeout(deadline()) -> timeout().
|
||||
deadline_to_timeout(Deadline) ->
|
||||
try
|
||||
woody_deadline:to_timeout(Deadline)
|
||||
@ -177,16 +164,17 @@ deadline_to_timeout(Deadline) ->
|
||||
0
|
||||
end.
|
||||
|
||||
-spec set_worker_deadline_timer(deadline()) ->
|
||||
ok.
|
||||
-spec set_worker_deadline_timer(deadline()) -> ok.
|
||||
set_worker_deadline_timer(Deadline) ->
|
||||
case deadline_to_timeout(Deadline) of
|
||||
infinity -> ok;
|
||||
Timeout -> _ = genlib:unwrap(timer:exit_after(Timeout, self(), deadline_reached)), ok
|
||||
infinity ->
|
||||
ok;
|
||||
Timeout ->
|
||||
_ = genlib:unwrap(timer:exit_after(Timeout, self(), deadline_reached)),
|
||||
ok
|
||||
end.
|
||||
|
||||
-spec do_task_safe(task(Result), deadline()) ->
|
||||
{ok, Result} | {exception, exception()}.
|
||||
-spec do_task_safe(task(Result), deadline()) -> {ok, Result} | {exception, exception()}.
|
||||
do_task_safe(Task, Deadline) ->
|
||||
try
|
||||
{ok, Task(Deadline)}
|
||||
|
@ -1,4 +1,5 @@
|
||||
-module(woody_monitor_h).
|
||||
|
||||
-behaviour(cowboy_stream).
|
||||
|
||||
-include("woody_defs.hrl").
|
||||
@ -30,20 +31,20 @@ set_event(Event, Req) ->
|
||||
|
||||
%% callbacks
|
||||
|
||||
-spec init(cowboy_stream:streamid(), cowboy_req:req(), cowboy:opts())
|
||||
-> {cowboy_stream:commands(), state()}.
|
||||
-spec init(cowboy_stream:streamid(), cowboy_req:req(), cowboy:opts()) -> {cowboy_stream:commands(), state()}.
|
||||
init(StreamID, Req, Opts) ->
|
||||
{Commands0, Next} = cowboy_stream:init(StreamID, Req, Opts),
|
||||
{Commands0, #{next => Next, event_to_emit => ?EV_SERVER_RECEIVE}}.
|
||||
|
||||
-spec data(cowboy_stream:streamid(), cowboy_stream:fin(), cowboy_req:resp_body(), State)
|
||||
-> {cowboy_stream:commands(), State} when State::state().
|
||||
-spec data(cowboy_stream:streamid(), cowboy_stream:fin(), cowboy_req:resp_body(), State) ->
|
||||
{cowboy_stream:commands(), State}
|
||||
when
|
||||
State :: state().
|
||||
data(StreamID, IsFin, Data, #{next := Next0} = State) ->
|
||||
{Commands0, Next} = cowboy_stream:data(StreamID, IsFin, Data, Next0),
|
||||
{Commands0, State#{next => Next}}.
|
||||
|
||||
-spec info(cowboy_stream:streamid(), any(), State)
|
||||
-> {cowboy_stream:commands(), State} when State::state().
|
||||
-spec info(cowboy_stream:streamid(), any(), State) -> {cowboy_stream:commands(), State} when State :: state().
|
||||
info(StreamID, {woody_state, WoodyState} = Info, #{next := Next0} = State) ->
|
||||
{Commands, Next} = cowboy_stream:info(StreamID, Info, Next0),
|
||||
{Commands, State#{next => Next, woody_state => WoodyState}};
|
||||
@ -61,7 +62,8 @@ terminate(
|
||||
{socket_error, _, HumanReadable} = Reason,
|
||||
#{woody_state := WoodyState, next := Next, event_to_emit := Event = ?EV_SERVER_RECEIVE}
|
||||
) ->
|
||||
woody_event_handler:handle_event(Event,
|
||||
woody_event_handler:handle_event(
|
||||
Event,
|
||||
WoodyState,
|
||||
#{status => error, reason => woody_util:to_binary(HumanReadable)}
|
||||
),
|
||||
@ -71,7 +73,8 @@ terminate(
|
||||
{socket_error, _, HumanReadable} = Reason,
|
||||
#{woody_state := WoodyState, next := Next, event_to_emit := Event = ?EV_SERVICE_HANDLER_RESULT}
|
||||
) ->
|
||||
woody_event_handler:handle_event(Event,
|
||||
woody_event_handler:handle_event(
|
||||
Event,
|
||||
WoodyState,
|
||||
#{status => error, class => system, result => woody_util:to_binary(HumanReadable)}
|
||||
),
|
||||
@ -79,9 +82,14 @@ terminate(
|
||||
terminate(StreamID, Reason, #{next := Next}) ->
|
||||
cowboy_stream:terminate(StreamID, Reason, Next).
|
||||
|
||||
-spec early_error(cowboy_stream:streamid(), cowboy_stream:reason(),
|
||||
cowboy_stream:partial_req(), Resp, cowboy:opts()) -> Resp
|
||||
when Resp::cowboy_stream:resp_command().
|
||||
-spec early_error(
|
||||
cowboy_stream:streamid(),
|
||||
cowboy_stream:reason(),
|
||||
cowboy_stream:partial_req(),
|
||||
Resp,
|
||||
cowboy:opts()
|
||||
) -> Resp when
|
||||
Resp :: cowboy_stream:resp_command().
|
||||
early_error(StreamID, Reason, PartialReq, Resp, Opts) ->
|
||||
% We can't really do anything about it
|
||||
cowboy_stream:early_error(StreamID, Reason, PartialReq, Resp, Opts).
|
||||
|
@ -8,8 +8,8 @@
|
||||
-type url() :: woody:url().
|
||||
-type parsed_url() :: #hackney_url{}.
|
||||
|
||||
-type resolve_result() :: {Old::parsed_url(), New::parsed_url()}.
|
||||
-type resolve_error() :: einval | nxdomain | timeout | {unsupported_url_scheme, woody:url()}.
|
||||
-type resolve_result() :: {Old :: parsed_url(), New :: parsed_url()}.
|
||||
-type resolve_error() :: einval | nxdomain | timeout | {unsupported_url_scheme, woody:url()}.
|
||||
|
||||
-type options() :: #{
|
||||
ip_picker => ip_picker(),
|
||||
@ -18,8 +18,8 @@
|
||||
|
||||
-type ip_picker() :: {module(), atom()} | predefined_ip_picker().
|
||||
-type predefined_ip_picker() ::
|
||||
random |
|
||||
first.
|
||||
random
|
||||
| first.
|
||||
|
||||
-export_type([options/0]).
|
||||
-export_type([ip_picker/0]).
|
||||
@ -36,14 +36,14 @@
|
||||
%%
|
||||
|
||||
-spec resolve_url(url(), woody_state:st()) ->
|
||||
{ok, resolve_result()} |
|
||||
{error, resolve_error()}.
|
||||
{ok, resolve_result()}
|
||||
| {error, resolve_error()}.
|
||||
resolve_url(Url, WoodyState) ->
|
||||
resolve_url(Url, WoodyState, #{}).
|
||||
|
||||
-spec resolve_url(url(), woody_state:st(), options()) ->
|
||||
{ok, resolve_result()} |
|
||||
{error, resolve_error()}.
|
||||
{ok, resolve_result()}
|
||||
| {error, resolve_error()}.
|
||||
resolve_url(Url, WoodyState, Opts) when is_list(Url) ->
|
||||
resolve_url(unicode:characters_to_binary(Url), WoodyState, Opts);
|
||||
resolve_url(<<"https://", _Rest/binary>> = Url, WoodyState, Opts) ->
|
||||
@ -60,7 +60,8 @@ parse_url(Url) ->
|
||||
|
||||
resolve_parsed_url(ParsedUrl = #hackney_url{}, WoodyState, Opts) ->
|
||||
case inet:parse_address(ParsedUrl#hackney_url.host) of
|
||||
{ok, _} -> {ok, {ParsedUrl, ParsedUrl}}; % url host is already an ip, move on
|
||||
% url host is already an ip, move on
|
||||
{ok, _} -> {ok, {ParsedUrl, ParsedUrl}};
|
||||
{error, _} -> do_resolve_url(ParsedUrl, WoodyState, Opts)
|
||||
end.
|
||||
|
||||
@ -138,8 +139,7 @@ apply_ip_picker({M, F}, AddrList) ->
|
||||
get_ip_family(HostEnt) ->
|
||||
HostEnt#hostent.h_addrtype.
|
||||
|
||||
-spec get_ip_family_preference() ->
|
||||
[inet:address_family()].
|
||||
-spec get_ip_family_preference() -> [inet:address_family()].
|
||||
get_ip_family_preference() ->
|
||||
case inet_db:res_option(inet6) of
|
||||
true -> [inet6, inet];
|
||||
@ -149,4 +149,4 @@ get_ip_family_preference() ->
|
||||
%%
|
||||
|
||||
log_event(Event, WoodyState, ExtraMeta) ->
|
||||
woody_event_handler:handle_event(Event, WoodyState, ExtraMeta).
|
||||
woody_event_handler:handle_event(Event, WoodyState, ExtraMeta).
|
||||
|
@ -9,15 +9,16 @@
|
||||
|
||||
%% Types
|
||||
-type options() :: #{
|
||||
event_handler := woody:ev_handlers(),
|
||||
protocol => thrift,
|
||||
transport => http,
|
||||
event_handler := woody:ev_handlers(),
|
||||
protocol => thrift,
|
||||
transport => http,
|
||||
%% Set to override protocol handler module selection, useful for test purposes, rarely
|
||||
%% if ever needed otherwise.
|
||||
protocol_handler_override => module(),
|
||||
%% Implementation-specific options
|
||||
_ => _
|
||||
_ => _
|
||||
}.
|
||||
|
||||
-export_type([options/0]).
|
||||
|
||||
%% Behaviour definition
|
||||
@ -27,14 +28,12 @@
|
||||
%%
|
||||
%% API
|
||||
%%
|
||||
-spec child_spec(_Id, options()) ->
|
||||
supervisor:child_spec().
|
||||
-spec child_spec(_Id, options()) -> supervisor:child_spec().
|
||||
child_spec(Id, Options) ->
|
||||
ProtocolHandler = woody_util:get_protocol_handler(server, Options),
|
||||
ProtocolHandler:child_spec(Id, Options).
|
||||
|
||||
-spec get_addr(_Id, options()) ->
|
||||
{inet:ip_address(), inet:port_number()}.
|
||||
-spec get_addr(_Id, options()) -> {inet:ip_address(), inet:port_number()}.
|
||||
get_addr(Id, Options) ->
|
||||
ProtocolHandler = woody_util:get_protocol_handler(server, Options),
|
||||
ProtocolHandler:get_addr(Id).
|
||||
|
@ -1,8 +1,9 @@
|
||||
-module(woody_server_http_drainer).
|
||||
|
||||
-behaviour(gen_server).
|
||||
|
||||
-type options() :: #{
|
||||
shutdown := timeout(),
|
||||
shutdown := timeout(),
|
||||
ranch_ref := ranch:ref()
|
||||
}.
|
||||
|
||||
@ -16,9 +17,7 @@
|
||||
|
||||
%% API
|
||||
|
||||
-spec child_spec(options()) ->
|
||||
supervisor:child_spec().
|
||||
|
||||
-spec child_spec(options()) -> supervisor:child_spec().
|
||||
child_spec(Opts) ->
|
||||
RanchRef = maps:get(ranch_ref, Opts),
|
||||
Shutdown = get_shutdown_param(Opts),
|
||||
@ -28,36 +27,26 @@ child_spec(Opts) ->
|
||||
shutdown => Shutdown
|
||||
}.
|
||||
|
||||
-spec start_link(ranch:ref()) ->
|
||||
genlib_gen:start_ret().
|
||||
|
||||
-spec start_link(ranch:ref()) -> genlib_gen:start_ret().
|
||||
start_link(RanchRef) ->
|
||||
gen_server:start_link(?MODULE, RanchRef, []).
|
||||
|
||||
%% supervisor callbacks
|
||||
|
||||
-spec init(ranch:ref()) ->
|
||||
{ok, ranch:ref()}.
|
||||
|
||||
-spec init(ranch:ref()) -> {ok, ranch:ref()}.
|
||||
init(RanchRef) ->
|
||||
process_flag(trap_exit, true),
|
||||
{ok, RanchRef}.
|
||||
|
||||
-spec handle_call(_, _, ranch:ref()) ->
|
||||
{noreply, ranch:ref()}.
|
||||
|
||||
-spec handle_call(_, _, ranch:ref()) -> {noreply, ranch:ref()}.
|
||||
handle_call(_Call, _From, St) ->
|
||||
{noreply, St}.
|
||||
|
||||
-spec handle_cast(_, ranch:ref()) ->
|
||||
{noreply, ranch:ref()}.
|
||||
|
||||
-spec handle_cast(_, ranch:ref()) -> {noreply, ranch:ref()}.
|
||||
handle_cast(_Call, St) ->
|
||||
{noreply, St}.
|
||||
|
||||
-spec terminate(_, ranch:ref()) ->
|
||||
ok.
|
||||
|
||||
-spec terminate(_, ranch:ref()) -> ok.
|
||||
terminate(shutdown, Ref) ->
|
||||
ok = ranch:suspend_listener(Ref),
|
||||
ok = ranch:wait_for_connections(Ref, '==', 0);
|
||||
|
@ -8,45 +8,48 @@
|
||||
|
||||
-include_lib("thrift/include/thrift_constants.hrl").
|
||||
-include_lib("thrift/include/thrift_protocol.hrl").
|
||||
|
||||
-include("woody_defs.hrl").
|
||||
|
||||
%% Types
|
||||
-type client_error() :: {client, woody_error:details()}.
|
||||
|
||||
-export_type([client_error/0]).
|
||||
|
||||
-type state() :: #{
|
||||
woody_state := woody_state:st(),
|
||||
handler := woody:handler(woody:options()),
|
||||
service := woody:service(),
|
||||
th_proto := term(),
|
||||
function => woody:func(),
|
||||
args => woody:args(),
|
||||
th_seqid => term(),
|
||||
woody_state := woody_state:st(),
|
||||
handler := woody:handler(woody:options()),
|
||||
service := woody:service(),
|
||||
th_proto := term(),
|
||||
function => woody:func(),
|
||||
args => woody:args(),
|
||||
th_seqid => term(),
|
||||
th_param_type => term(),
|
||||
th_msg_type => thrift_msg_type(),
|
||||
th_msg_type => thrift_msg_type(),
|
||||
th_reply_type => thrift_reply_type()
|
||||
}.
|
||||
|
||||
-export_type([state/0]).
|
||||
|
||||
-type thrift_msg_type() ::
|
||||
?tMessageType_CALL |
|
||||
?tMessageType_ONEWAY |
|
||||
?tMessageType_REPLY |
|
||||
?tMessageType_EXCEPTION.
|
||||
?tMessageType_CALL
|
||||
| ?tMessageType_ONEWAY
|
||||
| ?tMessageType_REPLY
|
||||
| ?tMessageType_EXCEPTION.
|
||||
|
||||
-type thrift_reply_type() :: oneway_void | tuple().
|
||||
|
||||
-type reply_type() :: oneway_void | call.
|
||||
|
||||
-export_type([reply_type/0]).
|
||||
|
||||
-type builtin_thrift_error() :: bad_binary_protocol_version | no_binary_protocol_version | _OtherError.
|
||||
-type thrift_error() :: unknown_function | multiplexed_request | builtin_thrift_error().
|
||||
-type thrift_error() :: unknown_function | multiplexed_request | builtin_thrift_error().
|
||||
|
||||
%% Behaviour definition
|
||||
-callback handle_function(woody:func(), woody:args(), woody_context:ctx(), woody:options()) ->
|
||||
{ok, woody:result()} | no_return().
|
||||
|
||||
|
||||
%%
|
||||
%% API
|
||||
%%
|
||||
@ -54,22 +57,27 @@
|
||||
{ok, reply_type(), state()} | {error, client_error()}.
|
||||
init_handler(Request, {Service, Handler}, WoodyState) ->
|
||||
{ok, Transport} = thrift_membuffer_transport:new(Request),
|
||||
{ok, Proto} = thrift_binary_protocol:new(Transport,
|
||||
{ok, Proto} = thrift_binary_protocol:new(
|
||||
Transport,
|
||||
[{strict_read, true}, {strict_write, true}]
|
||||
),
|
||||
try handle_decode_result(decode_request(decode_message_begin(#{
|
||||
woody_state => WoodyState,
|
||||
service => Service,
|
||||
handler => Handler,
|
||||
th_proto => Proto
|
||||
})))
|
||||
try
|
||||
handle_decode_result(
|
||||
decode_request(
|
||||
decode_message_begin(#{
|
||||
woody_state => WoodyState,
|
||||
service => Service,
|
||||
handler => Handler,
|
||||
th_proto => Proto
|
||||
})
|
||||
)
|
||||
)
|
||||
catch
|
||||
throw:{woody_decode_error, Error} ->
|
||||
handle_decode_error(Error, WoodyState)
|
||||
end.
|
||||
|
||||
-spec invoke_handler(state()) ->
|
||||
{ok, binary()} | {error, woody_error:error()}.
|
||||
-spec invoke_handler(state()) -> {ok, binary()} | {error, woody_error:error()}.
|
||||
invoke_handler(State) ->
|
||||
{Result, #{th_proto := Proto, th_reply_type := MsgType}} = call_handler_safe(State),
|
||||
{_, {ok, Reply}} = thrift_protocol:close_transport(Proto),
|
||||
@ -87,18 +95,19 @@ handle_function(Handler, Function, Args, WoodyState) ->
|
||||
%%
|
||||
|
||||
%% Decode request
|
||||
-spec decode_message_begin(state()) ->
|
||||
state() | no_return().
|
||||
-spec decode_message_begin(state()) -> state() | no_return().
|
||||
decode_message_begin(State = #{th_proto := Proto}) ->
|
||||
case thrift_protocol:read(Proto, message_begin) of
|
||||
{Proto1, #protocol_message_begin{name = Function, type = Type, seqid = SeqId}} when
|
||||
Type =:= ?tMessageType_CALL orelse
|
||||
Type =:= ?tMessageType_ONEWAY
|
||||
Type =:= ?tMessageType_ONEWAY
|
||||
->
|
||||
match_reply_type(get_params_type(
|
||||
get_function_name(Function),
|
||||
State#{th_proto := Proto1, th_msg_type => Type, th_seqid => SeqId}
|
||||
));
|
||||
match_reply_type(
|
||||
get_params_type(
|
||||
get_function_name(Function),
|
||||
State#{th_proto := Proto1, th_msg_type => Type, th_seqid => SeqId}
|
||||
)
|
||||
);
|
||||
{_, {error, Reason}} ->
|
||||
throw_decode_error(Reason)
|
||||
end.
|
||||
@ -108,14 +117,14 @@ get_function_name(Function) ->
|
||||
[_ServiceName, _FunctionName] ->
|
||||
throw_decode_error(multiplexed_request);
|
||||
_ ->
|
||||
try list_to_existing_atom(Function)
|
||||
try
|
||||
list_to_existing_atom(Function)
|
||||
catch
|
||||
error:badarg -> throw_decode_error(unknown_function)
|
||||
end
|
||||
end.
|
||||
|
||||
-spec get_params_type(woody:func() , state()) ->
|
||||
state() | no_return().
|
||||
-spec get_params_type(woody:func(), state()) -> state() | no_return().
|
||||
get_params_type(Function, State = #{service := Service}) ->
|
||||
try get_function_info(Service, Function, params_type) of
|
||||
ParamsType ->
|
||||
@ -124,21 +133,24 @@ get_params_type(Function, State = #{service := Service}) ->
|
||||
error:badarg -> throw_decode_error(unknown_function)
|
||||
end.
|
||||
|
||||
-spec match_reply_type(state()) ->
|
||||
state() | no_return().
|
||||
match_reply_type(State = #{
|
||||
service := Service,
|
||||
function := Function,
|
||||
th_msg_type := ReqType,
|
||||
woody_state := WoodyState
|
||||
}) ->
|
||||
-spec match_reply_type(state()) -> state() | no_return().
|
||||
match_reply_type(
|
||||
State = #{
|
||||
service := Service,
|
||||
function := Function,
|
||||
th_msg_type := ReqType,
|
||||
woody_state := WoodyState
|
||||
}
|
||||
) ->
|
||||
ReplyType = get_function_info(Service, Function, reply_type),
|
||||
ok = match_reply_type(ReplyType, ReqType),
|
||||
State#{th_reply_type => ReplyType, woody_state := add_ev_meta(WoodyState, Service, Function, ReplyType)}.
|
||||
|
||||
match_reply_type(ReplyType, ReqType) when
|
||||
ReplyType =:= oneway_void , ReqType =/= ?tMessageType_ONEWAY orelse
|
||||
ReplyType =/= oneway_void , ReqType =:= ?tMessageType_ONEWAY
|
||||
ReplyType =:= oneway_void,
|
||||
ReqType =/= ?tMessageType_ONEWAY orelse
|
||||
ReplyType =/= oneway_void,
|
||||
ReqType =:= ?tMessageType_ONEWAY
|
||||
->
|
||||
throw_decode_error(request_reply_type_mismatch);
|
||||
match_reply_type(_, _) ->
|
||||
@ -148,16 +160,18 @@ add_ev_meta(WoodyState, Args) ->
|
||||
woody_state:add_ev_meta(#{args => Args}, WoodyState).
|
||||
|
||||
add_ev_meta(WoodyState, Service = {_, ServiceName}, Function, ReplyType) ->
|
||||
woody_state:add_ev_meta(#{
|
||||
service => ServiceName,
|
||||
service_schema => Service,
|
||||
function => Function,
|
||||
type => woody_util:get_rpc_reply_type(ReplyType),
|
||||
deadline => woody_context:get_deadline(woody_state:get_context(WoodyState))
|
||||
}, WoodyState).
|
||||
woody_state:add_ev_meta(
|
||||
#{
|
||||
service => ServiceName,
|
||||
service_schema => Service,
|
||||
function => Function,
|
||||
type => woody_util:get_rpc_reply_type(ReplyType),
|
||||
deadline => woody_context:get_deadline(woody_state:get_context(WoodyState))
|
||||
},
|
||||
WoodyState
|
||||
).
|
||||
|
||||
-spec decode_request(state()) ->
|
||||
state() | no_return().
|
||||
-spec decode_request(state()) -> state() | no_return().
|
||||
decode_request(State = #{th_proto := Proto, th_param_type := ParamsType, woody_state := WoodyState}) ->
|
||||
case thrift_protocol:read(Proto, ParamsType) of
|
||||
{Proto1, {ok, Args}} ->
|
||||
@ -166,24 +180,21 @@ decode_request(State = #{th_proto := Proto, th_param_type := ParamsType, woody_s
|
||||
throw_decode_error(Error)
|
||||
end.
|
||||
|
||||
-spec handle_decode_result(state()) ->
|
||||
{ok, reply_type(), state()}.
|
||||
-spec handle_decode_result(state()) -> {ok, reply_type(), state()}.
|
||||
handle_decode_result(State = #{th_reply_type := oneway_void}) ->
|
||||
{ok, oneway_void, State};
|
||||
handle_decode_result(State) ->
|
||||
{ok, call, State}.
|
||||
|
||||
-spec handle_decode_error(thrift_error(), woody_state:st()) ->
|
||||
{error, client_error()}.
|
||||
-spec handle_decode_error(thrift_error(), woody_state:st()) -> {error, client_error()}.
|
||||
handle_decode_error(Error, WoodyState) ->
|
||||
_ = woody_event_handler:handle_event(?EV_INTERNAL_ERROR, WoodyState, #{
|
||||
error => <<"thrift protocol read failed">>,
|
||||
reason => woody_error:format_details(Error)
|
||||
}),
|
||||
error => <<"thrift protocol read failed">>,
|
||||
reason => woody_error:format_details(Error)
|
||||
}),
|
||||
{error, client_error(Error)}.
|
||||
|
||||
-spec client_error(thrift_error()) ->
|
||||
client_error().
|
||||
-spec client_error(thrift_error()) -> client_error().
|
||||
client_error({bad_binary_protocol_version, Version}) ->
|
||||
BinVersion = genlib:to_binary(Version),
|
||||
{client, <<"thrift: bad binary protocol version: ", BinVersion/binary>>};
|
||||
@ -198,38 +209,38 @@ client_error(request_reply_type_mismatch) ->
|
||||
client_error(Reason) ->
|
||||
{client, woody_util:to_binary(["thrift decode error: ", woody_error:format_details(Reason)])}.
|
||||
|
||||
-spec throw_decode_error(_) ->
|
||||
no_return().
|
||||
-spec throw_decode_error(_) -> no_return().
|
||||
throw_decode_error(Error) ->
|
||||
throw({woody_decode_error, Error}).
|
||||
|
||||
%% Handle request
|
||||
-spec call_handler_safe(state()) ->
|
||||
{ok | {error, woody_error:error()}, state()}.
|
||||
-spec call_handler_safe(state()) -> {ok | {error, woody_error:error()}, state()}.
|
||||
call_handler_safe(State) ->
|
||||
try handle_success(call_handler(State), State)
|
||||
try
|
||||
handle_success(call_handler(State), State)
|
||||
catch
|
||||
Class:Reason:Stacktrace ->
|
||||
handle_function_catch(Class, Reason, Stacktrace, State)
|
||||
end.
|
||||
|
||||
-spec call_handler(state()) ->
|
||||
{ok, woody:result()} | no_return().
|
||||
-spec call_handler(state()) -> {ok, woody:result()} | no_return().
|
||||
call_handler(#{
|
||||
woody_state := WoodyState,
|
||||
handler := Handler,
|
||||
function := Function,
|
||||
args := Args})
|
||||
->
|
||||
handler := Handler,
|
||||
function := Function,
|
||||
args := Args
|
||||
}) ->
|
||||
handle_function(Handler, Function, Args, WoodyState).
|
||||
|
||||
-spec handle_success({ok, woody:result()}, state()) ->
|
||||
{ok | {error, {system, woody_error:system_error()}}, state()}.
|
||||
handle_success(Result, State = #{
|
||||
function := Function,
|
||||
th_reply_type := ReplyType,
|
||||
woody_state := WoodyState
|
||||
}) ->
|
||||
-spec handle_success({ok, woody:result()}, state()) -> {ok | {error, {system, woody_error:system_error()}}, state()}.
|
||||
handle_success(
|
||||
Result,
|
||||
State = #{
|
||||
function := Function,
|
||||
th_reply_type := ReplyType,
|
||||
woody_state := WoodyState
|
||||
}
|
||||
) ->
|
||||
_ = log_handler_result(ok, WoodyState, #{result => Result}),
|
||||
StructName = atom_to_list(Function) ++ "_result",
|
||||
case Result of
|
||||
@ -245,42 +256,53 @@ handle_success(Result, State = #{
|
||||
encode_reply(ok, Reply, State#{th_msg_type => ?tMessageType_REPLY})
|
||||
end.
|
||||
|
||||
-spec handle_function_catch(woody_error:erlang_except(), _Except,
|
||||
woody_error:stack(), state())
|
||||
->
|
||||
{{error, woody_error:error()}, state()}.
|
||||
-spec handle_function_catch(
|
||||
woody_error:erlang_except(),
|
||||
_Except,
|
||||
woody_error:stack(),
|
||||
state()
|
||||
) -> {{error, woody_error:error()}, state()}.
|
||||
handle_function_catch(throw, Except, Stack, State) ->
|
||||
handle_exception(Except, Stack, State);
|
||||
handle_function_catch(error, {woody_error, Error = {_, _, _}}, _Stack, State) ->
|
||||
handle_woody_error(Error, State);
|
||||
handle_function_catch(Class, Error, Stack, State) when
|
||||
Class =:= error orelse Class =:= exit
|
||||
->
|
||||
handle_function_catch(Class, Error, Stack, State) when Class =:= error orelse Class =:= exit ->
|
||||
handle_internal_error(Error, Class, Stack, State).
|
||||
|
||||
|
||||
-spec handle_exception(woody_error:business_error() | _Throw, woody_error:stack(), state())
|
||||
->
|
||||
-spec handle_exception(woody_error:business_error() | _Throw, woody_error:stack(), state()) ->
|
||||
{{error, woody_error:error()}, state()}.
|
||||
handle_exception(Except, Stack, State = #{
|
||||
service := Service,
|
||||
function := Function,
|
||||
th_reply_type := ReplyType,
|
||||
woody_state := WoodyState
|
||||
}) ->
|
||||
handle_exception(
|
||||
Except,
|
||||
Stack,
|
||||
State = #{
|
||||
service := Service,
|
||||
function := Function,
|
||||
th_reply_type := ReplyType,
|
||||
woody_state := WoodyState
|
||||
}
|
||||
) ->
|
||||
{struct, _, XInfo} = ReplySpec = get_function_info(Service, Function, exceptions),
|
||||
{ExceptionList, FoundExcept} = lists:mapfoldl(
|
||||
fun(X, A) -> get_except(Except, X, A) end, undefined, XInfo),
|
||||
fun(X, A) -> get_except(Except, X, A) end,
|
||||
undefined,
|
||||
XInfo
|
||||
),
|
||||
case {FoundExcept, ReplyType} of
|
||||
{undefined, _} ->
|
||||
handle_internal_error(Except, throw, Stack, State);
|
||||
{{_Module, _Type}, oneway_void} ->
|
||||
log_handler_result(error, WoodyState,
|
||||
#{class => business, result => Except, ignore => true}),
|
||||
log_handler_result(
|
||||
error,
|
||||
WoodyState,
|
||||
#{class => business, result => Except, ignore => true}
|
||||
),
|
||||
{{error, {business, ignore}}, State};
|
||||
{{Module, Type}, _} ->
|
||||
log_handler_result(error, WoodyState,
|
||||
#{class => business, result => Except, ignore => false}),
|
||||
log_handler_result(
|
||||
error,
|
||||
WoodyState,
|
||||
#{class => business, result => Except, ignore => false}
|
||||
),
|
||||
ExceptTuple = list_to_tuple([Function | ExceptionList]),
|
||||
encode_reply(
|
||||
{error, {business, genlib:to_binary(get_except_name(Module, Type))}},
|
||||
@ -316,27 +338,42 @@ handle_woody_error(Error, State = #{woody_state := WoodyState}) ->
|
||||
-spec handle_internal_error(_Error, woody_error:erlang_except(), woody_error:stack(), state()) ->
|
||||
{{error, {system, {internal, woody_error:source(), woody_error:details()}}}, state()}.
|
||||
handle_internal_error(Error, ExcClass, Stack, State = #{woody_state := WoodyState, th_reply_type := oneway_void}) ->
|
||||
log_handler_result(error, WoodyState,
|
||||
#{class => system, result => Error, except_class => ExcClass, stack => Stack, ignore => true}),
|
||||
log_handler_result(
|
||||
error,
|
||||
WoodyState,
|
||||
#{class => system, result => Error, except_class => ExcClass, stack => Stack, ignore => true}
|
||||
),
|
||||
{{error, {system, {internal, result_unexpected, <<>>}}}, State};
|
||||
handle_internal_error(Error, ExcClass, Stack, State = #{woody_state := WoodyState}) ->
|
||||
log_handler_result(error, WoodyState,
|
||||
#{class => system, result => Error, except_class => ExcClass, stack => Stack, ignore => false}),
|
||||
{{error, {system, {internal, result_unexpected,
|
||||
format_unexpected_error(ExcClass, woody_error:format_details(Error), Stack)}}}, State}.
|
||||
log_handler_result(
|
||||
error,
|
||||
WoodyState,
|
||||
#{class => system, result => Error, except_class => ExcClass, stack => Stack, ignore => false}
|
||||
),
|
||||
{{error,
|
||||
{system,
|
||||
{internal, result_unexpected,
|
||||
format_unexpected_error(ExcClass, woody_error:format_details(Error), Stack)}}},
|
||||
State}.
|
||||
|
||||
-spec encode_reply(ok | {error, woody_error:business_error()}, _Result, state()) ->
|
||||
{ok | {error, woody_error:error()}, state()}.
|
||||
encode_reply(Status, Reply, State = #{
|
||||
th_proto := Proto,
|
||||
function := Function,
|
||||
th_msg_type := ReplyMessageType,
|
||||
th_seqid := SeqId,
|
||||
woody_state := WoodyState
|
||||
}) ->
|
||||
encode_reply(
|
||||
Status,
|
||||
Reply,
|
||||
State = #{
|
||||
th_proto := Proto,
|
||||
function := Function,
|
||||
th_msg_type := ReplyMessageType,
|
||||
th_seqid := SeqId,
|
||||
woody_state := WoodyState
|
||||
}
|
||||
) ->
|
||||
try
|
||||
StartMessage = #protocol_message_begin{
|
||||
name = atom_to_list(Function), type = ReplyMessageType, seqid = SeqId
|
||||
name = atom_to_list(Function),
|
||||
type = ReplyMessageType,
|
||||
seqid = SeqId
|
||||
},
|
||||
{Protocol1, ok} = thrift_protocol:write(Proto, StartMessage),
|
||||
{Protocol2, ok} = thrift_protocol:write(Protocol1, Reply),
|
||||
@ -347,11 +384,11 @@ encode_reply(Status, Reply, State = #{
|
||||
error:{badmatch, {_, {error, Error}}}:Stack ->
|
||||
Reason = woody_error:format_details(Error),
|
||||
_ = woody_event_handler:handle_event(?EV_INTERNAL_ERROR, WoodyState, #{
|
||||
error => <<"thrift protocol write failed">>,
|
||||
reason => Reason,
|
||||
class => error,
|
||||
stack => Stack
|
||||
}),
|
||||
error => <<"thrift protocol write failed">>,
|
||||
reason => Reason,
|
||||
class => error,
|
||||
stack => Stack
|
||||
}),
|
||||
{{error, {system, {internal, result_unexpected, format_unexpected_error(error, Reason, Stack)}}}, State}
|
||||
end.
|
||||
|
||||
|
@ -20,25 +20,29 @@
|
||||
|
||||
%% Types
|
||||
-type handler_limits() :: #{
|
||||
max_heap_size => integer() %% process words, see erlang:process_flag(max_heap_size, MaxHeapSize) for details.
|
||||
%% process words, see erlang:process_flag(max_heap_size, MaxHeapSize) for details.
|
||||
max_heap_size => integer()
|
||||
}.
|
||||
|
||||
-export_type([handler_limits/0]).
|
||||
|
||||
-type transport_opts() :: #{
|
||||
connection_type => worker | supervisor,
|
||||
connection_type => worker | supervisor,
|
||||
handshake_timeout => timeout(),
|
||||
max_connections => ranch:max_conns(),
|
||||
logger => module(),
|
||||
num_acceptors => pos_integer(),
|
||||
shutdown => timeout() | brutal_kill,
|
||||
socket => any(),
|
||||
socket_opts => any(),
|
||||
transport => module() % ranch_tcp | ranch_ssl
|
||||
max_connections => ranch:max_conns(),
|
||||
logger => module(),
|
||||
num_acceptors => pos_integer(),
|
||||
shutdown => timeout() | brutal_kill,
|
||||
socket => any(),
|
||||
socket_opts => any(),
|
||||
% ranch_tcp | ranch_ssl
|
||||
transport => module()
|
||||
}.
|
||||
|
||||
-export_type([transport_opts/0]).
|
||||
|
||||
-type route(T) :: {woody:path(), module(), T}.
|
||||
|
||||
-export_type([route/1]).
|
||||
|
||||
-type read_body_opts() :: cowboy_req:read_body_opts().
|
||||
@ -47,47 +51,50 @@
|
||||
%% get rid of separate route_opts() when backward compatibility isn't an issue.
|
||||
|
||||
-type options() :: #{
|
||||
handlers := list(woody:http_handler(woody:th_handler())),
|
||||
event_handler := woody:ev_handlers(),
|
||||
ip := inet:ip_address(),
|
||||
port := inet:port_number(),
|
||||
protocol => thrift,
|
||||
transport => http,
|
||||
transport_opts => transport_opts(),
|
||||
read_body_opts => read_body_opts(),
|
||||
protocol_opts => protocol_opts(),
|
||||
handler_limits => handler_limits(),
|
||||
additional_routes => [route(_)],
|
||||
handlers := list(woody:http_handler(woody:th_handler())),
|
||||
event_handler := woody:ev_handlers(),
|
||||
ip := inet:ip_address(),
|
||||
port := inet:port_number(),
|
||||
protocol => thrift,
|
||||
transport => http,
|
||||
transport_opts => transport_opts(),
|
||||
read_body_opts => read_body_opts(),
|
||||
protocol_opts => protocol_opts(),
|
||||
handler_limits => handler_limits(),
|
||||
additional_routes => [route(_)],
|
||||
%% shutdown_timeout: time to drain current connections when shutdown signal is recieved
|
||||
%% NOTE: when changing this value make sure to take into account the request_timeout and
|
||||
%% max_keepalive settings of protocol_opts() to achieve the desired effect
|
||||
shutdown_timeout => timeout()
|
||||
shutdown_timeout => timeout()
|
||||
}.
|
||||
|
||||
-export_type([options/0]).
|
||||
|
||||
-type route_opts() :: #{
|
||||
handlers := list(woody:http_handler(woody:th_handler())),
|
||||
event_handler := woody:ev_handlers(),
|
||||
read_body_opts => read_body_opts(),
|
||||
protocol => thrift,
|
||||
transport => http,
|
||||
handler_limits => handler_limits()
|
||||
handlers := list(woody:http_handler(woody:th_handler())),
|
||||
event_handler := woody:ev_handlers(),
|
||||
read_body_opts => read_body_opts(),
|
||||
protocol => thrift,
|
||||
transport => http,
|
||||
handler_limits => handler_limits()
|
||||
}.
|
||||
|
||||
-export_type([route_opts/0]).
|
||||
|
||||
-type re_mp() :: tuple(). %% fuck otp for hiding the types.
|
||||
%% fuck otp for hiding the types.
|
||||
-type re_mp() :: tuple().
|
||||
-type protocol_opts() :: cowboy_http:opts().
|
||||
|
||||
-export_type([protocol_opts/0]).
|
||||
|
||||
-type state() :: #{
|
||||
th_handler => woody:th_handler(),
|
||||
ev_handler := woody:ev_handlers(),
|
||||
th_handler => woody:th_handler(),
|
||||
ev_handler := woody:ev_handlers(),
|
||||
read_body_opts := read_body_opts(),
|
||||
handler_limits := handler_limits(),
|
||||
regexp_meta := re_mp(),
|
||||
url => woody:url(),
|
||||
woody_state => woody_state:st()
|
||||
regexp_meta := re_mp(),
|
||||
url => woody:url(),
|
||||
woody_state => woody_state:st()
|
||||
}.
|
||||
|
||||
-type cowboy_init_result() ::
|
||||
@ -99,7 +106,7 @@
|
||||
| {stop, cowboy_req:req(), undefined}.
|
||||
|
||||
-define(DEFAULT_ACCEPTORS_POOLSIZE, 100).
|
||||
-define(DEFAULT_SHUTDOWN_TIMEOUT, 0).
|
||||
-define(DEFAULT_SHUTDOWN_TIMEOUT, 0).
|
||||
|
||||
%% nginx should be configured to take care of various limits.
|
||||
|
||||
@ -108,8 +115,7 @@
|
||||
%%
|
||||
%% woody_server callback
|
||||
%%
|
||||
-spec child_spec(atom(), options()) ->
|
||||
supervisor:child_spec().
|
||||
-spec child_spec(atom(), options()) -> supervisor:child_spec().
|
||||
child_spec(Id, Opts) ->
|
||||
{Transport, TransportOpts} = get_socket_transport(Opts),
|
||||
CowboyOpts = get_cowboy_config(Opts),
|
||||
@ -118,8 +124,7 @@ child_spec(Id, Opts) ->
|
||||
RanchSpec = ranch:child_spec(RanchRef, Transport, TransportOpts, cowboy_clear, CowboyOpts),
|
||||
make_server_childspec(Id, [RanchSpec, DrainSpec]).
|
||||
|
||||
-spec get_addr(atom()) ->
|
||||
{inet:ip_address(), inet:port_number()}.
|
||||
-spec get_addr(atom()) -> {inet:ip_address(), inet:port_number()}.
|
||||
get_addr(Id) ->
|
||||
ranch:get_addr(create_ranch_ref(Id)).
|
||||
|
||||
@ -140,75 +145,72 @@ make_server_childspec(Id, Children) ->
|
||||
}.
|
||||
|
||||
get_socket_transport(Opts = #{ip := Ip, port := Port}) ->
|
||||
Defaults = #{num_acceptors => ?DEFAULT_ACCEPTORS_POOLSIZE},
|
||||
Defaults = #{num_acceptors => ?DEFAULT_ACCEPTORS_POOLSIZE},
|
||||
TransportOpts = maps:merge(Defaults, maps:get(transport_opts, Opts, #{})),
|
||||
Transport = maps:get(transport, TransportOpts, ranch_tcp),
|
||||
SocketOpts = [{ip, Ip}, {port, Port} | maps:get(socket_opts, TransportOpts, [])],
|
||||
Transport = maps:get(transport, TransportOpts, ranch_tcp),
|
||||
SocketOpts = [{ip, Ip}, {port, Port} | maps:get(socket_opts, TransportOpts, [])],
|
||||
{Transport, set_ranch_option(socket_opts, SocketOpts, TransportOpts)}.
|
||||
|
||||
set_ranch_option(Key, Value, Opts) ->
|
||||
Opts#{Key => Value}.
|
||||
|
||||
-spec get_cowboy_config(options()) ->
|
||||
protocol_opts().
|
||||
-spec get_cowboy_config(options()) -> protocol_opts().
|
||||
get_cowboy_config(Opts = #{event_handler := EvHandler}) ->
|
||||
ok = validate_event_handler(EvHandler),
|
||||
Dispatch = get_dispatch(Opts),
|
||||
ok = validate_event_handler(EvHandler),
|
||||
Dispatch = get_dispatch(Opts),
|
||||
ProtocolOpts = maps:get(protocol_opts, Opts, #{}),
|
||||
CowboyOpts = maps:put(stream_handlers, [woody_monitor_h, woody_trace_h, cowboy_stream_h], ProtocolOpts),
|
||||
CowboyOpts = maps:put(stream_handlers, [woody_monitor_h, woody_trace_h, cowboy_stream_h], ProtocolOpts),
|
||||
Env = woody_trace_h:env(maps:with([event_handler], Opts)),
|
||||
maps:merge(#{
|
||||
env => Env#{dispatch => Dispatch},
|
||||
max_header_name_length => 64
|
||||
}, CowboyOpts).
|
||||
maps:merge(
|
||||
#{
|
||||
env => Env#{dispatch => Dispatch},
|
||||
max_header_name_length => 64
|
||||
},
|
||||
CowboyOpts
|
||||
).
|
||||
|
||||
validate_event_handler(Handlers) when is_list(Handlers) ->
|
||||
true = lists:all(
|
||||
fun(Handler) ->
|
||||
is_tuple(woody_util:get_mod_opts(Handler))
|
||||
end, Handlers),
|
||||
end,
|
||||
Handlers
|
||||
),
|
||||
ok;
|
||||
validate_event_handler(Handler) ->
|
||||
validate_event_handler([Handler]).
|
||||
|
||||
|
||||
-spec get_dispatch(options())->
|
||||
cowboy_router:dispatch_rules().
|
||||
-spec get_dispatch(options()) -> cowboy_router:dispatch_rules().
|
||||
get_dispatch(Opts) ->
|
||||
cowboy_router:compile([{'_', get_all_routes(Opts)}]).
|
||||
|
||||
-spec get_all_routes(options())->
|
||||
[route(_)].
|
||||
-spec get_all_routes(options()) -> [route(_)].
|
||||
get_all_routes(Opts) ->
|
||||
RouteOpts = maps:with([handlers, event_handler, read_body_opts, handler_limits, protocol, transport], Opts),
|
||||
AdditionalRoutes = maps:get(additional_routes, Opts, []),
|
||||
AdditionalRoutes ++ get_routes(RouteOpts).
|
||||
|
||||
-spec get_routes(route_opts()) ->
|
||||
[route(state())].
|
||||
-spec get_routes(route_opts()) -> [route(state())].
|
||||
get_routes(Opts = #{handlers := Handlers}) ->
|
||||
State0 = init_state(Opts),
|
||||
[get_route(State0, Handler) || Handler <- Handlers].
|
||||
|
||||
-spec get_route(state(), woody:http_handler(woody:th_handler())) ->
|
||||
route(state()).
|
||||
-spec get_route(state(), woody:http_handler(woody:th_handler())) -> route(state()).
|
||||
get_route(State0, {PathMatch, {Service, Handler}}) ->
|
||||
{PathMatch, ?MODULE, State0#{th_handler => {Service, Handler}}};
|
||||
get_route(_, Handler) ->
|
||||
error({bad_handler_spec, Handler}).
|
||||
|
||||
-spec init_state(route_opts()) ->
|
||||
state().
|
||||
-spec init_state(route_opts()) -> state().
|
||||
init_state(Opts = #{}) ->
|
||||
#{
|
||||
ev_handler => maps:get(event_handler, Opts),
|
||||
ev_handler => maps:get(event_handler, Opts),
|
||||
read_body_opts => maps:get(read_body_opts, Opts, #{}),
|
||||
handler_limits => maps:get(handler_limits, Opts, #{}),
|
||||
regexp_meta => compile_filter_meta()
|
||||
regexp_meta => compile_filter_meta()
|
||||
}.
|
||||
|
||||
-spec compile_filter_meta() ->
|
||||
re_mp().
|
||||
-spec compile_filter_meta() -> re_mp().
|
||||
compile_filter_meta() ->
|
||||
{ok, Re} = re:compile(?HEADER_META_RE, [unicode, caseless]),
|
||||
Re.
|
||||
@ -216,8 +218,7 @@ compile_filter_meta() ->
|
||||
%%
|
||||
%% cowboy_http_handler callbacks
|
||||
%%
|
||||
-spec init(cowboy_req:req(), state()) ->
|
||||
cowboy_init_result().
|
||||
-spec init(cowboy_req:req(), state()) -> cowboy_init_result().
|
||||
init(Req, Opts = #{ev_handler := EvHandler, handler_limits := Limits}) ->
|
||||
ok = set_handler_limits(Limits),
|
||||
Url = unicode:characters_to_binary(cowboy_req:uri(Req)),
|
||||
@ -228,101 +229,92 @@ init(Req, Opts = #{ev_handler := EvHandler, handler_limits := Limits}) ->
|
||||
{stop, Req1, State} -> {ok, Req1, State}
|
||||
end.
|
||||
|
||||
-spec set_handler_limits(handler_limits()) ->
|
||||
ok.
|
||||
-spec set_handler_limits(handler_limits()) -> ok.
|
||||
set_handler_limits(Limits) ->
|
||||
case maps:get(max_heap_size, Limits, undefined) of
|
||||
undefined ->
|
||||
ok;
|
||||
MaxHeapSize ->
|
||||
_ = erlang:process_flag(max_heap_size, #{
|
||||
size => MaxHeapSize,
|
||||
kill => true,
|
||||
size => MaxHeapSize,
|
||||
kill => true,
|
||||
error_logger => true
|
||||
}),
|
||||
ok
|
||||
end.
|
||||
|
||||
-spec handle(cowboy_req:req(), state()) ->
|
||||
{ok, cowboy_req:req(), _}.
|
||||
handle(Req, State = #{
|
||||
url := Url,
|
||||
woody_state := WoodyState,
|
||||
read_body_opts := ReadBodyOpts,
|
||||
th_handler := ThriftHandler
|
||||
}) ->
|
||||
Req2 = case get_body(Req, ReadBodyOpts) of
|
||||
{ok, Body, Req1} when byte_size(Body) > 0 ->
|
||||
_ = woody_event_handler:handle_event(?EV_SERVER_RECEIVE, WoodyState, #{url => Url, status => ok}),
|
||||
handle_request(Body, ThriftHandler, WoodyState, Req1);
|
||||
{ok, <<>>, Req1} ->
|
||||
reply_client_error(400, <<"body empty">>, Req1, State)
|
||||
end,
|
||||
-spec handle(cowboy_req:req(), state()) -> {ok, cowboy_req:req(), _}.
|
||||
handle(
|
||||
Req,
|
||||
State = #{
|
||||
url := Url,
|
||||
woody_state := WoodyState,
|
||||
read_body_opts := ReadBodyOpts,
|
||||
th_handler := ThriftHandler
|
||||
}
|
||||
) ->
|
||||
Req2 =
|
||||
case get_body(Req, ReadBodyOpts) of
|
||||
{ok, Body, Req1} when byte_size(Body) > 0 ->
|
||||
_ = woody_event_handler:handle_event(?EV_SERVER_RECEIVE, WoodyState, #{url => Url, status => ok}),
|
||||
handle_request(Body, ThriftHandler, WoodyState, Req1);
|
||||
{ok, <<>>, Req1} ->
|
||||
reply_client_error(400, <<"body empty">>, Req1, State)
|
||||
end,
|
||||
{ok, Req2, undefined}.
|
||||
|
||||
create_dummy_state(EvHandler) ->
|
||||
DummyRpcID = #{
|
||||
span_id => ?DUMMY_REQ_ID,
|
||||
trace_id => ?DUMMY_REQ_ID,
|
||||
span_id => ?DUMMY_REQ_ID,
|
||||
trace_id => ?DUMMY_REQ_ID,
|
||||
parent_id => ?DUMMY_REQ_ID
|
||||
},
|
||||
woody_state:new(server, woody_context:new(DummyRpcID), EvHandler).
|
||||
|
||||
-spec terminate(_Reason, _Req, state() | _) ->
|
||||
ok.
|
||||
-spec terminate(_Reason, _Req, state() | _) -> ok.
|
||||
terminate(normal, _Req, _Status) ->
|
||||
ok;
|
||||
terminate(Reason, _Req, #{ev_handler := EvHandler} = Opts) ->
|
||||
WoodyState = maps:get(woody_state, Opts, create_dummy_state(EvHandler)),
|
||||
_ = woody_event_handler:handle_event(?EV_INTERNAL_ERROR, WoodyState, #{
|
||||
error => <<"http handler terminated abnormally">>,
|
||||
reason => woody_error:format_details(Reason),
|
||||
class => undefined,
|
||||
final => true
|
||||
}),
|
||||
error => <<"http handler terminated abnormally">>,
|
||||
reason => woody_error:format_details(Reason),
|
||||
class => undefined,
|
||||
final => true
|
||||
}),
|
||||
ok.
|
||||
|
||||
|
||||
%% init functions
|
||||
|
||||
%% First perform basic http checks: method, content type, etc,
|
||||
%% then check woody related headers: IDs, deadline, meta.
|
||||
|
||||
-spec check_request(cowboy_req:req(), state()) ->
|
||||
check_result().
|
||||
-spec check_request(cowboy_req:req(), state()) -> check_result().
|
||||
check_request(Req, State) ->
|
||||
check_method(cowboy_req:method(Req), Req, State).
|
||||
|
||||
-spec check_method(woody:http_header_val(), cowboy_req:req(), state()) ->
|
||||
check_result().
|
||||
-spec check_method(woody:http_header_val(), cowboy_req:req(), state()) -> check_result().
|
||||
check_method(<<"POST">>, Req, State) ->
|
||||
check_content_type(cowboy_req:header(<<"content-type">>, Req), Req, State);
|
||||
|
||||
check_method(Method, Req, State) ->
|
||||
Req1 = cowboy_req:set_resp_header(<<"allow">>, <<"POST">>, Req),
|
||||
Reason = woody_util:to_binary(["wrong method: ", Method]),
|
||||
reply_bad_header(405, Reason, Req1, State).
|
||||
|
||||
-spec check_content_type(woody:http_header_val() | undefined, cowboy_req:req(), state()) ->
|
||||
check_result().
|
||||
-spec check_content_type(woody:http_header_val() | undefined, cowboy_req:req(), state()) -> check_result().
|
||||
check_content_type(?CONTENT_TYPE_THRIFT, Req, State) ->
|
||||
Header = cowboy_req:header(<<"accept">>, Req),
|
||||
check_accept(Header, Req, State);
|
||||
check_content_type(BadCType, Req, State) ->
|
||||
reply_bad_header(415, woody_util:to_binary(["wrong content type: ", BadCType]), Req, State).
|
||||
|
||||
-spec check_accept(woody:http_header_val() | undefined, cowboy_req:req(), state()) ->
|
||||
check_result().
|
||||
check_accept(Accept, Req, State) when
|
||||
Accept =:= ?CONTENT_TYPE_THRIFT ;
|
||||
Accept =:= undefined
|
||||
->
|
||||
-spec check_accept(woody:http_header_val() | undefined, cowboy_req:req(), state()) -> check_result().
|
||||
check_accept(Accept, Req, State) when Accept =:= ?CONTENT_TYPE_THRIFT; Accept =:= undefined ->
|
||||
check_woody_headers(Req, State);
|
||||
check_accept(BadAccept, Req1, State) ->
|
||||
reply_bad_header(406, woody_util:to_binary(["wrong client accept: ", BadAccept]), Req1, State).
|
||||
|
||||
-spec check_woody_headers(cowboy_req:req(), state()) ->
|
||||
check_result().
|
||||
-spec check_woody_headers(cowboy_req:req(), state()) -> check_result().
|
||||
check_woody_headers(Req, State = #{woody_state := WoodyState0}) ->
|
||||
case get_rpc_id(Req) of
|
||||
{ok, RpcId, Req1} ->
|
||||
@ -334,23 +326,27 @@ check_woody_headers(Req, State = #{woody_state := WoodyState0}) ->
|
||||
);
|
||||
{error, BadRpcId, Req1} ->
|
||||
WoodyState1 = set_rpc_id(BadRpcId, WoodyState0),
|
||||
reply_bad_header(400, woody_util:to_binary(["bad ", ?HEADER_PREFIX, " id header"]),
|
||||
Req1, update_woody_state(State, WoodyState1, Req1)
|
||||
reply_bad_header(
|
||||
400,
|
||||
woody_util:to_binary(["bad ", ?HEADER_PREFIX, " id header"]),
|
||||
Req1,
|
||||
update_woody_state(State, WoodyState1, Req1)
|
||||
)
|
||||
end.
|
||||
|
||||
-spec get_rpc_id(cowboy_req:req()) ->
|
||||
{ok | error, woody:rpc_id(), cowboy_req:req()}.
|
||||
-spec get_rpc_id(cowboy_req:req()) -> {ok | error, woody:rpc_id(), cowboy_req:req()}.
|
||||
get_rpc_id(Req) ->
|
||||
check_ids(maps:fold(
|
||||
fun get_rpc_id/3,
|
||||
#{req => Req},
|
||||
#{
|
||||
span_id => ?HEADER_RPC_ID,
|
||||
trace_id => ?HEADER_RPC_ROOT_ID,
|
||||
parent_id => ?HEADER_RPC_PARENT_ID
|
||||
}
|
||||
)).
|
||||
check_ids(
|
||||
maps:fold(
|
||||
fun get_rpc_id/3,
|
||||
#{req => Req},
|
||||
#{
|
||||
span_id => ?HEADER_RPC_ID,
|
||||
trace_id => ?HEADER_RPC_ROOT_ID,
|
||||
parent_id => ?HEADER_RPC_PARENT_ID
|
||||
}
|
||||
)
|
||||
).
|
||||
|
||||
get_rpc_id(Id, Header, Acc = #{req := Req}) ->
|
||||
case cowboy_req:header(Header, Req) of
|
||||
@ -378,13 +374,15 @@ check_deadline_header(DeadlineBin, Req, State) ->
|
||||
reply_bad_header(400, ErrorDescription, Req, State)
|
||||
end.
|
||||
|
||||
-spec check_deadline(woody:deadline(), cowboy_req:req(), state()) ->
|
||||
check_result().
|
||||
-spec check_deadline(woody:deadline(), cowboy_req:req(), state()) -> check_result().
|
||||
check_deadline(Deadline, Req, State = #{url := Url, woody_state := WoodyState}) ->
|
||||
case woody_deadline:is_reached(Deadline) of
|
||||
true ->
|
||||
_ = woody_event_handler:handle_event(?EV_SERVER_RECEIVE, WoodyState,
|
||||
#{url => Url, status => error, reason => <<"Deadline reached">>}),
|
||||
_ = woody_event_handler:handle_event(
|
||||
?EV_SERVER_RECEIVE,
|
||||
WoodyState,
|
||||
#{url => Url, status => error, reason => <<"Deadline reached">>}
|
||||
),
|
||||
Req1 = handle_error({system, {internal, resource_unavailable, <<"deadline reached">>}}, Req, WoodyState),
|
||||
{stop, Req1, undefined};
|
||||
false ->
|
||||
@ -393,51 +391,49 @@ check_deadline(Deadline, Req, State = #{url := Url, woody_state := WoodyState})
|
||||
check_metadata_headers(Headers, Req, update_woody_state(State, WoodyState1, Req))
|
||||
end.
|
||||
|
||||
-spec check_metadata_headers(woody:http_headers(), cowboy_req:req(), state()) ->
|
||||
check_result().
|
||||
-spec check_metadata_headers(woody:http_headers(), cowboy_req:req(), state()) -> check_result().
|
||||
check_metadata_headers(Headers, Req, State = #{woody_state := WoodyState, regexp_meta := ReMeta}) ->
|
||||
WoodyState1 = set_metadata(find_metadata(Headers, ReMeta), WoodyState),
|
||||
{ok, Req, update_woody_state(State, WoodyState1, Req)}.
|
||||
|
||||
-spec find_metadata(woody:http_headers(), re_mp()) ->
|
||||
woody_context:meta().
|
||||
-spec find_metadata(woody:http_headers(), re_mp()) -> woody_context:meta().
|
||||
find_metadata(Headers, Re) ->
|
||||
RpcId = ?HEADER_RPC_ID,
|
||||
RootId = ?HEADER_RPC_ROOT_ID,
|
||||
ParentId = ?HEADER_RPC_PARENT_ID,
|
||||
maps:fold(
|
||||
fun(H, V, Acc) when
|
||||
H =/= RpcId andalso
|
||||
H =/= RootId andalso
|
||||
H =/= ParentId
|
||||
->
|
||||
case re:replace(H, Re, "", [{return, binary}, anchored]) of
|
||||
H -> Acc;
|
||||
MetaHeader -> Acc#{MetaHeader => V}
|
||||
end;
|
||||
(_, _, Acc) -> Acc
|
||||
fun
|
||||
(H, V, Acc) when
|
||||
H =/= RpcId andalso
|
||||
H =/= RootId andalso
|
||||
H =/= ParentId
|
||||
->
|
||||
case re:replace(H, Re, "", [{return, binary}, anchored]) of
|
||||
H -> Acc;
|
||||
MetaHeader -> Acc#{MetaHeader => V}
|
||||
end;
|
||||
(_, _, Acc) ->
|
||||
Acc
|
||||
end,
|
||||
#{}, Headers).
|
||||
#{},
|
||||
Headers
|
||||
).
|
||||
|
||||
-spec set_rpc_id(woody:rpc_id(), woody_state:st()) ->
|
||||
woody_state:st().
|
||||
-spec set_rpc_id(woody:rpc_id(), woody_state:st()) -> woody_state:st().
|
||||
set_rpc_id(RpcId, WoodyState) ->
|
||||
woody_state:update_context(woody_context:new(RpcId), WoodyState).
|
||||
|
||||
-spec set_cert(cowboy_req:req(), woody_state:st()) ->
|
||||
woody_state:st().
|
||||
-spec set_cert(cowboy_req:req(), woody_state:st()) -> woody_state:st().
|
||||
set_cert(Req, WoodyState) ->
|
||||
Cert = woody_cert:from_req(Req),
|
||||
Context = woody_state:get_context(WoodyState),
|
||||
woody_state:update_context(woody_context:set_cert(Cert, Context), WoodyState).
|
||||
|
||||
-spec set_deadline(woody:deadline(), woody_state:st()) ->
|
||||
woody_state:st().
|
||||
-spec set_deadline(woody:deadline(), woody_state:st()) -> woody_state:st().
|
||||
set_deadline(Deadline, WoodyState) ->
|
||||
woody_state:add_context_deadline(Deadline, WoodyState).
|
||||
|
||||
-spec set_metadata(woody_context:meta(), woody_state:st()) ->
|
||||
woody_state:st().
|
||||
-spec set_metadata(woody_context:meta(), woody_state:st()) -> woody_state:st().
|
||||
set_metadata(Meta, WoodyState) ->
|
||||
woody_state:add_context_meta(Meta, WoodyState).
|
||||
|
||||
@ -447,16 +443,17 @@ reply_bad_header(Code, Reason, Req, State) when is_integer(Code) ->
|
||||
Req1 = reply_client_error(Code, Reason, Req, State),
|
||||
{stop, Req1, undefined}.
|
||||
|
||||
-spec reply_client_error(woody:http_code(), woody:http_header_val(), cowboy_req:req(), state()) ->
|
||||
cowboy_req:req().
|
||||
-spec reply_client_error(woody:http_code(), woody:http_header_val(), cowboy_req:req(), state()) -> cowboy_req:req().
|
||||
reply_client_error(Code, Reason, Req, #{url := Url, woody_state := WoodyState}) ->
|
||||
_ = woody_event_handler:handle_event(?EV_SERVER_RECEIVE, WoodyState,
|
||||
#{url => Url, status => error, reason => Reason}),
|
||||
_ = woody_event_handler:handle_event(
|
||||
?EV_SERVER_RECEIVE,
|
||||
WoodyState,
|
||||
#{url => Url, status => error, reason => Reason}
|
||||
),
|
||||
reply(Code, set_error_headers(<<"Result Unexpected">>, Reason, Req), WoodyState).
|
||||
|
||||
%% handle functions
|
||||
-spec get_body(cowboy_req:req(), read_body_opts()) ->
|
||||
{ok, woody:http_body(), cowboy_req:req()}.
|
||||
-spec get_body(cowboy_req:req(), read_body_opts()) -> {ok, woody:http_body(), cowboy_req:req()}.
|
||||
get_body(Req, ReadBodyOpts) ->
|
||||
do_get_body(<<>>, Req, ReadBodyOpts).
|
||||
|
||||
@ -468,8 +465,7 @@ 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().
|
||||
-spec handle_request(woody:http_body(), woody:th_handler(), woody_state:st(), cowboy_req:req()) -> cowboy_req:req().
|
||||
handle_request(Body, ThriftHander, WoodyState, Req) ->
|
||||
ok = woody_monitor_h:set_event(?EV_SERVICE_HANDLER_RESULT, Req),
|
||||
case woody_server_thrift_handler:init_handler(Body, ThriftHander, WoodyState) of
|
||||
@ -509,8 +505,7 @@ handle_error({system, {external, resource_unavailable, Details}}, Req, WoodyStat
|
||||
handle_error({system, {external, result_unknown, Details}}, Req, WoodyState) ->
|
||||
reply(502, set_error_headers(<<"Result Unknown">>, Details, Req), WoodyState).
|
||||
|
||||
-spec set_error_headers(woody:http_header_val(), woody:http_header_val(), cowboy_req:req()) ->
|
||||
cowboy_req:req().
|
||||
-spec set_error_headers(woody:http_header_val(), woody:http_header_val(), cowboy_req:req()) -> cowboy_req:req().
|
||||
set_error_headers(Class, Reason, Req) ->
|
||||
Headers = #{
|
||||
?HEADER_E_CLASS => Class,
|
||||
@ -518,8 +513,7 @@ set_error_headers(Class, Reason, Req) ->
|
||||
},
|
||||
cowboy_req:set_resp_headers(Headers, Req).
|
||||
|
||||
-spec reply(woody:http_code(), cowboy_req:req(), woody_state:st()) ->
|
||||
cowboy_req:req().
|
||||
-spec reply(woody:http_code(), cowboy_req:req(), woody_state:st()) -> cowboy_req:req().
|
||||
reply(200, Req, WoodyState) ->
|
||||
do_reply(200, cowboy_req:set_resp_header(<<"content-type">>, ?CONTENT_TYPE_THRIFT, Req), WoodyState);
|
||||
reply(Code, Req, WoodyState) ->
|
||||
|
@ -15,30 +15,35 @@
|
||||
|
||||
%% cowboy_handler callbacks
|
||||
-behaviour(cowboy_handler).
|
||||
|
||||
-export([init/2]).
|
||||
-export([terminate/3]).
|
||||
|
||||
%% Types
|
||||
-type handler_limits() :: #{
|
||||
max_heap_size => integer() %% process words, see erlang:process_flag(max_heap_size, MaxHeapSize) for details.
|
||||
%% process words, see erlang:process_flag(max_heap_size, MaxHeapSize) for details.
|
||||
max_heap_size => integer()
|
||||
}.
|
||||
|
||||
-export_type([handler_limits/0]).
|
||||
|
||||
-type transport_opts() :: #{
|
||||
connection_type => worker | supervisor,
|
||||
connection_type => worker | supervisor,
|
||||
handshake_timeout => timeout(),
|
||||
max_connections => ranch:max_conns(),
|
||||
logger => module(),
|
||||
num_acceptors => pos_integer(),
|
||||
shutdown => timeout() | brutal_kill,
|
||||
socket => any(),
|
||||
socket_opts => any(),
|
||||
transport => module() % ranch_tcp | ranch_ssl
|
||||
max_connections => ranch:max_conns(),
|
||||
logger => module(),
|
||||
num_acceptors => pos_integer(),
|
||||
shutdown => timeout() | brutal_kill,
|
||||
socket => any(),
|
||||
socket_opts => any(),
|
||||
% ranch_tcp | ranch_ssl
|
||||
transport => module()
|
||||
}.
|
||||
|
||||
-export_type([transport_opts/0]).
|
||||
|
||||
-type route(T) :: {woody:path(), module(), T}.
|
||||
|
||||
-export_type([route/1]).
|
||||
|
||||
-type read_body_opts() :: cowboy_req:read_body_opts().
|
||||
@ -47,33 +52,36 @@
|
||||
%% get rid of separate route_opts() when backward compatibility isn't an issue.
|
||||
|
||||
-type options() :: #{
|
||||
handlers := list(woody:http_handler(woody:th_handler())),
|
||||
event_handler := woody:ev_handlers(),
|
||||
ip := inet:ip_address(),
|
||||
port := inet:port_number(),
|
||||
protocol => thrift,
|
||||
transport => http,
|
||||
transport_opts => transport_opts(),
|
||||
read_body_opts => read_body_opts(),
|
||||
protocol_opts => protocol_opts(),
|
||||
handler_limits => handler_limits(),
|
||||
additional_routes => [route(_)],
|
||||
handlers := list(woody:http_handler(woody:th_handler())),
|
||||
event_handler := woody:ev_handlers(),
|
||||
ip := inet:ip_address(),
|
||||
port := inet:port_number(),
|
||||
protocol => thrift,
|
||||
transport => http,
|
||||
transport_opts => transport_opts(),
|
||||
read_body_opts => read_body_opts(),
|
||||
protocol_opts => protocol_opts(),
|
||||
handler_limits => handler_limits(),
|
||||
additional_routes => [route(_)],
|
||||
%% shutdown_timeout: time to drain current connections when shutdown signal is recieved
|
||||
%% NOTE: when changing this value make sure to take into account the request_timeout and
|
||||
%% max_keepalive settings of protocol_opts() to achieve the desired effect
|
||||
shutdown_timeout => timeout()
|
||||
shutdown_timeout => timeout()
|
||||
}.
|
||||
|
||||
-export_type([options/0]).
|
||||
|
||||
-type route_opts() :: #{
|
||||
handlers := list(woody:http_handler(woody:th_handler())),
|
||||
event_handler := woody:ev_handlers(),
|
||||
protocol => thrift,
|
||||
transport => http,
|
||||
read_body_opts => read_body_opts(),
|
||||
handler_limits => handler_limits()
|
||||
handlers := list(woody:http_handler(woody:th_handler())),
|
||||
event_handler := woody:ev_handlers(),
|
||||
protocol => thrift,
|
||||
transport => http,
|
||||
read_body_opts => read_body_opts(),
|
||||
handler_limits => handler_limits()
|
||||
}.
|
||||
|
||||
-export_type([route_opts/0]).
|
||||
|
||||
-define(ROUTE_OPT_NAMES, [
|
||||
handlers,
|
||||
event_handler,
|
||||
@ -89,13 +97,13 @@
|
||||
-export_type([protocol_opts/0]).
|
||||
|
||||
-type state() :: #{
|
||||
th_handler => woody:th_handler(),
|
||||
ev_handler := woody:ev_handlers(),
|
||||
th_handler => woody:th_handler(),
|
||||
ev_handler := woody:ev_handlers(),
|
||||
read_body_opts := read_body_opts(),
|
||||
handler_limits := handler_limits(),
|
||||
regexp_meta := re_mp(),
|
||||
url => woody:url(),
|
||||
woody_state => woody_state:st()
|
||||
regexp_meta := re_mp(),
|
||||
url => woody:url(),
|
||||
woody_state => woody_state:st()
|
||||
}.
|
||||
|
||||
-type cowboy_init_result() ::
|
||||
@ -107,7 +115,7 @@
|
||||
| {stop, cowboy_req:req(), undefined}.
|
||||
|
||||
-define(DEFAULT_ACCEPTORS_POOLSIZE, 100).
|
||||
-define(DEFAULT_SHUTDOWN_TIMEOUT, 0).
|
||||
-define(DEFAULT_SHUTDOWN_TIMEOUT, 0).
|
||||
|
||||
%% nginx should be configured to take care of various limits.
|
||||
|
||||
@ -116,8 +124,7 @@
|
||||
%%
|
||||
%% woody_server callback
|
||||
%%
|
||||
-spec child_spec(atom(), options()) ->
|
||||
supervisor:child_spec().
|
||||
-spec child_spec(atom(), options()) -> supervisor:child_spec().
|
||||
child_spec(Id, Opts) ->
|
||||
{Transport, TransportOpts} = get_socket_transport(Opts),
|
||||
CowboyOpts = get_cowboy_config(Opts),
|
||||
@ -126,8 +133,7 @@ child_spec(Id, Opts) ->
|
||||
RanchSpec = ranch:child_spec(RanchRef, Transport, TransportOpts, cowboy_clear, CowboyOpts),
|
||||
make_server_childspec(Id, [RanchSpec, DrainSpec]).
|
||||
|
||||
-spec get_addr(atom()) ->
|
||||
{inet:ip_address(), inet:port_number()}.
|
||||
-spec get_addr(atom()) -> {inet:ip_address(), inet:port_number()}.
|
||||
get_addr(Id) ->
|
||||
ranch:get_addr(create_ranch_ref(Id)).
|
||||
|
||||
@ -148,74 +154,71 @@ make_server_childspec(Id, Children) ->
|
||||
}.
|
||||
|
||||
get_socket_transport(Opts = #{ip := Ip, port := Port}) ->
|
||||
Defaults = #{num_acceptors => ?DEFAULT_ACCEPTORS_POOLSIZE},
|
||||
Defaults = #{num_acceptors => ?DEFAULT_ACCEPTORS_POOLSIZE},
|
||||
TransportOpts = maps:merge(Defaults, maps:get(transport_opts, Opts, #{})),
|
||||
Transport = maps:get(transport, TransportOpts, ranch_tcp),
|
||||
SocketOpts = [{ip, Ip}, {port, Port} | maps:get(socket_opts, TransportOpts, [])],
|
||||
Transport = maps:get(transport, TransportOpts, ranch_tcp),
|
||||
SocketOpts = [{ip, Ip}, {port, Port} | maps:get(socket_opts, TransportOpts, [])],
|
||||
{Transport, set_ranch_option(socket_opts, SocketOpts, TransportOpts)}.
|
||||
|
||||
set_ranch_option(Key, Value, Opts) ->
|
||||
Opts#{Key => Value}.
|
||||
|
||||
-spec get_cowboy_config(options()) ->
|
||||
protocol_opts().
|
||||
-spec get_cowboy_config(options()) -> protocol_opts().
|
||||
get_cowboy_config(Opts = #{event_handler := EvHandler}) ->
|
||||
ok = validate_event_handler(EvHandler),
|
||||
Dispatch = get_dispatch(Opts),
|
||||
ok = validate_event_handler(EvHandler),
|
||||
Dispatch = get_dispatch(Opts),
|
||||
ProtocolOpts = maps:get(protocol_opts, Opts, #{}),
|
||||
CowboyOpts = maps:put(stream_handlers, [woody_monitor_h, woody_trace_h, cowboy_stream_h], ProtocolOpts),
|
||||
CowboyOpts = maps:put(stream_handlers, [woody_monitor_h, woody_trace_h, cowboy_stream_h], ProtocolOpts),
|
||||
Env = woody_trace_h:env(maps:with([event_handler], Opts)),
|
||||
maps:merge(#{
|
||||
env => Env#{dispatch => Dispatch},
|
||||
max_header_name_length => 64
|
||||
}, CowboyOpts).
|
||||
maps:merge(
|
||||
#{
|
||||
env => Env#{dispatch => Dispatch},
|
||||
max_header_name_length => 64
|
||||
},
|
||||
CowboyOpts
|
||||
).
|
||||
|
||||
validate_event_handler(Handlers) when is_list(Handlers) ->
|
||||
true = lists:all(
|
||||
fun(Handler) ->
|
||||
is_tuple(woody_util:get_mod_opts(Handler))
|
||||
end, Handlers),
|
||||
end,
|
||||
Handlers
|
||||
),
|
||||
ok;
|
||||
validate_event_handler(Handler) ->
|
||||
validate_event_handler([Handler]).
|
||||
|
||||
|
||||
-spec get_dispatch(options())->
|
||||
cowboy_router:dispatch_rules().
|
||||
-spec get_dispatch(options()) -> cowboy_router:dispatch_rules().
|
||||
get_dispatch(Opts) ->
|
||||
cowboy_router:compile([{'_', get_all_routes(Opts)}]).
|
||||
|
||||
-spec get_all_routes(options())->
|
||||
[route(_)].
|
||||
-spec get_all_routes(options()) -> [route(_)].
|
||||
get_all_routes(Opts) ->
|
||||
AdditionalRoutes = maps:get(additional_routes, Opts, []),
|
||||
AdditionalRoutes ++ get_routes(maps:with(?ROUTE_OPT_NAMES, Opts)).
|
||||
|
||||
-spec get_routes(route_opts()) ->
|
||||
[route(state())].
|
||||
-spec get_routes(route_opts()) -> [route(state())].
|
||||
get_routes(Opts = #{handlers := Handlers}) ->
|
||||
State0 = init_state(Opts),
|
||||
[get_route(State0, Handler) || Handler <- Handlers].
|
||||
|
||||
-spec get_route(state(), woody:http_handler(woody:th_handler())) ->
|
||||
route(state()).
|
||||
-spec get_route(state(), woody:http_handler(woody:th_handler())) -> route(state()).
|
||||
get_route(State0, {PathMatch, {Service, Handler}}) ->
|
||||
{PathMatch, ?MODULE, State0#{th_handler => {Service, Handler}}};
|
||||
get_route(_, Handler) ->
|
||||
error({bad_handler_spec, Handler}).
|
||||
|
||||
-spec init_state(route_opts()) ->
|
||||
state().
|
||||
-spec init_state(route_opts()) -> state().
|
||||
init_state(Opts = #{}) ->
|
||||
#{
|
||||
ev_handler => maps:get(event_handler, Opts),
|
||||
ev_handler => maps:get(event_handler, Opts),
|
||||
read_body_opts => maps:get(read_body_opts, Opts, #{}),
|
||||
handler_limits => maps:get(handler_limits, Opts, #{}),
|
||||
regexp_meta => compile_filter_meta()
|
||||
regexp_meta => compile_filter_meta()
|
||||
}.
|
||||
|
||||
-spec compile_filter_meta() ->
|
||||
re_mp().
|
||||
-spec compile_filter_meta() -> re_mp().
|
||||
compile_filter_meta() ->
|
||||
{ok, Re} = re:compile(?HEADER_META_RE, [unicode, caseless]),
|
||||
Re.
|
||||
@ -223,8 +226,7 @@ compile_filter_meta() ->
|
||||
%%
|
||||
%% cowboy_http_handler callbacks
|
||||
%%
|
||||
-spec init(cowboy_req:req(), state()) ->
|
||||
cowboy_init_result().
|
||||
-spec init(cowboy_req:req(), state()) -> cowboy_init_result().
|
||||
init(Req, Opts = #{ev_handler := EvHandler, handler_limits := Limits}) ->
|
||||
ok = set_handler_limits(Limits),
|
||||
Url = unicode:characters_to_binary(cowboy_req:uri(Req)),
|
||||
@ -235,61 +237,61 @@ init(Req, Opts = #{ev_handler := EvHandler, handler_limits := Limits}) ->
|
||||
{stop, Req1, State} -> {ok, Req1, State}
|
||||
end.
|
||||
|
||||
-spec set_handler_limits(handler_limits()) ->
|
||||
ok.
|
||||
-spec set_handler_limits(handler_limits()) -> ok.
|
||||
set_handler_limits(Limits) ->
|
||||
case maps:get(max_heap_size, Limits, undefined) of
|
||||
undefined ->
|
||||
ok;
|
||||
MaxHeapSize ->
|
||||
_ = erlang:process_flag(max_heap_size, #{
|
||||
size => MaxHeapSize,
|
||||
kill => true,
|
||||
size => MaxHeapSize,
|
||||
kill => true,
|
||||
error_logger => true
|
||||
}),
|
||||
ok
|
||||
end.
|
||||
|
||||
-spec handle(cowboy_req:req(), state()) ->
|
||||
{ok, cowboy_req:req(), _}.
|
||||
handle(Req, State = #{
|
||||
url := Url,
|
||||
woody_state := WoodyState,
|
||||
read_body_opts := ReadBodyOpts,
|
||||
th_handler := ThriftHandler
|
||||
}) ->
|
||||
Req2 = 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);
|
||||
{ok, <<>>, Req1} ->
|
||||
reply_client_error(400, <<"body empty">>, Req1, State)
|
||||
end,
|
||||
-spec handle(cowboy_req:req(), state()) -> {ok, cowboy_req:req(), _}.
|
||||
handle(
|
||||
Req,
|
||||
State = #{
|
||||
url := Url,
|
||||
woody_state := WoodyState,
|
||||
read_body_opts := ReadBodyOpts,
|
||||
th_handler := ThriftHandler
|
||||
}
|
||||
) ->
|
||||
Req2 =
|
||||
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);
|
||||
{ok, <<>>, Req1} ->
|
||||
reply_client_error(400, <<"body empty">>, Req1, State)
|
||||
end,
|
||||
{ok, Req2, undefined}.
|
||||
|
||||
create_dummy_state(EvHandler) ->
|
||||
DummyRpcID = #{
|
||||
span_id => ?DUMMY_REQ_ID,
|
||||
trace_id => ?DUMMY_REQ_ID,
|
||||
span_id => ?DUMMY_REQ_ID,
|
||||
trace_id => ?DUMMY_REQ_ID,
|
||||
parent_id => ?DUMMY_REQ_ID
|
||||
},
|
||||
woody_state:new(server, woody_context:new(DummyRpcID), EvHandler).
|
||||
|
||||
-spec terminate(_Reason, _Req, state() | _) ->
|
||||
ok.
|
||||
-spec terminate(_Reason, _Req, state() | _) -> ok.
|
||||
terminate(normal, _Req, _Status) ->
|
||||
ok;
|
||||
terminate(Reason, _Req, #{ev_handler := EvHandler} = Opts) ->
|
||||
WoodyState = maps:get(woody_state, Opts, create_dummy_state(EvHandler)),
|
||||
_ = woody_event_handler:handle_event(?EV_INTERNAL_ERROR, WoodyState, #{
|
||||
error => <<"http handler terminated abnormally">>,
|
||||
reason => woody_error:format_details(Reason),
|
||||
class => undefined,
|
||||
final => true
|
||||
}),
|
||||
error => <<"http handler terminated abnormally">>,
|
||||
reason => woody_error:format_details(Reason),
|
||||
class => undefined,
|
||||
final => true
|
||||
}),
|
||||
ok.
|
||||
|
||||
|
||||
%% init functions
|
||||
|
||||
-define(CODEC, thrift_strict_binary_codec).
|
||||
@ -297,41 +299,32 @@ terminate(Reason, _Req, #{ev_handler := EvHandler} = Opts) ->
|
||||
%% First perform basic http checks: method, content type, etc,
|
||||
%% then check woody related headers: IDs, deadline, meta.
|
||||
|
||||
-spec check_request(cowboy_req:req(), state()) ->
|
||||
check_result().
|
||||
-spec check_request(cowboy_req:req(), state()) -> check_result().
|
||||
check_request(Req, State) ->
|
||||
check_method(cowboy_req:method(Req), Req, State).
|
||||
|
||||
-spec check_method(woody:http_header_val(), cowboy_req:req(), state()) ->
|
||||
check_result().
|
||||
-spec check_method(woody:http_header_val(), cowboy_req:req(), state()) -> check_result().
|
||||
check_method(<<"POST">>, Req, State) ->
|
||||
check_content_type(cowboy_req:header(<<"content-type">>, Req), Req, State);
|
||||
|
||||
check_method(Method, Req, State) ->
|
||||
Req1 = cowboy_req:set_resp_header(<<"allow">>, <<"POST">>, Req),
|
||||
Reason = woody_util:to_binary(["wrong method: ", Method]),
|
||||
reply_bad_header(405, Reason, Req1, State).
|
||||
|
||||
-spec check_content_type(woody:http_header_val() | undefined, cowboy_req:req(), state()) ->
|
||||
check_result().
|
||||
-spec check_content_type(woody:http_header_val() | undefined, cowboy_req:req(), state()) -> check_result().
|
||||
check_content_type(?CONTENT_TYPE_THRIFT, Req, State) ->
|
||||
Header = cowboy_req:header(<<"accept">>, Req),
|
||||
check_accept(Header, Req, State);
|
||||
check_content_type(BadCType, Req, State) ->
|
||||
reply_bad_header(415, woody_util:to_binary(["wrong content type: ", BadCType]), Req, State).
|
||||
|
||||
-spec check_accept(woody:http_header_val() | undefined, cowboy_req:req(), state()) ->
|
||||
check_result().
|
||||
check_accept(Accept, Req, State) when
|
||||
Accept =:= ?CONTENT_TYPE_THRIFT ;
|
||||
Accept =:= undefined
|
||||
->
|
||||
-spec check_accept(woody:http_header_val() | undefined, cowboy_req:req(), state()) -> check_result().
|
||||
check_accept(Accept, Req, State) when Accept =:= ?CONTENT_TYPE_THRIFT; Accept =:= undefined ->
|
||||
check_woody_headers(Req, State);
|
||||
check_accept(BadAccept, Req1, State) ->
|
||||
reply_bad_header(406, woody_util:to_binary(["wrong client accept: ", BadAccept]), Req1, State).
|
||||
|
||||
-spec check_woody_headers(cowboy_req:req(), state()) ->
|
||||
check_result().
|
||||
-spec check_woody_headers(cowboy_req:req(), state()) -> check_result().
|
||||
check_woody_headers(Req, State = #{woody_state := WoodyState0}) ->
|
||||
case get_rpc_id(Req) of
|
||||
{ok, RpcId, Req1} ->
|
||||
@ -343,23 +336,27 @@ check_woody_headers(Req, State = #{woody_state := WoodyState0}) ->
|
||||
);
|
||||
{error, BadRpcId, Req1} ->
|
||||
WoodyState1 = set_rpc_id(BadRpcId, WoodyState0),
|
||||
reply_bad_header(400, woody_util:to_binary(["bad ", ?HEADER_PREFIX, " id header"]),
|
||||
Req1, update_woody_state(State, WoodyState1, Req1)
|
||||
reply_bad_header(
|
||||
400,
|
||||
woody_util:to_binary(["bad ", ?HEADER_PREFIX, " id header"]),
|
||||
Req1,
|
||||
update_woody_state(State, WoodyState1, Req1)
|
||||
)
|
||||
end.
|
||||
|
||||
-spec get_rpc_id(cowboy_req:req()) ->
|
||||
{ok | error, woody:rpc_id(), cowboy_req:req()}.
|
||||
-spec get_rpc_id(cowboy_req:req()) -> {ok | error, woody:rpc_id(), cowboy_req:req()}.
|
||||
get_rpc_id(Req) ->
|
||||
check_ids(maps:fold(
|
||||
fun get_rpc_id/3,
|
||||
#{req => Req},
|
||||
#{
|
||||
span_id => ?HEADER_RPC_ID,
|
||||
trace_id => ?HEADER_RPC_ROOT_ID,
|
||||
parent_id => ?HEADER_RPC_PARENT_ID
|
||||
}
|
||||
)).
|
||||
check_ids(
|
||||
maps:fold(
|
||||
fun get_rpc_id/3,
|
||||
#{req => Req},
|
||||
#{
|
||||
span_id => ?HEADER_RPC_ID,
|
||||
trace_id => ?HEADER_RPC_ROOT_ID,
|
||||
parent_id => ?HEADER_RPC_PARENT_ID
|
||||
}
|
||||
)
|
||||
).
|
||||
|
||||
get_rpc_id(Id, Header, Acc = #{req := Req}) ->
|
||||
case cowboy_req:header(Header, Req) of
|
||||
@ -387,8 +384,7 @@ check_deadline_header(DeadlineBin, Req, State) ->
|
||||
reply_bad_header(400, ErrorDescription, Req, State)
|
||||
end.
|
||||
|
||||
-spec check_deadline(woody:deadline(), cowboy_req:req(), state()) ->
|
||||
check_result().
|
||||
-spec check_deadline(woody:deadline(), cowboy_req:req(), state()) -> check_result().
|
||||
check_deadline(Deadline, Req, State = #{url := Url, woody_state := WoodyState}) ->
|
||||
case woody_deadline:is_reached(Deadline) of
|
||||
true ->
|
||||
@ -405,51 +401,49 @@ check_deadline(Deadline, Req, State = #{url := Url, woody_state := WoodyState})
|
||||
check_metadata_headers(Headers, Req, update_woody_state(State, WoodyState1, Req))
|
||||
end.
|
||||
|
||||
-spec check_metadata_headers(woody:http_headers(), cowboy_req:req(), state()) ->
|
||||
check_result().
|
||||
-spec check_metadata_headers(woody:http_headers(), cowboy_req:req(), state()) -> check_result().
|
||||
check_metadata_headers(Headers, Req, State = #{woody_state := WoodyState, regexp_meta := ReMeta}) ->
|
||||
WoodyState1 = set_metadata(find_metadata(Headers, ReMeta), WoodyState),
|
||||
{ok, Req, update_woody_state(State, WoodyState1, Req)}.
|
||||
|
||||
-spec find_metadata(woody:http_headers(), re_mp()) ->
|
||||
woody_context:meta().
|
||||
-spec find_metadata(woody:http_headers(), re_mp()) -> woody_context:meta().
|
||||
find_metadata(Headers, Re) ->
|
||||
RpcId = ?HEADER_RPC_ID,
|
||||
RootId = ?HEADER_RPC_ROOT_ID,
|
||||
ParentId = ?HEADER_RPC_PARENT_ID,
|
||||
maps:fold(
|
||||
fun(H, V, Acc) when
|
||||
H =/= RpcId andalso
|
||||
H =/= RootId andalso
|
||||
H =/= ParentId
|
||||
->
|
||||
case re:replace(H, Re, "", [{return, binary}, anchored]) of
|
||||
H -> Acc;
|
||||
MetaHeader -> Acc#{MetaHeader => V}
|
||||
end;
|
||||
(_, _, Acc) -> Acc
|
||||
fun
|
||||
(H, V, Acc) when
|
||||
H =/= RpcId andalso
|
||||
H =/= RootId andalso
|
||||
H =/= ParentId
|
||||
->
|
||||
case re:replace(H, Re, "", [{return, binary}, anchored]) of
|
||||
H -> Acc;
|
||||
MetaHeader -> Acc#{MetaHeader => V}
|
||||
end;
|
||||
(_, _, Acc) ->
|
||||
Acc
|
||||
end,
|
||||
#{}, Headers).
|
||||
#{},
|
||||
Headers
|
||||
).
|
||||
|
||||
-spec set_rpc_id(woody:rpc_id(), woody_state:st()) ->
|
||||
woody_state:st().
|
||||
-spec set_rpc_id(woody:rpc_id(), woody_state:st()) -> woody_state:st().
|
||||
set_rpc_id(RpcId, WoodyState) ->
|
||||
woody_state:update_context(woody_context:new(RpcId), WoodyState).
|
||||
|
||||
-spec set_cert(cowboy_req:req(), woody_state:st()) ->
|
||||
woody_state:st().
|
||||
-spec set_cert(cowboy_req:req(), woody_state:st()) -> woody_state:st().
|
||||
set_cert(Req, WoodyState) ->
|
||||
Cert = woody_cert:from_req(Req),
|
||||
Context = woody_state:get_context(WoodyState),
|
||||
woody_state:update_context(woody_context:set_cert(Cert, Context), WoodyState).
|
||||
|
||||
-spec set_deadline(woody:deadline(), woody_state:st()) ->
|
||||
woody_state:st().
|
||||
-spec set_deadline(woody:deadline(), woody_state:st()) -> woody_state:st().
|
||||
set_deadline(Deadline, WoodyState) ->
|
||||
woody_state:add_context_deadline(Deadline, WoodyState).
|
||||
|
||||
-spec set_metadata(woody_context:meta(), woody_state:st()) ->
|
||||
woody_state:st().
|
||||
-spec set_metadata(woody_context:meta(), woody_state:st()) -> woody_state:st().
|
||||
set_metadata(Meta, WoodyState) ->
|
||||
woody_state:add_context_meta(Meta, WoodyState).
|
||||
|
||||
@ -459,8 +453,7 @@ reply_bad_header(Code, Reason, Req, State) when is_integer(Code) ->
|
||||
Req1 = reply_client_error(Code, Reason, Req, State),
|
||||
{stop, Req1, undefined}.
|
||||
|
||||
-spec reply_client_error(woody:http_code(), woody:http_header_val(), cowboy_req:req(), state()) ->
|
||||
cowboy_req:req().
|
||||
-spec reply_client_error(woody:http_code(), woody:http_header_val(), cowboy_req:req(), state()) -> cowboy_req:req().
|
||||
reply_client_error(Code, Reason, Req, #{url := Url, woody_state := WoodyState}) ->
|
||||
_ = handle_event(
|
||||
?EV_SERVER_RECEIVE,
|
||||
@ -469,8 +462,7 @@ reply_client_error(Code, Reason, Req, #{url := Url, woody_state := WoodyState})
|
||||
),
|
||||
reply(Code, set_error_headers(<<"Result Unexpected">>, Reason, Req), WoodyState).
|
||||
|
||||
-spec get_body(cowboy_req:req(), read_body_opts()) ->
|
||||
{ok, woody:http_body(), cowboy_req:req()}.
|
||||
-spec get_body(cowboy_req:req(), read_body_opts()) -> {ok, woody:http_body(), cowboy_req:req()}.
|
||||
get_body(Req, Opts) ->
|
||||
do_get_body(<<>>, Req, Opts).
|
||||
|
||||
@ -482,8 +474,7 @@ 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().
|
||||
-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) ->
|
||||
ok = woody_monitor_h:set_event(?EV_SERVICE_HANDLER_RESULT, Req),
|
||||
Buffer = ?CODEC:new(Body),
|
||||
@ -499,21 +490,19 @@ handle_request(Body, ThriftHandler = {Service, _}, WoodyState, Req) ->
|
||||
handle_decode_error(Reason, Req, WoodyState)
|
||||
end.
|
||||
|
||||
-spec handle_decode_error(_Reason, cowboy_req:req(), woody_state:st()) ->
|
||||
cowboy_req:req().
|
||||
-spec handle_decode_error(_Reason, cowboy_req:req(), woody_state:st()) -> cowboy_req:req().
|
||||
handle_decode_error(Reason, Req, WoodyState) ->
|
||||
_ = handle_event(
|
||||
?EV_INTERNAL_ERROR,
|
||||
WoodyState,
|
||||
#{
|
||||
error => <<"thrift protocol read failed">>,
|
||||
error => <<"thrift protocol read failed">>,
|
||||
reason => woody_error:format_details(Reason)
|
||||
}
|
||||
),
|
||||
handle_error(client_error(Reason), Req, WoodyState).
|
||||
|
||||
-spec client_error(_Reason) ->
|
||||
{client, woody_error:details()}.
|
||||
-spec client_error(_Reason) -> {client, woody_error:details()}.
|
||||
client_error({bad_binary_protocol_version, Version}) ->
|
||||
BinVersion = genlib:to_binary(Version),
|
||||
{client, <<"thrift: bad binary protocol version: ", BinVersion/binary>>};
|
||||
@ -530,7 +519,9 @@ 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()) ->
|
||||
cowboy_req:req() when Invocation :: {call | oneway, woody:func(), woody:args()}.
|
||||
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
|
||||
@ -544,13 +535,12 @@ handle_invocation(SeqId, {ReplyType, Function, Args}, {Service, Handler}, Req, W
|
||||
end.
|
||||
|
||||
-type call_result() ::
|
||||
ok |
|
||||
{reply, woody:result()} |
|
||||
{exception, _TypeName, _Exception} |
|
||||
{error, {system, woody_error:system_error()}}.
|
||||
ok
|
||||
| {reply, woody:result()}
|
||||
| {exception, _TypeName, _Exception}
|
||||
| {error, {system, woody_error:system_error()}}.
|
||||
|
||||
-spec handle_call(woody:handler(_), woody:service(), woody:func(), woody:args(), woody_state:st()) ->
|
||||
call_result().
|
||||
-spec handle_call(woody:handler(_), woody:service(), woody:func(), woody:args(), woody_state:st()) -> call_result().
|
||||
handle_call(Handler, Service, Function, Args, WoodyState) ->
|
||||
try
|
||||
Result = call_handler(Handler, Function, Args, WoodyState),
|
||||
@ -576,8 +566,8 @@ 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} |
|
||||
{error, {system, woody_error:system_error()}}.
|
||||
{exception, _TypeName, _Exception}
|
||||
| {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} ->
|
||||
@ -609,11 +599,9 @@ 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 Req :: cowboy_req:req().
|
||||
handle_result(Res, Service, Function, SeqId, Req, WoodyState) when
|
||||
Res == ok; element(1, Res) == reply
|
||||
->
|
||||
-spec handle_result(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} ->
|
||||
@ -640,14 +628,13 @@ get_exception_name({{struct, exception, {_Mod, 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().
|
||||
-spec handle_encode_error(_Reason, cowboy_req:req(), woody_state:st()) -> cowboy_req:req().
|
||||
handle_encode_error(Reason, Req, WoodyState) ->
|
||||
_ = handle_event(
|
||||
?EV_INTERNAL_ERROR,
|
||||
WoodyState,
|
||||
#{
|
||||
error => <<"thrift protocol write failed">>,
|
||||
error => <<"thrift protocol write failed">>,
|
||||
reason => woody_error:format_details(Reason)
|
||||
}
|
||||
),
|
||||
@ -655,14 +642,17 @@ handle_encode_error(Reason, Req, WoodyState) ->
|
||||
handle_error({system, Error}, Req, WoodyState).
|
||||
|
||||
add_ev_meta(WoodyState, Service = {_, ServiceName}, ReplyType, Function, Args) ->
|
||||
woody_state:add_ev_meta(#{
|
||||
service => ServiceName,
|
||||
service_schema => Service,
|
||||
function => Function,
|
||||
args => Args,
|
||||
type => get_rpc_reply_type(ReplyType),
|
||||
deadline => woody_context:get_deadline(woody_state:get_context(WoodyState))
|
||||
}, WoodyState).
|
||||
woody_state:add_ev_meta(
|
||||
#{
|
||||
service => ServiceName,
|
||||
service_schema => Service,
|
||||
function => Function,
|
||||
args => Args,
|
||||
type => get_rpc_reply_type(ReplyType),
|
||||
deadline => woody_context:get_deadline(woody_state:get_context(WoodyState))
|
||||
},
|
||||
WoodyState
|
||||
).
|
||||
|
||||
get_rpc_reply_type(oneway) -> cast;
|
||||
get_rpc_reply_type(call) -> call.
|
||||
@ -691,8 +681,7 @@ handle_error({system, {external, resource_unavailable, Details}}, Req, WoodyStat
|
||||
handle_error({system, {external, result_unknown, Details}}, Req, WoodyState) ->
|
||||
reply(502, set_error_headers(<<"Result Unknown">>, Details, Req), WoodyState).
|
||||
|
||||
-spec set_error_headers(woody:http_header_val(), woody:http_header_val(), cowboy_req:req()) ->
|
||||
cowboy_req:req().
|
||||
-spec set_error_headers(woody:http_header_val(), woody:http_header_val(), cowboy_req:req()) -> cowboy_req:req().
|
||||
set_error_headers(Class, Reason, Req) ->
|
||||
Headers = #{
|
||||
?HEADER_E_CLASS => Class,
|
||||
@ -700,8 +689,7 @@ set_error_headers(Class, Reason, Req) ->
|
||||
},
|
||||
cowboy_req:set_resp_headers(Headers, Req).
|
||||
|
||||
-spec reply(woody:http_code(), cowboy_req:req(), woody_state:st()) ->
|
||||
cowboy_req:req().
|
||||
-spec reply(woody:http_code(), cowboy_req:req(), woody_state:st()) -> cowboy_req:req().
|
||||
reply(200, Req, WoodyState) ->
|
||||
do_reply(200, cowboy_req:set_resp_header(<<"content-type">>, ?CONTENT_TYPE_THRIFT, Req), WoodyState);
|
||||
reply(Code, Req, WoodyState) ->
|
||||
|
@ -16,17 +16,17 @@
|
||||
|
||||
%% Types
|
||||
-type st() :: #{
|
||||
context := woody_context:ctx(),
|
||||
context := woody_context:ctx(),
|
||||
ev_handler := woody:ev_handlers(),
|
||||
ev_meta := woody_event_handler:meta()
|
||||
ev_meta := woody_event_handler:meta()
|
||||
}.
|
||||
|
||||
-export_type([st/0]).
|
||||
|
||||
%%
|
||||
%% API
|
||||
%%
|
||||
-spec new(woody:role(), woody_context:ctx(), woody:ev_handlers()) ->
|
||||
st().
|
||||
-spec new(woody:role(), woody_context:ctx(), woody:ev_handlers()) -> st().
|
||||
new(Role, Context, EvHandler) ->
|
||||
Deadline = woody_context:get_deadline(Context),
|
||||
Metadata = woody_context:get_meta(Context),
|
||||
@ -35,73 +35,62 @@ new(Role, Context, EvHandler) ->
|
||||
add_metadata_to_ev_meta(
|
||||
Metadata,
|
||||
#{
|
||||
context => Context,
|
||||
context => Context,
|
||||
ev_handler => EvHandler,
|
||||
ev_meta => #{role => Role, execution_start_time => os:system_time(millisecond)}
|
||||
ev_meta => #{role => Role, execution_start_time => os:system_time(millisecond)}
|
||||
}
|
||||
)
|
||||
).
|
||||
|
||||
-spec get_context(st()) ->
|
||||
woody_context:ctx().
|
||||
-spec get_context(st()) -> woody_context:ctx().
|
||||
get_context(#{context := Context}) ->
|
||||
Context.
|
||||
|
||||
-spec get_ev_handler(st()) ->
|
||||
woody:ev_handlers().
|
||||
-spec get_ev_handler(st()) -> woody:ev_handlers().
|
||||
get_ev_handler(#{ev_handler := Handler}) ->
|
||||
Handler.
|
||||
|
||||
-spec get_ev_meta(st()) ->
|
||||
woody_event_handler:meta().
|
||||
-spec get_ev_meta(st()) -> woody_event_handler:meta().
|
||||
get_ev_meta(#{ev_meta := Meta}) ->
|
||||
Meta.
|
||||
|
||||
-spec add_ev_meta(woody_event_handler:meta(), st()) ->
|
||||
st().
|
||||
-spec add_ev_meta(woody_event_handler:meta(), st()) -> st().
|
||||
add_ev_meta(ExtraMeta, State = #{ev_meta := Meta}) ->
|
||||
State#{ev_meta => maps:merge(Meta, ExtraMeta)}.
|
||||
|
||||
-spec update_context(woody_context:ctx(), st()) ->
|
||||
st().
|
||||
-spec update_context(woody_context:ctx(), st()) -> st().
|
||||
update_context(NewContext, State) ->
|
||||
State#{context => NewContext}.
|
||||
|
||||
-spec add_context_meta(woody_context:meta(), st()) ->
|
||||
st().
|
||||
-spec add_context_meta(woody_context:meta(), st()) -> st().
|
||||
add_context_meta(ContextMeta, State) ->
|
||||
add_metadata_to_ev_meta(ContextMeta, add_metadata_to_context(ContextMeta, State)).
|
||||
|
||||
-spec add_context_deadline(woody:deadline(), st()) ->
|
||||
st().
|
||||
-spec add_context_deadline(woody:deadline(), st()) -> st().
|
||||
add_context_deadline(Deadline, State) ->
|
||||
add_deadline_to_ev_meta(Deadline, add_deadline_to_context(Deadline, State)).
|
||||
|
||||
%%
|
||||
%% Internal functions
|
||||
%%
|
||||
-spec add_metadata_to_context(woody_context:meta(), st()) ->
|
||||
st().
|
||||
-spec add_metadata_to_context(woody_context:meta(), st()) -> st().
|
||||
add_metadata_to_context(ContextMeta, State) ->
|
||||
update_context(
|
||||
woody_context:add_meta(get_context(State), ContextMeta),
|
||||
State
|
||||
).
|
||||
|
||||
-spec add_deadline_to_context(woody:deadline(), st()) ->
|
||||
st().
|
||||
-spec add_deadline_to_context(woody:deadline(), st()) -> st().
|
||||
add_deadline_to_context(Deadline, State) ->
|
||||
update_context(
|
||||
woody_context:set_deadline(Deadline, get_context(State)),
|
||||
State
|
||||
).
|
||||
|
||||
-spec add_metadata_to_ev_meta(woody_context:meta(), st()) ->
|
||||
st().
|
||||
-spec add_metadata_to_ev_meta(woody_context:meta(), st()) -> st().
|
||||
add_metadata_to_ev_meta(ContextMeta, State) ->
|
||||
add_ev_meta(#{metadata => ContextMeta}, State).
|
||||
|
||||
-spec add_deadline_to_ev_meta(woody:deadline(), st()) ->
|
||||
st().
|
||||
-spec add_deadline_to_ev_meta(woody:deadline(), st()) -> st().
|
||||
add_deadline_to_ev_meta(Deadline, State) ->
|
||||
add_ev_meta(#{deadline => Deadline}, State).
|
||||
|
@ -7,6 +7,7 @@
|
||||
-export([env/1]).
|
||||
|
||||
-behaviour(cowboy_stream).
|
||||
|
||||
-export([init/3]).
|
||||
-export([data/4]).
|
||||
-export([info/3]).
|
||||
@ -25,8 +26,7 @@
|
||||
event_handler := woody:ev_handlers()
|
||||
}.
|
||||
|
||||
-spec env(options()) ->
|
||||
cowboy_middleware:env().
|
||||
-spec env(options()) -> cowboy_middleware:env().
|
||||
env(Opts = #{}) ->
|
||||
EvHandler = maps:get(event_handler, Opts),
|
||||
#{?MODULE => EvHandler}.
|
||||
@ -42,8 +42,7 @@ trace_request(Req, EvHandler) ->
|
||||
trace_response(Req, {response, Code, Headers, Body}, EvHandler) ->
|
||||
trace_resp(genlib_app:env(woody, ?TRACER), Req, Code, Headers, Body, EvHandler).
|
||||
|
||||
-spec trace_req(true, cowboy_req:req(), woody:ev_handlers()) ->
|
||||
cowboy_req:req().
|
||||
-spec trace_req(true, cowboy_req:req(), woody:ev_handlers()) -> cowboy_req:req().
|
||||
trace_req(true, Req, EvHandler) ->
|
||||
Url = unicode:characters_to_binary(cowboy_req:uri(Req)),
|
||||
Headers = cowboy_req:headers(Req),
|
||||
@ -51,9 +50,9 @@ trace_req(true, Req, EvHandler) ->
|
||||
% No body here since with Cowboy 2 we can consume it only once.
|
||||
% Ideally we would need to embed tracing directly into handler itself.
|
||||
Meta = #{
|
||||
role => server,
|
||||
event => <<"http request received">>,
|
||||
url => Url,
|
||||
role => server,
|
||||
event => <<"http request received">>,
|
||||
url => Url,
|
||||
headers => Headers
|
||||
},
|
||||
_ = woody_event_handler:handle_event(EvHandler, ?EV_TRACE, undefined, Meta),
|
||||
@ -68,15 +67,14 @@ trace_req(_, Req, _) ->
|
||||
woody:http_headers(),
|
||||
woody:http_body(),
|
||||
woody:ev_handlers()
|
||||
) ->
|
||||
cowboy_req:req().
|
||||
) -> cowboy_req:req().
|
||||
trace_resp(true, Req, Code, Headers, Body, EvHandler) ->
|
||||
_ = woody_event_handler:handle_event(EvHandler, ?EV_TRACE, undefined, #{
|
||||
role => server,
|
||||
event => <<"http response send">>,
|
||||
code => Code,
|
||||
role => server,
|
||||
event => <<"http response send">>,
|
||||
code => Code,
|
||||
headers => Headers,
|
||||
body => Body
|
||||
body => Body
|
||||
}),
|
||||
Req;
|
||||
trace_resp(_, Req, _, _, _, _) ->
|
||||
@ -84,22 +82,22 @@ trace_resp(_, Req, _, _, _, _) ->
|
||||
|
||||
%% callbacks
|
||||
|
||||
-spec init(cowboy_stream:streamid(), cowboy_req:req(), cowboy:opts())
|
||||
-> {cowboy_stream:commands(), state()}.
|
||||
-spec init(cowboy_stream:streamid(), cowboy_req:req(), cowboy:opts()) -> {cowboy_stream:commands(), state()}.
|
||||
init(StreamID, Req, Opts) ->
|
||||
TraceOpts = extract_trace_options(Opts),
|
||||
_ = trace_request(Req, TraceOpts),
|
||||
{Commands0, Next} = cowboy_stream:init(StreamID, Req, Opts),
|
||||
{Commands0, #{next => Next, req => Req, opts => TraceOpts}}.
|
||||
|
||||
-spec data(cowboy_stream:streamid(), cowboy_stream:fin(), cowboy_req:resp_body(), State)
|
||||
-> {cowboy_stream:commands(), State} when State::state().
|
||||
-spec data(cowboy_stream:streamid(), cowboy_stream:fin(), cowboy_req:resp_body(), State) ->
|
||||
{cowboy_stream:commands(), State}
|
||||
when
|
||||
State :: state().
|
||||
data(StreamID, IsFin, Data, #{next := Next0} = State) ->
|
||||
{Commands0, Next} = cowboy_stream:data(StreamID, IsFin, Data, Next0),
|
||||
{Commands0, State#{next => Next}}.
|
||||
|
||||
-spec info(cowboy_stream:streamid(), any(), State)
|
||||
-> {cowboy_stream:commands(), State} when State::state().
|
||||
-spec info(cowboy_stream:streamid(), any(), State) -> {cowboy_stream:commands(), State} when State :: state().
|
||||
info(StreamID, {response, _, _, _} = Info, #{next := Next0, req := Req, opts := TraceOpts} = State) ->
|
||||
_ = trace_response(Req, Info, TraceOpts),
|
||||
{Commands0, Next} = cowboy_stream:info(StreamID, Info, Next0),
|
||||
@ -113,9 +111,14 @@ terminate(StreamID, Reason, #{next := Next}) ->
|
||||
cowboy_stream:terminate(StreamID, Reason, Next).
|
||||
|
||||
%% At this point we have both request (it's part actually) and response, so we might track them in one place
|
||||
-spec early_error(cowboy_stream:streamid(), cowboy_stream:reason(),
|
||||
cowboy_stream:partial_req(), Resp, cowboy:opts()) -> Resp
|
||||
when Resp::cowboy_stream:resp_command().
|
||||
-spec early_error(
|
||||
cowboy_stream:streamid(),
|
||||
cowboy_stream:reason(),
|
||||
cowboy_stream:partial_req(),
|
||||
Resp,
|
||||
cowboy:opts()
|
||||
) -> Resp when
|
||||
Resp :: cowboy_stream:resp_command().
|
||||
early_error(StreamID, Reason, PartialReq, Resp, Opts) ->
|
||||
TraceOpts = extract_trace_options(Opts),
|
||||
_ = trace_request(PartialReq, TraceOpts),
|
||||
|
@ -14,28 +14,25 @@
|
||||
%%
|
||||
%% Internal API
|
||||
%%
|
||||
-spec get_protocol_handler(woody:role(), map()) ->
|
||||
module() | no_return().
|
||||
-spec get_protocol_handler(woody:role(), map()) -> module() | no_return().
|
||||
get_protocol_handler(_Role, #{protocol_handler_override := Module}) when is_atom(Module) ->
|
||||
Module;
|
||||
get_protocol_handler(Role, Opts) ->
|
||||
Protocol = genlib_map:get(protocol, Opts, thrift),
|
||||
Protocol = genlib_map:get(protocol, Opts, thrift),
|
||||
Transport = genlib_map:get(transport, Opts, http),
|
||||
case {Role, Protocol, Transport} of
|
||||
{client, thrift, http} -> woody_client_thrift_v2;
|
||||
{server, thrift, http} -> woody_server_thrift_v2;
|
||||
_ -> error(badarg, [Role, Opts])
|
||||
_ -> error(badarg, [Role, Opts])
|
||||
end.
|
||||
|
||||
-spec get_mod_opts(woody:handler(woody:options())) ->
|
||||
{module(), woody:options()}.
|
||||
-spec get_mod_opts(woody:handler(woody:options())) -> {module(), woody:options()}.
|
||||
get_mod_opts(Handler = {Mod, _Opts}) when is_atom(Mod) ->
|
||||
Handler;
|
||||
get_mod_opts(Mod) when is_atom(Mod) ->
|
||||
{Mod, ?DEFAULT_HANDLER_OPTS}.
|
||||
|
||||
-spec to_binary(atom() | list() | binary()) ->
|
||||
binary().
|
||||
-spec to_binary(atom() | list() | binary()) -> binary().
|
||||
to_binary(Reason) when is_list(Reason) ->
|
||||
to_binary(Reason, <<>>);
|
||||
to_binary(Reason) ->
|
||||
@ -47,12 +44,10 @@ to_binary([Part | T], Reason) ->
|
||||
BinPart = genlib:to_binary(Part),
|
||||
to_binary(T, <<Reason/binary, BinPart/binary>>).
|
||||
|
||||
-spec get_rpc_type(woody:service(), woody:func()) ->
|
||||
woody:rpc_type().
|
||||
-spec get_rpc_type(woody:service(), woody:func()) -> woody:rpc_type().
|
||||
get_rpc_type({Module, Service}, Function) ->
|
||||
get_rpc_reply_type(Module:function_info(Service, Function, reply_type)).
|
||||
|
||||
-spec get_rpc_reply_type(_ThriftReplyType) ->
|
||||
woody:rpc_type().
|
||||
-spec get_rpc_reply_type(_ThriftReplyType) -> woody:rpc_type().
|
||||
get_rpc_reply_type(oneway_void) -> cast;
|
||||
get_rpc_reply_type(_) -> call.
|
||||
|
@ -8,105 +8,98 @@
|
||||
bench_thrift_formatter/2
|
||||
]).
|
||||
|
||||
-type input() :: {woody_thrift_formatter:event_meta(), woody:rpc_id()}.
|
||||
-type input() :: {woody_event_handler:event_meta(), woody:rpc_id()}.
|
||||
|
||||
-spec input() ->
|
||||
input().
|
||||
-spec input() -> input().
|
||||
input() ->
|
||||
Meta = #{
|
||||
args => [{mg_stateproc_CallArgs,
|
||||
{bin,
|
||||
<<131, 104, 4, 100, 0, 11, 116, 104, 114, 105, 102, 116, 95, 99, 97, 108, 108,
|
||||
100, 0, 16, 112, 97, 114, 116, 121, 95, 109, 97, 110, 97, 103, 101, 109, 101,
|
||||
110, 116, 104, 2, 100, 0, 15, 80, 97, 114, 116, 121, 77, 97, 110, 97, 103,
|
||||
101, 109, 101, 110, 116, 100, 0, 11, 67, 114, 101, 97, 116, 101, 67, 108, 97,
|
||||
105, 109, 109, 0, 0, 2, 145, 11, 0, 2, 0, 0, 0, 11, 49, 67, 83, 72, 84, 104, 84,
|
||||
69, 74, 56, 52, 15, 0, 3, 12, 0, 0, 0, 4, 12, 0, 4, 11, 0, 1, 0, 0, 0, 11, 49, 67,
|
||||
83, 72, 84, 106, 75, 108, 51, 52, 75, 12, 0, 2, 12, 0, 1, 12, 0, 2, 8, 0, 1, 0, 0,
|
||||
0, 1, 0, 12, 0, 3, 8, 0, 1, 0, 0, 0, 1, 0, 12, 0, 1, 12, 0, 1, 12, 0, 1, 11, 0, 1, 0, 0,
|
||||
0, 18, 72, 111, 111, 102, 115, 32, 38, 32, 72, 111, 114, 110, 115, 32, 79, 74,
|
||||
83, 67, 11, 0, 2, 0, 0, 0, 10, 49, 50, 51, 52, 53, 48, 57, 56, 55, 54, 11, 0, 3, 0,
|
||||
0, 0, 13, 49, 50, 49, 51, 52, 53, 54, 55, 56, 57, 48, 49, 50, 11, 0, 4, 0, 0, 0,
|
||||
48, 78, 101, 122, 97, 104, 117, 97, 108, 99, 111, 121, 111, 116, 108, 32, 49,
|
||||
48, 57, 32, 80, 105, 115, 111, 32, 56, 44, 32, 67, 101, 110, 116, 114, 111,
|
||||
44, 32, 48, 54, 48, 56, 50, 44, 32, 77, 69, 88, 73, 67, 79, 11, 0, 5, 0, 0, 0, 3,
|
||||
78, 97, 78, 11, 0, 6, 0, 0, 0, 8, 68, 105, 114, 101, 99, 116, 111, 114, 11, 0, 7,
|
||||
0, 0, 0, 7, 83, 111, 109, 101, 111, 110, 101, 11, 0, 8, 0, 0, 0, 13, 49, 48, 48,
|
||||
36, 32, 98, 97, 110, 107, 110, 111, 116, 101, 12, 0, 9, 11, 0, 1, 0, 0, 0, 19,
|
||||
52, 50, 55, 54, 51, 48, 48, 48, 49, 48, 57, 48, 56, 51, 49, 50, 56, 57, 51, 11,
|
||||
0, 2, 0, 0, 0, 8, 83, 111, 109, 101, 66, 97, 110, 107, 11, 0, 3, 0, 0, 0, 9, 49,
|
||||
50, 51, 49, 50, 57, 56, 55, 54, 11, 0, 4, 0, 0, 0, 8, 54, 54, 54, 52, 50, 54, 54,
|
||||
54, 0, 0, 0, 0, 0, 0, 0, 0, 12, 0, 4, 11, 0, 1, 0, 0, 0, 11, 49, 67, 83, 72, 84, 106,
|
||||
75, 108, 51, 52, 75, 12, 0, 2, 12, 0, 4, 11, 0, 1, 0, 0, 0, 11, 49, 67, 83, 72, 84,
|
||||
106, 75, 108, 51, 52, 76, 12, 0, 2, 12, 0, 1, 12, 0, 1, 11, 0, 1, 0, 0, 0, 3, 82,
|
||||
85, 66, 0, 12, 0, 2, 12, 0, 1, 11, 0, 1, 0, 0, 0, 19, 52, 50, 55, 54, 51, 48, 48,
|
||||
48, 49, 48, 57, 48, 56, 51, 49, 50, 56, 57, 51, 11, 0, 2, 0, 0, 0, 8, 83, 111,
|
||||
109, 101, 66, 97, 110, 107, 11, 0, 3, 0, 0, 0, 9, 49, 50, 51, 49, 50, 57, 56, 55,
|
||||
54, 11, 0, 4, 0, 0, 0, 8, 54, 54, 54, 52, 50, 54, 54, 54, 0, 0, 0, 0, 0, 0, 0, 0, 12,
|
||||
0, 6, 11, 0, 1, 0, 0, 0, 11, 49, 67, 83, 72, 84, 106, 75, 108, 51, 52, 77, 12, 0,
|
||||
2, 12, 0, 5, 12, 0, 1, 8, 0, 1, 0, 0, 0, 1, 0, 12, 0, 6, 11, 0, 1, 0, 0, 0, 0, 0, 12, 0,
|
||||
2, 11, 0, 1, 0, 0, 0, 17, 66, 97, 116, 116, 108, 101, 32, 82, 101, 97, 100, 121,
|
||||
32, 83, 104, 111, 112, 0, 11, 0, 3, 0, 0, 0, 11, 49, 67, 83, 72, 84, 106, 75,
|
||||
108, 51, 52, 75, 11, 0, 4, 0, 0, 0, 11, 49, 67, 83, 72, 84, 106, 75, 108, 51, 52,
|
||||
76, 0, 0, 0, 0, 12, 0, 6, 11, 0, 1, 0, 0, 0, 11, 49, 67, 83, 72, 84, 106, 75, 108,
|
||||
51, 52, 77, 12, 0, 2, 12, 0, 12, 12, 0, 1, 11, 0, 1, 0, 0, 0, 3, 82, 85, 66, 0, 0, 0,
|
||||
0, 0, 0>>},
|
||||
{mg_stateproc_Machine, <<"party">>, <<"1CSHThTEJ84">>,
|
||||
[{mg_stateproc_Event, 1, <<"2019-08-13T07:52:11.080519Z">>,
|
||||
undefined,
|
||||
{arr,
|
||||
[{obj,
|
||||
#{{str, <<"ct">>} =>
|
||||
{str, <<"application/x-erlang-binary">>},
|
||||
{str, <<"vsn">>} => {i, 6}}},
|
||||
{bin,
|
||||
<<131, 104, 2, 100, 0, 13, 112, 97, 114, 116, 121, 95,
|
||||
99, 104, 97, 110, 103, 101, 115, 108, 0, 0, 0, 2, 104,
|
||||
2, 100, 0, 13, 112, 97, 114, 116, 121, 95, 99, 114,
|
||||
101, 97, 116, 101, 100, 104, 4, 100, 0, 20, 112, 97,
|
||||
121, 112, 114, 111, 99, 95, 80, 97, 114, 116, 121, 67,
|
||||
114, 101, 97, 116, 101, 100, 109, 0, 0, 0, 11, 49, 67,
|
||||
83, 72, 84, 104, 84, 69, 74, 56, 52, 104, 2, 100, 0, 23,
|
||||
100, 111, 109, 97, 105, 110, 95, 80, 97, 114, 116,
|
||||
121, 67, 111, 110, 116, 97, 99, 116, 73, 110, 102,
|
||||
111, 109, 0, 0, 0, 12, 104, 103, 95, 99, 116, 95, 104,
|
||||
101, 108, 112, 101, 114, 109, 0, 0, 0, 27, 50, 48, 49,
|
||||
57, 45, 48, 56, 45, 49, 51, 84, 48, 55, 58, 53, 50, 58,
|
||||
49, 49, 46, 48, 55, 50, 56, 51, 53, 90, 104, 2, 100, 0,
|
||||
16, 114, 101, 118, 105, 115, 105, 111, 110, 95, 99,
|
||||
104, 97, 110, 103, 101, 100, 104, 3, 100, 0, 28, 112,
|
||||
97, 121, 112, 114, 111, 99, 95, 80, 97, 114, 116, 121,
|
||||
82, 101, 118, 105, 115, 105, 111, 110, 67, 104, 97,
|
||||
110, 103, 101, 100, 109, 0, 0, 0, 27, 50, 48, 49, 57,
|
||||
45, 48, 56, 45, 49, 51, 84, 48, 55, 58, 53, 50, 58, 49,
|
||||
49, 46, 48, 55, 50, 56, 51, 53, 90, 97, 0, 106>>}]}}],
|
||||
{mg_stateproc_HistoryRange, undefined, 10, backward},
|
||||
{mg_stateproc_Content, undefined,
|
||||
{obj,
|
||||
#{{str, <<"aux_state">>} =>
|
||||
{bin,
|
||||
<<131, 116, 0, 0, 0, 2, 100, 0, 20, 112, 97, 114, 116,
|
||||
121, 95, 114, 101, 118, 105, 115, 105, 111, 110, 95,
|
||||
105, 110, 100, 101, 120, 116, 0, 0, 0, 0, 100, 0, 14,
|
||||
115, 110, 97, 112, 115, 104, 111, 116, 95, 105, 110,
|
||||
100, 101, 120, 106>>},
|
||||
{str, <<"ct">>} => {str, <<"application/x-erlang-binary">>}}}},
|
||||
undefined,
|
||||
{obj,
|
||||
#{{str, <<"aux_state">>} =>
|
||||
args => {
|
||||
{mg_stateproc_CallArgs,
|
||||
{bin,
|
||||
<<131, 116, 0, 0, 0, 2, 100, 0, 20, 112, 97, 114, 116, 121, 95,
|
||||
114, 101, 118, 105, 115, 105, 111, 110, 95, 105, 110, 100,
|
||||
101, 120, 116, 0, 0, 0, 0, 100, 0, 14, 115, 110, 97, 112,
|
||||
115, 104, 111, 116, 95, 105, 110, 100, 101, 120, 106>>},
|
||||
{str, <<"ct">>} =>
|
||||
{str, <<"application/x-erlang-binary">>}}}}}],
|
||||
<<131, 104, 4, 100, 0, 11, 116, 104, 114, 105, 102, 116, 95, 99, 97, 108, 108, 100, 0, 16, 112, 97,
|
||||
114, 116, 121, 95, 109, 97, 110, 97, 103, 101, 109, 101, 110, 116, 104, 2, 100, 0, 15, 80, 97,
|
||||
114, 116, 121, 77, 97, 110, 97, 103, 101, 109, 101, 110, 116, 100, 0, 11, 67, 114, 101, 97, 116,
|
||||
101, 67, 108, 97, 105, 109, 109, 0, 0, 2, 145, 11, 0, 2, 0, 0, 0, 11, 49, 67, 83, 72, 84, 104,
|
||||
84, 69, 74, 56, 52, 15, 0, 3, 12, 0, 0, 0, 4, 12, 0, 4, 11, 0, 1, 0, 0, 0, 11, 49, 67, 83, 72,
|
||||
84, 106, 75, 108, 51, 52, 75, 12, 0, 2, 12, 0, 1, 12, 0, 2, 8, 0, 1, 0, 0, 0, 1, 0, 12, 0, 3, 8,
|
||||
0, 1, 0, 0, 0, 1, 0, 12, 0, 1, 12, 0, 1, 12, 0, 1, 11, 0, 1, 0, 0, 0, 18, 72, 111, 111, 102,
|
||||
115, 32, 38, 32, 72, 111, 114, 110, 115, 32, 79, 74, 83, 67, 11, 0, 2, 0, 0, 0, 10, 49, 50, 51,
|
||||
52, 53, 48, 57, 56, 55, 54, 11, 0, 3, 0, 0, 0, 13, 49, 50, 49, 51, 52, 53, 54, 55, 56, 57, 48,
|
||||
49, 50, 11, 0, 4, 0, 0, 0, 48, 78, 101, 122, 97, 104, 117, 97, 108, 99, 111, 121, 111, 116, 108,
|
||||
32, 49, 48, 57, 32, 80, 105, 115, 111, 32, 56, 44, 32, 67, 101, 110, 116, 114, 111, 44, 32, 48,
|
||||
54, 48, 56, 50, 44, 32, 77, 69, 88, 73, 67, 79, 11, 0, 5, 0, 0, 0, 3, 78, 97, 78, 11, 0, 6, 0,
|
||||
0, 0, 8, 68, 105, 114, 101, 99, 116, 111, 114, 11, 0, 7, 0, 0, 0, 7, 83, 111, 109, 101, 111,
|
||||
110, 101, 11, 0, 8, 0, 0, 0, 13, 49, 48, 48, 36, 32, 98, 97, 110, 107, 110, 111, 116, 101, 12,
|
||||
0, 9, 11, 0, 1, 0, 0, 0, 19, 52, 50, 55, 54, 51, 48, 48, 48, 49, 48, 57, 48, 56, 51, 49, 50, 56,
|
||||
57, 51, 11, 0, 2, 0, 0, 0, 8, 83, 111, 109, 101, 66, 97, 110, 107, 11, 0, 3, 0, 0, 0, 9, 49, 50,
|
||||
51, 49, 50, 57, 56, 55, 54, 11, 0, 4, 0, 0, 0, 8, 54, 54, 54, 52, 50, 54, 54, 54, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 12, 0, 4, 11, 0, 1, 0, 0, 0, 11, 49, 67, 83, 72, 84, 106, 75, 108, 51, 52, 75, 12, 0,
|
||||
2, 12, 0, 4, 11, 0, 1, 0, 0, 0, 11, 49, 67, 83, 72, 84, 106, 75, 108, 51, 52, 76, 12, 0, 2, 12,
|
||||
0, 1, 12, 0, 1, 11, 0, 1, 0, 0, 0, 3, 82, 85, 66, 0, 12, 0, 2, 12, 0, 1, 11, 0, 1, 0, 0, 0, 19,
|
||||
52, 50, 55, 54, 51, 48, 48, 48, 49, 48, 57, 48, 56, 51, 49, 50, 56, 57, 51, 11, 0, 2, 0, 0, 0,
|
||||
8, 83, 111, 109, 101, 66, 97, 110, 107, 11, 0, 3, 0, 0, 0, 9, 49, 50, 51, 49, 50, 57, 56, 55,
|
||||
54, 11, 0, 4, 0, 0, 0, 8, 54, 54, 54, 52, 50, 54, 54, 54, 0, 0, 0, 0, 0, 0, 0, 0, 12, 0, 6, 11,
|
||||
0, 1, 0, 0, 0, 11, 49, 67, 83, 72, 84, 106, 75, 108, 51, 52, 77, 12, 0, 2, 12, 0, 5, 12, 0, 1,
|
||||
8, 0, 1, 0, 0, 0, 1, 0, 12, 0, 6, 11, 0, 1, 0, 0, 0, 0, 0, 12, 0, 2, 11, 0, 1, 0, 0, 0, 17, 66,
|
||||
97, 116, 116, 108, 101, 32, 82, 101, 97, 100, 121, 32, 83, 104, 111, 112, 0, 11, 0, 3, 0, 0, 0,
|
||||
11, 49, 67, 83, 72, 84, 106, 75, 108, 51, 52, 75, 11, 0, 4, 0, 0, 0, 11, 49, 67, 83, 72, 84,
|
||||
106, 75, 108, 51, 52, 76, 0, 0, 0, 0, 12, 0, 6, 11, 0, 1, 0, 0, 0, 11, 49, 67, 83, 72, 84, 106,
|
||||
75, 108, 51, 52, 77, 12, 0, 2, 12, 0, 12, 12, 0, 1, 11, 0, 1, 0, 0, 0, 3, 82, 85, 66, 0, 0, 0,
|
||||
0, 0, 0>>},
|
||||
{mg_stateproc_Machine, <<"party">>, <<"1CSHThTEJ84">>,
|
||||
[
|
||||
{mg_stateproc_Event, 1, <<"2019-08-13T07:52:11.080519Z">>, undefined,
|
||||
{arr, [
|
||||
{obj, #{
|
||||
{str, <<"ct">>} =>
|
||||
{str, <<"application/x-erlang-binary">>},
|
||||
{str, <<"vsn">>} => {i, 6}
|
||||
}},
|
||||
{bin,
|
||||
<<131, 104, 2, 100, 0, 13, 112, 97, 114, 116, 121, 95, 99, 104, 97, 110, 103, 101,
|
||||
115, 108, 0, 0, 0, 2, 104, 2, 100, 0, 13, 112, 97, 114, 116, 121, 95, 99, 114,
|
||||
101, 97, 116, 101, 100, 104, 4, 100, 0, 20, 112, 97, 121, 112, 114, 111, 99, 95,
|
||||
80, 97, 114, 116, 121, 67, 114, 101, 97, 116, 101, 100, 109, 0, 0, 0, 11, 49,
|
||||
67, 83, 72, 84, 104, 84, 69, 74, 56, 52, 104, 2, 100, 0, 23, 100, 111, 109, 97,
|
||||
105, 110, 95, 80, 97, 114, 116, 121, 67, 111, 110, 116, 97, 99, 116, 73, 110,
|
||||
102, 111, 109, 0, 0, 0, 12, 104, 103, 95, 99, 116, 95, 104, 101, 108, 112, 101,
|
||||
114, 109, 0, 0, 0, 27, 50, 48, 49, 57, 45, 48, 56, 45, 49, 51, 84, 48, 55, 58,
|
||||
53, 50, 58, 49, 49, 46, 48, 55, 50, 56, 51, 53, 90, 104, 2, 100, 0, 16, 114,
|
||||
101, 118, 105, 115, 105, 111, 110, 95, 99, 104, 97, 110, 103, 101, 100, 104, 3,
|
||||
100, 0, 28, 112, 97, 121, 112, 114, 111, 99, 95, 80, 97, 114, 116, 121, 82, 101,
|
||||
118, 105, 115, 105, 111, 110, 67, 104, 97, 110, 103, 101, 100, 109, 0, 0, 0, 27,
|
||||
50, 48, 49, 57, 45, 48, 56, 45, 49, 51, 84, 48, 55, 58, 53, 50, 58, 49, 49, 46,
|
||||
48, 55, 50, 56, 51, 53, 90, 97, 0, 106>>}
|
||||
]}}
|
||||
],
|
||||
{mg_stateproc_HistoryRange, undefined, 10, backward},
|
||||
{mg_stateproc_Content, undefined,
|
||||
{obj, #{
|
||||
{str, <<"aux_state">>} =>
|
||||
{bin,
|
||||
<<131, 116, 0, 0, 0, 2, 100, 0, 20, 112, 97, 114, 116, 121, 95, 114, 101, 118, 105,
|
||||
115, 105, 111, 110, 95, 105, 110, 100, 101, 120, 116, 0, 0, 0, 0, 100, 0, 14,
|
||||
115, 110, 97, 112, 115, 104, 111, 116, 95, 105, 110, 100, 101, 120, 106>>},
|
||||
{str, <<"ct">>} => {str, <<"application/x-erlang-binary">>}
|
||||
}}},
|
||||
undefined,
|
||||
{obj, #{
|
||||
{str, <<"aux_state">>} =>
|
||||
{bin,
|
||||
<<131, 116, 0, 0, 0, 2, 100, 0, 20, 112, 97, 114, 116, 121, 95, 114, 101, 118, 105, 115,
|
||||
105, 111, 110, 95, 105, 110, 100, 101, 120, 116, 0, 0, 0, 0, 100, 0, 14, 115, 110,
|
||||
97, 112, 115, 104, 111, 116, 95, 105, 110, 100, 101, 120, 106>>},
|
||||
{str, <<"ct">>} =>
|
||||
{str, <<"application/x-erlang-binary">>}
|
||||
}}}}
|
||||
},
|
||||
deadline => {{{2019, 8, 13}, {7, 52, 41}}, 105},
|
||||
execution_start_time => 1565682731109,
|
||||
function => 'ProcessCall',
|
||||
metadata =>
|
||||
#{<<"user-identity.id">> => <<"1CSHThTEJ84">>,
|
||||
<<"user-identity.realm">> => <<"external">>},
|
||||
metadata => #{
|
||||
<<"user-identity.id">> => <<"1CSHThTEJ84">>,
|
||||
<<"user-identity.realm">> => <<"external">>
|
||||
},
|
||||
role => server,
|
||||
service => 'Processor',
|
||||
service_schema => {mg_proto_state_processing_thrift, 'Processor'},
|
||||
@ -119,26 +112,22 @@ input() ->
|
||||
},
|
||||
{Meta, RpcID}.
|
||||
|
||||
-spec iolib_formatter({input, _State}) ->
|
||||
input().
|
||||
-spec iolib_formatter({input, _State}) -> input().
|
||||
iolib_formatter({input, _}) ->
|
||||
input().
|
||||
|
||||
-spec thrift_formatter({input, _State}) ->
|
||||
input().
|
||||
-spec thrift_formatter({input, _State}) -> input().
|
||||
thrift_formatter({input, _}) ->
|
||||
input().
|
||||
|
||||
-spec bench_iolib_formatter(input(), _State) ->
|
||||
term().
|
||||
-spec bench_iolib_formatter(input(), _State) -> term().
|
||||
bench_iolib_formatter({Meta, RpcID}, _) ->
|
||||
format_msg(format_event_iolib(Meta, RpcID)).
|
||||
|
||||
format_event_iolib(#{service := Service, function := Function, args := Args}, _RpcID) ->
|
||||
{info, {"calling ~0p:~0p(~0tp)", [Service, Function, Args]}}.
|
||||
|
||||
-spec bench_thrift_formatter(input(), _State) ->
|
||||
term().
|
||||
-spec bench_thrift_formatter(input(), _State) -> term().
|
||||
bench_thrift_formatter({Meta, RpcID}, _) ->
|
||||
format_msg(woody_event_handler:format_event('call service', Meta, RpcID)).
|
||||
|
||||
|
@ -10,8 +10,7 @@
|
||||
|
||||
-type input() :: term().
|
||||
|
||||
-spec input() ->
|
||||
input().
|
||||
-spec input() -> input().
|
||||
input() ->
|
||||
%% NOTE
|
||||
%% You will need some reasonably complex term following `domain_config.Snapshot` thrift schema
|
||||
@ -20,23 +19,19 @@ input() ->
|
||||
{ok, Bin} = file:read_file("test/snapshot.term"),
|
||||
erlang:binary_to_term(Bin).
|
||||
|
||||
-spec iolib_formatter({input, _State}) ->
|
||||
input().
|
||||
-spec iolib_formatter({input, _State}) -> input().
|
||||
iolib_formatter({input, _}) ->
|
||||
input().
|
||||
|
||||
-spec thrift_formatter({input, _State}) ->
|
||||
input().
|
||||
-spec thrift_formatter({input, _State}) -> input().
|
||||
thrift_formatter({input, _}) ->
|
||||
input().
|
||||
|
||||
-spec bench_iolib_formatter(input(), _State) ->
|
||||
term().
|
||||
-spec bench_iolib_formatter(input(), _State) -> term().
|
||||
bench_iolib_formatter(Snapshot, _) ->
|
||||
format_msg({"~0tp", [Snapshot]}).
|
||||
|
||||
-spec bench_thrift_formatter(input(), _State) ->
|
||||
term().
|
||||
-spec bench_thrift_formatter(input(), _State) -> term().
|
||||
bench_thrift_formatter(Snapshot, _) ->
|
||||
Service = dmsl_domain_config_thrift,
|
||||
format_msg(woody_event_formatter:format_reply(Service, 'Repository', 'Checkout', Snapshot, #{})).
|
||||
|
@ -2,8 +2,7 @@
|
||||
|
||||
-export([run/0]).
|
||||
|
||||
-spec run() ->
|
||||
ok.
|
||||
-spec run() -> ok.
|
||||
run() ->
|
||||
Input = input(),
|
||||
Opts = #{iterations => 10},
|
||||
@ -11,14 +10,13 @@ run() ->
|
||||
_ = run(thrift, mk_thrift_runner(Input), Opts),
|
||||
ok.
|
||||
|
||||
-spec run(atom(), meter_memory_pressure:runner(), meter_memory_pressure:opts()) ->
|
||||
ok.
|
||||
-spec run(atom(), meter_memory_pressure:runner(), meter_memory_pressure:opts()) -> ok.
|
||||
run(Name, Runner, Opts) ->
|
||||
_ = io:format("Benchmarking '~s' memory pressure...~n", [Name]),
|
||||
_ = io:format("====================================~n", []),
|
||||
Metrics = meter_memory_pressure:measure(Runner, Opts),
|
||||
lists:foreach(
|
||||
fun (Metric) ->
|
||||
fun(Metric) ->
|
||||
io:format("~24s = ~-16b~n", [Metric, maps:get(Metric, Metrics)])
|
||||
end,
|
||||
[
|
||||
@ -35,8 +33,7 @@ run(Name, Runner, Opts) ->
|
||||
_ = io:format("====================================~n~n", []),
|
||||
ok.
|
||||
|
||||
-spec input() ->
|
||||
term().
|
||||
-spec input() -> term().
|
||||
input() ->
|
||||
%% NOTE
|
||||
%% You will need some reasonably complex term following `domain_config.Snapshot` thrift schema
|
||||
@ -45,16 +42,14 @@ input() ->
|
||||
{ok, Binary} = file:read_file("test/snapshot.term"),
|
||||
erlang:binary_to_term(Binary).
|
||||
|
||||
-spec mk_iolib_runner(term()) ->
|
||||
meter_memory_pressure:runner().
|
||||
-spec mk_iolib_runner(term()) -> meter_memory_pressure:runner().
|
||||
mk_iolib_runner(Snapshot) ->
|
||||
fun () ->
|
||||
fun() ->
|
||||
bench_woody_formatter:bench_iolib_formatter(Snapshot, [])
|
||||
end.
|
||||
|
||||
-spec mk_thrift_runner(term()) ->
|
||||
meter_memory_pressure:runner().
|
||||
-spec mk_thrift_runner(term()) -> meter_memory_pressure:runner().
|
||||
mk_thrift_runner(Snapshot) ->
|
||||
fun () ->
|
||||
fun() ->
|
||||
bench_woody_formatter:bench_thrift_formatter(Snapshot, [])
|
||||
end.
|
||||
|
@ -22,16 +22,15 @@
|
||||
|
||||
-type runner() :: fun(() -> _).
|
||||
-type opts() :: #{
|
||||
iterations => pos_integer(),
|
||||
spawn_opts => [{atom(), _}],
|
||||
iterations => pos_integer(),
|
||||
spawn_opts => [{atom(), _}],
|
||||
dump_traces => file:filename()
|
||||
}.
|
||||
|
||||
-export_type([runner/0]).
|
||||
-export_type([opts/0]).
|
||||
|
||||
-spec measure(runner(), opts()) ->
|
||||
metrics().
|
||||
-spec measure(runner(), opts()) -> metrics().
|
||||
measure(Runner, Opts0) ->
|
||||
Opts = maps:merge(get_default_opts(), Opts0),
|
||||
Token = make_ref(),
|
||||
@ -49,7 +48,7 @@ get_default_opts() ->
|
||||
run(Runner, Tracer, Opts) ->
|
||||
SpawnOpts = [monitor, {priority, high}] ++ maps:get(spawn_opts, Opts),
|
||||
{Staging, MRef} = erlang:spawn_opt(
|
||||
fun () -> run_staging(Runner, Tracer, Opts) end,
|
||||
fun() -> run_staging(Runner, Tracer, Opts) end,
|
||||
SpawnOpts
|
||||
),
|
||||
receive
|
||||
@ -73,7 +72,7 @@ iterate(_Runner, 0) ->
|
||||
|
||||
start_tracer(Token, Opts) ->
|
||||
Self = self(),
|
||||
erlang:spawn_link(fun () -> run_tracer(Self, Token, Opts) end).
|
||||
erlang:spawn_link(fun() -> run_tracer(Self, Token, Opts) end).
|
||||
|
||||
collect_metrics(Tracer, Token) ->
|
||||
_ = Tracer ! Token,
|
||||
@ -83,7 +82,10 @@ collect_metrics(Tracer, Token) ->
|
||||
end.
|
||||
|
||||
run_tracer(MeterPid, Token, Opts) ->
|
||||
_ = receive Token -> ok end,
|
||||
_ =
|
||||
receive
|
||||
Token -> ok
|
||||
end,
|
||||
Traces = collect_traces(),
|
||||
Metrics = analyze_traces(Traces),
|
||||
ok = maybe_dump_traces(Traces, Opts),
|
||||
@ -91,15 +93,14 @@ run_tracer(MeterPid, Token, Opts) ->
|
||||
|
||||
collect_traces() ->
|
||||
collect_traces([]).
|
||||
|
||||
collect_traces(Acc) ->
|
||||
receive
|
||||
{trace_ts, _Pid, Trace, Info, Clock} ->
|
||||
collect_traces([{Trace, Info, Clock} | Acc]);
|
||||
Unexpected ->
|
||||
error({unexpected, Unexpected})
|
||||
after
|
||||
0 ->
|
||||
lists:reverse(Acc)
|
||||
after 0 -> lists:reverse(Acc)
|
||||
end.
|
||||
|
||||
maybe_dump_traces(Traces, #{dump_traces := Filename}) ->
|
||||
@ -136,7 +137,7 @@ analyze_gc(InfoStart, InfoEnd, M0) ->
|
||||
M4.
|
||||
|
||||
difference(Name, Info1, Info2) ->
|
||||
combine(Name, fun (V1, V2) -> erlang:max(0, V2 - V1) end, Info1, Info2).
|
||||
combine(Name, fun(V1, V2) -> erlang:max(0, V2 - V1) end, Info1, Info2).
|
||||
|
||||
min(Name, Info1, Info2) ->
|
||||
combine(Name, fun erlang:min/2, Info1, Info2).
|
||||
@ -153,15 +154,14 @@ increment(Name, Metrics) ->
|
||||
increment(Name, 1, Metrics).
|
||||
|
||||
increment(Name, Delta, Metrics) ->
|
||||
maps:update_with(Name, fun (V) -> V + Delta end, Metrics).
|
||||
maps:update_with(Name, fun(V) -> V + Delta end, Metrics).
|
||||
|
||||
update(Name, Fun, I, Metrics) ->
|
||||
maps:update_with(Name, fun (V) -> Fun(V, I) end, I, Metrics).
|
||||
maps:update_with(Name, fun(V) -> Fun(V, I) end, I, Metrics).
|
||||
|
||||
%%
|
||||
|
||||
-spec export(file:filename(), file:filename(), csv) -> ok.
|
||||
|
||||
export(FilenameIn, FilenameOut, Format) ->
|
||||
{ok, Content} = file:read_file(FilenameIn),
|
||||
Traces = erlang:binary_to_term(Content),
|
||||
@ -171,7 +171,7 @@ export(FilenameIn, FilenameOut, Format) ->
|
||||
|
||||
format_traces(Traces, csv, FileOut) ->
|
||||
_ = format_csv_header(FileOut),
|
||||
_ = lists:foreach(fun (T) -> format_csv_trace(T, FileOut) end, Traces),
|
||||
_ = lists:foreach(fun(T) -> format_csv_trace(T, FileOut) end, Traces),
|
||||
ok.
|
||||
|
||||
format_csv_header(Out) ->
|
||||
@ -209,7 +209,8 @@ format_csv_trace({Event, Info, Clock}, Out) ->
|
||||
]).
|
||||
|
||||
get_info(Name, Info) ->
|
||||
{_Name, V} = lists:keyfind(Name, 1, Info), V.
|
||||
{_Name, V} = lists:keyfind(Name, 1, Info),
|
||||
V.
|
||||
|
||||
clock_to_mcs({MSec, Sec, USec}) ->
|
||||
(MSec * 1000000 + Sec) * 1000000 + USec.
|
||||
|
@ -1,4 +1,5 @@
|
||||
-module(woody_joint_workers_pt).
|
||||
|
||||
-include_lib("proper/include/proper.hrl").
|
||||
|
||||
-export([
|
||||
@ -20,11 +21,11 @@
|
||||
-type id_t() :: non_neg_integer().
|
||||
|
||||
-spec prop_test() -> any().
|
||||
-spec start_workers() -> genlib_gen:start_ret().
|
||||
-spec stop_workers(any()) -> ok.
|
||||
-spec start_workers() -> pid().
|
||||
-spec stop_workers(pid()) -> ok.
|
||||
-spec do(id_t(), successfulness()) -> any().
|
||||
-spec task_timeouts(successfulness()) -> {timeout(), timeout()}.
|
||||
-spec id() -> id_t().
|
||||
-spec id() -> proper_types:type().
|
||||
-spec command(any()) -> any().
|
||||
-spec initial_state() -> state().
|
||||
-spec precondition(any(), any()) -> boolean().
|
||||
@ -49,7 +50,6 @@ prop_test() ->
|
||||
end
|
||||
).
|
||||
|
||||
|
||||
start_workers() ->
|
||||
genlib:unwrap(woody_joint_workers:start_link({local, workers})).
|
||||
|
||||
@ -64,15 +64,15 @@ do(ID, Successfulness) ->
|
||||
% тестовый таск спит небольшое время
|
||||
% дедлайн ставится либо до, либо после него
|
||||
{TaskSleepTimeout, WorkerTimeout} = task_timeouts(Successfulness),
|
||||
Task =
|
||||
fun(_) ->
|
||||
ok = timer:sleep(TaskSleepTimeout),
|
||||
{ok, ID}
|
||||
end,
|
||||
Task = fun(_) ->
|
||||
ok = timer:sleep(TaskSleepTimeout),
|
||||
{ok, ID}
|
||||
end,
|
||||
catch woody_joint_workers:do(workers, {ID, Successfulness}, Task, woody_deadline:from_timeout(WorkerTimeout)).
|
||||
|
||||
% если уменьшать, то могут быть ложные срабатывания
|
||||
-define(timeout_k, 20).
|
||||
|
||||
task_timeouts(success) ->
|
||||
{?timeout_k * 1, ?timeout_k * 3};
|
||||
task_timeouts(fail) ->
|
||||
@ -84,7 +84,7 @@ id() ->
|
||||
command(_) ->
|
||||
frequency([
|
||||
{10, {call, ?MODULE, do, [id(), success]}},
|
||||
{1 , {call, ?MODULE, do, [id(), fail ]}}
|
||||
{1, {call, ?MODULE, do, [id(), fail]}}
|
||||
]).
|
||||
|
||||
initial_state() ->
|
||||
|
@ -13,8 +13,9 @@
|
||||
-export([get_socket_errors_caught/0]).
|
||||
|
||||
-type state() :: #{
|
||||
socket_errors_caught => pos_integer()
|
||||
socket_errors_caught => non_neg_integer()
|
||||
}.
|
||||
|
||||
-type event() :: woody_event_handler:event().
|
||||
-type rpc_id() :: woody:rpc_id().
|
||||
-type event_meta() :: woody_event_handler:event_meta().
|
||||
@ -25,18 +26,17 @@
|
||||
-define(SOCKET_CLOSED, <<"The socket has been closed.">>).
|
||||
|
||||
-spec get_socket_errors_caught() -> pos_integer().
|
||||
|
||||
get_socket_errors_caught() ->
|
||||
{ok, N} = gen_server:call(?MODULE, get_number_of_events),
|
||||
N.
|
||||
|
||||
-spec child_spec() -> supervisor:child_spec().
|
||||
|
||||
child_spec() -> #{
|
||||
id => ?MODULE,
|
||||
start => {?MODULE, start_link, []},
|
||||
type => worker
|
||||
}.
|
||||
child_spec() ->
|
||||
#{
|
||||
id => ?MODULE,
|
||||
start => {?MODULE, start_link, []},
|
||||
type => worker
|
||||
}.
|
||||
|
||||
%% woody_event_handler callbaacks
|
||||
|
||||
@ -46,45 +46,46 @@ child_spec() -> #{
|
||||
event_meta(),
|
||||
options()
|
||||
) -> _.
|
||||
|
||||
handle_event(Event, RpcId, Meta, Opts) ->
|
||||
gen_server:call(?MODULE, {Event, RpcId, Meta, Opts}).
|
||||
|
||||
|
||||
%% gen_server callbacks
|
||||
|
||||
-spec start_link() -> {ok, pid()}.
|
||||
|
||||
start_link() ->
|
||||
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
|
||||
|
||||
-spec init(_) -> {ok, state()}.
|
||||
|
||||
init(_) ->
|
||||
{ok, #{socket_errors_caught => 0}}.
|
||||
|
||||
-spec handle_call({event(), rpc_id(), event_meta(), options()}, _, state()) ->
|
||||
{reply, ok | {ok, pos_integer()}, state()}.
|
||||
handle_call({Event = ?EV_SERVICE_HANDLER_RESULT, Rpc,
|
||||
#{status := error, class := system, result := ?SOCKET_CLOSED} = Meta, Opts}, _, #{
|
||||
socket_errors_caught := Caught
|
||||
} = State) ->
|
||||
-spec handle_call(get_number_of_events | {event(), rpc_id(), event_meta(), options()}, _, state()) ->
|
||||
{reply, ok | {ok, non_neg_integer()}, state()}.
|
||||
handle_call(
|
||||
{Event = ?EV_SERVICE_HANDLER_RESULT, Rpc, #{status := error, class := system, result := ?SOCKET_CLOSED} = Meta,
|
||||
Opts},
|
||||
_,
|
||||
#{
|
||||
socket_errors_caught := Caught
|
||||
} = State
|
||||
) ->
|
||||
woody_tests_SUITE:handle_event(Event, Rpc, Meta, Opts),
|
||||
{reply, ok, State#{socket_errors_caught => Caught + 1}};
|
||||
handle_call({Event = ?EV_SERVER_RECEIVE, Rpc, #{status := error, reason := ?SOCKET_CLOSED} = Meta, Opts}, _, #{
|
||||
socket_errors_caught := Caught
|
||||
} = State) ->
|
||||
handle_call(
|
||||
{Event = ?EV_SERVER_RECEIVE, Rpc, #{status := error, reason := ?SOCKET_CLOSED} = Meta, Opts},
|
||||
_,
|
||||
#{
|
||||
socket_errors_caught := Caught
|
||||
} = State
|
||||
) ->
|
||||
woody_tests_SUITE:handle_event(Event, Rpc, Meta, Opts),
|
||||
{reply, ok, State#{socket_errors_caught => Caught + 1}};
|
||||
|
||||
handle_call({Event, Rpc, Meta, Opts}, _, State) ->
|
||||
woody_tests_SUITE:handle_event(Event, Rpc, Meta, Opts),
|
||||
{reply, ok, State};
|
||||
|
||||
handle_call(get_number_of_events, _, #{socket_errors_caught := N} = State) ->
|
||||
{reply, {ok, N}, State}.
|
||||
|
||||
-spec handle_cast(_, state()) -> {noreply, state()}.
|
||||
|
||||
handle_cast(_, S) ->
|
||||
{noreply, S}.
|
||||
|
@ -1,4 +1,5 @@
|
||||
-module(woody_joint_workers_SUITE).
|
||||
|
||||
-include_lib("common_test/include/ct.hrl").
|
||||
|
||||
-export([
|
||||
@ -33,7 +34,7 @@ init_per_suite(C) ->
|
||||
% dbg:tpl({woody_joint_workers, do, 4}, x),
|
||||
|
||||
{ok, Apps} = application:ensure_all_started(woody),
|
||||
[{apps, Apps}|C].
|
||||
[{apps, Apps} | C].
|
||||
|
||||
end_per_suite(C) ->
|
||||
[application:stop(App) || App <- ?config(apps, C)].
|
||||
@ -43,10 +44,11 @@ end_per_suite(C) ->
|
||||
%%
|
||||
prop_test(_C) ->
|
||||
R = proper:quickcheck(
|
||||
woody_joint_workers_pt:prop_test(),
|
||||
[noshrink] % default options
|
||||
),
|
||||
woody_joint_workers_pt:prop_test(),
|
||||
% default options
|
||||
[noshrink]
|
||||
),
|
||||
case R of
|
||||
true -> ok;
|
||||
true -> ok;
|
||||
Error -> exit(Error)
|
||||
end.
|
||||
|
@ -3,6 +3,7 @@
|
||||
-include_lib("public_key/include/public_key.hrl").
|
||||
-include_lib("common_test/include/ct.hrl").
|
||||
-include_lib("stdlib/include/assert.hrl").
|
||||
|
||||
-include("woody_test_thrift.hrl").
|
||||
|
||||
-behaviour(supervisor).
|
||||
@ -35,7 +36,7 @@
|
||||
%% supervisor callback
|
||||
-export([init/1]).
|
||||
|
||||
-type config() :: [{atom(), any()}].
|
||||
-type config() :: [{atom(), any()}].
|
||||
-type group_name() :: atom().
|
||||
-type case_name() :: atom().
|
||||
|
||||
@ -56,9 +57,7 @@
|
||||
%%% CT callbacks
|
||||
%%%
|
||||
|
||||
-spec all() ->
|
||||
[case_name()].
|
||||
|
||||
-spec all() -> [{group, group_name()}].
|
||||
all() ->
|
||||
[
|
||||
{group, 'tlsv1.3'},
|
||||
@ -66,9 +65,7 @@ all() ->
|
||||
{group, 'tlsv1.1'}
|
||||
].
|
||||
|
||||
-spec groups() ->
|
||||
[{group_name(), list(), [case_name()]}].
|
||||
|
||||
-spec groups() -> [{group_name(), list(), [case_name()]}].
|
||||
groups() ->
|
||||
TestGroup = [
|
||||
client_wo_cert_test,
|
||||
@ -81,34 +78,26 @@ groups() ->
|
||||
{'tlsv1.3', [parallel], TestGroup}
|
||||
].
|
||||
|
||||
-spec init_per_suite(config()) ->
|
||||
config().
|
||||
|
||||
-spec init_per_suite(config()) -> config().
|
||||
init_per_suite(C) ->
|
||||
{ok, Sup} = supervisor:start_link({local, ?MODULE}, ?MODULE, []),
|
||||
true = erlang:unlink(Sup),
|
||||
{ok, Apps} = application:ensure_all_started(woody),
|
||||
{ok, Sup} = supervisor:start_link({local, ?MODULE}, ?MODULE, []),
|
||||
true = erlang:unlink(Sup),
|
||||
{ok, Apps} = application:ensure_all_started(woody),
|
||||
[{sup, Sup}, {apps, Apps} | C].
|
||||
|
||||
-spec end_per_suite(config()) ->
|
||||
ok.
|
||||
|
||||
-spec end_per_suite(config()) -> ok.
|
||||
end_per_suite(C) ->
|
||||
Sup = ?config(sup, C),
|
||||
ok = proc_lib:stop(Sup),
|
||||
[application:stop(App) || App <- proplists:get_value(apps, C)],
|
||||
ok = proc_lib:stop(Sup),
|
||||
_ = [application:stop(App) || App <- proplists:get_value(apps, C)],
|
||||
ok.
|
||||
|
||||
-spec init_per_group(group_name(), config()) ->
|
||||
config().
|
||||
|
||||
-spec init_per_group(group_name(), config()) -> config().
|
||||
init_per_group(Name, C) ->
|
||||
{ok, WoodyServer} = start_woody_server(Name, C),
|
||||
[{woody_server, WoodyServer}, {group_name, Name} | C].
|
||||
|
||||
-spec end_per_group(group_name(), config()) ->
|
||||
any().
|
||||
|
||||
-spec end_per_group(group_name(), config()) -> any().
|
||||
end_per_group(_Name, C) ->
|
||||
stop_woody_server(C).
|
||||
|
||||
@ -117,12 +106,11 @@ end_per_group(_Name, C) ->
|
||||
%%%
|
||||
|
||||
-spec client_wo_cert_test(config()) -> _.
|
||||
|
||||
client_wo_cert_test(C) ->
|
||||
Vsn = ?config(group_name, C),
|
||||
SSLOptions = [{cacertfile, ?ca_cert(C)} | client_ssl_opts(Vsn)],
|
||||
try
|
||||
get_weapon(?FUNCTION_NAME, <<"BFG">>, SSLOptions),
|
||||
_ = get_weapon(?FUNCTION_NAME, <<"BFG">>, SSLOptions),
|
||||
error(unreachable)
|
||||
catch
|
||||
% NOTE
|
||||
@ -136,19 +124,17 @@ client_wo_cert_test(C) ->
|
||||
end.
|
||||
|
||||
-spec valid_client_cert_test(config()) -> _.
|
||||
|
||||
valid_client_cert_test(C) ->
|
||||
Vsn = ?config(group_name, C),
|
||||
SSLOptions = [{cacertfile, ?ca_cert(C)}, {certfile, ?client_cert(C)} | client_ssl_opts(Vsn)],
|
||||
{ok, #'Weapon'{}} = get_weapon(?FUNCTION_NAME, <<"BFG">>, SSLOptions).
|
||||
|
||||
-spec invalid_client_cert_test(config()) -> _.
|
||||
|
||||
invalid_client_cert_test(C) ->
|
||||
Vsn = ?config(group_name, C),
|
||||
SSLOptions = [{cacertfile, ?ca_cert(C)}, {certfile, ?invalid_client_cert(C)} | client_ssl_opts(Vsn)],
|
||||
try
|
||||
get_weapon(?FUNCTION_NAME, <<"BFG">>, SSLOptions),
|
||||
_ = get_weapon(?FUNCTION_NAME, <<"BFG">>, SSLOptions),
|
||||
error(unreachable)
|
||||
catch
|
||||
% NOTE
|
||||
@ -159,9 +145,7 @@ invalid_client_cert_test(C) ->
|
||||
{match, _} = re:run(Reason, <<"^{tls_alert,[\"\{]unknown[ _]ca.*$">>, [])
|
||||
end.
|
||||
|
||||
-spec client_ssl_opts(atom()) ->
|
||||
[ssl:tls_client_option()].
|
||||
|
||||
-spec client_ssl_opts(atom()) -> [ssl:tls_client_option()].
|
||||
client_ssl_opts('tlsv1.3') ->
|
||||
% NOTE
|
||||
% We need at least an extra TLSv1.2 here and default OTP cipher suites,
|
||||
@ -183,12 +167,26 @@ client_ssl_opts(Vsn) ->
|
||||
woody_event_handler:event_meta(),
|
||||
woody:options()
|
||||
) -> _.
|
||||
|
||||
handle_event(Event, RpcId, Meta, _) ->
|
||||
{_Severity, {Format, Msg}, EvMeta} = woody_event_handler:format_event_and_meta(
|
||||
Event, Meta, RpcId,
|
||||
[event, role, service, service_schema, function, type, args, metadata,
|
||||
deadline, status, url, code, result, execution_time]
|
||||
Event,
|
||||
Meta,
|
||||
RpcId,
|
||||
[
|
||||
event,
|
||||
role,
|
||||
service,
|
||||
service_schema,
|
||||
function,
|
||||
type,
|
||||
args,
|
||||
metadata,
|
||||
deadline,
|
||||
status,
|
||||
url,
|
||||
code,
|
||||
result
|
||||
]
|
||||
),
|
||||
ct:pal(Format ++ "~nmeta: ~p", Msg ++ [EvMeta]).
|
||||
|
||||
@ -196,9 +194,7 @@ handle_event(Event, RpcId, Meta, _) ->
|
||||
%%% woody_server_thrift_handler callback
|
||||
%%%
|
||||
|
||||
-spec handle_function(woody:func(), woody:args(), woody_context:ctx(), woody:options()) ->
|
||||
{ok, woody:result()}.
|
||||
|
||||
-spec handle_function(woody:func(), woody:args(), woody_context:ctx(), woody:options()) -> {ok, woody:result()}.
|
||||
handle_function(get_weapon, {Name, _Data}, Context, _Opts) ->
|
||||
_ = assert_common_name([<<"Valid Test Client">>], Context),
|
||||
{ok, #'Weapon'{name = Name, slot_pos = 0}}.
|
||||
@ -207,12 +203,13 @@ handle_function(get_weapon, {Name, _Data}, Context, _Opts) ->
|
||||
%%% Supervisor callback
|
||||
%%%
|
||||
|
||||
-spec init(_) -> _.
|
||||
|
||||
-spec init(_) -> genlib_gen:supervisor_ret().
|
||||
init(_) ->
|
||||
{ok, {
|
||||
{one_for_one, 1, 1}, []
|
||||
}}.
|
||||
{ok,
|
||||
{
|
||||
{one_for_one, 1, 1},
|
||||
[]
|
||||
}}.
|
||||
|
||||
%%%
|
||||
%%% Internal functions
|
||||
@ -221,26 +218,24 @@ init(_) ->
|
||||
start_woody_server(Vsn, C) ->
|
||||
Sup = ?config(sup, C),
|
||||
Server = woody_server:child_spec(?MODULE, #{
|
||||
handlers => [{?PATH, {{?THRIFT_DEFS, 'Weapons'}, ?MODULE}}],
|
||||
event_handler => ?MODULE,
|
||||
ip => {0, 0, 0, 0},
|
||||
port => 8043,
|
||||
handlers => [{?PATH, {{?THRIFT_DEFS, 'Weapons'}, ?MODULE}}],
|
||||
event_handler => ?MODULE,
|
||||
ip => {0, 0, 0, 0},
|
||||
port => 8043,
|
||||
transport_opts => #{
|
||||
transport => ranch_ssl,
|
||||
transport => ranch_ssl,
|
||||
socket_opts => [
|
||||
{cacertfile, ?ca_cert(C)},
|
||||
{certfile, ?server_cert(C)},
|
||||
{verify, verify_peer},
|
||||
{cacertfile, ?ca_cert(C)},
|
||||
{certfile, ?server_cert(C)},
|
||||
{verify, verify_peer},
|
||||
{fail_if_no_peer_cert, true},
|
||||
{versions, [Vsn]}
|
||||
{versions, [Vsn]}
|
||||
]
|
||||
}
|
||||
}),
|
||||
supervisor:start_child(Sup, Server).
|
||||
|
||||
-spec stop_woody_server(config()) ->
|
||||
ok.
|
||||
|
||||
-spec stop_woody_server(config()) -> ok.
|
||||
stop_woody_server(C) ->
|
||||
ok = supervisor:terminate_child(?config(sup, C), ?MODULE),
|
||||
ok = supervisor:delete_child(?config(sup, C), ?MODULE).
|
||||
@ -254,8 +249,8 @@ get_weapon(Id, Gun, SSLOptions) ->
|
||||
transport_opts => #{
|
||||
ssl_options => [
|
||||
{server_name_indication, "Test Server"},
|
||||
{verify, verify_peer} |
|
||||
SSLOptions
|
||||
{verify, verify_peer}
|
||||
| SSLOptions
|
||||
]
|
||||
}
|
||||
},
|
||||
@ -268,9 +263,7 @@ get_service_endpoint('Weapons') ->
|
||||
}.
|
||||
|
||||
to_binary(Atom) when is_atom(Atom) ->
|
||||
erlang:atom_to_binary(Atom, utf8);
|
||||
to_binary(Binary) when is_binary(Binary) ->
|
||||
Binary.
|
||||
erlang:atom_to_binary(Atom, utf8).
|
||||
|
||||
assert_common_name(CNs, Context) ->
|
||||
CN = woody_context:get_common_name(Context),
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -7,10 +7,12 @@
|
||||
|
||||
%% woody_server_thrift_handler callbacks
|
||||
-behaviour(woody_server_thrift_handler).
|
||||
|
||||
-export([handle_function/4]).
|
||||
|
||||
%% woody_event_handler callbacks
|
||||
-behaviour(woody_event_handler).
|
||||
|
||||
-export([handle_event/4]).
|
||||
|
||||
-export([all/0]).
|
||||
@ -32,8 +34,7 @@
|
||||
-spec respects_max_connections(config()) -> any().
|
||||
-spec shuts_down_gracefully(config()) -> any().
|
||||
|
||||
-spec handle_function(woody:func(), woody:args(), woody_context:ctx(), woody:options()) ->
|
||||
{ok, woody:result()}.
|
||||
-spec handle_function(woody:func(), woody:args(), woody_context:ctx(), woody:options()) -> {ok, woody:result()}.
|
||||
|
||||
-spec handle_event(
|
||||
woody_event_handler:event(),
|
||||
@ -65,22 +66,24 @@ init_per_testcase(Name, C) ->
|
||||
Port = get_random_port(),
|
||||
[
|
||||
{client, #{
|
||||
url => iolist_to_binary(["http://localhost:", integer_to_list(Port), "/"]),
|
||||
event_handler => {?MODULE, {client, Name}}
|
||||
url => iolist_to_binary(["http://localhost:", integer_to_list(Port), "/"]),
|
||||
event_handler => {?MODULE, {client, Name}}
|
||||
}},
|
||||
{server , #{
|
||||
ip => {127, 0, 0, 1},
|
||||
port => Port,
|
||||
event_handler => [{?MODULE, {server, Name}}],
|
||||
{server, #{
|
||||
ip => {127, 0, 0, 1},
|
||||
port => Port,
|
||||
event_handler => [{?MODULE, {server, Name}}],
|
||||
shutdown_timeout => 5000
|
||||
}},
|
||||
{testcase, Name} | C
|
||||
{testcase, Name}
|
||||
| C
|
||||
].
|
||||
|
||||
%%
|
||||
|
||||
respects_max_connections(C) ->
|
||||
MaxConns = 10 + rand:uniform(10), % (10; 20]
|
||||
% (10; 20]
|
||||
MaxConns = 10 + rand:uniform(10),
|
||||
Table = ets:new(?MODULE, [public, {read_concurrency, true}, {write_concurrency, true}]),
|
||||
true = ets:insert_new(Table, [{slot, 0}]),
|
||||
Service = {woody_test_thrift, 'Weapons'},
|
||||
@ -99,19 +102,24 @@ respects_max_connections(C) ->
|
||||
ReadBodyOpts = #{},
|
||||
{ok, ServerPid} = start_woody_server(Handler, TransportOpts, ProtocolOpts, ReadBodyOpts, C),
|
||||
Results = genlib_pmap:map(
|
||||
fun (_) ->
|
||||
fun(_) ->
|
||||
woody_client:call({Service, 'get_weapon', {<<"BFG">>, <<>>}}, Client)
|
||||
end,
|
||||
lists:seq(1, MaxConns * 10)
|
||||
),
|
||||
Slots = lists:map(
|
||||
fun ({ok, #'Weapon'{slot_pos = Slot}}) -> Slot end,
|
||||
fun({ok, #'Weapon'{slot_pos = Slot}}) -> Slot end,
|
||||
Results
|
||||
),
|
||||
?assert(lists:max(Slots) =< MaxConns),
|
||||
ok = stop_woody_server(ServerPid).
|
||||
|
||||
-define(receive_or_timeout(Msg, Timeout), receive Msg -> ok after Timeout -> timeout end).
|
||||
-define(receive_or_timeout(Msg, Timeout),
|
||||
receive
|
||||
Msg -> ok
|
||||
after Timeout -> timeout
|
||||
end
|
||||
).
|
||||
|
||||
shuts_down_gracefully(C) ->
|
||||
Client = ?config(client, C),
|
||||
@ -124,12 +132,12 @@ shuts_down_gracefully(C) ->
|
||||
ParentPid = self(),
|
||||
%% send a shutdown signal to the server in 1000ms
|
||||
%% then try making a new connection with it, expect econnrefused
|
||||
TestPid = spawn_link(fun() -> process_econnrefused_test(Client, ParentPid) end),
|
||||
_ = spawn_link(fun() -> process_delayed_kill(ServerPid, TestPid, 1000) end),
|
||||
TestPid = spawn_link(fun() -> process_econnrefused_test(Client, ParentPid) end),
|
||||
_ = spawn_link(fun() -> process_delayed_kill(ServerPid, TestPid, 1000) end),
|
||||
%% fire some requests and expect them to finish successfuly
|
||||
%% even when server is shutting down in the meantime
|
||||
_ = genlib_pmap:map(
|
||||
fun (_) ->
|
||||
fun(_) ->
|
||||
?assertEqual(
|
||||
{ok, #'Powerup'{name = <<"Warbanner">>}},
|
||||
get_powerup(Client, <<"Warbanner">>, <<>>)
|
||||
@ -161,13 +169,13 @@ process_delayed_kill(ServerPid, TestPid, Timeout) ->
|
||||
start_woody_server(Handler, TransportOpts, ProtocolOpts, ReadBodyOpts, C) ->
|
||||
ServerOpts0 = ?config(server, C),
|
||||
SupervisorOpts = woody_server:child_spec(
|
||||
{?MODULE, ?config(testcase, C)},
|
||||
ServerOpts0#{
|
||||
handlers => [Handler],
|
||||
read_body_opts => ReadBodyOpts,
|
||||
transport_opts => TransportOpts,
|
||||
protocol_opts => ProtocolOpts
|
||||
}
|
||||
{?MODULE, ?config(testcase, C)},
|
||||
ServerOpts0#{
|
||||
handlers => [Handler],
|
||||
read_body_opts => ReadBodyOpts,
|
||||
transport_opts => TransportOpts,
|
||||
protocol_opts => ProtocolOpts
|
||||
}
|
||||
),
|
||||
genlib_adhoc_supervisor:start_link(#{}, [SupervisorOpts]).
|
||||
|
||||
@ -181,7 +189,6 @@ handle_function(get_weapon, {Name, _}, _Context, {respects_max_connections, Tabl
|
||||
ok = timer:sleep(rand:uniform(10)),
|
||||
_ = ets:update_counter(Table, slot, -1),
|
||||
{ok, #'Weapon'{name = Name, slot_pos = Slot}};
|
||||
|
||||
handle_function(get_powerup, {Name, _}, _Context, _) ->
|
||||
ok = timer:sleep(2000),
|
||||
{ok, #'Powerup'{name = Name}}.
|
||||
|
Loading…
Reference in New Issue
Block a user