How to upload multiple files into cowboy - erlang

trying to upload files using curl
curl -v -F 'file1=#/home/user/test/aaa/validation_utils.erl' -F 'file2=#/home/user/test/aaa/README.md' http://localhost:8083/api/list
but receiving only one (as i can see from logs)
[warning] Form_Data:
[{<<"content-type">>,<<"application/octet-stream">>},{<<"content-disposition">>,<<"form-data; name=\"file1\"; filename=\"validation_utils.erl\"">>}]
this is my get_body function:
get_body(<<"POST">>, AllHeaders, Req, _MaxFileSize) ->
case maps:get(<<"content-type">>, AllHeaders) of
<<"application/json", _Rest/binary>> ->
case catch cowboy_req:read_body(Req) of
{ok, <<>>, Req2} ->
{ok, [], Req2};
{ok, JsonBody, Req2} when JsonBody =/= <<>> ->
try
{ok, ?JSON_DECODE(JsonBody), Req2}
catch
_:Err ->
Err
end
end;
<<"multipart/form-data", _Rest/binary>> ->
{ok, Headers, Req2} = cowboy_req:read_part(Req),
{ok, Data, _Req3} = cowboy_req:read_part_body(Req2),
{file, _, Filename, _ContentType, _BitSize} = cow_multipart:form_data(Headers),
Body = [{<<"filename">>, Filename}, {<<"base64_file">>, base64:encode(Data)} | cowboy_req:parse_qs(Req)],
{ok, Body, Req};
_ ->
cowboy_req:read_urlencoded_body(Req)
end;

Got it.
I need to iterate the cowboy_req:read_part(Req)
multipart(Req0) ->
case cowboy_req:read_part(Req0) of
{ok, Headers, Req1} ->
?LOG_WARNING("Headers: p~n", [Headers]),
{ok, _Body, Req} = cowboy_req:read_part_body(Req1),
multipart(Req);
{done, Req} ->
?LOG_WARNING("Req: p~n", [Req]),
Req
end.
as mentioned here : https://ninenines.eu/docs/en/cowboy/2.0/guide/multipart/

Related

gen_server:call with new State

A module calls a gen_server to handle a stream, which uses record as State.
handle_call handles stream using a function from State, which separates completed piece of data and tail,
Now next time, the tail should be fed first but with an updated State, before the module sends in more data.
handle_call({stream, Data}, _From, State = #mystate{myfun=Fun}) ->
case Fun(Data) of
{completed piece,tail} ->
dosomethingwithpieace,
NewState = State##mystate{myfun=resetfun()};
% How do i call this again to feed Tail first with new state?
{stillstreaming, Currentstate} ->
NewState = State##mystate{myfun=CurrentState};
I cannot call gen_server:call(self(),{stream, Tail}) because State needs to be updated first.
And I cannot reply with a new State, because module will send in more data and tail will disappear.
Is there a way to call it again with updated State without replying with tail and feeding tail back from module??
Update,Code:
% caller module
case gen_tcp:recv(Socket, 0) of % cannot set Length as it will block untill it is done reading Length number of bytes
{ok, Data} ->
Response = gen_server:call(Pid, {handle_init,Socket,Data}),
case Response of
{ok, continue} ->
pre_loop(Socket, Pid);
{ok, logged_in} ->
{UserId, UserName} = get_user_data(), % ignore it for now.
receiver_loop(UserId, UserName, Socket, Pid);
{stop, Reason} ->
io:format("Error at pre_loop: ~p~n", [Reason]);
_ ->
io:format("Unknown response from call in pre-loop: ~p~n", [Response])
end;
{error, closed} -> % done here as no data was stored in mnesia yet.
gen_server:stop(Pid),
io:format("Client died in pre_loop~n")
end.
and gen_server module:
% gen_server module
handle_call({handle_init, _Socket, Data}, _From, State = #server_state{data_fun = {incomplete, Fun}}) ->
case catch Fun(Data) of
{incomplete, F} ->
NewState = State#server_state{data_fun = {incomplete, F}},
{reply, {ok, continue}, NewState};
{with_tail, Term, Tail} ->
% handle Term login/register only
case handle_input_init(Term, Tail) of
{incomplete, Fn, logged_in} ->
NewState = State#server_state{data_fun = {incomplete, Fn}},
{reply, {ok, logged_in}, NewState};
{incomplete, Fn} ->
NewState = State#server_state{data_fun = {incomplete, Fn}},
{reply, {ok, continue}, NewState};
{stop, malformed_data} ->
{reply, {stop, malformed_data}, State}
end;
_ ->
{reply, {stop, malformed_data}, State}
end;
handle_call(_Message, _From, State = #server_state{}) ->
{reply, {stop , unknown_call}, State}.
handle_input_init(Term, Tail) ->
case handle_term_init(Term) of
{ok, login_passed} ->
io:format("send user a login pass msg"),
handle_tail_init(Tail, logged_in);
{error, login_failed} ->
io:format("send user a login failed error~n"),
handle_tail_init(Tail);
{ok, registration_passed} ->
io:format("send user a registeration passed msg"),
handle_tail_init(Tail);
{error, registration_failed} ->
io:format("send user a registeration failed error"),
handle_tail_init(Tail);
{error, invalidreq} ->
io:format("send user an invalid requst error~n"),
handle_tail_init(Tail)
end.
handle_tail_init(Tail) ->
case catch jsx:decode(Tail, [stream, return_tail, return_maps]) of
{incomplete, F} ->
{incomplete, F};
{with_tail, Term, Tail2} ->
handle_input_init(Term, Tail2);
_ ->
{stop, malformed_data}
end.
handle_tail_init(Tail, logged_in) -> % because it was logged in already, any further requests should be ignored
case catch jsx:decode(Tail, [stream, return_tail, return_maps]) of
{incomplete, F} ->
{incomplete, F, logged_in};
{with_tail, _Term, Tail2} ->
io:format("send user an invalid requst error~n"),
handle_tail_init(Tail2, logged_in);
_ ->
{stop, malformed_data}
end.
handle_term_init(Term) ->
case Term of
#{<<"Login">> := [UserName,Password]} ->
login_user(UserName,Password);
#{<<"Register">> := [UserName,Password]} ->
register_user(UserName,Password);
_ ->
{error, invalidreq}
end.
It is working as expected but this is my very first Erlang code ever and i am positive that it can be simplified to a single recursive handle_call, maintaining OTP style, the reason I chose Erlang.
I cannot call gen_server:call(self(),{stream, Tail}) because State
needs to be updated first.
I can't really understand what you are trying to say, but if you mean:
I cannot recursively call gen_server:call(self(),{stream, Tail}), i.e. I cannot write code in handle:call() that recursively calls handle_call().
then you can certainly send all the data inside handle:call() to another function that recursively calls itself:
handle_call(...) ->
...
NewState = ....
Result = ...
{FinalResult, FinalState} = helper_func(Result, NewState, Tail)
{reply, FinalResult, FinalState}
helper_func(Result, State, []) ->
{Result, State};
helper_func(Result, State, [Piece|Tail]) ->
...
NewState = ...
NewResult = ...
helper_func(NewResult, NewState, Tail). %% Recursive call here

User send packet hook returning `badmatch` error

I am using user send packet hook in my custom module, I am not understanding why getting badmatch error for each of the packet received. i have added declaration part here too.
**Code**
-module(mod_post_log).
-behaviour(gen_mod).
-export([start/2,stop/1,log_user_send/4,post_result/1,send_message/3]).
**strong text**
-include("ejabberd.hrl").
-include("jlib.hrl").
-include("logger.hrl").
start(Host, _Opts) ->
ok = case inets:start() of
{error, {already_started, inets}} ->
ok;
ok ->
ok
end,
ejabberd_hooks:add(user_send_packet, Host,
?MODULE, log_user_send, 50),
ok.
stop(Host) ->
ejabberd_hooks:delete(user_send_packet, Host,
?MODULE, log_user_send, 50),
ok.
log_user_send(Packet, _C2SState, From, To) ->
ok = log_packet(From, To, Packet),
Packet.
log_packet(From, To, #xmlel{name = <<"message">>} = Packet) ->
MessageId = xml:get_tag_attr_s(<<"id">>, Packet),
Type = xml:get_tag_attr_s(list_to_binary("type"), Packet),
send_message(From, To, MessageId, Type),
%% send_message(From, To, xml:get_tag_attr_s(list_to_binary("id"), Packet),xml:get_tag_attr_s(list_to_binary("type"), Packet)),
%% ?INFO_MSG("mod_ack_receipt - MsgID: ~p To: ~p From: ~p", [MessageId, To, From]),
ok = log_message(From, To, Packet);
log_packet(_From, _To, _Packet) ->
ok.
log_message(From, To, #xmlel{attrs = Attrs} = Packet) ->
Type = lists:keyfind(<<"type">>, 1, Attrs),
log_message_filter(Type, From, To, Packet).
log_message_filter({<<"type">>, Type}, From, To, Packet)
when Type =:= <<"chat">>;
Type =:= <<"groupchat">> ->
log_chat(From, To, Packet);
log_message_filter(_Other, _From, _To, _Packet) ->
ok.
log_chat(From, To, #xmlel{children = Els} = Packet) ->
case get_body(Els) of
no_body ->
ok;
{ok, _Body} ->
log_chat_with_body(From, To, Packet)
end.
log_chat_with_body(From, To, Packet) ->
case should_acknowledge(Packet) of
S when (S==true) ->
post_xml(From, To, Packet);
false ->
ok
end,
Packet.
should_acknowledge(#xmlel{name = <<"message">>} = Packet) ->
case {xml:get_attr_s(<<"type">>, Packet#xmlel.attrs),
xml:get_subtag_cdata(Packet, <<"body">>)} of
{<<"error">>, _} ->
false;
{_, <<>>} ->
%% Empty body
false;
_ ->
true
end;
should_acknowledge(#xmlel{}) ->
false.
post_xml(From, To, Packet) ->
Ts = to_iso_8601_date(os:timestamp()),
MessageId = xml:get_tag_attr_s(list_to_binary("id"), Packet),
Type = xml:get_tag_attr_s(list_to_binary("type"), Packet),
Chat_sent = xml:get_tag_attr_s(list_to_binary("chat_sent"), Packet),
Subject = xml:get_path_s(Packet, [{elem, list_to_binary("subject")},cdata]),
Body = xml:get_path_s(Packet,[{elem, list_to_binary("body")}, cdata]),
Sep = "&",
Post = [
"from=", From#jid.luser, Sep,
"to=", To#jid.luser, Sep,
"body=", binary_to_list(Body), Sep,
"type=", binary_to_list(Type),Sep,
"chat_sent=", binary_to_list(Chat_sent),Sep,
"subject=",binary_to_list(Subject),Sep,
"message_id=", binary_to_list(MessageId),Sep,
"xml=",xml:element_to_binary(Packet),Sep,
"done=11"
],
Url = get_opt(url),
TsHeader = get_opt(ts_header, "X-Message-Timestamp"),
FromHeader = get_opt(from_header, "X-Message-From"),
ToHeader = get_opt(to_header, "X-Message-To"),
HttpOptions = get_opt(http_options, []),
ReqOptions = get_opt(req_options, []),
{ok, _ReqId} = httpc:request(post,
{Url, [], "application/x-www-form-urlencoded", list_to_binary(Post)},
HttpOptions,
[ {sync, false},
{receiver, {?MODULE, post_result, []}}
| ReqOptions ]),
?INFO_MSG("post http - MsgID: xml: ~p", [post]),
ok.
post_result({_ReqId, {error, Reason}}) ->
report_error([ {error, Reason } ]);
post_result({_ReqId, Result}) ->
{StatusLine, Headers, Body} = Result,
{_HttpVersion, StatusCode, ReasonPhrase} = StatusLine,
if StatusCode < 200;
StatusCode > 299 ->
ok = report_error([ {status_code, StatusCode},
{reason_phrase, ReasonPhrase},
{headers, Headers},
{body, Body} ]),
ok;
true ->
ok
end.
send_message(From, To, MessageId,Type) ->
Packet = #xmlel{name = <<"iq">>,
attrs = [{<<"to">>, From},{<<"ack">>, MessageId}, {<<"type">>, <<"result">>},{<<"chattype">>, Type}],
children =
[#xmlel{name = <<"ack">>,
attrs = [{<<"xmlns">>,<<"xmpp:ack">> }],
children = []}]},
ejabberd_router:route(From, From, Packet).
get_body(Els) ->
XmlEls = [ El || El <- Els, is_record(El, xmlel) ],
case lists:keyfind(<<"body">>, #xmlel.name, XmlEls) of
false ->
no_body;
#xmlel{children = InnerEls} ->
case lists:keyfind(xmlcdata, 1, InnerEls) of
false ->
no_body;
{xmlcdata, Body} ->
{ok, Body}
end
end.
get_opt(Opt) ->
get_opt(Opt, undefined).
get_opt(Opt, Default) ->
F = fun(Val) when is_binary(Val) -> binary_to_list(Val);
(Val) -> Val
end,
gen_mod:get_module_opt(global, ?MODULE, Opt, F, Default).
report_error(ReportArgs) ->
ok = error_logger:error_report([ mod_post_log_cannot_post | ReportArgs ]).
**Error Log**
{{badmatch,{xmlel,<<"message">>,
[{<<"xml:lang">>,<<"en">>},
{<<"to">>,<<"123456789#xyz.myapp.com">>},
{<<"id">>,<<"AF1xJ-149">>},
{<<"chat_sent">>,<<"2015-12-15T06:45:29.399Z">>},
{<<"type">>,<<"chat">>}],
[{xmlel,<<"size">>,[],[{xmlcdata,<<"0">>}]},
{xmlel,<<"subject">>,[],[{xmlcdata,<<"poke">>}]},
{xmlel,<<"body">>,[],[{xmlcdata,<<"95">>}]},
{xmlel,<<"request">>,
[{<<"xmlns">>,<<"urn:xmpp:receipts">>}],
[]}]}},
[{mod_post_log,log_packet,3,
[{file,"src/mod_post_log.erl"},{line,44}]},
{mod_post_log,log_user_send,4,
[{file,"src/mod_post_log.erl"},{line,33}]},
{ejabberd_hooks,safe_apply,3,
[{file,"src/ejabberd_hooks.erl"},{line,382}]},
{ejabberd_hooks,run_fold1,4,
[{file,"src/ejabberd_hooks.erl"},{line,365}]},
{ejabberd_c2s,session_established2,2,
[{file,"src/ejabberd_c2s.erl"},{line,1296}]},
{p1_fsm,handle_msg,10,[{file,"src/p1_fsm.erl"},{line,582}]},
{proc_lib,init_p_do_apply,3,
[{file,"proc_lib.erl"},{line,237}]}]}
In file src/mod_post_log.erl at line 44 in function mod_post_log:log_packet/3 is some match operator which doesn't expect to get
{xmlel,<<"message">>,
[{<<"xml:lang">>,<<"en">>},
{<<"to">>,<<"123456789#xyz.myapp.com">>},
{<<"id">>,<<"AF1xJ-149">>},
{<<"chat_sent">>,<<"2015-12-15T06:45:29.399Z">>},
{<<"type">>,<<"chat">>}],
[{xmlel,<<"size">>,[],[{xmlcdata,<<"0">>}]},
{xmlel,<<"subject">>,[],[{xmlcdata,<<"poke">>}]},
{xmlel,<<"body">>,[],[{xmlcdata,<<"95">>}]},
{xmlel,<<"request">>,
[{<<"xmlns">>,<<"urn:xmpp:receipts">>}],
[]}]}
it would be much better to see full module "mod_post_log.erl" as is.
But i think that problem is here :
MessageId = xml:get_tag_attr_s(<<"id">>, Packet),
Packet is a record, and function wait for iolist, so it should be something like
MessageId = xml:get_tag_attr_s(<<"id">>, Packet#xmlel.{{see record declaration}}),

Erl file not compiling due to syntax errors

I am trying to figure out what is wrong with this code, cause it is giving me errors preventing it from compiling properly into a beam file. I do not see what is wrong with the syntax. Is there an IDE which could help me out?
These are the errors:
parallels#parallels-Parallels-Virtual-Platform:/var/backend/ejabberd_modules# erlc -I /var/tmp/ejabberd/src/ mod_stanza_ack.erl
./mod_stanza_ack.erl:97: syntax error before: '.'
./mod_stanza_ack.erl:98: syntax error before: Body
./mod_stanza_ack.erl:16: function route/3 undefined
./mod_stanza_ack.erl:3: Warning: behaviour gen_mod undefined
./mod_stanza_ack.erl:111: Warning: function strip_bom/1 is unused
./mod_stanza_ack.erl:114: Warning: function send_presence/3 is unused
./mod_stanza_ack.erl:120: Warning: function echo/3 is unused
./mod_stanza_ack.erl:123: Warning: function send_message/4 is unused
This is the code:
-module(mod_stanza_ack).
-behavior(gen_server).
-behavior(gen_mod).
-export([start_link/2]).
-export([start/2,
stop/1,
init/1,
handle_call/3,
handle_cast/2,
handle_info/2,
terminate/2,
code_change/3]).
-export([route/3]).
-include("ejabberd.hrl").
-define(PROCNAME, ejabberd_mod_stanza_ack).
-define(BOTNAME, stanza_ack).
start_link(Host, Opts) ->
Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
gen_server:start_link({local, Proc}, ?MODULE, [Host, Opts], []).
start(Host, Opts) ->
Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
ChildSpec = {Proc,
{?MODULE, start_link, [Host, Opts]},
temporary,
1000,
worker,
[?MODULE]},
supervisor:start_child(ejabberd_sup, ChildSpec).
stop(Host) ->
Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
gen_server:call(Proc, stop),
supervisor:terminate_child(ejabberd_sup, Proc),
supervisor:delete_child(ejabberd_sup, Proc).
init([Host, Opts]) ->
?DEBUG("ECHO_BOT: Starting echo_bot", []),
% add a new virtual host / subdomain "echo".example.com
MyHost = gen_mod:get_opt_host(Host, Opts, "echo.#HOST#"),
ejabberd_router:register_route(MyHost, {apply, ?MODULE, route}),
{ok, Host}.
handle_call(stop, _From, Host) ->
{stop, normal, ok, Host}.
handle_cast(_Msg, Host) ->
{noreply, Host}.
handle_info(_Msg, Host) ->
{noreply, Host}.
terminate(_Reason, Host) ->
ejabberd_router:unregister_route(Host),
ok.
code_change(_OldVsn, Host, _Extra) ->
{ok, Host}.
% Checks a presence /subscription/ is a part of this.
% we may want to impliment blacklisting / some kind of
% protection here to prevent malicious users
%route(From, #jid{luser = ?BOTNAME} = To, {xmlelement, "presence", _, _} = Packet) ->
route(From, To, {xmlelement, "presence", _, _} = Packet) ->
case xml:get_tag_attr_s("type", Packet) of
"subscribe" ->
send_presence(To, From, "subscribe");
"subscribed" ->
send_presence(To, From, "subscribed"),
send_presence(To, From, "");
"unsubscribe" ->
send_presence(To, From, "unsubscribed"),
send_presence(To, From, "unsubscribe");
"unsubscribed" ->
send_presence(To, From, "unsubscribed");
"" ->
send_presence(To, From, "");
"unavailable" ->
ok;
"probe" ->
send_presence(To, From, "");
_Other ->
?INFO_MSG("Other kind of presence~n~p", [Packet])
end,
ok;
%route(From, #jid{luser = ?BOTNAME} = To, {xmlelement, "message", _, _} = Packet) ->
route(From, To, {xmlelement, "message", _, _} = Packet) ->
case xml:get_subtag_cdata(Packet, "body") of
"" ->
ok.
Body ->
case xml:get_tag_attr_s("type", Packet) of
"error" ->
?ERROR_MSG("Received error message~n~p -> ~p~n~p", [From, To, Packet]);
_ ->
echo(To, From, strip_bom(Body))
end
end,
ok.
%% HELPER FUNCTIONS
strip_bom([239,187,191|C]) -> C;
strip_bom(C) -> C.
send_presence(From, To, "") ->
ejabberd_router:route(From, To, {xmlelement, "presence", [], []});
send_presence(From, To, TypeStr) ->
ejabberd_router:route(From, To, {xmlelement, "presence", [{"type", TypeStr}], []}).
echo(From, To, Body) ->
send_message(From, To, "chat", Body).
send_message(From, To, TypeStr, BodyStr) ->
XmlBody = {xmlelement, "message",
[{"type", TypeStr},
{"from", jlib:jid_to_string(From)},
{"to", jlib:jid_to_string(To)}],
[{xmlelement, "body", [],
[{xmlcdata, BodyStr}]}]},
ejabberd_router:route(From, To, XmlBody).
./mod_stanza_ack.erl:97: syntax error before: '.' is saying the error is in line 97.
At line 97 change ok. to ok;. This will fix the issue.
Erlide plugin for eclipse is a good IDE to try out.
On line 97 in the function route/3 the first clause of the case is
"" ->
ok.
The . here ends the function which is illegal in the middle of a case, it should be a ;. This also means that the parser assumes that the variable Body on the next line starts a new function which is also illegal as a function name cannot be a variable.
Seeing the function route/3 has not been defined it cannot be exported which is the cause of the undefined function error on line 16.
Sometimes the compiler error messages are a bit cryptic but the line numbers usually help.

Why doesn't spawn/3 (MFA) work in Erlang?

Here is a simple server in Erlang. I am trying to spawn using spawn/3 but I have to resort to using spawn/1 in order to get it to work.
-module(server_by_name).
-export([start/0]).
start() ->
{ok, Listen} = gen_tcp:listen(1234, [binary, {packet, 0}, {reuseaddr, true}, {active, true}]),
io:format("Server running."),
% --> Why doesn't this work in place of the line below? --> spawn(server_by_name, connect, [Listen]).
spawn(fun() -> connect(Listen) end).
connect(Listen) ->
{ok, Socket} = gen_tcp:accept(Listen),
spawn(fun() -> connect(Listen) end),
io:format("Connection accepted on ~p Pid ~p~n", [Socket, self()]),
receive
{tcp, Socket, Bin} ->
Request = parse_request(Bin),
io:format("File requested: ~p~n", [Request]),
LoadHtml = file:read_file(Request),
case LoadHtml of
{ok, Reply} ->
Header = "HTTP/1.1 200 OK \r\n Content-Type:HTML\r\n\r\n",
ConnectCheck = gen_tcp:send(Socket, Header ++ Reply),
io:format("Connection?? ~p~n", [ConnectCheck]);
{error, Reason} ->
io:format("Error in loading html file: ~n~p~n", [Reason]);
end;
{tcp_closed, Socket} ->
io:format("Server socket closed~n")
after 5000 ->
io:format("Client unresponsive~n")
end.
parse_request(Bin) when is_binary(Bin)->
parse_request(binary_to_list(Bin));
parse_request("GET /" ++ RestStr) ->
get_request([], RestStr);
parse_request([_H|T]) ->
parse_request(T).
get_request(Request, [32|_RestStr]) ->
lists:reverse(Request);
get_request(Request, [H|RestStr]) ->
get_request([H|Request], RestStr);
get_request(Request, []) ->
io:format("Unexpected result in server_by_name:get_request()"),
list:reverse(Request).
I have included all the code so you can copy/paste it and try it yourself if need be. You just need to have a file that can be served in the same directory and request it by name. Obviously, it has no security precautions, etc.
spawn/3 requires the function to be exported. Your solution is perfectly reasonable, but you could also export connect/1:
-export([connect/1]). %% do not use, exported for spawn

Error reports in Erlang

I've taken the cowboy example code and brooken it.
The code for the default request handler looked like this:
-module(default_handler).
-behaviour(cowboy_http_handler).
-export([init/3, handle/2, terminate/2]).
init({_Any, http}, Req, []) ->
{ok, Req, undefined}.
handle(Req, State) ->
{ok, Req2} = cowboy_http_req:reply(200, [], <<"Hello world!">>, Req),
{ok, Req2, State}.
terminate(_Req, _State) ->
ok.
its straight forward, but I wanted to make return files so I changed it to:
-module(default_handler).
-behaviour(cowboy_http_handler).
-export([init/3, handle/2, terminate/2]).
init({_Any, http}, Req, []) ->
{ok, Req, undefined}.
handle(Req, State) ->
try
{Path, Req1} = cowboy_http_req:path(Req),
{ok, File} = file:read_file(Path),
cowboy_http_req:reply(200, [], File, Req1)
of
{ok, Req2} ->
{ok, Req2, State}
catch
_ ->
{ok, Req3} = cowboy_http_req:reply(200, [], <<"Hello world!">>, Req),
{ok, Req3, State}
end.
terminate(_Req, _State) ->
ok.
The try-catch thing should handle the fact that there might not be a file, but it does not. Why is that?
When I try to fetch a file that is not there I get a large error report in the console, can anyone tell me why?
=ERROR REPORT==== 15-Jun-2012::14:24:54 ===
** Handler default_handler terminating in handle/2
for the reason error:{badmatch,{error,badarg}}
** Options were []
** Handler state was undefined
** Request was [{socket,#Port<0.1515>},
{transport,cowboy_tcp_transport},
{connection,keepalive},
{pid,<0.1175.0>},
{method,'GET'},
{version,{1,1}},
{peer,undefined},
{host,[<<"localhost">>]},
{host_info,undefined},
{raw_host,<<"localhost">>},
{port,8080},
{path,[<<"favicon.ico">>]},
{path_info,undefined},
{raw_path,<<"/favicon.ico">>},
{qs_vals,undefined},
{raw_qs,<<>>},
{bindings,[]},
{headers,
[{'Accept-Charset',<<"ISO-8859-1,utf-8;q=0.7,*;q=0.3">>},
{'Accept-Language',<<"en-US,en;q=0.8">>},
{'Accept-Encoding',<<"gzip,deflate,sdch">>},
{'User-Agent',
<<"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/535.19 (KHTML, like Gecko) Ubuntu/10.10 Chromium/18.0.1025.151 Chrome/18.0.1025.151 Safari/535.19">>},
{'Accept',<<"*/*">>},
{'Connection',<<"keep-alive">>},
{'Host',<<"localhost">>}]},
{p_headers,[{'Connection',[<<"keep-alive">>]}]},
{cookies,undefined},
{meta,[]},
{body_state,waiting},
{buffer,<<>>},
{resp_state,waiting},
{resp_headers,[]},
{resp_body,<<>>},
{onresponse,undefined},
{urldecode,{#Fun<cowboy_http.urldecode.2>,crash}}]
** Stacktrace: [{default_handler,handle,2,
[{file,"src/default_handler.erl"},{line,13}]},
{cowboy_http_protocol,handler_handle,3,
[{file,"src/cowboy_http_protocol.erl"},
{line,298}]}]
probably because of how it evaluates the catch clause, see http://www.erlang.org/doc/reference_manual/expressions.html#try
If an exception occurs during evaluation of Exprs but there is no matching ExceptionPattern of the right Class with a true guard sequence, the exception is passed on as if Exprs had not been enclosed in a try expression.
You need to specify an error class (error, throw or exit) if not looking for the default, which is throw.
try Exprs of
Pattern1 [when GuardSeq1] ->
Body1;
...;
PatternN [when GuardSeqN] ->
BodyN
catch
[Class1:]ExceptionPattern1 [when ExceptionGuardSeq1] ->
ExceptionBody1;
...;
[ClassN:]ExceptionPatternN [when ExceptionGuardSeqN] ->
ExceptionBodyN
a catch-all-errors would be written as
catch
_:_ ->
notice how you specify both class and ExpressionPattern as "don't care".
Hope this helps because I only 'dabbled' in erlang until now. :)
This line:
{Path, Req1} = cowboy_http_req:path(Req),
Actually returns a list of binaries, like [<<"path">>,<<"path2">>] instead of something like "/path/path2" which should be what you're actually looking for.
So, to form the filesystem path:
{Path, Req1} = cowboy_http_req:path(Req),
FsPath = lists:foldl(
fun(PathComponent, Acc) ->
string:join([Acc, erlang:binary_to_list(PathComponent)], "/")
end,
"",
Path
),
{ok, File} = file:read_file(FsPath),
(The badarg error you're getting is because the argument to file:read_file/1 is not a string (a list) but a list of binaries, which is not the expected argument.
And the catch needs a _:_ clause, just like Harald answer states.
Cheers.

Resources