aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/gun_http.erl3
-rw-r--r--src/gun_http2.erl7
-rw-r--r--test/rfc7230_SUITE.erl2
-rw-r--r--test/rfc7540_SUITE.erl74
4 files changed, 76 insertions, 10 deletions
diff --git a/src/gun_http.erl b/src/gun_http.erl
index 29ba758..313671a 100644
--- a/src/gun_http.erl
+++ b/src/gun_http.erl
@@ -28,6 +28,9 @@
-export([down/1]).
-export([ws_upgrade/7]).
+%% Functions shared with gun_http2.
+-export([host_header/3]).
+
-type io() :: head | {body, non_neg_integer()} | body_close | body_chunked | body_trailer.
%% @todo Make that a record.
diff --git a/src/gun_http2.erl b/src/gun_http2.erl
index f5cf834..3d02fcf 100644
--- a/src/gun_http2.erl
+++ b/src/gun_http2.erl
@@ -268,14 +268,9 @@ request(State=#http2_state{socket=Socket, transport=Transport,
streams=[Stream|Streams]}, StreamID, fin, Body).
prepare_headers(#http2_state{transport=Transport}, Method, Host0, Port, Path, Headers0) ->
- Host1 = case Host0 of
- {local, _SocketPath} -> <<>>;
- Tuple when is_tuple(Tuple) -> inet:ntoa(Tuple);
- _ -> Host0
- end,
Authority = case lists:keyfind(<<"host">>, 1, Headers0) of
{_, Host} -> Host;
- _ -> [Host1, $:, integer_to_binary(Port)]
+ _ -> gun_http:host_header(Transport, Host0, Port)
end,
%% @todo We also must remove any header found in the connection header.
Headers =
diff --git a/test/rfc7230_SUITE.erl b/test/rfc7230_SUITE.erl
index 517e2d5..f6e077b 100644
--- a/test/rfc7230_SUITE.erl
+++ b/test/rfc7230_SUITE.erl
@@ -114,7 +114,7 @@ do_host_port(Transport, DefaultPort, HostHeaderPort) ->
{ok, OriginPid, OriginPort} = do_origin_start(Transport, http),
{ok, ConnPid} = gun:open("localhost", OriginPort, #{transport => Transport}),
{ok, http} = gun:await_up(ConnPid),
- %% Change the port in the state to trigger the default port behavior.
+ %% Change the origin's port in the state to trigger the default port behavior.
_ = sys:replace_state(ConnPid, fun({StateName, StateData}) ->
{StateName, setelement(7, StateData, DefaultPort)}
end, 5000),
diff --git a/test/rfc7540_SUITE.erl b/test/rfc7540_SUITE.erl
index 9398318..54667b7 100644
--- a/test/rfc7540_SUITE.erl
+++ b/test/rfc7540_SUITE.erl
@@ -16,6 +16,10 @@
-compile(export_all).
-compile(nowarn_export_all).
+-ifdef(OTP_RELEASE).
+-compile({nowarn_deprecated_function, [{ssl, ssl_accept, 2}]}).
+-endif.
+
-import(ct_helper, [doc/1]).
all() ->
@@ -23,9 +27,16 @@ all() ->
%% Server helpers.
-do_origin_start(Fun) ->
+do_origin_start(Transport, Fun) ->
Self = self(),
- Pid = spawn_link(fun() -> do_origin_init_tcp(Self, Fun) end),
+ Pid = spawn_link(fun() ->
+ case Transport of
+ tcp ->
+ do_origin_init_tcp(Self, Fun);
+ tls ->
+ do_origin_init_tls_h2(Self, Fun)
+ end
+ end),
Port = do_receive(Pid),
{ok, Pid, Port}.
@@ -37,6 +48,18 @@ do_origin_init_tcp(Parent, Fun) ->
do_handshake(ClientSocket, gen_tcp),
Fun(Parent, ClientSocket, gen_tcp).
+do_origin_init_tls_h2(Parent, Fun) ->
+ Opts = ct_helper:get_certs_from_ets(),
+ {ok, ListenSocket} = ssl:listen(0, [binary, {active, false},
+ {alpn_preferred_protocols, [<<"h2">>]}|Opts]),
+ {ok, {_, Port}} = ssl:sockname(ListenSocket),
+ Parent ! {self(), Port},
+ {ok, ClientSocket} = ssl:transport_accept(ListenSocket, 5000),
+ ok = ssl:ssl_accept(ClientSocket, 5000),
+ {ok, <<"h2">>} = ssl:negotiated_protocol(ClientSocket),
+ do_handshake(ClientSocket, ssl),
+ Fun(Parent, ClientSocket, ssl).
+
do_handshake(Socket, Transport) ->
%% Send a valid preface.
ok = Transport:send(Socket, cow_http2:settings(#{})),
@@ -65,10 +88,55 @@ do_receive(Pid, Timeout) ->
%% Tests.
+authority_default_port_http(_) ->
+ doc("The default port for http should not be sent in "
+ "the :authority pseudo-header. (RFC7540 3, RFC7230 2.7.1)"),
+ do_authority_port(tcp, 80, <<>>).
+
+authority_default_port_https(_) ->
+ doc("The default port for https should not be sent in "
+ "the :authority pseudo-header. (RFC7540 3, RFC7230 2.7.2)"),
+ do_authority_port(tls, 443, <<>>).
+
+authority_other_port_http(_) ->
+ doc("Non-default ports for http must be sent in "
+ "the :authority pseudo-header. (RFC7540 3, RFC7230 2.7.1)"),
+ do_authority_port(tcp, 443, <<":443">>).
+
+authority_other_port_https(_) ->
+ doc("Non-default ports for https must be sent in "
+ "the :authority pseudo-header. (RFC7540 3, RFC7230 2.7.2)"),
+ do_authority_port(tls, 80, <<":80">>).
+
+do_authority_port(Transport0, DefaultPort, AuthorityHeaderPort) ->
+ {ok, OriginPid, OriginPort} = do_origin_start(Transport0, 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("localhost", OriginPort, #{
+ transport => Transport0,
+ protocols => [http2]
+ }),
+ {ok, http2} = gun:await_up(ConnPid),
+ %% Change the origin's port in the state to trigger the default port behavior.
+ _ = sys:replace_state(ConnPid, fun({StateName, StateData}) ->
+ {StateName, setelement(7, StateData, DefaultPort)}
+ end, 5000),
+ %% Confirm the default port is not sent in the request.
+ timer:sleep(100), %% Give enough time for the handshake to fully complete.
+ _ = gun:get(ConnPid, "/"),
+ ReqHeaders = do_receive(OriginPid),
+ {_, <<"localhost", Rest/bits>>} = lists:keyfind(<<":authority">>, 1, ReqHeaders),
+ AuthorityHeaderPort = Rest,
+ gun:close(ConnPid).
+
headers_priority_flag(_) ->
doc("HEADERS frames may include a PRIORITY flag indicating "
"that stream dependency information is attached. (RFC7540 6.2)"),
- {ok, _, Port} = do_origin_start(fun(_, Socket, Transport) ->
+ {ok, _, Port} = do_origin_start(tcp, fun(_, Socket, Transport) ->
%% Receive a HEADERS frame.
{ok, <<_:24, 1:8, _:8, 1:32>>} = Transport:recv(Socket, 9, 1000),
%% Send a HEADERS frame with PRIORITY back.