Current section
Files
Jump to
Current section
Files
src/grpcbox_utils.erl
-module(grpcbox_utils).-export([headers_to_metadata/1, maybe_decode_header/2, decode_header/1, encode_headers/1, is_reserved_header/1, status_to_string/1, get_timeout_from_ctx/2]).-include("grpcbox.hrl").headers_to_metadata(H) -> lists:foldl(fun({K, V}, Acc) -> case is_reserved_header(K) of true -> Acc; false -> maps:put(K, maybe_decode_header(K, V), Acc) end end, #{}, H).%% TODO: consolidate with grpc_lib. But have to update their header map to support%% a list of values for a key.maybe_decode_header(Key, Value) -> case binary:longest_common_suffix([Key, <<"-bin">>]) == 4 of true -> decode_header(Value); false -> Value end.%% golang gRPC implementation does not add the padding that the Erlang%% decoder needs...decode_header(Base64) when byte_size(Base64) rem 4 == 3 -> base64:decode(<<Base64/bytes, "=">>);decode_header(Base64) when byte_size(Base64) rem 4 == 2 -> base64:decode(<<Base64/bytes, "==">>);decode_header(Base64) -> base64:decode(Base64).encode_headers([]) -> [];encode_headers([{Key, Value} | Rest]) -> case binary:longest_common_suffix([Key, <<"-bin">>]) == 4 of true -> [{Key, base64:encode(Value)} | encode_headers(Rest)]; false -> [{Key, Value} | encode_headers(Rest)] end.is_reserved_header(<<"content-type">>) -> true;is_reserved_header(<<"grpc-message-type">>) -> true;is_reserved_header(<<"grpc-encoding">>) -> true;is_reserved_header(<<"grpc-message">>) -> true;is_reserved_header(<<"grpc-status">>) -> true;is_reserved_header(<<"grpc-timeout">>) -> true;is_reserved_header(<<"grpc-status-details-bin">>) -> true;is_reserved_header(<<"te">>) -> true;is_reserved_header(_) -> false.-spec status_to_string(binary()) -> binary().status_to_string(?GRPC_STATUS_OK) -> <<"OK">>;status_to_string(?GRPC_STATUS_CANCELLED) -> <<"CANCELLED">>;status_to_string(?GRPC_STATUS_UNKNOWN) -> <<"UNKNOWN">>;status_to_string(?GRPC_STATUS_INVALID_ARGUMENT) -> <<"INVALID_ARGUMENT">>;status_to_string(?GRPC_STATUS_DEADLINE_EXCEEDED) -> <<"DEADLINE_EXCEEDED">>;status_to_string(?GRPC_STATUS_NOT_FOUND) -> <<"NOT_FOUND">>;status_to_string(?GRPC_STATUS_ALREADY_EXISTS) -> <<"ALREADY_EXISTS">>;status_to_string(?GRPC_STATUS_PERMISSION_DENIED) -> <<"PERMISSION_DENIED">>;status_to_string(?GRPC_STATUS_RESOURCE_EXHAUSTED) -> <<"RESOURCE_EXHAUSTED">>;status_to_string(?GRPC_STATUS_FAILED_PRECONDITION) -> <<"FAILED_PRECONDITION">>;status_to_string(?GRPC_STATUS_ABORTED) -> <<"ABORTED">>;status_to_string(?GRPC_STATUS_OUT_OF_RANGE) -> <<"OUT_OF_RANGE">>;status_to_string(?GRPC_STATUS_UNIMPLEMENTED) -> <<"UNIMPLEMENTED">>;status_to_string(?GRPC_STATUS_INTERNAL) -> <<"INTERNAL">>;status_to_string(?GRPC_STATUS_UNAVAILABLE) -> <<"UNAVAILABLE">>;status_to_string(?GRPC_STATUS_DATA_LOSS) -> <<"DATA_LOSS">>;status_to_string(?GRPC_STATUS_UNAUTHENTICATED) -> <<"UNAUTHENTICATED">>;status_to_string(Code) -> <<"CODE_", Code/binary>>.get_timeout_from_ctx(Ctx, DefaultTimeout) -> case ctx:deadline(Ctx) of undefined -> DefaultTimeout; infinity -> infinity; {Deadline, _} -> Timeout = erlang:convert_time_unit(Deadline - erlang:monotonic_time(), native, millisecond), case Timeout < 0 of true -> 0; false -> Timeout end end.