flm01/server/api/webmachine/demo/src/demo_fs_resource.erl

158 lines
4.8 KiB
Erlang

%% @author Bryan Fink <bryan@basho.com>
%% @author Andy Gross <andy@basho.com>
%% @author Justin Sheehy <justin@basho.com>
%% @copyright 2008-2009 Basho Technologies, Inc.
-module(demo_fs_resource).
-export([init/1]).
-export([allowed_methods/2,
resource_exists/2,
last_modified/2,
content_types_provided/2,
content_types_accepted/2,
delete_resource/2,
post_is_create/2,
create_path/2,
provide_content/2,
accept_content/2,
generate_etag/2]).
-record(context, {root,response_body=undefined,metadata=[]}).
-include_lib("kernel/include/file.hrl").
-include_lib("webmachine/include/webmachine.hrl").
init(ConfigProps) ->
{root, Root} = proplists:lookup(root, ConfigProps),
{ok, #context{root=Root}}.
allowed_methods(ReqData, Context) ->
{['HEAD', 'GET', 'PUT', 'DELETE', 'POST'], ReqData, Context}.
file_path(Context, Name) ->
RelName = case hd(Name) of
"/" -> tl(Name);
_ -> Name
end,
filename:join([Context#context.root, RelName]).
file_exists(Context, Name) ->
NamePath = file_path(Context, Name),
case filelib:is_regular(NamePath) of
true ->
{true, NamePath};
false ->
false
end.
resource_exists(ReqData, Context) ->
Path = wrq:disp_path(ReqData),
case file_exists(Context, Path) of
{true, _} ->
{true, ReqData, Context};
_ ->
case Path of
"p" -> {true, ReqData, Context};
_ -> {false, ReqData, Context}
end
end.
maybe_fetch_object(Context, Path) ->
% if returns {true, NewContext} then NewContext has response_body
case Context#context.response_body of
undefined ->
case file_exists(Context, Path) of
{true, FullPath} ->
{ok, Value} = file:read_file(FullPath),
{true, Context#context{response_body=Value}};
false ->
{false, Context}
end;
_Body ->
{true, Context}
end.
content_types_provided(ReqData, Context) ->
CT = webmachine_util:guess_mime(wrq:disp_path(ReqData)),
{[{CT, provide_content}], ReqData,
Context#context{metadata=[{'content-type', CT}|Context#context.metadata]}}.
content_types_accepted(ReqData, Context) ->
CT = case wrq:get_req_header("content-type", ReqData) of
undefined -> "application/octet-stream";
X -> X
end,
{MT, _Params} = webmachine_util:media_type_to_detail(CT),
{[{MT, accept_content}], ReqData,
Context#context{metadata=[{'content-type', MT}|Context#context.metadata]}}.
accept_content(ReqData, Context) ->
Path = wrq:disp_path(ReqData),
FP = file_path(Context, Path),
ok = filelib:ensure_dir(filename:dirname(FP)),
ReqData1 = case file_exists(Context, Path) of
{true, _} ->
ReqData;
_ ->
LOC = "http://" ++
wrq:get_req_header("host", ReqData) ++
"/fs/" ++ Path,
wrq:set_resp_header("Location", LOC, ReqData)
end,
Value = wrq:req_body(ReqData1),
case file:write_file(FP, Value) of
ok ->
{true, wrq:set_resp_body(Value, ReqData1), Context};
Err ->
{{error, Err}, ReqData1, Context}
end.
post_is_create(ReqData, Context) ->
{true, ReqData, Context}.
create_path(ReqData, Context) ->
case wrq:get_req_header("slug", ReqData) of
undefined -> {undefined, ReqData, Context};
Slug ->
case file_exists(Context, Slug) of
{true, _} -> {undefined, ReqData, Context};
_ -> {Slug, ReqData, Context}
end
end.
delete_resource(ReqData, Context) ->
case file:delete(file_path(
Context, wrq:disp_path(ReqData))) of
ok -> {true, ReqData, Context};
_ -> {false, ReqData, Context}
end.
provide_content(ReqData, Context) ->
case maybe_fetch_object(Context, wrq:disp_path(ReqData)) of
{true, NewContext} ->
Body = NewContext#context.response_body,
{Body, ReqData, Context};
{false, NewContext} ->
{error, ReqData, NewContext}
end.
last_modified(ReqData, Context) ->
{true, FullPath} = file_exists(Context,
wrq:disp_path(ReqData)),
LMod = filelib:last_modified(FullPath),
{LMod, ReqData, Context#context{metadata=[{'last-modified',
httpd_util:rfc1123_date(LMod)}|Context#context.metadata]}}.
hash_body(Body) -> mochihex:to_hex(binary_to_list(crypto:sha(Body))).
generate_etag(ReqData, Context) ->
case maybe_fetch_object(Context, wrq:disp_path(ReqData)) of
{true, BodyContext} ->
ETag = hash_body(BodyContext#context.response_body),
{ETag, ReqData,
BodyContext#context{metadata=[{etag,ETag}|
BodyContext#context.metadata]}};
_ ->
{undefined, ReqData, Context}
end.