diff options
-rw-r--r-- | src/gun_http.erl | 4 | ||||
-rw-r--r-- | src/gun_http3.erl | 6 | ||||
-rw-r--r-- | src/gun_ws.erl | 4 | ||||
-rw-r--r-- | test/ping_SUITE.erl | 118 | ||||
-rw-r--r-- | test/rfc7540_SUITE.erl | 74 |
5 files changed, 125 insertions, 81 deletions
diff --git a/src/gun_http.erl b/src/gun_http.erl index 005f04b..6e349b1 100644 --- a/src/gun_http.erl +++ b/src/gun_http.erl @@ -561,8 +561,8 @@ keepalive(#http_state{socket=Socket, transport=Transport, out=head}, _, EvHandle keepalive(_State, _, EvHandlerState) -> {[], EvHandlerState}. -ping(_State, undefined, _ReplyTo, _PingRef) -> - {error, unsupported_by_protocol}. +ping(_State, undefined, _ReplyTo, PingRef) -> + {error, {ping_unsupported_by_protocol, PingRef}}. headers(State, StreamRef, ReplyTo, _, _, _, _, _, _, CookieStore, _, EvHandlerState) when is_list(StreamRef) -> diff --git a/src/gun_http3.erl b/src/gun_http3.erl index 99f14cc..5e2395a 100644 --- a/src/gun_http3.erl +++ b/src/gun_http3.erl @@ -487,10 +487,10 @@ close(_Reason, _State, _, EvHandlerState) -> keepalive(_State, _, _EvHandlerState) -> error(todo). --spec ping(_, _, _, _) -> {error, not_implemented}. +-spec ping(_, _, _, _) -> {error, {ping_not_implemented, reference()}}. -ping(_State, _Tunnel, _ReplyTo, _PingRef) -> - {error, not_implemented}. +ping(_State, _Tunnel, _ReplyTo, PingRef) -> + {error, {ping_not_implemented, PingRef}}. headers(State0=#http3_state{conn=Conn, transport=Transport, http3_machine=HTTP3Machine0}, StreamRef, ReplyTo, Method, Host, Port, diff --git a/src/gun_ws.erl b/src/gun_ws.erl index 07df766..3c20852 100644 --- a/src/gun_ws.erl +++ b/src/gun_ws.erl @@ -308,8 +308,8 @@ close(_, _, _, EvHandlerState) -> keepalive(State=#ws_state{reply_to=ReplyTo}, EvHandler, EvHandlerState0) -> send(ping, State, ReplyTo, EvHandler, EvHandlerState0). -ping(_State, undefined, _ReplyTo, _PingRef) -> - {error, not_implemented}. +ping(_State, undefined, _ReplyTo, PingRef) -> + {error, {ping_not_implemented, PingRef}}. %% Send one frame. send(Frame, State=#ws_state{stream_ref=StreamRef, diff --git a/test/ping_SUITE.erl b/test/ping_SUITE.erl new file mode 100644 index 0000000..1509999 --- /dev/null +++ b/test/ping_SUITE.erl @@ -0,0 +1,118 @@ +%% Copyright (c) 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(ping_SUITE). +-compile(export_all). +-compile(nowarn_export_all). + +-import(ct_helper, [config/2]). +-import(ct_helper, [doc/1]). +-import(gun_test, [init_origin/3]). +-import(gun_test, [receive_from/1]). + +all() -> + ct_helper:all(?MODULE). + +%% Tests. + +h1_user_ping(Config0) -> + doc("The PING frame cannot be used to test an HTTP/1.1 connection."), + Config = gun_test:init_cowboy_tcp(?FUNCTION_NAME, #{}, Config0), + OriginPort = config(port, Config), + {ok, ConnPid} = gun:open("localhost", OriginPort, #{ + protocols => [http] + }), + {ok, http} = gun:await_up(ConnPid), + PingRef = gun:ping(ConnPid), + receive + {gun_down, ConnPid, http, {error, {ping_unsupported_by_protocol, PingRef}}, []} -> + gun:close(ConnPid) + after 1000 -> + ct:pal("~p", [process_info(self(), messages)]), + error(timeout) + end. + +h2_user_ping(_) -> + doc("The PING frame may be used to easily test an HTTP/2 connection."), + {ok, OriginPid, OriginPort} = init_origin(tcp, http2, fun (_, _, Socket, Transport) -> + {ok, Data} = Transport:recv(Socket, 9, infinity), + <<Len:24, 6:8, %% PING + 0:8, %% Flags + 0:1, 0:31>> = Data, + {ok, Payload} = Transport:recv(Socket, Len, 1000), + 8 = Len = byte_size(Payload), + Ack = <<8:24, 6:8, %% PING + 1:8, %% Ack flag + 0:1, 0:31, Payload/binary>>, + ok = Transport:send(Socket, Ack) + end), + {ok, ConnPid} = gun:open("localhost", OriginPort, #{ + protocols => [http2] + }), + {ok, http2} = gun:await_up(ConnPid), + handshake_completed = receive_from(OriginPid), + PingRef = gun:ping(ConnPid), + {notify, ping_ack, PingRef} = gun:await(ConnPid, undefined), + gun:close(ConnPid). + +h2c_user_ping_via_http(_) -> + doc("The PING frame may be used to easily test an HTTP/2 connection."), + do_h2c_user_ping_tunnel(http). + +h2c_user_ping_via_https(_) -> + doc("The PING frame may be used to easily test an HTTP/2 connection."), + do_h2c_user_ping_tunnel(https). + +h2c_user_ping_via_h2c(_) -> + doc("The PING frame may be used to easily test an HTTP/2 connection."), + do_h2c_user_ping_tunnel(h2c). + +h2c_user_ping_via_h2(_) -> + doc("The PING frame may be used to easily test an HTTP/2 connection."), + do_h2c_user_ping_tunnel(h2). + +do_h2c_user_ping_tunnel(ProxyType) -> + {ok, OriginPid, OriginPort} = init_origin(tcp, http2, fun (_, _, Socket, Transport) -> + {ok, Data} = Transport:recv(Socket, 9, infinity), + <<Len:24, 6:8, %% PING + 0:8, %% Flags + 0:1, 0:31>> = Data, + {ok, Payload} = Transport:recv(Socket, Len, 1000), + 8 = Len = byte_size(Payload), + Ack = <<8:24, 6:8, %% PING + 1:8, %% Ack flag + 0:1, 0:31, Payload/binary>>, + ok = Transport:send(Socket, Ack) + end), + {ok, ProxyPid, ProxyPort} = tunnel_SUITE:do_proxy_start(ProxyType), + {ProxyTransport, ProxyProtocol} = tunnel_SUITE:do_type(ProxyType), + {ok, ConnPid} = gun:open("localhost", ProxyPort, #{ + transport => ProxyTransport, + tls_opts => [{verify, verify_none}, {versions, ['tlsv1.2']}], + protocols => [ProxyProtocol] + }), + {ok, ProxyProtocol} = gun:await_up(ConnPid), + tunnel_SUITE:do_handshake_completed(ProxyProtocol, ProxyPid), + StreamRef = gun:connect(ConnPid, #{ + host => "localhost", + port => OriginPort, + transport => tcp, + protocols => [http2] + }), + {response, fin, 200, _} = gun:await(ConnPid, StreamRef), + handshake_completed = receive_from(OriginPid), + {up, http2} = gun:await(ConnPid, StreamRef), + PingRef = gun:ping(ConnPid, #{tunnel => StreamRef}), + {notify, ping_ack, PingRef} = gun:await(ConnPid, undefined), + gun:close(ConnPid). diff --git a/test/rfc7540_SUITE.erl b/test/rfc7540_SUITE.erl index 5d470eb..5bfa6cd 100644 --- a/test/rfc7540_SUITE.erl +++ b/test/rfc7540_SUITE.erl @@ -554,80 +554,6 @@ do_ping_ack_loop_fun() -> Loop(Parent, ListenSocket, Socket, Transport) end. -user_ping(_) -> - doc("The PING frame may be used to easily test a connection. (RFC7540 8.1.4)"), - {ok, OriginPid, OriginPort} = init_origin(tcp, http2, fun (_, _, Socket, Transport) -> - {ok, Data} = Transport:recv(Socket, 9, infinity), - <<Len:24, 6:8, %% PING - 0:8, %% Flags - 0:1, 0:31>> = Data, - {ok, Payload} = Transport:recv(Socket, Len, 1000), - 8 = Len = byte_size(Payload), - Ack = <<8:24, 6:8, %% PING - 1:8, %% Ack flag - 0:1, 0:31, Payload/binary>>, - ok = Transport:send(Socket, Ack) - end), - {ok, ConnPid} = gun:open("localhost", OriginPort, #{ - protocols => [http2] - }), - {ok, http2} = gun:await_up(ConnPid), - handshake_completed = receive_from(OriginPid), - PingRef = gun:ping(ConnPid), - {notify, ping_ack, PingRef} = gun:await(ConnPid, undefined), - gun:close(ConnPid). - -user_ping_via_http(_) -> - doc("The PING frame may be used to easily test a connection. (RFC7540 8.1.4)"), - do_user_ping_tunnel(http). - -user_ping_via_https(_) -> - doc("The PING frame may be used to easily test a connection. (RFC7540 8.1.4)"), - do_user_ping_tunnel(https). - -user_ping_via_h2c(_) -> - doc("The PING frame may be used to easily test a connection. (RFC7540 8.1.4)"), - do_user_ping_tunnel(h2c). - -user_ping_via_h2(_) -> - doc("The PING frame may be used to easily test a connection. (RFC7540 8.1.4)"), - do_user_ping_tunnel(h2). - -do_user_ping_tunnel(ProxyType) -> - {ok, OriginPid, OriginPort} = init_origin(tcp, http2, fun (_, _, Socket, Transport) -> - {ok, Data} = Transport:recv(Socket, 9, infinity), - <<Len:24, 6:8, %% PING - 0:8, %% Flags - 0:1, 0:31>> = Data, - {ok, Payload} = Transport:recv(Socket, Len, 1000), - 8 = Len = byte_size(Payload), - Ack = <<8:24, 6:8, %% PING - 1:8, %% Ack flag - 0:1, 0:31, Payload/binary>>, - ok = Transport:send(Socket, Ack) - end), - {ok, ProxyPid, ProxyPort} = tunnel_SUITE:do_proxy_start(ProxyType), - {ProxyTransport, ProxyProtocol} = tunnel_SUITE:do_type(ProxyType), - {ok, ConnPid} = gun:open("localhost", ProxyPort, #{ - transport => ProxyTransport, - tls_opts => [{verify, verify_none}, {versions, ['tlsv1.2']}], - protocols => [ProxyProtocol] - }), - {ok, ProxyProtocol} = gun:await_up(ConnPid), - tunnel_SUITE:do_handshake_completed(ProxyProtocol, ProxyPid), - StreamRef = gun:connect(ConnPid, #{ - host => "localhost", - port => OriginPort, - transport => tcp, - protocols => [http2] - }), - {response, fin, 200, _} = gun:await(ConnPid, StreamRef), - handshake_completed = receive_from(OriginPid), - {up, http2} = gun:await(ConnPid, StreamRef), - PingRef = gun:ping(ConnPid, #{tunnel => StreamRef}), - {notify, ping_ack, PingRef} = gun:await(ConnPid, undefined), - gun:close(ConnPid). - connect_http_via_h2c(_) -> doc("CONNECT can be used to establish a TCP connection " "to an HTTP/1.1 server via a TCP HTTP/2 proxy. (RFC7540 8.3)"), |