inets httpd: server example in User's Guide not working - erlang
I copied the code straight from the inets User's Guide:
$ 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> inets:start().
ok
2> {ok, Pid} = inets:start(httpd, [{port, 0}, {server_name,"httpd_test"}, {server_root,"/tmp"}, {document_root,"/tmp/htdocs"}, {bind_address, "localhost"}]).
=ERROR REPORT==== 25-Feb-2018::03:08:14 ===
Failed initiating web server:
undefined
{invalid_option,{non_existing,{document_root,"/tmp/htdocs"}}}
** exception error: no match of right hand side value
{error,
{{shutdown,
{failed_to_start_child,
{httpd_manager,{127,0,0,1},60152,default},
{error,
{invalid_option,
{non_existing,{document_root,"/tmp/htdocs"}}}}}},
{child,undefined,
{httpd_instance_sup,{127,0,0,1},60152,default},
{httpd_instance_sup,start_link,
[[{port,60152},
{bind_address,{127,0,0,1}},
{server_name,"httpd_test"},
{server_root,"/tmp"},
{document_root,"/tmp/htdocs"}],
15000,
{<0.73.0>,#Port<0.904>},
[]]},
permanent,infinity,supervisor,
[httpd_instance_sup]}}}
3>
document_root is an invalid option? Okay, I'll check the list of valid options and correct the mistake in the example....hmmmm, there doesn't seem to be one.
Okay, I needed to do this:
$ cd /tmp
$ mkdir htdocs
Now, I'm trying to bind to an ipv6 version of localhost, but I'm having no luck. The httpd docs say:
{bind_address, ip_address() | hostname() | any}
and ip_address() is defined as:
ip_address() = {N1,N2,N3,N4} % IPv4 | {K1,K2,K3,K4,K5,K6,K7,K8} % IPv6
but N and K are not defined. If N is an integer, what is a K? I tried:
{bind_address, {0,0,0,0,0,0,0,1}}
but I got an error:
$ 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> inets:start().
ok
2> inets:start(httpd, [{port, 0}, {server_name, "httpd_test"}, {server_root, "."}, {document_root, "./htdocs"}, {bind_address,{0,0,0,0,0,0,0,1}}]).
{error,{listen,{exit,badarg}}}
Yet, with an ipv4 address everything works as expected:
3> inets:start(httpd, [{port, 0}, {server_name, "httpd_test"}, {server_root, "."}, {document_root, "./htdocs"}, {bind_address,{127,0,0,1}}]).
{ok,<0.74.0>}
4> httpd:info(pid(0,74,0)).
[{mime_types,[{"htm","text/html"},{"html","text/html"}]},
{server_name,"httpd_test"},
{bind_address,{127,0,0,1}},
{server_root,"."},
{port,63069},
{document_root,"./htdocs"}]
5> httpc:request("http://localhost:63069/file1.txt").
{ok,{{"HTTP/1.1",200,"OK"},
[{"date","Mon, 26 Feb 2018 03:02:33 GMT"},
{"etag","nCZT0114"},
{"server","inets/6.3.4"},
{"content-length","14"},
{"content-type","text/plain"},
{"last-modified","Mon, 26 Feb 2018 02:51:52 GMT"}],
"Hello, world!\n"}}
/ets/hosts:
##
# Host Database
#
# localhost is used to configure the loopback interface
# when the system is booting. Do not change this entry.
##
127.0.0.1 localhost
255.255.255.255 broadcasthost
::1 localhost
Next, I tried {bind_address, any}:
$ 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> inets:start().
ok
2> {ok, Server} = inets:start(httpd, [{port, 0}, {server_name, "httpd_test"}, {server_root, "."}, {document_root, "./htdocs"}, {bind_address, any}]).
{ok,<0.72.0>}
3> httpd:info(Server).
[{mime_types,[{"htm","text/html"},{"html","text/html"}]},
{server_name,"httpd_test"},
{bind_address,any},
{server_root,"."},
{port,63679},
{document_root,"./htdocs"}]
But, I can't perform a get request with an ipv6 address:
4> httpc:request("http://[::1]:63679/file1.txt").
{error,{failed_connect,[{to_address,{"::1",63679}},
{inet,[inet],nxdomain}]}}
5> httpc:request("http://127.0.0.1:63679/file1.txt").
{ok,{{"HTTP/1.1",200,"OK"},
[{"date","Mon, 26 Feb 2018 03:13:35 GMT"},
{"etag","nCZT0114"},
{"server","inets/6.3.4"},
{"content-length","14"},
{"content-type","text/plain"},
{"last-modified","Mon, 26 Feb 2018 02:51:52 GMT"}],
"Hello, world!\n"}}
Okay, I solved the error I got when I tried to bind the server to an ipv6 address: I needed to specify the option {ipfamily, inet6}:
inets:start(httpd, [{port, 0},
{server_name, "httpd_test"},
{server_root, "."},
{document_root, "./htdocs"},
{ipfamily, inet6},
{bind_address,{0,0,0,0,0,0,0,1}}]).
However, my httpc:request() still fails:
4> httpd:info(Server).
[{mime_types,[{"htm","text/html"},{"html","text/html"}]},
{ipfamily,inet6},
{server_name,"httpd_test"},
{bind_address,{0,0,0,0,0,0,0,1}},
{server_root,"."},
{port,51284},
{document_root,"./htdocs"}]
5> httpc:request("http://[::1]:51284/file1.txt").
{error,{failed_connect,[{to_address,{"::1",52489}},
{inet,[inet],nxdomain}]}
I can use curl to make a get request with an ipv6 address:
~$ curl -v "http://[::1]:52489/file1.txt"
* Trying ::1...
* TCP_NODELAY set
* Connected to ::1 (::1) port 52489 (#0)
> GET /file1.txt HTTP/1.1
> Host: [::1]:52489
> User-Agent: curl/7.58.0
> Accept: */*
>
< HTTP/1.1 200 OK
< Date: Mon, 26 Feb 2018 05:07:07 GMT
< Server: inets/6.3.4
< Content-Type: text/plain
< Etag: nCZT0114
< Content-Length: 14
< Last-Modified: Mon, 26 Feb 2018 02:51:52 GMT
<
Hello, world!
* Connection #0 to host ::1 left intact
That leads me to believe that httpc:request() is having trouble with the ipv6 address.
Okay, I tried to configure the client for ipv6:
1> inets:start().
ok
2> {ok, Client} = inets:start(httpc, [{profile, client1_config}] ).
{ok,<0.72.0>}
3> Client.
<0.72.0>
7> httpc:set_options([{ipfamily, inet6}], client1_config).
ok
Fingers crossed....
8> httpc:request("http://[::1]:52489/file1.txt", client1_config).
{error,
{failed_connect,
[{to_address,{"::1",52489}},
{inet6,[inet6],nxdomain}]}}
Then I tried (spacing added by me for legibility):
9> httpc:request(
get,
"http://[::1]:52489/file1.txt",
[],
[{ipv6_host_with_brackets, true}],
client1_config
).
** exception error: no function clause matching httpc:request(get,"http://[::1]:52489/file1.txt",[],
[{ipv6_host_with_brackets,true}],
client1_config) (httpc.erl, line 149)
The error makes no sense to me. There is a five arg version of httpc:request(), and I've carefully checked the types of all the args, and my types are correct:
httpc:request(atom, string, list_of_tuples, list_of_tuples, atom)
Okay, the second argument is actually a tuple: {string, []}. Here's where I'm at now with the server:
7> httpd:info(Server).
[{mime_types,[{"htm","text/html"},{"html","text/html"}]},
{ipfamily,inet6},
{server_name,"httpd_test"},
{bind_address,{0,0,0,0,0,0,0,1}},
{server_root,"."},
{port,53686},
{document_root,"./htdocs"}]
And the client:
32> httpc:get_options(all, client1_config).
{ok,[{proxy,{undefined,[]}},
{https_proxy,{undefined,[]}},
{pipeline_timeout,0},
{max_pipeline_length,2},
{max_keep_alive_length,5},
{keep_alive_timeout,120000},
{max_sessions,2},
{cookies,disabled},
{verbose,verbose},
{ipfamily,inet6},
{ip,default},
{port,default},
{socket_opts,[]}]}
But my client still can't connect with an ipv6 address. I don't know if I'm supposed to be using the httpc:request() Option {ipv6_host_with_brackets, true} or not, so I've been trying it both ways:
34> httpc:request(get, {"http://[::1]:52489/file1.txt", []}, [], [{ipv6_host_with_brackets, true}], client1_config).
(<0.124.0>) << {dbg,{ok,[{matched,nonode#nohost,1}]}}
(<0.124.0>) << {#Ref<0.0.3.431>,
{ok,<<16,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0>>}}
(<0.124.0>) << {inet_async,#Port<0.981>,5,{error,econnrefused}}
(<0.124.0>) << {'EXIT',#Port<0.981>,normal}
(<0.124.0>) << {init_error,error_connecting,
{#Ref<0.0.3.426>,
{error,
{failed_connect,
[{to_address,{"::1",52489}},
{inet6,[inet6],econnrefused}]}}}}
{error,{failed_connect,[{to_address,{"::1",52489}},
{inet6,[inet6],econnrefused}]}}
35> httpc:request(get, {"http://[::1]:52489/file1.txt", []}, [], [], client1_config).
(<0.126.0>) << {dbg,{ok,[{matched,nonode#nohost,1}]}}
(<0.126.0>) << {#Ref<0.0.3.447>,
{ok,<<16,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0>>}}
(<0.126.0>) << {inet_async,#Port<0.982>,6,{error,econnrefused}}
(<0.126.0>) << {'EXIT',#Port<0.982>,normal}
(<0.126.0>) << {init_error,error_connecting,
{#Ref<0.0.3.442>,
{error,
{failed_connect,
[{to_address,{"::1",52489}},
{inet6,[inet6],econnrefused}]}}}}
{error,{failed_connect,[{to_address,{"::1",52489}},
{inet6,[inet6],econnrefused}]}}
36> httpc:request(get, {"http://[0:0:0:0:0:0:0:1]:52489/file1.txt", []}, [], [{ipv6_host_with_brackets, true}], client1_config).
(<0.128.0>) << {dbg,{ok,[{matched,nonode#nohost,1}]}}
(<0.128.0>) << {#Ref<0.0.3.463>,
{ok,<<16,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0>>}}
(<0.128.0>) << {inet_async,#Port<0.983>,7,{error,econnrefused}}
(<0.128.0>) << {'EXIT',#Port<0.983>,normal}
(<0.128.0>) << {init_error,error_connecting,
{#Ref<0.0.3.458>,
{error,
{failed_connect,
[{to_address,{"0:0:0:0:0:0:0:1",52489}},
{inet6,[inet6],econnrefused}]}}}}
{error,{failed_connect,[{to_address,{"0:0:0:0:0:0:0:1",
52489}},
{inet6,[inet6],econnrefused}]}}
37> httpc:request(get, {"http://[0:0:0:0:0:0:0:1]:52489/file1.txt", []}, [], [], client1_config).
(<0.130.0>) << {dbg,{ok,[{matched,nonode#nohost,1}]}}
(<0.130.0>) << {#Ref<0.0.3.479>,
{ok,<<16,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0>>}}
(<0.130.0>) << {inet_async,#Port<0.984>,8,{error,econnrefused}}
(<0.130.0>) << {'EXIT',#Port<0.984>,normal}
(<0.130.0>) << {init_error,error_connecting,
{#Ref<0.0.3.474>,
{error,
{failed_connect,
[{to_address,{"0:0:0:0:0:0:0:1",52489}},
{inet6,[inet6],econnrefused}]}}}}
{error,{failed_connect,[{to_address,{"0:0:0:0:0:0:0:1",
52489}},
{inet6,[inet6],econnrefused}]}}
Okay! I got a client to successfully make a request with an ipv6 address. For all the requests at the bottom of my question, I specified the wrong port. Once I got the client port to match the server port, then all those requests succeeded. Here's my setup:
Server config:
7> httpd:info(Server).
[{mime_types,[{"htm","text/html"},{"html","text/html"}]},
{ipfamily,inet6},
{server_name,"httpd_test"},
{bind_address,{0,0,0,0,0,0,0,1}},
{server_root,"."},
{port,53686},
{document_root,"./htdocs"}]
8>
Client config:
52> httpc:get_options(all, client1_config).
{ok,[{proxy,{undefined,[]}},
{https_proxy,{undefined,[]}},
{pipeline_timeout,0},
{max_pipeline_length,2},
{max_keep_alive_length,5},
{keep_alive_timeout,120000},
{max_sessions,2},
{cookies,disabled},
{verbose,false},
{ipfamily,inet6},
{ip,default},
{port,default},
{socket_opts,[]}]}
53>
And here's the shortest syntax for a request:
53> httpc:request("http://[::1]:53686/file1.txt", client1_config).
{ok,{{"HTTP/1.1",200,"OK"},
[{"date","Mon, 26 Feb 2018 10:21:39 GMT"},
{"etag","nCZT0114"},
{"server","inets/6.3.4"},
{"content-length","14"},
{"content-type","text/plain"},
{"last-modified","Mon, 26 Feb 2018 02:51:52 GMT"}],
"Hello, world!\n"}}
So the httpc:request() Option {ipv6_host_with_brackets, true} isn't required.
When I read the httpc docs, the profile part didn't really register with me. But a profile consists of the configuration options for the client, and it's also where cookies are stored, so you need to include the profile with subsequent requests.
There's a default profile, which I guess automatically gets sent with every request when you don't specify your own profile, and I think the default profile will handle cookies for you. (Nope, not by default. I found the following below the definition for httpc:set_options():
CookieMode = enabled | disabled | verify
If cookies are enabled, all
valid cookies are automatically saved in the cookie database of the
client manager. If option verify is used, function store_cookies/2 has
to be called for the cookies to be saved. Default is disabled.
You can also add configuration options to the default profile.
But if you need to use a configuration like ipv6 for some requests and not others, then you can create a named profile and use the named profile when needed in your httpc:request()'s and the default profile for other requests (by not specifying a named profile). See httpc:set_options() for the client configuration options. Somewhat confusingly, httpc:request() has an Options argument, which allows you to specify other options (look in the docs below the definition of httpc:request/5 for a list of those options). Some of the request Options, like sync and stream, seem to be more appropriate for a profile:
A profile keeps track of proxy options, cookies, and other options
that can be applied to more than one request.
http://erlang.org/doc/man/httpc.html
If you create a profile by calling inets:start(httpc, profile_name), the return value is the Pid of the client, which gets spun off in a separate process and handles requests when using that profile. You can kill the client with either of the following:
inets:stop(httpc, name_of_profile)
inets:stop(httpc, ClientPid)
Related
rebar3 undef when running release console
I've been playing around with rebar3 (3.15.0) just to get it to run with the basic templates, and running into issues just trying to get a simple Hello World type example program going. I am starting with the template command to build a new release: rebar3 new release myfirstproj. It works fine when I run rebar3 shell but gives me the error below when running the release command: myfirstproj.cmd console after building it with rebar3 release. The only thing I've added to the base template is a io:format("Hello world!") in the supervisor.. It seems like the undef below is indicating it can't find the myfirstproj_app:start function.. any ideas why it doesn't work here but works in the rebar3 shell? OTP 23 [erts-11.0] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:30] =CRASH REPORT==== 21-Apr-2021::22:39:52.740000 === crasher: initial call: application_master:init/4 pid: <0.85.0> registered_name: [] exception exit: {bad_return, {{myfirstproj_app,start,[normal,[]]}, {'EXIT', {undef, [{myfirstproj_app,start,[normal,[]],[]}, {application_master,start_it_old,4, [{file,"application_master.erl"}, {line,277}]}]}}}} in function application_master:init/4 (application_master.erl, line 138) ancestors: [<0.83.0>] message_queue_len: 1 messages: [{'EXIT',<0.85.0>,normal}] links: [<0.83.0>,<0.44.0>] dictionary: [] trap_exit: true status: running heap_size: 376 stack_size: 28 reductions: 224 neighbours: =INFO REPORT==== 21-Apr-2021::22:39:21.439000 === application: myfirstproj exited: {bad_return, {{myfirstproj_app,start,[normal,[]]}, {'EXIT', {undef, [{myfirstproj_app,start,[normal,[]],[]}, {application_master,start_it_old,4, [{file,"application_master.erl"}, {line,277}]}]}}}} type: permanent My app code hasn't been changed from the template: %%%------------------------------------------------------------------- %% #doc myfirstproj public API %% #end %%%------------------------------------------------------------------- -module(myfirstproj_app). -behaviour(application). -export([start/2, stop/1]). start(_StartType, _StartArgs) -> myfirstproj_sup:start_link(). stop(_State) -> ok. %% internal functions Same with my app.src: {application, myfirstproj, [{description, "An OTP application"}, {vsn, "0.1.0"}, {registered, []}, {mod, {myfirstproj_app, []}}, {applications, [kernel, stdlib ]}, {env,[]}, {modules, []}, {licenses, ["Apache 2.0"]}, {links, []} ]}. The rebar.config hasn't been modified from the template. myfirstproj_sup.erl: %%%------------------------------------------------------------------- %% #doc myfirstproj top level supervisor. %% #end %%%------------------------------------------------------------------- -module(myfirstproj_sup). -behaviour(supervisor). -export([start_link/0]). -export([init/1]). -define(SERVER, ?MODULE). start_link() -> io:format("Hey, we started!"), supervisor:start_link({local, ?SERVER}, ?MODULE, []). init([]) -> SupFlags = #{strategy => one_for_all, intensity => 0, period => 1}, %ChildSpecs = [{worker1, %{file_watcher, start_link, []}, permanent, 1000, worker, [file_watcher]}], ChildSpecs = [], {ok, {SupFlags, ChildSpecs}}. %% internal functions
inets httpd: server configuration file syntax (proplist_file)
What is the syntax for the configuration file when you start an httpd server like this: inets:start(httpd, [{proplist_file, "./server_config.txt"}] ). The httpd docs say: {proplist_file, path()} If this property is defined, Inets expects to find all other properties defined in this file. And: the properties are to be fetched from a configuration file that can consist of a regular Erlang property list But with this file: server_config.txt: [ {port, 0}, {server_name, "httpd_test"}, {server_root, "/Users/7stud/erlang_programs/inets_proj"}, {document_root, "/Users/7stud/erlang_programs/inets_proj/htdocs"}, {ipfamily, inet6}, { bind_address, {0,0,0,0,0,0,0,1} } ] I get the error: $ 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> inets:start(). ok 2> inets:start(httpd, [{proplist_file, "/Users/7stud/erlang_programs/inets_proj/server_config.txt"}]). ** exception error: no try clause matching {error,{8,erl_parse,["syntax error before: ",[]]}} in function httpd_sup:httpd_config/1 (httpd_sup.erl, line 144) in call from httpd_sup:start_child/1 (httpd_sup.erl, line 52) in call from inets:call_service/3 (inets.erl, line 461) Next, I tried the Apache syntax, and that didn't work either: server_config.txt: Port 0 ServerName "httpd_test" ServerRoot "/Users/7stud/erlang_programs/inets_proj" DocumentRoot "./htdocs" Ipfamily inet6 BindAddress {0,0,0,0,0,0,0,1} 3> inets:start(httpd, [{file, "./server_config.txt"}]). {error,"httpd_conf: \"/Users/7stud/erlang_programs/inets_proj\" is an invalid ServerRoot"} 4> Okay, I made some progress on the Apache style syntax by getting rid of the quotes: Port 0 ServerName httpd_test ServerRoot /Users/7stud/erlang_programs/inets_proj DocumentRoot ./htdocs Ipfamily inet6 BindAddress 0:0:0:0:0:0:0:1 Now, I get the error: 8> inets:start(httpd, [{file, "./server_config.txt"}]). {error,"httpd_conf: 0:0:0:0:0:0:0:1 is an invalid address"}
I figured out the proplist syntax. I shortened up the paths once I got the proplist syntax to work: server_config.txt: [ {port, 0}, {server_name, "httpd_test"}, {server_root, "."}, {document_root, "./htdocs"}, {ipfamily, inet6}, { bind_address, {0,0,0,0,0,0,0,1} } ]. Notice the . at the end! The syntax is so obvious no wonder the docs don't give an example. :( $ 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> inets:start(). ok 2> {ok, Server} = inets:start(httpd, [{proplist_file, "./server_config.txt"}]). {ok,<0.73.0>} 3> httpd:info(Server). [{mime_types,[{"htm","text/html"},{"html","text/html"}]}, {ipfamily,inet6}, {server_name,"httpd_test"}, {bind_address,{0,0,0,0,0,0,0,1}}, {server_root,"."}, {port,49400}, {document_root,"./htdocs"}] I'm still wondering how to specify an ipv6 bind address with the Apache syntax. Maybe ipv6 came after the erlang Apache syntax was implemented?
Why is my cowboy server not running?
I'm trying to run my server. I copied my src files into a fresh cowboy install, this is the error I'm getting. ~/cowboy_ninja:.ls src action_handler.erl gate.beam resolution.beam arena.beam gate.erl resolution.erl arena.erl guestbook.beam temple.beam cowboy_ninja_app.erl guestbook.erl temple.erl cowboy_ninja_sup.erl hello_handler.erl ~/cowboy_ninja:. cat src/cowboy_ninja_app.erl -module(cowboy_ninja_app). -behaviour(application). -export([start/2]). -export([stop/1]). start(_Type, _Args) -> Dispatch = cowboy_router:compile([ {'_', [ {"/action", action_handler, []}, {"/join", hello_handler, []}]} ]), {ok, _} = cowboy:start_clear(my_http_listener, 100, [{port, 8080}], #{env => #{dispatch => Dispatch}} ), {ok, Pid} = gate:start_link(), register(gate, Pid), {ok, Guestbook} = guestbook:start_link(), register(guestbook, Guestbook), gate:action(Pid, {fight}), cowboy_ninja_sup:start_link(). stop(_State) -> ok. See below. ~/cowboy_ninja:.gmake run ===> Starting relx build process ... ===> Resolving OTP Applications from directories: /Users/quantum/cowboy_ninja/ebin /Users/quantum/cowboy_ninja/deps /usr/local/Cellar/erlang/19.1/lib/erlang/lib /Users/quantum/cowboy_ninja/apps /Users/quantum/cowboy_ninja/_rel ===> Resolved cowboy_ninja_release-1 ===> Including Erts from /usr/local/Cellar/erlang/19.1/lib/erlang ===> release successfully created! ===> tarball /Users/quantum/cowboy_ninja/_rel/cowboy_ninja_release/cowboy_ninja_release-1.tar.gz successfully created! Exec: /Users/quantum/cowboy_ninja/_rel/cowboy_ninja_release/erts-8.1/bin/erlexec -boot /Users/quantum/cowboy_ninja/_rel/cowboy_ninja_release/releases/1/cowboy_ninja_release -mode embedded -boot_var ERTS_LIB_DIR /Users/quantum/cowboy_ninja/_rel/cowboy_ninja_release/erts-8.1/../lib -config /Users/quantum/cowboy_ninja/_rel/cowboy_ninja_release/releases/1/sys.config -args_file /Users/quantum/cowboy_ninja/_rel/cowboy_ninja_release/releases/1/vm.args -- console Root: /Users/quantum/cowboy_ninja/_rel/cowboy_ninja_release /Users/quantum/cowboy_ninja/_rel/cowboy_ninja_release heart_beat_kill_pid = 52128 Erlang/OTP 19 [erts-8.1] [source] [64-bit] [smp:8:8] [async-threads:10] [hipe] [kernel-poll:false] [dtrace] =INFO REPORT==== 1-Jan-2017::17:03:57 === application: cowboy_ninja exited: {bad_return, {{cowboy_ninja_app,start,[normal,[]]}, {'EXIT', {undef, [{cowboy_router,compile, [[{'_', [{"/action",action_handler,[]}, {"/join",hello_handler,[]}]}]], []}, {cowboy_ninja_app,start,2, [{file,"src/cowboy_ninja_app.erl"},{line,8}]}, {application_master,start_it_old,4, [{file,"application_master.erl"}, {line,273}]}]}}}} type: permanent {"Kernel pid terminated",application_controller,"{application_start_failure,cowboy_ninja,{bad_return,{{cowboy_ninja_app,start,[normal,[]]},{'EXIT',{undef,[{cowboy_router,compile,[[{'_',[{\"/action\",action_handler,[]},{\"/join\",hello_handler,[]}]}]],[]},{cowboy_ninja_app,start,2,[{file,\"src/cowboy_ninja_app.erl\"},{line,8}]},{application_master,start_it_old,4,[{file,\"application_master.erl\"},{line,273}]}]}}}}}"} Kernel pid terminated (application_controller) ({application_start_failure,cowboy_ninja,{bad_return,{{cowboy_ninja_app,start,[normal,[]]},{'EXIT',{undef,[{cowboy_router,compile,[[{'_',[{"/action",acti heart: Sun Jan 1 17:03:58 2017: Erlang is crashing .. (waiting for crash dump file) heart: Sun Jan 1 17:03:58 2017: Would reboot. Terminating. gmake: *** [erlang.mk:6448: run] Error 1 It looks like it can't find my files? What is this error message saying?
You need to create a cowboy_ninja.app file and list all the applications you are using (cowboy, etc.). Relx will look at that file to see which applications it needs to include in your release. http://learnyousomeerlang.com/building-otp-applications Currently it is not including the cowboy application or any of it's compiled modules into your release. Here is an example from the cowboy examples {application, 'hello_world', [ {description, "Cowboy Hello World example"}, {vsn, "1"}, {modules, ['hello_world_app','hello_world_sup','toppage_handler']}, {registered, [hello_world_sup]}, {applications, [kernel,stdlib,cowboy]}, {mod, {hello_world_app, []}}, {env, []} ]}. You also need to tell relx where to look for your applications (in your relx.config). {lib_dirs, [ "/usr/local/lib/elixir/lib/*/ebin", "./_build/prod/lib/*/ebin", "./deps/*/ebin"]}. If you are not using a build tool for your app take a look at erlang.mk. it automatically creates an app file for you.
gen_server and the run-time errors
I have a run-time error in the init part of a gen_server. - Init begin by process_flag(trap_exit,true) - gen_server is part of a supervision tree I try to print the reason in the terminate module but it seems to exit elsewhere. - why terminate is not called ? The application stops with shutdown as reason. - How and where to catch the run-time error ?
The terminate callback is normally called in this situation, namely because you have trapped exits. The only place where this is not the case is if the crash happens in the init-function. In that case, the responsibility is on the supervisor, who usually terminates itself as a result. Then this error crawls up the supervisor tree until it ends up terminating your whole application. Usually, the supervisor will log a supervisor report with the context set to start_error. This is your hint that the part of the supervision tree has problems you should handle. You should check for this, because you may have the wrong assumption on where the error occurs. EDITED FROM HERE Your problem is that you don't know about SASL at all. Study it. Here is an example of how to use it. Hoisted code from your example: First, the bahlonga needed to tell Erlang we have a gen_server. -module(foo). -behaviour(gen_server). -export([start_link/0]). -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). We hack the #state{} record so it can be used with your code -record(state, { name, port, socket_listen }). Basic start_linkage... start_link() -> gen_server:start_link({local, foo}, ?MODULE, [], []). Your init function, spawn problem included. init([]) -> Port = 3252, Name = "foo", Above we have hacked a bit for the sake of simplification... process_flag(trap_exit, true), erlang:error(blabla), Opts = [binary, {reuseaddr, true}, {backlog,5}, {packet, 0}, {active, false}, {nodelay, true}], case gen_tcp:listen(Port,Opts) of {ok,Socket_Listen} -> logger:fmsg("--> [~s,init] Socket_Listen crée = ~p", [Name,Socket_Listen]), {ok,handle_accept(#state{socket_listen=Socket_Listen})}; {error, Reason} -> logger:fmsg("--> [~s,init] Erreur, Raison =~p", [Name,Reason]), {stop, Reason} end. Hacks for missing functions.... handle_accept(_) -> #state{}. The rest is just the basics..., so I omit them. Now for foo_sup the supervisor for foo: -module(foo_sup). -behaviour(supervisor). -export([start_link/0]). -export([init/1]). -define(SERVER, ?MODULE). Basic start link... start_link() -> supervisor:start_link({local, ?SERVER}, ?MODULE, []). Basic ChildSpec. Get the foo child up and running... init([]) -> FooCh = {foo, {foo, start_link, []}, permanent, 2000, worker, [foo]}, {ok, {{one_for_all,0,1}, [FooCh]}}. Boot Erlang with SASL enabled: jlouis#illithid:~$ erl -boot start_sasl Erlang R14B02 (erts-5.8.3) [source] [64-bit] [smp:2:2] [rq:2] [async-threads:0] [hipe] [kernel-poll:false] =PROGRESS REPORT==== 9-Dec-2010::01:01:51 === [..] Eshell V5.8.3 (abort with ^G) Let us try to spawn the supervisor... 1> foo_sup:start_link(). And we get this: =CRASH REPORT==== 9-Dec-2010::01:05:48 === crasher: initial call: foo:init/1 pid: <0.58.0> registered_name: [] exception exit: {blabla,[{foo,init,1}, {gen_server,init_it,6}, {proc_lib,init_p_do_apply,3}]} Above we see that we have a crash in foo:init/1 due to an exception blabla. in function gen_server:init_it/6 ancestors: [foo_sup,<0.45.0>] messages: [] links: [<0.57.0>] dictionary: [] trap_exit: true status: running heap_size: 233 stack_size: 24 reductions: 108 neighbours: And now the supervisor gets to report about the problem! =SUPERVISOR REPORT==== 9-Dec-2010::01:05:48 === Supervisor: {local,foo_sup} Context: start_error The context is exactly as I said it would be... Reason: {blabla,[{foo,init,1}, {gen_server,init_it,6}, {proc_lib,init_p_do_apply,3}]} And with the expected reason. Offender: [{pid,undefined}, {name,foo}, {mfargs,{foo,start_link,[]}}, {restart_type,permanent}, {shutdown,2000}, {child_type,worker}]
Receive binary data from a TCP server and save it to a file
How Does a TCP client receive binary data it requests from a TCP server? If the server sends the requested data (some binary data at a certain position of file) how is this received and saved to a file.
Here is an example of connecting to a remote server, receiving data, and writing it to a file: -module(recv). -export([example/1]). recv_data(IoDevice, Sock) -> case gen_tcp:recv(Sock, 0) of {ok, B} -> ok = file:write(IoDevice, B), recv_data(IoDevice, Sock); {error, Reason} -> error_logger:info_msg("Done receiving data: ~p~n", [Reason]), file:close(IoDevice), gen_tcp:close(Sock) end. example(FileName) -> {ok, IoDevice} = file:open(FileName, [write, binary]), {ok, Sock} = gen_tcp:connect("www.google.com", 80, [binary, {packet, raw}, {active, false}]), ok = gen_tcp:send(Sock, "GET / HTTP/1.0\r\n\r\n"), recv_data(IoDevice, Sock). This will connect to google and write the response to a file: Erlang R14A (erts-5.8) [source] [smp:2:2] [rq:2] [async-threads:0] [hipe] [kernel-poll:false] Eshell V5.8 (abort with ^G) 1> c(recv). {ok,recv} 2> recv:example("/tmp/test.bin"). =INFO REPORT==== 4-Nov-2010::08:52:59 === Done receiving data: closed ok 3>
You use gen_tcp:recv to receive the data. A regular write can then save them to a file.