I followed the Getting Started instructions for Cowboy, and I've got Cowboy running and listening on port 8080, and I got the Hello Erlang!
response when I entered http://localhost:8080
in my browser. Now, how do I use Gun to connect to Cowboy?
I read the Gun docs, and it says to add "Gun as an erlang.mk dependency". So I downloaded erlang.mk:
~/erlang_programs/my_gun$ curl -O https://erlang.mk/erlang.mk
and following the Erlang.mk User Guide, I created an application:
~/erlang_programs/my_gun$ gmake -f erlang.mk bootstrap
Then I added gun as a dependency to the Makefile:
PROJECT = my_gun
PROJECT_DESCRIPTION = New project
PROJECT_VERSION = 0.1.0
DEPS = gun
include erlang.mk
Then I compiled:
~/erlang_programs/my_gun$ gmake
gmake[1]: Entering directory '/Users/7stud/erlang_programs/my_gun/deps/gun'
gmake[2]: Entering directory '/Users/7stud/erlang_programs/my_gun/deps/cowlib'
ERLC cow_cookie.erl cow_date.erl cow_hpack.erl cow_http.erl cow_http2.erl cow_http_hd.erl cow_http_te.erl cow_mimetypes.erl cow_multipart.erl cow_qs.erl cow_spdy.erl cow_sse.erl cow_uri.erl cow_ws.erl
APP cowlib
gmake[2]: Leaving directory '/Users/7stud/erlang_programs/my_gun/deps/cowlib'
gmake[2]: Entering directory '/Users/7stud/erlang_programs/my_gun/deps/ranch'
DEPEND ranch.d
ERLC ranch.erl ranch_acceptor.erl ranch_acceptors_sup.erl ranch_app.erl ranch_conns_sup.erl ranch_listener_sup.erl ranch_protocol.erl ranch_server.erl ranch_ssl.erl ranch_sup.erl ranch_tcp.erl ranch_transport.erl
APP ranch
gmake[2]: Leaving directory '/Users/7stud/erlang_programs/my_gun/deps/ranch'
DEPEND gun.d
ERLC gun.erl gun_app.erl gun_content_handler.erl gun_data.erl gun_http.erl gun_http2.erl gun_spdy.erl gun_sse.erl gun_sup.erl gun_ws.erl gun_ws_handler.erl
APP gun
GEN rebar.config
gmake[1]: Leaving directory '/Users/7stud/erlang_programs/my_gun/deps/gun'
DEPEND my_gun.d
ERLC my_gun_app.erl my_gun_sup.erl
APP my_gun
GEN /Users/7stud/erlang_programs/my_gun/.erlang.mk/relx
===> Starting relx build process ...
===> Resolving OTP Applications from directories:
/Users/7stud/erlang_programs/my_gun/ebin
/Users/7stud/erlang_programs/my_gun/deps
/Users/7stud/.evm/erlang_versions/otp_src_19.2/lib/erlang/lib
/Users/7stud/erlang_programs/my_gun/apps
===> Resolved my_gun_release-1
===> rendering builtin_hook_status hook to "/Users/7stud/erlang_programs/my_gun/_rel/my_gun_release/bin/hooks/builtin/status"
===> Including Erts from /Users/7stud/.evm/erlang_versions/otp_src_19.2/lib/erlang
===> release successfully created!
===> tarball /Users/7stud/erlang_programs/my_gun/_rel/my_gun_release/my_gun_release-1.tar.gz successfully created!
But when I switch to the erlang shell and try to start gun, I get an error:
~/erlang_programs/my_gun$ erl
Erlang/OTP 19 [erts-8.2] [source] [64-bit] [smp:4:4] [async-threads:10] [hipe] [kernel-poll:false]
Eshell V8.2 (abort with ^G)
1> application:ensure_all_started(gun).
{error,{gun,{"no such file or directory","gun.app"}}}
Can someone post a simple example of how to use Gun (or any other http client with websocket support) to connect to Cowboy?
Okay, I made some progress. I deleted the my_gun directory, recreated the directory, redownloaded erlang.mk, and created a release with the following command:
~/erlang_programs/my_gun$ gmake -f erlang.mk bootstrap-lib bootstrap-rel
Then I added the gun dependency to the Makefile (as described above). Then I did:
~/erlang_programs/my_gun$ gmake run
If there are no errors in the code, then an erlang shell will launch. In the erlang shell, I entered the following code (see the tip
below to avoid having to type all the code in the shell):
(my_gun@127.0.0.1)1> application:ensure_all_started(gun).
{ok,[]}
(my_gun@127.0.0.1)2> {ok, ConnPid} = gun:open("localhost", 8080).
=PROGRESS REPORT==== 10-Jul-2017::05:21:58 ===
supervisor: {local,inet_gethost_native_sup}
started: [{pid,<0.366.0>},{mfa,{inet_gethost_native,init,[[]]}}]
{ok,<0.364.0>}
=PROGRESS REPORT==== 10-Jul-2017::05:21:58 ===
supervisor: {local,kernel_safe_sup}
started: [{pid,<0.365.0>},
{id,inet_gethost_native_sup},
{mfargs,{inet_gethost_native,start_link,[]}},
{restart_type,temporary},
{shutdown,1000},
{child_type,worker}]
(my_gun@127.0.0.1)3> {ok, Protocol} = gun:await_up(ConnPid).
{ok,http}
(my_gun@127.0.0.1)4> gun:ws_upgrade(ConnPid, "/websocket").
#Ref<0.0.3.244>
(my_gun@127.0.0.1)5> receive
(my_gun@127.0.0.1)5> {gun_ws_upgrade, ConnPid, ok, Headers} ->
(my_gun@127.0.0.1)5> upgrade_success(ConnPid);
(my_gun@127.0.0.1)5> {gun_response, ConnPid, _, _, Status, Headers} ->
(my_gun@127.0.0.1)5> exit({ws_upgrade_failed, Status, Headers});
(my_gun@127.0.0.1)5> {gun_error, ConnPid, StreamRef, Reason} ->
(my_gun@127.0.0.1)5> exit({ws_upgrade_failed, Reason})
(my_gun@127.0.0.1)5> after 1000 ->
(my_gun@127.0.0.1)5> exit(timeout)
(my_gun@127.0.0.1)5> end.
=CRASH REPORT==== 10-Jul-2017::05:25:17 ===
crasher:
initial call: gun:proc_lib_hack/5
pid: <0.364.0>
registered_name: []
exception exit: {{owner_gone,normal},
[{gun,loop,1,[{file,"src/gun.erl"},{line,706}]},
{gun,proc_lib_hack,5,[{file,"src/gun.erl"},{line,535}]},
{proc_lib,init_p_do_apply,3,
[{file,"proc_lib.erl"},{line,247}]}]}
in function gun:proc_lib_hack/5 (src/gun.erl, line 540)
ancestors: [gun_sup,<0.343.0>]
messages: []
links: [<0.344.0>]
dictionary: []
trap_exit: false
status: running
heap_size: 2586
stack_size: 27
reductions: 10857
neighbours:
=SUPERVISOR REPORT==== 10-Jul-2017::05:25:17 ===
Supervisor: {local,gun_sup}
Context: child_terminated
Reason: {{owner_gone,normal},
[{gun,loop,1,[{file,"src/gun.erl"},{line,706}]},
{gun,proc_lib_hack,5,[{file,"src/gun.erl"},{line,535}]},
{proc_lib,init_p_do_apply,3,
[{file,"proc_lib.erl"},{line,247}]}]}
Offender: [{pid,<0.364.0>},
{id,gun},
{mfargs,{gun,start_link,undefined}},
{restart_type,temporary},
{shutdown,5000},
{child_type,worker}]
** exception exit: {ws_upgrade_failed,404,
[{<<"content-length">>,<<"0">>},
{<<"date">>,
<<"Mon, 10 Jul 2017 11:22:38 GMT">>},
{<<"server">>,<<"Cowboy">>}]}
(my_gun@127.0.0.1)6>
Now, I'm getting a 404 error. Cowboy is running, and when I enter http://localhost:8080 in my browser
, I see a response message. Why is Gun giving me a 404 error?
Next I tried using the instructions in the Gun docs to make a GET request:
StreamRef = gun:get(ConnPid, "/").
case gun:await(ConnPid, StreamRef) of
{response, fin, Status, Headers} ->
no_data;
{response, nofin, Status, Headers} ->
{ok, Body} = gun:await_body(ConnPid, StreamRef),
io:format("~s~n", [Body])
end.
and that was successful:
=PROGRESS REPORT==== 10-Jul-2017::06:36:14 ===
supervisor: {local,inet_gethost_native_sup}
started: [{pid,<0.367.0>},{mfa,{inet_gethost_native,init,[[]]}}]
=PROGRESS REPORT==== 10-Jul-2017::06:36:14 ===
supervisor: {local,kernel_safe_sup}
started: [{pid,<0.366.0>},
{id,inet_gethost_native_sup},
{mfargs,{inet_gethost_native,start_link,[]}},
{restart_type,temporary},
{shutdown,1000},
{child_type,worker}]
Hello Erlang!
ok
(my_gun@127.0.0.1)2>
The response means that I was able to use Gun to interact with a Cowboy server--but I want to use websockets. Any ideas what I am doing wrong?
tip:
To avoid having to type all that code in the gun shell, I created the file ~/erlang_programs/my_gun/src/my.erl
:
-module(my).
-compile(export_all).
get() ->
{ok, _} = application:ensure_all_started(gun),
{ok, ConnPid} = gun:open("localhost", 8080),
{ok, _Protocol} = gun:await_up(ConnPid),
StreamRef = gun:get(ConnPid, "/"),
case gun:await(ConnPid, StreamRef) of
{response, fin, _Status, _Headers} ->
no_data;
{response, nofin, _Status, _Headers} ->
{ok, Body} = gun:await_body(ConnPid, StreamRef),
io:format("~s~n", [Body])
end.
ws() ->
{ok, _} = application:ensure_all_started(gun),
{ok, ConnPid} = gun:open("localhost", 8080),
{ok, _Protocol} = gun:await_up(ConnPid),
gun:ws_upgrade(ConnPid, "/websocket"),
receive
{gun_ws_upgrade, ConnPid, ok, Headers} ->
upgrade_success(ConnPid, Headers);
{gun_response, ConnPid, _, _, Status, Headers} ->
exit({ws_upgrade_failed, Status, Headers});
{gun_error, _ConnPid, _StreamRef, Reason} ->
exit({ws_upgrade_failed, Reason})
%% More clauses here as needed.
after 1000 ->
exit(timeout)
end,
gun:shutdown(ConnPid).
upgrade_success(ConnPid, Headers) ->
io:format("Upgraded ~w. Success!~nHeaders:~n~p~n",
[ConnPid, Headers]).
Then the make (or gmake) command:
~/erlang_programs/my_gun$ gmake run
will compile everything in the src/
directory and alert you to any errors. Once the gun shell successfully launches in response to gmake run
, you can for instance do:
(my_gun@127.0.0.1)1> my:get().
=PROGRESS REPORT==== 10-Jul-2017::06:36:14 ===
supervisor: {local,inet_gethost_native_sup}
started: [{pid,<0.367.0>},{mfa,{inet_gethost_native,init,[[]]}}]
=PROGRESS REPORT==== 10-Jul-2017::06:36:14 ===
supervisor: {local,kernel_safe_sup}
started: [{pid,<0.366.0>},
{id,inet_gethost_native_sup},
{mfargs,{inet_gethost_native,start_link,[]}},
{restart_type,temporary},
{shutdown,1000},
{child_type,worker}]
Hello Erlang!
ok
(my_gun@127.0.0.1)2>
Response to comment:
Since you're getting a 404, I guess you don't have a websocket handler defined in the cowboy routes.
You are right. I only had the handler shown in the Cowboy Getting Started guide. Now, I've added the websocket setup code and a websocket handler to cowboy. I now have the routes:
hello_erlang_app.erl:
-module(hello_erlang_app).
-behaviour(application).
-export([start/2]).
-export([stop/1]).
start(_Type, _Args) ->
Dispatch = cowboy_router:compile([
{'_', [{"/", hello_handler, []}] },
{'_', [{"/websocket", myws_handler, []}] }
]),
{ok, _} = cowboy:start_clear(my_http_listener,
[{port, 8080}],
#{env => #{dispatch => Dispatch} }
),
hello_erlang_sup:start_link().
stop(_State) ->
ok.
Here's my handler:
-module(myws_handler).
-compile(export_all).
init(Req, State) ->
{cowboy_websocket, Req, State}. %Perform websocket setup
websocket_handle({text, Msg}, State) ->
{
reply,
{text, <<"Server received: ", Msg/binary>>, State} %%Error in format here, too!
};
websocket_handle(_Data, State) ->
{ok, State}.
But I'm still getting a 404 error when I execute my:ws()
in the gun shell:
-module(my).
-compile(export_all).
get() ->
{ok, _} = application:ensure_all_started(gun),
{ok, ConnPid} = gun:open("localhost", 8080),
{ok, _Protocol} = gun:await_up(ConnPid),
StreamRef = gun:get(ConnPid, "/"),
case gun:await(ConnPid, StreamRef) of
{response, fin, _Status, _Headers} ->
no_data;
{response, nofin, _Status, _Headers} ->
{ok, Body} = gun:await_body(ConnPid, StreamRef),
io:format("~s~n", [Body])
end.
ws() ->
{ok, _} = application:ensure_all_started(gun),
{ok, ConnPid} = gun:open("localhost", 8080),
{ok, _Protocol} = gun:await_up(ConnPid),
gun:ws_upgrade(ConnPid, "/websocket"),
receive
{gun_ws_upgrade, ConnPid, ok, Headers} ->
upgrade_success(ConnPid, Headers);
{gun_response, ConnPid, _, _, Status, Headers} ->
exit({ws_upgrade_failed, Status, Headers});
{gun_error, _ConnPid, _StreamRef, Reason} ->
exit({ws_upgrade_failed, Reason})
%% More clauses here as needed.
after 1000 ->
exit(timeout)
end,
gun:shutdown(ConnPid).
upgrade_success(ConnPid, Headers) ->
io:format("Upgraded ~w. Success!~nHeaders:~n~w~n",
[ConnPid, Headers]).
Here's the output:
~/erlang_programs/my_gun$ gmake run
gmake[1]: Entering directory '/Users/7stud/erlang_programs/my_gun/deps/gun'
gmake[2]: Entering directory '/Users/7stud/erlang_programs/my_gun/deps/cowlib'
gmake[2]: Leaving directory '/Users/7stud/erlang_programs/my_gun/deps/cowlib'
gmake[2]: Entering directory '/Users/7stud/erlang_programs/my_gun/deps/ranch'
gmake[2]: Leaving directory '/Users/7stud/erlang_programs/my_gun/deps/ranch'
GEN rebar.config
gmake[1]: Leaving directory '/Users/7stud/erlang_programs/my_gun/deps/gun'
DEPEND my_gun.d
ERLC my.erl
APP my_gun
===> Starting relx build process ...
===> Resolving OTP Applications from directories:
/Users/7stud/erlang_programs/my_gun/ebin
/Users/7stud/erlang_programs/my_gun/deps
/Users/7stud/.evm/erlang_versions/otp_src_19.2/lib/erlang/lib
/Users/7stud/erlang_programs/my_gun/apps
/Users/7stud/erlang_programs/my_gun/_rel
===> Resolved my_gun_release-1
===> rendering builtin_hook_status hook to "/Users/7stud/erlang_programs/my_gun/_rel/my_gun_release/bin/hooks/builtin/status"
===> Including Erts from /Users/7stud/.evm/erlang_versions/otp_src_19.2/lib/erlang
===> release successfully created!
===> tarball /Users/7stud/erlang_programs/my_gun/_rel/my_gun_release/my_gun_release-1.tar.gz successfully created!
Exec: /Users/7stud/erlang_programs/my_gun/_rel/my_gun_release/erts-8.2/bin/erlexec -boot /Users/7stud/erlang_programs/my_gun/_rel/my_gun_release/releases/1/my_gun_release -mode embedded -boot_var ERTS_LIB_DIR /Users/7stud/erlang_programs/my_gun/_rel/my_gun_release/lib -config /Users/7stud/erlang_programs/my_gun/_rel/my_gun_release/releases/1/sys.config -args_file /Users/7stud/erlang_programs/my_gun/_rel/my_gun_release/releases/1/vm.args -pa -- console
Root: /Users/7stud/erlang_programs/my_gun/_rel/my_gun_release
/Users/7stud/erlang_programs/my_gun/_rel/my_gun_release
heart_beat_kill_pid = 32843
Erlang/OTP 19 [erts-8.2] [source] [64-bit] [smp:4:4] [async-threads:10] [hipe] [kernel-poll:false]
=PROGRESS REPORT==== 10-Jul-2017::16:26:05 ===
supervisor: {local,sasl_safe_sup}
started: [{pid,<0.353.0>},
{id,alarm_handler},
{mfargs,{alarm_handler,start_link,[]}},
{restart_type,permanent},
{shutdown,2000},
{child_type,worker}]
=PROGRESS REPORT==== 10-Jul-2017::16:26:05 ===
supervisor: {local,sasl_sup}
started: [{pid,<0.352.0>},
{id,sasl_safe_sup},
{mfargs,
{supervisor,start_link,
[{local,sasl_safe_sup},sasl,safe]}},
{restart_type,permanent},
{shutdown,infinity},
{child_type,supervisor}]
=PROGRESS REPORT==== 10-Jul-2017::16:26:05 ===
supervisor: {local,sasl_sup}
started: [{pid,<0.354.0>},
{id,release_handler},
{mfargs,{release_handler,start_link,[]}},
{restart_type,permanent},
{shutdown,2000},
{child_type,worker}]
=PROGRESS REPORT==== 10-Jul-2017::16:26:05 ===
application: sasl
started_at: 'my_gun@127.0.0.1'
=PROGRESS REPORT==== 10-Jul-2017::16:26:05 ===
supervisor: {local,runtime_tools_sup}
started: [{pid,<0.360.0>},
{id,ttb_autostart},
{mfargs,{ttb_autostart,start_link,[]}},
{restart_type,temporary},
{shutdown,3000},
{child_type,worker}]
=PROGRESS REPORT==== 10-Jul-2017::16:26:05 ===
application: runtime_tools
started_at: 'my_gun@127.0.0.1'
Eshell V8.2 (abort with ^G)
(my_gun@127.0.0.1)1> my:ws().
=PROGRESS REPORT==== 10-Jul-2017::16:26:08 ===
supervisor: {local,inet_gethost_native_sup}
started: [{pid,<0.367.0>},{mfa,{inet_gethost_native,init,[[]]}}]
=PROGRESS REPORT==== 10-Jul-2017::16:26:08 ===
supervisor: {local,kernel_safe_sup}
started: [{pid,<0.366.0>},
{id,inet_gethost_native_sup},
{mfargs,{inet_gethost_native,start_link,[]}},
{restart_type,temporary},
{shutdown,1000},
{child_type,worker}]
=CRASH REPORT==== 10-Jul-2017::16:26:08 ===
crasher:
initial call: gun:proc_lib_hack/5
pid: <0.365.0>
registered_name: []
exception exit: {{owner_gone,normal},
[{gun,loop,1,[{file,"src/gun.erl"},{line,706}]},
{gun,proc_lib_hack,5,[{file,"src/gun.erl"},{line,535}]},
{proc_lib,init_p_do_apply,3,
[{file,"proc_lib.erl"},{line,247}]}]}
in function gun:proc_lib_hack/5 (src/gun.erl, line 540)
ancestors: [gun_sup,<0.345.0>]
messages: []
links: [<0.346.0>]
dictionary: []
trap_exit: false
status: running
heap_size: 610
stack_size: 27
reductions: 1042
neighbours:
=SUPERVISOR REPORT==== 10-Jul-2017::16:26:08 ===
Supervisor: {local,gun_sup}
Context: child_terminated
Reason: {{owner_gone,normal},
[{gun,loop,1,[{file,"src/gun.erl"},{line,706}]},
{gun,proc_lib_hack,5,[{file,"src/gun.erl"},{line,535}]},
{proc_lib,init_p_do_apply,3,
[{file,"proc_lib.erl"},{line,247}]}]}
Offender: [{pid,<0.365.0>},
{id,gun},
{mfargs,{gun,start_link,undefined}},
{restart_type,temporary},
{shutdown,5000},
{child_type,worker}]
** exception exit: {ws_upgrade_failed,404,
[{<<"content-length">>,<<"0">>},
{<<"date">>,
<<"Mon, 10 Jul 2017 22:26:08 GMT">>},
{<<"server">>,<<"Cowboy">>}]}
in function my:ws/0 (src/my.erl, line 30)
(my_gun@127.0.0.1)2>
I saved all my files and restarted cowboy and gun, so the changes I made to the code are being executed, but I still get the 404 error.
I compared the format of my routes to the routes in the example that spawn_think linked to in the comments, and my format was wrong. Here is what I have now:
-module(hello_erlang_app).
-behaviour(application).
-export([start/2]).
-export([stop/1]).
start(_Type, _Args) ->
Dispatch = cowboy_router:compile([
{'_', [
{"/", hello_handler, []},
{"/websocket", myws_handler, []}
]}
]),
{ok, _} = cowboy:start_clear(my_http_listener,
[{port, 8080}],
#{env => #{dispatch => Dispatch} }
),
hello_erlang_sup:start_link().
stop(_State) ->
ok.
And after adjusting one of the control sequences in the io:format() statement in my gun client:
-module(my).
-compile(export_all).
get() ->
...
ws() ->
...
upgrade_success(ConnPid, Headers) ->
io:format("Upgraded ~w. Success!~nHeaders:~n~p~n", %% <*** CHANGED ~w to ~p
[ConnPid, Headers]).
here is the output:
~/erlang_programs/my_gun$ gmake run
gmake[1]: Entering directory '/Users/7stud/erlang_programs/my_gun/deps/gun'
gmake[2]: Entering directory '/Users/7stud/erlang_programs/my_gun/deps/cowlib'
gmake[2]: Leaving directory '/Users/7stud/erlang_programs/my_gun/deps/cowlib'
gmake[2]: Entering directory '/Users/7stud/erlang_programs/my_gun/deps/ranch'
gmake[2]: Leaving directory '/Users/7stud/erlang_programs/my_gun/deps/ranch'
GEN rebar.config
gmake[1]: Leaving directory '/Users/7stud/erlang_programs/my_gun/deps/gun'
DEPEND my_gun.d
ERLC my.erl
APP my_gun
===> Starting relx build process ...
===> Resolving OTP Applications from directories:
/Users/7stud/erlang_programs/my_gun/ebin
/Users/7stud/erlang_programs/my_gun/deps
/Users/7stud/.evm/erlang_versions/otp_src_19.2/lib/erlang/lib
/Users/7stud/erlang_programs/my_gun/apps
/Users/7stud/erlang_programs/my_gun/_rel
===> Resolved my_gun_release-1
===> rendering builtin_hook_status hook to "/Users/7stud/erlang_programs/my_gun/_rel/my_gun_release/bin/hooks/builtin/status"
===> Including Erts from /Users/7stud/.evm/erlang_versions/otp_src_19.2/lib/erlang
===> release successfully created!
===> tarball /Users/7stud/erlang_programs/my_gun/_rel/my_gun_release/my_gun_release-1.tar.gz successfully created!
Exec: /Users/7stud/erlang_programs/my_gun/_rel/my_gun_release/erts-8.2/bin/erlexec -boot /Users/7stud/erlang_programs/my_gun/_rel/my_gun_release/releases/1/my_gun_release -mode embedded -boot_var ERTS_LIB_DIR /Users/7stud/erlang_programs/my_gun/_rel/my_gun_release/lib -config /Users/7stud/erlang_programs/my_gun/_rel/my_gun_release/releases/1/sys.config -args_file /Users/7stud/erlang_programs/my_gun/_rel/my_gun_release/releases/1/vm.args -pa -- console
Root: /Users/7stud/erlang_programs/my_gun/_rel/my_gun_release
/Users/7stud/erlang_programs/my_gun/_rel/my_gun_release
heart_beat_kill_pid = 34141
Erlang/OTP 19 [erts-8.2] [source] [64-bit] [smp:4:4] [async-threads:10] [hipe] [kernel-poll:false]
=PROGRESS REPORT==== 10-Jul-2017::16:50:53 ===
supervisor: {local,sasl_safe_sup}
started: [{pid,<0.353.0>},
{id,alarm_handler},
{mfargs,{alarm_handler,start_link,[]}},
{restart_type,permanent},
{shutdown,2000},
{child_type,worker}]
=PROGRESS REPORT==== 10-Jul-2017::16:50:53 ===
supervisor: {local,sasl_sup}
started: [{pid,<0.352.0>},
{id,sasl_safe_sup},
{mfargs,
{supervisor,start_link,
[{local,sasl_safe_sup},sasl,safe]}},
{restart_type,permanent},
{shutdown,infinity},
{child_type,supervisor}]
=PROGRESS REPORT==== 10-Jul-2017::16:50:53 ===
supervisor: {local,sasl_sup}
started: [{pid,<0.354.0>},
{id,release_handler},
{mfargs,{release_handler,start_link,[]}},
{restart_type,permanent},
{shutdown,2000},
{child_type,worker}]
=PROGRESS REPORT==== 10-Jul-2017::16:50:53 ===
application: sasl
started_at: 'my_gun@127.0.0.1'
=PROGRESS REPORT==== 10-Jul-2017::16:50:53 ===
supervisor: {local,runtime_tools_sup}
started: [{pid,<0.360.0>},
{id,ttb_autostart},
{mfargs,{ttb_autostart,start_link,[]}},
{restart_type,temporary},
{shutdown,3000},
{child_type,worker}]
=PROGRESS REPORT==== 10-Jul-2017::16:50:53 ===
application: runtime_tools
started_at: 'my_gun@127.0.0.1'
Eshell V8.2 (abort with ^G)
(my_gun@127.0.0.1)1> my:ws().
=PROGRESS REPORT==== 10-Jul-2017::16:50:57 ===
supervisor: {local,inet_gethost_native_sup}
started: [{pid,<0.367.0>},{mfa,{inet_gethost_native,init,[[]]}}]
=PROGRESS REPORT==== 10-Jul-2017::16:50:57 ===
supervisor: {local,kernel_safe_sup}
started: [{pid,<0.366.0>},
{id,inet_gethost_native_sup},
{mfargs,{inet_gethost_native,start_link,[]}},
{restart_type,temporary},
{shutdown,1000},
{child_type,worker}]
Upgraded <0.365.0>. Success!
Headers:
[{<<"connection">>,<<"Upgrade">>},
{<<"date">>,<<"Mon, 10 Jul 2017 22:50:56 GMT">>},
{<<"sec-websocket-accept">>,<<"S1w7rWXToZefi/NOEcDAEDb4yEU=">>},
{<<"server">>,<<"Cowboy">>},
{<<"upgrade">>,<<"websocket">>}]
ok
(my_gun@127.0.0.1)2>
I've reached the forum's limit on text, so see my answer for how I was actually able to send and receive data using a websocket.
A summary of the issues/suggestions from the comments discussion
For running your Erlang app (in this case including gun as a dependency), you need to have the path to beam and .app files discoverable to the shell. When using erlang.mk, the easiest way to do that would be running the release using
make run
On Cowboy side, to have Websocket handled correctly, you need to have a handler defined in your cowboy routes. See example in Cowboy Websocket example
See also Gun user guide for more details about handling Websocket in Gun
Success!
~/erlang_programs/my_gun/src/my.erl:
-module(my).
-compile(export_all).
get() ->
...
ws() ->
{ok, _} = application:ensure_all_started(gun),
{ok, ConnPid} = gun:open("localhost", 8080),
{ok, _Protocol} = gun:await_up(ConnPid),
gun:ws_upgrade(ConnPid, "/websocket"),
receive
{gun_ws_upgrade, ConnPid, ok, Headers} ->
upgrade_success(ConnPid, Headers);
{gun_response, ConnPid, _, _, Status, Headers} ->
exit({ws_upgrade_failed, Status, Headers});
{gun_error, _ConnPid, _StreamRef, Reason} ->
exit({ws_upgrade_failed, Reason})
%% More clauses here as needed.
after 1000 ->
exit(timeout)
end,
upgrade_success(ConnPid, Headers) ->
io:format("Upgraded ~w. Success!~nHeaders:~n~p~n",
[ConnPid, Headers]),
gun:ws_send(ConnPid, {text, "It's raining!"}),
receive
{gun_ws, ConnPid, {text, Msg} } ->
io:format("~s~n", [Msg])
end.
On the cowboy side:
-module(myws_handler).
-compile(export_all).
init(Req, State) ->
{cowboy_websocket, Req, State}. %Perform websocket setup
websocket_handle({text, Msg}, State) ->
{
reply,
{text, io_lib:format("Server received: ~s", [Msg]) },
State
};
websocket_handle(_Other, State) -> %Ignore
{ok, State}.
Here's the output:
~/erlang_programs/my_gun$ gmake run
gmake[1]: Entering directory '/Users/7stud/erlang_programs/my_gun/deps/gun'
gmake[2]: Entering directory '/Users/7stud/erlang_programs/my_gun/deps/cowlib'
gmake[2]: Leaving directory '/Users/7stud/erlang_programs/my_gun/deps/cowlib'
gmake[2]: Entering directory '/Users/7stud/erlang_programs/my_gun/deps/ranch'
gmake[2]: Leaving directory '/Users/7stud/erlang_programs/my_gun/deps/ranch'
GEN rebar.config
gmake[1]: Leaving directory '/Users/7stud/erlang_programs/my_gun/deps/gun'
DEPEND my_gun.d
ERLC my.erl
APP my_gun
===> Starting relx build process ...
===> Resolving OTP Applications from directories:
/Users/7stud/erlang_programs/my_gun/ebin
/Users/7stud/erlang_programs/my_gun/deps
/Users/7stud/.evm/erlang_versions/otp_src_19.2/lib/erlang/lib
/Users/7stud/erlang_programs/my_gun/apps
/Users/7stud/erlang_programs/my_gun/_rel
===> Resolved my_gun_release-1
===> rendering builtin_hook_status hook to "/Users/7stud/erlang_programs/my_gun/_rel/my_gun_release/bin/hooks/builtin/status"
===> Including Erts from /Users/7stud/.evm/erlang_versions/otp_src_19.2/lib/erlang
===> release successfully created!
===> tarball /Users/7stud/erlang_programs/my_gun/_rel/my_gun_release/my_gun_release-1.tar.gz successfully created!
Exec: /Users/7stud/erlang_programs/my_gun/_rel/my_gun_release/erts-8.2/bin/erlexec -boot /Users/7stud/erlang_programs/my_gun/_rel/my_gun_release/releases/1/my_gun_release -mode embedded -boot_var ERTS_LIB_DIR /Users/7stud/erlang_programs/my_gun/_rel/my_gun_release/lib -config /Users/7stud/erlang_programs/my_gun/_rel/my_gun_release/releases/1/sys.config -args_file /Users/7stud/erlang_programs/my_gun/_rel/my_gun_release/releases/1/vm.args -pa -- console
Root: /Users/7stud/erlang_programs/my_gun/_rel/my_gun_release
/Users/7stud/erlang_programs/my_gun/_rel/my_gun_release
heart_beat_kill_pid = 38883
Erlang/OTP 19 [erts-8.2] [source] [64-bit] [smp:4:4] [async-threads:10] [hipe] [kernel-poll:false]
=PROGRESS REPORT==== 10-Jul-2017::18:04:38 ===
supervisor: {local,sasl_safe_sup}
started: [{pid,<0.353.0>},
{id,alarm_handler},
{mfargs,{alarm_handler,start_link,[]}},
{restart_type,permanent},
{shutdown,2000},
{child_type,worker}]
=PROGRESS REPORT==== 10-Jul-2017::18:04:38 ===
supervisor: {local,sasl_sup}
started: [{pid,<0.352.0>},
{id,sasl_safe_sup},
{mfargs,
{supervisor,start_link,
[{local,sasl_safe_sup},sasl,safe]}},
{restart_type,permanent},
{shutdown,infinity},
{child_type,supervisor}]
=PROGRESS REPORT==== 10-Jul-2017::18:04:38 ===
supervisor: {local,sasl_sup}
started: [{pid,<0.354.0>},
{id,release_handler},
{mfargs,{release_handler,start_link,[]}},
{restart_type,permanent},
{shutdown,2000},
{child_type,worker}]
=PROGRESS REPORT==== 10-Jul-2017::18:04:38 ===
application: sasl
started_at: 'my_gun@127.0.0.1'
=PROGRESS REPORT==== 10-Jul-2017::18:04:38 ===
supervisor: {local,runtime_tools_sup}
started: [{pid,<0.360.0>},
{id,ttb_autostart},
{mfargs,{ttb_autostart,start_link,[]}},
{restart_type,temporary},
{shutdown,3000},
{child_type,worker}]
=PROGRESS REPORT==== 10-Jul-2017::18:04:38 ===
application: runtime_tools
started_at: 'my_gun@127.0.0.1'
Eshell V8.2 (abort with ^G)
(my_gun@127.0.0.1)1> my:ws().
=PROGRESS REPORT==== 10-Jul-2017::18:04:41 ===
supervisor: {local,inet_gethost_native_sup}
started: [{pid,<0.367.0>},{mfa,{inet_gethost_native,init,[[]]}}]
=PROGRESS REPORT==== 10-Jul-2017::18:04:41 ===
supervisor: {local,kernel_safe_sup}
started: [{pid,<0.366.0>},
{id,inet_gethost_native_sup},
{mfargs,{inet_gethost_native,start_link,[]}},
{restart_type,temporary},
{shutdown,1000},
{child_type,worker}]
Upgraded <0.365.0>. Success!
Headers:
[{<<"connection">>,<<"Upgrade">>},
{<<"date">>,<<"Tue, 11 Jul 2017 00:04:40 GMT">>},
{<<"sec-websocket-accept">>,<<"pYv8QeeJfzQgaS/x8flZHyrIexk=">>},
{<<"server">>,<<"Cowboy">>},
{<<"upgrade">>,<<"websocket">>}]
Server received: It's raining!
ok
(my_gun@127.0.0.1)2>
来源:https://stackoverflow.com/questions/45005984/how-do-you-use-gun-as-a-cowboy-client