From f1e7517c05bb97c257ad7a39e170ebc91ca42149 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Hoguin?= Date: Wed, 29 Jul 2020 15:30:38 +0200 Subject: Make HTTP/2 CONNECT to a SOCKS server work --- test/rfc7540_SUITE.erl | 6 +++- test/socks_SUITE.erl | 86 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 91 insertions(+), 1 deletion(-) (limited to 'test') diff --git a/test/rfc7540_SUITE.erl b/test/rfc7540_SUITE.erl index 879c342..ebc5392 100644 --- a/test/rfc7540_SUITE.erl +++ b/test/rfc7540_SUITE.erl @@ -559,7 +559,8 @@ do_connect_cowboy(_OriginScheme, OriginTransport, OriginProtocol, _ProxyScheme, {ok, Ref, OriginPort} = do_cowboy_origin(OriginTransport, OriginProtocol), try {ok, ProxyPid, ProxyPort} = do_proxy_start(ProxyTransport, [ - #proxy_stream{id=1, status=200} + #proxy_stream{id=1, status=200}, + #proxy_stream{id=3, status=299} ]), Authority = iolist_to_binary(["localhost:", integer_to_binary(OriginPort)]), {ok, ConnPid} = gun:open("localhost", ProxyPort, #{ @@ -581,6 +582,9 @@ do_connect_cowboy(_OriginScheme, OriginTransport, OriginProtocol, _ProxyScheme, {response, nofin, 200, _} = gun:await(ConnPid, StreamRef), ProxiedStreamRef = gun:get(ConnPid, "/proxied", #{}, #{tunnel => StreamRef}), {response, nofin, 200, _} = gun:await(ConnPid, ProxiedStreamRef), + %% We can create more requests on the proxy as well. + ProxyStreamRef = gun:get(ConnPid, "/"), + {response, fin, 299, _} = gun:await(ConnPid, ProxyStreamRef), gun:close(ConnPid) after cowboy:stop_listener(Ref) diff --git a/test/socks_SUITE.erl b/test/socks_SUITE.erl index e692a82..72b038c 100644 --- a/test/socks_SUITE.erl +++ b/test/socks_SUITE.erl @@ -413,3 +413,89 @@ do_socks5_through_connect_proxy(OriginScheme, OriginTransport, ProxyTransport) - protocol := socks }]} = gun:info(ConnPid), gun:close(ConnPid). + +socks5_tcp_through_h2_connect_tcp_to_tcp_origin(_) -> + doc("CONNECT can be used to establish a TCP connection " + "to an HTTP/1.1 server via a tunnel going through " + "a TCP HTTP/2 proxy followed by a Socks5 proxy."), + do_socks5_through_h2_connect_proxy(<<"http">>, tcp, <<"http">>, tcp). + +do_socks5_through_h2_connect_proxy(OriginScheme, OriginTransport, ProxyScheme, ProxyTransport) -> + {ok, OriginPid, OriginPort} = init_origin(OriginTransport, http), + {ok, Proxy1Pid, Proxy1Port} = rfc7540_SUITE:do_proxy_start(ProxyTransport, [ + {proxy_stream, 1, 200, [], 0, undefined} + ]), + {ok, Proxy2Pid, Proxy2Port} = do_proxy_start(ProxyTransport, none), + {ok, ConnPid} = gun:open("localhost", Proxy1Port, #{ + transport => ProxyTransport, + protocols => [http2] + }), + %% We receive a gun_up first. This is the HTTP proxy. + {ok, http2} = gun:await_up(ConnPid), + handshake_completed = receive_from(Proxy1Pid), + Authority1 = iolist_to_binary(["localhost:", integer_to_binary(Proxy2Port)]), + StreamRef = gun:connect(ConnPid, #{ + host => "localhost", + port => Proxy2Port, + transport => ProxyTransport, + protocols => [{socks, #{ + host => "localhost", + port => OriginPort, + transport => OriginTransport + }}] + }), + {request, #{ + <<":method">> := <<"CONNECT">>, + <<":authority">> := Authority1 + }} = receive_from(Proxy1Pid), + {response, nofin, 200, _} = gun:await(ConnPid, StreamRef), + %% We receive a gun_socks_up afterwards. This is the origin HTTP server. + {ok, http} = gun:await_up(ConnPid), + %% The second proxy receives a Socks5 auth/connect request. + {auth_methods, 1, [none]} = receive_from(Proxy2Pid), + {connect, <<"localhost">>, OriginPort} = receive_from(Proxy2Pid), + handshake_completed = receive_from(OriginPid), + ProxiedStreamRef = gun:get(ConnPid, "/proxied", #{}, #{tunnel => StreamRef}), + Authority2 = iolist_to_binary(["localhost:", integer_to_binary(OriginPort)]), + Data = receive_from(OriginPid), + Lines = binary:split(Data, <<"\r\n">>, [global]), + [<<"host: ", Authority2/bits>>] = [L || <<"host: ", _/bits>> = L <- Lines], + #{ + transport := ProxyTransport, + protocol := http2, + origin_scheme := ProxyScheme, + origin_host := "localhost", + origin_port := Proxy1Port, + intermediaries := [] %% Intermediaries are specific to the CONNECT stream. + } = gun:info(ConnPid), + {ok, #{ + ref := StreamRef, + reply_to := Self, + state := running, + tunnel := #{ + transport := OriginTransport, + protocol := http, + origin_scheme := OriginScheme, + origin_host := "localhost", + origin_port := OriginPort + } + }} = gun:stream_info(ConnPid, StreamRef), + {ok, #{ + ref := ProxiedStreamRef, + reply_to := Self, + state := running, + intermediaries := [#{ + type := connect, + host := "localhost", + port := Proxy1Port, + transport := ProxyTransport, + protocol := http2 + }, #{ + type := socks5, + host := "localhost", + port := Proxy2Port, + transport := ProxyTransport, + protocol := socks + }] + }} = gun:stream_info(ConnPid, ProxiedStreamRef), + gun:close(ConnPid). -- cgit v1.2.3