diff --git a/test/cowboy-ca.crt b/test/cowboy-ca.crt new file mode 100644 index 00000000..a35ac390 --- /dev/null +++ b/test/cowboy-ca.crt @@ -0,0 +1,16 @@ +-----BEGIN CERTIFICATE----- +MIICeDCCAeGgAwIBAgIJAOvpU0y2e5J4MA0GCSqGSIb3DQEBBQUAMFUxCzAJBgNV +BAYTAlVTMQ4wDAYDVQQIDAVUZXhhczETMBEGA1UECgwKTmluZSBOaW5lczEPMA0G +A1UECwwGQ293Ym95MRAwDgYDVQQDDAdST09UIENBMB4XDTEzMDIyODA1MTAwMVoX +DTMzMDIyMzA1MTAwMVowVTELMAkGA1UEBhMCVVMxDjAMBgNVBAgMBVRleGFzMRMw +EQYDVQQKDApOaW5lIE5pbmVzMQ8wDQYDVQQLDAZDb3dib3kxEDAOBgNVBAMMB1JP +T1QgQ0EwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMzmY7Us06yjyUbpqwPx +Iv+xh/g3V7we07ClC9GEYnvr3OQvdA1jFEHccMBUUjRoQ8DPd6uSyK5UkixABs08 +Tt5B3VsnGKr0DIN+IO4SN2PkmBqIU/BN3KdcwN65YNr3iM0KsKWeFtAZdYx4CakX +7REbO0wjK20AH3xSBn3uFGiBAgMBAAGjUDBOMB0GA1UdDgQWBBRKfZ8KF2jlLBDm +NL6IuEuGY0pdbzAfBgNVHSMEGDAWgBRKfZ8KF2jlLBDmNL6IuEuGY0pdbzAMBgNV +HRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4GBAG1I0kBxXiLkM1b7rl2zPLizREYg +1m+ajb6rWzPOBg6TXjv58Be+H4tqoHIL/M/crixew5emftBkuAGjiKMhbIokjvan +aPTCV8U6HHvNvz9c68HpESWbd+56cHqfsS5XCKp1OpW5tbL2UQYpFKMP4qmbv3Ea +pBfPPmSFMBb1i2AI +-----END CERTIFICATE----- diff --git a/test/hackney_https_pool_tests.erl b/test/hackney_https_pool_tests.erl new file mode 100644 index 00000000..837148f3 --- /dev/null +++ b/test/hackney_https_pool_tests.erl @@ -0,0 +1,59 @@ +-module(hackney_https_pool_tests). +-include_lib("eunit/include/eunit.hrl"). +-include("hackney_lib.hrl"). + +%% This seems necessary to list the tests including the generator +dummy_test() -> + ?assertEqual(ok, ok). + +multipart_test_() -> + {setup, fun start/0, fun stop/1, + [{timeout, 120, requests_to_different_servers_go_to_different_servers()}]}. + +start() -> + error_logger:tty(false), + {ok, _} = application:ensure_all_started(cowboy), + {ok, _} = application:ensure_all_started(hackney), + ProxyPid = simple_proxy:start_proxy_server(), + register(proxy_server, ProxyPid), + + hackney_pool:start_pool(pool_test, [{pool_size, 1}]), + Host = '_', + Resource = {"/pool", https_pool_resource1, []}, + Dispatch = cowboy_router:compile([{Host, [Resource]}]), + Resource2 = {"/pool", https_pool_resource2, []}, + Dispatch2 = cowboy_router:compile([{Host, [Resource2]}]), + + CaCertFile = "test/cowboy-ca.crt", + CertFile = "test/server.crt", + KeyFile = "test/server.key", + + cowboy:start_https(test_server, 10, [{port, 8443}, {cacertfile, CaCertFile}, {certfile, CertFile}, {keyfile, KeyFile}], [{env, [{dispatch, Dispatch}]}]), + cowboy:start_https(test_server2, 10, [{port, 8444}, {cacertfile, CaCertFile}, {certfile, CertFile}, {keyfile, KeyFile}], [{env, [{dispatch, Dispatch2}]}]). + +stop({ok, _Pid}) -> + cowboy:stop_listener(test_server), + application:stop(cowboy), + hackney_pool:stop_pool(pool_test), + application:stop(hackney), + error_logger:tty(true), + exit(whereis(proxy_server), kill), + ok. + +requests_to_different_servers_go_to_different_servers() -> + fun() -> + URL = <<"https://localhost:8443/pool">>, + OtherURL = <<"https://127.0.0.1:8444/pool">>, + + Headers = [], + SSLOptions = [{verify, verify_none}], + Opts = [{pool, pool_test}, {connect_timeout, 100}, {ssl_options, SSLOptions}, {proxy, "http://localhost:5555"}], + {ok, _, _, Ref1} = hackney:request(get, URL, Headers, <<>>, Opts), + {ok, Body1} = hackney:body(Ref1), + ?assertEqual(<<"pool_resource1">>, Body1), + + {ok, _, _, Ref2} = hackney:request(get, OtherURL, Headers, <<>>, Opts), + {ok, Body2} = hackney:body(Ref2), + ?assertEqual(<<"pool_resource2">>, Body2) + end. + diff --git a/test/https_pool_resource1.erl b/test/https_pool_resource1.erl new file mode 100644 index 00000000..d5898363 --- /dev/null +++ b/test/https_pool_resource1.erl @@ -0,0 +1,15 @@ +-module(https_pool_resource1). + +-export([init/3]). +-export([handle/2]). +-export([terminate/3]). + +init(_, Req, _Opts) -> + {ok, Req, undefined}. + +handle(Req, State) -> + {ok, Req2} = cowboy_req:reply(200, [], <<"pool_resource1">>, Req), + {ok, Req2, State}. + +terminate(_Reason, _Req, _State) -> + ok. diff --git a/test/https_pool_resource2.erl b/test/https_pool_resource2.erl new file mode 100644 index 00000000..8ff730af --- /dev/null +++ b/test/https_pool_resource2.erl @@ -0,0 +1,15 @@ +-module(https_pool_resource2). + +-export([init/3]). +-export([handle/2]). +-export([terminate/3]). + +init(_, Req, _Opts) -> + {ok, Req, undefined}. + +handle(Req, State) -> + {ok, Req2} = cowboy_req:reply(200, [], <<"pool_resource2">>, Req), + {ok, Req2, State}. + +terminate(_Reason, _Req, _State) -> + ok. diff --git a/test/server.crt b/test/server.crt new file mode 100644 index 00000000..0bdfaed0 --- /dev/null +++ b/test/server.crt @@ -0,0 +1,17 @@ +-----BEGIN CERTIFICATE----- +MIICpTCCAg6gAwIBAgIJAOvpU0y2e5J5MA0GCSqGSIb3DQEBBQUAMFUxCzAJBgNV +BAYTAlVTMQ4wDAYDVQQIDAVUZXhhczETMBEGA1UECgwKTmluZSBOaW5lczEPMA0G +A1UECwwGQ293Ym95MRAwDgYDVQQDDAdST09UIENBMB4XDTEzMDIyODA1MjMzNFoX +DTMzMDIyMzA1MjMzNFowVzELMAkGA1UEBhMCVVMxDjAMBgNVBAgMBVRleGFzMRMw +EQYDVQQKDApOaW5lIE5pbmVzMQ8wDQYDVQQLDAZDb3dib3kxEjAQBgNVBAMMCWxv +Y2FsaG9zdDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAzbW1GjECzHUc/WST +qLiAGqjCNccR5saVS+yoz2SPRhpoyf0/qBrX5BY0tzmgozoTiRfE4wCiVD99Cc+D +rp/FM49r4EpZdocIovprmOmv/gwkoj95zaA6PKNn1OdmDp2hwJsX2Zm3kpbGUZTx +jDkkccmgUb4EjL7qNHq7saQtivUCAwEAAaN7MHkwCQYDVR0TBAIwADAsBglghkgB +hvhCAQ0EHxYdT3BlblNTTCBHZW5lcmF0ZWQgQ2VydGlmaWNhdGUwHQYDVR0OBBYE +FB6jTEIWI8T1ckORA4GezbyYxtbvMB8GA1UdIwQYMBaAFEp9nwoXaOUsEOY0voi4 +S4ZjSl1vMA0GCSqGSIb3DQEBBQUAA4GBACMboVQjrx8u/fk3gl/sR0tbA0Wf/NcS +2Dzsy2czndgVUAG4Sqb+hfgn0dqAyUKghRrj3JDcYxYksGPIklDfPzZb7yJ39l16 +6x5ZiIzhp8CAVdPvRxRznw5rZwaXesryXu1jVSZxTr3MYZdkG6KaAM0t90+YlGLZ +UG8fAicx0Bf+ +-----END CERTIFICATE----- diff --git a/test/server.key b/test/server.key new file mode 100644 index 00000000..b6f73742 --- /dev/null +++ b/test/server.key @@ -0,0 +1,15 @@ +-----BEGIN RSA PRIVATE KEY----- +MIICXQIBAAKBgQDNtbUaMQLMdRz9ZJOouIAaqMI1xxHmxpVL7KjPZI9GGmjJ/T+o +GtfkFjS3OaCjOhOJF8TjAKJUP30Jz4Oun8Uzj2vgSll2hwii+muY6a/+DCSiP3nN +oDo8o2fU52YOnaHAmxfZmbeSlsZRlPGMOSRxyaBRvgSMvuo0eruxpC2K9QIDAQAB +AoGAaD85c/h6bpq7Aj7CBbLaWKhFI3OqwsTITB22vsM7SE+B4zsP02UnG1OVi3UM +zytTUxpUkKV1njQ+bYZYOVqGWF4Up8tTqUglHn0FTPok1AIemELWtz3sXvdSHC1T +lqvFBAZ9kibn13qGyVOiyCFaMwfOM/05RvV7p3jfUMTWnNECQQDs7yCJZ8Ol8MyH +TGZzvkjoN2zg1KwmTbSD1hkP6QAJtPdRuqFbjlEru0/PefgOXsWLRIa3/3v0qw2G +xGkV6AXTAkEA3kNbFisqUydjPnZIYv/P6SvPdUimHJEjXbAbfNfzS9dzszrOVJd2 +XqGH7z5yzjoH3IyaIMW8GnubVzGDSjrHFwJAKSU5vELlygpwKkrNO+pelN0TLlQg +dSJnZ8GlZorq88SWcn37iX/EftivenNO7YftvEqxLoDSkOGnnrC7Iw/A+wJBAIEe +L/QY72WPJCBNJpAce/PA96vyoE1II3txqwZDjZspdpVQPDz4IFOpEwbxCFC1dYuy +Qnd3Z2cbF4r3wIWGz9ECQQCJGNhUNtY+Om1ELdqPcquxE2VRV/pucnvJSTKwyo2C +Rvm6H7kFDwPDuN23YnTOlTiho0zzCkclcIukhIVJ+dKz +-----END RSA PRIVATE KEY----- diff --git a/test/simple_proxy.erl b/test/simple_proxy.erl new file mode 100644 index 00000000..5c3980ec --- /dev/null +++ b/test/simple_proxy.erl @@ -0,0 +1,75 @@ +-module(simple_proxy). + +-export([handle_client/0]). +-export([proxy_server_init/0]). +-export([start_proxy_server/0]). + +start_proxy_server() -> + {ok, LSock} = gen_tcp:listen(5555, [binary, {packet, http_bin}, {active, false}]), + Pid = spawn(?MODULE, proxy_server_init, []), + gen_tcp:controlling_process(LSock, Pid), + Pid ! {socket, LSock}, + Pid. + +proxy_server_init() -> + LSock = receive + {socket, Sock} -> Sock + end, + + io:format(user, "PROXY SERVER RUNNING~n", []), + proxy_server_loop(LSock). + +proxy_server_loop(LSock) -> + case gen_tcp:accept(LSock) of + {ok, Sock} -> + Pid = spawn(?MODULE, handle_client, []), + ok = gen_tcp:controlling_process(Sock, Pid), + Pid ! {socket, Sock}, + proxy_server_loop(LSock); + Error -> + io:format(user, "UNEXPECTED ERROR~p~n", [Error]) + end. + + +skip_headers(Sock) -> + + {ok, Header} = gen_tcp:recv(Sock, 0, 5000), + case Header of + {http_header,_,_,_,_} -> skip_headers(Sock); + http_eoh -> + ok + end. + +handle_client() -> + Sock = receive + {socket, Socket} -> Socket + end, + + {ok, {http_request,<<"CONNECT">>, {scheme, Host, Port}, {1,1}}} = gen_tcp:recv(Sock, 0, 5000), + skip_headers(Sock), + IntPort = binary_to_integer(Port), + ok = inet:setopts(Sock, [{active, true}, {packet, raw}, binary]), + {ok, ForeignSock} = gen_tcp:connect(binary_to_list(Host), IntPort, [binary, {packet, raw}, {active, true}]), + io:format(user, "CONNECT TO FOREIGN ADDRESS ~p ~p ~n", [Host, IntPort]), + ok = gen_tcp:send(Sock, <<"HTTP/1.1 200 OK\r\n\r\n">>), + proxy_data_loop(Sock, ForeignSock). + +proxy_data_loop(Sock, ForeignSock) -> + receive + {tcp, Sock, Data} -> + gen_tcp:send(ForeignSock, Data), + proxy_data_loop(Sock, ForeignSock); + {tcp, ForeignSock, Data} -> + gen_tcp:send(Sock, Data), + proxy_data_loop(Sock, ForeignSock); + {tcp_closed, Sock} -> + io:format(user, "CLIENT SOCK CLOSED~n", []), + gen_tcp:close(Sock); + {tcp_closed, ForeignSock} -> + io:format(user, "FOREIGN SOCK CLOSED~n", []), + gen_tcp:close(ForeignSock) + after 30000 -> + io:format(user, "TIMEOUT~n", []), + gen_tcp:close(Sock), + gen_tcp:close(ForeignSock) + end.