aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLoïc Hoguin <[email protected]>2019-09-19 15:14:31 +0200
committerLoïc Hoguin <[email protected]>2019-09-22 16:46:35 +0200
commit02dd576a837b8b47b1c656c6f4b8769c1aeb4ed0 (patch)
treeb405a4fc250ce60c7ee2fdba5e29858c54df8c4a
parent617554f305dc3bd031779ba0b8ef8d52edb52edd (diff)
downloadgun-02dd576a837b8b47b1c656c6f4b8769c1aeb4ed0.tar.gz
gun-02dd576a837b8b47b1c656c6f4b8769c1aeb4ed0.tar.bz2
gun-02dd576a837b8b47b1c656c6f4b8769c1aeb4ed0.zip
Add Socks5->HTTP/2 tests
Also consolidate the ALPN code in the tls_handshake state rather than doing it in CONNECT/Socks separately. Also improves the origin tests by sending a message once the handshake is completed instead of having timeouts.
-rw-r--r--src/gun.erl28
-rw-r--r--test/gun_SUITE.erl12
-rw-r--r--test/gun_test.erl2
-rw-r--r--test/rfc7230_SUITE.erl1
-rw-r--r--test/rfc7231_SUITE.erl8
-rw-r--r--test/rfc7540_SUITE.erl10
-rw-r--r--test/shutdown_SUITE.erl9
-rw-r--r--test/socks_SUITE.erl75
8 files changed, 100 insertions, 45 deletions
diff --git a/src/gun.erl b/src/gun.erl
index a6c1edc..3802ee0 100644
--- a/src/gun.erl
+++ b/src/gun.erl
@@ -962,10 +962,14 @@ tls_handshake(internal, {tls_handshake, HandshakeEvent, Protocols},
end;
%% TLS over TLS.
tls_handshake(internal, {tls_handshake,
- HandshakeEvent0=#{tls_opts := TLSOpts, timeout := TLSTimeout}, Protocols},
+ HandshakeEvent0=#{tls_opts := TLSOpts0, timeout := TLSTimeout}, Protocols},
State=#state{socket=Socket, transport=gun_tls, origin_host=OriginHost, origin_port=OriginPort,
event_handler=EvHandler, event_handler_state=EvHandlerState0}) ->
- HandshakeEvent = HandshakeEvent0#{socket => Socket},
+ TLSOpts = ensure_alpn(Protocols, TLSOpts0),
+ HandshakeEvent = HandshakeEvent0#{
+ tls_opts => TLSOpts,
+ socket => Socket
+ },
EvHandlerState = EvHandler:tls_handshake_start(HandshakeEvent, EvHandlerState0),
{ok, ProxyPid} = gun_tls_proxy:start_link(OriginHost, OriginPort,
TLSOpts, TLSTimeout, Socket, gun_tls, {HandshakeEvent, Protocols}),
@@ -1003,8 +1007,12 @@ tls_handshake(Type, Event, State) ->
handle_common_connected(Type, Event, ?FUNCTION_NAME, State).
normal_tls_handshake(Socket, State=#state{event_handler=EvHandler, event_handler_state=EvHandlerState0},
- HandshakeEvent0=#{tls_opts := TLSOpts, timeout := TLSTimeout}, Protocols) ->
- HandshakeEvent = HandshakeEvent0#{socket => Socket},
+ HandshakeEvent0=#{tls_opts := TLSOpts0, timeout := TLSTimeout}, Protocols) ->
+ TLSOpts = ensure_alpn(Protocols, TLSOpts0),
+ HandshakeEvent = HandshakeEvent0#{
+ tls_opts => TLSOpts,
+ socket => Socket
+ },
EvHandlerState1 = EvHandler:tls_handshake_start(HandshakeEvent, EvHandlerState0),
case gun_tls:connect(Socket, TLSOpts, TLSTimeout) of
{ok, TLSSocket} ->
@@ -1068,7 +1076,7 @@ connected(cast, {connect, ReplyTo, StreamRef, Destination0, Headers, InitialFlow
State=#state{protocol=Protocol, protocol_state=ProtoState}) ->
%% The protocol option has been deprecated in favor of the protocols option.
%% Nobody probably ended up using it, but let's not break the interface.
- Destination1 = case Destination0 of
+ Destination = case Destination0 of
#{protocols := _} ->
Destination0;
#{protocol := DestProto} ->
@@ -1076,14 +1084,6 @@ connected(cast, {connect, ReplyTo, StreamRef, Destination0, Headers, InitialFlow
_ ->
Destination0
end,
- Destination = case Destination1 of
- #{transport := tls} ->
- Destination1#{tls_opts => ensure_alpn(
- maps:get(protocols, Destination1, [http]),
- maps:get(tls_opts, Destination1, []))};
- _ ->
- Destination1
- end,
ProtoState2 = Protocol:connect(ProtoState, StreamRef, ReplyTo, Destination, Headers, InitialFlow),
{keep_state, State#state{protocol_state=ProtoState2}};
%% Public Websocket interface.
@@ -1326,7 +1326,7 @@ commands([TLSHandshake={tls_handshake, _, _}], State) ->
{next_event, internal, TLSHandshake}};
%% Switch from not_fully_connected to connected.
commands([{mode, http}], State) ->
- {next_state, connected, State}.
+ {next_state, connected, active(State)}.
disconnect(State0=#state{owner=Owner, status=Status, opts=Opts,
socket=Socket, transport=Transport,
diff --git a/test/gun_SUITE.erl b/test/gun_SUITE.erl
index 0beee43..a0ecddd 100644
--- a/test/gun_SUITE.erl
+++ b/test/gun_SUITE.erl
@@ -42,6 +42,7 @@ atom_header_name(_) ->
{ok, OriginPid, OriginPort} = init_origin(tcp, http),
{ok, Pid} = gun:open("localhost", OriginPort),
{ok, http} = gun:await_up(Pid),
+ handshake_completed = receive_from(OriginPid),
_ = gun:get(Pid, "/", [
{'User-Agent', "Gun/atom-headers"}
]),
@@ -55,6 +56,7 @@ atom_hostname(_) ->
{ok, OriginPid, OriginPort} = init_origin(tcp, http),
{ok, Pid} = gun:open('localhost', OriginPort),
{ok, http} = gun:await_up(Pid),
+ handshake_completed = receive_from(OriginPid),
_ = gun:get(Pid, "/"),
Data = receive_from(OriginPid),
Lines = binary:split(Data, <<"\r\n">>, [global]),
@@ -95,6 +97,7 @@ ignore_empty_data_http(_) ->
{ok, OriginPid, OriginPort} = init_origin(tcp, http),
{ok, Pid} = gun:open("localhost", OriginPort),
{ok, http} = gun:await_up(Pid),
+ handshake_completed = receive_from(OriginPid),
Ref = gun:put(Pid, "/", []),
gun:data(Pid, Ref, nofin, "hello "),
gun:data(Pid, Ref, nofin, ["", <<>>]),
@@ -110,7 +113,7 @@ ignore_empty_data_http2(_) ->
{ok, OriginPid, OriginPort} = init_origin(tcp, http2),
{ok, Pid} = gun:open("localhost", OriginPort, #{protocols => [http2]}),
{ok, http2} = gun:await_up(Pid),
- timer:sleep(100), %% Give enough time for the handshake to fully complete.
+ handshake_completed = receive_from(OriginPid),
Ref = gun:put(Pid, "/", []),
gun:data(Pid, Ref, nofin, "hello "),
gun:data(Pid, Ref, nofin, ["", <<>>]),
@@ -180,6 +183,7 @@ list_header_name(_) ->
{ok, OriginPid, OriginPort} = init_origin(tcp, http),
{ok, Pid} = gun:open("localhost", OriginPort),
{ok, http} = gun:await_up(Pid),
+ handshake_completed = receive_from(OriginPid),
_ = gun:get(Pid, "/", [
{"User-Agent", "Gun/list-headers"}
]),
@@ -193,6 +197,7 @@ map_headers(_) ->
{ok, OriginPid, OriginPort} = init_origin(tcp, http),
{ok, Pid} = gun:open("localhost", OriginPort),
{ok, http} = gun:await_up(Pid),
+ handshake_completed = receive_from(OriginPid),
_ = gun:get(Pid, "/", #{
<<"USER-agent">> => "Gun/map-headers"
}),
@@ -434,13 +439,13 @@ stream_info_http(_) ->
stream_info_http2(_) ->
doc("Ensure the function gun:stream_info/2 works as expected for HTTP/2."),
- {ok, _, OriginPort} = init_origin(tcp, http2,
+ {ok, OriginPid, OriginPort} = init_origin(tcp, http2,
fun(_, _, _) -> timer:sleep(200) end),
{ok, Pid} = gun:open("localhost", OriginPort, #{
protocols => [http2]
}),
{ok, http2} = gun:await_up(Pid),
- timer:sleep(100), %% Give enough time for the handshake to fully complete.
+ handshake_completed = receive_from(OriginPid),
{ok, undefined} = gun:stream_info(Pid, make_ref()),
StreamRef = gun:get(Pid, "/"),
Self = self(),
@@ -535,6 +540,7 @@ uppercase_header_name(_) ->
{ok, OriginPid, OriginPort} = init_origin(tcp, http),
{ok, Pid} = gun:open("localhost", OriginPort),
{ok, http} = gun:await_up(Pid),
+ handshake_completed = receive_from(OriginPid),
_ = gun:get(Pid, "/", [
{<<"USER-agent">>, "Gun/uppercase-headers"}
]),
diff --git a/test/gun_test.erl b/test/gun_test.erl
index a2cbf6d..a263335 100644
--- a/test/gun_test.erl
+++ b/test/gun_test.erl
@@ -49,6 +49,7 @@ init_origin(Parent, tcp, Protocol, Fun) ->
http2 -> http2_handshake(ClientSocket, gen_tcp);
_ -> ok
end,
+ Parent ! {self(), handshake_completed},
Fun(Parent, ClientSocket, gen_tcp);
init_origin(Parent, tls, Protocol, Fun) ->
Opts0 = ct_helper:get_certs_from_ets(),
@@ -68,6 +69,7 @@ init_origin(Parent, tls, Protocol, Fun) ->
_ ->
ok
end,
+ Parent ! {self(), handshake_completed},
Fun(Parent, ClientSocket, ssl).
http2_handshake(Socket, Transport) ->
diff --git a/test/rfc7230_SUITE.erl b/test/rfc7230_SUITE.erl
index e090545..da7b426 100644
--- a/test/rfc7230_SUITE.erl
+++ b/test/rfc7230_SUITE.erl
@@ -52,6 +52,7 @@ do_host_port(Transport, DefaultPort, HostHeaderPort) ->
end, 5000),
%% Confirm the default port is not sent in the request.
_ = gun:get(ConnPid, "/"),
+ handshake_completed = receive_from(OriginPid),
Data = receive_from(OriginPid),
Lines = binary:split(Data, <<"\r\n">>, [global]),
[<<"host: localhost", Rest/bits>>] = [L || <<"host: ", _/bits>> = L <- Lines],
diff --git a/test/rfc7231_SUITE.erl b/test/rfc7231_SUITE.erl
index e5df4ef..ec63cbe 100644
--- a/test/rfc7231_SUITE.erl
+++ b/test/rfc7231_SUITE.erl
@@ -168,6 +168,7 @@ do_connect_http(OriginScheme, OriginTransport, ProxyTransport) ->
}),
{request, <<"CONNECT">>, Authority, 'HTTP/1.1', _} = receive_from(ProxyPid),
{response, fin, 200, _} = gun:await(ConnPid, StreamRef),
+ handshake_completed = receive_from(OriginPid),
_ = gun:get(ConnPid, "/proxied"),
Data = receive_from(OriginPid),
Lines = binary:split(Data, <<"\r\n">>, [global]),
@@ -221,7 +222,7 @@ do_connect_h2(OriginScheme, OriginTransport, ProxyTransport) ->
}),
{request, <<"CONNECT">>, Authority, 'HTTP/1.1', _} = receive_from(ProxyPid),
{response, fin, 200, _} = gun:await(ConnPid, StreamRef),
- timer:sleep(1000), %% Give enough time for the ssl/h2 handshakes to fully complete.
+ handshake_completed = receive_from(OriginPid),
_ = gun:get(ConnPid, "/proxied"),
<<_:24, 1:8, _/bits>> = receive_from(OriginPid),
#{
@@ -262,6 +263,7 @@ connect_through_multiple_proxies(_) ->
}),
{request, <<"CONNECT">>, Authority2, 'HTTP/1.1', _} = receive_from(Proxy2Pid),
{response, fin, 200, _} = gun:await(ConnPid, StreamRef2),
+ handshake_completed = receive_from(OriginPid),
_ = gun:get(ConnPid, "/proxied"),
Data = receive_from(OriginPid),
Lines = binary:split(Data, <<"\r\n">>, [global]),
@@ -301,6 +303,7 @@ connect_delay(_) ->
}),
{request, <<"CONNECT">>, Authority, 'HTTP/1.1', _} = receive_from(ProxyPid, 3000),
{response, fin, 201, _} = gun:await(ConnPid, StreamRef),
+ handshake_completed = receive_from(OriginPid),
_ = gun:get(ConnPid, "/proxied"),
Data = receive_from(OriginPid),
Lines = binary:split(Data, <<"\r\n">>, [global]),
@@ -334,6 +337,7 @@ connect_response_201(_) ->
}),
{request, <<"CONNECT">>, Authority, 'HTTP/1.1', _} = receive_from(ProxyPid),
{response, fin, 201, _} = gun:await(ConnPid, StreamRef),
+ handshake_completed = receive_from(OriginPid),
_ = gun:get(ConnPid, "/proxied"),
Data = receive_from(OriginPid),
Lines = binary:split(Data, <<"\r\n">>, [global]),
@@ -474,6 +478,7 @@ connect_response_ignore_transfer_encoding(_) ->
}),
{request, <<"CONNECT">>, Authority, 'HTTP/1.1', _} = receive_from(ProxyPid),
{response, fin, 200, Headers} = gun:await(ConnPid, StreamRef),
+ handshake_completed = receive_from(OriginPid),
_ = gun:get(ConnPid, "/proxied"),
Data = receive_from(OriginPid),
Lines = binary:split(Data, <<"\r\n">>, [global]),
@@ -495,6 +500,7 @@ connect_response_ignore_content_length(_) ->
}),
{request, <<"CONNECT">>, Authority, 'HTTP/1.1', _} = receive_from(ProxyPid),
{response, fin, 200, Headers} = gun:await(ConnPid, StreamRef),
+ handshake_completed = receive_from(OriginPid),
_ = gun:get(ConnPid, "/proxied"),
Data = receive_from(OriginPid),
Lines = binary:split(Data, <<"\r\n">>, [global]),
diff --git a/test/rfc7540_SUITE.erl b/test/rfc7540_SUITE.erl
index 14e9c0c..6f08ce3 100644
--- a/test/rfc7540_SUITE.erl
+++ b/test/rfc7540_SUITE.erl
@@ -58,12 +58,12 @@ do_authority_port(Transport0, DefaultPort, AuthorityHeaderPort) ->
protocols => [http2]
}),
{ok, http2} = gun:await_up(ConnPid),
+ handshake_completed = receive_from(OriginPid),
%% Change the origin's port in the state to trigger the default port behavior.
_ = sys:replace_state(ConnPid, fun({StateName, StateData}) ->
{StateName, setelement(8, StateData, DefaultPort)}
end, 5000),
%% Confirm the default port is not sent in the request.
- timer:sleep(100), %% Give enough time for the handshake to fully complete.
_ = gun:get(ConnPid, "/"),
ReqHeaders = receive_from(OriginPid),
{_, <<"localhost", Rest/bits>>} = lists:keyfind(<<":authority">>, 1, ReqHeaders),
@@ -73,7 +73,7 @@ do_authority_port(Transport0, DefaultPort, AuthorityHeaderPort) ->
lingering_data_counts_toward_connection_window(_) ->
doc("DATA frames received after sending RST_STREAM must be counted "
"toward the connection flow-control window. (RFC7540 5.1)"),
- {ok, _, Port} = init_origin(tcp, http2, fun(_, Socket, Transport) ->
+ {ok, OriginPid, Port} = init_origin(tcp, http2, fun(_, Socket, Transport) ->
%% Step 2.
%% Receive a HEADERS frame.
{ok, <<SkipLen:24, 1:8, _:8, 1:32>>} = Transport:recv(Socket, 9, 1000),
@@ -110,7 +110,7 @@ lingering_data_counts_toward_connection_window(_) ->
}
}),
{ok, http2} = gun:await_up(ConnPid),
- timer:sleep(100), %% Give enough time for the handshake to fully complete.
+ handshake_completed = receive_from(OriginPid),
%% Step 1.
StreamRef = gun:get(ConnPid, "/"),
%% Step 4.
@@ -124,7 +124,7 @@ lingering_data_counts_toward_connection_window(_) ->
headers_priority_flag(_) ->
doc("HEADERS frames may include a PRIORITY flag indicating "
"that stream dependency information is attached. (RFC7540 6.2)"),
- {ok, _, Port} = init_origin(tcp, http2, fun(_, Socket, Transport) ->
+ {ok, OriginPid, Port} = init_origin(tcp, http2, fun(_, Socket, Transport) ->
%% Receive a HEADERS frame.
{ok, <<_:24, 1:8, _:8, 1:32>>} = Transport:recv(Socket, 9, 1000),
%% Send a HEADERS frame with PRIORITY back.
@@ -151,7 +151,7 @@ headers_priority_flag(_) ->
end),
{ok, ConnPid} = gun:open("localhost", Port, #{protocols => [http2]}),
{ok, http2} = gun:await_up(ConnPid),
- timer:sleep(100), %% Give enough time for the handshake to fully complete.
+ handshake_completed = receive_from(OriginPid),
StreamRef = gun:get(ConnPid, "/"),
{response, fin, 200, _} = gun:await(ConnPid, StreamRef),
gun:close(ConnPid).
diff --git a/test/shutdown_SUITE.erl b/test/shutdown_SUITE.erl
index d3a2f56..173fd50 100644
--- a/test/shutdown_SUITE.erl
+++ b/test/shutdown_SUITE.erl
@@ -19,6 +19,7 @@
-import(ct_helper, [doc/1]).
-import(ct_helper, [config/2]).
-import(gun_test, [init_origin/3]).
+-import(gun_test, [receive_from/1]).
all() ->
[{group, shutdown}].
@@ -377,7 +378,7 @@ http2_server_goaway_one_stream(_) ->
doc("HTTP/2: Confirm that the Gun process shuts down gracefully "
"when receiving a GOAWAY frame with one active stream and "
"retry is disabled."),
- {ok, _, OriginPort} = init_origin(tcp, http2, fun(_, Socket, Transport) ->
+ {ok, OriginPid, OriginPort} = init_origin(tcp, http2, fun(_, Socket, Transport) ->
%% Receive a HEADERS frame.
{ok, <<SkipLen:24, 1:8, _:8, 1:32>>} = Transport:recv(Socket, 9, 1000),
%% Skip the header.
@@ -401,7 +402,7 @@ http2_server_goaway_one_stream(_) ->
retry => 0
}),
{ok, Protocol} = gun:await_up(ConnPid),
- timer:sleep(100), %% Give enough time for the handshake to fully complete.
+ handshake_completed = receive_from(OriginPid),
StreamRef = gun:get(ConnPid, "/"),
ConnRef = monitor(process, ConnPid),
{response, fin, 200, _} = gun:await(ConnPid, StreamRef),
@@ -411,7 +412,7 @@ http2_server_goaway_many_streams(_) ->
doc("HTTP/2: Confirm that the Gun process shuts down gracefully "
"when receiving a GOAWAY frame with many active streams and "
"retry is disabled."),
- {ok, _, OriginPort} = init_origin(tcp, http2, fun(_, Socket, Transport) ->
+ {ok, OriginPid, OriginPort} = init_origin(tcp, http2, fun(_, Socket, Transport) ->
%% Stream 1.
%% Receive a HEADERS frame.
{ok, <<SkipLen1:24, 1:8, _:8, 1:32>>} = Transport:recv(Socket, 9, 1000),
@@ -458,7 +459,7 @@ http2_server_goaway_many_streams(_) ->
retry => 0
}),
{ok, Protocol} = gun:await_up(ConnPid),
- timer:sleep(100), %% Give enough time for the handshake to fully complete.
+ handshake_completed = receive_from(OriginPid),
StreamRef1 = gun:get(ConnPid, "/"),
StreamRef2 = gun:get(ConnPid, "/"),
StreamRef3 = gun:get(ConnPid, "/"),
diff --git a/test/socks_SUITE.erl b/test/socks_SUITE.erl
index 81d4c10..9d80328 100644
--- a/test/socks_SUITE.erl
+++ b/test/socks_SUITE.erl
@@ -146,38 +146,70 @@ do_auth_method({username_password, _, _}) -> username_password.
socks5_tcp_http_none(_) ->
doc("Use Socks5 over TCP and without authentication to connect to an HTTP server."),
- do_socks5_http(<<"http">>, tcp, tcp, none).
+ do_socks5(<<"http">>, tcp, http, tcp, none).
socks5_tcp_http_username_password(_) ->
doc("Use Socks5 over TCP and without authentication to connect to an HTTP server."),
- do_socks5_http(<<"http">>, tcp, tcp, {username_password, <<"user">>, <<"password">>}).
+ do_socks5(<<"http">>, tcp, http, tcp, {username_password, <<"user">>, <<"password">>}).
socks5_tcp_https_none(_) ->
doc("Use Socks5 over TCP and without authentication to connect to an HTTPS server."),
- do_socks5_http(<<"https">>, tls, tcp, none).
+ do_socks5(<<"https">>, tls, http, tcp, none).
socks5_tcp_https_username_password(_) ->
doc("Use Socks5 over TCP and without authentication to connect to an HTTPS server."),
- do_socks5_http(<<"https">>, tls, tcp, {username_password, <<"user">>, <<"password">>}).
+ do_socks5(<<"https">>, tls, http, tcp, {username_password, <<"user">>, <<"password">>}).
socks5_tls_http_none(_) ->
doc("Use Socks5 over TLS and without authentication to connect to an HTTP server."),
- do_socks5_http(<<"http">>, tcp, tls, none).
+ do_socks5(<<"http">>, tcp, http, tls, none).
socks5_tls_http_username_password(_) ->
doc("Use Socks5 over TLS and without authentication to connect to an HTTP server."),
- do_socks5_http(<<"http">>, tcp, tls, {username_password, <<"user">>, <<"password">>}).
+ do_socks5(<<"http">>, tcp, http, tls, {username_password, <<"user">>, <<"password">>}).
socks5_tls_https_none(_) ->
- doc("Use Socks5 over TLS and without authentication to connect to an HTTP server."),
- do_socks5_http(<<"https">>, tls, tls, none).
+ doc("Use Socks5 over TLS and without authentication to connect to an HTTPS server."),
+ do_socks5(<<"https">>, tls, http, tls, none).
socks5_tls_https_username_password(_) ->
- doc("Use Socks5 over TLS and without authentication to connect to an HTTP server."),
- do_socks5_http(<<"https">>, tls, tls, {username_password, <<"user">>, <<"password">>}).
+ doc("Use Socks5 over TLS and without authentication to connect to an HTTPS server."),
+ do_socks5(<<"https">>, tls, http, tls, {username_password, <<"user">>, <<"password">>}).
+
+socks5_tcp_h2c_none(_) ->
+ doc("Use Socks5 over TCP and without authentication to connect to an HTTP/2 server over TCP."),
+ do_socks5(<<"http">>, tcp, http2, tcp, none).
+
+socks5_tcp_h2c_username_password(_) ->
+ doc("Use Socks5 over TCP and without authentication to connect to an HTTP/2 server over TCP."),
+ do_socks5(<<"http">>, tcp, http2, tcp, {username_password, <<"user">>, <<"password">>}).
+
+socks5_tcp_h2_none(_) ->
+ doc("Use Socks5 over TCP and without authentication to connect to an HTTP/2 server over TLS."),
+ do_socks5(<<"https">>, tls, http2, tcp, none).
+
+socks5_tcp_h2_username_password(_) ->
+ doc("Use Socks5 over TCP and without authentication to connect to an HTTP/2 server over TLS."),
+ do_socks5(<<"https">>, tls, http2, tcp, {username_password, <<"user">>, <<"password">>}).
-do_socks5_http(OriginScheme, OriginTransport, ProxyTransport, SocksAuth) ->
- {ok, OriginPid, OriginPort} = init_origin(OriginTransport, http),
+socks5_tls_h2c_none(_) ->
+ doc("Use Socks5 over TLS and without authentication to connect to an HTTP/2 server over TCP."),
+ do_socks5(<<"http">>, tcp, http2, tls, none).
+
+socks5_tls_h2c_username_password(_) ->
+ doc("Use Socks5 over TLS and without authentication to connect to an HTTP/2 server over TCP."),
+ do_socks5(<<"http">>, tcp, http2, tls, {username_password, <<"user">>, <<"password">>}).
+
+socks5_tls_h2_none(_) ->
+ doc("Use Socks5 over TLS and without authentication to connect to an HTTP/2 server over TLS."),
+ do_socks5(<<"https">>, tls, http2, tls, none).
+
+socks5_tls_h2_username_password(_) ->
+ doc("Use Socks5 over TLS and without authentication to connect to an HTTP/2 server over TLS."),
+ do_socks5(<<"https">>, tls, http2, tls, {username_password, <<"user">>, <<"password">>}).
+
+do_socks5(OriginScheme, OriginTransport, OriginProtocol, ProxyTransport, SocksAuth) ->
+ {ok, OriginPid, OriginPort} = init_origin(OriginTransport, OriginProtocol),
{ok, ProxyPid, ProxyPort} = do_proxy_start(ProxyTransport, SocksAuth),
Authority = iolist_to_binary(["localhost:", integer_to_binary(OriginPort)]),
{ok, ConnPid} = gun:open("localhost", ProxyPort, #{
@@ -186,12 +218,13 @@ do_socks5_http(OriginScheme, OriginTransport, ProxyTransport, SocksAuth) ->
auth => [SocksAuth],
host => "localhost",
port => OriginPort,
- transport => OriginTransport
+ transport => OriginTransport,
+ protocols => [OriginProtocol]
}}]
}),
%% We receive a gun_up and a gun_socks_connected.
{ok, socks} = gun:await_up(ConnPid),
- {ok, http} = gun:await_up(ConnPid),
+ {ok, OriginProtocol} = gun:await_up(ConnPid),
%% The proxy received two packets.
AuthMethod = do_auth_method(SocksAuth),
{auth_methods, 1, [AuthMethod]} = receive_from(ProxyPid),
@@ -200,13 +233,19 @@ do_socks5_http(OriginScheme, OriginTransport, ProxyTransport, SocksAuth) ->
username_password -> SocksAuth = receive_from(ProxyPid)
end,
{connect, <<"localhost">>, OriginPort} = receive_from(ProxyPid),
+ handshake_completed = receive_from(OriginPid),
_ = gun:get(ConnPid, "/proxied"),
- Data = receive_from(OriginPid),
- Lines = binary:split(Data, <<"\r\n">>, [global]),
- [<<"host: ", Authority/bits>>] = [L || <<"host: ", _/bits>> = L <- Lines],
+ _ = case OriginProtocol of
+ http ->
+ Data = receive_from(OriginPid),
+ Lines = binary:split(Data, <<"\r\n">>, [global]),
+ [<<"host: ", Authority/bits>>] = [L || <<"host: ", _/bits>> = L <- Lines];
+ http2 ->
+ <<_:24, 1:8, _/bits>> = receive_from(OriginPid)
+ end,
#{
transport := OriginTransport,
- protocol := http,
+ protocol := OriginProtocol,
origin_scheme := OriginScheme,
origin_host := "localhost",
origin_port := OriginPort,