1%% Copyright (c) 2014, 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_test). 16-compile(export_all). 17 18%% Start and stop applications and their dependencies. 19 20start(Apps) -> 21 _ = [do_start(App) || App <- Apps], 22 ok. 23 24do_start(App) -> 25 case application:start(App) of 26 ok -> 27 ok; 28 {error, {not_started, Dep}} -> 29 do_start(Dep), 30 do_start(App) 31 end. 32 33%% SSL certificate creation and safekeeping. 34 35make_certs() -> 36 {_, Cert, Key} = ct_helper:make_certs(), 37 CertOpts = [{cert, Cert}, {key, Key}], 38 Pid = spawn(fun() -> receive after infinity -> ok end end), 39 ?MODULE = ets:new(?MODULE, [ordered_set, public, named_table, 40 {heir, Pid, undefined}]), 41 ets:insert(?MODULE, {cert_opts, CertOpts}), 42 ok. 43 44get_certs() -> 45 ets:lookup_element(?MODULE, cert_opts, 2). 46 47%% Quick configuration value retrieval. 48 49config(Key, Config) -> 50 {_, Value} = lists:keyfind(Key, 1, Config), 51 Value. 52 53%% Test case description. 54 55doc(String) -> 56 ct:comment(String), 57 ct:log(String). 58 59%% List of all test cases in the suite. 60 61all(Suite) -> 62 lists:usort([F || {F, 1} <- Suite:module_info(exports), 63 F =/= module_info, 64 F =/= test, %% This is leftover from the eunit parse_transform... 65 F =/= all, 66 F =/= groups, 67 string:substr(atom_to_list(F), 1, 5) =/= "init_", 68 string:substr(atom_to_list(F), 1, 4) =/= "end_", 69 string:substr(atom_to_list(F), 1, 3) =/= "do_" 70 ]). 71 72%% Listeners initialization. 73 74init_http(Ref, ProtoOpts, Config) -> 75 {ok, _} = cowboy:start_http(Ref, 100, [{port, 0}], [ 76 {max_keepalive, 50}, 77 {timeout, 500} 78 |ProtoOpts]), 79 Port = ranch:get_port(Ref), 80 [{type, tcp}, {port, Port}, {opts, []}|Config]. 81 82init_https(Ref, ProtoOpts, Config) -> 83 Opts = get_certs(), 84 {ok, _} = cowboy:start_https(Ref, 100, Opts ++ [{port, 0}], [ 85 {max_keepalive, 50}, 86 {timeout, 500} 87 |ProtoOpts]), 88 Port = ranch:get_port(Ref), 89 [{type, ssl}, {port, Port}, {opts, Opts}|Config]. 90 91init_spdy(Ref, ProtoOpts, Config) -> 92 Opts = get_certs(), 93 {ok, _} = cowboy:start_spdy(Ref, 100, Opts ++ [{port, 0}], 94 ProtoOpts), 95 Port = ranch:get_port(Ref), 96 [{type, ssl}, {port, Port}, {opts, Opts}|Config]. 97 98%% Common group of listeners used by most suites. 99 100common_all() -> 101 [ 102 {group, http}, 103 {group, https}, 104 {group, spdy}, 105 {group, http_compress}, 106 {group, https_compress}, 107 {group, spdy_compress} 108 ]. 109 110common_groups(Tests) -> 111 [ 112 {http, [parallel], Tests}, 113 {https, [parallel], Tests}, 114 {spdy, [parallel], Tests}, 115 {http_compress, [parallel], Tests}, 116 {https_compress, [parallel], Tests}, 117 {spdy_compress, [parallel], Tests} 118 ]. 119 120init_common_groups(Name = http, Config, Mod) -> 121 init_http(Name, [ 122 {env, [{dispatch, Mod:init_dispatch(Config)}]} 123 ], Config); 124init_common_groups(Name = https, Config, Mod) -> 125 init_https(Name, [ 126 {env, [{dispatch, Mod:init_dispatch(Config)}]} 127 ], Config); 128init_common_groups(Name = spdy, Config, Mod) -> 129 init_spdy(Name, [ 130 {env, [{dispatch, Mod:init_dispatch(Config)}]} 131 ], Config); 132init_common_groups(Name = http_compress, Config, Mod) -> 133 init_http(Name, [ 134 {env, [{dispatch, Mod:init_dispatch(Config)}]}, 135 {compress, true} 136 ], Config); 137init_common_groups(Name = https_compress, Config, Mod) -> 138 init_https(Name, [ 139 {env, [{dispatch, Mod:init_dispatch(Config)}]}, 140 {compress, true} 141 ], Config); 142init_common_groups(Name = spdy_compress, Config, Mod) -> 143 init_spdy(Name, [ 144 {env, [{dispatch, Mod:init_dispatch(Config)}]}, 145 {compress, true} 146 ], Config). 147 148%% Support functions for testing using Gun. 149 150gun_open(Config) -> 151 gun_open(Config, []). 152 153gun_open(Config, Opts) -> 154 {ok, ConnPid} = gun:open("localhost", config(port, Config), 155 [{retry, 0}, {type, config(type, Config)}|Opts]), 156 ConnPid. 157 158gun_monitor_open(Config) -> 159 gun_monitor_open(Config, []). 160 161gun_monitor_open(Config, Opts) -> 162 ConnPid = gun_open(Config, Opts), 163 {ConnPid, monitor(process, ConnPid)}. 164 165gun_is_gone(ConnPid, MRef) -> 166 receive {'DOWN', MRef, process, ConnPid, gone} -> ok 167 after 500 -> error(timeout) end. 168 169%% Support functions for testing using a raw socket. 170 171raw_open(Config) -> 172 Transport = case config(type, Config) of 173 tcp -> gen_tcp; 174 ssl -> ssl 175 end, 176 {_, Opts} = lists:keyfind(opts, 1, Config), 177 {ok, Socket} = Transport:connect("localhost", config(port, Config), 178 [binary, {active, false}, {packet, raw}, 179 {reuseaddr, true}, {nodelay, true}|Opts]), 180 {raw_client, Socket, Transport}. 181 182raw_send({raw_client, Socket, Transport}, Data) -> 183 Transport:send(Socket, Data). 184 185raw_recv_head({raw_client, Socket, Transport}) -> 186 {ok, Data} = Transport:recv(Socket, 0, 5000), 187 raw_recv_head(Socket, Transport, Data). 188 189raw_recv_head(Socket, Transport, Buffer) -> 190 case binary:match(Buffer, <<"\r\n\r\n">>) of 191 nomatch -> 192 {ok, Data} = Transport:recv(Socket, 0, 5000), 193 raw_recv_head(Socket, Transport, << Buffer/binary, Data/binary >>); 194 {_, _} -> 195 Buffer 196 end. 197 198raw_expect_recv({raw_client, Socket, Transport}, Expect) -> 199 {ok, Expect} = Transport:recv(Socket, iolist_size(Expect), 5000), 200 ok. 201