diff --git a/src/oidcc_http_util.erl b/src/oidcc_http_util.erl index adb5072..2b2d6a0 100644 --- a/src/oidcc_http_util.erl +++ b/src/oidcc_http_util.erl @@ -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( @@ -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 + ). diff --git a/test/oidcc_http_util_SUITE.erl b/test/oidcc_http_util_SUITE.erl index 09d331b..58bdd96 100644 --- a/test/oidcc_http_util_SUITE.erl +++ b/test/oidcc_http_util_SUITE.erl @@ -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, #{