zf

zenflows testing
git clone https://s.sonu.ch/~srfsh/zf.git
Log | Files | Refs | Submodules | README | LICENSE

cowboy_tracer_h.erl (6702B)


      1 %% Copyright (c) 2017, Loïc Hoguin <essen@ninenines.eu>
      2 %%
      3 %% Permission to use, copy, modify, and/or distribute this software for any
      4 %% purpose with or without fee is hereby granted, provided that the above
      5 %% copyright notice and this permission notice appear in all copies.
      6 %%
      7 %% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
      8 %% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
      9 %% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
     10 %% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
     11 %% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
     12 %% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
     13 %% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     14 
     15 -module(cowboy_tracer_h).
     16 -behavior(cowboy_stream).
     17 
     18 -export([init/3]).
     19 -export([data/4]).
     20 -export([info/3]).
     21 -export([terminate/3]).
     22 -export([early_error/5]).
     23 
     24 -export([set_trace_patterns/0]).
     25 
     26 -export([tracer_process/3]).
     27 -export([system_continue/3]).
     28 -export([system_terminate/4]).
     29 -export([system_code_change/4]).
     30 
     31 -type match_predicate()
     32 	:: fun((cowboy_stream:streamid(), cowboy_req:req(), cowboy:opts()) -> boolean()).
     33 
     34 -type tracer_match_specs() :: [match_predicate()
     35 	| {method, binary()}
     36 	| {host, binary()}
     37 	| {path, binary()}
     38 	| {path_start, binary()}
     39 	| {header, binary()}
     40 	| {header, binary(), binary()}
     41 	| {peer_ip, inet:ip_address()}
     42 ].
     43 -export_type([tracer_match_specs/0]).
     44 
     45 -type tracer_callback() :: fun((init | terminate | tuple(), any()) -> any()).
     46 -export_type([tracer_callback/0]).
     47 
     48 -spec init(cowboy_stream:streamid(), cowboy_req:req(), cowboy:opts())
     49 	-> {cowboy_stream:commands(), any()}.
     50 init(StreamID, Req, Opts) ->
     51 	init_tracer(StreamID, Req, Opts),
     52 	cowboy_stream:init(StreamID, Req, Opts).
     53 
     54 -spec data(cowboy_stream:streamid(), cowboy_stream:fin(), cowboy_req:resp_body(), State)
     55 	-> {cowboy_stream:commands(), State} when State::any().
     56 data(StreamID, IsFin, Data, Next) ->
     57 	cowboy_stream:data(StreamID, IsFin, Data, Next).
     58 
     59 -spec info(cowboy_stream:streamid(), any(), State)
     60 	-> {cowboy_stream:commands(), State} when State::any().
     61 info(StreamID, Info, Next) ->
     62 	cowboy_stream:info(StreamID, Info, Next).
     63 
     64 -spec terminate(cowboy_stream:streamid(), cowboy_stream:reason(), any()) -> any().
     65 terminate(StreamID, Reason, Next) ->
     66 	cowboy_stream:terminate(StreamID, Reason, Next).
     67 
     68 -spec early_error(cowboy_stream:streamid(), cowboy_stream:reason(),
     69 	cowboy_stream:partial_req(), Resp, cowboy:opts()) -> Resp
     70 	when Resp::cowboy_stream:resp_command().
     71 early_error(StreamID, Reason, PartialReq, Resp, Opts) ->
     72 	cowboy_stream:early_error(StreamID, Reason, PartialReq, Resp, Opts).
     73 
     74 %% API.
     75 
     76 %% These trace patterns are most likely not suitable for production.
     77 -spec set_trace_patterns() -> ok.
     78 set_trace_patterns() ->
     79 	erlang:trace_pattern({'_', '_', '_'}, [{'_', [], [{return_trace}]}], [local]),
     80 	erlang:trace_pattern(on_load, [{'_', [], [{return_trace}]}], [local]),
     81 	ok.
     82 
     83 %% Internal.
     84 
     85 init_tracer(StreamID, Req, Opts=#{tracer_match_specs := List, tracer_callback := _}) ->
     86 	case match(List, StreamID, Req, Opts) of
     87 		false ->
     88 			ok;
     89 		true ->
     90 			start_tracer(StreamID, Req, Opts)
     91 	end;
     92 %% When the options tracer_match_specs or tracer_callback
     93 %% are not provided we do not enable tracing.
     94 init_tracer(_, _, _) ->
     95 	ok.
     96 
     97 match([], _, _, _) ->
     98 	true;
     99 match([Predicate|Tail], StreamID, Req, Opts) when is_function(Predicate) ->
    100 	case Predicate(StreamID, Req, Opts) of
    101 		true -> match(Tail, StreamID, Req, Opts);
    102 		false -> false
    103 	end;
    104 match([{method, Value}|Tail], StreamID, Req=#{method := Value}, Opts) ->
    105 	match(Tail, StreamID, Req, Opts);
    106 match([{host, Value}|Tail], StreamID, Req=#{host := Value}, Opts) ->
    107 	match(Tail, StreamID, Req, Opts);
    108 match([{path, Value}|Tail], StreamID, Req=#{path := Value}, Opts) ->
    109 	match(Tail, StreamID, Req, Opts);
    110 match([{path_start, PathStart}|Tail], StreamID, Req=#{path := Path}, Opts) ->
    111 	Len = byte_size(PathStart),
    112 	case Path of
    113 		<<PathStart:Len/binary, _/bits>> -> match(Tail, StreamID, Req, Opts);
    114 		_ -> false
    115 	end;
    116 match([{header, Name}|Tail], StreamID, Req=#{headers := Headers}, Opts) ->
    117 	case Headers of
    118 		#{Name := _} -> match(Tail, StreamID, Req, Opts);
    119 		_ -> false
    120 	end;
    121 match([{header, Name, Value}|Tail], StreamID, Req=#{headers := Headers}, Opts) ->
    122 	case Headers of
    123 		#{Name := Value} -> match(Tail, StreamID, Req, Opts);
    124 		_ -> false
    125 	end;
    126 match([{peer_ip, IP}|Tail], StreamID, Req=#{peer := {IP, _}}, Opts) ->
    127 	match(Tail, StreamID, Req, Opts);
    128 match(_, _, _, _) ->
    129 	false.
    130 
    131 %% We only start the tracer if one wasn't started before.
    132 start_tracer(StreamID, Req, Opts) ->
    133 	case erlang:trace_info(self(), tracer) of
    134 		{tracer, []} ->
    135 			TracerPid = proc_lib:spawn_link(?MODULE, tracer_process, [StreamID, Req, Opts]),
    136 			%% The default flags are probably not suitable for production.
    137 			Flags = maps:get(tracer_flags, Opts, [
    138 				send, 'receive', call, return_to,
    139 				procs, ports, monotonic_timestamp,
    140 				%% The set_on_spawn flag is necessary to catch events
    141 				%% from request processes.
    142 				set_on_spawn
    143 			]),
    144 			erlang:trace(self(), true, [{tracer, TracerPid}|Flags]),
    145 			ok;
    146 		_ ->
    147 			ok
    148 	end.
    149 
    150 %% Tracer process.
    151 
    152 -spec tracer_process(_, _, _) -> no_return().
    153 tracer_process(StreamID, Req=#{pid := Parent}, Opts=#{tracer_callback := Fun}) ->
    154 	%% This is necessary because otherwise the tracer could stop
    155 	%% before it has finished processing the events in its queue.
    156 	process_flag(trap_exit, true),
    157 	State = Fun(init, {StreamID, Req, Opts}),
    158 	tracer_loop(Parent, Opts, State).
    159 
    160 tracer_loop(Parent, Opts=#{tracer_callback := Fun}, State0) ->
    161 	receive
    162 		Msg when element(1, Msg) =:= trace; element(1, Msg) =:= trace_ts ->
    163 			State = Fun(Msg, State0),
    164 			tracer_loop(Parent, Opts, State);
    165 		{'EXIT', Parent, Reason} ->
    166 			tracer_terminate(Reason, Opts, State0);
    167 		{system, From, Request} ->
    168 			sys:handle_system_msg(Request, From, Parent, ?MODULE, [], {Opts, State0});
    169 		Msg ->
    170 			cowboy:log(warning, "~p: Tracer process received stray message ~9999p~n",
    171 				[?MODULE, Msg], Opts),
    172 			tracer_loop(Parent, Opts, State0)
    173 	end.
    174 
    175 -spec tracer_terminate(_, _, _) -> no_return().
    176 tracer_terminate(Reason, #{tracer_callback := Fun}, State) ->
    177 	_ = Fun(terminate, State),
    178 	exit(Reason).
    179 
    180 %% System callbacks.
    181 
    182 -spec system_continue(pid(), _, {cowboy:opts(), any()}) -> no_return().
    183 system_continue(Parent, _, {Opts, State}) ->
    184 	tracer_loop(Parent, Opts, State).
    185 
    186 -spec system_terminate(any(), _, _, _) -> no_return().
    187 system_terminate(Reason, _, _, {Opts, State}) ->
    188 	tracer_terminate(Reason, Opts, State).
    189 
    190 -spec system_code_change(Misc, _, _, _) -> {ok, Misc} when Misc::any().
    191 system_code_change(Misc, _, _, _) ->
    192 	{ok, Misc}.