Packages
launchdarkly_server_sdk
2.1.2
3.11.0
3.10.1
3.9.0
3.8.1
3.8.0
3.7.2
3.7.1
3.7.0
3.6.0
3.5.0
3.4.0
3.3.1
3.3.0
3.2.0
3.1.0
3.0.4
3.0.3
3.0.2
3.0.1
3.0.0
2.1.2
2.1.1
2.1.0
2.0.5
2.0.4
2.0.3
2.0.2
2.0.1
2.0.0
1.6.0
1.5.0
1.4.0
1.3.2
1.3.1
1.3.0
1.2.0
1.1.3
1.1.2
1.1.1
retired
1.1.0
retired
1.0.1
retired
1.0.0
retired
1.0.0-beta4
retired
1.0.0-beta3
retired
1.0.0-beta2
retired
LaunchDarkly SDK for Erlang
Current section
Files
Jump to
Current section
Files
src/ldclient_event_dispatch_httpc.erl
%%-------------------------------------------------------------------
%% @doc Event dispatcher
%% @private
%% @end
%%-------------------------------------------------------------------
-module(ldclient_event_dispatch_httpc).
-behaviour(ldclient_event_dispatch).
%% Behavior callbacks
-export([init/2, send/4]).
%% Internal type for ETag cache state
-type state() :: #{
headers => list(),
http_options => list()
}.
%% Expose non-exported methods for tests.
-ifdef(TEST).
-compile(export_all).
-endif.
%%===================================================================
%% Behavior callbacks
%%===================================================================
-spec init(Tag :: atom(), SdkKey :: string()) -> state().
init(Tag, _SdkKey) ->
Options = ldclient_config:get_value(Tag, http_options),
HttpOptions = ldclient_http_options:httpc_parse_http_options(Options),
DefaultHeaders = ldclient_headers:get_default_headers(Tag, string_pairs),
Headers = ldclient_http_options:httpc_append_custom_headers([
{"X-LaunchDarkly-Event-Schema", ldclient_config:get_event_schema()}
| DefaultHeaders
], Options),
#{
headers => Headers,
http_options => HttpOptions
}.
%% @doc Send events to LaunchDarkly event server
%%
%% @end
-spec send(State :: state(), JsonEvents :: binary(), PayloadId :: uuid:uuid(), Uri :: string()) ->
{ok, integer()} | {error, temporary, string()} | {error, permanent, string()}.
send(State, JsonEvents, PayloadId, Uri) ->
#{headers := BaseHeaders, http_options := HttpOptions} = State,
Headers = [
{"X-LaunchDarkly-Payload-ID", uuid:uuid_to_string(PayloadId)} |
BaseHeaders
],
Request = httpc:request(post, {Uri, Headers, "application/json", JsonEvents}, HttpOptions, []),
process_request(Request).
%%===================================================================
%% Internal functions
%%===================================================================
-type http_request() :: {ok, {{string(), integer(), string()}, [{string(), string()}], string() | binary()}}.
-spec process_request({error, term()} | http_request())
-> {ok, integer()} | {error, temporary, string()} | {error, permanent, string()}.
process_request({error, Reason}) ->
{error, temporary, Reason};
process_request({ok, {{_Version, StatusCode, _ReasonPhrase}, Headers, _Body}}) when StatusCode < 400 ->
{ok, get_server_time(Headers)};
process_request({ok, {{Version, StatusCode, ReasonPhrase}, _Headers, _Body}}) ->
Reason = format_response(Version, StatusCode, ReasonPhrase),
HttpErrorType = ldclient_http:is_http_error_code_recoverable(StatusCode),
{error, HttpErrorType, Reason}.
-spec format_response(Version :: string(), StatusCode :: integer(), ReasonPhrase :: string()) ->
string().
format_response(Version, StatusCode, ReasonPhrase) ->
io_lib:format("~s ~b ~s", [Version, StatusCode, ReasonPhrase]).
%% Get the server time, and if there is not time, then return 0.
-spec get_server_time(Headers :: [{Field :: [byte()], Value :: binary() | iolist()}]) -> integer().
get_server_time([{"date", Date}|_T]) when is_list(Date) ->
%% convert_request_date expects a string that is a list of characters.
%% Not a binary string. The guard can make sure it is a list, but not
%% that it is a char list. So that gets checked here.
case io_lib:char_list(Date) of
true -> case httpd_util:convert_request_date(Date) of
bad_date ->
%% This would be a date in a bad format.
0;
ParsedDate ->
ldclient_time:datetime_to_timestamp(ParsedDate)
end;
false ->
%% The date was a list, but was not a list of characters.
0
end;
get_server_time([_H|T]) ->
get_server_time(T);
get_server_time(_) -> 0.