diff options
Diffstat (limited to 'test')
-rw-r--r-- | test/cowboy_test.erl | 6 | ||||
-rw-r--r-- | test/tracer_SUITE.erl | 423 |
2 files changed, 426 insertions, 3 deletions
diff --git a/test/cowboy_test.erl b/test/cowboy_test.erl index 31cba63..bdeb801 100644 --- a/test/cowboy_test.erl +++ b/test/cowboy_test.erl @@ -22,19 +22,19 @@ init_http(Ref, ProtoOpts, Config) -> {ok, _} = cowboy:start_clear(Ref, [{port, 0}], ProtoOpts), Port = ranch:get_port(Ref), - [{type, tcp}, {protocol, http}, {port, Port}, {opts, []}|Config]. + [{ref, Ref}, {type, tcp}, {protocol, http}, {port, Port}, {opts, []}|Config]. init_https(Ref, ProtoOpts, Config) -> Opts = ct_helper:get_certs_from_ets(), {ok, _} = cowboy:start_tls(Ref, Opts ++ [{port, 0}], ProtoOpts), Port = ranch:get_port(Ref), - [{type, ssl}, {protocol, http}, {port, Port}, {opts, Opts}|Config]. + [{ref, Ref}, {type, ssl}, {protocol, http}, {port, Port}, {opts, Opts}|Config]. init_http2(Ref, ProtoOpts, Config) -> Opts = ct_helper:get_certs_from_ets(), {ok, _} = cowboy:start_tls(Ref, Opts ++ [{port, 0}], ProtoOpts), Port = ranch:get_port(Ref), - [{type, ssl}, {protocol, http2}, {port, Port}, {opts, Opts}|Config]. + [{ref, Ref}, {type, ssl}, {protocol, http2}, {port, Port}, {opts, Opts}|Config]. %% Common group of listeners used by most suites. diff --git a/test/tracer_SUITE.erl b/test/tracer_SUITE.erl new file mode 100644 index 0000000..40ede6c --- /dev/null +++ b/test/tracer_SUITE.erl @@ -0,0 +1,423 @@ +%% Copyright (c) 2017, Loïc Hoguin <[email protected]> +%% +%% Permission to use, copy, modify, and/or distribute this software for any +%% purpose with or without fee is hereby granted, provided that the above +%% copyright notice and this permission notice appear in all copies. +%% +%% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +%% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +%% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +%% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +%% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +%% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +%% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +-module(tracer_SUITE). +-compile(export_all). + +-import(ct_helper, [config/2]). +-import(ct_helper, [doc/1]). +-import(cowboy_test, [gun_open/1]). +-import(cowboy_test, [gun_down/1]). + +%% ct. + +all() -> + cowboy_test:common_all(). + +%% We want tests for each group to execute sequentially +%% because we need to modify the protocol options. Groups +%% can run in parallel however. +groups() -> + Tests = ct_helper:all(?MODULE), + [ + {http, [], Tests}, + {https, [], Tests}, + {h2, [], Tests}, + {h2c, [], Tests}, + {http_compress, [], Tests}, + {https_compress, [], Tests}, + {h2_compress, [], Tests}, + {h2c_compress, [], Tests} + ]. + +init_per_group(Name = http, Config) -> + cowboy_test:init_http(Name, init_plain_opts(Config), Config); +init_per_group(Name = https, Config) -> + cowboy_test:init_http(Name, init_plain_opts(Config), Config); +init_per_group(Name = h2, Config) -> + cowboy_test:init_http2(Name, init_plain_opts(Config), Config); +init_per_group(Name = h2c, Config) -> + Config1 = cowboy_test:init_http(Name, init_plain_opts(Config), Config), + lists:keyreplace(protocol, 1, Config1, {protocol, http2}); +init_per_group(Name = http_compress, Config) -> + cowboy_test:init_http(Name, init_compress_opts(Config), Config); +init_per_group(Name = https_compress, Config) -> + cowboy_test:init_http(Name, init_compress_opts(Config), Config); +init_per_group(Name = h2_compress, Config) -> + cowboy_test:init_http2(Name, init_compress_opts(Config), Config); +init_per_group(Name = h2c_compress, Config) -> + Config1 = cowboy_test:init_http(Name, init_compress_opts(Config), Config), + lists:keyreplace(protocol, 1, Config1, {protocol, http2}). + +end_per_group(Name, _) -> + cowboy:stop_listener(Name). + +init_plain_opts(Config) -> + #{ + env => #{dispatch => cowboy_router:compile(init_routes(Config))}, + stream_handlers => [cowboy_tracer_h, cowboy_stream_h] + }. + +init_compress_opts(Config) -> + #{ + env => #{dispatch => cowboy_router:compile(init_routes(Config))}, + stream_handlers => [cowboy_tracer_h, cowboy_compress_h, cowboy_stream_h] + }. + +init_routes(_) -> [ + {"localhost", [ + {"/", hello_h, []}, + {"/longer/hello/path", hello_h, []} + ]} +]. + +do_get(Path, Config) -> + %% Perform a GET request. + ConnPid = gun_open(Config), + Ref = gun:get(ConnPid, Path, [ + {<<"accept-encoding">>, <<"gzip">>}, + {<<"x-test-pid">>, pid_to_list(self())} + ]), + {response, nofin, 200, _Headers} = gun:await(ConnPid, Ref), + {ok, _Body} = gun:await_body(ConnPid, Ref), + gun:close(ConnPid). + +%% We only care about cowboy_req:reply/4 calls. +do_tracer_callback(Pid) -> + fun + (init, _) -> undefined; + (Event={trace_ts, _, call, {cowboy_req, reply, _}, _}, State) -> Pid ! Event, State; + (_, State) -> State + end. + +%% Tests. + +predicate_true(Config) -> + doc("Predicate function returns true, unconditionally enable tracing."), + Ref = config(ref, Config), + Opts = ranch:get_protocol_options(Ref), + ranch:set_protocol_options(Ref, Opts#{ + tracer_callback => do_tracer_callback(self()), + tracer_match_specs => [fun(_,_,_) -> true end] + }), + do_get("/", Config), + receive + {trace_ts, _, call, {cowboy_req, reply, [200, _, _, _]}, _} -> + ok + after 100 -> + error(timeout) + end. + +predicate_false(Config) -> + doc("Predicate function returns false, unconditionally disable tracing."), + Ref = config(ref, Config), + Opts = ranch:get_protocol_options(Ref), + ranch:set_protocol_options(Ref, Opts#{ + tracer_callback => do_tracer_callback(self()), + tracer_match_specs => [fun(_,_,_) -> false end] + }), + do_get("/", Config), + receive + Msg when element(1, Msg) =:= trace_ts -> + error(Msg) + after 100 -> + ok + end. + +method(Config) -> + doc("Method is the same as the request's, enable tracing."), + Ref = config(ref, Config), + Opts = ranch:get_protocol_options(Ref), + ranch:set_protocol_options(Ref, Opts#{ + tracer_callback => do_tracer_callback(self()), + tracer_match_specs => [{method, <<"GET">>}] + }), + do_get("/", Config), + receive + {trace_ts, _, call, {cowboy_req, reply, [200, _, _, _]}, _} -> + ok + after 100 -> + error(timeout) + end. + +method_no_match(Config) -> + doc("Method is different from the request's, disable tracing."), + Ref = config(ref, Config), + Opts = ranch:get_protocol_options(Ref), + ranch:set_protocol_options(Ref, Opts#{ + tracer_callback => do_tracer_callback(self()), + tracer_match_specs => [{method, <<"POST">>}] + }), + do_get("/", Config), + receive + Msg when element(1, Msg) =:= trace_ts -> + error(Msg) + after 100 -> + ok + end. + +host(Config) -> + doc("Host is the same as the request's, enable tracing."), + Ref = config(ref, Config), + Opts = ranch:get_protocol_options(Ref), + ranch:set_protocol_options(Ref, Opts#{ + tracer_callback => do_tracer_callback(self()), + tracer_match_specs => [{host, <<"localhost">>}] + }), + do_get("/", Config), + receive + {trace_ts, _, call, {cowboy_req, reply, [200, _, _, _]}, _} -> + ok + after 100 -> + error(timeout) + end. + +host_no_match(Config) -> + doc("Host is different from the request's, disable tracing."), + Ref = config(ref, Config), + Opts = ranch:get_protocol_options(Ref), + ranch:set_protocol_options(Ref, Opts#{ + tracer_callback => do_tracer_callback(self()), + tracer_match_specs => [{host, <<"ninenines.eu">>}] + }), + do_get("/", Config), + receive + Msg when element(1, Msg) =:= trace_ts -> + error(Msg) + after 100 -> + ok + end. + +path(Config) -> + doc("Path is the same as the request's, enable tracing."), + Ref = config(ref, Config), + Opts = ranch:get_protocol_options(Ref), + ranch:set_protocol_options(Ref, Opts#{ + tracer_callback => do_tracer_callback(self()), + tracer_match_specs => [{path, <<"/longer/hello/path">>}] + }), + do_get("/longer/hello/path", Config), + receive + {trace_ts, _, call, {cowboy_req, reply, [200, _, _, _]}, _} -> + ok + after 100 -> + error(timeout) + end. + +path_no_match(Config) -> + doc("Path is different from the request's, disable tracing."), + Ref = config(ref, Config), + Opts = ranch:get_protocol_options(Ref), + ranch:set_protocol_options(Ref, Opts#{ + tracer_callback => do_tracer_callback(self()), + tracer_match_specs => [{path, <<"/some/other/path">>}] + }), + do_get("/longer/hello/path", Config), + receive + Msg when element(1, Msg) =:= trace_ts -> + error(Msg) + after 100 -> + ok + end. + +path_start(Config) -> + doc("Start of path is the same as request's, enable tracing."), + Ref = config(ref, Config), + Opts = ranch:get_protocol_options(Ref), + ranch:set_protocol_options(Ref, Opts#{ + tracer_callback => do_tracer_callback(self()), + tracer_match_specs => [{path_start, <<"/longer/hello">>}] + }), + do_get("/longer/hello/path", Config), + receive + {trace_ts, _, call, {cowboy_req, reply, [200, _, _, _]}, _} -> + ok + after 100 -> + error(timeout) + end. + +path_start_no_match(Config) -> + doc("Start of path is different from the request's, disable tracing."), + Ref = config(ref, Config), + Opts = ranch:get_protocol_options(Ref), + ranch:set_protocol_options(Ref, Opts#{ + tracer_callback => do_tracer_callback(self()), + tracer_match_specs => [{path_start, <<"/shorter/hello">>}] + }), + do_get("/longer/hello/path", Config), + receive + Msg when element(1, Msg) =:= trace_ts -> + error(Msg) + after 100 -> + ok + end. + +header_defined(Config) -> + doc("Header is defined in the request, enable tracing."), + Ref = config(ref, Config), + Opts = ranch:get_protocol_options(Ref), + ranch:set_protocol_options(Ref, Opts#{ + tracer_callback => do_tracer_callback(self()), + tracer_match_specs => [{header, <<"accept-encoding">>}] + }), + do_get("/", Config), + receive + {trace_ts, _, call, {cowboy_req, reply, [200, _, _, _]}, _} -> + ok + after 100 -> + error(timeout) + end. + +header_defined_no_match(Config) -> + doc("Header is not defined in the request, disable tracing."), + Ref = config(ref, Config), + Opts = ranch:get_protocol_options(Ref), + ranch:set_protocol_options(Ref, Opts#{ + tracer_callback => do_tracer_callback(self()), + tracer_match_specs => [{header, <<"accept-language">>}] + }), + do_get("/", Config), + receive + Msg when element(1, Msg) =:= trace_ts -> + error(Msg) + after 100 -> + ok + end. + +header_value(Config) -> + doc("Header value is the same as the request's, enable tracing."), + Ref = config(ref, Config), + Opts = ranch:get_protocol_options(Ref), + ranch:set_protocol_options(Ref, Opts#{ + tracer_callback => do_tracer_callback(self()), + tracer_match_specs => [{header, <<"accept-encoding">>, <<"gzip">>}] + }), + do_get("/", Config), + receive + {trace_ts, _, call, {cowboy_req, reply, [200, _, _, _]}, _} -> + ok + after 100 -> + error(timeout) + end. + +header_value_no_match(Config) -> + doc("Header value is different from the request's, disable tracing."), + Ref = config(ref, Config), + Opts = ranch:get_protocol_options(Ref), + ranch:set_protocol_options(Ref, Opts#{ + tracer_callback => do_tracer_callback(self()), + tracer_match_specs => [{header, <<"accept-encoding">>, <<"nope">>}] + }), + do_get("/", Config), + receive + Msg when element(1, Msg) =:= trace_ts -> + error(Msg) + after 100 -> + ok + end. + +peer_ip(Config) -> + doc("Peer IP is the same as the request's, enable tracing."), + Ref = config(ref, Config), + Opts = ranch:get_protocol_options(Ref), + ranch:set_protocol_options(Ref, Opts#{ + tracer_callback => do_tracer_callback(self()), + tracer_match_specs => [{peer_ip, {127, 0, 0, 1}}] + }), + do_get("/", Config), + receive + {trace_ts, _, call, {cowboy_req, reply, [200, _, _, _]}, _} -> + ok + after 100 -> + error(timeout) + end. + +peer_ip_no_match(Config) -> + doc("Peer IP is different from the request's, disable tracing."), + Ref = config(ref, Config), + Opts = ranch:get_protocol_options(Ref), + ranch:set_protocol_options(Ref, Opts#{ + tracer_callback => do_tracer_callback(self()), + tracer_match_specs => [{peer_ip, {8, 8, 8, 8}}] + }), + do_get("/", Config), + receive + Msg when element(1, Msg) =:= trace_ts -> + error(Msg) + after 100 -> + ok + end. + +missing_callback(Config) -> + doc("Ensure the request is still processed if the callback is not provided."), + Ref = config(ref, Config), + Opts0 = ranch:get_protocol_options(Ref), + Opts = maps:remove(tracer_callback, Opts0), + ranch:set_protocol_options(Ref, Opts#{ + tracer_match_specs => [{method, <<"GET">>}] + }), + do_get("/", Config), + receive + Msg when element(1, Msg) =:= trace_ts -> + error(Msg) + after 100 -> + ok + end. + +missing_match_specs(Config) -> + doc("Ensure the request is still processed if match specs are not provided."), + Ref = config(ref, Config), + Opts0 = ranch:get_protocol_options(Ref), + Opts = maps:remove(tracer_match_specs, Opts0), + ranch:set_protocol_options(Ref, Opts#{ + tracer_callback => do_tracer_callback(self()) + }), + do_get("/", Config), + receive + Msg when element(1, Msg) =:= trace_ts -> + error(Msg) + after 100 -> + ok + end. + +two_matching_requests(Config) -> + doc("Perform two requests that enable tracing on the same connection."), + Ref = config(ref, Config), + Opts = ranch:get_protocol_options(Ref), + ranch:set_protocol_options(Ref, Opts#{ + tracer_callback => do_tracer_callback(self()), + tracer_match_specs => [fun(_,_,_) -> true end] + }), + %% Perform a GET request. + ConnPid = gun_open(Config), + Ref1 = gun:get(ConnPid, "/", []), + {response, nofin, 200, _} = gun:await(ConnPid, Ref1), + {ok, _} = gun:await_body(ConnPid, Ref1), + receive + {trace_ts, _, call, {cowboy_req, reply, [200, _, _, _]}, _} -> + ok + after 100 -> + error(timeout) + end, + %% Perform a second GET request on the same connection. + Ref2 = gun:get(ConnPid, "/", []), + {response, nofin, 200, _} = gun:await(ConnPid, Ref2), + {ok, _} = gun:await_body(ConnPid, Ref2), + receive + {trace_ts, _, call, {cowboy_req, reply, [200, _, _, _]}, _} -> + ok + after 100 -> + error(timeout) + end, + gun:close(ConnPid). |