Skip to content

Commit

Permalink
feat: tell httpc to close the connection if we're using client certs
Browse files Browse the repository at this point in the history
In addition to the separate profile, this header serves as a note to
both the remote server and `httpc` that we're not keeping the connection
open after the request.`
  • Loading branch information
paulswartz committed Jan 8, 2024
1 parent 014e374 commit a23417d
Show file tree
Hide file tree
Showing 2 changed files with 50 additions and 4 deletions.
41 changes: 37 additions & 4 deletions src/oidcc_http_util.erl
Original file line number Diff line number Diff line change
Expand Up @@ -85,17 +85,32 @@ when
Accumulator :: term()},
TelemetryOpts :: telemetry_opts(),
RequestOpts :: request_opts().
request(Method, Request, TelemetryOpts, RequestOpts) ->
request(Method, Request0, TelemetryOpts, RequestOpts) ->
TelemetryTopic = maps:get(topic, TelemetryOpts),
TelemetryExtraMeta = maps:get(extra_meta, TelemetryOpts, #{}),
Timeout = maps:get(timeout, RequestOpts, timer:minutes(1)),
SslOpts = maps:get(ssl, RequestOpts, undefined),

HttpOpts0 = [{timeout, Timeout}],
{HttpOpts, HttpProfile} =
HttpOpts =
case SslOpts of
undefined -> {HttpOpts0, default};
_Opts -> {[{ssl, SslOpts} | HttpOpts0], oidcc_app:httpc_profile()}
undefined -> HttpOpts0;
_Opts -> [{ssl, SslOpts} | HttpOpts0]
end,

%% if we're using special SSL opts, always close the connection after we're
%% done. we do this to work around an issue with httpc where it doesn't take
%% the client certificates into account when pipelining, so if the HTTP
%% implementation changes we should think about undoing this.
{Request, HttpProfile} =
case using_client_certificate(SslOpts) of
false ->
{Request0, default};
true ->
ReqHeaders0 = element(2, Request0),
ReqHeaders1 = [{"connection", "close"} | ReqHeaders0],
ReqHeaders = lists:ukeysort(1, ReqHeaders1),
{setelement(2, Request0, ReqHeaders), oidcc_app:httpc_profile()}
end,

telemetry:span(
Expand Down Expand Up @@ -166,3 +181,21 @@ fetch_content_type(Headers) ->
_Other ->
unknown
end.

-spec using_client_certificate([ssl:tls_client_option()] | undefined) -> boolean().
using_client_certificate(undefined) ->
false;
using_client_certificate(SslOpts) ->
lists:foldl(
fun
(_, true) -> true;
({key, _}, _) -> true;
({keyfile, _}, _) -> true;
({cert, _}, _) -> true;
({certfile, _}, _) -> true;
({certs_keys, _}, _) -> true;
(_, Acc) -> Acc
end,
false,
SslOpts
).
13 changes: 13 additions & 0 deletions test/oidcc_http_util_SUITE.erl
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,19 @@ client_cert(_Config) ->
keyfile => KeyFile
}
],

?assertMatch(
{error, {http_error, 403, <<"">>}},
oidcc_http_util:request(
get, {"https://certauth.idrix.fr/json/", []}, telemetry_opts(), #{
ssl => [
{verify, verify_peer},
{cacerts, public_key:cacerts_get()}
]
}
)
),

?assertMatch(
{ok, {
{json, #{
Expand Down

0 comments on commit a23417d

Please sign in to comment.