aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLoïc Hoguin <[email protected]>2020-03-25 11:22:59 +0100
committerLoïc Hoguin <[email protected]>2020-03-25 11:22:59 +0100
commit2eaf28d3876c1ebcbf31d843e77f809e0c25aefb (patch)
tree1e93f66be05bc60bf27e5e5f30d31f44cfdac3eb
parent3deadc01ee34e27cfba49b78debcf8006d7d7e1c (diff)
downloadgun-2eaf28d3876c1ebcbf31d843e77f809e0c25aefb.tar.gz
gun-2eaf28d3876c1ebcbf31d843e77f809e0c25aefb.tar.bz2
gun-2eaf28d3876c1ebcbf31d843e77f809e0c25aefb.zip
Fix host/:authority header when connecting to an IPv6 address
-rw-r--r--src/gun_http.erl3
-rw-r--r--test/gun_test.erl3
-rw-r--r--test/rfc7230_SUITE.erl13
-rw-r--r--test/rfc7540_SUITE.erl21
4 files changed, 38 insertions, 2 deletions
diff --git a/src/gun_http.erl b/src/gun_http.erl
index 401e23a..dd48c42 100644
--- a/src/gun_http.erl
+++ b/src/gun_http.erl
@@ -605,7 +605,8 @@ send_request(State=#http_state{socket=Socket, transport=Transport, version=Versi
host_header(Transport, Host0, Port) ->
Host = case Host0 of
{local, _SocketPath} -> <<>>;
- Tuple when is_tuple(Tuple) -> inet:ntoa(Tuple);
+ Tuple when tuple_size(Tuple) =:= 8 -> [$[, inet:ntoa(Tuple), $]]; %% IPv6.
+ Tuple when tuple_size(Tuple) =:= 4 -> inet:ntoa(Tuple); %% IPv4.
Atom when is_atom(Atom) -> atom_to_list(Atom);
_ -> Host0
end,
diff --git a/test/gun_test.erl b/test/gun_test.erl
index bb162f4..c6cdd8c 100644
--- a/test/gun_test.erl
+++ b/test/gun_test.erl
@@ -45,7 +45,8 @@ init_origin(Transport, Protocol, Fun) ->
{ok, Pid, Port}.
init_origin(Parent, tcp, Protocol, Fun) ->
- {ok, ListenSocket} = gen_tcp:listen(0, [binary, {active, false}]),
+ %% We setup the socket for both IPv4 and IPv6.
+ {ok, ListenSocket} = gen_tcp:listen(0, [binary, {active, false}, inet6]),
{ok, {_, Port}} = inet:sockname(ListenSocket),
Parent ! {self(), Port},
{ok, ClientSocket} = gen_tcp:accept(ListenSocket, 5000),
diff --git a/test/rfc7230_SUITE.erl b/test/rfc7230_SUITE.erl
index da7b426..d6afb2c 100644
--- a/test/rfc7230_SUITE.erl
+++ b/test/rfc7230_SUITE.erl
@@ -34,6 +34,19 @@ host_default_port_https(_) ->
doc("The default port for https should not be sent in the host header. (RFC7230 2.7.2)"),
do_host_port(tls, 443, <<>>).
+host_ipv6(_) ->
+ doc("When connecting to a server using an IPv6 address the host "
+ "header must wrap the address with brackets. (RFC7230 5.4, RFC3986 3.2.2)"),
+ {ok, OriginPid, OriginPort} = init_origin(tcp, http),
+ {ok, ConnPid} = gun:open({0,0,0,0,0,0,0,1}, OriginPort, #{transport => tcp}),
+ {ok, http} = gun:await_up(ConnPid),
+ _ = gun:get(ConnPid, "/"),
+ handshake_completed = receive_from(OriginPid),
+ Data = receive_from(OriginPid),
+ Lines = binary:split(Data, <<"\r\n">>, [global]),
+ [<<"host: [::1]", _/bits>>] = [L || <<"host: ", _/bits>> = L <- Lines],
+ gun:close(ConnPid).
+
host_other_port_http(_) ->
doc("Non-default ports for http must be sent in the host header. (RFC7230 2.7.1)"),
do_host_port(tcp, 443, <<":443">>).
diff --git a/test/rfc7540_SUITE.erl b/test/rfc7540_SUITE.erl
index 44794ef..56447a3 100644
--- a/test/rfc7540_SUITE.erl
+++ b/test/rfc7540_SUITE.erl
@@ -35,6 +35,27 @@ authority_default_port_https(_) ->
"the :authority pseudo-header. (RFC7540 3, RFC7230 2.7.2)"),
do_authority_port(tls, 443, <<>>).
+authority_ipv6(_) ->
+ doc("When connecting to a server using an IPv6 address the :authority "
+ "pseudo-header must wrap the address with brackets. (RFC7540 8.1.2.3, RFC3986 3.2.2)"),
+ {ok, OriginPid, OriginPort} = init_origin(tcp, http2, fun(Parent, Socket, Transport) ->
+ %% Receive the HEADERS frame and send the headers decoded.
+ {ok, <<Len:24, 1:8, _:8, 1:32>>} = Transport:recv(Socket, 9, 1000),
+ {ok, ReqHeadersBlock} = Transport:recv(Socket, Len, 1000),
+ {ReqHeaders, _} = cow_hpack:decode(ReqHeadersBlock),
+ Parent ! {self(), ReqHeaders}
+ end),
+ {ok, ConnPid} = gun:open({0,0,0,0,0,0,0,1}, OriginPort, #{
+ transport => tcp,
+ protocols => [http2]
+ }),
+ {ok, http2} = gun:await_up(ConnPid),
+ handshake_completed = receive_from(OriginPid),
+ _ = gun:get(ConnPid, "/"),
+ ReqHeaders = receive_from(OriginPid),
+ {_, <<"[::1]", _/bits>>} = lists:keyfind(<<":authority">>, 1, ReqHeaders),
+ gun:close(ConnPid).
+
authority_other_port_http(_) ->
doc("Non-default ports for http must be sent in "
"the :authority pseudo-header. (RFC7540 3, RFC7230 2.7.1)"),