diff options
author | Loïc Hoguin <[email protected]> | 2020-10-22 18:48:06 +0200 |
---|---|---|
committer | Loïc Hoguin <[email protected]> | 2020-11-02 17:16:57 +0100 |
commit | d5f1a47e9ab758a51b23440eb72a0251527f3f7b (patch) | |
tree | e7c27355da10d89483394314a25cd5307ccc7da6 /test | |
parent | 465d072abf4a76104d4562ed15345b27fe9a0cff (diff) | |
download | gun-d5f1a47e9ab758a51b23440eb72a0251527f3f7b.tar.gz gun-d5f1a47e9ab758a51b23440eb72a0251527f3f7b.tar.bz2 gun-d5f1a47e9ab758a51b23440eb72a0251527f3f7b.zip |
Initial implementation of Websocket over HTTP/2http2-websocket
Diffstat (limited to 'test')
-rw-r--r-- | test/event_SUITE.erl | 183 | ||||
-rw-r--r-- | test/ws_SUITE.erl | 139 |
2 files changed, 230 insertions, 92 deletions
diff --git a/test/event_SUITE.erl b/test/event_SUITE.erl index d883dc5..8be10b1 100644 --- a/test/event_SUITE.erl +++ b/test/event_SUITE.erl @@ -34,25 +34,25 @@ groups() -> HTTP1Tests = [T || T <- Tests, lists:sublist(atom_to_list(T), 6) =:= "http1_"], %% Push is not possible over HTTP/1.1. PushTests = [T || T <- Tests, lists:sublist(atom_to_list(T), 5) =:= "push_"], - %% We currently do not support Websocket over HTTP/2. - WsTests = [T || T <- Tests, lists:sublist(atom_to_list(T), 3) =:= "ws_"], [ {http, [parallel], Tests -- [cancel_remote, cancel_remote_connect|PushTests]}, - {http2, [parallel], (Tests -- WsTests) -- HTTP1Tests} + {http2, [parallel], Tests -- HTTP1Tests} ]. init_per_suite(Config) -> - ProtoOpts = #{env => #{ - dispatch => cowboy_router:compile([{'_', [ - {"/", hello_h, []}, - {"/empty", empty_h, []}, - {"/inform", inform_h, []}, - {"/push", push_h, []}, - {"/stream", stream_h, []}, - {"/trailers", trailers_h, []}, - {"/ws", ws_echo_h, []} - ]}]) - }}, + Routes = [ + {"/", hello_h, []}, + {"/empty", empty_h, []}, + {"/inform", inform_h, []}, + {"/push", push_h, []}, + {"/stream", stream_h, []}, + {"/trailers", trailers_h, []}, + {"/ws", ws_echo_h, []} + ], + ProtoOpts = #{ + enable_connect_protocol => true, + env => #{dispatch => cowboy_router:compile([{'_', Routes}])} + }, {ok, _} = cowboy:start_clear({?MODULE, tcp}, [], ProtoOpts), TCPOriginPort = ranch:get_port({?MODULE, tcp}), {ok, _} = cowboy:start_tls({?MODULE, tls}, ct_helper:get_certs_from_ets(), ProtoOpts), @@ -1227,8 +1227,10 @@ http1_response_end_body_close(Config) -> ws_upgrade(Config) -> doc("Confirm that the ws_upgrade event callback is called."), + Protocol = config(name, config(tc_group_properties, Config)), {ok, Pid, _} = do_gun_open(Config), - {ok, _} = gun:await_up(Pid), + {ok, Protocol} = gun:await_up(Pid), + ws_SUITE:do_await_enable_connect_protocol(Protocol, Pid), StreamRef = gun:ws_upgrade(Pid, "/ws"), ReplyTo = self(), #{ @@ -1258,11 +1260,15 @@ do_ws_upgrade_connect(Config, ProxyProtocol) -> StreamRef1 = gun:connect(ConnPid, #{ host => "localhost", port => OriginPort, - protocols => [OriginProtocol] + protocols => [case OriginProtocol of + http -> http; + http2 -> {http2, #{notify_settings_changed => true}} + end] }, []), %% @todo _IsFin is 'fin' for HTTP and 'nofin' for HTTP/2... {response, _IsFin, 200, _} = gun:await(ConnPid, StreamRef1), {up, OriginProtocol} = gun:await(ConnPid, StreamRef1), + ws_SUITE:do_await_enable_connect_protocol(OriginProtocol, ConnPid), StreamRef2 = gun:ws_upgrade(ConnPid, "/ws", [], #{tunnel => StreamRef1}), #{ stream_ref := StreamRef2, @@ -1273,8 +1279,10 @@ do_ws_upgrade_connect(Config, ProxyProtocol) -> ws_upgrade_all_events(Config) -> doc("Confirm that a Websocket upgrade triggers all relevant events."), + Protocol = config(name, config(tc_group_properties, Config)), {ok, Pid, OriginPort} = do_gun_open(Config), - {ok, _} = gun:await_up(Pid), + {ok, Protocol} = gun:await_up(Pid), + ws_SUITE:do_await_enable_connect_protocol(Protocol, Pid), StreamRef = gun:ws_upgrade(Pid, "/ws"), ReplyTo = self(), #{ @@ -1283,11 +1291,15 @@ ws_upgrade_all_events(Config) -> opts := #{} } = do_receive_event(ws_upgrade), Authority = iolist_to_binary([<<"localhost:">>, integer_to_list(OriginPort)]), + Method = case Protocol of + http -> <<"GET">>; + http2 -> <<"CONNECT">> + end, #{ stream_ref := StreamRef, reply_to := ReplyTo, function := ws_upgrade, - method := <<"GET">>, + method := Method, authority := EventAuthority1, path := "/ws", headers := [_|_] @@ -1297,7 +1309,7 @@ ws_upgrade_all_events(Config) -> stream_ref := StreamRef, reply_to := ReplyTo, function := ws_upgrade, - method := <<"GET">>, + method := Method, authority := EventAuthority2, path := "/ws", headers := [_|_] @@ -1311,12 +1323,26 @@ ws_upgrade_all_events(Config) -> stream_ref := StreamRef, reply_to := ReplyTo } = do_receive_event(response_start), - #{ - stream_ref := StreamRef, - reply_to := ReplyTo, - status := 101, - headers := [_|_] - } = do_receive_event(response_inform), + _ = case Protocol of + http -> + #{ + stream_ref := StreamRef, + reply_to := ReplyTo, + status := 101, + headers := [_|_] + } = do_receive_event(response_inform); + http2 -> + #{ + stream_ref := StreamRef, + reply_to := ReplyTo, + status := 200, + headers := [_|_] + } = do_receive_event(response_headers), + #{ + stream_ref := StreamRef, + reply_to := ReplyTo + } = do_receive_event(response_end) + end, #{ stream_ref := StreamRef, protocol := ws @@ -1343,16 +1369,27 @@ do_ws_upgrade_all_events_connect(Config, ProxyProtocol) -> StreamRef1 = gun:connect(ConnPid, #{ host => "localhost", port => OriginPort, - protocols => [OriginProtocol] + protocols => [case OriginProtocol of + http -> http; + http2 -> {http2, #{notify_settings_changed => true}} + end] }, []), %% @todo _IsFin is 'fin' for HTTP and 'nofin' for HTTP/2... {response, _IsFin, 200, _} = gun:await(ConnPid, StreamRef1), {up, OriginProtocol} = gun:await(ConnPid, StreamRef1), + ws_SUITE:do_await_enable_connect_protocol(OriginProtocol, ConnPid), %% Skip all CONNECT-related events that may conflict. _ = do_receive_event(request_start), _ = do_receive_event(request_headers), _ = do_receive_event(request_end), _ = do_receive_event(response_start), + case OriginProtocol of + http -> ok; + http2 -> + _ = do_receive_event(response_headers), +% _ = do_receive_event(response_end), @todo Probably should response_end CONNECT responses for both protocols. + ok + end, _ = do_receive_event(protocol_changed), %% Check the Websocket events. StreamRef2 = gun:ws_upgrade(ConnPid, "/ws", [], #{tunnel => StreamRef1}), @@ -1362,11 +1399,15 @@ do_ws_upgrade_all_events_connect(Config, ProxyProtocol) -> opts := #{} } = do_receive_event(ws_upgrade), Authority = iolist_to_binary([<<"localhost:">>, integer_to_list(OriginPort)]), + Method = case OriginProtocol of + http -> <<"GET">>; + http2 -> <<"CONNECT">> + end, #{ stream_ref := StreamRef2, reply_to := ReplyTo, function := ws_upgrade, - method := <<"GET">>, + method := Method, authority := EventAuthority1, path := "/ws", headers := [_|_] @@ -1376,7 +1417,7 @@ do_ws_upgrade_all_events_connect(Config, ProxyProtocol) -> stream_ref := StreamRef2, reply_to := ReplyTo, function := ws_upgrade, - method := <<"GET">>, + method := Method, authority := EventAuthority2, path := "/ws", headers := [_|_] @@ -1390,12 +1431,26 @@ do_ws_upgrade_all_events_connect(Config, ProxyProtocol) -> stream_ref := StreamRef2, reply_to := ReplyTo } = do_receive_event(response_start), - #{ - stream_ref := StreamRef2, - reply_to := ReplyTo, - status := 101, - headers := [_|_] - } = do_receive_event(response_inform), + _ = case OriginProtocol of + http -> + #{ + stream_ref := StreamRef2, + reply_to := ReplyTo, + status := 101, + headers := [_|_] + } = do_receive_event(response_inform); + http2 -> + #{ + stream_ref := StreamRef2, + reply_to := ReplyTo, + status := 200, + headers := [_|_] + } = do_receive_event(response_headers), + #{ + stream_ref := StreamRef2, + reply_to := ReplyTo + } = do_receive_event(response_end) + end, #{ stream_ref := StreamRef2, protocol := ws @@ -1406,11 +1461,13 @@ do_ws_upgrade_all_events_connect(Config, ProxyProtocol) -> ws_recv_frame_start(Config) -> doc("Confirm that the ws_recv_frame_start event callback is called."), + Protocol = config(name, config(tc_group_properties, Config)), {ok, Pid, _} = do_gun_open(Config), - {ok, _} = gun:await_up(Pid), + {ok, Protocol} = gun:await_up(Pid), + ws_SUITE:do_await_enable_connect_protocol(Protocol, Pid), StreamRef = gun:ws_upgrade(Pid, "/ws"), {upgrade, [<<"websocket">>], _} = gun:await(Pid, StreamRef), - gun:ws_send(Pid, {text, <<"Hello!">>}), + gun:ws_send(Pid, StreamRef, {text, <<"Hello!">>}), ReplyTo = self(), #{ stream_ref := StreamRef, @@ -1440,11 +1497,15 @@ do_ws_recv_frame_start_connect(Config, ProxyProtocol) -> StreamRef1 = gun:connect(ConnPid, #{ host => "localhost", port => OriginPort, - protocols => [OriginProtocol] + protocols => [case OriginProtocol of + http -> http; + http2 -> {http2, #{notify_settings_changed => true}} + end] }, []), %% @todo _IsFin is 'fin' for HTTP and 'nofin' for HTTP/2... {response, _IsFin, 200, _} = gun:await(ConnPid, StreamRef1), {up, OriginProtocol} = gun:await(ConnPid, StreamRef1), + ws_SUITE:do_await_enable_connect_protocol(OriginProtocol, ConnPid), StreamRef2 = gun:ws_upgrade(ConnPid, "/ws", [], #{tunnel => StreamRef1}), {upgrade, [<<"websocket">>], _} = gun:await(ConnPid, StreamRef2), gun:ws_send(ConnPid, StreamRef2, {text, <<"Hello!">>}), @@ -1458,11 +1519,13 @@ do_ws_recv_frame_start_connect(Config, ProxyProtocol) -> ws_recv_frame_header(Config) -> doc("Confirm that the ws_recv_frame_header event callback is called."), + Protocol = config(name, config(tc_group_properties, Config)), {ok, Pid, _} = do_gun_open(Config), - {ok, _} = gun:await_up(Pid), + {ok, Protocol} = gun:await_up(Pid), + ws_SUITE:do_await_enable_connect_protocol(Protocol, Pid), StreamRef = gun:ws_upgrade(Pid, "/ws"), {upgrade, [<<"websocket">>], _} = gun:await(Pid, StreamRef), - gun:ws_send(Pid, {text, <<"Hello!">>}), + gun:ws_send(Pid, StreamRef, {text, <<"Hello!">>}), ReplyTo = self(), #{ stream_ref := StreamRef, @@ -1496,11 +1559,15 @@ do_ws_recv_frame_header_connect(Config, ProxyProtocol) -> StreamRef1 = gun:connect(ConnPid, #{ host => "localhost", port => OriginPort, - protocols => [OriginProtocol] + protocols => [case OriginProtocol of + http -> http; + http2 -> {http2, #{notify_settings_changed => true}} + end] }, []), %% @todo _IsFin is 'fin' for HTTP and 'nofin' for HTTP/2... {response, _IsFin, 200, _} = gun:await(ConnPid, StreamRef1), {up, OriginProtocol} = gun:await(ConnPid, StreamRef1), + ws_SUITE:do_await_enable_connect_protocol(OriginProtocol, ConnPid), StreamRef2 = gun:ws_upgrade(ConnPid, "/ws", [], #{tunnel => StreamRef1}), {upgrade, [<<"websocket">>], _} = gun:await(ConnPid, StreamRef2), gun:ws_send(ConnPid, StreamRef2, {text, <<"Hello!">>}), @@ -1518,11 +1585,13 @@ do_ws_recv_frame_header_connect(Config, ProxyProtocol) -> ws_recv_frame_end(Config) -> doc("Confirm that the ws_recv_frame_end event callback is called."), + Protocol = config(name, config(tc_group_properties, Config)), {ok, Pid, _} = do_gun_open(Config), - {ok, _} = gun:await_up(Pid), + {ok, Protocol} = gun:await_up(Pid), + ws_SUITE:do_await_enable_connect_protocol(Protocol, Pid), StreamRef = gun:ws_upgrade(Pid, "/ws"), {upgrade, [<<"websocket">>], _} = gun:await(Pid, StreamRef), - gun:ws_send(Pid, {text, <<"Hello!">>}), + gun:ws_send(Pid, StreamRef, {text, <<"Hello!">>}), ReplyTo = self(), #{ stream_ref := StreamRef, @@ -1553,11 +1622,15 @@ do_ws_recv_frame_end_connect(Config, ProxyProtocol) -> StreamRef1 = gun:connect(ConnPid, #{ host => "localhost", port => OriginPort, - protocols => [OriginProtocol] + protocols => [case OriginProtocol of + http -> http; + http2 -> {http2, #{notify_settings_changed => true}} + end] }, []), %% @todo _IsFin is 'fin' for HTTP and 'nofin' for HTTP/2... {response, _IsFin, 200, _} = gun:await(ConnPid, StreamRef1), {up, OriginProtocol} = gun:await(ConnPid, StreamRef1), + ws_SUITE:do_await_enable_connect_protocol(OriginProtocol, ConnPid), StreamRef2 = gun:ws_upgrade(ConnPid, "/ws", [], #{tunnel => StreamRef1}), {upgrade, [<<"websocket">>], _} = gun:await(ConnPid, StreamRef2), gun:ws_send(ConnPid, StreamRef2, {text, <<"Hello!">>}), @@ -1581,11 +1654,13 @@ ws_send_frame_end(Config) -> do_ws_send_frame(Config, ?FUNCTION_NAME). do_ws_send_frame(Config, EventName) -> + Protocol = config(name, config(tc_group_properties, Config)), {ok, Pid, _} = do_gun_open(Config), - {ok, _} = gun:await_up(Pid), + {ok, Protocol} = gun:await_up(Pid), + ws_SUITE:do_await_enable_connect_protocol(Protocol, Pid), StreamRef = gun:ws_upgrade(Pid, "/ws"), {upgrade, [<<"websocket">>], _} = gun:await(Pid, StreamRef), - gun:ws_send(Pid, {text, <<"Hello!">>}), + gun:ws_send(Pid, StreamRef, {text, <<"Hello!">>}), ReplyTo = self(), #{ stream_ref := StreamRef, @@ -1621,11 +1696,15 @@ do_ws_send_frame_connect(Config, ProxyProtocol, EventName) -> StreamRef1 = gun:connect(ConnPid, #{ host => "localhost", port => OriginPort, - protocols => [OriginProtocol] + protocols => [case OriginProtocol of + http -> http; + http2 -> {http2, #{notify_settings_changed => true}} + end] }, []), %% @todo _IsFin is 'fin' for HTTP and 'nofin' for HTTP/2... {response, _IsFin, 200, _} = gun:await(ConnPid, StreamRef1), {up, OriginProtocol} = gun:await(ConnPid, StreamRef1), + ws_SUITE:do_await_enable_connect_protocol(OriginProtocol, ConnPid), StreamRef2 = gun:ws_upgrade(ConnPid, "/ws", [], #{tunnel => StreamRef1}), {upgrade, [<<"websocket">>], _} = gun:await(ConnPid, StreamRef2), gun:ws_send(ConnPid, StreamRef2, {text, <<"Hello!">>}), @@ -1641,8 +1720,10 @@ do_ws_send_frame_connect(Config, ProxyProtocol, EventName) -> ws_protocol_changed(Config) -> doc("Confirm that the protocol_changed event callback is called on Websocket upgrade success."), + Protocol = config(name, config(tc_group_properties, Config)), {ok, Pid, _} = do_gun_open(Config), - {ok, _} = gun:await_up(Pid), + {ok, Protocol} = gun:await_up(Pid), + ws_SUITE:do_await_enable_connect_protocol(Protocol, Pid), _ = gun:ws_upgrade(Pid, "/ws"), #{ protocol := ws @@ -1668,11 +1749,15 @@ do_ws_protocol_changed_connect(Config, ProxyProtocol) -> StreamRef1 = gun:connect(ConnPid, #{ host => "localhost", port => OriginPort, - protocols => [OriginProtocol] + protocols => [case OriginProtocol of + http -> http; + http2 -> {http2, #{notify_settings_changed => true}} + end] }, []), %% @todo _IsFin is 'fin' for HTTP and 'nofin' for HTTP/2... {response, _IsFin, 200, _} = gun:await(ConnPid, StreamRef1), {up, OriginProtocol} = gun:await(ConnPid, StreamRef1), + ws_SUITE:do_await_enable_connect_protocol(OriginProtocol, ConnPid), #{ stream_ref := StreamRef1, protocol := OriginProtocol @@ -1951,6 +2036,7 @@ do_gun_open(Config) -> do_gun_open(OriginPort, Config) -> Opts = #{ event_handler => {?MODULE, self()}, + http2_opts => #{notify_settings_changed => true}, protocols => [config(name, config(tc_group_properties, Config))] }, {ok, Pid} = gun:open("localhost", OriginPort, Opts), @@ -1960,6 +2046,7 @@ do_gun_open_tls(Config) -> OriginPort = config(tls_origin_port, Config), Opts = #{ event_handler => {?MODULE, self()}, + http2_opts => #{notify_settings_changed => true}, protocols => [config(name, config(tc_group_properties, Config))], transport => tls }, diff --git a/test/ws_SUITE.erl b/test/ws_SUITE.erl index c04acc2..201403e 100644 --- a/test/ws_SUITE.erl +++ b/test/ws_SUITE.erl @@ -22,19 +22,30 @@ %% ct. all() -> - [{group, ws}]. + [{group, http}, {group, http2}]. groups() -> - [{ws, [], ct_helper:all(?MODULE)}]. + Tests = ct_helper:all(?MODULE), + HTTP1Tests = [ + http10_upgrade_error, + http11_request_error, + http11_keepalive, + http11_keepalive_default_silence_pings + ], + [ + {http, [], Tests}, + {http2, [], Tests -- HTTP1Tests} + ]. init_per_suite(Config) -> Routes = [ {"/", ws_echo_h, []}, {"/reject", ws_reject_h, []} ], - {ok, _} = cowboy:start_clear(ws, [], #{env => #{ - dispatch => cowboy_router:compile([{'_', Routes}]) - }}), + {ok, _} = cowboy:start_clear(ws, [], #{ + enable_connect_protocol => true, + env => #{dispatch => cowboy_router:compile([{'_', Routes}])} + }), Port = ranch:get_port(ws), [{port, Port}|Config]. @@ -45,16 +56,37 @@ end_per_suite(_) -> await(Config) -> doc("Ensure gun:await/2 can be used to receive Websocket frames."), - {ok, ConnPid} = gun:open("localhost", config(port, Config)), - {ok, _} = gun:await_up(ConnPid), + Protocol = config(name, config(tc_group_properties, Config)), + {ok, ConnPid} = gun:open("localhost", config(port, Config), #{ + protocols => [Protocol], + http2_opts => #{notify_settings_changed => true} + }), + {ok, Protocol} = gun:await_up(ConnPid), + do_await_enable_connect_protocol(Protocol, ConnPid), StreamRef = gun:ws_upgrade(ConnPid, "/", []), {upgrade, [<<"websocket">>], _} = gun:await(ConnPid, StreamRef), Frame = {text, <<"Hello!">>}, - gun:ws_send(ConnPid, Frame), + gun:ws_send(ConnPid, StreamRef, Frame), {ws, Frame} = gun:await(ConnPid, StreamRef), gun:close(ConnPid). -error_http10_upgrade(Config) -> +headers_normalized_upgrade(Config) -> + doc("Headers passed to ws_upgrade are normalized before being used."), + Protocol = config(name, config(tc_group_properties, Config)), + {ok, ConnPid} = gun:open("localhost", config(port, Config), #{ + protocols => [Protocol], + http2_opts => #{notify_settings_changed => true} + }), + {ok, Protocol} = gun:await_up(ConnPid), + do_await_enable_connect_protocol(Protocol, ConnPid), + StreamRef = gun:ws_upgrade(ConnPid, "/", #{ + atom_header_name => <<"value">>, + "string_header_name" => <<"value">> + }), + {upgrade, [<<"websocket">>], _} = gun:await(ConnPid, StreamRef), + gun:close(ConnPid). + +http10_upgrade_error(Config) -> doc("Attempting to upgrade HTTP/1.0 to Websocket produces an error."), {ok, ConnPid} = gun:open("localhost", config(port, Config), #{ http_opts => #{version => 'HTTP/1.0'} @@ -70,28 +102,7 @@ error_http10_upgrade(Config) -> error(timeout) end. -headers_normalized_upgrade(Config) -> - doc("Headers passed to ws_upgrade are normalized before being used."), - {ok, ConnPid} = gun:open("localhost", config(port, Config)), - {ok, _} = gun:await_up(ConnPid), - StreamRef = gun:ws_upgrade(ConnPid, "/", #{ - atom_header_name => <<"value">>, - "string_header_name" => <<"value">> - }), - {upgrade, [<<"websocket">>], _} = gun:await(ConnPid, StreamRef), - gun:close(ConnPid). - -error_http_request(Config) -> - doc("Ensure that requests are rejected while using Websocket."), - {ok, ConnPid} = gun:open("localhost", config(port, Config)), - {ok, _} = gun:await_up(ConnPid), - StreamRef1 = gun:ws_upgrade(ConnPid, "/", []), - {upgrade, [<<"websocket">>], _} = gun:await(ConnPid, StreamRef1), - StreamRef2 = gun:get(ConnPid, "/"), - {error, {connection_error, {badstate, _}}} = gun:await(ConnPid, StreamRef2), - gun:close(ConnPid). - -keepalive(Config) -> +http11_keepalive(Config) -> doc("Ensure that Gun automatically sends ping frames."), {ok, ConnPid} = gun:open("localhost", config(port, Config), #{ ws_opts => #{ @@ -106,7 +117,7 @@ keepalive(Config) -> {ws, pong} = gun:await(ConnPid, StreamRef), gun:close(ConnPid). -keepalive_default_silence_pings(Config) -> +http11_keepalive_default_silence_pings(Config) -> doc("Ensure that Gun does not forward ping/pong by default."), {ok, ConnPid} = gun:open("localhost", config(port, Config), #{ ws_opts => #{keepalive => 100} @@ -118,10 +129,25 @@ keepalive_default_silence_pings(Config) -> {error, timeout} = gun:await(ConnPid, StreamRef, 1000), gun:close(ConnPid). -reject_upgrade(Config) -> - doc("Ensure Websocket connections can be rejected."), +http11_request_error(Config) -> + doc("Ensure that HTTP/1.1 requests are rejected while using Websocket."), {ok, ConnPid} = gun:open("localhost", config(port, Config)), {ok, _} = gun:await_up(ConnPid), + StreamRef1 = gun:ws_upgrade(ConnPid, "/", []), + {upgrade, [<<"websocket">>], _} = gun:await(ConnPid, StreamRef1), + StreamRef2 = gun:get(ConnPid, "/"), + {error, {connection_error, {badstate, _}}} = gun:await(ConnPid, StreamRef2), + gun:close(ConnPid). + +reject_upgrade(Config) -> + doc("Ensure Websocket connections can be rejected."), + Protocol = config(name, config(tc_group_properties, Config)), + {ok, ConnPid} = gun:open("localhost", config(port, Config), #{ + protocols => [Protocol], + http2_opts => #{notify_settings_changed => true} + }), + {ok, Protocol} = gun:await_up(ConnPid), + do_await_enable_connect_protocol(Protocol, ConnPid), StreamRef = gun:ws_upgrade(ConnPid, "/reject", []), receive {gun_response, ConnPid, StreamRef, nofin, 400, _} -> @@ -134,7 +160,7 @@ reject_upgrade(Config) -> end. reply_to(Config) -> - doc("Ensure we can send a list of frames in one gun:ws_send call."), + doc("Ensure the reply_to request option is respected."), Self = self(), Frame = {text, <<"Hello!">>}, ReplyTo = spawn(fun() -> @@ -144,36 +170,61 @@ reply_to(Config) -> {ws, Frame} = gun:await(ConnPid, StreamRef), Self ! {self(), ok} end), - {ok, ConnPid} = gun:open("localhost", config(port, Config)), - {ok, _} = gun:await_up(ConnPid), + Protocol = config(name, config(tc_group_properties, Config)), + {ok, ConnPid} = gun:open("localhost", config(port, Config), #{ + protocols => [Protocol], + http2_opts => #{notify_settings_changed => true} + }), + {ok, Protocol} = gun:await_up(ConnPid), + do_await_enable_connect_protocol(Protocol, ConnPid), StreamRef = gun:ws_upgrade(ConnPid, "/", [], #{reply_to => ReplyTo}), ReplyTo ! {ConnPid, StreamRef}, - receive {ReplyTo, ready} -> gun:ws_send(ConnPid, Frame) after 1000 -> error(timeout) end, + receive {ReplyTo, ready} -> gun:ws_send(ConnPid, StreamRef, Frame) after 1000 -> error(timeout) end, receive {ReplyTo, ok} -> gun:close(ConnPid) after 1000 -> error(timeout) end. send_many(Config) -> doc("Ensure we can send a list of frames in one gun:ws_send call."), - {ok, ConnPid} = gun:open("localhost", config(port, Config)), - {ok, _} = gun:await_up(ConnPid), + Protocol = config(name, config(tc_group_properties, Config)), + {ok, ConnPid} = gun:open("localhost", config(port, Config), #{ + protocols => [Protocol], + http2_opts => #{notify_settings_changed => true} + }), + {ok, Protocol} = gun:await_up(ConnPid), + do_await_enable_connect_protocol(Protocol, ConnPid), StreamRef = gun:ws_upgrade(ConnPid, "/", []), {upgrade, [<<"websocket">>], _} = gun:await(ConnPid, StreamRef), Frame1 = {text, <<"Hello!">>}, Frame2 = {binary, <<"World!">>}, - gun:ws_send(ConnPid, [Frame1, Frame2]), + gun:ws_send(ConnPid, StreamRef, [Frame1, Frame2]), {ws, Frame1} = gun:await(ConnPid, StreamRef), {ws, Frame2} = gun:await(ConnPid, StreamRef), gun:close(ConnPid). send_many_close(Config) -> doc("Ensure we can send a list of frames in one gun:ws_send call, including a close frame."), - {ok, ConnPid} = gun:open("localhost", config(port, Config)), - {ok, _} = gun:await_up(ConnPid), + Protocol = config(name, config(tc_group_properties, Config)), + {ok, ConnPid} = gun:open("localhost", config(port, Config), #{ + protocols => [Protocol], + http2_opts => #{notify_settings_changed => true} + }), + {ok, Protocol} = gun:await_up(ConnPid), + do_await_enable_connect_protocol(Protocol, ConnPid), StreamRef = gun:ws_upgrade(ConnPid, "/", []), {upgrade, [<<"websocket">>], _} = gun:await(ConnPid, StreamRef), Frame1 = {text, <<"Hello!">>}, Frame2 = {binary, <<"World!">>}, - gun:ws_send(ConnPid, [Frame1, Frame2, close]), + gun:ws_send(ConnPid, StreamRef, [Frame1, Frame2, close]), {ws, Frame1} = gun:await(ConnPid, StreamRef), {ws, Frame2} = gun:await(ConnPid, StreamRef), {ws, close} = gun:await(ConnPid, StreamRef), gun:close(ConnPid). + +%% Internal. + +do_await_enable_connect_protocol(http, _) -> + ok; +%% We cannot do a CONNECT :protocol request until the server tells us we can. +do_await_enable_connect_protocol(http2, ConnPid) -> + {notify, settings_changed, #{enable_connect_protocol := true}} + = gun:await(ConnPid, undefined), %% @todo Maybe have a gun:await/1? + ok. |