1616
1717package com .linecorp .armeria .client .athenz ;
1818
19- import static java .util .Objects .requireNonNull ;
20-
2119import java .time .Duration ;
2220import java .util .List ;
2321import java .util .concurrent .CompletableFuture ;
22+ import java .util .concurrent .TimeUnit ;
2423import java .util .function .Function ;
2524
26- import com .google .common .collect .ImmutableList ;
27-
2825import com .linecorp .armeria .client .ClientRequestContext ;
2926import com .linecorp .armeria .client .HttpClient ;
3027import com .linecorp .armeria .client .SimpleDecoratingHttpClient ;
3330import com .linecorp .armeria .common .RequestHeadersBuilder ;
3431import com .linecorp .armeria .common .annotation .UnstableApi ;
3532import com .linecorp .armeria .common .athenz .TokenType ;
33+ import com .linecorp .armeria .common .metric .MeterIdPrefix ;
34+ import com .linecorp .armeria .common .metric .MoreMeters ;
3635import com .linecorp .armeria .common .util .Exceptions ;
3736
37+ import io .micrometer .core .instrument .MeterRegistry ;
38+ import io .micrometer .core .instrument .Timer ;
39+
3840/**
3941 * An {@link HttpClient} that adds an Athenz token to the request headers.
4042 * {@link TokenType#ACCESS_TOKEN} and {@link TokenType#YAHOO_ROLE_TOKEN} are supported.
5355 * .keyPair("/var/lib/athenz/service.key.pem", "/var/lib/athenz/service.cert.pem")
5456 * .build();
5557 *
58+ * // Using builder
59+ * WebClient
60+ * .builder()
61+ * .decorator(AthenzClient.builder(ztsBaseClient)
62+ * .domainName("my-domain")
63+ * .tokenType(TokenType.ROLE_TOKEN)
64+ * .newDecorator())
65+ * ...
66+ * .build();
67+ *
68+ * // Or using static factory method
5669 * WebClient
5770 * .builder()
5871 * .decorator(AthenzClient.newDecorator(ztsBaseClient, "my-domain",
59- * TokenType.ROLE_TOKEN)
72+ * TokenType.ROLE_TOKEN))
6073 * ...
6174 * .build();
6275 * }</pre>
6376 */
6477@ UnstableApi
6578public final class AthenzClient extends SimpleDecoratingHttpClient {
6679
67- private static final Duration DEFAULT_REFRESH_BEFORE = Duration .ofMinutes (10 );
80+ /**
81+ * Returns a new {@link AthenzClientBuilder} with the specified {@link ZtsBaseClient}.
82+ */
83+ public static AthenzClientBuilder builder (ZtsBaseClient ztsBaseClient ) {
84+ return new AthenzClientBuilder (ztsBaseClient );
85+ }
6886
6987 /**
7088 * Returns a new {@link HttpClient} decorator that obtains an Athenz token for the specified domain and
@@ -74,9 +92,12 @@ public final class AthenzClient extends SimpleDecoratingHttpClient {
7492 * @param domainName the Athenz domain name
7593 * @param tokenType the type of Athenz token to obtain
7694 */
77- public static Function <HttpClient , AthenzClient > newDecorator (ZtsBaseClient ztsBaseClient ,
78- String domainName , TokenType tokenType ) {
79- return newDecorator (ztsBaseClient , domainName , ImmutableList .of (), tokenType );
95+ public static Function <? super HttpClient , AthenzClient > newDecorator (
96+ ZtsBaseClient ztsBaseClient , String domainName , TokenType tokenType ) {
97+ return builder (ztsBaseClient )
98+ .domainName (domainName )
99+ .tokenType (tokenType )
100+ .newDecorator ();
80101 }
81102
82103 /**
@@ -88,10 +109,13 @@ public static Function<HttpClient, AthenzClient> newDecorator(ZtsBaseClient ztsB
88109 * @param roleName the Athenz role name
89110 * @param tokenType the type of Athenz token to obtain
90111 */
91- public static Function <HttpClient , AthenzClient > newDecorator (ZtsBaseClient ztsBaseClient ,
92- String domainName , String roleName ,
93- TokenType tokenType ) {
94- return newDecorator (ztsBaseClient , domainName , ImmutableList .of (roleName ), tokenType );
112+ public static Function <? super HttpClient , AthenzClient > newDecorator (
113+ ZtsBaseClient ztsBaseClient , String domainName , String roleName , TokenType tokenType ) {
114+ return builder (ztsBaseClient )
115+ .domainName (domainName )
116+ .roleNames (roleName )
117+ .tokenType (tokenType )
118+ .newDecorator ();
95119 }
96120
97121 /**
@@ -103,10 +127,13 @@ public static Function<HttpClient, AthenzClient> newDecorator(ZtsBaseClient ztsB
103127 * @param roleNames the list of Athenz role names
104128 * @param tokenType the type of Athenz token to obtain
105129 */
106- public static Function <HttpClient , AthenzClient > newDecorator (ZtsBaseClient ztsBaseClient ,
107- String domainName , List <String > roleNames ,
108- TokenType tokenType ) {
109- return newDecorator (ztsBaseClient , domainName , roleNames , tokenType , DEFAULT_REFRESH_BEFORE );
130+ public static Function <? super HttpClient , AthenzClient > newDecorator (
131+ ZtsBaseClient ztsBaseClient , String domainName , List <String > roleNames , TokenType tokenType ) {
132+ return builder (ztsBaseClient )
133+ .domainName (domainName )
134+ .roleNames (roleNames )
135+ .tokenType (tokenType )
136+ .newDecorator ();
110137 }
111138
112139 /**
@@ -119,26 +146,40 @@ public static Function<HttpClient, AthenzClient> newDecorator(ZtsBaseClient ztsB
119146 * @param tokenType the type of Athenz token to obtain
120147 * @param refreshBefore the duration before the token expires to refresh it
121148 */
122- public static Function <HttpClient , AthenzClient > newDecorator (ZtsBaseClient ztsBaseClient ,
123- String domainName , List <String > roleNames ,
124- TokenType tokenType , Duration refreshBefore ) {
125- requireNonNull (ztsBaseClient , "ztsBaseClient" );
126- requireNonNull (domainName , "domainName" );
127- requireNonNull (roleNames , "roleNames" );
128- final ImmutableList <String > roleNames0 = ImmutableList .copyOf (roleNames );
129- requireNonNull (tokenType , "tokenType" );
130- requireNonNull (refreshBefore , "refreshBefore" );
131- return delegate -> new AthenzClient (delegate , ztsBaseClient , domainName , roleNames0 ,
132- tokenType , refreshBefore );
149+ public static Function <? super HttpClient , AthenzClient > newDecorator (
150+ ZtsBaseClient ztsBaseClient , String domainName , List <String > roleNames ,
151+ TokenType tokenType , Duration refreshBefore ) {
152+ return builder (ztsBaseClient )
153+ .domainName (domainName )
154+ .roleNames (roleNames )
155+ .tokenType (tokenType )
156+ .refreshBefore (refreshBefore )
157+ .newDecorator ();
133158 }
134159
135160 private final TokenType tokenType ;
136161 private final TokenClient tokenClient ;
162+ private final Timer successTimer ;
163+ private final Timer failureTimer ;
137164
138- private AthenzClient (HttpClient delegate , ZtsBaseClient ztsBaseClient , String domainName ,
139- List <String > roleNames , TokenType tokenType , Duration refreshBefore ) {
165+ AthenzClient (HttpClient delegate , ZtsBaseClient ztsBaseClient , String domainName ,
166+ List <String > roleNames , TokenType tokenType , Duration refreshBefore ,
167+ MeterIdPrefix meterIdPrefix ) {
140168 super (delegate );
141169 this .tokenType = tokenType ;
170+ final MeterRegistry meterRegistry = ztsBaseClient .clientFactory ().meterRegistry ();
171+ final String prefix = meterIdPrefix .name ("token.fetch" );
172+ successTimer = MoreMeters .newTimer (meterRegistry , prefix ,
173+ meterIdPrefix .tags ("result" , "success" ,
174+ "domain" , domainName ,
175+ "roles" , String .join ("," , roleNames ),
176+ "type" , tokenType .name ()));
177+ failureTimer = MoreMeters .newTimer (meterRegistry , prefix ,
178+ meterIdPrefix .tags ("result" , "failure" ,
179+ "domain" , domainName ,
180+ "roles" , String .join ("," , roleNames ),
181+ "type" , tokenType .name ()));
182+
142183 if (tokenType .isRoleToken ()) {
143184 tokenClient = new RoleTokenClient (ztsBaseClient , domainName , roleNames , refreshBefore );
144185 } else {
@@ -148,7 +189,16 @@ private AthenzClient(HttpClient delegate, ZtsBaseClient ztsBaseClient, String do
148189
149190 @ Override
150191 public HttpResponse execute (ClientRequestContext ctx , HttpRequest req ) throws Exception {
151- final CompletableFuture <HttpResponse > future = tokenClient .getToken ().thenApply (token -> {
192+ final long startNanos = System .nanoTime ();
193+
194+ final CompletableFuture <HttpResponse > future = tokenClient .getToken ().handle ((token , cause ) -> {
195+ final long elapsedNanos = System .nanoTime () - startNanos ;
196+ if (cause != null ) {
197+ failureTimer .record (elapsedNanos , TimeUnit .NANOSECONDS );
198+ return Exceptions .throwUnsafely (cause );
199+ }
200+
201+ successTimer .record (elapsedNanos , TimeUnit .NANOSECONDS );
152202 final HttpRequest newReq = req .mapHeaders (headers -> {
153203 final RequestHeadersBuilder builder = headers .toBuilder ();
154204 String token0 = token ;
0 commit comments