aboutsummaryrefslogtreecommitdiffstats
path: root/src/gun.erl
diff options
context:
space:
mode:
authorLoïc Hoguin <[email protected]>2020-08-19 17:24:27 +0200
committerLoïc Hoguin <[email protected]>2020-09-21 15:51:57 +0200
commitca68d184abbf7bd1030b2f2035cc66c13d08dd5d (patch)
tree77a5800cbfc3da76e7863e1bbb51d83442bddf50 /src/gun.erl
parenta1729d5584364412f72d0d6337447da653da865b (diff)
downloadgun-ca68d184abbf7bd1030b2f2035cc66c13d08dd5d.tar.gz
gun-ca68d184abbf7bd1030b2f2035cc66c13d08dd5d.tar.bz2
gun-ca68d184abbf7bd1030b2f2035cc66c13d08dd5d.zip
First working HTTPS over secure HTTP/2
Has a timer:sleep/1 though because there is currently no way to wait for the TLS handshake to complete.
Diffstat (limited to 'src/gun.erl')
-rw-r--r--src/gun.erl62
1 files changed, 55 insertions, 7 deletions
diff --git a/src/gun.erl b/src/gun.erl
index 9d16f58..24ec9c0 100644
--- a/src/gun.erl
+++ b/src/gun.erl
@@ -102,7 +102,9 @@
-export([domain_lookup/3]).
-export([connecting/3]).
-export([initial_tls_handshake/3]).
+-export([ensure_alpn_sni/3]).
-export([tls_handshake/3]).
+-export([protocol_negotiated/2]).
-export([connected/3]).
-export([connected_data_only/3]).
-export([connected_no_input/3]).
@@ -180,7 +182,10 @@
origin_port => inet:port_number(),
%% Non-stream intermediaries (for example SOCKS).
- intermediaries => [intermediary()]
+ intermediaries => [intermediary()],
+
+ %% TLS proxy.
+ tls_proxy_pid => pid()
}.
-export_type([tunnel_info/0]).
@@ -1049,10 +1054,10 @@ connecting(_, {retries, Retries, LookupInfo}, State=#state{opts=Opts,
{next_event, internal, {retries, Retries, Reason}}}
end.
-initial_tls_handshake(_, {retries, Retries, Socket}, State0=#state{opts=Opts}) ->
+initial_tls_handshake(_, {retries, Retries, Socket}, State0=#state{opts=Opts, origin_host=OriginHost}) ->
Protocols = maps:get(protocols, Opts, [http2, http]),
HandshakeEvent = #{
- tls_opts => ensure_alpn_sni(Protocols, maps:get(tls_opts, Opts, []), State0),
+ tls_opts => ensure_alpn_sni(Protocols, maps:get(tls_opts, Opts, []), OriginHost),
timeout => maps:get(tls_handshake_timeout, Opts, infinity)
},
case normal_tls_handshake(Socket, State0, HandshakeEvent, Protocols) of
@@ -1064,7 +1069,7 @@ initial_tls_handshake(_, {retries, Retries, Socket}, State0=#state{opts=Opts}) -
{next_event, internal, {retries, Retries, Reason}}}
end.
-ensure_alpn_sni(Protocols0, TransOpts0, #state{origin_host=OriginHost}) ->
+ensure_alpn_sni(Protocols0, TransOpts0, OriginHost) ->
%% ALPN.
Protocols = [case P of
http -> <<"http/1.1">>;
@@ -1101,7 +1106,7 @@ tls_handshake(internal, {tls_handshake,
HandshakeEvent0=#{tls_opts := TLSOpts0, timeout := TLSTimeout}, Protocols, ReplyTo},
State=#state{socket=Socket, transport=Transport, origin_host=OriginHost, origin_port=OriginPort,
event_handler=EvHandler, event_handler_state=EvHandlerState0}) ->
- TLSOpts = ensure_alpn_sni(Protocols, TLSOpts0, State),
+ TLSOpts = ensure_alpn_sni(Protocols, TLSOpts0, OriginHost),
HandshakeEvent = HandshakeEvent0#{
tls_opts => TLSOpts,
socket => Socket
@@ -1130,9 +1135,10 @@ tls_handshake(info, {gun_tls_proxy, Socket, Error = {error, Reason}, {HandshakeE
tls_handshake(Type, Event, State) ->
handle_common_connected_no_input(Type, Event, ?FUNCTION_NAME, State).
-normal_tls_handshake(Socket, State=#state{event_handler=EvHandler, event_handler_state=EvHandlerState0},
+normal_tls_handshake(Socket, State=#state{
+ origin_host=OriginHost, event_handler=EvHandler, event_handler_state=EvHandlerState0},
HandshakeEvent0=#{tls_opts := TLSOpts0, timeout := TLSTimeout}, Protocols) ->
- TLSOpts = ensure_alpn_sni(Protocols, TLSOpts0, State),
+ TLSOpts = ensure_alpn_sni(Protocols, TLSOpts0, OriginHost),
HandshakeEvent = HandshakeEvent0#{
tls_opts => TLSOpts,
socket => Socket
@@ -1355,6 +1361,48 @@ handle_common_connected_no_input(info, {Closed, Socket}, _,
handle_common_connected_no_input(info, {Error, Socket, Reason}, _,
State=#state{socket=Socket, messages={_, _, Error}}) ->
disconnect(State, {error, Reason});
+%% Socket events from TLS proxy sockets set up by HTTP/2 CONNECT.
+%% We always forward the messages to Protocol:handle_continue.
+handle_common_connected_no_input(info,
+ Msg={gun_tls_proxy, _, _, {handle_continue, StreamRef, _, _}}, _,
+ State0=#state{protocol=Protocol, protocol_state=ProtoState,
+ event_handler=EvHandler, event_handler_state=EvHandlerState0}) ->
+ {Commands, EvHandlerState} = Protocol:handle_continue(StreamRef, Msg,
+ ProtoState, EvHandler, EvHandlerState0),
+ case commands(Commands, State0#state{event_handler_state=EvHandlerState}) of
+ {keep_state, State} ->
+ {keep_state, active(State)};
+ {next_state, closing, State, Actions} ->
+ {next_state, closing, active(State), Actions};
+ Res ->
+ Res
+ end;
+%% @todo
+% NewProtocol = protocol_negotiated(Negotiated, Protocols),
+% EvHandlerState = EvHandler:tls_handshake_end(HandshakeEvent#{
+% socket => Socket,
+% protocol => NewProtocol
+% }, EvHandlerState0),
+% commands([{switch_protocol, NewProtocol, ReplyTo}], State0#state{event_handler_state=EvHandlerState});
+%%
+% State=#state{socket=Socket, event_handler=EvHandler, event_handler_state=EvHandlerState0}) ->
+% EvHandlerState = EvHandler:tls_handshake_end(HandshakeEvent#{
+% error => Reason
+% }, EvHandlerState0),
+% commands([Error], State#state{event_handler_state=EvHandlerState});
+handle_common_connected_no_input(info, {handle_continue, StreamRef, Msg}, _,
+ State0=#state{protocol=Protocol, protocol_state=ProtoState,
+ event_handler=EvHandler, event_handler_state=EvHandlerState0}) ->
+ {Commands, EvHandlerState} = Protocol:handle_continue(StreamRef, Msg,
+ ProtoState, EvHandler, EvHandlerState0),
+ case commands(Commands, State0#state{event_handler_state=EvHandlerState}) of
+ {keep_state, State} ->
+ {keep_state, active(State)};
+ {next_state, closing, State, Actions} ->
+ {next_state, closing, active(State), Actions};
+ Res ->
+ Res
+ end;
%% Timeouts.
%% @todo HTTP/2 requires more timeouts than just the keepalive timeout.
%% We should have a timeout function in protocols that deal with