Skip to content

Commit

Permalink
basic multiple-Range request support (bz://487)
Browse files Browse the repository at this point in the history
probably isn't rfc-compliant yet (missing specific headers, no overlapping-range
collapsing, etc.), but basic mechanism for sending all parts is there
  • Loading branch information
beerriot committed Aug 5, 2010
1 parent 5ae5490 commit 0db0bf6
Showing 1 changed file with 44 additions and 1 deletion.
45 changes: 44 additions & 1 deletion src/luwak_wm_file.erl
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@
process_post/2,
produce_doc_body/2,
produce_single_byterange/2,
produce_byteranges/2,
accept_doc_body/2,
delete_resource/2
]).
Expand Down Expand Up @@ -606,12 +607,25 @@ produce_single_byterange(RD, Ctx=#ctx{handle={ok, H},
ranges=[Range]}) ->
{Start, End} = concrete_range(C, H, Range),
FileLength = luwak_file:length(C, H),
RangeHead = io_lib:format("~b-~b/~b", [Start, Start+End-1, FileLength]),
RangeHead = content_range_header(Start, End, FileLength),
CLRD = wrq:set_resp_header(?HEAD_CRANGE, RangeHead, RD),
{send_file(C, H, Start, End),
add_user_metadata(CLRD, H),
Ctx}.

produce_byteranges(RD, Ctx=#ctx{handle={ok, H},
client=C,
ranges=Ranges}) ->
CRanges = [ concrete_range(C, H, R) || R <- Ranges ],
FileLength = luwak_file:length(C, H),
Boundary = riak_core_util:unique_id_62(),
BRD = wrq:set_resp_header(?HEAD_CTYPE,
"multipart/mixed; boundary="++Boundary,
RD),
{{stream, multi_send_file(C, H, FileLength, Boundary, CRanges)},
add_user_metadata(BRD, H),
Ctx}.

add_user_metadata(RD, Handle) ->
Attr = luwak_file:get_attributes(Handle),
case dict:find(?MD_USERMETA, Attr) of
Expand All @@ -623,6 +637,9 @@ add_user_metadata(RD, Handle) ->
error -> RD
end.

content_range_header(Start, End, Length) ->
io_lib:format("~b-~b/~b", [Start, Start+End-1, Length]).

send_file(Client, Handle, Start, End) ->
Stream = luwak_get_stream:start(Client, Handle, Start, End),
{stream, (send_file_helper(Stream))()}.
Expand All @@ -643,6 +660,32 @@ send_file_helper(Stream) ->
end
end.

%% basic strategy of multi_send_file is to wrap up calls to
%% send_file in thunks, so that as each range finishes sending
%% we can start the next one
multi_send_file(_C, _H, _Length, Boundary, []) ->
{[<<"\r\n--">>, Boundary, <<"--\r\n">>], done};
multi_send_file(C, H, Length, Boundary, [{Start, End}|Rest]) ->
{stream, {Data, Thunk}} = send_file(C, H, Start, End),
{[multipart_header(Boundary, Start, End, Length), Data],
fun() ->
multi_send_helper(C, H, Length, Boundary, Rest, Thunk)
end}.

multi_send_helper(C, H, Length, Boundary, Rest, done) ->
multi_send_file(C, H, Length, Boundary, Rest);
multi_send_helper(C, H, Length, Boundary, Rest, Thunk) ->
{Data, NewThunk} = Thunk(),
{Data,
fun() ->
multi_send_helper(C, H, Length, Boundary, Rest, NewThunk)
end}.

multipart_header(Boundary, Start, End, Length) ->
[<<"\r\n--">>, Boundary, <<"\r\n">>,
?HEAD_CRANGE, <<": ">>,
content_range_header(Start, End, Length), <<"\r\n">>].

%% @spec ensure_doc(context()) -> context()
%% @doc Ensure that the 'doc' field of the context() has been filled
%% with the result of a riak_client:get request. This is a
Expand Down

0 comments on commit 0db0bf6

Please sign in to comment.