%% %% %CopyrightBegin% %% %% Copyright Ericsson AB 2012-2023. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. %% You may obtain a copy of the License at %% %% http://www.apache.org/licenses/LICENSE-2.0 %% %% Unless required by applicable law or agreed to in writing, software %% distributed under the License is distributed on an "AS IS" BASIS, %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %% See the License for the specific language governing permissions and %% limitations under the License. %% %% %CopyrightEnd% %% %% -module(httpd_bench_SUITE). -compile(export_all). -include_lib("common_test/include/ct.hrl"). -include_lib("common_test/include/ct_event.hrl"). -include_lib("public_key/include/public_key.hrl"). -include_lib("kernel/include/file.hrl"). -define(remote_host, "NETMARKS_REMOTE_HOST"). -define(LF, [10]). -define(CR, [13]). -define(CRLF, ?CR ++ ?LF). %%-------------------------------------------------------------------- %% Common Test interface functions ----------------------------------- %%-------------------------------------------------------------------- suite() -> [{timetrap, {minutes, 1}}, {ct_hooks,[{ts_install_cth,[{nodenames,2}]}]}]. all() -> [ {group, http_dummy}, {group, http_inets}, {group, http_nginx}, {group, https_inets}, {group, https_dummy}, {group, https_nginx}, {group, http_dummy_keep_alive}, {group, http_inets_keep_alive}, {group, http_nginx_keep_alive}, {group, https_inets_keep_alive}, {group, https_dummy_keep_alive}, {group, https_nginx_keep_alive} ]. groups() -> [ {http_dummy, [], client_tests()}, {http_inets, [], client_tests()}, {http_nginx, [], client_tests()}, {https_dummy, [], client_tests()}, {https_inets, [], client_tests()}, {https_nginx, [], client_tests()}, {http_dummy_keep_alive, [], client_tests()}, {http_inets_keep_alive, [], client_tests()}, {http_nginx_keep_alive, [], client_tests()}, {https_dummy_keep_alive, [], client_tests()}, {https_inets_keep_alive, [], client_tests()}, {https_nginx_keep_alive, [], client_tests()} ]. client_tests() -> [wget_small, erl_dummy_small, httpc_small, wget_big, erl_dummy_big, httpc_big ]. init_per_suite(Config) -> try {Node, Host} = setup(Config, node()), init_ssl(Config), [{iter, 10}, {server_node, Node}, {server_host, Host} | Config] catch E:R:ST -> ct:pal("~p:~p:~p",[E,R,ST]), {skipped, "Benchmark machines only"} end. end_per_suite(_Config) -> [application:stop(App) || App <- [asn1, crypto, public_key, ssl, inets]]. init_per_group(Group, Config) when Group == http_dummy_keep_alive; Group == https_dummy_keep_alive; Group == http_inets_keep_alive; Group == https_inets_keep_alive; Group == http_nginx_keep_alive; Group == https_nginx_keep_alive -> Version = http_version(Group), start_web_server(Group, [{keep_alive, true}, {reuse_sessions, false}, {http_version, Version}, {http_opts,[{version, Version}]}, {http_headers, [{"connection", "keep-alive"}]}, {httpc_opts, [{keep_alive_timeout, 1500}, {max_keep_alive_length, ?config(iter, Config)}]} | Config]); init_per_group(Group, Config) when Group == http_dummy; Group == https_dummy; Group == http_inets; Group == https_inets; Group == http_nginx; Group == https_nginx -> Version = http_version(Group), start_web_server(Group, [{keep_alive, false}, {reuse_sessions, false}, {http_version, Version}, {http_headers, [{"connection", "close"}]}, {http_opts,[{version, Version}]}, {httpc_opts, [{keep_alive_timeout, 0}, {max_keep_alive_length, 0}]} | Config]); init_per_group(_, Config) -> Config. end_per_group(Group, Config) -> stop_web_server(Group, Config). init_per_testcase(TestCase, Config) when TestCase == httpc_small; TestCase == httpc_big -> Opts = ?config(httpc_opts, Config), inets:start(httpc, [{profile, TestCase}, {socket_opts, [{nodelay, true}]}]), httpc:set_options(Opts, TestCase), [{profile, TestCase} | proplists:delete(profile, Config)]; init_per_testcase(_, Config) -> Config. end_per_testcase(TestCase, _Config) when TestCase == httpc_small; TestCase == httpc_big -> ok = inets:stop(httpc, TestCase); end_per_testcase(_TestCase, Config) -> Config. %%-------------------------------------------------------------------- %% Test Cases -------------------------------------------------------- %%-------------------------------------------------------------------- erl_dummy_small(Config) when is_list(Config) -> {ok, Result} = run_test(httpd_lib_client, "1k_file", Config), notify(Result, Config, "erl_1k_file"). erl_dummy_big(Config) when is_list(Config) -> {ok, Result} = run_test(httpd_lib_client, "1M_file", Config), notify(Result, Config, "erl_1M_file"). wget_small(Config) when is_list(Config) -> {ok, Result} = run_test(wget_client, "1k_file", Config), notify(Result, Config, "wget_1k_file"). wget_big(Config) when is_list(Config) -> {ok, Result} = run_test(wget_client, "1M_file", Config), notify(Result, Config, "wget_1M_file"). httpc_small(Config) when is_list(Config) -> {ok, Result} = run_test(httpc_client, "1k_file", Config), notify(Result, Config, "httpc_1k_file"). httpc_big(Config) when is_list(Config) -> {ok, Result} = run_test(httpc_client, "1M_file", Config), notify(Result, Config, "httpc_1M_file"). %%-------------------------------------------------------------------- %% Internal functions ------------------------------------------------ %%-------------------------------------------------------------------- %%-------------------------------------------------------------------- %% Report benchmark results ------------------------------------------------ %%-------------------------------------------------------------------- notify({TestPerSec, _MBps}, Config, Suffix) -> Name = lists:concat([?config(protocol,Config), " ", server_name(Config, [dummy_pid, httpd_pid, nginx_port]), "", Suffix]), ct:comment("~p tps", [TestPerSec]), ct_event:notify(#event{name = benchmark_data, data=[{value, TestPerSec}, {suite, ?MODULE}, {name, Name}]}), ok. %%-------------------------------------------------------------------- %% Setup erlang nodes ------------------------------------------------ %%-------------------------------------------------------------------- server_name(Config, [Server | Rest]) -> case proplists:get_value(Server, Config) of undefined -> server_name(Config, Rest); _ -> server_name(Server) end. server_name(httpd_pid) -> "inets"; server_name(nginx_port) -> "nginx"; server_name(dummy_pid) -> "erlang". setup(_Config, nonode@nohost) -> exit(dist_not_enabled); setup(_Config, _LocalNode) -> Host = case os:getenv(?remote_host) of false -> {ok, This} = inet:gethostname(), This; RemHost -> RemHost end, Node = list_to_atom("inets_perf_server@" ++ Host), SlaveArgs = case init:get_argument(pa) of {ok, PaPaths} -> lists:append([" -pa " ++ P || [P] <- PaPaths]); _ -> [] end, Prog = case os:find_executable("erl") of false -> "erl"; P -> P end, case net_adm:ping(Node) of pong -> ok; pang -> {ok, Node} = slave:start(Host, inets_perf_server, SlaveArgs, no_link, Prog) end, Path = code:get_path(), true = rpc:call(Node, code, set_path, [Path]), [ensure_started(Node, App) || App <- [asn1, crypto, public_key, ssl, inets]], [ensure_started(node(), App) || App <- [asn1, crypto, public_key, ssl, inets]], (Node =:= node()) andalso restrict_schedulers(client), {Node, Host}. ensure_started(Node, App) -> ok = rpc:call(Node, application, ensure_started, [App]). restrict_schedulers(Type) -> %% We expect this to run on 8 core machine Extra0 = 1, Extra = if (Type =:= server) -> -Extra0; true -> Extra0 end, Scheds = erlang:system_info(schedulers), erlang:system_flag(schedulers_online, (Scheds div 2) + Extra). %%-------------------------------------------------------------------- %% Setup TLS input files ------------------------------------------------ %%-------------------------------------------------------------------- init_ssl(Config) -> DDir = ?config(data_dir, Config), PDir = ?config(priv_dir, Config), {ok, _} = make_certs:all(DDir, PDir). cert_opts(Config) -> ClientCaCertFile = filename:join([?config(priv_dir, Config), "client", "cacerts.pem"]), ClientCertFile = filename:join([?config(priv_dir, Config), "client", "cert.pem"]), ServerCaCertFile = filename:join([?config(priv_dir, Config), "server", "cacerts.pem"]), ServerCertFile = filename:join([?config(priv_dir, Config), "server", "cert.pem"]), ServerKeyFile = filename:join([?config(priv_dir, Config), "server", "key.pem"]), ClientKeyFile = filename:join([?config(priv_dir, Config), "client", "key.pem"]), [{server_verification_opts, [{cacertfile, ServerCaCertFile}, {ciphers, ["ECDHE-RSA-AES256-GCM-SHA384", "TLS_AES_256_GCM_SHA384"]}, {certfile, ServerCertFile}, {keyfile, ServerKeyFile}]}, {client_verification_opts, [ %%{verify, verify_peer}, {cacertfile, ClientCaCertFile}, {certfile, ClientCertFile}, {keyfile, ClientKeyFile}]}]. %%-------------------------------------------------------------------- %% Run clients ------------------------------------------------ %%-------------------------------------------------------------------- run_test(Client, File, Config) -> Parent = self(), Pid = spawn(fun() -> receive go -> Parent ! {self(), do_runs(Client, [{file, File} | Config])} end end), Pid ! go, receive {Pid,{{tps, Tps}, {mbps, MBps}}} -> ct:pal("Tps: ~p Bps~p", [Tps, MBps]), {ok, {Tps, MBps}} end. do_runs(Client, Config) -> N = ?config(iter, Config), DataDir = ?config(data_dir, Config), File = ?config(file, Config), Name = filename:join(DataDir, File), Args = ?MODULE:Client(Config), ?MODULE:Client({init, Args}), Run = fun() -> ok = ?MODULE:Client(Args, N) end, {ok, Info} = file:read_file_info(Name, []), Length = Info#file_info.size, {TimeInMicro, _} = timer:tc(Run), ReqPerSecond = (1000000 * N) div TimeInMicro, BytesPerSecond = (1000000 * N * Length) div TimeInMicro, {{tps, ReqPerSecond}, {mbps, BytesPerSecond}}. httpc_client({init, [_, Profile, URL, Headers, HTTPOpts]}) -> %% Make sure pipelining feature will kick in when appropriate. {ok, {{_ ,200, "OK"}, _,_}} = httpc:request(get,{URL, Headers}, HTTPOpts, [{body_format, binary}, {socket_opts, [{nodelay, true}]}], Profile), ct:sleep(1000); httpc_client(Config) -> File = ?config(file, Config), Protocol = ?config(protocol, Config), Profile = ?config(profile, Config), URL = (?config(urlfun,Config))(File), Headers = ?config(http_headers, Config), HTTPOpts = ?config(http_opts, Config), [Protocol, Profile, URL, Headers, HTTPOpts]. httpc_client(_,0) -> ok; httpc_client([Protocol, Profile, URL, Headers, HTTPOpts], N) -> {ok, {{_ ,200,"OK"}, _,_}} = httpc:request(get,{URL, Headers}, HTTPOpts, [{body_format, binary}, {socket_opts, [{nodelay, true}]}], Profile), httpc_client([Protocol, Profile, URL, Headers, HTTPOpts], N-1). httpd_lib_client({init, [_, Type, Version, Request, Host, Port, Opts]}) -> ok = httpd_test_lib:verify_request(Type, Host, Port, Opts, node(), Request, [{statuscode, 200}, {version, Version}], infinity), ct:sleep(1000); httpd_lib_client(Config) -> File = ?config(file, Config), KeepAlive = ?config(keep_alive, Config), Host = ?config(server_host, Config), Port = ?config(port, Config), ReuseSession = ?config(reuse_sessions, Config), {Type, Opts} = case ?config(protocol, Config) of "http" -> {ip_comm, [{active, true}, {mode, binary},{nodelay, true}]}; "https" -> SSLOpts = proplists:get_value(client_verification_opts, cert_opts(Config)), {ssl, [{active, true}, {mode, binary}, {nodelay, true}, {reuse_sessions, ReuseSession} | SSLOpts]} end, Version = ?config(http_version, Config), Request = case KeepAlive of true -> http_request("GET /" ++ File ++ " ", Version, Host, {"connection:keep-alive\r\n", ""}); false -> http_request("GET /" ++ File ++ " ", Version, Host) end, Args = [KeepAlive, Type, Version, Request, Host, Port, Opts], httpd_lib_client(Args, 1), Args. httpd_lib_client(_, 0) -> ok; httpd_lib_client([true, Type, Version, Request, Host, Port, Opts], N) -> ok = httpd_test_lib:verify_request_N(Type, Host, Port, Opts, node(), Request, [{statuscode, 200}, {version, Version}], infinity, N); httpd_lib_client([false, Type, Version, Request, Host, Port, Opts] = List, N) -> ok = httpd_test_lib:verify_request(Type, Host, Port, Opts, node(), Request, [{statuscode, 200}, {version, Version}], infinity), httpd_lib_client(List, N-1). wget_client({init,_}) -> ok; wget_client(Config) -> File = ?config(file, Config), URL = (?config(urlfun,Config))(File), KeepAlive = ?config(keep_alive, Config), PrivDir = ?config(priv_dir, Config), Protocol = ?config(protocol, Config), Iter = ?config(iter, Config), FileName = filename:join(PrivDir, "wget_req"), ProtocolOpts = case Protocol of "http" -> []; "https" -> proplists:get_value(client_verification_opts, cert_opts(Config)) end, wget_req_file(FileName,URL,Iter), [KeepAlive, FileName, URL, Protocol, ProtocolOpts, Iter]. wget_client([KeepAlive, WgetFile, _URL, Protocol, ProtocolOpts, _], _) -> process_flag(trap_exit, true), Cmd = wget_N(KeepAlive, WgetFile, Protocol, ProtocolOpts), %%ct:pal("Wget cmd: ~p", [Cmd]), Port = open_port({spawn, Cmd}, [stderr_to_stdout]), wait_for_wget(Port). %%-------------------------------------------------------------------- %% Start/stop servers ------------------------------------------------ %%-------------------------------------------------------------------- start_web_server(Group, Config) when Group == http_dummy; Group == http_dummy_keep_alive -> start_dummy("http", Config); start_web_server(Group, Config) when Group == https_dummy; Group == https_dummy_keep_alive -> start_dummy("https", Config); start_web_server(Group, Config) when Group == http_inets; Group == http_inets_keep_alive -> start_inets("http", [], Config); start_web_server(Group, Config) when Group == https_inets; Group == https_inets_keep_alive -> Opts = proplists:get_value(server_verification_opts, cert_opts(Config)), ReuseSessions = ?config(reuse_sessions, Config), SSLConfHttpd = [{socket_type, {ssl, [{nodelay, true}, {reuse_sessions, ReuseSessions} | Opts]}}], start_inets("https", SSLConfHttpd, Config); start_web_server(Group, Config) when Group == http_nginx; Group == http_nginx_keep_alive -> case os:find_executable("nginx") of false -> {skip, "nginx not found"}; _ -> start_nginx("http", Config) end; start_web_server(Group, Config) when Group == https_nginx; Group == https_nginx_keep_alive -> case os:find_executable("nginx") of false -> {skip, "nginx not found"}; _ -> start_nginx("https", cert_opts(Config) ++ Config) end. start_inets(Protocol, ConfHttpd, Config) -> PrivDir = ?config(priv_dir, Config), DataDir = ?config(data_dir, Config), Node = ?config(server_node, Config), Host = ?config(server_host, Config), HTTPVersion = ?config(http_version, Config), Conf = [httpd, [{port,0}, {http_version, HTTPVersion}, {ipfamily, inet}, {server_name, "inets_test"}, {server_root, PrivDir}, {document_root, DataDir}, {keep_alive, ?config(keep_alive, Config)}, {keep_alive_timeout, 360} | ConfHttpd]], {ok, Pid} = rpc:call(Node, inets, start, Conf), Port = proplists:get_value(port, rpc:call(Node, httpd, info, [Pid])), F = fun(File) -> lists:concat([Protocol,"://",Host,":",Port,"/",File]) end, [{httpd_pid,Pid},{urlfun,F},{protocol,Protocol},{port,Port} | Config]. start_dummy("http"= Protocol, Config) -> HTTPVersion = ?config(http_version, Config), Node = ?config(server_node, Config), %%DataDir= ?config(data_dir, Config), Host = ?config(server_host, Config), Conf = [ %%{big, filename:join(DataDir, "1M_file")}, %%{small, filename:join(DataDir, "1k_file")}, {big, {gen, crypto:strong_rand_bytes(1000000)}}, {small, {gen, crypto:strong_rand_bytes(1000)}}, {http_version, HTTPVersion}, {keep_alive, ?config(keep_alive, Config)} ], {Pid, Port} = rpc:call(Node, http_test_lib, dummy_server, [ip_comm, inet, [{content_cb, ?MODULE}, {conf, Conf}]]), F = fun(File) -> lists:concat([Protocol,"://",Host,":",Port,"/",File]) end, [{dummy_pid,Pid},{urlfun,F},{protocol, Protocol},{port,Port} | Config]; start_dummy("https" = Protocol, Config) -> HTTPVersion = ?config(http_version, Config), Node = ?config(server_node, Config), %% DataDir= ?config(data_dir, Config), Host = ?config(server_host, Config), SSLOpts = proplists:get_value(server_verification_opts, cert_opts(Config)), Opts = [{active, true}, {nodelay, true} | SSLOpts], Conf = [%%{big, filename:join(DataDir, "1M_file")}, %%{small, filename:join(DataDir, "1k_file")}, {big, {gen, crypto:strong_rand_bytes(1000000)}}, {small, {gen, crypto:strong_rand_bytes(1000)}}, {http_version, HTTPVersion}, {keep_alive, ?config(keep_alive, Config)} ], {Pid, Port} = rpc:call(Node, http_test_lib, dummy_server, [ssl, inet, [{ssl, Opts}, {content_cb, ?MODULE}, {conf, Conf}]]), F = fun(File) -> lists:concat([Protocol,"://",Host,":",Port,"/",File]) end, [{dummy_pid,Pid},{urlfun,F},{protocol,Protocol},{port,Port} | Config]. start_nginx(Protocol, Config) -> PrivDir = ?config(priv_dir, Config), DataDir = ?config(data_dir, Config), Host = ?config(server_host, Config), Port = inet_port(node()), ConfFile = filename:join(PrivDir, "nginx.conf"), nginx_conf(ConfFile, [{port, Port}, {protocol, Protocol} | Config]), Cmd = "nginx -c " ++ ConfFile, NginxPort = open_port({spawn, Cmd}, [{cd, DataDir}, stderr_to_stdout]), F = fun(File) -> lists:concat([Protocol,"://",Host,":",Port,"/",File]) end, wait_for_nginx_up(Host, Port), [{port, Port},{nginx_port, NginxPort},{urlfun,F},{protocol, Protocol} | Config ]. stop_nginx(Config)-> PrivDir = ?config(priv_dir, Config), {ok, Bin} = file:read_file(filename:join(PrivDir, "nginx.pid")), Pid = string:strip(binary_to_list(Bin), right, $\n), Cmd = "kill " ++ Pid, os:cmd(Cmd). stop_web_server(Group, Config) when Group == http_inets; Group == http_inets_keep_alive; Group == https_inets; Group == https_inets_keep_alive -> ServerNode = ?config(server_node, Config), rpc:call(ServerNode, inets, stop, [httpd, ?config(httpd_pid, Config)]); stop_web_server(Group, Config) when Group == http_dummy; Group == http_dummy_keep_alive; Group == https_dummy; Group == https_dummy_keep_alive -> stop_dummy_server(Config); stop_web_server(Group, Config) when Group == http_nginx; Group == http_nginx_keep_alive; Group == https_nginx; Group == https_nginx_keep_alive -> stop_nginx(Config). stop_dummy_server(Config) -> case ?config(dummy_pid, Config) of Pid when is_pid(Pid) -> exit(Pid, kill); _ -> ok end. %%-------------------------------------------------------------------- %% Misc ------------------------------------------------ %%-------------------------------------------------------------------- http_request(Request, "HTTP/1.1" = Version, Host, {Headers, Body}) -> Request ++ Version ++ "\r\nhost:" ++ Host ++ "\r\n" ++ Headers ++ "\r\n" ++ Body; http_request(Request, Version, _, {Headers, Body}) -> Request ++ Version ++ "\r\n" ++ Headers ++ "\r\n" ++ Body. http_request(Request, "HTTP/1.1" = Version, Host) -> Request ++ Version ++ "\r\nhost:" ++ Host ++ "\r\n\r\n"; http_request(Request, Version, _) -> Request ++ Version ++ "\r\n\r\n". http_version(_) -> "HTTP/1.1". inet_port(Node) -> {Port, Socket} = do_inet_port(Node), rpc:call(Node, gen_tcp, close, [Socket]), Port. do_inet_port(Node) -> {ok, Socket} = rpc:call(Node, gen_tcp, listen, [0, [{reuseaddr, true}]]), {ok, Port} = rpc:call(Node, inet, port, [Socket]), {Port, Socket}. %%-------------------------------------------------------------------- %% Dummy server callbacks ------------------------------------------------ %%-------------------------------------------------------------------- handle_request(CB, S, "/1M_file" ++ _, Opts) -> Name = proplists:get_value(big, Opts), KeepAlive = proplists:get_value(keep_alive, Opts), do_handle_request(CB, S, Name, Opts, KeepAlive); handle_request(CB, S, "/1k_file" ++ _, Opts) -> Name = proplists:get_value(small, Opts), KeepAlive = proplists:get_value(keep_alive, Opts), do_handle_request(CB, S, Name, Opts, KeepAlive). do_handle_request(CB, S, Name, Opts, KeepAlive) when is_list(Name) -> Version = proplists:get_value(http_version, Opts), {ok, Fdesc} = file:open(Name, [read, binary]), {ok, Info} = file:read_file_info(Name, []), Length = Info#file_info.size, Response = response_status_line_and_headers(Version, "Content-Length:" ++ integer_to_list(Length) ++ ?CRLF, keep_alive(KeepAlive)), CB:send(S, Response), send_file(CB, S, Fdesc); do_handle_request(CB, S, {gen, Data}, Opts, KeepAlive) -> Version = proplists:get_value(http_version, Opts), Length = byte_size(Data), Response = response_status_line_and_headers(Version, "Content-Length:" ++ integer_to_list(Length) ++ ?CRLF, keep_alive(KeepAlive)), CB:send(S, Response), send_file(CB, S, {gen, Data}). send_file(CB, S, {gen, Data}) -> CB:send(S, Data); %% ChunkSize = 64*1024, %% case byte_size(Data) of %% N when N > ChunkSize -> %% <> = Data, %% %%{Chunk, Rest} = lists:split(N, Data), %% CB:send(S, Chunk), %% send_file(CB, S, {gen, Rest}); %% _ -> %% CB:send(S, Data) %% end; send_file(CB, S, FileDesc) -> case file:read(FileDesc, 64*1024) of {ok, Chunk} -> CB:send(S, Chunk), send_file(CB, S, FileDesc); eof -> file:close(FileDesc), ok end. response_status_line_and_headers(Version, Headers, ConnectionHeader) -> StatusLine = [Version, " ", "200 OK", ?CRLF], [StatusLine, Headers, ConnectionHeader, ?CRLF]. keep_alive(true)-> "Connection:keep-alive\r\n"; keep_alive(false) -> "Connection:close\r\n". handle_http_msg({_Method, RelUri, _, {_, _Headers}, _Body}, Socket, Conf) -> handle_request(connect_cb(Socket), Socket, RelUri, Conf), case proplists:get_value(keep_alive, Conf) of true -> <<>>; false -> stop end. connect_cb({sslsocket, _, _}) -> ssl; connect_cb(_) -> gen_tcp. %%-------------------------------------------------------------------- %% Setup wget ------------------------------------------------ %%-------------------------------------------------------------------- wget_req_file(FileName, Url, Iter) -> {ok, File} = file:open(FileName, [write]), write_urls(File, Url, Iter). write_urls(File, Url, 1) -> file:write(File, Url), file:close(File); write_urls(File, Url, N) -> file:write(File, Url), file:write(File, "\n"), write_urls(File, Url, N-1). wait_for_wget(Port) -> receive {Port, {data, _Data}} when is_port(Port) -> wait_for_wget(Port); {Port, closed} -> ok; {'EXIT', Port, _Reason} -> ok end. wget_N(KeepAlive, WegetFile, "http", _ProtocolOpts) -> "wget -i " ++ WegetFile ++ " " ++ wget_keep_alive(KeepAlive) ++ " --no-cache --timeout=120" ; wget_N(KeepAlive, WegetFile, "https", ProtocolOpts) -> "wget -i " ++ WegetFile ++ " " ++ wget_keep_alive(KeepAlive) ++ wget_cert(ProtocolOpts) ++ wget_key(ProtocolOpts) ++ wget_cacert(ProtocolOpts) ++ " --no-cache --timeout=120". wget(KeepAlive, URL, "http", _ProtocolOpts) -> "wget " ++ URL ++ " " ++ wget_keep_alive(KeepAlive) ++ " --no-cache --timeout=120" ; wget(KeepAlive, URL, "https", ProtocolOpts) -> "wget " ++ URL ++ " " ++ wget_keep_alive(KeepAlive) ++ wget_cert(ProtocolOpts) ++ wget_key(ProtocolOpts) ++ wget_cacert(ProtocolOpts) ++ " --no-cache --timeout=120". wget_keep_alive(true)-> ""; wget_keep_alive(false) -> "--no-http-keep-alive ". wget_cacert(ProtocolOpts) -> "--ca-certificate=" ++ proplists:get_value(cacertfile, ProtocolOpts) ++ " ". wget_cert(ProtocolOpts) -> "--certificate=" ++ proplists:get_value(certfile, ProtocolOpts) ++ " ". wget_key(ProtocolOpts) -> "--private-key=" ++ proplists:get_value(keyfile, ProtocolOpts) ++ " ". %%-------------------------------------------------------------------- %% Setup nginx ------------------------------------------------ %%-------------------------------------------------------------------- nginx_conf(ConfFile, Config)-> Protocol = ?config(protocol, Config), file:write_file(ConfFile, [format_nginx_conf(nginx_global(Config)), format_nginx_conf(nginx_events(Config)), format_nginx_conf(nginx_http(Protocol, Config))]). format_nginx_conf(Directives) -> lists:map(fun({Key, Value}) -> io_lib:format("~s ~s;\n", [Key, Value]); (Str) -> Str end, Directives). nginx_global(Config) -> PrivDir = ?config(priv_dir, Config), [{"pid", filename:join(PrivDir, "nginx.pid")}, {"error_log", filename:join(PrivDir, "nginx.pid")}, {"worker_processes", "1"}]. nginx_events(_Config) -> ["events {\n", {"worker_connections", "1024"}, "\n}" ]. nginx_http("http", Config) -> PrivDir = ?config(priv_dir, Config), DataDir = ?config(data_dir, Config), Port = ?config(port, Config), ["http {\n" | nginx_defaults(PrivDir) ++ [" server {", {root, DataDir}, {listen, integer_to_list(Port)}, " location / {\n try_files $uri $uri/ /index.html;\n}" "}\n", "}\n" ] ]; nginx_http("https", Config) -> PrivDir = ?config(priv_dir, Config), DataDir = ?config(data_dir, Config), Port = ?config(port, Config), SSLOpts = ?config(server_verification_opts, Config), Ciphers = proplists:get_value(ciphers, SSLOpts), ReuseSession = ?config(reuse_sessions, Config), ["http {" | nginx_defaults(PrivDir) ++ [" server {", {"root", DataDir}, {"listen", integer_to_list(Port) ++ " ssl"}, {"ssl_certificate", ?config(certfile, SSLOpts)}, {"ssl_certificate_key", ?config(keyfile, SSLOpts)}, {"ssl_protocols", "TLSv1 TLSv1.1 TLSv1.2"}, {"ssl_ciphers", Ciphers}, {"ssl_session_cache", nginx_reuse_session(ReuseSession)}, " location / {\n try_files $uri $uri/ /index.html;\n}" "}\n", "}\n" ] ]. nginx_defaults(PrivDir) -> [ %% Set temp and cache file options that will otherwise default to %% restricted locations accessible only to root. {"client_body_temp_path", filename:join(PrivDir, "client_body")}, {"fastcgi_temp_path", filename:join(PrivDir, "fastcgi_temp")}, {"proxy_temp_path", filename:join(PrivDir, "proxy_temp")}, {"scgi_temp_path", filename:join(PrivDir, "scgi_temp")}, {"uwsgi_temp_path", filename:join(PrivDir, "uwsgi_temp_path")}, {"access_log", filename:join(PrivDir, "access.log")}, {"error_log", filename:join(PrivDir, "error.log")}, %% Standard options {"sendfile", "on"}, {"tcp_nopush", "on"}, {"tcp_nodelay", "on"}, {"keepalive_timeout", "360"}, {"types_hash_max_size", "2048"}, {"include", "/etc/nginx/mime.types"}, {"default_type", "application/octet-stream"} ]. nginx_reuse_session(true) -> "on"; nginx_reuse_session(false) -> "off". wait_for_nginx_up(Host, Port) -> case gen_tcp:connect(Host, Port, []) of {ok, Socket} -> gen_tcp:close(Socket); _ -> ct:sleep(100), wait_for_nginx_up(Host, Port) end.