aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLoïc Hoguin <[email protected]>2018-09-26 13:51:13 +0200
committerLoïc Hoguin <[email protected]>2018-09-26 13:51:13 +0200
commitd8e91eefa6ea9973f60657db05c85ec594aa7d0b (patch)
tree28e047ef6e925b96508f2d29859ca2c3be113b47
parentf328916937c67b3c9679e4f11d4594c39d36f85d (diff)
downloadgun-d8e91eefa6ea9973f60657db05c85ec594aa7d0b.tar.gz
gun-d8e91eefa6ea9973f60657db05c85ec594aa7d0b.tar.bz2
gun-d8e91eefa6ea9973f60657db05c85ec594aa7d0b.zip
Don't send keep-alive while waiting for CONNECT responses
Otherwise this can mess up the underlying protocol we will switch to, like TLS or HTTP/2.
-rw-r--r--src/gun_http.erl3
-rw-r--r--test/rfc7231_SUITE.erl55
2 files changed, 50 insertions, 8 deletions
diff --git a/src/gun_http.erl b/src/gun_http.erl
index c4291bc..e2b37d1 100644
--- a/src/gun_http.erl
+++ b/src/gun_http.erl
@@ -319,6 +319,9 @@ close_streams([#stream{ref=StreamRef, reply_to=ReplyTo}|Tail]) ->
"The connection was lost."}},
close_streams(Tail).
+%% We don't send a keep-alive when a CONNECT request was initiated.
+keepalive(State=#http_state{streams=[#stream{ref={connect, _, _}}]}) ->
+ State;
%% We can only keep-alive by sending an empty line in-between streams.
keepalive(State=#http_state{socket=Socket, transport=Transport, out=head}) ->
Transport:send(Socket, <<"\r\n">>),
diff --git a/test/rfc7231_SUITE.erl b/test/rfc7231_SUITE.erl
index a42e06c..f985bd8 100644
--- a/test/rfc7231_SUITE.erl
+++ b/test/rfc7231_SUITE.erl
@@ -34,12 +34,15 @@ do_proxy_start(Status) ->
do_proxy_start(Status, []).
do_proxy_start(Status, ConnectRespHeaders) ->
+ do_proxy_start(Status, ConnectRespHeaders, 0).
+
+do_proxy_start(Status, ConnectRespHeaders, Delay) ->
Self = self(),
- Pid = spawn_link(fun() -> do_proxy_init(Self, Status, ConnectRespHeaders) end),
+ Pid = spawn_link(fun() -> do_proxy_init(Self, Status, ConnectRespHeaders, Delay) end),
Port = do_receive(Pid),
{ok, Pid, Port}.
-do_proxy_init(Parent, Status, ConnectRespHeaders) ->
+do_proxy_init(Parent, Status, ConnectRespHeaders, Delay) ->
{ok, ListenSocket} = gen_tcp:listen(0, [binary, {active, false}]),
{ok, {_, Port}} = inet:sockname(ListenSocket),
Parent ! {self(), Port},
@@ -47,6 +50,7 @@ do_proxy_init(Parent, Status, ConnectRespHeaders) ->
{ok, Data} = gen_tcp:recv(ClientSocket, 0, 1000),
{Method= <<"CONNECT">>, Authority, Version, Rest} = cow_http:parse_request_line(Data),
{Headers, <<>>} = cow_http:parse_headers(Rest),
+ timer:sleep(Delay),
Parent ! {self(), {request, Method, Authority, Version, Headers}},
{OriginHost, OriginPort} = cow_http_hd:parse_host(Authority),
ok = gen_tcp:send(ClientSocket, [
@@ -109,7 +113,7 @@ do_origin_init_tcp(Parent) ->
{ok, ListenSocket} = gen_tcp:listen(0, [binary, {active, false}]),
{ok, {_, Port}} = inet:sockname(ListenSocket),
Parent ! {self(), Port},
- {ok, ClientSocket} = gen_tcp:accept(ListenSocket, 1000),
+ {ok, ClientSocket} = gen_tcp:accept(ListenSocket, 5000),
do_origin_loop(Parent, ClientSocket, gen_tcp).
do_origin_init_tls(Parent) ->
@@ -117,8 +121,8 @@ do_origin_init_tls(Parent) ->
{ok, ListenSocket} = ssl:listen(0, [binary, {active, false}|Opts]),
{ok, {_, Port}} = ssl:sockname(ListenSocket),
Parent ! {self(), Port},
- {ok, ClientSocket} = ssl:transport_accept(ListenSocket, 1000),
- ok = ssl:ssl_accept(ClientSocket, 1000),
+ {ok, ClientSocket} = ssl:transport_accept(ListenSocket, 5000),
+ ok = ssl:ssl_accept(ClientSocket, 5000),
do_origin_loop(Parent, ClientSocket, ssl).
do_origin_init_tls_h2(Parent) ->
@@ -127,8 +131,8 @@ do_origin_init_tls_h2(Parent) ->
{alpn_preferred_protocols, [<<"h2">>]}|Opts]),
{ok, {_, Port}} = ssl:sockname(ListenSocket),
Parent ! {self(), Port},
- {ok, ClientSocket} = ssl:transport_accept(ListenSocket, 1000),
- ok = ssl:ssl_accept(ClientSocket, 1000),
+ {ok, ClientSocket} = ssl:transport_accept(ListenSocket, 5000),
+ ok = ssl:ssl_accept(ClientSocket, 5000),
{ok, <<"h2">>} = ssl:negotiated_protocol(ClientSocket),
do_origin_loop(Parent, ClientSocket, ssl).
@@ -142,10 +146,13 @@ do_origin_loop(Parent, ClientSocket, ClientTransport) ->
end.
do_receive(Pid) ->
+ do_receive(Pid, 1000).
+
+do_receive(Pid, Timeout) ->
receive
{Pid, Msg} ->
Msg
- after 1000 ->
+ after Timeout ->
error(timeout)
end.
@@ -287,6 +294,38 @@ connect_through_multiple_proxies(_) ->
}]} = gun:info(ConnPid),
gun:close(ConnPid).
+connect_delay(_) ->
+ doc("The CONNECT response may not be immediate."),
+ {ok, OriginPid, OriginPort} = do_origin_start(tcp),
+ {ok, ProxyPid, ProxyPort} = do_proxy_start(201, [], 2000),
+ Authority = iolist_to_binary(["localhost:", integer_to_binary(OriginPort)]),
+ {ok, ConnPid} = gun:open("localhost", ProxyPort,
+ #{http_opts => #{keepalive => 1000}}),
+ {ok, http} = gun:await_up(ConnPid),
+ StreamRef = gun:connect(ConnPid, #{
+ host => "localhost",
+ port => OriginPort
+ }),
+ {request, <<"CONNECT">>, Authority, 'HTTP/1.1', _} = do_receive(ProxyPid, 3000),
+ {response, fin, 201, _} = gun:await(ConnPid, StreamRef),
+ _ = gun:get(ConnPid, "/proxied"),
+ Len = byte_size(Authority),
+ <<"GET /proxied HTTP/1.1\r\nhost: ", Authority:Len/binary, "\r\n", _/bits>>
+ = do_receive(OriginPid),
+ #{
+ transport := tcp,
+ protocol := http,
+ origin_host := "localhost",
+ origin_port := OriginPort,
+ intermediaries := [#{
+ type := connect,
+ host := "localhost",
+ port := ProxyPort,
+ transport := tcp,
+ protocol := http
+ }]} = gun:info(ConnPid),
+ gun:close(ConnPid).
+
connect_response_201(_) ->
doc("2xx responses to CONNECT requests indicate "
"the tunnel was set up successfully. (RFC7231 4.3.6)"),