aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ebin/gun.app2
-rw-r--r--src/gun.erl56
-rw-r--r--src/gun_http.erl7
-rw-r--r--src/gun_http2.erl33
-rw-r--r--src/gun_protocols.erl49
-rw-r--r--src/gun_socks.erl9
6 files changed, 75 insertions, 81 deletions
diff --git a/ebin/gun.app b/ebin/gun.app
index 7ca0c8f..9a5ab45 100644
--- a/ebin/gun.app
+++ b/ebin/gun.app
@@ -1,7 +1,7 @@
{application, 'gun', [
{description, "HTTP/1.1, HTTP/2 and Websocket client for Erlang/OTP."},
{vsn, "2.0.0-pre.2"},
- {modules, ['gun','gun_app','gun_content_handler','gun_cookies','gun_cookies_list','gun_data_h','gun_default_event_h','gun_event','gun_http','gun_http2','gun_public_suffix','gun_raw','gun_socks','gun_sse_h','gun_sup','gun_tcp','gun_tcp_proxy','gun_tls','gun_tls_proxy','gun_tls_proxy_cb','gun_tls_proxy_http2_connect','gun_ws','gun_ws_h']},
+ {modules, ['gun','gun_app','gun_content_handler','gun_cookies','gun_cookies_list','gun_data_h','gun_default_event_h','gun_event','gun_http','gun_http2','gun_protocols','gun_public_suffix','gun_raw','gun_socks','gun_sse_h','gun_sup','gun_tcp','gun_tcp_proxy','gun_tls','gun_tls_proxy','gun_tls_proxy_cb','gun_tls_proxy_http2_connect','gun_ws','gun_ws_h']},
{registered, [gun_sup]},
{applications, [kernel,stdlib,ssl,cowlib]},
{mod, {gun_app, []}},
diff --git a/src/gun.erl b/src/gun.erl
index 38c479e..919d481 100644
--- a/src/gun.erl
+++ b/src/gun.erl
@@ -104,13 +104,11 @@
-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]).
-export([connected_ws_only/3]).
-export([closing/3]).
--export([protocol_handler/1]).
-export([terminate/3]).
-type req_headers() :: [{binary() | string() | atom(), iodata()}]
@@ -212,6 +210,7 @@
}.
-export_type([http_opts/0]).
+%% @todo Accept http_opts, http2_opts, and so on.
-type http2_opts() :: #{
closing_timeout => timeout(),
flow => pos_integer(),
@@ -1093,13 +1092,8 @@ tls_handshake(internal, {tls_handshake, HandshakeEvent, Protocols, ReplyTo},
StreamRef = maps:get(stream_ref, HandshakeEvent, undefined),
case normal_tls_handshake(Socket, State0, HandshakeEvent, Protocols) of
{ok, TLSSocket, NewProtocol0, State} ->
- NewProtocol = {Protocol0, _} = case {StreamRef, NewProtocol0} of
- {undefined, {_, _}} -> NewProtocol0;
- {undefined, P} -> {P, #{}};
- {_, {P, POpts}} -> {P, POpts#{stream_ref => StreamRef}};
- {_, P} -> {P, #{stream_ref => StreamRef}}
- end,
- Protocol = gun:protocol_handler(Protocol0),
+ NewProtocol = gun_protocols:add_stream_ref(NewProtocol0, StreamRef),
+ Protocol = gun_protocols:handler(NewProtocol),
ReplyTo ! {gun_tunnel_up, self(), StreamRef, Protocol:name()},
commands([
{switch_transport, gun_tls, TLSSocket},
@@ -1127,15 +1121,10 @@ tls_handshake(internal, {tls_handshake,
%% the handshake succeeded and whether we need to switch to a different protocol.
tls_handshake(info, {gun_tls_proxy, Socket, {ok, Negotiated}, {HandshakeEvent, Protocols, ReplyTo}},
State0=#state{socket=Socket, event_handler=EvHandler, event_handler_state=EvHandlerState0}) ->
- NewProtocol0 = protocol_negotiated(Negotiated, Protocols),
+ NewProtocol0 = gun_protocols:negotiated(Negotiated, Protocols),
StreamRef = maps:get(stream_ref, HandshakeEvent, undefined),
- NewProtocol = {Protocol0, _} = case {StreamRef, NewProtocol0} of
- {undefined, {_, _}} -> NewProtocol0;
- {undefined, P} -> {P, #{}};
- {_, {P, POpts}} -> {P, POpts#{stream_ref => StreamRef}};
- {_, P} -> {P, #{stream_ref => StreamRef}}
- end,
- Protocol = gun:protocol_handler(Protocol0),
+ NewProtocol = gun_protocols:add_stream_ref(NewProtocol0, StreamRef),
+ Protocol = gun_protocols:handler(NewProtocol),
ReplyTo ! {gun_tunnel_up, self(), StreamRef, Protocol:name()},
EvHandlerState = EvHandler:tls_handshake_end(HandshakeEvent#{
socket => Socket,
@@ -1162,7 +1151,7 @@ normal_tls_handshake(Socket, State=#state{
EvHandlerState1 = EvHandler:tls_handshake_start(HandshakeEvent, EvHandlerState0),
case gun_tls:connect(Socket, TLSOpts, TLSTimeout) of
{ok, TLSSocket} ->
- Protocol = protocol_negotiated(ssl:negotiated_protocol(TLSSocket), Protocols),
+ Protocol = gun_protocols:negotiated(ssl:negotiated_protocol(TLSSocket), Protocols),
EvHandlerState = EvHandler:tls_handshake_end(HandshakeEvent#{
socket => TLSSocket,
protocol => Protocol
@@ -1175,11 +1164,6 @@ normal_tls_handshake(Socket, State=#state{
{error, Reason, State#state{event_handler_state=EvHandlerState}}
end.
-protocol_negotiated({ok, <<"h2">>}, _) -> http2;
-protocol_negotiated({ok, <<"http/1.1">>}, _) -> http;
-protocol_negotiated({error, protocol_not_negotiated}, [Protocol]) -> Protocol;
-protocol_negotiated({error, protocol_not_negotiated}, _) -> http.
-
connected_no_input(Type, Event, State) ->
handle_common_connected_no_input(Type, Event, ?FUNCTION_NAME, State).
@@ -1210,16 +1194,9 @@ connected_ws_only(cast, Msg, _)
connected_ws_only(Type, Event, State) ->
handle_common_connected_no_input(Type, Event, ?FUNCTION_NAME, State).
-connected(internal, {connected, Socket, Protocol0},
+connected(internal, {connected, Socket, NewProtocol},
State0=#state{owner=Owner, opts=Opts, transport=Transport}) ->
- %% Protocol options may have been given along the protocol name.
- {Protocol, ProtoOpts} = case Protocol0 of
- {P, PO} ->
- {protocol_handler(P), PO};
- _ ->
- P = protocol_handler(Protocol0),
- {P, maps:get(P:opts_name(), Opts, #{})}
- end,
+ {Protocol, ProtoOpts} = gun_protocols:handler_and_opts(NewProtocol, Opts),
{StateName, ProtoState} = Protocol:init(Owner, Socket, Transport, ProtoOpts),
Owner ! {gun_up, self(), Protocol:name()},
State = active(State0#state{socket=Socket, protocol=Protocol, protocol_state=ProtoState}),
@@ -1593,15 +1570,10 @@ commands([{switch_transport, Transport, Socket}|Tail], State=#state{
commands(Tail, active(State#state{socket=Socket, transport=Transport,
messages=Transport:messages(), protocol_state=ProtoState,
event_handler_state=EvHandlerState}));
-commands([{switch_protocol, Protocol0, ReplyTo}], State0=#state{
+commands([{switch_protocol, NewProtocol, ReplyTo}], State0=#state{
opts=Opts, socket=Socket, transport=Transport,
event_handler=EvHandler, event_handler_state=EvHandlerState0}) ->
- {Protocol, ProtoOpts} = case Protocol0 of
- {P, PO} -> {protocol_handler(P), PO};
- P ->
- Protocol1 = protocol_handler(P),
- {Protocol1, maps:get(Protocol1:opts_name(), Opts, #{})}
- end,
+ {Protocol, ProtoOpts} = gun_protocols:handler_and_opts(NewProtocol, Opts),
{StateName, ProtoState} = Protocol:init(ReplyTo, Socket, Transport, ProtoOpts),
EvHandlerState = EvHandler:protocol_changed(#{protocol => Protocol:name()}, EvHandlerState0),
%% We cancel the existing keepalive and, depending on the protocol,
@@ -1652,12 +1624,6 @@ disconnect_flush(State=#state{socket=Socket, messages={OK, Closed, Error}}) ->
ok
end.
-protocol_handler(http) -> gun_http;
-protocol_handler(http2) -> gun_http2;
-protocol_handler(raw) -> gun_raw;
-protocol_handler(socks) -> gun_socks;
-protocol_handler(ws) -> gun_ws.
-
active(State=#state{active=false}) ->
State;
active(State=#state{socket=Socket, transport=Transport}) ->
diff --git a/src/gun_http.erl b/src/gun_http.erl
index 950bda1..2536369 100644
--- a/src/gun_http.erl
+++ b/src/gun_http.erl
@@ -347,11 +347,8 @@ handle_connect(Rest, State=#http_state{
], State), EvHandlerState1};
_ ->
[NewProtocol0] = maps:get(protocols, Destination, [http]),
- NewProtocol = {Protocol0, _} = case NewProtocol0 of
- {P, POpts} -> {P, POpts#{stream_ref => RealStreamRef}};
- P -> {P, #{stream_ref => RealStreamRef}}
- end,
- Protocol = gun:protocol_handler(Protocol0),
+ NewProtocol = gun_protocols:add_stream_ref(NewProtocol0, RealStreamRef),
+ Protocol = gun_protocols:handler(NewProtocol),
ReplyTo ! {gun_tunnel_up, self(), RealStreamRef, Protocol:name()},
{handle_ret([
{origin, <<"http">>, NewHost, NewPort, connect},
diff --git a/src/gun_http2.erl b/src/gun_http2.erl
index 4908b6f..d8a84b4 100644
--- a/src/gun_http2.erl
+++ b/src/gun_http2.erl
@@ -98,6 +98,7 @@
check_options(Opts) ->
do_check_options(maps:to_list(Opts)).
+%% @todo Accept http_opts, http2_opts, and so on.
do_check_options([]) ->
ok;
do_check_options([{closing_timeout, infinity}|Opts]) ->
@@ -357,15 +358,9 @@ tunnel_commands([{origin, _, NewHost, NewPort, Type}|Tail], Stream, Protocol, Tu
protocol => Protocol:name()
}|maps:get(intermediaries, TunnelInfo, [])]
}, State);
-tunnel_commands([{switch_protocol, Protocol0, ReplyTo}|Tail], Stream=#stream{ref=StreamRef},
- CurrentProtocol, TunnelInfo, State=#http2_state{opts=Opts}) ->
- {Protocol, ProtoOpts} = case Protocol0 of
- {P, PO} -> {gun:protocol_handler(P), PO};
- P ->
- Protocol1 = gun:protocol_handler(P),
- %% @todo We need to allow other protocol opts in http2_opts too.
- {Protocol1, maps:get(Protocol1:opts_name(), Opts, #{})}
- end,
+tunnel_commands([{switch_protocol, NewProtocol, ReplyTo}|Tail], Stream=#stream{ref=StreamRef},
+ _CurrentProtocol, TunnelInfo, State=#http2_state{opts=Opts}) ->
+ {Protocol, ProtoOpts} = gun_protocols:handler_and_opts(NewProtocol, Opts),
RealStreamRef = stream_ref(State, StreamRef),
OriginSocket = #{
gun_pid => self(),
@@ -415,7 +410,7 @@ data_frame(State0, StreamID, IsFin, Data, EvHandler, EvHandlerState0,
end,
{maybe_delete_stream(State, StreamID, remote, IsFin), EvHandlerState}.
-headers_frame(State0=#http2_state{content_handlers=Handlers0, commands_queue=Commands},
+headers_frame(State0=#http2_state{opts=Opts, content_handlers=Handlers0, commands_queue=Commands},
StreamID, IsFin, Headers, #{status := Status}, _BodyLen,
EvHandler, EvHandlerState0) ->
Stream = get_stream_by_id(State0, StreamID),
@@ -489,13 +484,8 @@ headers_frame(State0=#http2_state{content_handlers=Handlers0, commands_queue=Com
tls_proxy_pid => ProxyPid}}}),
EvHandlerState};
_ ->
- [Protocol0] = maps:get(protocols, Destination, [http]),
- %% Options are either passed directly or #{} is used. Since the
- %% protocol only applies to a stream we cannot use connection-wide options.
- {Protocol, ProtoOpts} = case Protocol0 of
- {P, PO} -> {gun:protocol_handler(P), PO};
- P -> {gun:protocol_handler(P), #{}}
- end,
+ [NewProtocol] = maps:get(protocols, Destination, [http]),
+ {Protocol, ProtoOpts} = gun_protocols:handler_and_opts(NewProtocol, Opts),
%% @todo What about the StateName returned?
{_, ProtoState} = Protocol:init(ReplyTo, OriginSocket, gun_tcp_proxy, ProtoOpts#{stream_ref => RealStreamRef}),
%% @todo EvHandlerState = EvHandler:protocol_changed(#{protocol => Protocol:name()}, EvHandlerState0),
@@ -604,7 +594,7 @@ ignored_frame(State=#http2_state{http2_machine=HTTP2Machine0}) ->
end.
%% Continue handling or sending the data.
-handle_continue(StreamRef, Msg, State, EvHandler, EvHandlerState0)
+handle_continue(StreamRef, Msg, State=#http2_state{opts=Opts}, EvHandler, EvHandlerState0)
when is_reference(StreamRef) ->
case get_stream_by_ref(State, StreamRef) of
Stream=#stream{id=StreamID, reply_to=ReplyTo,
@@ -614,7 +604,7 @@ handle_continue(StreamRef, Msg, State, EvHandler, EvHandlerState0)
{handle_continue, _, _HandshakeEvent, Protocols}} ->
#{host := DestHost, port := DestPort} = Destination,
RealStreamRef = stream_ref(State, StreamRef),
- NewProtocol = gun:protocol_negotiated(Negotiated, Protocols),
+ NewProtocol = gun_protocols:negotiated(Negotiated, Protocols),
% EvHandlerState = EvHandler:tls_handshake_end(HandshakeEvent#{
% socket => Socket,
% protocol => NewProtocol
@@ -624,10 +614,7 @@ handle_continue(StreamRef, Msg, State, EvHandler, EvHandlerState0)
reply_to => ReplyTo,
stream_ref => RealStreamRef
},
- {Protocol, ProtoOpts} = case NewProtocol of
- {P, PO} -> {gun:protocol_handler(P), PO};
- P -> {gun:protocol_handler(P), #{}}
- end,
+ {Protocol, ProtoOpts} = gun_protocols:handler_and_opts(NewProtocol, Opts),
{_, ProtoState} = Protocol:init(ReplyTo, OriginSocket, gun_tcp_proxy,
ProtoOpts#{stream_ref => RealStreamRef}),
ReplyTo ! {gun_tunnel_up, self(), RealStreamRef, Protocol:name()},
diff --git a/src/gun_protocols.erl b/src/gun_protocols.erl
new file mode 100644
index 0000000..e5c3c93
--- /dev/null
+++ b/src/gun_protocols.erl
@@ -0,0 +1,49 @@
+%% Copyright (c) 2020, 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(gun_protocols).
+
+-export([add_stream_ref/2]).
+-export([handler/1]).
+-export([handler_and_opts/2]).
+-export([negotiated/2]).
+
+add_stream_ref(Protocol, undefined) ->
+ Protocol;
+add_stream_ref({ProtocolName, ProtocolOpts}, StreamRef) ->
+ {ProtocolName, ProtocolOpts#{stream_ref => StreamRef}};
+add_stream_ref(ProtocolName, StreamRef) ->
+ {ProtocolName, #{stream_ref => StreamRef}}.
+
+handler(http) -> gun_http;
+handler({http, _}) -> gun_http;
+handler(http2) -> gun_http2;
+handler({http2, _}) -> gun_http2;
+handler(raw) -> gun_raw;
+handler({raw, _}) -> gun_raw;
+handler(socks) -> gun_socks;
+handler({socks, _}) -> gun_socks;
+handler(ws) -> gun_ws;
+handler({ws, _}) -> gun_ws.
+
+handler_and_opts({ProtocolName, ProtocolOpts}, _) ->
+ {handler(ProtocolName), ProtocolOpts};
+handler_and_opts(ProtocolName, Opts) ->
+ Protocol = handler(ProtocolName),
+ {Protocol, maps:get(Protocol:opts_name(), Opts, #{})}.
+
+negotiated({ok, <<"h2">>}, _) -> http2;
+negotiated({ok, <<"http/1.1">>}, _) -> http;
+negotiated({error, protocol_not_negotiated}, [Protocol]) -> Protocol;
+negotiated({error, protocol_not_negotiated}, _) -> http.
diff --git a/src/gun_socks.erl b/src/gun_socks.erl
index b7a08b2..ce91f93 100644
--- a/src/gun_socks.erl
+++ b/src/gun_socks.erl
@@ -152,13 +152,8 @@ handle(<<5, 0, 0, Rest0/bits>>, #socks_state{ref=StreamRef, reply_to=ReplyTo, op
{tls_handshake, HandshakeEvent, maps:get(protocols, Opts, [http2, http]), ReplyTo}];
_ ->
[NewProtocol0] = maps:get(protocols, Opts, [http]),
- NewProtocol = {Protocol0, _} = case {StreamRef, NewProtocol0} of
- {undefined, {_, _}} -> NewProtocol0;
- {undefined, P} -> {P, #{}};
- {_, {P, POpts}} -> {P, POpts#{stream_ref => StreamRef}};
- {_, P} -> {P, #{stream_ref => StreamRef}}
- end,
- Protocol = gun:protocol_handler(Protocol0),
+ NewProtocol = gun_protocols:add_stream_ref(NewProtocol0, StreamRef),
+ Protocol = gun_protocols:handler(NewProtocol),
ReplyTo ! {gun_tunnel_up, self(), StreamRef, Protocol:name()},
[{origin, <<"http">>, NewHost, NewPort, socks5},
{switch_protocol, NewProtocol, ReplyTo}]