Skip to content

Commit

Permalink
fix: correctly encode and sign block ids
Browse files Browse the repository at this point in the history
  • Loading branch information
thalesmg committed May 20, 2024
1 parent 4b072a0 commit 4495fba
Show file tree
Hide file tree
Showing 3 changed files with 50 additions and 2 deletions.
7 changes: 6 additions & 1 deletion src/erlazure.erl
Original file line number Diff line number Diff line change
Expand Up @@ -667,7 +667,7 @@ handle_call({delete_blob, Container, Blob, Options}, _From, State) ->
handle_call({put_block, Container, Blob, BlockId, Content, Options}, _From, State) ->
ServiceContext = new_service_context(?blob_service, State),
Params = [{comp, block},
{blob_block_id, base64:encode_to_string(BlockId)}],
{blob_block_id, uri_string:quote(base64:encode_to_string(BlockId))}],
ReqOptions = [{method, put},
{path, lists:concat([Container, "/", Blob])},
{body, Content},
Expand Down Expand Up @@ -938,6 +938,11 @@ combine_canonical_param({Param, Value}, _PreviousParam, Acc, ParamList) ->
[H | T] = ParamList,
combine_canonical_param(H, Param, add_param_value(Param, Value, Acc), T).

add_param_value(Param, Value, Acc) ->
%% special case: `blockid' must be URL-encoded when sending the request, but not
%% when signing it. At this point, we've already encoded it.
Acc ++ "\n" ++ string:to_lower(Param) ++ ":" ++ uri_string:unquote(Value);

add_param_value(Param, Value, Acc) ->
Acc ++ "\n" ++ string:to_lower(Param) ++ ":" ++ Value.

Expand Down
6 changes: 5 additions & 1 deletion src/erlazure_blob.erl
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,11 @@ get_request_body(BlockRefs) ->
FoldFun = fun(BlockRef=#blob_block{}, Acc) ->
[{block_type_to_node(BlockRef#blob_block.type),
[],
[base64:encode_to_string(BlockRef#blob_block.id)]} | Acc]
[base64:encode_to_string(BlockRef#blob_block.id)]} | Acc];
({BlockId, BlockType}, Acc) ->
[{block_type_to_node(BlockType),
[],
[base64:encode_to_string(BlockId)]} | Acc]
end,
Data = {'BlockList', [], lists:reverse(lists:foldl(FoldFun, [], BlockRefs))},
lists:flatten(xmerl:export_simple([Data], xmerl_xml)).
Expand Down
39 changes: 39 additions & 0 deletions test/erlazure_SUITE.erl
Original file line number Diff line number Diff line change
Expand Up @@ -166,3 +166,42 @@ t_blob_failure_to_connect(_Config) ->
?assertMatch({error, {failed_connect, _}}, erlazure:append_block(Pid, "c", "b1", <<"a">>)),
?assertMatch({error, {failed_connect, _}}, erlazure:get_blob(Pid, "c", "b1")),
ok.

%% Basic smoke test for block blob storage operations.
t_put_block(Config) ->
Endpoint = ?config(endpoint, Config),
{ok, Pid} = erlazure:start(#{account => ?ACCOUNT, key => ?KEY, endpoint => Endpoint}),
%% Create a container
Container = container_name(?FUNCTION_NAME),
?assertMatch({[], _}, erlazure:list_containers(Pid)),
?assertMatch({ok, created}, erlazure:create_container(Pid, Container)),
%% Upload some blocks
Opts = [{content_type, "text/csv"}],
BlobName = "blob1",
?assertMatch({ok, created}, erlazure:put_block_blob(Pid, Container, BlobName, <<>>, Opts)),
%% Note: this short name is important for this test. It'll produce a base64 string
%% that's padded. That padding must be URL-encoded when sending the request, but not
%% when generating the string to sign.
BlockId1 = <<"blo1">>,
?assertMatch({ok, created}, erlazure:put_block(Pid, Container, BlobName, BlockId1, <<"a">>)),
%% Testing iolists
BlockId2 = <<"blo2">>,
?assertMatch({ok, created}, erlazure:put_block(Pid, Container, BlobName, BlockId2, [<<"\n">>, ["b", [$\n]]])),
%% Not yet committed.
?assertMatch({ok, <<"">>}, erlazure:get_blob(Pid, Container, BlobName)),
%% Committing
BlockList1 = [{BlockId1, latest}],
?assertMatch({ok, created}, erlazure:put_block_list(Pid, Container, BlobName, BlockList1)),
%% Committed only first block.
?assertMatch({ok, <<"a">>}, erlazure:get_blob(Pid, Container, BlobName)),
%% Block 2 was dropped after committing.
?assertMatch({[#blob_block{id = "blo1"}], _}, erlazure:get_block_list(Pid, Container, BlobName)),
BlockId3 = <<"blo3">>,
?assertMatch({ok, created}, erlazure:put_block(Pid, Container, BlobName, BlockId3, [<<"\n">>, ["b", [$\n]]])),
%% Commit both blocks
BlockList2 = [{BlockId1, committed}, {BlockId3, uncommitted}],
?assertMatch({ok, created}, erlazure:put_block_list(Pid, Container, BlobName, BlockList2)),
?assertMatch({ok, <<"a\nb\n">>}, erlazure:get_blob(Pid, Container, BlobName)),
%% Delete container
?assertMatch({ok, deleted}, erlazure:delete_container(Pid, Container)),
ok.

0 comments on commit 4495fba

Please sign in to comment.