nitro_static.erl 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105
  1. -module(nitro_static).
  2. %%-author('Maxim Sokhatsky').
  3. %% NITRO Static bridge for MAD containers
  4. -include_lib("kernel/include/file.hrl").
  5. -export([
  6. init/3,
  7. rest_init/2,
  8. malformed_request/2,
  9. forbidden/2,
  10. fix/1,
  11. content_types_provided/2,
  12. resource_exists/2,
  13. generate_etag/2,
  14. generate_default_etag/2,
  15. last_modified/2,
  16. get_file/2
  17. ]).
  18. init(_, _, _) -> {upgrade, protocol, cowboy_rest}.
  19. rest_init(Req, {dir, Path, Extra}) when erlang:is_binary(Path) ->
  20. rest_init(Req, {dir, erlang:binary_to_list(Path), Extra});
  21. rest_init(Req, {dir, Path, Extra}) ->
  22. {PathInfo, Req2} = cowboy_req:path_info(Req),
  23. Info = {ok, #file_info{type = regular, size = 0}},
  24. FileName = filename:join([Path|PathInfo]),
  25. {ok, Req2, {FileName, Info, Extra}}.
  26. malformed_request(Req, State) -> {State =:= error, Req, State}.
  27. forbidden(Req, State = {_, {ok, #file_info{type = directory}}, _}) -> {true, Req, State};
  28. forbidden(Req, State = {_, {error, eacces}, _}) -> {true, Req, State};
  29. forbidden(Req, State = {_, {ok, #file_info{access = Access}}, _}) when Access =:= write; Access =:= none -> {true, Req, State};
  30. forbidden(Req, State) -> {false, Req, State}.
  31. fixpath(Path) ->
  32. {Module,Fun} = application:get_env(n4u, fixpath, {nitro_static, fix}), %% todo mv this from nitro
  33. Module:Fun(Path).
  34. fix(A) -> A.
  35. content_types_provided(Req, State = {Path, _, Extra}) ->
  36. case lists:keyfind(mimetypes, 1, Extra) of
  37. false ->
  38. {[{cow_mimetypes:web(Path), get_file}], Req, State};
  39. {mimetypes, Module, Function} ->
  40. {[{Module:Function(fixpath(Path)), get_file}], Req, State};
  41. {mimetypes, Type} ->
  42. {[{Type, get_file}], Req, State}
  43. end.
  44. resource_exists(Req, State = {_, {ok, #file_info{type = regular}}, _}) -> {true, Req, State};
  45. resource_exists(Req, State) -> {false, Req, State}.
  46. generate_etag(Req, State = {Path, {ok, #file_info{size = Size, mtime = Mtime}}, Extra}) ->
  47. case lists:keyfind(etag, 1, Extra) of
  48. false ->
  49. {generate_default_etag(Size, Mtime), Req, State};
  50. {etag, Module, Function} ->
  51. {Module:Function(Path, Size, Mtime), Req, State};
  52. {etag, false} ->
  53. {undefined, Req, State}
  54. end.
  55. generate_default_etag(Size, Mtime) ->
  56. {strong, erlang:list_to_binary( erlang:integer_to_list(erlang:phash2({Size, Mtime}, 16#ffffffff)) )}.
  57. last_modified(Req, State = {_, {ok, #file_info{mtime = Modified}}, _}) -> {Modified, Req, State}.
  58. get_file(Req, State = {P, {ok, #file_info{size = _Size}}, _}) ->
  59. Path = fixpath(P),
  60. StringPath = nitro:to_list(unicode:characters_to_binary(Path,utf8,utf8)),
  61. [_Type,Name|RestPath] = filename:split(StringPath),
  62. FileName = filename:absname(StringPath),
  63. Raw = case file:read_file(FileName) of
  64. {ok, Bin} -> Bin;
  65. {error, _} ->
  66. case mad_repl:load_file(StringPath) of
  67. {ok, ETSFile} -> ETSFile;
  68. {error, _} ->
  69. case file:read_file(filename:join([ code:lib_dir(erlang:list_to_atom(Name)) |RestPath])) of
  70. {ok, ReleaseFile} -> ReleaseFile;
  71. {error, _} -> <<>>
  72. end
  73. end
  74. end,
  75. Sendfile = fun(Socket, Transport) ->
  76. Transport:send(Socket, Raw)
  77. end,
  78. {{stream, erlang:size(Raw), Sendfile}, Req, State}.