aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/gun_http.erl4
-rw-r--r--src/gun_http3.erl6
-rw-r--r--src/gun_ws.erl4
-rw-r--r--test/ping_SUITE.erl118
-rw-r--r--test/rfc7540_SUITE.erl74
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)"),