aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLoïc Hoguin <[email protected]>2019-09-18 13:55:37 +0200
committerLoïc Hoguin <[email protected]>2019-09-22 16:46:35 +0200
commit28b24a32b82c6429462569be14e00bec65a850ee (patch)
treee328b8dc6465e88b61b615f6d3dc9e073556316b
parent28c19230eda29c42b376e35046d39acc1365881a (diff)
downloadgun-28b24a32b82c6429462569be14e00bec65a850ee.tar.gz
gun-28b24a32b82c6429462569be14e00bec65a850ee.tar.bz2
gun-28b24a32b82c6429462569be14e00bec65a850ee.zip
Support connecting to HTTPS server via TCP Socks5
-rw-r--r--src/gun.erl9
-rw-r--r--src/gun_socks.erl19
-rw-r--r--test/socks_SUITE.erl11
3 files changed, 28 insertions, 11 deletions
diff --git a/src/gun.erl b/src/gun.erl
index 85e9405..a6c1edc 100644
--- a/src/gun.erl
+++ b/src/gun.erl
@@ -1306,10 +1306,15 @@ commands([{switch_protocol, Protocol=gun_ws, ProtoState}], State=#state{
{keep_state, keepalive_cancel(State#state{protocol=Protocol, protocol_state=ProtoState,
event_handler_state=EvHandlerState})};
%% @todo And this state should probably not be ignored.
-%% @todo Socks is switching to *http* and we don't seem to support it properly yet.
+%% @todo Socks can be switching to *http* and we don't seem to support it properly yet.
commands([{switch_protocol, Protocol, _ProtoState0}|Tail], State=#state{
- owner=Owner, opts=Opts, socket=Socket, transport=Transport,
+ owner=Owner, opts=Opts, socket=Socket, transport=Transport, protocol=CurrentProtocol,
event_handler=EvHandler, event_handler_state=EvHandlerState0}) ->
+ %% When we switch_protocol from socks we must send a gun_socks_connected message.
+ _ = case CurrentProtocol of
+ gun_socks -> Owner ! {gun_socks_connected, self(), Protocol:name()};
+ _ -> ok
+ end,
ProtoOpts = maps:get(http2_opts, Opts, #{}),
ProtoState = Protocol:init(Owner, Socket, Transport, ProtoOpts),
EvHandlerState = EvHandler:protocol_changed(#{protocol => Protocol:name()}, EvHandlerState0),
diff --git a/src/gun_socks.erl b/src/gun_socks.erl
index 74f1767..16684f8 100644
--- a/src/gun_socks.erl
+++ b/src/gun_socks.erl
@@ -17,6 +17,7 @@
-export([check_options/1]).
-export([name/0]).
-export([init/4]).
+-export([switch_transport/3]).
-export([handle/4]).
-export([closing/4]).
-export([close/4]).
@@ -89,6 +90,9 @@ init(Owner, Socket, Transport, Opts) ->
#socks_state{owner=Owner, socket=Socket, transport=Transport, opts=Opts,
version=Version, status=auth_method_select}.
+switch_transport(Transport, Socket, State) ->
+ State#socks_state{socket=Socket, transport=Transport}.
+
handle(Data, State, _, EvHandlerState) ->
{handle(Data, State), EvHandlerState}.
@@ -128,22 +132,21 @@ handle(<<5, 0, 0, Rest0/bits>>, State=#socks_state{owner=Owner, socket=Socket, t
%% @todo Maybe an event indicating success.
#{host := NewHost, port := NewPort} = Opts,
case Opts of
- %% @todo TLS over TLS here as well.
-% #{protocols := Protocols, transport := tls} ->
-% TLSOpts = maps:get(tls_opts, Destination, []),
-% TLSTimeout = maps:get(tls_handshake_timeout, Destination, infinity),
- %%
+ #{transport := tls} ->
+ HandshakeEvent = #{
+ tls_opts => maps:get(tls_opts, Opts, []),
+ timeout => maps:get(tls_handshake_timeout, Opts, infinity)
+ },
+ [{origin, <<"https">>, NewHost, NewPort, socks5},
+ {tls_handshake, HandshakeEvent, maps:get(protocols, Opts, [http])}];
#{protocols := [{socks, SockOpts}]} ->
- Owner ! {gun_socks_connected, self(), name()},
[{origin, <<"http">>, NewHost, NewPort, socks5},
{switch_protocol, ?MODULE, init(Owner, Socket, Transport, SockOpts)}];
#{protocols := [http2]} ->
- Owner ! {gun_socks_connected, self(), gun_http2:name()},
[{origin, <<"http">>, NewHost, NewPort, socks5},
{switch_protocol, gun_http2, State},
{mode, http}];
_ ->
- Owner ! {gun_socks_connected, self(), gun_http:name()},
[{origin, <<"http">>, NewHost, NewPort, socks5},
{switch_protocol, gun_http, State},
{mode, http}]
diff --git a/test/socks_SUITE.erl b/test/socks_SUITE.erl
index b0c84b0..07b3497 100644
--- a/test/socks_SUITE.erl
+++ b/test/socks_SUITE.erl
@@ -152,6 +152,14 @@ socks5_tcp_http_username_password(_) ->
doc("Use Socks5 over TCP and without authentication to connect to an HTTP server."),
do_socks5_tcp_http(<<"http">>, tcp, 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_tcp_http(<<"https">>, tls, tcp, none).
+
+socks5_tcp_https_username_password(_) ->
+ doc("Use Socks5 over TCP and without authentication to connect to an HTTPS server."),
+ do_socks5_tcp_http(<<"https">>, tls, tcp, {username_password, <<"user">>, <<"password">>}).
+
do_socks5_tcp_http(OriginScheme, OriginTransport, ProxyTransport, SocksAuth) ->
{ok, OriginPid, OriginPort} = init_origin(OriginTransport, http),
{ok, ProxyPid, ProxyPort} = do_proxy_start(ProxyTransport, SocksAuth),
@@ -161,7 +169,8 @@ do_socks5_tcp_http(OriginScheme, OriginTransport, ProxyTransport, SocksAuth) ->
protocols => [{socks, #{
auth => [SocksAuth],
host => "localhost",
- port => OriginPort
+ port => OriginPort,
+ transport => OriginTransport
}}]
}),
%% We receive a gun_up and a gun_socks_connected.