diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/gun.erl | 56 | ||||
-rw-r--r-- | src/gun_http.erl | 7 | ||||
-rw-r--r-- | src/gun_http2.erl | 33 | ||||
-rw-r--r-- | src/gun_protocols.erl | 49 | ||||
-rw-r--r-- | src/gun_socks.erl | 9 |
5 files changed, 74 insertions, 80 deletions
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}] |