From 8e7a8b90df3fb69300b46aeae9688230e0375289 Mon Sep 17 00:00:00 2001 From: Robert Newson Date: Wed, 9 Mar 2011 13:45:12 +0000 Subject: [PATCH] hook up POST to webmachine. --- src/monic_file.erl | 38 ++++++++--------- src/monic_file_resource.erl | 81 ++++++++++++++++++++++++++++++------- src/monic_utils.erl | 8 ++-- test/monic_file_test.erl | 21 ++++++---- 4 files changed, 99 insertions(+), 49 deletions(-) diff --git a/src/monic_file.erl b/src/monic_file.erl index 23a4546..e743c1e 100644 --- a/src/monic_file.erl +++ b/src/monic_file.erl @@ -172,8 +172,9 @@ load_main_items(Tid, Fd, {_, Location}=Hints) -> add_item(Size, Fun, #state{tid=Tid, index_fd=IndexFd, main_fd=MainFd, next_key=Key, next_location=Location}) -> Cookie = monic_utils:new_cookie(), + Flags = <<0:16>>, Version = 1, - Header = #header{key=Key, cookie=Cookie, size=Size, version=Version, flags=0}, + Header = #header{key=Key, cookie=Cookie, size=Size, version=Version, flags=Flags}, case monic_utils:pwrite_header(MainFd, Location, Header) of ok -> case copy_in(MainFd, Fun, Location + ?HEADER_SIZE, Size) of @@ -184,7 +185,7 @@ add_item(Size, Fun, #state{tid=Tid, index_fd=IndexFd, main_fd=MainFd, case file:datasync(MainFd) of ok -> monic_utils:write_index(IndexFd, - #index{key=Key,location=Location,size=Size,version=Version,flags=0} + #index{key=Key,location=Location,size=Size,version=Version,flags=Flags} ), ets:insert(Tid, {Key, Location, Size, Version}), {ok, Key, Cookie}; @@ -204,27 +205,20 @@ add_item(Size, Fun, #state{tid=Tid, index_fd=IndexFd, main_fd=MainFd, copy_in(Fd, Fun, Location, Remaining) -> copy_in(Fd, Fun, Location, Remaining, crypto:sha_init()). -copy_in(_Fd, _Fun, _Location, 0, Sha) -> +copy_in(_Fd, done, _Location, 0, Sha) -> {ok, crypto:sha_final(Sha)}; -copy_in(Fd, Fun, Location, Remaining, Sha) -> - case Fun(?BUFFER_SIZE) of - {ok, Bin} -> - Size = iolist_size(Bin), - case Size =< Remaining of - true -> - case file:pwrite(Fd, Location, Bin) of - ok -> - copy_in(Fd, Fun, Location + Size, - Remaining - Size, - crypto:sha_update(Sha, Bin)); - Else -> - Else - end; - false -> - {error, overflow} - end; - Else -> - Else +copy_in(_Fd, done, _Location, _Remaining, _Sha) -> + {error, underflow}; +copy_in(Fd, Fun, Location, Remaining, Sha) -> + {Bin, Next} = Fun(), + Size = iolist_size(Bin), + case Size =< Remaining of + true -> + file:pwrite(Fd, Location, Bin), + copy_in(Fd, Next, Location + Size, Remaining - Size, + crypto:sha_update(Sha, Bin)); + false -> + {error, overflow} end. copy_out(_Fd, _Fun, _Location, 0) -> diff --git a/src/monic_file_resource.erl b/src/monic_file_resource.erl index 7e017aa..1794521 100644 --- a/src/monic_file_resource.erl +++ b/src/monic_file_resource.erl @@ -17,26 +17,43 @@ allowed_methods/2, content_types_accepted/2, content_types_provided/2, + create_path/2, delete_resource/2, is_conflict/2, - resource_exists/2]). - --export([from_json/2, to_json/2]). --import(monic_utils, [path/2, exists/2]). + post_is_create/2, + resource_exists/2, + valid_entity_length/2]). +-export([show_file/2, create_file/2, add_item/2]). -include_lib("webmachine/include/webmachine.hrl"). +-define(BUFFER_SIZE, 64*1024). allowed_methods(ReqData, Context) -> - {['DELETE', 'GET', 'PUT'], ReqData, Context}. + {['DELETE', 'GET', 'PUT', 'POST'], ReqData, Context}. content_types_accepted(ReqData, Context) -> - {[{"application/json", from_json}], ReqData, Context}. + case wrq:method(ReqData) of + 'PUT' -> + {[{"application/json", create_file}], ReqData, Context}; + 'POST' -> + CT = case wrq:get_req_header("Content-Type", ReqData) of + undefined -> "application/octet-stream"; + Other -> Other + end, + {[{CT, add_item}], ReqData, Context} + end. content_types_provided(ReqData, Context) -> - {[{"application/json", to_json}], ReqData, Context}. + {[{"application/json", show_file}], ReqData, Context}. + +create_path(ReqData, Context) -> + File = wrq:path_info(file, ReqData), + Key = 1, %% TODO increment. + Cookie = crypto:rand_uniform(1, 1 bsl 32), + {io_lib:format("/~s/~B/~B", [File, Key, Cookie]), ReqData, Context}. delete_resource(ReqData, Context) -> - case file:delete(path(ReqData, Context)) of + case file:delete(monic_utils:path(ReqData, Context)) of ok -> {true, ReqData, Context}; _ -> @@ -47,15 +64,49 @@ init(ConfigProps) -> {ok, ConfigProps}. is_conflict(ReqData, Context) -> - {exists(ReqData, Context), ReqData, Context}. + {monic_utils:exists(ReqData, Context), ReqData, Context}. + +post_is_create(ReqData, Context) -> + {true, ReqData, Context}. resource_exists(ReqData, Context) -> - {exists(ReqData, Context), ReqData, Context}. + {monic_utils:exists(ReqData, Context), ReqData, Context}. -from_json(ReqData, Context) -> - ok = file:write_file(path(ReqData, Context), <<>>, [exclusive]), - {true, ReqData, Context}. - -to_json(ReqData, Context) -> +valid_entity_length(ReqData, Context) -> + Valid = case wrq:method(ReqData) of + 'POST' -> + wrq:get_req_header("Content-Length", ReqData) /= undefined; + _ -> + true + end, + {Valid, ReqData, Context}. + +%% private functions + +show_file(ReqData, Context) -> {"{\"ok\": true}", ReqData, Context}. +create_file(ReqData, Context) -> + case file:write_file(monic_utils:path(ReqData, Context), <<>>, [exclusive]) of + ok -> {true, ReqData, Context}; + _ -> {false, ReqData, Context} + end. + +add_item(ReqData, Context) -> + case monic_file:open(monic_utils:path(ReqData, Context)) of + {ok, Pid} -> + try + Size = list_to_integer(wrq:get_req_header("Content-Length", ReqData)), + StreamBody = fun() -> wrq:stream_req_body(ReqData, ?BUFFER_SIZE) end, + case monic_file:add(Pid, Size, StreamBody) of + {ok, Key, Cookie} -> + {true, ReqData, Context}; + _ -> + {false, ReqData, Context} + end + after + monic_file:close(Pid) + end; + _ -> + {false, ReqData, Context} + end. diff --git a/src/monic_utils.erl b/src/monic_utils.erl index 4d8a98e..04346bb 100644 --- a/src/monic_utils.erl +++ b/src/monic_utils.erl @@ -35,13 +35,13 @@ exists(ReqData, Context) -> pwrite_header(Fd, Location, #header{key=Key,cookie=Cookie,size=Size, version=Version,flags=Flags}) -> Bin = <>, + Size:64/integer, Version:16/integer, Flags:16/bitstring>>, file:pwrite(Fd, Location, Bin). pread_header(Fd, Location) -> case file:pread(Fd, Location, ?HEADER_SIZE) of {ok, <>} -> + Size:64/integer, Version:16/integer, Flags:16/bitstring>>} -> {ok, #header{key=Key,cookie=Cookie,size=Size,version=Version,flags=Flags}}; {ok, _} -> {error, invalid_header}; @@ -65,13 +65,13 @@ pread_footer(Fd, Location) -> write_index(Fd, #index{key=Key,location=Location,size=Size,version=Version,flags=Flags}) -> Bin = <>, + Version:16/integer, Flags:16/bitstring>>, file:write(Fd, Bin). read_index(Fd) -> case file:read(Fd, ?INDEX_SIZE) of {ok, <>} -> + Version:16/integer, Flags:16/bitstring>>} -> {ok, #index{key=Key,location=Location,size=Size, version=Version,flags=Flags}}; {ok, _} -> diff --git a/test/monic_file_test.erl b/test/monic_file_test.erl index 7f8dcf3..706cfec 100644 --- a/test/monic_file_test.erl +++ b/test/monic_file_test.erl @@ -23,7 +23,8 @@ all_test_() -> [fun add/1, fun add_read/1, fun add_multi/1, - fun overflow/1 + fun overflow/1, + fun underflow/1 ]}. setup() -> @@ -36,18 +37,22 @@ cleanup(Pid) -> monic_file:close(Pid). add(Pid) -> - ?_assertMatch({ok, 0, _}, monic_file:add(Pid, 3, fun(_Max) -> {ok, <<"123">>} end)). + ?_assertMatch({ok, 0, _}, monic_file:add(Pid, 3, fun() -> {<<"123">>, done} end)). add_read(Pid) -> - {ok, Key, Cookie} = monic_file:add(Pid, 3, fun(_Max) -> {ok, <<"123">>} end), + {ok, Key, Cookie} = monic_file:add(Pid, 3, fun() -> {<<"123">>, done} end), ?_assertEqual(ok, monic_file:read(Pid, Key, Cookie, fun({ok, <<"123">>}) -> ok end)). add_multi(Pid) -> - [?_assertMatch({ok, 0, _}, monic_file:add(Pid, 3, fun(_Max) -> {ok, <<"123">>} end)), - ?_assertMatch({ok, 1, _}, monic_file:add(Pid, 3, fun(_Max) -> {ok, <<"456">>} end)), - ?_assertMatch({ok, 2, _}, monic_file:add(Pid, 3, fun(_Max) -> {ok, <<"789">>} end)), - ?_assertMatch({ok, 3, _}, monic_file:add(Pid, 3, fun(_Max) -> {ok, <<"abc">>} end))]. + [?_assertMatch({ok, 0, _}, monic_file:add(Pid, 3, fun() -> {<<"123">>, done} end)), + ?_assertMatch({ok, 1, _}, monic_file:add(Pid, 3, fun() -> {<<"456">>, done} end)), + ?_assertMatch({ok, 2, _}, monic_file:add(Pid, 3, fun() -> {<<"789">>, done} end)), + ?_assertMatch({ok, 3, _}, monic_file:add(Pid, 3, fun() -> {<<"abc">>, done} end))]. overflow(Pid) -> - Res = monic_file:add(Pid, 3, fun(_Max) -> {ok, <<"1234">>} end), + Res = monic_file:add(Pid, 3, fun() -> {<<"1234">>, done} end), ?_assertEqual({error, overflow}, Res). + +underflow(Pid) -> + Res = monic_file:add(Pid, 3, fun() -> {<<"12">>, done} end), + ?_assertEqual({error, underflow}, Res).