diff options
author | Loïc Hoguin <[email protected]> | 2018-10-10 11:54:50 +0200 |
---|---|---|
committer | Loïc Hoguin <[email protected]> | 2018-10-10 11:55:50 +0200 |
commit | 4b9970bcd725972dbd845e07c34a8ef8409ec04c (patch) | |
tree | a7d05ae457ed8a99c2c7c615430696203ac15bc6 /test/proxy_header_SUITE.erl | |
parent | 1cc7de15b6f04835faa2f6505669cb7f90c29796 (diff) | |
download | ranch-4b9970bcd725972dbd845e07c34a8ef8409ec04c.tar.gz ranch-4b9970bcd725972dbd845e07c34a8ef8409ec04c.tar.bz2 ranch-4b9970bcd725972dbd845e07c34a8ef8409ec04c.zip |
Add ranch_tcp:recv_proxy_header/2
This uses the undocumented function gen_tcp:unrecv/2.
Tests have been added for both gen_tcp and ssl connections,
including sending data in the same first packet, at least
for gen_tcp (ssl tests may or may not end up buffering some
of the TLS handshake before the recv call, but there's no
guarantees).
Diffstat (limited to 'test/proxy_header_SUITE.erl')
-rw-r--r-- | test/proxy_header_SUITE.erl | 236 |
1 files changed, 236 insertions, 0 deletions
diff --git a/test/proxy_header_SUITE.erl b/test/proxy_header_SUITE.erl new file mode 100644 index 0000000..b2308f8 --- /dev/null +++ b/test/proxy_header_SUITE.erl @@ -0,0 +1,236 @@ +%% Copyright (c) 2018, 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(proxy_header_SUITE). +-compile(export_all). +-compile(nowarn_export_all). + +-import(ct_helper, [doc/1]). +-import(ct_helper, [name/0]). + +%% ct. + +all() -> + ct_helper:all(?MODULE). + +%% Tests. + +recv_v1_proxy_header_tcp(_) -> + doc("Confirm we can read the proxy header at the start of the connection."), + Name = name(), + ProxyInfo = #{ + version => 1, + command => proxy, + transport_family => ipv4, + transport_protocol => stream, + src_address => {127, 0, 0, 1}, + src_port => 444, + dest_address => {192, 168, 0, 1}, + dest_port => 443 + }, + do_proxy_header_tcp(Name, ProxyInfo, <<>>, <<"TCP Ranch is working!">>). + +recv_v1_proxy_header_tcp_extra_data(_) -> + doc("Confirm we can read the proxy header at the start of the connection " + "and that the extra data in the first packet can be read afterwards."), + Name = name(), + ProxyInfo = #{ + version => 1, + command => proxy, + transport_family => ipv4, + transport_protocol => stream, + src_address => {127, 0, 0, 1}, + src_port => 444, + dest_address => {192, 168, 0, 1}, + dest_port => 443 + }, + do_proxy_header_tcp(Name, ProxyInfo, <<"HELLO">>, <<"TCP Ranch is working!">>). + +recv_v2_proxy_header_tcp(_) -> + doc("Confirm we can read the proxy header at the start of the connection."), + Name = name(), + ProxyInfo = #{ + version => 2, + command => proxy, + transport_family => ipv4, + transport_protocol => stream, + src_address => {127, 0, 0, 1}, + src_port => 444, + dest_address => {192, 168, 0, 1}, + dest_port => 443 + }, + do_proxy_header_tcp(Name, ProxyInfo, <<>>, <<"TCP Ranch is working!">>). + +recv_v2_proxy_header_tcp_extra_data(_) -> + doc("Confirm we can read the proxy header at the start of the connection " + "and that the extra data in the first packet can be read afterwards."), + Name = name(), + ProxyInfo = #{ + version => 2, + command => proxy, + transport_family => ipv4, + transport_protocol => stream, + src_address => {127, 0, 0, 1}, + src_port => 444, + dest_address => {192, 168, 0, 1}, + dest_port => 443 + }, + do_proxy_header_tcp(Name, ProxyInfo, <<"HELLO">>, <<"TCP Ranch is working!">>). + +recv_v2_local_header_tcp(_) -> + doc("Confirm we can read the proxy header at the start of the connection."), + Name = name(), + ProxyInfo = #{ + version => 2, + command => local + }, + do_proxy_header_tcp(Name, ProxyInfo, <<>>, <<"TCP Ranch is working!">>). + +recv_v2_local_header_tcp_extra_data(_) -> + doc("Confirm we can read the proxy header at the start of the connection " + "and that the extra data in the first packet can be read afterwards."), + Name = name(), + ProxyInfo = #{ + version => 2, + command => local + }, + do_proxy_header_tcp(Name, ProxyInfo, <<"HELLO">>, <<"TCP Ranch is working!">>). + +do_proxy_header_tcp(Name, ProxyInfo, Data1, Data2) -> + {ok, _} = ranch:start_listener(Name, + ranch_tcp, #{}, + proxy_protocol, []), + Port = ranch:get_port(Name), + {ok, Socket} = gen_tcp:connect("localhost", Port, [binary, {active, false}, {packet, raw}]), + ok = gen_tcp:send(Socket, [ranch_proxy_header:header(ProxyInfo), Data1]), + receive + {proxy_protocol, ProxyInfo} -> + ok + after 2000 -> + error(timeout) + end, + ok = gen_tcp:send(Socket, Data2), + Len1 = byte_size(Data1), + Len2 = byte_size(Data2), + {ok, <<Data1:Len1/binary, Data2/binary>>} = gen_tcp:recv(Socket, Len1 + Len2, 1000), + ok = ranch:stop_listener(Name), + ok. + +recv_v1_proxy_header_ssl(_) -> + doc("Confirm we can read the proxy header at the start of the connection."), + Name = name(), + ProxyInfo = #{ + version => 1, + command => proxy, + transport_family => ipv4, + transport_protocol => stream, + src_address => {127, 0, 0, 1}, + src_port => 444, + dest_address => {192, 168, 0, 1}, + dest_port => 443 + }, + do_proxy_header_ssl(Name, ProxyInfo, <<>>, <<"TCP Ranch is working!">>). + +recv_v1_proxy_header_ssl_extra_data(_) -> + doc("Confirm we can read the proxy header at the start of the connection " + "and that the extra data in the first packet can be read afterwards."), + Name = name(), + ProxyInfo = #{ + version => 1, + command => proxy, + transport_family => ipv4, + transport_protocol => stream, + src_address => {127, 0, 0, 1}, + src_port => 444, + dest_address => {192, 168, 0, 1}, + dest_port => 443 + }, + do_proxy_header_ssl(Name, ProxyInfo, <<"HELLO">>, <<"TCP Ranch is working!">>). + +recv_v2_proxy_header_ssl(_) -> + doc("Confirm we can read the proxy header at the start of the connection."), + Name = name(), + ProxyInfo = #{ + version => 2, + command => proxy, + transport_family => ipv4, + transport_protocol => stream, + src_address => {127, 0, 0, 1}, + src_port => 444, + dest_address => {192, 168, 0, 1}, + dest_port => 443 + }, + do_proxy_header_ssl(Name, ProxyInfo, <<>>, <<"TCP Ranch is working!">>). + +recv_v2_proxy_header_ssl_extra_data(_) -> + doc("Confirm we can read the proxy header at the start of the connection " + "and that the extra data in the first packet can be read afterwards."), + Name = name(), + ProxyInfo = #{ + version => 2, + command => proxy, + transport_family => ipv4, + transport_protocol => stream, + src_address => {127, 0, 0, 1}, + src_port => 444, + dest_address => {192, 168, 0, 1}, + dest_port => 443 + }, + do_proxy_header_ssl(Name, ProxyInfo, <<"HELLO">>, <<"TCP Ranch is working!">>). + +recv_v2_local_header_ssl(_) -> + doc("Confirm we can read the proxy header at the start of the connection."), + Name = name(), + ProxyInfo = #{ + version => 2, + command => local + }, + do_proxy_header_ssl(Name, ProxyInfo, <<>>, <<"TCP Ranch is working!">>). + +recv_v2_local_header_ssl_extra_data(_) -> + doc("Confirm we can read the proxy header at the start of the connection " + "and that the extra data in the first packet can be read afterwards."), + Name = name(), + ProxyInfo = #{ + version => 2, + command => local + }, + do_proxy_header_ssl(Name, ProxyInfo, <<"HELLO">>, <<"TCP Ranch is working!">>). + +do_proxy_header_ssl(Name, ProxyInfo, Data1, Data2) -> + {ok, _} = ranch:start_listener(Name, + ranch_tcp, #{}, + proxy_protocol_ssl, []), + Port = ranch:get_port(Name), + {ok, Socket0} = gen_tcp:connect("localhost", Port, [binary, {active, false}, {packet, raw}]), + ok = gen_tcp:send(Socket0, [ranch_proxy_header:header(ProxyInfo)]), + %% This timeout is necessary to avoid a race condition when trying + %% to obtain the pid of the test case from the protocol. The race + %% condition is due to the TLS upgrade which changes the process + %% owning the socket. + timer:sleep(100), + {ok, Socket} = ssl:connect(Socket0, [], 1000), + ok = ssl:send(Socket, Data1), + receive + {proxy_protocol_ssl, ProxyInfo} -> + ok + after 2000 -> + error(timeout) + end, + ok = ssl:send(Socket, Data2), + Len1 = byte_size(Data1), + Len2 = byte_size(Data2), + {ok, <<Data1:Len1/binary, Data2/binary>>} = ssl:recv(Socket, Len1 + Len2, 1000), + ok = ranch:stop_listener(Name), + ok. |