-module(nitro_static). %%-author('Maxim Sokhatsky'). %% NITRO Static bridge for MAD containers -include_lib("kernel/include/file.hrl"). -export([ init/3, rest_init/2, malformed_request/2, forbidden/2, fix/1, content_types_provided/2, resource_exists/2, generate_etag/2, generate_default_etag/2, last_modified/2, get_file/2 ]). init(_, _, _) -> {upgrade, protocol, cowboy_rest}. rest_init(Req, {dir, Path, Extra}) when erlang:is_binary(Path) -> rest_init(Req, {dir, erlang:binary_to_list(Path), Extra}); rest_init(Req, {dir, Path, Extra}) -> {PathInfo, Req2} = cowboy_req:path_info(Req), Info = {ok, #file_info{type = regular, size = 0}}, FileName = filename:join([Path|PathInfo]), {ok, Req2, {FileName, Info, Extra}}. malformed_request(Req, State) -> {State =:= error, Req, State}. forbidden(Req, State = {_, {ok, #file_info{type = directory}}, _}) -> {true, Req, State}; forbidden(Req, State = {_, {error, eacces}, _}) -> {true, Req, State}; forbidden(Req, State = {_, {ok, #file_info{access = Access}}, _}) when Access =:= write; Access =:= none -> {true, Req, State}; forbidden(Req, State) -> {false, Req, State}. fixpath(Path) -> {Module,Fun} = application:get_env(n4u, fixpath, {nitro_static, fix}), %% todo mv this from nitro Module:Fun(Path). fix(A) -> A. content_types_provided(Req, State = {Path, _, Extra}) -> case lists:keyfind(mimetypes, 1, Extra) of false -> {[{cow_mimetypes:web(Path), get_file}], Req, State}; {mimetypes, Module, Function} -> {[{Module:Function(fixpath(Path)), get_file}], Req, State}; {mimetypes, Type} -> {[{Type, get_file}], Req, State} end. resource_exists(Req, State = {_, {ok, #file_info{type = regular}}, _}) -> {true, Req, State}; resource_exists(Req, State) -> {false, Req, State}. generate_etag(Req, State = {Path, {ok, #file_info{size = Size, mtime = Mtime}}, Extra}) -> case lists:keyfind(etag, 1, Extra) of false -> {generate_default_etag(Size, Mtime), Req, State}; {etag, Module, Function} -> {Module:Function(Path, Size, Mtime), Req, State}; {etag, false} -> {undefined, Req, State} end. generate_default_etag(Size, Mtime) -> {strong, erlang:list_to_binary( erlang:integer_to_list(erlang:phash2({Size, Mtime}, 16#ffffffff)) )}. last_modified(Req, State = {_, {ok, #file_info{mtime = Modified}}, _}) -> {Modified, Req, State}. get_file(Req, State = {P, {ok, #file_info{size = _Size}}, _}) -> Path = fixpath(P), StringPath = nitro:to_list(unicode:characters_to_binary(Path,utf8,utf8)), [_Type,Name|RestPath] = filename:split(StringPath), FileName = filename:absname(StringPath), Raw = case file:read_file(FileName) of {ok, Bin} -> Bin; {error, _} -> case mad_repl:load_file(StringPath) of {ok, ETSFile} -> ETSFile; {error, _} -> case file:read_file(filename:join([ code:lib_dir(erlang:list_to_atom(Name)) |RestPath])) of {ok, ReleaseFile} -> ReleaseFile; {error, _} -> <<>> end end end, Sendfile = fun(Socket, Transport) -> Transport:send(Socket, Raw) end, {{stream, erlang:size(Raw), Sendfile}, Req, State}.