|
@@ -0,0 +1,112 @@
|
|
|
|
+-module(n4u_static).
|
|
|
|
+-include_lib("kernel/include/file.hrl").
|
|
|
|
+
|
|
|
|
+-export([init/3, rest_init/2, forbidden/2, malformed_request/2,
|
|
|
|
+ content_types_provided/2, resource_exists/2, generate_etag/2,
|
|
|
|
+ last_modified/2, get_file/2, mad_load_file/1]). % todo check last func - maybe needs outside this module
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+% N4U Static Bridge to files
|
|
|
|
+
|
|
|
|
+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), %% todo check - replace with cowboy bridge func
|
|
|
|
+ Info = {ok, #file_info{type = regular, 'size' = 0}},
|
|
|
|
+ FileName = filename:join([Path|PathInfo]),
|
|
|
|
+ wf:info(?MODULE, "Rest Init: ~p~n", [FileName]),
|
|
|
|
+ {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}.
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+content_types_provided(Req, State = {Path, _, Extra}) ->
|
|
|
|
+ wf:info(?MODULE,"Content Type Provided: ~p~n",[Path]),
|
|
|
|
+ case lists:keyfind(mimetypes, 1, Extra) of
|
|
|
|
+ false ->
|
|
|
|
+ {[{cow_mimetypes:web(Path), get_file}], Req, State};
|
|
|
|
+ {mimetypes, Module, Function} ->
|
|
|
|
+ {[{Module:Function(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 = {Path, {ok, #file_info{'size' = _Size}}, _}) ->
|
|
|
|
+ StringPath = nitro:to_list(unicode:characters_to_binary(Path, utf8, utf8)),
|
|
|
|
+ [_Type, Name|RestPath] = SplitPath = filename:split(StringPath),
|
|
|
|
+ wf:info(?MODULE, "Split Path: ~p~n", [SplitPath]),
|
|
|
|
+ %wf:info(?MODULE, "Code Path: ~p~n", [filename:join([code:lib_dir(Name)|RestPath])]),
|
|
|
|
+ FileName = filename:absname(StringPath),
|
|
|
|
+ wf:info(?MODULE, "Abs Name: ~p~n", [FileName]),
|
|
|
|
+ Raw = case file:read_file(FileName) of
|
|
|
|
+ {ok, Bin} -> Bin;
|
|
|
|
+ {error, _} ->
|
|
|
|
+ case ?MODULE:mad_load_file(StringPath) of % todo check how it works
|
|
|
|
+ {ok, ETSFile} -> ETSFile;
|
|
|
|
+ {error, _} ->
|
|
|
|
+ case file:read_file( filename:join( [code:lib_dir(Name)|RestPath] ) ) of
|
|
|
|
+ {ok, ReleaseFile} -> ReleaseFile;
|
|
|
|
+ {error, _} -> <<>>
|
|
|
|
+ end
|
|
|
|
+ end
|
|
|
|
+ end,
|
|
|
|
+ wf:info(?MODULE, "Cowboy Requested Static File: ~p~n", [FileName]),
|
|
|
|
+ Sendfile = fun(Socket, Transport) ->
|
|
|
|
+ case Transport:send(Socket, Raw) of
|
|
|
|
+ {ok, _} -> ok;
|
|
|
|
+ {error, closed} -> ok;
|
|
|
|
+ {error, etimedout} -> ok;
|
|
|
|
+ _ -> ok
|
|
|
|
+ end
|
|
|
|
+ end,
|
|
|
|
+ {{stream, erlang:size(Raw), Sendfile}, Req, State}.
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ets_created() ->
|
|
|
|
+ case ets:info(filesystem) of
|
|
|
|
+ undefined ->
|
|
|
|
+ ets:new(filesystem, [set, named_table, {keypos, 1}, public]);
|
|
|
|
+ _ ->
|
|
|
|
+ skip
|
|
|
|
+ end.
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+mad_load_file(Name) ->
|
|
|
|
+ ets_created(),
|
|
|
|
+ case ets:lookup(filesystem, Name) of
|
|
|
|
+ [{Name, Bin}] -> {ok, Bin};
|
|
|
|
+ _ -> {error, etsfs}
|
|
|
|
+ end.
|
|
|
|
+
|