Skip to content

Commit

Permalink
Replace gun:ws_send/2 with gun:ws_send/3
Browse files Browse the repository at this point in the history
Switching from /2 to /3 should be easy enough.

Also update the documentation about HTTP/2 Websocket support.
  • Loading branch information
essen committed Nov 12, 2020
1 parent 1ebad8a commit 4a58077
Show file tree
Hide file tree
Showing 9 changed files with 43 additions and 57 deletions.
6 changes: 5 additions & 1 deletion doc/src/guide/migrating_from_1.3.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ Gun 2.0 requires Erlang/OTP 22.0 or greater.
example.

* It is now possible to send many Websocket frames in
a single `gun:ws_send/2` call.
a single `gun:ws_send/3` call.

* Gun may now send Websocket ping frames automatically
at intervals determined by the `keepalive` option. It
Expand Down Expand Up @@ -186,6 +186,10 @@ Gun 2.0 requires Erlang/OTP 22.0 or greater.
cleaner separation between requests that are followed by
a body and those that don't.

* The function `gun:ws_send/2` has been replaced with the
function `gun:ws_send/3`. The stream reference for the
corresponding Websocket upgrade request must now be given.

=== Messages added

* The `gun_tunnel_up` message has been added.
Expand Down
17 changes: 5 additions & 12 deletions doc/src/guide/protocols.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -65,23 +65,16 @@ cancellation mechanism which allows Gun to inform the
server to stop sending a response for this particular
request, saving resources.

It is not currently possible to upgrade an HTTP/2 connection
to Websocket. Support for this will be added in a future
release.

=== Websocket

Websocket is a binary protocol built on top of HTTP that
allows asynchronous concurrent communication between the
client and the server. A Websocket server can push data to
the client at any time.

Websocket is only available as a connection upgrade over
an HTTP/1.1 connection.

Once the Websocket connection is established, the only
operation available on this connection is sending Websocket
frames using `gun:ws_send/2`.
Once the Websocket connection is established over an HTTP/1.1
connection, the only operation available on this connection
is sending Websocket frames using `gun:ws_send/3`.

Gun will send a `gun_ws` message for every frame received.

Expand All @@ -108,7 +101,7 @@ current protocol.
| await_body | yes | yes | no
| flush | yes | yes | no
| cancel | yes | yes | no
| ws_upgrade | yes | no | no
| ws_upgrade | yes | yes | no
| ws_send | no | no | yes
|===

Expand All @@ -122,6 +115,6 @@ current protocol.
| gun_data | yes | yes | no
| gun_trailers | yes | yes | no
| gun_error | yes | yes | yes
| gun_upgrade | yes | no | no
| gun_upgrade | yes | yes | no
| gun_ws | no | no | yes
|===
8 changes: 4 additions & 4 deletions doc/src/guide/websocket.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ undocumented and must be set to `gun_ws_h`.
.Upgrade to Websocket with protocol negotiation
[source,erlang]
----
gun:ws_upgrade(ConnPid, "/websocket", []
StreamRef = gun:ws_upgrade(ConnPid, "/websocket", []
#{protocols => [{<<"xmpp">>, gun_ws_h}]}).
----

Expand Down Expand Up @@ -88,18 +88,18 @@ Once the Websocket upgrade has completed successfully, you no
longer have access to functions for performing requests. You
can only send and receive Websocket messages.

Use `gun:ws_send/2` to send messages to the server.
Use `gun:ws_send/3` to send messages to the server.

.Send a text frame
[source,erlang]
----
gun:ws_send(ConnPid, {text, "Hello!"}).
gun:ws_send(ConnPid, StreamRef, {text, "Hello!"}).
----

.Send a text frame, a binary frame and then close the connection
[source,erlang]
----
gun:ws_send(ConnPid, [
gun:ws_send(ConnPid, StreamRef, [
{text, "Hello!"},
{binary, BinaryValue},
close
Expand Down
25 changes: 16 additions & 9 deletions doc/src/manual/gun.ws_send.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,14 @@ gun:ws_send - Send Websocket frames

[source,erlang]
----
ws_send(ConnPid, Frames) -> ok
ConnPid :: pid()
Frames :: Frame | [Frame]
Frame :: close | ping | pong
| {text | binary | close | ping | pong, iodata()}
| {close, non_neg_integer(), iodata()}
ws_send(ConnPid, StreamRef, Frames) -> ok
ConnPid :: pid()
StreamRef :: gun:stream_ref()
Frames :: Frame | [Frame]
Frame :: close | ping | pong
| {text | binary | close | ping | pong, iodata()}
| {close, non_neg_integer(), iodata()}
----

Send Websocket frames.
Expand All @@ -28,6 +29,10 @@ ConnPid::

The pid of the Gun connection process.

StreamRef::

Identifier of the stream that was upgraded to Websocket.

Frames::

One or more Websocket frame(s).
Expand All @@ -38,20 +43,22 @@ The atom `ok` is returned.

== Changelog

* *2.0*: The mandatory `StreamRef` argument was added.
* *2.0*: It is now possible to send multiple frames at once.
* *1.0*: Function introduced.

== Examples

.Send a single frame
[source,erlang]
----
gun:ws_send(ConnPid, {text, <<"Hello world!">>}).
gun:ws_send(ConnPid, StreamRef, {text, <<"Hello world!">>}).
----

.Send many frames including a close frame
[source,erlang]
----
gun:ws_send(ConnPid, [
gun:ws_send(ConnPid, StreamRef, [
{text, <<"See you later, world!">>},
close
]).
Expand Down
18 changes: 0 additions & 18 deletions src/gun.erl
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,6 @@
-export([ws_upgrade/2]).
-export([ws_upgrade/3]).
-export([ws_upgrade/4]).
-export([ws_send/2]).
-export([ws_send/3]).

%% Internals.
Expand Down Expand Up @@ -930,12 +929,6 @@ ws_upgrade(ServerPid, Path, Headers, Opts0) ->
gen_statem:cast(ServerPid, {ws_upgrade, ReplyTo, StreamRef, Path, normalize_headers(Headers), Opts}),
StreamRef.

%% @todo ws_send/2 will need to be deprecated in favor of a variant with StreamRef.
%% But it can be kept for the time being since it can still work for HTTP/1.1 (connected_ws_only).
-spec ws_send(pid(), ws_frame() | [ws_frame()]) -> ok.
ws_send(ServerPid, Frames) ->
gen_statem:cast(ServerPid, {ws_send, self(), Frames}).

-spec ws_send(pid(), stream_ref(), ws_frame() | [ws_frame()]) -> ok.
ws_send(ServerPid, StreamRef, Frames) ->
gen_statem:cast(ServerPid, {ws_send, self(), StreamRef, Frames}).
Expand Down Expand Up @@ -1219,11 +1212,6 @@ connected_ws_only(cast, {ws_send, ReplyTo, StreamRef, Frames}, State=#state{
ProtoState, dereference_stream_ref(StreamRef, State),
ReplyTo, EvHandler, EvHandlerState0),
commands(Commands, State#state{event_handler_state=EvHandlerState});
connected_ws_only(cast, {ws_send, ReplyTo, Frames}, State=#state{
protocol=Protocol=gun_ws, protocol_state=ProtoState,
event_handler=EvHandler, event_handler_state=EvHandlerState0}) ->
{Commands, EvHandlerState} = Protocol:ws_send(Frames, ProtoState, ReplyTo, EvHandler, EvHandlerState0),
commands(Commands, State#state{event_handler_state=EvHandlerState});
connected_ws_only(cast, Msg, _)
when element(1, Msg) =:= headers; element(1, Msg) =:= request; element(1, Msg) =:= data;
element(1, Msg) =:= connect; element(1, Msg) =:= ws_upgrade ->
Expand Down Expand Up @@ -1309,12 +1297,6 @@ connected(cast, {ws_send, ReplyTo, StreamRef, Frames}, State=#state{
ProtoState, dereference_stream_ref(StreamRef, State),
ReplyTo, EvHandler, EvHandlerState0),
commands(Commands, State#state{event_handler_state=EvHandlerState});
%% Catch-all for the StreamRef-free variant.
connected(cast, {ws_send, ReplyTo, _}, _) ->
ReplyTo ! {gun_error, self(), {badstate,
"Connection needs to be upgraded to Websocket "
"before the gun:ws_send/1 function can be used."}},
keep_state_and_data;
connected(Type, Event, State) ->
handle_common_connected(Type, Event, ?FUNCTION_NAME, State).

Expand Down
2 changes: 1 addition & 1 deletion src/gun_ws.erl
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@
-export([closing/4]).
-export([close/4]).
-export([keepalive/3]).
-export([ws_send/5]).
-export([ws_send/6]).
-export([down/1]).

Expand Down Expand Up @@ -343,6 +342,7 @@ ws_send([Frame|Tail], State, ReplyTo, EvHandler, EvHandlerState0) ->
Other
end.

%% @todo We should probably check the _StreamRef value.
ws_send(Frames, State, _StreamRef, ReplyTo, EvHandler, EvHandlerState) ->
ws_send(Frames, State, ReplyTo, EvHandler, EvHandlerState).

Expand Down
12 changes: 6 additions & 6 deletions test/flow_SUITE.erl
Original file line number Diff line number Diff line change
Expand Up @@ -183,19 +183,19 @@ flow_ws(_) ->
%% We send 2 frames with some time in between to make sure that
%% Gun handles them in separate Protocol:handle calls.
Frame = {text, <<"Hello!">>},
gun:ws_send(ConnPid, Frame),
gun:ws_send(ConnPid, StreamRef, Frame),
timer:sleep(500),
gun:ws_send(ConnPid, Frame),
gun:ws_send(ConnPid, StreamRef, Frame),
%% We set the flow to 1 therefore we will receive 1 data message,
%% and then nothing because Gun doesn't read from the socket.
{ws, _} = gun:await(ConnPid, StreamRef),
{error, timeout} = gun:await(ConnPid, StreamRef, 3000),
%% We then update the flow, send 2 frames with some time in between
%% and get 2 more data messages but no more.
gun:update_flow(ConnPid, StreamRef, 2),
gun:ws_send(ConnPid, Frame),
gun:ws_send(ConnPid, StreamRef, Frame),
timer:sleep(500),
gun:ws_send(ConnPid, Frame),
gun:ws_send(ConnPid, StreamRef, Frame),
{ws, _} = gun:await(ConnPid, StreamRef),
{ws, _} = gun:await(ConnPid, StreamRef),
{error, timeout} = gun:await(ConnPid, StreamRef, 1000),
Expand Down Expand Up @@ -259,9 +259,9 @@ no_flow_ws(_) ->
{upgrade, [<<"websocket">>], _} = gun:await(ConnPid, StreamRef),
gun:update_flow(ConnPid, StreamRef, 2),
Frame = {text, <<"Hello!">>},
gun:ws_send(ConnPid, Frame),
gun:ws_send(ConnPid, StreamRef, Frame),
timer:sleep(100),
gun:ws_send(ConnPid, Frame),
gun:ws_send(ConnPid, StreamRef, Frame),
{ws, _} = gun:await(ConnPid, StreamRef),
{ws, _} = gun:await(ConnPid, StreamRef),
gun:close(ConnPid)
Expand Down
6 changes: 3 additions & 3 deletions test/shutdown_SUITE.erl
Original file line number Diff line number Diff line change
Expand Up @@ -534,7 +534,7 @@ ws_gun_send_close_frame(Config) ->
%% We send a close frame. We expect the same frame back
%% before the connection is closed.
Frame = {close, 3333, <<>>},
gun:ws_send(ConnPid, Frame),
gun:ws_send(ConnPid, StreamRef, Frame),
{ws, Frame} = gun:await(ConnPid, StreamRef),
gun_is_down(ConnPid, ConnRef, normal).

Expand Down Expand Up @@ -563,7 +563,7 @@ closing_gun_shutdown(Config) ->
%% We send a close frame then immediately call gun:shutdown/1.
%% We expect Gun to go down without retrying to reconnect.
Frame = {close, 3333, <<>>},
gun:ws_send(ConnPid, Frame),
gun:ws_send(ConnPid, StreamRef, Frame),
gun:shutdown(ConnPid),
{ws, Frame} = gun:await(ConnPid, StreamRef),
gun_is_down(ConnPid, ConnRef, shutdown).
Expand All @@ -586,7 +586,7 @@ do_closing_owner_down(Config, ExitReason, DownReason) ->
{ok, http} = gun:await_up(ConnPid),
StreamRef = gun:ws_upgrade(ConnPid, "/ws_frozen", []),
{upgrade, [<<"websocket">>], _} = gun:await(ConnPid, StreamRef),
gun:ws_send(ConnPid, {close, 3333, <<>>}),
gun:ws_send(ConnPid, StreamRef, {close, 3333, <<>>}),
timer:sleep(100),
exit(ExitReason)
end),
Expand Down
6 changes: 3 additions & 3 deletions test/ws_autobahn_SUITE.erl
Original file line number Diff line number Diff line change
Expand Up @@ -115,13 +115,13 @@ run_cases(N, Total) ->
loop(Pid, MRef, StreamRef) ->
receive
{gun_ws, Pid, StreamRef, close} ->
gun:ws_send(Pid, close),
gun:ws_send(Pid, StreamRef, close),
loop(Pid, MRef, StreamRef);
{gun_ws, Pid, StreamRef, {close, Code, _}} ->
gun:ws_send(Pid, {close, Code, <<>>}),
gun:ws_send(Pid, StreamRef, {close, Code, <<>>}),
loop(Pid, MRef, StreamRef);
{gun_ws, Pid, StreamRef, Frame} ->
gun:ws_send(Pid, Frame),
gun:ws_send(Pid, StreamRef, Frame),
loop(Pid, MRef, StreamRef);
{gun_down, Pid, ws, _, _} ->
close(Pid, MRef);
Expand Down

0 comments on commit 4a58077

Please sign in to comment.